[compiler] Ensure code unsupported by Crankshaft goes to Ignition.

BUG=v8:4280,v8:5657

Review-Url: https://codereview.chromium.org/2505933008
Cr-Commit-Position: refs/heads/master@{#41209}
This commit is contained in:
rmcilroy 2016-11-23 01:30:11 -08:00 committed by Commit bot
parent de330e13da
commit 5f5300a61b
13 changed files with 91 additions and 92 deletions

View File

@ -55,8 +55,8 @@ class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> {
dont_optimize_reason_ = reason;
DisableSelfOptimization();
}
void DisableCrankshaft(BailoutReason reason) {
properties_.flags() |= AstProperties::kDontCrankshaft;
void DisableFullCodegenAndCrankshaft(BailoutReason reason) {
properties_.flags() |= AstProperties::kMustUseIgnitionTurbo;
}
template <typename Node>
@ -149,10 +149,11 @@ void AstNumberingVisitor::VisitVariableProxyReference(VariableProxy* node) {
IncrementNodeCount();
switch (node->var()->location()) {
case VariableLocation::LOOKUP:
DisableCrankshaft(kReferenceToAVariableWhichRequiresDynamicLookup);
DisableFullCodegenAndCrankshaft(
kReferenceToAVariableWhichRequiresDynamicLookup);
break;
case VariableLocation::MODULE:
DisableCrankshaft(kReferenceToModuleVariable);
DisableFullCodegenAndCrankshaft(kReferenceToModuleVariable);
break;
default:
break;
@ -176,7 +177,7 @@ void AstNumberingVisitor::VisitThisFunction(ThisFunction* node) {
void AstNumberingVisitor::VisitSuperPropertyReference(
SuperPropertyReference* node) {
IncrementNodeCount();
DisableCrankshaft(kSuperReference);
DisableFullCodegenAndCrankshaft(kSuperReference);
node->set_base_id(ReserveIdRange(SuperPropertyReference::num_ids()));
Visit(node->this_var());
Visit(node->home_object());
@ -185,7 +186,7 @@ void AstNumberingVisitor::VisitSuperPropertyReference(
void AstNumberingVisitor::VisitSuperCallReference(SuperCallReference* node) {
IncrementNodeCount();
DisableCrankshaft(kSuperReference);
DisableFullCodegenAndCrankshaft(kSuperReference);
node->set_base_id(ReserveIdRange(SuperCallReference::num_ids()));
Visit(node->this_var());
Visit(node->new_target_var());
@ -282,7 +283,7 @@ void AstNumberingVisitor::VisitCallRuntime(CallRuntime* node) {
void AstNumberingVisitor::VisitWithStatement(WithStatement* node) {
IncrementNodeCount();
DisableCrankshaft(kWithStatement);
DisableFullCodegenAndCrankshaft(kWithStatement);
node->set_base_id(ReserveIdRange(WithStatement::num_ids()));
Visit(node->expression());
Visit(node->statement());
@ -313,7 +314,7 @@ void AstNumberingVisitor::VisitWhileStatement(WhileStatement* node) {
void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
IncrementNodeCount();
DisableCrankshaft(kTryCatchStatement);
DisableFullCodegenAndCrankshaft(kTryCatchStatement);
{
const HandlerTable::CatchPrediction old_prediction = catch_prediction_;
// This node uses its own prediction, unless it's "uncaught", in which case
@ -332,7 +333,7 @@ void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
void AstNumberingVisitor::VisitTryFinallyStatement(TryFinallyStatement* node) {
IncrementNodeCount();
DisableCrankshaft(kTryFinallyStatement);
DisableFullCodegenAndCrankshaft(kTryFinallyStatement);
// We can't know whether the finally block will override ("catch") an
// exception thrown in the try block, so we just adopt the outer prediction.
node->set_catch_prediction(catch_prediction_);
@ -417,7 +418,7 @@ void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) {
void AstNumberingVisitor::VisitForOfStatement(ForOfStatement* node) {
IncrementNodeCount();
DisableCrankshaft(kForOfStatement);
DisableFullCodegenAndCrankshaft(kForOfStatement);
node->set_base_id(ReserveIdRange(ForOfStatement::num_ids()));
Visit(node->assign_iterator()); // Not part of loop.
node->set_first_yield_id(yield_count_);
@ -484,7 +485,7 @@ void AstNumberingVisitor::VisitForStatement(ForStatement* node) {
void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
IncrementNodeCount();
DisableCrankshaft(kClassLiteral);
DisableFullCodegenAndCrankshaft(kClassLiteral);
node->set_base_id(ReserveIdRange(node->num_ids()));
if (node->extends()) Visit(node->extends());
if (node->constructor()) Visit(node->constructor());
@ -513,7 +514,8 @@ void AstNumberingVisitor::VisitObjectLiteral(ObjectLiteral* node) {
}
void AstNumberingVisitor::VisitLiteralProperty(LiteralProperty* node) {
if (node->is_computed_name()) DisableCrankshaft(kComputedPropertyName);
if (node->is_computed_name())
DisableFullCodegenAndCrankshaft(kComputedPropertyName);
Visit(node->key());
Visit(node->value());
}
@ -584,22 +586,22 @@ void AstNumberingVisitor::VisitRewritableExpression(
bool AstNumberingVisitor::Renumber(FunctionLiteral* node) {
DeclarationScope* scope = node->scope();
if (scope->new_target_var()) DisableCrankshaft(kSuperReference);
if (scope->calls_eval()) DisableCrankshaft(kFunctionCallsEval);
if (scope->new_target_var()) DisableFullCodegenAndCrankshaft(kSuperReference);
if (scope->calls_eval()) DisableFullCodegenAndCrankshaft(kFunctionCallsEval);
if (scope->arguments() != NULL && !scope->arguments()->IsStackAllocated()) {
DisableCrankshaft(kContextAllocatedArguments);
DisableFullCodegenAndCrankshaft(kContextAllocatedArguments);
}
if (scope->rest_parameter() != nullptr) {
DisableCrankshaft(kRestParameter);
DisableFullCodegenAndCrankshaft(kRestParameter);
}
if (IsResumableFunction(node->kind())) {
DisableCrankshaft(kGenerator);
DisableFullCodegenAndCrankshaft(kGenerator);
}
if (IsClassConstructor(node->kind())) {
DisableCrankshaft(kClassConstructorFunction);
DisableFullCodegenAndCrankshaft(kClassConstructorFunction);
}
VisitDeclarations(scope->declarations());

View File

@ -154,7 +154,7 @@ class AstProperties final BASE_EMBEDDED {
enum Flag {
kNoFlags = 0,
kDontSelfOptimize = 1 << 0,
kDontCrankshaft = 1 << 1
kMustUseIgnitionTurbo = 1 << 1
};
typedef base::Flags<Flag> Flags;

View File

@ -305,7 +305,7 @@ void EnsureFeedbackMetadata(CompilationInfo* info) {
bool UseTurboFan(Handle<SharedFunctionInfo> shared) {
bool optimization_disabled = shared->optimization_disabled();
bool dont_crankshaft = shared->dont_crankshaft();
bool must_use_ignition_turbo = shared->must_use_ignition_turbo();
// Check the enabling conditions for Turbofan.
// 1. "use asm" code.
@ -314,7 +314,7 @@ bool UseTurboFan(Handle<SharedFunctionInfo> shared) {
// 2. Fallback for features unsupported by Crankshaft.
bool is_unsupported_by_crankshaft_but_turbofanable =
dont_crankshaft && strcmp(FLAG_turbo_filter, "~~") == 0 &&
must_use_ignition_turbo && strcmp(FLAG_turbo_filter, "~~") == 0 &&
!optimization_disabled;
// 3. Explicitly enabled by the command-line filter.
@ -326,42 +326,44 @@ bool UseTurboFan(Handle<SharedFunctionInfo> shared) {
bool ShouldUseIgnition(CompilationInfo* info) {
DCHECK(info->has_shared_info());
Handle<SharedFunctionInfo> shared = info->shared_info();
// Code which can't be supported by the old pipeline should use Ignition.
if (shared->must_use_ignition_turbo()) return true;
// Resumable functions are not supported by {FullCodeGenerator}, suspended
// activations stored as {JSGeneratorObject} on the heap always assume the
// underlying code to be based on the bytecode array.
// TODO(mstarzinger): Once we want to deprecate even more support from the
// {FullCodeGenerator}, we will compute an appropriate bit in {AstNumbering}
// and turn this predicate into a DCHECK instead.
if (IsResumableFunction(info->shared_info()->kind())) {
return true;
}
DCHECK(!IsResumableFunction(shared->kind()));
// Skip Ignition for asm.js functions.
if (info->shared_info()->asm_function()) {
if (shared->asm_function()) return false;
// Skip Ignition for asm wasm code.
if (FLAG_validate_asm && shared->HasAsmWasmData()) {
return false;
}
// When requesting debug code as a replacement for existing code, we provide
// the same kind as the existing code (to prevent implicit tier-change).
if (info->is_debug() && info->shared_info()->is_compiled()) {
return !info->shared_info()->HasBaselineCode();
if (info->is_debug() && shared->is_compiled()) {
return !shared->HasBaselineCode();
}
// Code destined for TurboFan should be compiled with Ignition first.
if (UseTurboFan(info->shared_info())) return true;
if (UseTurboFan(shared)) return true;
// Only use Ignition for any other function if FLAG_ignition is true.
if (!FLAG_ignition) return false;
// Checks whether top level functions should be passed by the filter.
if (info->shared_info()->is_toplevel()) {
if (shared->is_toplevel()) {
Vector<const char> filter = CStrVector(FLAG_ignition_filter);
return (filter.length() == 0) || (filter.length() == 1 && filter[0] == '*');
}
// Finally respect the filter.
return info->shared_info()->PassesFilter(FLAG_ignition_filter);
return shared->PassesFilter(FLAG_ignition_filter);
}
CompilationJob* GetUnoptimizedCompilationJob(CompilationInfo* info) {
@ -527,8 +529,8 @@ bool Renumber(ParseInfo* parse_info) {
if (lit->dont_optimize_reason() != kNoReason) {
shared_info->DisableOptimization(lit->dont_optimize_reason());
}
if (lit->flags() & AstProperties::kDontCrankshaft) {
shared_info->set_dont_crankshaft(true);
if (lit->flags() & AstProperties::kMustUseIgnitionTurbo) {
shared_info->set_must_use_ignition_turbo(true);
}
}
return true;
@ -650,6 +652,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
}
// Reset profiler ticks, function is no longer considered hot.
DCHECK(shared->is_compiled());
if (shared->HasBaselineCode()) {
shared->code()->set_profiler_ticks(0);
} else if (shared->HasBytecodeArray()) {
@ -689,10 +692,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
// TurboFan can optimize directly from existing bytecode.
if (use_turbofan && ShouldUseIgnition(info)) {
if (info->is_osr() && !ignition_osr) return MaybeHandle<Code>();
if (!Compiler::EnsureBytecode(info)) {
if (isolate->has_pending_exception()) isolate->clear_pending_exception();
return MaybeHandle<Code>();
}
DCHECK(shared->HasBytecodeArray());
info->MarkAsOptimizeFromBytecode();
}
@ -822,13 +822,11 @@ MaybeHandle<Code> GetBaselineCode(Handle<JSFunction> function) {
return MaybeHandle<Code>();
}
// TODO(4280): For now we do not switch generators or async functions to
// baseline code because there might be suspended activations stored in
// generator objects on the heap. We could eventually go directly to
// TurboFan in this case.
if (IsResumableFunction(function->shared()->kind())) {
// Don't generate full-codegen code for functions it can't support.
if (function->shared()->must_use_ignition_turbo()) {
return MaybeHandle<Code>();
}
DCHECK(!IsResumableFunction(function->shared()->kind()));
if (FLAG_trace_opt) {
OFStream os(stdout);
@ -1194,21 +1192,13 @@ MaybeHandle<JSArray> Compiler::CompileForLiveEdit(Handle<Script> script) {
}
bool Compiler::EnsureBytecode(CompilationInfo* info) {
if (!ShouldUseIgnition(info)) return false;
if (!info->shared_info()->HasBytecodeArray()) {
Handle<Code> original_code(info->shared_info()->code());
if (!info->shared_info()->is_compiled()) {
if (GetUnoptimizedCode(info).is_null()) return false;
if (info->shared_info()->HasAsmWasmData()) return false;
DCHECK(info->shared_info()->is_compiled());
if (original_code->kind() == Code::FUNCTION) {
// Generating bytecode will install the {InterpreterEntryTrampoline} as
// shared code on the function. To avoid an implicit tier down we restore
// original baseline code in case it existed beforehand.
info->shared_info()->ReplaceCode(*original_code);
}
}
DCHECK(info->shared_info()->HasBytecodeArray());
return true;
DCHECK(info->shared_info()->is_compiled());
DCHECK_EQ(ShouldUseIgnition(info), info->shared_info()->HasBytecodeArray());
return info->shared_info()->HasBytecodeArray();
}
// TODO(turbofan): In the future, unoptimized code with deopt support could
@ -1222,11 +1212,9 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
CompilationInfo unoptimized(info->parse_info(), info->closure());
unoptimized.EnableDeoptimizationSupport();
// TODO(4280): For now we do not switch generators or async functions to
// baseline code because there might be suspended activations stored in
// generator objects on the heap. We could eventually go directly to
// TurboFan in this case.
if (IsResumableFunction(shared->kind())) return false;
// Don't generate full-codegen code for functions it can't support.
if (shared->must_use_ignition_turbo()) return false;
DCHECK(!IsResumableFunction(shared->kind()));
// When we call PrepareForSerializing below, we will change the shared
// ParseInfo. Make sure to reset it.
@ -1240,6 +1228,14 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
unoptimized.PrepareForSerializing();
}
EnsureFeedbackMetadata(&unoptimized);
// Ensure we generate and install bytecode first if the function should use
// Ignition to avoid implicit tier-down.
if (!shared->is_compiled() && ShouldUseIgnition(info) &&
!GenerateUnoptimizedCode(info)) {
return false;
}
if (!FullCodeGenerator::MakeCode(&unoptimized)) return false;
info->parse_info()->set_will_serialize(old_will_serialize_value);
@ -1714,7 +1710,8 @@ void Compiler::PostInstantiation(Handle<JSFunction> function,
PretenureFlag pretenure) {
Handle<SharedFunctionInfo> shared(function->shared());
if (FLAG_always_opt && shared->allows_lazy_compilation()) {
if (FLAG_always_opt && shared->allows_lazy_compilation() &&
function->shared()->is_compiled()) {
function->MarkForOptimization();
}

View File

@ -118,7 +118,7 @@ class HOptimizedGraphBuilderWithPositions : public HOptimizedGraphBuilder {
HCompilationJob::Status HCompilationJob::PrepareJobImpl() {
if (!isolate()->use_crankshaft() ||
info()->shared_info()->dont_crankshaft()) {
info()->shared_info()->must_use_ignition_turbo()) {
// Crankshaft is entirely disabled.
return FAILED;
}
@ -8079,7 +8079,7 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
TraceInline(target, caller, "parse failure");
return false;
}
if (target_shared->dont_crankshaft()) {
if (target_shared->must_use_ignition_turbo()) {
TraceInline(target, caller, "ParseAndAnalyze found incompatibility");
return false;
}

View File

@ -84,6 +84,7 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
bool FullCodeGenerator::MakeCode(CompilationInfo* info, uintptr_t stack_limit) {
Isolate* isolate = info->isolate();
DCHECK(!info->shared_info()->must_use_ignition_turbo());
DCHECK(!FLAG_minimal);
RuntimeCallTimerScope runtimeTimer(isolate,
&RuntimeCallStats::CompileFullCode);

View File

@ -6199,8 +6199,8 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints,
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_anonymous_expression,
kIsAnonymousExpression)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_function, kIsFunction)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_crankshaft,
kDontCrankshaft)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, must_use_ignition_turbo,
kMustUseIgnitionTurbo)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_flush, kDontFlush)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_asm_wasm_broken,
kIsAsmWasmBroken)

View File

@ -7640,8 +7640,10 @@ class SharedFunctionInfo: public HeapObject {
// Is this a function or top-level/eval code.
DECL_BOOLEAN_ACCESSORS(is_function)
// Indicates that code for this function cannot be compiled with Crankshaft.
DECL_BOOLEAN_ACCESSORS(dont_crankshaft)
// Indicates that code for this function must be compiled through the
// Ignition / TurboFan pipeline, and is unsupported by
// FullCodegen / Crankshaft.
DECL_BOOLEAN_ACCESSORS(must_use_ignition_turbo)
// Indicates that code for this function cannot be flushed.
DECL_BOOLEAN_ACCESSORS(dont_flush)
@ -7930,7 +7932,7 @@ class SharedFunctionInfo: public HeapObject {
kIsAnonymousExpression,
kNameShouldPrintAsAnonymous,
kIsFunction,
kDontCrankshaft,
kMustUseIgnitionTurbo,
kDontFlush,
// byte 2
kFunctionKind,

View File

@ -127,6 +127,12 @@ RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) {
return isolate->heap()->undefined_value();
}
// If function isn't compiled, compile it now.
if (!function->shared()->is_compiled() &&
!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) {
return isolate->heap()->undefined_value();
}
// If the function is already optimized, just return.
if (function->IsOptimized()) return isolate->heap()->undefined_value();

View File

@ -330,22 +330,6 @@
# in interpreter.
'test-heap/CompilationCacheCachingBehavior': [FAIL],
# TODO(mstarzinger): Triggers Ignition+TurboFan on everything now and makes
# the stack traces within the profilers look different. Needs investigation.
'test-api/SetFunctionEntryHook': [SKIP],
'test-cpu-profiler/BoundFunctionCall': [FAIL],
'test-cpu-profiler/CollectSampleAPI': [FAIL],
'test-cpu-profiler/FunctionApplySample': [FAIL],
'test-cpu-profiler/FunctionCallSample': [FAIL],
'test-cpu-profiler/JsNativeJsRuntimeJsSample': [FAIL],
'test-cpu-profiler/JsNativeJsSample': [FAIL],
'test-cpu-profiler/JsNativeJsRuntimeJsSampleMultiple': [FAIL],
'test-cpu-profiler/JsNative1JsNative2JsSample': [FAIL],
'test-cpu-profiler/NativeMethodUninitializedIC': [FAIL],
'test-cpu-profiler/NativeAccessorUninitializedIC': [FAIL],
'test-profile-generator/LineNumber': [FAIL],
'test-sampler-api/StackFramesConsistent': [FAIL],
# BUG(v8:5457)
'test-api/SetJitCodeEventHandler': [PASS, ['no_snap', SKIP]],
}], # variant == turbofan_opt
@ -362,8 +346,6 @@
##############################################################################
['variant == ignition_staging', {
'test-cpu-profiler/DeoptUntrackedFunction': [SKIP],
'test-cpu-profiler/TickLinesOptimized': [SKIP],
'test-heap/CompilationCacheCachingBehavior': [FAIL],
# BUG(5193): Flaky.
@ -376,9 +358,8 @@
# in interpreter.
'test-heap/CompilationCacheCachingBehavior': [FAIL],
# BUG(4680): Missing type feedback makes optimistic optimizations fail.
'test-cpu-profiler/CollectDeoptEvents': [FAIL],
'test-cpu-profiler/DeoptUntrackedFunction': [SKIP],
# BUG(4751). Deopts with kNotASmi instead of expected deopt reason.
'test-cpu-profiler/CollectDeoptEvents': [SKIP],
# BUG(4751). Flaky with Ignition.
'test-cpu-profiler/JsNativeJsSample': [SKIP],

View File

@ -164,7 +164,9 @@ Handle<JSFunction> FunctionTester::Compile(Handle<JSFunction> function) {
if (flags_ & CompilationInfo::kInliningEnabled) {
info.MarkAsInliningEnabled();
}
if (Compiler::EnsureBytecode(&info)) {
CHECK(Compiler::Compile(function, Compiler::CLEAR_EXCEPTION));
if (info.shared_info()->HasBytecodeArray()) {
info.MarkAsOptimizeFromBytecode();
} else {
CHECK(Compiler::ParseAndAnalyze(info.parse_info()));

View File

@ -14560,7 +14560,7 @@ void SetFunctionEntryHookTest::RunTest() {
RunLoopInNewEnv(isolate);
// Check the expected invocation counts.
if (!i::FLAG_ignition && !i::FLAG_turbo) {
if (i::FLAG_always_opt || (!i::FLAG_ignition && !i::FLAG_turbo)) {
CHECK_EQ(2, CountInvocations(NULL, "bar"));
CHECK_EQ(200, CountInvocations("bar", "foo"));
CHECK_EQ(200, CountInvocations(NULL, "foo"));

View File

@ -1575,6 +1575,7 @@ TEST(JsNativeJsRuntimeJsSampleMultiple) {
static const char* inlining_test_source =
"%NeverOptimizeFunction(action);\n"
"%NeverOptimizeFunction(start);\n"
"level1()\n"
"%OptimizeFunctionOnNextCall(level1);\n"
"%OptimizeFunctionOnNextCall(level2);\n"
"%OptimizeFunctionOnNextCall(level3);\n"

View File

@ -27,6 +27,13 @@
'debug/es6/debug-scope-default-param-with-eval': [FAIL],
}], # ALWAYS
##############################################################################
['variant == stress', {
# TODO(jarin/mstarzinger): Functions with eval or debugger now get optimized
# with Turbofan, which has issues with the debugger issues.
'debug/debug-evaluate-locals': [FAIL],
}], # 'stress == True'
##############################################################################
['gc_stress == True', {
# Skip tests not suitable for GC stress.