8d921ca7f3
This removes the --turbo flag and solely relies on the filter pattern provided via --turbo-filter when deciding whether to use TurboFan. Note that disabling optimization wholesale can still be done with --no-opt, which should be used in favor of --no-turbo everywhere. Also note that this contains semantic changes to the TurboFan activation criteria. We respect the filter pattern more stringently and no longer activate TurboFan just because the source contains patterns forcing use of Ignition via {AstNumberingVisitor::DisableFullCodegenAndCrankshaft}. R=rmcilroy@chromium.org BUG=v8:6408 Change-Id: I0c855f6a62350eb62283a3431c8cc1baa750950e Reviewed-on: https://chromium-review.googlesource.com/528121 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Reviewed-by: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Commit-Queue: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#46167}
567 lines
15 KiB
Python
567 lines
15 KiB
Python
#!/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 = 97
|
|
|
|
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 --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 increaseAndReturn15_noopt_inner() {
|
|
if (deopt) %DeoptimizeFunction(f);
|
|
counter++;
|
|
return 15;
|
|
}
|
|
|
|
%NeverOptimizeFunction(increaseAndReturn15_noopt_inner);
|
|
|
|
function increaseAndThrow42_noopt_inner() {
|
|
if (deopt) %DeoptimizeFunction(f);
|
|
counter++;
|
|
throw 42;
|
|
}
|
|
|
|
%NeverOptimizeFunction(increaseAndThrow42_noopt_inner);
|
|
|
|
// Alternative 1
|
|
|
|
function returnOrThrow(doReturn) {
|
|
if (doReturn) {
|
|
return increaseAndReturn15();
|
|
} else {
|
|
return increaseAndThrow42();
|
|
}
|
|
}
|
|
|
|
// Alternative 2
|
|
|
|
function increaseAndReturn15_calls_noopt() {
|
|
return increaseAndReturn15_noopt_inner();
|
|
}
|
|
|
|
function increaseAndThrow42_calls_noopt() {
|
|
return increaseAndThrow42_noopt_inner();
|
|
}
|
|
|
|
// Alternative 3.
|
|
// 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;
|
|
}
|
|
|
|
// Alternative 4: constructor
|
|
function increaseAndStore15Constructor() {
|
|
if (deopt) %DeoptimizeFunction(f);
|
|
++counter;
|
|
this.x = 15;
|
|
}
|
|
|
|
function increaseAndThrow42Constructor() {
|
|
if (deopt) %DeoptimizeFunction(f);
|
|
++counter;
|
|
this.x = 42;
|
|
throw this.x;
|
|
}
|
|
|
|
// Alternative 5: property
|
|
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, increaseAndReturn15_calls_noopt());
|
|
assertThrowsEquals(function() { return increaseAndThrow42_noopt_inner() }, 42);
|
|
|
|
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,)
|
|
|
|
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.
|
|
(
|
|
alternativeFn5, # use alternative #5 for returning/throwing:
|
|
# return/throw using property
|
|
alternativeFn4, # use alternative #4 for returning/throwing:
|
|
# return/throw using constructor
|
|
alternativeFn3, # use alternative #3 for returning/throwing:
|
|
# return/throw indirectly, based on function argument
|
|
alternativeFn2, # use alternative #2 for returning/throwing:
|
|
# return/throw indirectly in unoptimized code,
|
|
# no branching
|
|
alternativeFn1, # use alternative #1 for returning/throwing:
|
|
# return/throw indirectly, based on boolean arg
|
|
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
|
|
+ alternativeFn5 > 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, alternativeFn5])
|
|
specificAlternative = any([alternativeFn2, alternativeFn3])
|
|
rareAlternative = not specificAlternative
|
|
|
|
# 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 or #3.
|
|
if ((tryResultToLocal or catchWithLocal or endReturnLocal) and
|
|
not specificAlternative):
|
|
return
|
|
# We don't need to test deopting into a finally.
|
|
if doFinally and deopt: return
|
|
|
|
# We're only interested in alternative #2 if we have endReturnLocal, no
|
|
# catchReturns, and no catchThrows, and deopt.
|
|
if (alternativeFn2 and
|
|
(not endReturnLocal or catchReturns or catchThrows or not 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': 'increaseAndReturn15_calls_noopt()',
|
|
'increaseAndThrow42': 'increaseAndThrow42_calls_noopt()',
|
|
}
|
|
elif alternativeFn3:
|
|
fragments = {
|
|
'increaseAndReturn15': 'invertFunctionCall(increaseAndThrow42)',
|
|
'increaseAndThrow42': 'invertFunctionCall(increaseAndReturn15)',
|
|
}
|
|
elif alternativeFn4:
|
|
fragments = {
|
|
'increaseAndReturn15': '(new increaseAndStore15Constructor()).x',
|
|
'increaseAndThrow42': '(new increaseAndThrow42Constructor()).x',
|
|
}
|
|
else:
|
|
assert alternativeFn5
|
|
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 = 888;")
|
|
write( " deopt = {};".format("true" if deopt else "false"))
|
|
local = 888
|
|
write( " try {")
|
|
write( " counter++;")
|
|
counter += 1
|
|
resultTo = "local +=" if tryResultToLocal else "return"
|
|
if tryReturns and not (tryThrows and not tryFirstReturns):
|
|
write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments))
|
|
if result == None:
|
|
counter += 1
|
|
if tryResultToLocal:
|
|
local += 19
|
|
else:
|
|
result = ("return", 19)
|
|
if tryThrows:
|
|
write( " {} 4 + {increaseAndThrow42};".format(resultTo, **fragments))
|
|
if result == None:
|
|
counter += 1
|
|
result = ("throw", 42)
|
|
if tryReturns and tryThrows and not tryFirstReturns:
|
|
write( " {} 4 + {increaseAndReturn15};".format(resultTo, **fragments))
|
|
if result == None:
|
|
counter += 1
|
|
if tryResultToLocal:
|
|
local += 19
|
|
else:
|
|
result = ("return", 19)
|
|
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();")
|
|
|
|
FLAGLETTERS="54321trflcrltfrtld"
|
|
|
|
flagtuple = namedtuple('flagtuple', (
|
|
"alternativeFn5",
|
|
"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()
|
|
|
|
if MODE == 'shard':
|
|
print("Total: {} tests.".format(NUM_TESTS_PRINTED))
|