gen-inlining-tests.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. #!/usr/bin/env python
  2. # Copyright 2016 the V8 project authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. # for py2/py3 compatibility
  6. from __future__ import print_function
  7. from collections import namedtuple
  8. import textwrap
  9. import sys
  10. SHARD_FILENAME_TEMPLATE = "test/mjsunit/compiler/inline-exception-{shard}.js"
  11. # Generates 2 files. Found by trial and error.
  12. SHARD_SIZE = 97
  13. PREAMBLE = """
  14. // Copyright 2016 the V8 project authors. All rights reserved.
  15. // Use of this source code is governed by a BSD-style license that can be
  16. // found in the LICENSE file.
  17. // Flags: --allow-natives-syntax --no-always-opt
  18. // This test file was generated by tools/gen-inlining-tests.py .
  19. // Global variables
  20. var deopt = undefined; // either true or false
  21. var counter = 0;
  22. function resetState() {
  23. counter = 0;
  24. }
  25. function warmUp(f) {
  26. try {
  27. f();
  28. } catch (ex) {
  29. // ok
  30. }
  31. try {
  32. f();
  33. } catch (ex) {
  34. // ok
  35. }
  36. }
  37. function resetOptAndAssertResultEquals(expected, f) {
  38. warmUp(f);
  39. resetState();
  40. // %DebugPrint(f);
  41. eval("'dont optimize this function itself please, but do optimize f'");
  42. %OptimizeFunctionOnNextCall(f);
  43. assertEquals(expected, f());
  44. }
  45. function resetOptAndAssertThrowsWith(expected, f) {
  46. warmUp(f);
  47. resetState();
  48. // %DebugPrint(f);
  49. eval("'dont optimize this function itself please, but do optimize f'");
  50. %OptimizeFunctionOnNextCall(f);
  51. try {
  52. var result = f();
  53. fail("resetOptAndAssertThrowsWith",
  54. "exception: " + expected,
  55. "result: " + result);
  56. } catch (ex) {
  57. assertEquals(expected, ex);
  58. }
  59. }
  60. function increaseAndReturn15() {
  61. if (deopt) %DeoptimizeFunction(f);
  62. counter++;
  63. return 15;
  64. }
  65. function increaseAndThrow42() {
  66. if (deopt) %DeoptimizeFunction(f);
  67. counter++;
  68. throw 42;
  69. }
  70. function increaseAndReturn15_noopt_inner() {
  71. if (deopt) %DeoptimizeFunction(f);
  72. counter++;
  73. return 15;
  74. }
  75. %NeverOptimizeFunction(increaseAndReturn15_noopt_inner);
  76. function increaseAndThrow42_noopt_inner() {
  77. if (deopt) %DeoptimizeFunction(f);
  78. counter++;
  79. throw 42;
  80. }
  81. %NeverOptimizeFunction(increaseAndThrow42_noopt_inner);
  82. // Alternative 1
  83. function returnOrThrow(doReturn) {
  84. if (doReturn) {
  85. return increaseAndReturn15();
  86. } else {
  87. return increaseAndThrow42();
  88. }
  89. }
  90. // Alternative 2
  91. function increaseAndReturn15_calls_noopt() {
  92. return increaseAndReturn15_noopt_inner();
  93. }
  94. function increaseAndThrow42_calls_noopt() {
  95. return increaseAndThrow42_noopt_inner();
  96. }
  97. // Alternative 3.
  98. // When passed either {increaseAndReturn15} or {increaseAndThrow42}, it acts
  99. // as the other one.
  100. function invertFunctionCall(f) {
  101. var result;
  102. try {
  103. result = f();
  104. } catch (ex) {
  105. return ex - 27;
  106. }
  107. throw result + 27;
  108. }
  109. // Alternative 4: constructor
  110. function increaseAndStore15Constructor() {
  111. if (deopt) %DeoptimizeFunction(f);
  112. ++counter;
  113. this.x = 15;
  114. }
  115. function increaseAndThrow42Constructor() {
  116. if (deopt) %DeoptimizeFunction(f);
  117. ++counter;
  118. this.x = 42;
  119. throw this.x;
  120. }
  121. // Alternative 5: property
  122. var magic = {};
  123. Object.defineProperty(magic, 'prop', {
  124. get: function () {
  125. if (deopt) %DeoptimizeFunction(f);
  126. return 15 + 0 * ++counter;
  127. },
  128. set: function(x) {
  129. // argument should be 37
  130. if (deopt) %DeoptimizeFunction(f);
  131. counter -= 36 - x; // increments counter
  132. throw 42;
  133. }
  134. })
  135. // Generate type feedback.
  136. assertEquals(15, increaseAndReturn15_calls_noopt());
  137. assertThrowsEquals(function() { return increaseAndThrow42_noopt_inner() }, 42);
  138. assertEquals(15, (new increaseAndStore15Constructor()).x);
  139. assertThrowsEquals(function() {
  140. return (new increaseAndThrow42Constructor()).x;
  141. },
  142. 42);
  143. function runThisShard() {
  144. """.strip()
  145. def booltuples(n):
  146. """booltuples(2) yields 4 tuples: (False, False), (False, True),
  147. (True, False), (True, True)."""
  148. assert isinstance(n, int)
  149. if n <= 0:
  150. yield ()
  151. else:
  152. for initial in booltuples(n-1):
  153. yield initial + (False,)
  154. yield initial + (True,)
  155. def fnname(flags):
  156. assert len(FLAGLETTERS) == len(flags)
  157. return "f_" + ''.join(
  158. FLAGLETTERS[i] if b else '_'
  159. for (i, b) in enumerate(flags))
  160. NUM_TESTS_PRINTED = 0
  161. NUM_TESTS_IN_SHARD = 0
  162. def printtest(flags):
  163. """Print a test case. Takes a couple of boolean flags, on which the
  164. printed Javascript code depends."""
  165. assert all(isinstance(flag, bool) for flag in flags)
  166. # The alternative flags are in reverse order so that if we take all possible
  167. # tuples, ordered lexicographically from false to true, we get first the
  168. # default, then alternative 1, then 2, etc.
  169. (
  170. alternativeFn5, # use alternative #5 for returning/throwing:
  171. # return/throw using property
  172. alternativeFn4, # use alternative #4 for returning/throwing:
  173. # return/throw using constructor
  174. alternativeFn3, # use alternative #3 for returning/throwing:
  175. # return/throw indirectly, based on function argument
  176. alternativeFn2, # use alternative #2 for returning/throwing:
  177. # return/throw indirectly in unoptimized code,
  178. # no branching
  179. alternativeFn1, # use alternative #1 for returning/throwing:
  180. # return/throw indirectly, based on boolean arg
  181. tryThrows, # in try block, call throwing function
  182. tryReturns, # in try block, call returning function
  183. tryFirstReturns, # in try block, returning goes before throwing
  184. tryResultToLocal, # in try block, result goes to local variable
  185. doCatch, # include catch block
  186. catchReturns, # in catch block, return
  187. catchWithLocal, # in catch block, modify or return the local variable
  188. catchThrows, # in catch block, throw
  189. doFinally, # include finally block
  190. finallyReturns, # in finally block, return local variable
  191. finallyThrows, # in finally block, throw
  192. endReturnLocal, # at very end, return variable local
  193. deopt, # deopt inside inlined function
  194. ) = flags
  195. # BASIC RULES
  196. # Only one alternative can be applied at any time.
  197. if (alternativeFn1 + alternativeFn2 + alternativeFn3 + alternativeFn4
  198. + alternativeFn5 > 1):
  199. return
  200. # In try, return or throw, or both.
  201. if not (tryReturns or tryThrows): return
  202. # Either doCatch or doFinally.
  203. if not doCatch and not doFinally: return
  204. # Catch flags only make sense when catching
  205. if not doCatch and (catchReturns or catchWithLocal or catchThrows):
  206. return
  207. # Finally flags only make sense when finallying
  208. if not doFinally and (finallyReturns or finallyThrows):
  209. return
  210. # tryFirstReturns is only relevant when both tryReturns and tryThrows are
  211. # true.
  212. if tryFirstReturns and not (tryReturns and tryThrows): return
  213. # From the try and finally block, we can return or throw, but not both.
  214. if catchReturns and catchThrows: return
  215. if finallyReturns and finallyThrows: return
  216. # If at the end we return the local, we need to have touched it.
  217. if endReturnLocal and not (tryResultToLocal or catchWithLocal): return
  218. # PRUNING
  219. anyAlternative = any([alternativeFn1, alternativeFn2, alternativeFn3,
  220. alternativeFn4, alternativeFn5])
  221. specificAlternative = any([alternativeFn2, alternativeFn3])
  222. rareAlternative = not specificAlternative
  223. # If try returns and throws, then don't catchWithLocal, endReturnLocal, or
  224. # deopt, or do any alternative.
  225. if (tryReturns and tryThrows and
  226. (catchWithLocal or endReturnLocal or deopt or anyAlternative)):
  227. return
  228. # We don't do any alternative if we do a finally.
  229. if doFinally and anyAlternative: return
  230. # We only use the local variable if we do alternative #2 or #3.
  231. if ((tryResultToLocal or catchWithLocal or endReturnLocal) and
  232. not specificAlternative):
  233. return
  234. # We don't need to test deopting into a finally.
  235. if doFinally and deopt: return
  236. # We're only interested in alternative #2 if we have endReturnLocal, no
  237. # catchReturns, and no catchThrows, and deopt.
  238. if (alternativeFn2 and
  239. (not endReturnLocal or catchReturns or catchThrows or not deopt)):
  240. return
  241. # Flag check succeeded.
  242. trueFlagNames = [name for (name, value) in flags._asdict().items() if value]
  243. flagsMsgLine = " // Variant flags: [{}]".format(', '.join(trueFlagNames))
  244. write(textwrap.fill(flagsMsgLine, subsequent_indent=' // '))
  245. write("")
  246. if not anyAlternative:
  247. fragments = {
  248. 'increaseAndReturn15': 'increaseAndReturn15()',
  249. 'increaseAndThrow42': 'increaseAndThrow42()',
  250. }
  251. elif alternativeFn1:
  252. fragments = {
  253. 'increaseAndReturn15': 'returnOrThrow(true)',
  254. 'increaseAndThrow42': 'returnOrThrow(false)',
  255. }
  256. elif alternativeFn2:
  257. fragments = {
  258. 'increaseAndReturn15': 'increaseAndReturn15_calls_noopt()',
  259. 'increaseAndThrow42': 'increaseAndThrow42_calls_noopt()',
  260. }
  261. elif alternativeFn3:
  262. fragments = {
  263. 'increaseAndReturn15': 'invertFunctionCall(increaseAndThrow42)',
  264. 'increaseAndThrow42': 'invertFunctionCall(increaseAndReturn15)',
  265. }
  266. elif alternativeFn4:
  267. fragments = {
  268. 'increaseAndReturn15': '(new increaseAndStore15Constructor()).x',
  269. 'increaseAndThrow42': '(new increaseAndThrow42Constructor()).x',
  270. }
  271. else:
  272. assert alternativeFn5
  273. fragments = {
  274. 'increaseAndReturn15': 'magic.prop /* returns 15 */',
  275. 'increaseAndThrow42': '(magic.prop = 37 /* throws 42 */)',
  276. }
  277. # As we print code, we also maintain what the result should be. Variable
  278. # {result} can be one of three things:
  279. #
  280. # - None, indicating returning JS null
  281. # - ("return", n) with n an integer
  282. # - ("throw", n), with n an integer
  283. result = None
  284. # We also maintain what the counter should be at the end.
  285. # The counter is reset just before f is called.
  286. counter = 0
  287. write( " f = function {} () {{".format(fnname(flags)))
  288. write( " var local = 888;")
  289. write( " deopt = {};".format("true" if deopt else "false"))
  290. local = 888
  291. write( " try {")
  292. write( " counter++;")
  293. counter += 1
  294. resultTo = "local +=" if tryResultToLocal else "return"
  295. if tryReturns and not (tryThrows and not tryFirstReturns):
  296. write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments))
  297. if result == None:
  298. counter += 1
  299. if tryResultToLocal:
  300. local += 19
  301. else:
  302. result = ("return", 19)
  303. if tryThrows:
  304. write( " {} 4 + {increaseAndThrow42};".format(resultTo, **fragments))
  305. if result == None:
  306. counter += 1
  307. result = ("throw", 42)
  308. if tryReturns and tryThrows and not tryFirstReturns:
  309. write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments))
  310. if result == None:
  311. counter += 1
  312. if tryResultToLocal:
  313. local += 19
  314. else:
  315. result = ("return", 19)
  316. write( " counter++;")
  317. if result == None:
  318. counter += 1
  319. if doCatch:
  320. write( " } catch (ex) {")
  321. write( " counter++;")
  322. if isinstance(result, tuple) and result[0] == 'throw':
  323. counter += 1
  324. if catchThrows:
  325. write(" throw 2 + ex;")
  326. if isinstance(result, tuple) and result[0] == "throw":
  327. result = ('throw', 2 + result[1])
  328. elif catchReturns and catchWithLocal:
  329. write(" return 2 + local;")
  330. if isinstance(result, tuple) and result[0] == "throw":
  331. result = ('return', 2 + local)
  332. elif catchReturns and not catchWithLocal:
  333. write(" return 2 + ex;");
  334. if isinstance(result, tuple) and result[0] == "throw":
  335. result = ('return', 2 + result[1])
  336. elif catchWithLocal:
  337. write(" local += ex;");
  338. if isinstance(result, tuple) and result[0] == "throw":
  339. local += result[1]
  340. result = None
  341. counter += 1
  342. else:
  343. if isinstance(result, tuple) and result[0] == "throw":
  344. result = None
  345. counter += 1
  346. write( " counter++;")
  347. if doFinally:
  348. write( " } finally {")
  349. write( " counter++;")
  350. counter += 1
  351. if finallyThrows:
  352. write(" throw 25;")
  353. result = ('throw', 25)
  354. elif finallyReturns:
  355. write(" return 3 + local;")
  356. result = ('return', 3 + local)
  357. elif not finallyReturns and not finallyThrows:
  358. write(" local += 2;")
  359. local += 2
  360. counter += 1
  361. else: assert False # unreachable
  362. write( " counter++;")
  363. write( " }")
  364. write( " counter++;")
  365. if result == None:
  366. counter += 1
  367. if endReturnLocal:
  368. write( " return 5 + local;")
  369. if result == None:
  370. result = ('return', 5 + local)
  371. write( " }")
  372. if result == None:
  373. write( " resetOptAndAssertResultEquals(undefined, f);")
  374. else:
  375. tag, value = result
  376. if tag == "return":
  377. write( " resetOptAndAssertResultEquals({}, f);".format(value))
  378. else:
  379. assert tag == "throw"
  380. write( " resetOptAndAssertThrowsWith({}, f);".format(value))
  381. write( " assertEquals({}, counter);".format(counter))
  382. write( "")
  383. global NUM_TESTS_PRINTED, NUM_TESTS_IN_SHARD
  384. NUM_TESTS_PRINTED += 1
  385. NUM_TESTS_IN_SHARD += 1
  386. FILE = None # to be initialised to an open file
  387. SHARD_NUM = 1
  388. def write(*args):
  389. return print(*args, file=FILE)
  390. def rotateshard():
  391. global FILE, NUM_TESTS_IN_SHARD, SHARD_SIZE
  392. if MODE != 'shard':
  393. return
  394. if FILE != None and NUM_TESTS_IN_SHARD < SHARD_SIZE:
  395. return
  396. if FILE != None:
  397. finishshard()
  398. assert FILE == None
  399. FILE = open(SHARD_FILENAME_TEMPLATE.format(shard=SHARD_NUM), 'w')
  400. write_shard_header()
  401. NUM_TESTS_IN_SHARD = 0
  402. def finishshard():
  403. global FILE, SHARD_NUM, MODE
  404. assert FILE
  405. write_shard_footer()
  406. if MODE == 'shard':
  407. print("Wrote shard {}.".format(SHARD_NUM))
  408. FILE.close()
  409. FILE = None
  410. SHARD_NUM += 1
  411. def write_shard_header():
  412. if MODE == 'shard':
  413. write("// Shard {}.".format(SHARD_NUM))
  414. write("")
  415. write(PREAMBLE)
  416. write("")
  417. def write_shard_footer():
  418. write("}")
  419. write("%NeverOptimizeFunction(runThisShard);")
  420. write("")
  421. write("// {} tests in this shard.".format(NUM_TESTS_IN_SHARD))
  422. write("// {} tests up to here.".format(NUM_TESTS_PRINTED))
  423. write("")
  424. write("runThisShard();")
  425. FLAGLETTERS="54321trflcrltfrtld"
  426. flagtuple = namedtuple('flagtuple', (
  427. "alternativeFn5",
  428. "alternativeFn4",
  429. "alternativeFn3",
  430. "alternativeFn2",
  431. "alternativeFn1",
  432. "tryThrows",
  433. "tryReturns",
  434. "tryFirstReturns",
  435. "tryResultToLocal",
  436. "doCatch",
  437. "catchReturns",
  438. "catchWithLocal",
  439. "catchThrows",
  440. "doFinally",
  441. "finallyReturns",
  442. "finallyThrows",
  443. "endReturnLocal",
  444. "deopt"
  445. ))
  446. emptyflags = flagtuple(*((False,) * len(flagtuple._fields)))
  447. f1 = emptyflags._replace(tryReturns=True, doCatch=True)
  448. # You can test function printtest with f1.
  449. allFlagCombinations = [
  450. flagtuple(*bools)
  451. for bools in booltuples(len(flagtuple._fields))
  452. ]
  453. if __name__ == '__main__':
  454. global MODE
  455. if sys.argv[1:] == []:
  456. MODE = 'stdout'
  457. print("// Printing all shards together to stdout.")
  458. print("")
  459. write_shard_header()
  460. FILE = sys.stdout
  461. elif sys.argv[1:] == ['--shard-and-overwrite']:
  462. MODE = 'shard'
  463. else:
  464. print("Usage:")
  465. print("")
  466. print(" python {}".format(sys.argv[0]))
  467. print(" print all tests to standard output")
  468. print(" python {} --shard-and-overwrite".format(sys.argv[0]))
  469. print(" print all tests to {}".format(SHARD_FILENAME_TEMPLATE))
  470. print("")
  471. print(sys.argv[1:])
  472. print("")
  473. sys.exit(1)
  474. rotateshard()
  475. for flags in allFlagCombinations:
  476. printtest(flags)
  477. rotateshard()
  478. finishshard()
  479. if MODE == 'shard':
  480. print("Total: {} tests.".format(NUM_TESTS_PRINTED))