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