Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(615)

Unified Diff: tools/gen-inlining-tests.py

Issue 2245263003: [turbofan] Tests for inlining calls, constructors, property access inside try..catch..finally. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@p6-base
Patch Set: Wrap long lines. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « test/mjsunit/compiler/inline-exception-2.js ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gen-inlining-tests.py
diff --git a/tools/gen-inlining-tests.py b/tools/gen-inlining-tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..179b4ad3255415888062778c5929c8b949231dab
--- /dev/null
+++ b/tools/gen-inlining-tests.py
@@ -0,0 +1,510 @@
+#!/usr/bin/env python3
+
+# Copyright 2016 the V8 project authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+from collections import namedtuple
+import textwrap
+import sys
+
+SHARD_FILENAME_TEMPLATE = "test/mjsunit/compiler/inline-exception-{shard}.js"
+# Generates 2 files. Found by trial and error.
+SHARD_SIZE = 94
+
+PREAMBLE = """
+
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --turbo --no-always-opt
+
+// This test file was generated by tools/gen-inlining-tests.py .
+
+// Global variables
+var deopt = undefined; // either true or false
+var counter = 0;
+
+function resetState() {
+ counter = 0;
+}
+
+function warmUp(f) {
+ try {
+ f();
+ } catch (ex) {
+ // ok
+ }
+ try {
+ f();
+ } catch (ex) {
+ // ok
+ }
+}
+
+function resetOptAndAssertResultEquals(expected, f) {
+ warmUp(f);
+ resetState();
+ // %DebugPrint(f);
+ eval("'dont optimize this function itself please, but do optimize f'");
+ %OptimizeFunctionOnNextCall(f);
+ assertEquals(expected, f());
+}
+
+function resetOptAndAssertThrowsWith(expected, f) {
+ warmUp(f);
+ resetState();
+ // %DebugPrint(f);
+ eval("'dont optimize this function itself please, but do optimize f'");
+ %OptimizeFunctionOnNextCall(f);
+ try {
+ var result = f();
+ fail("resetOptAndAssertThrowsWith",
+ "exception: " + expected,
+ "result: " + result);
+ } catch (ex) {
+ assertEquals(expected, ex);
+ }
+}
+
+function increaseAndReturn15() {
+ if (deopt) %DeoptimizeFunction(f);
+ counter++;
+ return 15;
+}
+
+function increaseAndThrow42() {
+ if (deopt) %DeoptimizeFunction(f);
+ counter++;
+ throw 42;
+}
+
+function returnOrThrow(doReturn) {
+ if (doReturn) {
+ return increaseAndReturn15();
+ } else {
+ return increaseAndThrow42();
+ }
+}
+
+// When passed either {increaseAndReturn15} or {increaseAndThrow42}, it acts
+// as the other one.
+function invertFunctionCall(f) {
+ var result;
+ try {
+ result = f();
+ } catch (ex) {
+ return ex - 27;
+ }
+ throw result + 27;
+}
+
+function increaseAndStore15Constructor() {
+ if (deopt) %DeoptimizeFunction(f);
+ ++counter;
+ this.x = 15;
+}
+
+function increaseAndThrow42Constructor() {
+ if (deopt) %DeoptimizeFunction(f);
+ ++counter;
+ this.x = 42;
+ throw this.x;
+}
+
+var magic = {};
+Object.defineProperty(magic, 'prop', {
+ get: function () {
+ if (deopt) %DeoptimizeFunction(f);
+ return 15 + 0 * ++counter;
+ },
+
+ set: function(x) {
+ // argument should be 37
+ if (deopt) %DeoptimizeFunction(f);
+ counter -= 36 - x; // increments counter
+ throw 42;
+ }
+})
+
+// Generate type feedback.
+
+assertEquals(15, (new increaseAndStore15Constructor()).x);
+assertThrowsEquals(function() {
+ return (new increaseAndThrow42Constructor()).x;
+ },
+ 42);
+
+function runThisShard() {
+
+""".strip()
+
+def booltuples(n):
+ """booltuples(2) yields 4 tuples: (False, False), (False, True),
+ (True, False), (True, True)."""
+
+ assert isinstance(n, int)
+ if n <= 0:
+ yield ()
+ else:
+ for initial in booltuples(n-1):
+ yield initial + (False,)
+ yield initial + (True,)
+
+FLAGLETTERS="4321trflcrltfrtld"
+
+def fnname(flags):
+ assert len(FLAGLETTERS) == len(flags)
+
+ return "f_" + ''.join(
+ FLAGLETTERS[i] if b else '_'
+ for (i, b) in enumerate(flags))
+
+NUM_TESTS_PRINTED = 0
+NUM_TESTS_IN_SHARD = 0
+
+def printtest(flags):
+ """Print a test case. Takes a couple of boolean flags, on which the
+ printed Javascript code depends."""
+
+ assert all(isinstance(flag, bool) for flag in flags)
+
+ # The alternative flags are in reverse order so that if we take all possible
+ # tuples, ordered lexicographically from false to true, we get first the
+ # default, then alternative 1, then 2, etc.
+ (
+ alternativeFn4, # use alternative #4 for returning/throwing.
+ alternativeFn3, # use alternative #3 for returning/throwing.
+ alternativeFn2, # use alternative #2 for returning/throwing.
+ alternativeFn1, # use alternative #1 for returning/throwing.
+ tryThrows, # in try block, call throwing function
+ tryReturns, # in try block, call returning function
+ tryFirstReturns, # in try block, returning goes before throwing
+ tryResultToLocal, # in try block, result goes to local variable
+ doCatch, # include catch block
+ catchReturns, # in catch block, return
+ catchWithLocal, # in catch block, modify or return the local variable
+ catchThrows, # in catch block, throw
+ doFinally, # include finally block
+ finallyReturns, # in finally block, return local variable
+ finallyThrows, # in finally block, throw
+ endReturnLocal, # at very end, return variable local
+ deopt, # deopt inside inlined function
+ ) = flags
+
+ # BASIC RULES
+
+ # Only one alternative can be applied at any time.
+ if alternativeFn1 + alternativeFn2 + alternativeFn3 + alternativeFn4 > 1:
+ return
+
+ # In try, return or throw, or both.
+ if not (tryReturns or tryThrows): return
+
+ # Either doCatch or doFinally.
+ if not doCatch and not doFinally: return
+
+ # Catch flags only make sense when catching
+ if not doCatch and (catchReturns or catchWithLocal or catchThrows):
+ return
+
+ # Finally flags only make sense when finallying
+ if not doFinally and (finallyReturns or finallyThrows):
+ return
+
+ # tryFirstReturns is only relevant when both tryReturns and tryThrows are
+ # true.
+ if tryFirstReturns and not (tryReturns and tryThrows): return
+
+ # From the try and finally block, we can return or throw, but not both.
+ if catchReturns and catchThrows: return
+ if finallyReturns and finallyThrows: return
+
+ # If at the end we return the local, we need to have touched it.
+ if endReturnLocal and not (tryResultToLocal or catchWithLocal): return
+
+ # PRUNING
+
+ anyAlternative = any([alternativeFn1, alternativeFn2, alternativeFn3,
+ alternativeFn4])
+ rareAlternative = any([alternativeFn1, alternativeFn3, alternativeFn4])
+
+ # If try returns and throws, then don't catchWithLocal, endReturnLocal, or
+ # deopt, or do any alternative.
+ if (tryReturns and tryThrows and
+ (catchWithLocal or endReturnLocal or deopt or anyAlternative)):
+ return
+ # We don't do any alternative if we do a finally.
+ if doFinally and anyAlternative: return
+ # We only use the local variable if we do alternative #2.
+ if ((tryResultToLocal or catchWithLocal or endReturnLocal) and
+ not alternativeFn2):
+ return
+ # We don't need to test deopting into a finally.
+ if doFinally and deopt: return
+
+
+
+ # Flag check succeeded.
+
+ trueFlagNames = [name for (name, value) in flags._asdict().items() if value]
+ flagsMsgLine = " // Variant flags: [{}]".format(', '.join(trueFlagNames))
+ write(textwrap.fill(flagsMsgLine, subsequent_indent=' // '))
+ write("")
+
+ if not anyAlternative:
+ fragments = {
+ 'increaseAndReturn15': 'increaseAndReturn15()',
+ 'increaseAndThrow42': 'increaseAndThrow42()',
+ }
+ elif alternativeFn1:
+ fragments = {
+ 'increaseAndReturn15': 'returnOrThrow(true)',
+ 'increaseAndThrow42': 'returnOrThrow(false)',
+ }
+ elif alternativeFn2:
+ fragments = {
+ 'increaseAndReturn15': 'invertFunctionCall(increaseAndThrow42)',
+ 'increaseAndThrow42': 'invertFunctionCall(increaseAndReturn15)',
+ }
+ elif alternativeFn3:
+ fragments = {
+ 'increaseAndReturn15': '(new increaseAndStore15Constructor()).x',
+ 'increaseAndThrow42': '(new increaseAndThrow42Constructor()).x',
+ }
+ else:
+ assert alternativeFn4
+ fragments = {
+ 'increaseAndReturn15': 'magic.prop /* returns 15 */',
+ 'increaseAndThrow42': '(magic.prop = 37 /* throws 42 */)',
+ }
+
+ # As we print code, we also maintain what the result should be. Variable
+ # {result} can be one of three things:
+ #
+ # - None, indicating returning JS null
+ # - ("return", n) with n an integer
+ # - ("throw", n), with n an integer
+
+ result = None
+ # We also maintain what the counter should be at the end.
+ # The counter is reset just before f is called.
+ counter = 0
+
+ write( " f = function {} () {{".format(fnname(flags)))
+ write( " var local = 3;")
+ write( " deopt = {};".format("true" if deopt else "false"))
+ local = 3
+ write( " try {")
+ write( " counter++;")
+ counter += 1
+ resultTo = "local +=" if tryResultToLocal else "return"
+ if tryReturns and not (tryThrows and not tryFirstReturns):
+ write( " {} {increaseAndReturn15};".format(resultTo, **fragments))
+ if result == None:
+ counter += 1
+ if tryResultToLocal:
+ local += 15
+ else:
+ result = ("return", 15)
+ if tryThrows:
+ write( " {} {increaseAndThrow42};".format(resultTo, **fragments))
+ if result == None:
+ counter += 1
+ result = ("throw", 42)
+ if tryReturns and tryThrows and not tryFirstReturns:
+ write( " {} {increaseAndReturn15};".format(resultTo, **fragments))
+ if result == None:
+ counter += 1
+ if tryResultToLocal:
+ local += 15
+ else:
+ result = ("return", 15)
+ write( " counter++;")
+ if result == None:
+ counter += 1
+
+ if doCatch:
+ write( " } catch (ex) {")
+ write( " counter++;")
+ if isinstance(result, tuple) and result[0] == 'throw':
+ counter += 1
+ if catchThrows:
+ write(" throw 2 + ex;")
+ if isinstance(result, tuple) and result[0] == "throw":
+ result = ('throw', 2 + result[1])
+ elif catchReturns and catchWithLocal:
+ write(" return 2 + local;")
+ if isinstance(result, tuple) and result[0] == "throw":
+ result = ('return', 2 + local)
+ elif catchReturns and not catchWithLocal:
+ write(" return 2 + ex;");
+ if isinstance(result, tuple) and result[0] == "throw":
+ result = ('return', 2 + result[1])
+ elif catchWithLocal:
+ write(" local += ex;");
+ if isinstance(result, tuple) and result[0] == "throw":
+ local += result[1]
+ result = None
+ counter += 1
+ else:
+ if isinstance(result, tuple) and result[0] == "throw":
+ result = None
+ counter += 1
+ write( " counter++;")
+
+ if doFinally:
+ write( " } finally {")
+ write( " counter++;")
+ counter += 1
+ if finallyThrows:
+ write(" throw 25;")
+ result = ('throw', 25)
+ elif finallyReturns:
+ write(" return 3 + local;")
+ result = ('return', 3 + local)
+ elif not finallyReturns and not finallyThrows:
+ write(" local += 2;")
+ local += 2
+ counter += 1
+ else: assert False # unreachable
+ write( " counter++;")
+
+ write( " }")
+ write( " counter++;")
+ if result == None:
+ counter += 1
+ if endReturnLocal:
+ write( " return 5 + local;")
+ if result == None:
+ result = ('return', 5 + local)
+ write( " }")
+
+ if result == None:
+ write( " resetOptAndAssertResultEquals(undefined, f);")
+ else:
+ tag, value = result
+ if tag == "return":
+ write( " resetOptAndAssertResultEquals({}, f);".format(value))
+ else:
+ assert tag == "throw"
+ write( " resetOptAndAssertThrowsWith({}, f);".format(value))
+
+ write( " assertEquals({}, counter);".format(counter))
+ write( "")
+
+ global NUM_TESTS_PRINTED, NUM_TESTS_IN_SHARD
+ NUM_TESTS_PRINTED += 1
+ NUM_TESTS_IN_SHARD += 1
+
+FILE = None # to be initialised to an open file
+SHARD_NUM = 1
+
+def write(*args):
+ return print(*args, file=FILE)
+
+
+
+def rotateshard():
+ global FILE, NUM_TESTS_IN_SHARD, SHARD_SIZE
+ if MODE != 'shard':
+ return
+ if FILE != None and NUM_TESTS_IN_SHARD < SHARD_SIZE:
+ return
+ if FILE != None:
+ finishshard()
+ assert FILE == None
+ FILE = open(SHARD_FILENAME_TEMPLATE.format(shard=SHARD_NUM), 'w')
+ write_shard_header()
+ NUM_TESTS_IN_SHARD = 0
+
+def finishshard():
+ global FILE, SHARD_NUM, MODE
+ assert FILE
+ write_shard_footer()
+ if MODE == 'shard':
+ print("Wrote shard {}.".format(SHARD_NUM))
+ FILE.close()
+ FILE = None
+ SHARD_NUM += 1
+
+
+def write_shard_header():
+ if MODE == 'shard':
+ write("// Shard {}.".format(SHARD_NUM))
+ write("")
+ write(PREAMBLE)
+ write("")
+
+def write_shard_footer():
+ write("}")
+ write("%NeverOptimizeFunction(runThisShard);")
+ write("")
+ write("// {} tests in this shard.".format(NUM_TESTS_IN_SHARD))
+ write("// {} tests up to here.".format(NUM_TESTS_PRINTED))
+ write("")
+ write("runThisShard();")
+
+
+flagtuple = namedtuple('flagtuple', (
+ "alternativeFn4",
+ "alternativeFn3",
+ "alternativeFn2",
+ "alternativeFn1",
+ "tryThrows",
+ "tryReturns",
+ "tryFirstReturns",
+ "tryResultToLocal",
+ "doCatch",
+ "catchReturns",
+ "catchWithLocal",
+ "catchThrows",
+ "doFinally",
+ "finallyReturns",
+ "finallyThrows",
+ "endReturnLocal",
+ "deopt"
+ ))
+
+emptyflags = flagtuple(*((False,) * len(flagtuple._fields)))
+f1 = emptyflags._replace(tryReturns=True, doCatch=True)
+
+# You can test function printtest with f1.
+
+allFlagCombinations = [
+ flagtuple(*bools)
+ for bools in booltuples(len(flagtuple._fields))
+]
+
+if __name__ == '__main__':
+ global MODE
+ if sys.argv[1:] == []:
+ MODE = 'stdout'
+ print("// Printing all shards together to stdout.")
+ print("")
+ write_shard_header()
+ FILE = sys.stdout
+ elif sys.argv[1:] == ['--shard-and-overwrite']:
+ MODE = 'shard'
+ else:
+ print("Usage:")
+ print("")
+ print(" python {}".format(sys.argv[0]))
+ print(" print all tests to standard output")
+ print(" python {} --shard-and-overwrite".format(sys.argv[0]))
+ print(" print all tests to {}".format(SHARD_FILENAME_TEMPLATE))
+
+ print("")
+ print(sys.argv[1:])
+ print("")
+ sys.exit(1)
+
+ rotateshard()
+
+ for flags in allFlagCombinations:
+ printtest(flags)
+ rotateshard()
+
+ finishshard()
« no previous file with comments | « test/mjsunit/compiler/inline-exception-2.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698