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