runtest.py 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506
  1. #!/usr/bin/env python
  2. from __future__ import print_function
  3. import argparse
  4. import array
  5. import atexit
  6. import math
  7. import os
  8. import re
  9. import shutil
  10. import struct
  11. import subprocess
  12. import sys
  13. import tempfile
  14. import time
  15. import threading
  16. import traceback
  17. from select import select
  18. from queue import Queue
  19. from subprocess import PIPE, STDOUT, Popen
  20. from typing import BinaryIO, Optional, Tuple
  21. if sys.version_info[0] == 2:
  22. IS_PY_3 = False
  23. else:
  24. IS_PY_3 = True
  25. test_aot = False
  26. # Available targets:
  27. # "aarch64" "aarch64_vfp" "armv7" "armv7_vfp" "thumbv7" "thumbv7_vfp"
  28. # "riscv32" "riscv32_ilp32f" "riscv32_ilp32d" "riscv64" "riscv64_lp64f" "riscv64_lp64d"
  29. test_target = "x86_64"
  30. debug_file = None
  31. log_file = None
  32. # to save the register module with self-define name
  33. temp_file_repo = []
  34. # to save the mapping of module files in /tmp by name
  35. temp_module_table = {}
  36. # AOT compilation options mapping
  37. aot_target_options_map = {
  38. "i386": ["--target=i386"],
  39. "x86_32": ["--target=i386"],
  40. "x86_64": ["--target=x86_64", "--cpu=skylake"],
  41. "aarch64": ["--target=aarch64", "--target-abi=eabi", "--cpu=cortex-a53"],
  42. "aarch64_vfp": ["--target=aarch64", "--target-abi=gnueabihf", "--cpu=cortex-a53"],
  43. "armv7": ["--target=armv7", "--target-abi=eabi", "--cpu=cortex-a9", "--cpu-features=-neon"],
  44. "armv7_vfp": ["--target=armv7", "--target-abi=gnueabihf", "--cpu=cortex-a9"],
  45. "thumbv7": ["--target=thumbv7", "--target-abi=eabi", "--cpu=cortex-a9", "--cpu-features=-neon,-vfpv3"],
  46. "thumbv7_vfp": ["--target=thumbv7", "--target-abi=gnueabihf", "--cpu=cortex-a9", "--cpu-features=-neon"],
  47. "riscv32": ["--target=riscv32", "--target-abi=ilp32", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"],
  48. "riscv32_ilp32f": ["--target=riscv32", "--target-abi=ilp32f", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c,+f"],
  49. "riscv32_ilp32d": ["--target=riscv32", "--target-abi=ilp32d", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c,+f,+d"],
  50. "riscv64": ["--target=riscv64", "--target-abi=lp64", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c"],
  51. "riscv64_lp64f": ["--target=riscv64", "--target-abi=lp64f", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c,+f"],
  52. "riscv64_lp64d": ["--target=riscv64", "--target-abi=lp64d", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c,+f,+d"],
  53. }
  54. def debug(data):
  55. if debug_file:
  56. debug_file.write(data)
  57. debug_file.flush()
  58. def log(data, end='\n'):
  59. if log_file:
  60. log_file.write(data + end)
  61. log_file.flush()
  62. print(data, end=end)
  63. sys.stdout.flush()
  64. def create_tmp_file(suffix: str) -> str:
  65. with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp_file:
  66. return tmp_file.name
  67. # TODO: do we need to support '\n' too
  68. import platform
  69. if platform.system().find("CYGWIN_NT") >= 0:
  70. # TODO: this is weird, is this really right on Cygwin?
  71. sep = "\n\r\n"
  72. else:
  73. sep = "\r\n"
  74. rundir = None
  75. class AsyncStreamReader:
  76. def __init__(self, stream: BinaryIO) -> None:
  77. self._queue = Queue()
  78. self._reader_thread = threading.Thread(
  79. daemon=True,
  80. target=AsyncStreamReader._stdout_reader,
  81. args=(self._queue, stream))
  82. self._reader_thread.start()
  83. def read(self) -> Optional[bytes]:
  84. return self._queue.get()
  85. def cleanup(self) -> None:
  86. self._reader_thread.join()
  87. @staticmethod
  88. def _stdout_reader(queue: Queue, stdout: BinaryIO) -> None:
  89. while True:
  90. try:
  91. queue.put(stdout.read(1))
  92. except ValueError as e:
  93. if stdout.closed:
  94. queue.put(None)
  95. break
  96. raise e
  97. class Runner():
  98. def __init__(self, args, no_pty=False):
  99. self.no_pty = no_pty
  100. # Cleanup child process on exit
  101. atexit.register(self.cleanup)
  102. self.process = None
  103. env = os.environ
  104. env['TERM'] = 'dumb'
  105. env['INPUTRC'] = '/dev/null'
  106. env['PERL_RL'] = 'false'
  107. if no_pty:
  108. self.process = Popen(args, bufsize=0,
  109. stdin=PIPE, stdout=PIPE, stderr=STDOUT,
  110. env=env)
  111. self.stdin = self.process.stdin
  112. self.stdout = self.process.stdout
  113. else:
  114. import fcntl
  115. # Pseudo-TTY and terminal manipulation
  116. import pty
  117. import termios
  118. # Use tty to setup an interactive environment
  119. master, slave = pty.openpty()
  120. # Set terminal size large so that readline will not send
  121. # ANSI/VT escape codes when the lines are long.
  122. buf = array.array('h', [100, 200, 0, 0])
  123. fcntl.ioctl(master, termios.TIOCSWINSZ, buf, True)
  124. self.process = Popen(args, bufsize=0,
  125. stdin=slave, stdout=slave, stderr=STDOUT,
  126. preexec_fn=os.setsid,
  127. env=env)
  128. # Now close slave so that we will get an exception from
  129. # read when the child exits early
  130. # http://stackoverflow.com/questions/11165521
  131. os.close(slave)
  132. self.stdin = os.fdopen(master, 'r+b', 0)
  133. self.stdout = self.stdin
  134. if platform.system().lower() == "windows":
  135. self._stream_reader = AsyncStreamReader(self.stdout)
  136. else:
  137. self._stream_reader = None
  138. self.buf = ""
  139. def _read_stdout_byte(self) -> Tuple[bool, Optional[bytes]]:
  140. if self._stream_reader:
  141. return True, self._stream_reader.read()
  142. else:
  143. # select doesn't work on file descriptors on Windows.
  144. # however, this method is much faster than using
  145. # queue, so we keep it for non-windows platforms.
  146. [outs,_,_] = select([self.stdout], [], [], 1)
  147. if self.stdout in outs:
  148. return True, self.stdout.read(1)
  149. else:
  150. return False, None
  151. def read_to_prompt(self, prompts, timeout):
  152. wait_until = time.time() + timeout
  153. while time.time() < wait_until:
  154. has_value, read_byte = self._read_stdout_byte()
  155. if not has_value:
  156. continue
  157. if not read_byte:
  158. # EOF on macOS ends up here.
  159. break
  160. read_byte = read_byte.decode('utf-8') if IS_PY_3 else read_byte
  161. debug(read_byte)
  162. if self.no_pty:
  163. self.buf += read_byte.replace('\n', '\r\n')
  164. else:
  165. self.buf += read_byte
  166. self.buf = self.buf.replace('\r\r', '\r')
  167. # filter the prompts
  168. for prompt in prompts:
  169. pattern = re.compile(prompt)
  170. match = pattern.search(self.buf)
  171. if match:
  172. end = match.end()
  173. buf = self.buf[0:end-len(prompt)]
  174. self.buf = self.buf[end:]
  175. return buf
  176. return None
  177. def writeline(self, str):
  178. str_to_write = str + '\n'
  179. str_to_write = bytes(
  180. str_to_write, 'utf-8') if IS_PY_3 else str_to_write
  181. self.stdin.write(str_to_write)
  182. def cleanup(self):
  183. atexit.unregister(self.cleanup)
  184. if self.process:
  185. try:
  186. self.writeline("__exit__")
  187. time.sleep(.020)
  188. self.process.kill()
  189. except OSError:
  190. pass
  191. except IOError:
  192. pass
  193. self.process = None
  194. self.stdin.close()
  195. if self.stdin != self.stdout:
  196. self.stdout.close()
  197. self.stdin = None
  198. self.stdout = None
  199. if not IS_PY_3:
  200. sys.exc_clear()
  201. if self._stream_reader:
  202. self._stream_reader.cleanup()
  203. def assert_prompt(runner, prompts, timeout, is_need_execute_result):
  204. # Wait for the initial prompt
  205. header = runner.read_to_prompt(prompts, timeout=timeout)
  206. if not header and is_need_execute_result:
  207. log(" ---------- will terminate cause the case needs result while there is none inside of buf. ----------")
  208. sys.exit(1)
  209. if not header == None:
  210. if header:
  211. log("Started with:\n%s" % header)
  212. else:
  213. log("Did not one of following prompt(s): %s" % repr(prompts))
  214. log(" Got : %s" % repr(r.buf))
  215. sys.exit(1)
  216. ### WebAssembly specific
  217. parser = argparse.ArgumentParser(
  218. description="Run a test file against a WebAssembly interpreter")
  219. parser.add_argument('--wast2wasm', type=str,
  220. default=os.environ.get("WAST2WASM", "wast2wasm"),
  221. help="Path to wast2wasm program")
  222. parser.add_argument('--interpreter', type=str,
  223. default=os.environ.get("IWASM_CMD", "iwasm"),
  224. help="Path to WebAssembly interpreter")
  225. parser.add_argument('--aot-compiler', type=str,
  226. default=os.environ.get("WAMRC_CMD", "wamrc"),
  227. help="Path to WebAssembly AoT compiler")
  228. parser.add_argument('--no_cleanup', action='store_true',
  229. help="Keep temporary *.wasm files")
  230. parser.add_argument('--rundir',
  231. help="change to the directory before running tests")
  232. parser.add_argument('--start-timeout', default=30, type=int,
  233. help="default timeout for initial prompt")
  234. parser.add_argument('--test-timeout', default=20, type=int,
  235. help="default timeout for each individual test action")
  236. parser.add_argument('--no-pty', action='store_true',
  237. help="Use direct pipes instead of pseudo-tty")
  238. parser.add_argument('--log-file', type=str,
  239. help="Write messages to the named file in addition the screen")
  240. parser.add_argument('--log-dir', type=str,
  241. help="The log directory to save the case file if test failed")
  242. parser.add_argument('--debug-file', type=str,
  243. help="Write all test interaction the named file")
  244. parser.add_argument('test_file', type=argparse.FileType('r'),
  245. help="a WebAssembly *.wast test file")
  246. parser.add_argument('--aot', action='store_true',
  247. help="Test with AOT")
  248. parser.add_argument('--target', type=str,
  249. default="x86_64",
  250. help="Set running target")
  251. parser.add_argument('--sgx', action='store_true',
  252. help="Test SGX")
  253. parser.add_argument('--simd', default=False, action='store_true',
  254. help="Enable SIMD")
  255. parser.add_argument('--xip', default=False, action='store_true',
  256. help="Enable XIP")
  257. parser.add_argument('--eh', default=False, action='store_true',
  258. help="Enable Exception Handling")
  259. parser.add_argument('--multi-module', default=False, action='store_true',
  260. help="Enable Multi-thread")
  261. parser.add_argument('--multi-thread', default=False, action='store_true',
  262. help="Enable Multi-thread")
  263. parser.add_argument('--gc', default=False, action='store_true',
  264. help='Test with GC')
  265. parser.add_argument('--memory64', default=False, action='store_true',
  266. help='Test with Memory64')
  267. parser.add_argument('--qemu', default=False, action='store_true',
  268. help="Enable QEMU")
  269. parser.add_argument('--qemu-firmware', default='', help="Firmware required by qemu")
  270. parser.add_argument('--verbose', default=False, action='store_true',
  271. help='show more logs')
  272. # regex patterns of tests to skip
  273. C_SKIP_TESTS = ()
  274. PY_SKIP_TESTS = (
  275. # names.wast
  276. 'invoke \"~!',
  277. # conversions.wast
  278. '18446742974197923840.0',
  279. '18446744073709549568.0',
  280. '9223372036854775808',
  281. 'reinterpret_f.*nan',
  282. # endianness
  283. '.const 0x1.fff' )
  284. def read_forms(string):
  285. forms = []
  286. form = ""
  287. depth = 0
  288. line = 0
  289. pos = 0
  290. while pos < len(string):
  291. # Keep track of line number
  292. if string[pos] == '\n': line += 1
  293. # Handle top-level elements
  294. if depth == 0:
  295. # Add top-level comments
  296. if string[pos:pos+2] == ";;":
  297. end = string.find("\n", pos)
  298. if end == -1: end == len(string)
  299. forms.append(string[pos:end])
  300. pos = end
  301. continue
  302. # TODO: handle nested multi-line comments
  303. if string[pos:pos+2] == "(;":
  304. # Skip multi-line comment
  305. end = string.find(";)", pos)
  306. if end == -1:
  307. raise Exception("mismatch multiline comment on line %d: '%s'" % (
  308. line, string[pos:pos+80]))
  309. pos = end+2
  310. continue
  311. # Ignore whitespace between top-level forms
  312. if string[pos] in (' ', '\n', '\t'):
  313. pos += 1
  314. continue
  315. # Read a top-level form
  316. if string[pos] == '(': depth += 1
  317. if string[pos] == ')': depth -= 1
  318. if depth == 0 and not form:
  319. raise Exception("garbage on line %d: '%s'" % (
  320. line, string[pos:pos+80]))
  321. form += string[pos]
  322. if depth == 0 and form:
  323. forms.append(form)
  324. form = ""
  325. pos += 1
  326. return forms
  327. def get_module_exp_from_assert(string):
  328. depth = 0
  329. pos = 0
  330. module = ""
  331. exception = ""
  332. start_record = False
  333. result = []
  334. while pos < len(string):
  335. # record from the " (module "
  336. if string[pos:pos+7] == "(module":
  337. start_record = True
  338. if start_record:
  339. if string[pos] == '(' : depth += 1
  340. if string[pos] == ')' : depth -= 1
  341. module += string[pos]
  342. # if we get all (module ) .
  343. if depth == 0 and module:
  344. result.append(module)
  345. start_record = False
  346. # get expected exception
  347. if string[pos] == '"':
  348. end = string.find("\"", pos+1)
  349. if end != -1:
  350. end_rel = string.find("\"",end+1)
  351. if end_rel == -1:
  352. result.append(string[pos+1:end])
  353. pos += 1
  354. return result
  355. def string_to_unsigned(number_in_string, lane_type):
  356. if not lane_type in ['i8x16', 'i16x8', 'i32x4', 'i64x2']:
  357. raise Exception("invalid value {} and type {} and lane_type {}".format(number_in_string, type, lane_type))
  358. number = int(number_in_string, 16) if '0x' in number_in_string else int(number_in_string)
  359. if "i8x16" == lane_type:
  360. if number < 0:
  361. packed = struct.pack('b', number)
  362. number = struct.unpack('B', packed)[0]
  363. elif "i16x8" == lane_type:
  364. if number < 0:
  365. packed = struct.pack('h', number)
  366. number = struct.unpack('H', packed)[0]
  367. elif "i32x4" == lane_type:
  368. if number < 0:
  369. packed = struct.pack('i', number)
  370. number = struct.unpack('I', packed)[0]
  371. else: # "i64x2" == lane_type:
  372. if number < 0:
  373. packed = struct.pack('q', number)
  374. number = struct.unpack('Q', packed)[0]
  375. return number
  376. def cast_v128_to_i64x2(numbers, type, lane_type):
  377. numbers = [n.replace("_", "") for n in numbers]
  378. if "i8x16" == lane_type:
  379. assert(16 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  380. # str -> int
  381. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  382. # i8 -> i64
  383. packed = struct.pack(16 * "B", *numbers)
  384. elif "i16x8" == lane_type:
  385. assert(8 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  386. # str -> int
  387. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  388. # i16 -> i64
  389. packed = struct.pack(8 * "H", *numbers)
  390. elif "i32x4" == lane_type:
  391. assert(4 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  392. # str -> int
  393. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  394. # i32 -> i64
  395. packed = struct.pack(4 * "I", *numbers)
  396. elif "i64x2" == lane_type:
  397. assert(2 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  398. # str -> int
  399. numbers = [string_to_unsigned(n, lane_type) for n in numbers]
  400. # i64 -> i64
  401. packed = struct.pack(2 * "Q", *numbers)
  402. elif "f32x4" == lane_type:
  403. assert(4 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  404. # str -> int
  405. numbers = [parse_simple_const_w_type(n, "f32")[0] for n in numbers]
  406. # f32 -> i64
  407. packed = struct.pack(4 * "f", *numbers)
  408. elif "f64x2" == lane_type:
  409. assert(2 == len(numbers)), "{} should like {}".format(numbers, lane_type)
  410. # str -> int
  411. numbers = [parse_simple_const_w_type(n, "f64")[0] for n in numbers]
  412. # f64 -> i64
  413. packed = struct.pack(2 * "d", *numbers)
  414. else:
  415. raise Exception("invalid value {} and type {} and lane_type {}".format(numbers, type, lane_type))
  416. assert(packed)
  417. unpacked = struct.unpack("Q Q", packed)
  418. return unpacked, f"[{unpacked[0]:#x} {unpacked[1]:#x}]:{lane_type}:v128"
  419. def parse_simple_const_w_type(number, type):
  420. number = number.replace('_', '')
  421. number = re.sub(r"nan\((ind|snan)\)", "nan", number)
  422. if type in ["i32", "i64"]:
  423. number = int(number, 16) if '0x' in number else int(number)
  424. return number, "0x{:x}:{}".format(number, type) \
  425. if number >= 0 \
  426. else "-0x{:x}:{}".format(0 - number, type)
  427. elif type in ["f32", "f64"]:
  428. if "nan:" in number:
  429. return float('nan'), "nan:{}".format(type)
  430. else:
  431. number = float.fromhex(number) if '0x' in number else float(number)
  432. return number, "{:.7g}:{}".format(number, type)
  433. elif type == "ref.null":
  434. if number == "func":
  435. return "func", "func:ref.null"
  436. elif number == "extern":
  437. return "extern", "extern:ref.null"
  438. elif number == "any":
  439. return "any", "any:ref.null"
  440. else:
  441. raise Exception("invalid value {} and type {}".format(number, type))
  442. elif type == "ref.extern":
  443. number = int(number, 16) if '0x' in number else int(number)
  444. return number, "0x{:x}:ref.extern".format(number)
  445. elif type == "ref.host":
  446. number = int(number, 16) if '0x' in number else int(number)
  447. return number, "0x{:x}:ref.host".format(number)
  448. else:
  449. raise Exception("invalid value {} and type {}".format(number, type))
  450. def parse_assertion_value(val):
  451. """
  452. Parse something like:
  453. "ref.null extern" in (assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern))
  454. "ref.extern 1" in (assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1))
  455. "i32.const 0" in (assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0))
  456. in summary:
  457. type.const (sub-type) (val1 val2 val3 val4) ...
  458. type.const val
  459. ref.extern val
  460. ref.null ref_type
  461. ref.array
  462. ref.struct
  463. ref.func
  464. ref.i31
  465. """
  466. if not val:
  467. return None, ""
  468. splitted = re.split('\s+', val)
  469. splitted = [s for s in splitted if s]
  470. type = splitted[0].split(".")[0]
  471. lane_type = splitted[1] if len(splitted) > 2 else ""
  472. numbers = splitted[2:] if len(splitted) > 2 else splitted[1:]
  473. if type in ["i32", "i64", "f32", "f64"]:
  474. return parse_simple_const_w_type(numbers[0], type)
  475. elif type == "ref":
  476. if splitted[0] in ["ref.array", "ref.struct", "ref.func", "ref.i31"]:
  477. return splitted[0]
  478. # need to distinguish between "ref.null" and "ref.extern"
  479. return parse_simple_const_w_type(numbers[0], splitted[0])
  480. else:
  481. return cast_v128_to_i64x2(numbers, type, lane_type)
  482. def int2uint32(i):
  483. return i & 0xffffffff
  484. def int2int32(i):
  485. val = i & 0xffffffff
  486. if val & 0x80000000:
  487. return val - 0x100000000
  488. else:
  489. return val
  490. def int2uint64(i):
  491. return i & 0xffffffffffffffff
  492. def int2int64(i):
  493. val = i & 0xffffffffffffffff
  494. if val & 0x8000000000000000:
  495. return val - 0x10000000000000000
  496. else:
  497. return val
  498. def num_repr(i):
  499. if isinstance(i, int) or isinstance(i, long):
  500. return re.sub("L$", "", hex(i))
  501. else:
  502. return "%.16g" % i
  503. def hexpad16(i):
  504. return "0x%04x" % i
  505. def hexpad24(i):
  506. return "0x%06x" % i
  507. def hexpad32(i):
  508. return "0x%08x" % i
  509. def hexpad64(i):
  510. return "0x%016x" % i
  511. def invoke(r, args, cmd):
  512. r.writeline(cmd)
  513. return r.read_to_prompt(['\r\nwebassembly> ', '\nwebassembly> '],
  514. timeout=args.test_timeout)
  515. def vector_value_comparison(out, expected):
  516. """
  517. out likes "<number number>:v128"
  518. expected likes "[number number]:v128"
  519. """
  520. # print("vector value comparision {} vs {}".format(out, expected))
  521. out_val, out_type = out.split(':')
  522. # <number nubmer> => number number
  523. out_val = out_val[1:-1]
  524. expected_val, lane_type, expected_type = expected.split(':')
  525. # [number nubmer] => number number
  526. expected_val = expected_val[1:-1]
  527. assert("v128" == out_type), "out_type should be v128"
  528. assert("v128" == expected_type), "expected_type should be v128"
  529. if out_type != expected_type:
  530. return False
  531. out_val = out_val.split(" ")
  532. expected_val = expected_val.split(" ")
  533. # since i64x2
  534. out_packed = struct.pack("QQ", int(out_val[0], 16), int(out_val[1], 16))
  535. expected_packed = struct.pack("QQ",
  536. int(expected_val[0]) if not "0x" in expected_val[0] else int(expected_val[0], 16),
  537. int(expected_val[1]) if not "0x" in expected_val[1] else int(expected_val[1], 16))
  538. if lane_type in ["i8x16", "i16x8", "i32x4", "i64x2"]:
  539. return out_packed == expected_packed
  540. else:
  541. assert(lane_type in ["f32x4", "f64x2"]), "unexpected lane_type"
  542. if "f32x4" == lane_type:
  543. out_unpacked = struct.unpack("ffff", out_packed)
  544. expected_unpacked = struct.unpack("ffff", expected_packed)
  545. else:
  546. out_unpacked = struct.unpack("dd", out_packed)
  547. expected_unpacked = struct.unpack("dd", expected_packed)
  548. out_is_nan = [math.isnan(o) for o in out_unpacked]
  549. expected_is_nan = [math.isnan(e) for e in expected_unpacked]
  550. if any(out_is_nan):
  551. nan_comparision = [o == e for o, e in zip(out_is_nan, expected_is_nan)]
  552. if all(nan_comparision):
  553. print(f"Pass NaN comparision")
  554. return True
  555. # print(f"compare {out_unpacked} and {expected_unpacked}")
  556. result = [o == e for o, e in zip(out_unpacked, expected_unpacked)]
  557. if not all(result):
  558. result = [
  559. "{:.7g}".format(o) == "{:.7g}".format(e)
  560. for o, e in zip(out_unpacked, expected_packed)
  561. ]
  562. return all(result)
  563. def simple_value_comparison(out, expected):
  564. """
  565. compare out of simple types which may like val:i32, val:f64 and so on
  566. """
  567. if expected == "2.360523e+13:f32" and out == "2.360522e+13:f32":
  568. # one case in float_literals.wast, due to float precision of python
  569. return True
  570. if expected == "1.797693e+308:f64" and out == "inf:f64":
  571. # one case in float_misc.wast:
  572. # (assert_return (invoke "f64.add" (f64.const 0x1.fffffffffffffp+1023)
  573. # (f64.const 0x1.fffffffffffffp+969))
  574. # (f64.const 0x1.fffffffffffffp+1023))
  575. # the add result in x86_32 is inf
  576. return True
  577. out_val, out_type = out.split(':')
  578. expected_val, expected_type = expected.split(':')
  579. if not out_type == expected_type:
  580. return False
  581. out_val, _ = parse_simple_const_w_type(out_val, out_type)
  582. expected_val, _ = parse_simple_const_w_type(expected_val, expected_type)
  583. if out_val == expected_val \
  584. or (math.isnan(out_val) and math.isnan(expected_val)):
  585. return True
  586. if "i32" == expected_type:
  587. out_val_binary = struct.pack('I', out_val) if out_val > 0 \
  588. else struct.pack('i', out_val)
  589. expected_val_binary = struct.pack('I', expected_val) \
  590. if expected_val > 0 \
  591. else struct.pack('i', expected_val)
  592. elif "i64" == expected_type:
  593. out_val_binary = struct.pack('Q', out_val) if out_val > 0 \
  594. else struct.pack('q', out_val)
  595. expected_val_binary = struct.pack('Q', expected_val) \
  596. if expected_val > 0 \
  597. else struct.pack('q', expected_val)
  598. elif "f32" == expected_type:
  599. out_val_binary = struct.pack('f', out_val)
  600. expected_val_binary = struct.pack('f', expected_val)
  601. elif "f64" == expected_type:
  602. out_val_binary = struct.pack('d', out_val)
  603. expected_val_binary = struct.pack('d', expected_val)
  604. elif "ref.extern" == expected_type:
  605. out_val_binary = out_val
  606. expected_val_binary = expected_val
  607. elif "ref.host" == expected_type:
  608. out_val_binary = out_val
  609. expected_val_binary = expected_val
  610. else:
  611. assert(0), "unknown 'expected_type' {}".format(expected_type)
  612. if out_val_binary == expected_val_binary:
  613. return True
  614. if expected_type in ["f32", "f64"]:
  615. # compare with a lower precision
  616. out_str = "{:.7g}".format(out_val)
  617. expected_str = "{:.7g}".format(expected_val)
  618. if out_str == expected_str:
  619. return True
  620. return False
  621. def value_comparison(out, expected):
  622. if out == expected:
  623. return True
  624. if not expected:
  625. return False
  626. if not out in ["ref.array", "ref.struct", "ref.func", "ref.any", "ref.i31"]:
  627. assert(':' in out), "out should be in a form likes numbers:type, but {}".format(out)
  628. if not expected in ["ref.array", "ref.struct", "ref.func", "ref.any", "ref.i31"]:
  629. assert(':' in expected), "expected should be in a form likes numbers:type, but {}".format(expected)
  630. if 'v128' in out:
  631. return vector_value_comparison(out, expected)
  632. else:
  633. return simple_value_comparison(out, expected)
  634. def is_result_match_expected(out, expected):
  635. # compare value instead of comparing strings of values
  636. return value_comparison(out, expected)
  637. def test_assert(r, opts, mode, cmd, expected):
  638. log("Testing(%s) %s = %s" % (mode, cmd, expected))
  639. out = invoke(r, opts, cmd)
  640. if '\n' in out or ' ' in out:
  641. outs = [''] + out.split('\n')[1:]
  642. out = outs[-1]
  643. if mode=='trap':
  644. o = re.sub('^Exception: ', '', out)
  645. e = re.sub('^Exception: ', '', expected)
  646. if o.find(e) >= 0 or e.find(o) >= 0:
  647. return True
  648. if mode=='exhaustion':
  649. o = re.sub('^Exception: ', '', out)
  650. expected = 'Exception: stack overflow'
  651. e = re.sub('^Exception: ', '', expected)
  652. if o.find(e) >= 0 or e.find(o) >= 0:
  653. return True
  654. # wasm-exception thrown out of function call, not a trap
  655. if mode=='wasmexception':
  656. o = re.sub('^Exception: ', '', out)
  657. e = re.sub('^Exception: ', '', expected)
  658. if o.find(e) >= 0 or e.find(o) >= 0:
  659. return True
  660. ## 0x9:i32,-0x1:i32 -> ['0x9:i32', '-0x1:i32']
  661. expected_list = re.split(',', expected)
  662. out_list = re.split(',', out)
  663. if len(expected_list) != len(out_list):
  664. raise Exception("Failed:\n Results count incorrect:\n expected: '%s'\n got: '%s'" % (expected, out))
  665. for i in range(len(expected_list)):
  666. if not is_result_match_expected(out_list[i], expected_list[i]):
  667. raise Exception("Failed:\n Result %d incorrect:\n expected: '%s'\n got: '%s'" % (i, expected_list[i], out_list[i]))
  668. return True
  669. def test_assert_return(r, opts, form):
  670. """
  671. m. to search a pattern like (assert_return (invoke function_name ... ) ...)
  672. n. to search a pattern like (assert_return (invoke $module_name function_name ... ) ...)
  673. """
  674. # params, return
  675. m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
  676. # judge if assert_return cmd includes the module name
  677. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
  678. # print("assert_return with {}".format(form))
  679. if not m:
  680. # no params, return
  681. m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
  682. if not m:
  683. # params, no return
  684. m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
  685. if not m:
  686. # no params, no return
  687. m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S)
  688. if not m:
  689. # params, return
  690. if not n:
  691. # no params, return
  692. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
  693. if not n:
  694. # params, no return
  695. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
  696. if not n:
  697. # no params, no return
  698. n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"*()()\)\s*\)\s*$', form, re.S)
  699. if not m and not n:
  700. if re.search('^\(assert_return\s+\(get.*\).*\)$', form, re.S):
  701. log("ignoring assert_return get")
  702. return
  703. else:
  704. raise Exception("unparsed assert_return: '%s'" % form)
  705. if m and not n:
  706. func = m.group(1)
  707. if ' ' in func:
  708. func = func.replace(' ', '\\')
  709. if m.group(2) == '':
  710. args = []
  711. else:
  712. #args = [re.split(' +', v)[1].replace('_', "") for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  713. # split arguments with ')spaces(', remove leading and tailing ) and (
  714. args_type_and_value = re.split(r'\)\s+\(', m.group(2)[1:-1])
  715. args_type_and_value = [s.replace('_', '') for s in args_type_and_value]
  716. # args are in two forms:
  717. # f32.const -0x1.000001fffffffffffp-50
  718. # v128.const i32x4 0 0 0 0
  719. args = []
  720. for arg in args_type_and_value:
  721. # remove leading and tailing spaces, it might confuse following assertions
  722. arg = arg.strip()
  723. splitted = re.split('\s+', arg)
  724. splitted = [s for s in splitted if s]
  725. if splitted[0] in ["i32.const", "i64.const"]:
  726. assert(2 == len(splitted)), "{} should have two parts".format(splitted)
  727. # in wast 01234 means 1234
  728. # in c 0123 means 83 in oct
  729. number, _ = parse_simple_const_w_type(splitted[1], splitted[0][:3])
  730. args.append(str(number))
  731. elif splitted[0] in ["f32.const", "f64.const"]:
  732. # let strtof or strtod handle original arguments
  733. assert(2 == len(splitted)), "{} should have two parts".format(splitted)
  734. args.append(splitted[1])
  735. elif "v128.const" == splitted[0]:
  736. assert(len(splitted) > 2), "{} should have more than two parts".format(splitted)
  737. numbers, _ = cast_v128_to_i64x2(splitted[2:], 'v128', splitted[1])
  738. assert(len(numbers) == 2), "has to reform arguments into i64x2"
  739. args.append(f"{numbers[0]:#x}\{numbers[1]:#x}")
  740. elif "ref.null" == splitted[0]:
  741. args.append("null")
  742. elif "ref.extern" == splitted[0]:
  743. number, _ = parse_simple_const_w_type(splitted[1], splitted[0])
  744. args.append(str(number))
  745. elif "ref.host" == splitted[0]:
  746. number, _ = parse_simple_const_w_type(splitted[1], splitted[0])
  747. args.append(str(number))
  748. else:
  749. assert(0), "an unkonwn parameter type"
  750. if m.group(3) == '':
  751. returns= []
  752. else:
  753. returns = re.split("\)\s*\(", m.group(3)[1:-1])
  754. # processed numbers in strings
  755. if len(returns) == 1 and returns[0] in ["ref.array", "ref.struct", "ref.i31",
  756. "ref.eq", "ref.any", "ref.extern",
  757. "ref.func", "ref.null"]:
  758. expected = [returns[0]]
  759. elif len(returns) == 1 and returns[0] in ["func:ref.null", "any:ref.null",
  760. "extern:ref.null"]:
  761. expected = [returns[0]]
  762. else:
  763. expected = [parse_assertion_value(v)[1] for v in returns]
  764. expected = ",".join(expected)
  765. test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected)
  766. elif not m and n:
  767. module = temp_module_table[n.group(1)].split(".wasm")[0]
  768. # assume the cmd is (assert_return(invoke $ABC "func")).
  769. # run the ABC.wasm firstly
  770. if test_aot:
  771. r = compile_wasm_to_aot(module+".wasm", module+".aot", True, opts, r)
  772. try:
  773. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  774. except:
  775. _, exc, _ = sys.exc_info()
  776. log("Run wamrc failed:\n got: '%s'" % r.buf)
  777. ret_code = 1
  778. sys.exit(1)
  779. r = run_wasm_with_repl(module+".wasm", module+".aot" if test_aot else module, opts, r)
  780. # Wait for the initial prompt
  781. try:
  782. assert_prompt(r, ['webassembly> '], opts.start_timeout, False)
  783. except:
  784. _, exc, _ = sys.exc_info()
  785. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  786. (repr(exc), r.buf))
  787. func = n.group(2)
  788. if ' ' in func:
  789. func = func.replace(' ', '\\')
  790. if n.group(3) == '':
  791. args=[]
  792. else:
  793. # convert (ref.null extern/func) into (ref.null null)
  794. n1 = n.group(3).replace("(ref.null extern)", "(ref.null null)")
  795. n1 = n1.replace("ref.null func)", "(ref.null null)")
  796. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n1[1:-1])]
  797. _, expected = parse_assertion_value(n.group(4)[1:-1])
  798. test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected)
  799. def test_assert_trap(r, opts, form):
  800. # params
  801. m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
  802. # judge if assert_return cmd includes the module name
  803. n = re.search('^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
  804. if not m:
  805. # no params
  806. m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
  807. if not m:
  808. if not n:
  809. # no params
  810. n = re.search('^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
  811. if not m and not n:
  812. raise Exception("unparsed assert_trap: '%s'" % form)
  813. if m and not n:
  814. func = m.group(1)
  815. if m.group(2) == '':
  816. args = []
  817. else:
  818. # convert (ref.null extern/func) into (ref.null null)
  819. m1 = m.group(2).replace("(ref.null extern)", "(ref.null null)")
  820. m1 = m1.replace("ref.null func)", "(ref.null null)")
  821. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m1[1:-1])]
  822. expected = "Exception: %s" % m.group(3)
  823. test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
  824. elif not m and n:
  825. module = n.group(1)
  826. module = tempfile.gettempdir() + "/" + module
  827. # will trigger the module named in assert_return(invoke $ABC).
  828. # run the ABC.wasm firstly
  829. if test_aot:
  830. r = compile_wasm_to_aot(module+".wasm", module+".aot", True, opts, r)
  831. try:
  832. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  833. except:
  834. _, exc, _ = sys.exc_info()
  835. log("Run wamrc failed:\n got: '%s'" % r.buf)
  836. ret_code = 1
  837. sys.exit(1)
  838. r = run_wasm_with_repl(module+".wasm", module+".aot" if test_aot else module, opts, r)
  839. # Wait for the initial prompt
  840. try:
  841. assert_prompt(r, ['webassembly> '], opts.start_timeout, False)
  842. except:
  843. _, exc, _ = sys.exc_info()
  844. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  845. (repr(exc), r.buf))
  846. func = n.group(2)
  847. if n.group(3) == '':
  848. args = []
  849. else:
  850. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n.group(3)[1:-1])]
  851. expected = "Exception: %s" % n.group(4)
  852. test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
  853. def test_assert_exhaustion(r,opts,form):
  854. # params
  855. m = re.search('^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
  856. if not m:
  857. # no params
  858. m = re.search('^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
  859. if not m:
  860. raise Exception("unparsed assert_exhaustion: '%s'" % form)
  861. func = m.group(1)
  862. if m.group(2) == '':
  863. args = []
  864. else:
  865. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  866. expected = "Exception: %s\n" % m.group(3)
  867. test_assert(r, opts, "exhaustion", "%s %s" % (func, " ".join(args)), expected)
  868. # added to support WASM_ENABLE_EXCE_HANDLING
  869. def test_assert_wasmexception(r,opts,form):
  870. # params
  871. # ^
  872. # \(assert_exception\s+
  873. # \(invoke\s+"([^"]+)"\s+
  874. # (\(.*\))\s*
  875. # ()
  876. # \)\s*
  877. # \)\s*
  878. # $
  879. m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*\)\s*$', form)
  880. if not m:
  881. # no params
  882. # ^
  883. # \(assert_exception\s+
  884. # \(invoke\s+"([^"]+)"\s*
  885. # ()
  886. # \)\s*
  887. # \)\s*
  888. # $
  889. m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s*()\)\s*\)\s*$', form)
  890. if not m:
  891. raise Exception("unparsed assert_exception: '%s'" % form)
  892. func = m.group(1) # function name
  893. if m.group(2) == '': # arguments
  894. args = []
  895. else:
  896. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  897. expected = "Exception: uncaught wasm exception\n"
  898. test_assert(r, opts, "wasmexception", "%s %s" % (func, " ".join(args)), expected)
  899. def do_invoke(r, opts, form):
  900. # params
  901. m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form)
  902. if not m:
  903. # no params
  904. m = re.search('^\(invoke\s+"([^"]+)"\s*()\)\s*$', form)
  905. if not m:
  906. raise Exception("unparsed invoke: '%s'" % form)
  907. func = m.group(1)
  908. if ' ' in func:
  909. func = func.replace(' ', '\\')
  910. if m.group(2) == '':
  911. args = []
  912. else:
  913. args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
  914. log("Invoking %s(%s)" % (
  915. func, ", ".join([str(a) for a in args])))
  916. invoke(r, opts, "%s %s" % (func, " ".join(args)))
  917. def skip_test(form, skip_list):
  918. for s in skip_list:
  919. if re.search(s, form):
  920. return True
  921. return False
  922. def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
  923. log("Writing WAST module to '%s'" % wast_tempfile)
  924. with open(wast_tempfile, 'w') as file:
  925. file.write(form)
  926. log("Compiling WASM to '%s'" % wasm_tempfile)
  927. # default arguments
  928. if opts.gc:
  929. cmd = [opts.wast2wasm, "-u", "-d", wast_tempfile, "-o", wasm_tempfile]
  930. elif opts.eh:
  931. cmd = [opts.wast2wasm, "--enable-threads", "--no-check", "--enable-exceptions", "--enable-tail-call", wast_tempfile, "-o", wasm_tempfile ]
  932. elif opts.memory64:
  933. cmd = [opts.wast2wasm, "--enable-memory64", "--no-check", wast_tempfile, "-o", wasm_tempfile ]
  934. else:
  935. cmd = [opts.wast2wasm, "--enable-threads", "--no-check",
  936. wast_tempfile, "-o", wasm_tempfile ]
  937. # remove reference-type and bulk-memory enabling options since a WABT
  938. # commit 30c1e983d30b33a8004b39fd60cbd64477a7956c
  939. # Enable reference types by default (#1729)
  940. log("Running: %s" % " ".join(cmd))
  941. try:
  942. subprocess.check_call(cmd)
  943. except subprocess.CalledProcessError as e:
  944. print(str(e))
  945. return False
  946. return True
  947. def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r, output = 'default'):
  948. log("Compiling '%s' to '%s'" % (wasm_tempfile, aot_tempfile))
  949. cmd = [opts.aot_compiler]
  950. if test_target in aot_target_options_map:
  951. cmd += aot_target_options_map[test_target]
  952. if opts.sgx:
  953. cmd.append("-sgx")
  954. if not opts.simd:
  955. cmd.append("--disable-simd")
  956. if opts.xip:
  957. cmd.append("--enable-indirect-mode")
  958. cmd.append("--disable-llvm-intrinsics")
  959. if opts.multi_thread:
  960. cmd.append("--enable-multi-thread")
  961. if opts.gc:
  962. cmd.append("--enable-gc")
  963. cmd.append("--enable-tail-call")
  964. if output == 'object':
  965. cmd.append("--format=object")
  966. elif output == 'ir':
  967. cmd.append("--format=llvmir-opt")
  968. # disable llvm link time optimization as it might convert
  969. # code of tail call into code of dead loop, and stack overflow
  970. # exception isn't thrown in several cases
  971. cmd.append("--disable-llvm-lto")
  972. # Bounds checks is disabled by default for 64-bit targets, to
  973. # use the hardware based bounds checks. But it is not supported
  974. # in QEMU with NuttX and in memory64 mode.
  975. # Enable bounds checks explicitly for all targets if running in QEMU or all targets
  976. # running in memory64 mode.
  977. if opts.qemu or opts.memory64:
  978. cmd.append("--bounds-checks=1")
  979. # RISCV64 requires -mcmodel=medany, which can be set by --size-level=1
  980. if test_target.startswith("riscv64"):
  981. cmd.append("--size-level=1")
  982. cmd += ["-o", aot_tempfile, wasm_tempfile]
  983. log("Running: %s" % " ".join(cmd))
  984. if not runner:
  985. subprocess.check_call(cmd)
  986. else:
  987. if (r != None):
  988. r.cleanup()
  989. r = Runner(cmd, no_pty=opts.no_pty)
  990. return r
  991. def run_wasm_with_repl(wasm_tempfile, aot_tempfile, opts, r):
  992. tmpfile = aot_tempfile if test_aot else wasm_tempfile
  993. log("Starting interpreter for module '%s'" % tmpfile)
  994. cmd_iwasm = [opts.interpreter, "--heap-size=0", "-v=5" if opts.verbose else "-v=0", "--repl", tmpfile]
  995. if opts.multi_module:
  996. cmd_iwasm.insert(1, "--module-path=" + (tempfile.gettempdir() if not opts.qemu else "/tmp" ))
  997. if opts.qemu:
  998. if opts.qemu_firmware == '':
  999. raise Exception("QEMU firmware missing")
  1000. if opts.target.startswith("aarch64"):
  1001. cmd = "qemu-system-aarch64 -cpu cortex-a53 -nographic -machine virt,virtualization=on,gic-version=3 -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel".split()
  1002. cmd.append(opts.qemu_firmware)
  1003. elif opts.target.startswith("thumbv7"):
  1004. cmd = "qemu-system-arm -semihosting -M sabrelite -m 1024 -smp 1 -nographic -kernel".split()
  1005. cmd.append(opts.qemu_firmware)
  1006. elif opts.target.startswith("riscv32"):
  1007. cmd = "qemu-system-riscv32 -semihosting -M virt,aclint=on -cpu rv32 -smp 1 -nographic -bios none -kernel".split()
  1008. cmd.append(opts.qemu_firmware)
  1009. elif opts.target.startswith("riscv64"):
  1010. cmd = "qemu-system-riscv64 -semihosting -M virt,aclint=on -cpu rv64 -smp 1 -nographic -bios none -kernel".split()
  1011. cmd.append(opts.qemu_firmware)
  1012. else:
  1013. raise Exception("Unknwon target for QEMU: %s" % opts.target)
  1014. else:
  1015. cmd = cmd_iwasm
  1016. log("Running: %s" % " ".join(cmd))
  1017. if (r != None):
  1018. r.cleanup()
  1019. r = Runner(cmd, no_pty=opts.no_pty)
  1020. if opts.qemu:
  1021. r.read_to_prompt(['nsh> '], 10)
  1022. r.writeline("mount -t hostfs -o fs={} /tmp".format(tempfile.gettempdir()))
  1023. r.read_to_prompt(['nsh> '], 10)
  1024. r.writeline(" ".join(cmd_iwasm))
  1025. return r
  1026. def create_tmpfiles(wast_name):
  1027. tempfiles = []
  1028. tempfiles.append(create_tmp_file(".wast"))
  1029. tempfiles.append(create_tmp_file(".wasm"))
  1030. if test_aot:
  1031. tempfiles.append(create_tmp_file(".aot"))
  1032. # add these temp file to temporal repo, will be deleted when finishing the test
  1033. temp_file_repo.extend(tempfiles)
  1034. return tempfiles
  1035. def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile, opts, r, loadable = True):
  1036. details_inside_ast = get_module_exp_from_assert(form)
  1037. log("module is ....'%s'"%details_inside_ast[0])
  1038. log("exception is ....'%s'"%details_inside_ast[1])
  1039. # parse the module
  1040. module = details_inside_ast[0]
  1041. expected = details_inside_ast[1]
  1042. if not compile_wast_to_wasm(module, wast_tempfile, wasm_tempfile, opts):
  1043. raise Exception("compile wast to wasm failed")
  1044. if test_aot:
  1045. r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
  1046. try:
  1047. assert_prompt(r, ['Compile success'], opts.start_timeout, True)
  1048. except:
  1049. _, exc, _ = sys.exc_info()
  1050. if (r.buf.find(expected) >= 0):
  1051. log("Out exception includes expected one, pass:")
  1052. log(" Expected: %s" % expected)
  1053. log(" Got: %s" % r.buf)
  1054. return
  1055. else:
  1056. log("Run wamrc failed:\n expected: '%s'\n got: '%s'" % \
  1057. (expected, r.buf))
  1058. ret_code = 1
  1059. sys.exit(1)
  1060. r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1061. # Some module couldn't load so will raise an error directly, so shell prompt won't show here
  1062. if loadable:
  1063. # Wait for the initial prompt
  1064. try:
  1065. assert_prompt(r, ['webassembly> '], opts.start_timeout, True)
  1066. except:
  1067. _, exc, _ = sys.exc_info()
  1068. if (r.buf.find(expected) >= 0):
  1069. log("Out exception includes expected one, pass:")
  1070. log(" Expected: %s" %expected)
  1071. log(" Got: %s" % r.buf)
  1072. else:
  1073. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  1074. (expected, r.buf))
  1075. if __name__ == "__main__":
  1076. opts = parser.parse_args(sys.argv[1:])
  1077. # print('Input param :',opts)
  1078. if opts.aot: test_aot = True
  1079. # default x86_64
  1080. test_target = opts.target
  1081. if opts.rundir: os.chdir(opts.rundir)
  1082. if opts.log_file: log_file = open(opts.log_file, "a")
  1083. if opts.debug_file: debug_file = open(opts.debug_file, "a")
  1084. if opts.interpreter.endswith(".py"):
  1085. SKIP_TESTS = PY_SKIP_TESTS
  1086. else:
  1087. SKIP_TESTS = C_SKIP_TESTS
  1088. wast_tempfile = create_tmp_file(".wast")
  1089. wasm_tempfile = create_tmp_file(".wasm")
  1090. if test_aot:
  1091. aot_tempfile = create_tmp_file(".aot")
  1092. ret_code = 0
  1093. try:
  1094. log("\n################################################")
  1095. log("### Testing %s" % opts.test_file.name)
  1096. log("################################################")
  1097. forms = read_forms(opts.test_file.read())
  1098. r = None
  1099. for form in forms:
  1100. # log("\n### Current Case is " + form + "\n")
  1101. if ";;" == form[0:2]:
  1102. log(form)
  1103. elif skip_test(form, SKIP_TESTS):
  1104. log("Skipping test: %s" % form[0:60])
  1105. elif re.match("^\(assert_trap\s+\(module", form):
  1106. test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1107. elif re.match("^\(assert_exhaustion\\b.*", form):
  1108. test_assert_exhaustion(r, opts, form)
  1109. elif re.match("^\(assert_exception\\b.*", form):
  1110. test_assert_wasmexception(r, opts, form)
  1111. elif re.match("^\(assert_unlinkable\\b.*", form):
  1112. test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False)
  1113. elif re.match("^\(assert_malformed\\b.*", form):
  1114. # remove comments in wast
  1115. form,n = re.subn(";;.*\n", "", form)
  1116. m = re.match("^\(assert_malformed\s*\(module binary\s*(\".*\").*\)\s*\"(.*)\"\s*\)$", form, re.DOTALL)
  1117. if m:
  1118. # workaround: spec test changes error message to "malformed" while iwasm still use "invalid"
  1119. error_msg = m.group(2).replace("malformed", "invalid")
  1120. log("Testing(malformed)")
  1121. with open(wasm_tempfile, 'wb') as f:
  1122. s = m.group(1)
  1123. while s:
  1124. res = re.match("[^\"]*\"([^\"]*)\"(.*)", s, re.DOTALL)
  1125. if IS_PY_3:
  1126. context = res.group(1).replace("\\", "\\x").encode("latin1").decode("unicode-escape").encode("latin1")
  1127. f.write(context)
  1128. else:
  1129. f.write(res.group(1).replace("\\", "\\x").decode("string-escape"))
  1130. s = res.group(2)
  1131. # compile wasm to aot
  1132. if test_aot:
  1133. r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
  1134. try:
  1135. assert_prompt(r, ['Compile success'], opts.start_timeout, True)
  1136. except:
  1137. _, exc, _ = sys.exc_info()
  1138. if (r.buf.find(error_msg) >= 0):
  1139. log("Out exception includes expected one, pass:")
  1140. log(" Expected: %s" % error_msg)
  1141. log(" Got: %s" % r.buf)
  1142. continue
  1143. # one case in binary.wast
  1144. elif (error_msg == "unexpected end of section or function"
  1145. and r.buf.find("unexpected end")):
  1146. continue
  1147. # one case in binary.wast
  1148. elif (error_msg == "invalid value type"
  1149. and r.buf.find("unexpected end")):
  1150. continue
  1151. # one case in binary.wast
  1152. elif (error_msg == "integer too large"
  1153. and r.buf.find("tables cannot be shared")):
  1154. continue
  1155. # one case in binary.wast
  1156. elif (error_msg == "zero byte expected"
  1157. and r.buf.find("unknown table")):
  1158. continue
  1159. # one case in binary.wast
  1160. elif (error_msg == "invalid section id"
  1161. and r.buf.find("unexpected end of section or function")):
  1162. continue
  1163. # one case in binary.wast
  1164. elif (error_msg == "illegal opcode"
  1165. and r.buf.find("unexpected end of section or function")):
  1166. continue
  1167. # one case in custom.wast
  1168. elif (error_msg == "length out of bounds"
  1169. and r.buf.find("unexpected end")):
  1170. continue
  1171. # several cases in binary-leb128.wast
  1172. elif (error_msg == "integer representation too long"
  1173. and r.buf.find("invalid section id")):
  1174. continue
  1175. else:
  1176. log("Run wamrc failed:\n expected: '%s'\n got: '%s'" % \
  1177. (error_msg, r.buf))
  1178. ret_code = 1
  1179. sys.exit(1)
  1180. r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1181. elif re.match("^\(assert_malformed\s*\(module quote", form):
  1182. log("ignoring assert_malformed module quote")
  1183. else:
  1184. log("unrecognized assert_malformed")
  1185. elif re.match("^\(assert_return[_a-z]*_nan\\b.*", form):
  1186. log("ignoring assert_return_.*_nan")
  1187. pass
  1188. elif re.match(".*\(invoke\s+\$\\b.*", form):
  1189. # invoke a particular named module's function
  1190. if form.startswith("(assert_return"):
  1191. test_assert_return(r,opts,form)
  1192. elif form.startswith("(assert_trap"):
  1193. test_assert_trap(r,opts,form)
  1194. elif re.match("^\(module\\b.*", form):
  1195. # if the module includes the particular name startswith $
  1196. m = re.search("^\(module\s+\$.\S+", form)
  1197. if m:
  1198. # get module name
  1199. module_name = re.split('\$', m.group(0).strip())[1]
  1200. if module_name:
  1201. # create temporal files
  1202. temp_files = create_tmpfiles(module_name)
  1203. if not compile_wast_to_wasm(form, temp_files[0], temp_files[1], opts):
  1204. raise Exception("compile wast to wasm failed")
  1205. if test_aot:
  1206. r = compile_wasm_to_aot(temp_files[1], temp_files[2], True, opts, r)
  1207. try:
  1208. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  1209. except:
  1210. _, exc, _ = sys.exc_info()
  1211. log("Run wamrc failed:\n got: '%s'" % r.buf)
  1212. ret_code = 1
  1213. sys.exit(1)
  1214. temp_module_table[module_name] = temp_files[1]
  1215. r = run_wasm_with_repl(temp_files[1], temp_files[2] if test_aot else None, opts, r)
  1216. else:
  1217. if not compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
  1218. raise Exception("compile wast to wasm failed")
  1219. if test_aot:
  1220. r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
  1221. try:
  1222. assert_prompt(r, ['Compile success'], opts.start_timeout, False)
  1223. except:
  1224. _, exc, _ = sys.exc_info()
  1225. log("Run wamrc failed:\n got: '%s'" % r.buf)
  1226. ret_code = 1
  1227. sys.exit(1)
  1228. r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1229. # Wait for the initial prompt
  1230. try:
  1231. assert_prompt(r, ['webassembly> '], opts.start_timeout, False)
  1232. except:
  1233. _, exc, _ = sys.exc_info()
  1234. raise Exception("Failed:\n expected: '%s'\n got: '%s'" % \
  1235. (repr(exc), r.buf))
  1236. elif re.match("^\(assert_return\\b.*", form):
  1237. assert(r), "iwasm repl runtime should be not null"
  1238. test_assert_return(r, opts, form)
  1239. elif re.match("^\(assert_trap\\b.*", form):
  1240. test_assert_trap(r, opts, form)
  1241. elif re.match("^\(invoke\\b.*", form):
  1242. assert(r), "iwasm repl runtime should be not null"
  1243. do_invoke(r, opts, form)
  1244. elif re.match("^\(assert_invalid\\b.*", form):
  1245. test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
  1246. elif re.match("^\(register\\b.*", form):
  1247. # get module's new name from the register cmd
  1248. name_new =re.split('\"',re.search('\".*\"',form).group(0))[1]
  1249. if name_new:
  1250. new_module = os.path.join(tempfile.gettempdir(), name_new + ".wasm")
  1251. shutil.copyfile(temp_module_table.get(name_new, wasm_tempfile), new_module)
  1252. # add new_module copied from the old into temp_file_repo[]
  1253. temp_file_repo.append(new_module)
  1254. if test_aot:
  1255. new_module_aot = os.path.join(tempfile.gettempdir(), name_new + ".aot")
  1256. r = compile_wasm_to_aot(new_module, new_module_aot, True, opts, r)
  1257. try:
  1258. assert_prompt(r, ['Compile success'], opts.start_timeout, True)
  1259. except:
  1260. raise Exception("compile wasm to aot failed")
  1261. # add aot module into temp_file_repo[]
  1262. temp_file_repo.append(new_module_aot)
  1263. else:
  1264. # there is no name defined in register cmd
  1265. raise Exception("can not find module name from the register")
  1266. else:
  1267. raise Exception("unrecognized form '%s...'" % form[0:40])
  1268. except Exception as e:
  1269. traceback.print_exc()
  1270. print("THE FINAL EXCEPTION IS {}".format(e))
  1271. ret_code = 101
  1272. shutil.copyfile(wasm_tempfile, os.path.join(opts.log_dir, os.path.basename(wasm_tempfile)))
  1273. if opts.aot or opts.xip:
  1274. shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)))
  1275. if "indirect-mode" in str(e):
  1276. compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "object")
  1277. shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+'.o'))
  1278. subprocess.check_call(["llvm-objdump", "-r", aot_tempfile])
  1279. compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "ir")
  1280. shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+".ir"))
  1281. else:
  1282. ret_code = 0
  1283. finally:
  1284. if not opts.no_cleanup:
  1285. log("Removing tempfiles")
  1286. os.remove(wast_tempfile)
  1287. os.remove(wasm_tempfile)
  1288. if test_aot:
  1289. os.remove(aot_tempfile)
  1290. # remove the files under /tempfiles/ and copy of .wasm files
  1291. if temp_file_repo:
  1292. for t in temp_file_repo:
  1293. if(len(str(t))!=0 and os.path.exists(t)):
  1294. os.remove(t)
  1295. log("### End testing %s" % opts.test_file.name)
  1296. else:
  1297. log("Leaving tempfiles: %s" % ([wast_tempfile, wasm_tempfile]))
  1298. sys.exit(ret_code)