From: titzer@chromium.org Date: Mon, 9 Sep 2013 16:34:40 +0000 (+0000) Subject: Generate a custom OSR entrypoint for OSR compiles on all platforms, and transition... X-Git-Tag: upstream/4.7.83~12605 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=49d9555a971c5f3cd6982702025765d3abbe92c7;p=platform%2Fupstream%2Fv8.git Generate a custom OSR entrypoint for OSR compiles on all platforms, and transition to optimized code using the special entrypoint, instead of through the deoptimizer. Do not install the OSR compiled code as _the_ optimized code for a function. Remove OSR-related stuff from deoptimizer. BUG= R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/21340002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16599 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 9ebee93..8862702 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -975,22 +975,30 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); } - // If the result was -1 it means that we couldn't optimize the - // function. Just return and continue in the unoptimized version. + // If the code object is null, just return to the unoptimized code. Label skip; - __ cmp(r0, Operand(Smi::FromInt(-1))); + __ cmp(r0, Operand(Smi::FromInt(0))); __ b(ne, &skip); __ Ret(); __ bind(&skip); - // Untag the AST id and push it on the stack. - __ SmiUntag(r0); - __ push(r0); - - // Generate the code for doing the frame-to-frame translation using - // the deoptimizer infrastructure. - Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR); - generator.Generate(); + + // Load deoptimization data from the code object. + // = [#deoptimization_data_offset] + __ ldr(r1, MemOperand(r0, Code::kDeoptimizationDataOffset - kHeapObjectTag)); + + // Load the OSR entrypoint offset from the deoptimization data. + // = [#header_size + #osr_pc_offset] + __ ldr(r1, MemOperand(r1, FixedArray::OffsetOfElementAt( + DeoptimizationInputData::kOsrPcOffsetIndex) - kHeapObjectTag)); + + // Compute the target address = code_obj + header_size + osr_offset + // = + #header_size + + __ add(r0, r0, Operand::SmiUntag(r1)); + __ add(lr, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); + + // And "return" to the OSR entry point of the function. + __ Ret(); } diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc index e49e5bc..3c57b64 100644 --- a/src/arm/deoptimizer-arm.cc +++ b/src/arm/deoptimizer-arm.cc @@ -175,169 +175,6 @@ Deoptimizer::InterruptPatchState Deoptimizer::GetInterruptPatchState( #endif // DEBUG -static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) { - ByteArray* translations = data->TranslationByteArray(); - int length = data->DeoptCount(); - for (int i = 0; i < length; i++) { - if (data->AstId(i) == ast_id) { - TranslationIterator it(translations, data->TranslationIndex(i)->value()); - int value = it.Next(); - ASSERT(Translation::BEGIN == static_cast(value)); - // Read the number of frames. - value = it.Next(); - if (value == 1) return i; - } - } - UNREACHABLE(); - return -1; -} - - -void Deoptimizer::DoComputeOsrOutputFrame() { - DeoptimizationInputData* data = DeoptimizationInputData::cast( - compiled_code_->deoptimization_data()); - unsigned ast_id = data->OsrAstId()->value(); - - int bailout_id = LookupBailoutId(data, BailoutId(ast_id)); - unsigned translation_index = data->TranslationIndex(bailout_id)->value(); - ByteArray* translations = data->TranslationByteArray(); - - TranslationIterator iterator(translations, translation_index); - Translation::Opcode opcode = - static_cast(iterator.Next()); - ASSERT(Translation::BEGIN == opcode); - USE(opcode); - int count = iterator.Next(); - iterator.Skip(1); // Drop JS frame count. - ASSERT(count == 1); - USE(count); - - opcode = static_cast(iterator.Next()); - USE(opcode); - ASSERT(Translation::JS_FRAME == opcode); - unsigned node_id = iterator.Next(); - USE(node_id); - ASSERT(node_id == ast_id); - int closure_id = iterator.Next(); - USE(closure_id); - ASSERT_EQ(Translation::kSelfLiteralId, closure_id); - unsigned height = iterator.Next(); - unsigned height_in_bytes = height * kPointerSize; - USE(height_in_bytes); - - unsigned fixed_size = ComputeFixedSize(function_); - unsigned input_frame_size = input_->GetFrameSize(); - ASSERT(fixed_size + height_in_bytes == input_frame_size); - - unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize; - unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value(); - unsigned outgoing_size = outgoing_height * kPointerSize; - unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size; - ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call. - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => node=%u, frame=%d->%d]\n", - ast_id, - input_frame_size, - output_frame_size); - } - - // There's only one output frame in the OSR case. - output_count_ = 1; - output_ = new FrameDescription*[1]; - output_[0] = new(output_frame_size) FrameDescription( - output_frame_size, function_); - output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); - - // Clear the incoming parameters in the optimized frame to avoid - // confusing the garbage collector. - unsigned output_offset = output_frame_size - kPointerSize; - int parameter_count = function_->shared()->formal_parameter_count() + 1; - for (int i = 0; i < parameter_count; ++i) { - output_[0]->SetFrameSlot(output_offset, 0); - output_offset -= kPointerSize; - } - - // Translate the incoming parameters. This may overwrite some of the - // incoming argument slots we've just cleared. - int input_offset = input_frame_size - kPointerSize; - bool ok = true; - int limit = input_offset - (parameter_count * kPointerSize); - while (ok && input_offset > limit) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // There are no translation commands for the caller's pc and fp, the - // context, and the function. Set them up explicitly. - for (int i = StandardFrameConstants::kCallerPCOffset; - ok && i >= StandardFrameConstants::kMarkerOffset; - i -= kPointerSize) { - uint32_t input_value = input_->GetFrameSlot(input_offset); - if (FLAG_trace_osr) { - const char* name = "UNKNOWN"; - switch (i) { - case StandardFrameConstants::kCallerPCOffset: - name = "caller's pc"; - break; - case StandardFrameConstants::kCallerFPOffset: - name = "fp"; - break; - case StandardFrameConstants::kContextOffset: - name = "context"; - break; - case StandardFrameConstants::kMarkerOffset: - name = "function"; - break; - } - PrintF(" [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n", - output_offset, - input_value, - input_offset, - name); - } - - output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset)); - input_offset -= kPointerSize; - output_offset -= kPointerSize; - } - - // Translate the rest of the frame. - while (ok && input_offset >= 0) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // If translation of any command failed, continue using the input frame. - if (!ok) { - delete output_[0]; - output_[0] = input_; - output_[0]->SetPc(reinterpret_cast(from_)); - } else { - // Set up the frame pointer and the context pointer. - output_[0]->SetRegister(fp.code(), input_->GetRegister(fp.code())); - output_[0]->SetRegister(cp.code(), input_->GetRegister(cp.code())); - - unsigned pc_offset = data->OsrPcOffset()->value(); - uint32_t pc = reinterpret_cast( - compiled_code_->entry() + pc_offset); - output_[0]->SetPc(pc); - } - Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR); - output_[0]->SetContinuation( - reinterpret_cast(continuation->entry())); - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", - ok ? "finished" : "aborted", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => pc=0x%0x]\n", output_[0]->GetPc()); - } -} - - void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { // Set the register values. The values are not important as there are no // callee saved registers in JavaScript frames, so all registers are @@ -550,11 +387,8 @@ void Deoptimizer::EntryGenerator::Generate() { } // Push state, pc, and continuation from the last output frame. - if (type() != OSR) { - __ ldr(r6, MemOperand(r2, FrameDescription::state_offset())); - __ push(r6); - } - + __ ldr(r6, MemOperand(r2, FrameDescription::state_offset())); + __ push(r6); __ ldr(r6, MemOperand(r2, FrameDescription::pc_offset())); __ push(r6); __ ldr(r6, MemOperand(r2, FrameDescription::continuation_offset())); diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 132e1a6..9695072 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -30,6 +30,7 @@ #include "lithium-allocator-inl.h" #include "arm/lithium-arm.h" #include "arm/lithium-codegen-arm.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -433,6 +434,15 @@ LPlatformChunk* LChunkBuilder::Build() { chunk_ = new(zone()) LPlatformChunk(info(), graph()); LPhase phase("L_Building chunk", chunk_); status_ = BUILDING; + + // If compiling for OSR, reserve space for the unoptimized frame, + // which will be subsumed into this frame. + if (graph()->has_osr()) { + for (int i = graph()->osr()->UnoptimizedFrameSlots(); i > 0; i--) { + chunk_->GetNextSpillIndex(false); + } + } + const ZoneList* blocks = graph()->blocks(); for (int i = 0; i < blocks->length(); i++) { HBasicBlock* next = NULL; @@ -2439,10 +2449,18 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { - int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width. - if (spill_index > LUnallocated::kMaxFixedSlotIndex) { - Abort(kTooManySpillSlotsNeededForOSR); - spill_index = 0; + // Use an index that corresponds to the location in the unoptimized frame, + // which the optimized frame will subsume. + int env_index = instr->index(); + int spill_index = 0; + if (instr->environment()->is_parameter_index(env_index)) { + spill_index = chunk()->GetParameterStackSlot(env_index); + } else { + spill_index = env_index - instr->environment()->first_local_index(); + if (spill_index > LUnallocated::kMaxFixedSlotIndex) { + Abort(kTooManySpillSlotsNeededForOSR); + spill_index = 0; + } } return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index); } diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index d880286..cf76a08 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -31,6 +31,7 @@ #include "arm/lithium-gap-resolver-arm.h" #include "code-stubs.h" #include "stub-cache.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -253,6 +254,21 @@ bool LCodeGen::GeneratePrologue() { } +void LCodeGen::GenerateOsrPrologue() { + // Generate the OSR entry prologue at the first unknown OSR value, or if there + // are none, at the OSR entrypoint instruction. + if (osr_pc_offset_ >= 0) return; + + osr_pc_offset_ = masm()->pc_offset(); + + // Adjust the frame size, subsuming the unoptimized frame into the + // optimized frame. + int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots(); + ASSERT(slots >= 0); + __ sub(sp, sp, Operand(slots * kPointerSize)); +} + + bool LCodeGen::GenerateBody() { ASSERT(is_generating()); bool emit_instructions = true; @@ -1098,8 +1114,7 @@ void LCodeGen::DoCallStub(LCallStub* instr) { void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { - // Record the address of the first unknown OSR value as the place to enter. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } @@ -5684,9 +5699,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { ASSERT(!environment->HasBeenRegistered()); RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); - // Normally we record the first unknown OSR value as the entrypoint to the OSR - // code, but if there were none, record the entrypoint here. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index 8a0d3be..5826849 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -227,6 +227,9 @@ class LCodeGen V8_FINAL BASE_EMBEDDED { bool GenerateDeoptJumpTable(); bool GenerateSafepointTable(); + // Generates the custom OSR entrypoint and sets the osr_pc_offset. + void GenerateOsrPrologue(); + enum SafepointMode { RECORD_SIMPLE_SAFEPOINT, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS diff --git a/src/compiler.cc b/src/compiler.cc index a206066..2e1a400 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -358,7 +358,7 @@ OptimizingCompiler::Status OptimizingCompiler::CreateGraph() { } const int locals_limit = LUnallocated::kMaxFixedSlotIndex; - if (!info()->osr_ast_id().IsNone() && + if (info()->is_osr() && scope->num_parameters() + 1 + scope->num_stack_slots() > locals_limit) { info()->set_bailout_reason(kTooManyParametersLocals); return AbortOptimization(); @@ -884,7 +884,7 @@ static void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) { if (code->kind() != Code::OPTIMIZED_FUNCTION) return; // Nothing to do. // Cache non-OSR optimized code. - if (FLAG_cache_optimized_code && info->osr_ast_id().IsNone()) { + if (FLAG_cache_optimized_code && !info->is_osr()) { Handle function = info->closure(); Handle shared(function->shared()); Handle literals(function->literals()); @@ -899,7 +899,7 @@ static bool InstallCodeFromOptimizedCodeMap(CompilationInfo* info) { if (!info->IsOptimizing()) return false; // Nothing to look up. // Lookup non-OSR optimized code. - if (FLAG_cache_optimized_code && info->osr_ast_id().IsNone()) { + if (FLAG_cache_optimized_code && !info->is_osr()) { Handle shared = info->shared_info(); Handle function = info->closure(); ASSERT(!function.is_null()); @@ -955,12 +955,15 @@ bool Compiler::CompileLazy(CompilationInfo* info) { InstallCodeCommon(info); if (info->IsOptimizing()) { + // Optimized code successfully created. Handle code = info->code(); ASSERT(shared->scope_info() != ScopeInfo::Empty(isolate)); + // TODO(titzer): Only replace the code if it was not an OSR compile. info->closure()->ReplaceCode(*code); InsertCodeIntoOptimizedCodeMap(info); return true; - } else { + } else if (!info->is_osr()) { + // Compilation failed. Replace with full code if not OSR compile. return InstallFullCode(info); } } @@ -1149,10 +1152,9 @@ static bool IsSuitableForOnStackReplacement(Isolate* isolate, } -BailoutId Compiler::CompileForOnStackReplacement(Handle function) { +Handle Compiler::CompileForOnStackReplacement( + Handle function) { Isolate* isolate = function->GetIsolate(); - // We have hit a back edge in an unoptimized frame for a function that was - // selected for on-stack replacement. Find the unoptimized code object. Handle unoptimized(function->shared()->code(), isolate); Deoptimizer::RevertInterruptCode(isolate, *unoptimized); @@ -1162,47 +1164,44 @@ BailoutId Compiler::CompileForOnStackReplacement(Handle function) { PrintF("]\n"); } - if (!IsSuitableForOnStackReplacement(isolate, function, unoptimized)) { - return BailoutId::None(); - } - - uint32_t pc_offset = CurrentPcOffset(isolate, function, unoptimized); + if (IsSuitableForOnStackReplacement(isolate, function, unoptimized)) { + // Find the PC offset in unoptimized code and translate to an AST id. + uint32_t pc_offset = CurrentPcOffset(isolate, function, unoptimized); + BailoutId ast_id = unoptimized->TranslatePcOffsetToAstId(pc_offset); + ASSERT(!ast_id.IsNone()); + if (FLAG_trace_osr) { + PrintF("[OSR - replacing at AST id %d in ", ast_id.ToInt()); + function->PrintName(); + PrintF("]\n"); + } - BailoutId ast_id = unoptimized->TranslatePcOffsetToAstId(pc_offset); - ASSERT(!ast_id.IsNone()); - if (FLAG_trace_osr) { - PrintF("[OSR - replacing at AST id %d in ", ast_id.ToInt()); - function->PrintName(); - PrintF("]\n"); - } + // Attempt OSR compilation. + Handle result = JSFunction::CompileOsr( + function, ast_id, CLEAR_EXCEPTION); - // Try to compile the optimized code. A true return value from - // CompileOptimized means that compilation succeeded, not necessarily - // that optimization succeeded. - if (JSFunction::CompileOptimized(function, ast_id, CLEAR_EXCEPTION) && - function->IsOptimized()) { - DeoptimizationInputData* data = DeoptimizationInputData::cast( - function->code()->deoptimization_data()); - if (data->OsrPcOffset()->value() >= 0) { + if (!result.is_null() && result->kind() == Code::OPTIMIZED_FUNCTION) { + // OSR compilation succeeded. + DeoptimizationInputData* data = + DeoptimizationInputData::cast(result->deoptimization_data()); if (FLAG_trace_osr) { PrintF("[OSR - entry, offset %d in optimized code]\n", data->OsrPcOffset()->value()); } ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id); - return ast_id; - } - } else { - if (FLAG_trace_osr) { - PrintF("[OSR - optimization failed for "); - function->PrintName(); - PrintF("]\n"); + return result; } } - return BailoutId::None(); + + if (FLAG_trace_osr) { + PrintF("[OSR - attempt failed for "); + function->PrintName(); + PrintF("]\n"); + } + return Handle::null(); } -BailoutId Compiler::CompileForConcurrentOSR(Handle function) { +Handle Compiler::CompileForConcurrentOSR(Handle function) { Isolate* isolate = function->GetIsolate(); Handle unoptimized(function->shared()->code(), isolate); @@ -1216,7 +1215,7 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle function) { function->PrintName(); PrintF("]\n"); } - return BailoutId::None(); + return Handle::null(); } OptimizingCompiler* compiler = isolate->optimizing_compiler_thread()-> @@ -1230,8 +1229,7 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle function) { } Deoptimizer::RevertInterruptCode(isolate, *unoptimized); - BailoutId ast_id = compiler->info()->osr_ast_id(); - + // TODO(titzer): don't install the OSR code into the function. bool succeeded = InstallOptimizedCode(compiler); isolate->optimizing_compiler_thread()->RemoveStaleOSRCandidates(); @@ -1242,21 +1240,26 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle function) { function->PrintName(); PrintF("]\n"); } - return BailoutId::None(); + return Handle::null(); } - - DeoptimizationInputData* data = DeoptimizationInputData::cast( - function->code()->deoptimization_data()); - - if (data->OsrPcOffset()->value() >= 0) { - ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id); - if (FLAG_trace_osr) { - PrintF("[COSR - entry at AST id %d, offset %d in optimized code]\n", - ast_id.ToInt(), data->OsrPcOffset()->value()); + Handle result = compiler->info()->code(); + + // Check the result matches our expectations, and don't use it otherwise. + if (result->kind() == Code::OPTIMIZED_FUNCTION) { + DeoptimizationInputData* data = + DeoptimizationInputData::cast(result->deoptimization_data()); + + if (data->OsrPcOffset()->value() >= 0) { + BailoutId ast_id = compiler->info()->osr_ast_id(); + ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id); + if (FLAG_trace_osr) { + PrintF("[COSR - entry at AST id %d, offset %d in optimized code]\n", + ast_id.ToInt(), data->OsrPcOffset()->value()); + } + return result; } - return ast_id; } - return BailoutId::None(); + return Handle::null(); } if (!IsSuitableForOnStackReplacement(isolate, function, unoptimized)) { @@ -1266,13 +1269,13 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle function) { PrintF(" is unsuitable, restoring interrupt calls]\n"); } Deoptimizer::RevertInterruptCode(isolate, *unoptimized); - return BailoutId::None(); + return Handle::null(); } if (!RecompileConcurrent(function, pc_offset)) { Deoptimizer::RevertInterruptCode(isolate, *unoptimized); } - return BailoutId::None(); + return Handle::null(); } diff --git a/src/compiler.h b/src/compiler.h index 65618dd..f09a86d 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -63,7 +63,8 @@ class CompilationInfo { Isolate* isolate() const { return isolate_; } - Zone* zone() const { return zone_; } + Zone* zone() { return zone_; } + bool is_osr() const { return !osr_ast_id_.IsNone(); } bool is_lazy() const { return IsLazy::decode(flags_); } bool is_eval() const { return IsEval::decode(flags_); } bool is_global() const { return IsGlobal::decode(flags_); } @@ -626,9 +627,9 @@ class Compiler : public AllStatic { static bool InstallOptimizedCode(OptimizingCompiler* info); - static BailoutId CompileForOnStackReplacement(Handle function); + static Handle CompileForOnStackReplacement(Handle function); - static BailoutId CompileForConcurrentOSR(Handle function); + static Handle CompileForConcurrentOSR(Handle function); #ifdef ENABLE_DEBUGGER_SUPPORT static bool MakeCodeForLiveEdit(CompilationInfo* info); diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc index 61ce780..c979a53 100644 --- a/src/deoptimizer.cc +++ b/src/deoptimizer.cc @@ -495,8 +495,6 @@ bool Deoptimizer::TraceEnabledFor(BailoutType deopt_type, return (frame_type == StackFrame::STUB) ? FLAG_trace_stub_failures : FLAG_trace_deopt; - case OSR: - return FLAG_trace_osr; } UNREACHABLE(); return false; @@ -509,7 +507,6 @@ const char* Deoptimizer::MessageFor(BailoutType type) { case SOFT: return "soft"; case LAZY: return "lazy"; case DEBUGGER: return "debugger"; - case OSR: return "OSR"; } UNREACHABLE(); return NULL; @@ -563,6 +560,14 @@ Deoptimizer::Deoptimizer(Isolate* isolate, } } compiled_code_ = FindOptimizedCode(function, optimized_code); + +#if DEBUG + ASSERT(compiled_code_ != NULL); + if (type == EAGER || type == SOFT || type == LAZY) { + ASSERT(compiled_code_->kind() != Code::FUNCTION); + } +#endif + StackFrame::Type frame_type = function == NULL ? StackFrame::STUB : StackFrame::JAVA_SCRIPT; @@ -588,15 +593,6 @@ Code* Deoptimizer::FindOptimizedCode(JSFunction* function, ? static_cast(isolate_->FindCodeObject(from_)) : compiled_code; } - case Deoptimizer::OSR: { - // The function has already been optimized and we're transitioning - // from the unoptimized shared version to the optimized one in the - // function. The return address (from_) points to unoptimized code. - Code* compiled_code = function->code(); - ASSERT(compiled_code->kind() == Code::OPTIMIZED_FUNCTION); - ASSERT(!compiled_code->contains(from_)); - return compiled_code; - } case Deoptimizer::DEBUGGER: ASSERT(optimized_code->contains(from_)); return optimized_code; @@ -720,11 +716,6 @@ int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) { // We rely on this function not causing a GC. It is called from generated code // without having a real stack frame in place. void Deoptimizer::DoComputeOutputFrames() { - if (bailout_type_ == OSR) { - DoComputeOsrOutputFrame(); - return; - } - // Print some helpful diagnostic information. if (FLAG_log_timer_events && compiled_code_->kind() == Code::OPTIMIZED_FUNCTION) { @@ -2346,192 +2337,6 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, } -bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, - int* input_offset) { - disasm::NameConverter converter; - FrameDescription* output = output_[0]; - - // The input values are all part of the unoptimized frame so they - // are all tagged pointers. - uintptr_t input_value = input_->GetFrameSlot(*input_offset); - Object* input_object = reinterpret_cast(input_value); - - Translation::Opcode opcode = - static_cast(iterator->Next()); - - switch (opcode) { - case Translation::BEGIN: - case Translation::JS_FRAME: - case Translation::ARGUMENTS_ADAPTOR_FRAME: - case Translation::CONSTRUCT_STUB_FRAME: - case Translation::GETTER_STUB_FRAME: - case Translation::SETTER_STUB_FRAME: - case Translation::COMPILED_STUB_FRAME: - UNREACHABLE(); // Malformed input. - return false; - - case Translation::REGISTER: { - int output_reg = iterator->Next(); - if (FLAG_trace_osr) { - PrintF(" %s <- 0x%08" V8PRIxPTR " ; [sp + %d]\n", - converter.NameOfCPURegister(output_reg), - input_value, - *input_offset); - } - output->SetRegister(output_reg, input_value); - break; - } - - case Translation::INT32_REGISTER: { - int32_t int32_value = 0; - if (!input_object->ToInt32(&int32_value)) return false; - - int output_reg = iterator->Next(); - if (FLAG_trace_osr) { - PrintF(" %s <- %d (int32) ; [sp + %d]\n", - converter.NameOfCPURegister(output_reg), - int32_value, - *input_offset); - } - output->SetRegister(output_reg, int32_value); - break; - } - - case Translation::UINT32_REGISTER: { - uint32_t uint32_value = 0; - if (!input_object->ToUint32(&uint32_value)) return false; - - int output_reg = iterator->Next(); - if (FLAG_trace_osr) { - PrintF(" %s <- %u (uint32) ; [sp + %d]\n", - converter.NameOfCPURegister(output_reg), - uint32_value, - *input_offset); - } - output->SetRegister(output_reg, static_cast(uint32_value)); - } - - - case Translation::DOUBLE_REGISTER: { - // Abort OSR if we don't have a number. - if (!input_object->IsNumber()) return false; - - int output_reg = iterator->Next(); - double double_value = input_object->Number(); - if (FLAG_trace_osr) { - PrintF(" %s <- %g (double) ; [sp + %d]\n", - DoubleRegister::AllocationIndexToString(output_reg), - double_value, - *input_offset); - } - output->SetDoubleRegister(output_reg, double_value); - break; - } - - case Translation::STACK_SLOT: { - int output_index = iterator->Next(); - unsigned output_offset = - output->GetOffsetFromSlotIndex(output_index); - if (FLAG_trace_osr) { - PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ", - output_offset, - input_value, - *input_offset); - reinterpret_cast(input_value)->ShortPrint(); - PrintF("\n"); - } - output->SetFrameSlot(output_offset, input_value); - break; - } - - case Translation::INT32_STACK_SLOT: { - int32_t int32_value = 0; - if (!input_object->ToInt32(&int32_value)) return false; - - int output_index = iterator->Next(); - unsigned output_offset = - output->GetOffsetFromSlotIndex(output_index); - if (FLAG_trace_osr) { - PrintF(" [sp + %d] <- %d (int32) ; [sp + %d]\n", - output_offset, - int32_value, - *input_offset); - } - output->SetFrameSlot(output_offset, int32_value); - break; - } - - case Translation::UINT32_STACK_SLOT: { - uint32_t uint32_value = 0; - if (!input_object->ToUint32(&uint32_value)) return false; - - int output_index = iterator->Next(); - unsigned output_offset = - output->GetOffsetFromSlotIndex(output_index); - if (FLAG_trace_osr) { - PrintF(" [sp + %d] <- %u (uint32) ; [sp + %d]\n", - output_offset, - uint32_value, - *input_offset); - } - output->SetFrameSlot(output_offset, static_cast(uint32_value)); - break; - } - - case Translation::DOUBLE_STACK_SLOT: { - static const int kLowerOffset = 0 * kPointerSize; - static const int kUpperOffset = 1 * kPointerSize; - - // Abort OSR if we don't have a number. - if (!input_object->IsNumber()) return false; - - int output_index = iterator->Next(); - unsigned output_offset = - output->GetOffsetFromSlotIndex(output_index); - double double_value = input_object->Number(); - uint64_t int_value = BitCast(double_value); - int32_t lower = static_cast(int_value); - int32_t upper = static_cast(int_value >> kBitsPerInt); - if (FLAG_trace_osr) { - PrintF(" [sp + %d] <- 0x%08x (upper bits of %g) ; [sp + %d]\n", - output_offset + kUpperOffset, - upper, - double_value, - *input_offset); - PrintF(" [sp + %d] <- 0x%08x (lower bits of %g) ; [sp + %d]\n", - output_offset + kLowerOffset, - lower, - double_value, - *input_offset); - } - output->SetFrameSlot(output_offset + kLowerOffset, lower); - output->SetFrameSlot(output_offset + kUpperOffset, upper); - break; - } - - case Translation::LITERAL: { - // Just ignore non-materialized literals. - iterator->Next(); - break; - } - - case Translation::DUPLICATED_OBJECT: - case Translation::ARGUMENTS_OBJECT: - case Translation::CAPTURED_OBJECT: { - // Optimized code assumes that the argument object has not been - // materialized and so bypasses it when doing arguments access. - // We should have bailed out before starting the frame - // translation. - UNREACHABLE(); - return false; - } - } - - *input_offset -= kPointerSize; - return true; -} - - void Deoptimizer::PatchInterruptCode(Isolate* isolate, Code* unoptimized) { DisallowHeapAllocation no_gc; @@ -2617,12 +2422,7 @@ unsigned Deoptimizer::ComputeInputFrameSize() const { // into account so we have to avoid double counting them (-2). unsigned result = fixed_size + fp_to_sp_delta_ - (2 * kPointerSize); #ifdef DEBUG - if (bailout_type_ == OSR) { - // TODO(kasperl): It would be nice if we could verify that the - // size matches with the stack height we can compute based on the - // environment at the OSR entry. The code for that his built into - // the DoComputeOsrOutputFrame function for now. - } else if (compiled_code_->kind() == Code::OPTIMIZED_FUNCTION) { + if (compiled_code_->kind() == Code::OPTIMIZED_FUNCTION) { unsigned stack_slots = compiled_code_->stack_slots(); unsigned outgoing_size = ComputeOutgoingArgumentSize(); ASSERT(result == fixed_size + (stack_slots * kPointerSize) + outgoing_size); diff --git a/src/deoptimizer.h b/src/deoptimizer.h index c28b7b9..7ee5908 100644 --- a/src/deoptimizer.h +++ b/src/deoptimizer.h @@ -126,7 +126,6 @@ class Deoptimizer : public Malloced { EAGER, LAZY, SOFT, - OSR, // This last bailout type is not really a bailout, but used by the // debugger to deoptimize stack frames to allow inspection. DEBUGGER @@ -356,7 +355,6 @@ class Deoptimizer : public Malloced { void DeleteFrameDescriptions(); void DoComputeOutputFrames(); - void DoComputeOsrOutputFrame(); void DoComputeJSFrame(TranslationIterator* iterator, int frame_index); void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, int frame_index); @@ -382,13 +380,6 @@ class Deoptimizer : public Malloced { unsigned output_offset, DeoptimizerTranslatedValueType value_type = TRANSLATED_VALUE_IS_TAGGED); - // Translate a command for OSR. Updates the input offset to be used for - // the next command. Returns false if translation of the command failed - // (e.g., a number conversion failed) and may or may not have updated the - // input offset. - bool DoOsrTranslateCommand(TranslationIterator* iterator, - int* input_offset); - unsigned ComputeInputFrameSize() const; unsigned ComputeFixedSize(JSFunction* function) const; diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 2855e8a..4c2e308 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1491,6 +1491,15 @@ void HCallStub::PrintDataTo(StringStream* stream) { } +void HUnknownOSRValue::PrintDataTo(StringStream *stream) { + const char* type = "expression"; + if (environment_->is_local_index(index_)) type = "local"; + if (environment_->is_special_index(index_)) type = "special"; + if (environment_->is_parameter_index(index_)) type = "parameter"; + stream->Add("%s @ %d", type, index_); +} + + void HInstanceOf::PrintDataTo(StringStream* stream) { left()->PrintNameTo(stream); stream->Add(" "); diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 2bfb284..2ab6c89 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -4988,19 +4988,18 @@ class HCallStub V8_FINAL : public HUnaryCall { class HUnknownOSRValue V8_FINAL : public HTemplateInstruction<0> { public: - DECLARE_INSTRUCTION_FACTORY_P0(HUnknownOSRValue) + DECLARE_INSTRUCTION_FACTORY_P2(HUnknownOSRValue, HEnvironment*, int); + + virtual void PrintDataTo(StringStream* stream); virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE { return Representation::None(); } - void set_incoming_value(HPhi* value) { - incoming_value_ = value; - } - - HPhi* incoming_value() { - return incoming_value_; - } + void set_incoming_value(HPhi* value) { incoming_value_ = value; } + HPhi* incoming_value() { return incoming_value_; } + HEnvironment *environment() { return environment_; } + int index() { return index_; } virtual Representation KnownOptimalRepresentation() V8_OVERRIDE { if (incoming_value_ == NULL) return Representation::None(); @@ -5010,11 +5009,15 @@ class HUnknownOSRValue V8_FINAL : public HTemplateInstruction<0> { DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue) private: - HUnknownOSRValue() - : incoming_value_(NULL) { + HUnknownOSRValue(HEnvironment* environment, int index) + : environment_(environment), + index_(index), + incoming_value_(NULL) { set_representation(Representation::Tagged()); } + HEnvironment* environment_; + int index_; HPhi* incoming_value_; }; diff --git a/src/hydrogen-osr.cc b/src/hydrogen-osr.cc index bf6233b..6b1df1e 100644 --- a/src/hydrogen-osr.cc +++ b/src/hydrogen-osr.cc @@ -80,7 +80,8 @@ HBasicBlock* HOsrBuilder::BuildPossibleOsrLoopEntry( osr_values_ = new(zone) ZoneList(length, zone); for (int i = 0; i < first_expression_index; ++i) { - HUnknownOSRValue* osr_value = builder_->Add(); + HUnknownOSRValue* osr_value + = builder_->Add(environment, i); environment->Bind(i, osr_value); osr_values_->Add(osr_value, zone); } @@ -88,12 +89,21 @@ HBasicBlock* HOsrBuilder::BuildPossibleOsrLoopEntry( if (first_expression_index != length) { environment->Drop(length - first_expression_index); for (int i = first_expression_index; i < length; ++i) { - HUnknownOSRValue* osr_value = builder_->Add(); + HUnknownOSRValue* osr_value + = builder_->Add(environment, i); environment->Push(osr_value); osr_values_->Add(osr_value, zone); } } + unoptimized_frame_slots_ = + environment->local_count() + environment->push_count(); + + // Keep a copy of the old environment, since the OSR values need it + // to figure out where exactly they are located in the unoptimized frame. + environment = environment->Copy(); + builder_->current_block()->UpdateEnvironment(environment); + builder_->Add(osr_entry_id); builder_->Add(osr_entry_id); HContext* context = builder_->Add(); diff --git a/src/hydrogen-osr.h b/src/hydrogen-osr.h index 0c6b65d..5014a75 100644 --- a/src/hydrogen-osr.h +++ b/src/hydrogen-osr.h @@ -40,7 +40,8 @@ namespace internal { class HOsrBuilder : public ZoneObject { public: explicit HOsrBuilder(HOptimizedGraphBuilder* builder) - : builder_(builder), + : unoptimized_frame_slots_(0), + builder_(builder), osr_entry_(NULL), osr_loop_entry_(NULL), osr_values_(NULL) { } @@ -55,10 +56,16 @@ class HOsrBuilder : public ZoneObject { // Process the OSR values and phis after initial graph optimization. void FinishOsrValues(); + // Return the number of slots in the unoptimized frame at the entry to OSR. + int UnoptimizedFrameSlots() const { + return unoptimized_frame_slots_; + } + private: HBasicBlock* BuildLoopEntry(); bool HasOsrEntryAt(IterationStatement* statement); + int unoptimized_frame_slots_; HOptimizedGraphBuilder* builder_; HBasicBlock* osr_entry_; HBasicBlock* osr_loop_entry_; diff --git a/src/hydrogen.h b/src/hydrogen.h index 395d1cd..979a350 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -553,9 +553,6 @@ class HEnvironment V8_FINAL : public ZoneObject { void set_entry(HEnterInlined* entry) { entry_ = entry; } int length() const { return values_.length(); } - bool is_special_index(int i) const { - return i >= parameter_count() && i < parameter_count() + specials_count(); - } int first_expression_index() const { return parameter_count() + specials_count() + local_count(); @@ -674,8 +671,15 @@ class HEnvironment V8_FINAL : public ZoneObject { } bool is_local_index(int i) const { - return i >= first_local_index() && - i < first_expression_index(); + return i >= first_local_index() && i < first_expression_index(); + } + + bool is_parameter_index(int i) const { + return i >= 0 && i < parameter_count(); + } + + bool is_special_index(int i) const { + return i >= parameter_count() && i < parameter_count() + specials_count(); } void PrintTo(StringStream* stream); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index c02726b..a617da7 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -1337,22 +1337,30 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); } - // If the result was -1 it means that we couldn't optimize the - // function. Just return and continue in the unoptimized version. Label skip; - __ cmp(eax, Immediate(Smi::FromInt(-1))); + // If the code object is null, just return to the unoptimized code. + __ cmp(eax, Immediate(0)); __ j(not_equal, &skip, Label::kNear); __ ret(0); __ bind(&skip); - // Untag the AST id and push it on the stack. - __ SmiUntag(eax); - __ push(eax); - - // Generate the code for doing the frame-to-frame translation using - // the deoptimizer infrastructure. - Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR); - generator.Generate(); + + // Load deoptimization data from the code object. + __ mov(ebx, Operand(eax, Code::kDeoptimizationDataOffset - kHeapObjectTag)); + + // Load the OSR entrypoint offset from the deoptimization data. + __ mov(ebx, Operand(ebx, FixedArray::OffsetOfElementAt( + DeoptimizationInputData::kOsrPcOffsetIndex) - kHeapObjectTag)); + __ SmiUntag(ebx); + + // Compute the target address = code_obj + header_size + osr_offset + __ lea(eax, Operand(eax, ebx, times_1, Code::kHeaderSize - kHeapObjectTag)); + + // Overwrite the return address on the stack. + __ mov(Operand(esp, 0), eax); + + // And "return" to the OSR entry point of the function. + __ ret(0); } diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc index a4f7ee8..13a70af 100644 --- a/src/ia32/deoptimizer-ia32.cc +++ b/src/ia32/deoptimizer-ia32.cc @@ -258,192 +258,6 @@ Deoptimizer::InterruptPatchState Deoptimizer::GetInterruptPatchState( #endif // DEBUG -static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) { - ByteArray* translations = data->TranslationByteArray(); - int length = data->DeoptCount(); - for (int i = 0; i < length; i++) { - if (data->AstId(i) == ast_id) { - TranslationIterator it(translations, data->TranslationIndex(i)->value()); - int value = it.Next(); - ASSERT(Translation::BEGIN == static_cast(value)); - // Read the number of frames. - value = it.Next(); - if (value == 1) return i; - } - } - UNREACHABLE(); - return -1; -} - - -void Deoptimizer::DoComputeOsrOutputFrame() { - DeoptimizationInputData* data = DeoptimizationInputData::cast( - compiled_code_->deoptimization_data()); - unsigned ast_id = data->OsrAstId()->value(); - // TODO(kasperl): This should not be the bailout_id_. It should be - // the ast id. Confusing. - ASSERT(bailout_id_ == ast_id); - - int bailout_id = LookupBailoutId(data, BailoutId(ast_id)); - unsigned translation_index = data->TranslationIndex(bailout_id)->value(); - ByteArray* translations = data->TranslationByteArray(); - - TranslationIterator iterator(translations, translation_index); - Translation::Opcode opcode = - static_cast(iterator.Next()); - ASSERT(Translation::BEGIN == opcode); - USE(opcode); - int count = iterator.Next(); - iterator.Next(); // Drop JS frames count. - ASSERT(count == 1); - USE(count); - - opcode = static_cast(iterator.Next()); - USE(opcode); - ASSERT(Translation::JS_FRAME == opcode); - unsigned node_id = iterator.Next(); - USE(node_id); - ASSERT(node_id == ast_id); - int closure_id = iterator.Next(); - USE(closure_id); - ASSERT_EQ(Translation::kSelfLiteralId, closure_id); - unsigned height = iterator.Next(); - unsigned height_in_bytes = height * kPointerSize; - USE(height_in_bytes); - - unsigned fixed_size = ComputeFixedSize(function_); - unsigned input_frame_size = input_->GetFrameSize(); - ASSERT(fixed_size + height_in_bytes == input_frame_size); - - unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize; - unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value(); - unsigned outgoing_size = outgoing_height * kPointerSize; - unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size; - ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call. - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => node=%u, frame=%d->%d, ebp:esp=0x%08x:0x%08x]\n", - ast_id, - input_frame_size, - output_frame_size, - input_->GetRegister(ebp.code()), - input_->GetRegister(esp.code())); - } - - // There's only one output frame in the OSR case. - output_count_ = 1; - output_ = new FrameDescription*[1]; - output_[0] = new(output_frame_size) FrameDescription( - output_frame_size, function_); - output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); - - // Clear the incoming parameters in the optimized frame to avoid - // confusing the garbage collector. - unsigned output_offset = output_frame_size - kPointerSize; - int parameter_count = function_->shared()->formal_parameter_count() + 1; - for (int i = 0; i < parameter_count; ++i) { - output_[0]->SetFrameSlot(output_offset, 0); - output_offset -= kPointerSize; - } - - // Translate the incoming parameters. This may overwrite some of the - // incoming argument slots we've just cleared. - int input_offset = input_frame_size - kPointerSize; - bool ok = true; - int limit = input_offset - (parameter_count * kPointerSize); - while (ok && input_offset > limit) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // There are no translation commands for the caller's pc and fp, the - // context, and the function. Set them up explicitly. - for (int i = StandardFrameConstants::kCallerPCOffset; - ok && i >= StandardFrameConstants::kMarkerOffset; - i -= kPointerSize) { - uint32_t input_value = input_->GetFrameSlot(input_offset); - if (FLAG_trace_osr) { - const char* name = "UNKNOWN"; - switch (i) { - case StandardFrameConstants::kCallerPCOffset: - name = "caller's pc"; - break; - case StandardFrameConstants::kCallerFPOffset: - name = "fp"; - break; - case StandardFrameConstants::kContextOffset: - name = "context"; - break; - case StandardFrameConstants::kMarkerOffset: - name = "function"; - break; - } - PrintF(" [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n", - output_offset, - input_value, - input_offset, - name); - } - output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset)); - input_offset -= kPointerSize; - output_offset -= kPointerSize; - } - - // All OSR stack frames are dynamically aligned to an 8-byte boundary. - int frame_pointer = input_->GetRegister(ebp.code()); - if ((frame_pointer & kPointerSize) != 0) { - frame_pointer -= kPointerSize; - has_alignment_padding_ = 1; - } - - int32_t alignment_state = (has_alignment_padding_ == 1) ? - kAlignmentPaddingPushed : - kNoAlignmentPadding; - if (FLAG_trace_osr) { - PrintF(" [sp + %d] <- 0x%08x ; (alignment state)\n", - output_offset, - alignment_state); - } - output_[0]->SetFrameSlot(output_offset, alignment_state); - output_offset -= kPointerSize; - - // Translate the rest of the frame. - while (ok && input_offset >= 0) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // If translation of any command failed, continue using the input frame. - if (!ok) { - delete output_[0]; - output_[0] = input_; - output_[0]->SetPc(reinterpret_cast(from_)); - } else { - // Set up the frame pointer and the context pointer. - output_[0]->SetRegister(ebp.code(), frame_pointer); - output_[0]->SetRegister(esi.code(), input_->GetRegister(esi.code())); - - unsigned pc_offset = data->OsrPcOffset()->value(); - uint32_t pc = reinterpret_cast( - compiled_code_->entry() + pc_offset); - output_[0]->SetPc(pc); - } - Code* continuation = - function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR); - output_[0]->SetContinuation( - reinterpret_cast(continuation->entry())); - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", - ok ? "finished" : "aborted", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => pc=0x%0x]\n", output_[0]->GetPc()); - } -} - - void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { // Set the register values. The values are not important as there are no // callee saved registers in JavaScript frames, so all registers are @@ -611,27 +425,17 @@ void Deoptimizer::EntryGenerator::Generate() { } __ pop(eax); - if (type() != OSR) { - // If frame was dynamically aligned, pop padding. - Label no_padding; - __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()), - Immediate(0)); - __ j(equal, &no_padding); - __ pop(ecx); - if (FLAG_debug_code) { - __ cmp(ecx, Immediate(kAlignmentZapValue)); - __ Assert(equal, kAlignmentMarkerExpected); - } - __ bind(&no_padding); - } else { - // If frame needs dynamic alignment push padding. - Label no_padding; - __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()), - Immediate(0)); - __ j(equal, &no_padding); - __ push(Immediate(kAlignmentZapValue)); - __ bind(&no_padding); + // If frame was dynamically aligned, pop padding. + Label no_padding; + __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()), + Immediate(0)); + __ j(equal, &no_padding); + __ pop(ecx); + if (FLAG_debug_code) { + __ cmp(ecx, Immediate(kAlignmentZapValue)); + __ Assert(equal, kAlignmentMarkerExpected); } + __ bind(&no_padding); // Replace the current frame with the output frames. Label outer_push_loop, inner_push_loop, @@ -658,7 +462,7 @@ void Deoptimizer::EntryGenerator::Generate() { __ cmp(eax, edx); __ j(below, &outer_push_loop); - // In case of OSR or a failed STUB, we have to restore the XMM registers. + // In case of a failed STUB, we have to restore the XMM registers. if (CpuFeatures::IsSupported(SSE2)) { CpuFeatureScope scope(masm(), SSE2); for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) { @@ -669,9 +473,7 @@ void Deoptimizer::EntryGenerator::Generate() { } // Push state, pc, and continuation from the last output frame. - if (type() != OSR) { - __ push(Operand(ebx, FrameDescription::state_offset())); - } + __ push(Operand(ebx, FrameDescription::state_offset())); __ push(Operand(ebx, FrameDescription::pc_offset())); __ push(Operand(ebx, FrameDescription::continuation_offset())); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 32c9f00..b3b9813 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -35,6 +35,7 @@ #include "deoptimizer.h" #include "stub-cache.h" #include "codegen.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -332,6 +333,28 @@ bool LCodeGen::GeneratePrologue() { } +void LCodeGen::GenerateOsrPrologue() { + // Generate the OSR entry prologue at the first unknown OSR value, or if there + // are none, at the OSR entrypoint instruction. + if (osr_pc_offset_ >= 0) return; + + osr_pc_offset_ = masm()->pc_offset(); + + // Save the first local, which is overwritten by the alignment state. + Operand alignment_loc = MemOperand(ebp, -3 * kPointerSize); + __ push(alignment_loc); + + // Set the dynamic frame alignment state to "not aligned". + __ mov(alignment_loc, Immediate(kNoAlignmentPadding)); + + // Adjust the frame size, subsuming the unoptimized frame into the + // optimized frame. + int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots(); + ASSERT(slots >= 1); + __ sub(esp, Immediate((slots - 1) * kPointerSize)); +} + + bool LCodeGen::GenerateBody() { ASSERT(is_generating()); bool emit_instructions = true; @@ -1317,8 +1340,7 @@ void LCodeGen::DoCallStub(LCallStub* instr) { void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { - // Record the address of the first unknown OSR value as the place to enter. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } @@ -6214,9 +6236,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { ASSERT(!environment->HasBeenRegistered()); RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); - // Normally we record the first unknown OSR value as the entrypoint to the OSR - // code, but if there were none, record the entrypoint here. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index 23b2e48..f684b6d 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -233,6 +233,9 @@ class LCodeGen V8_FINAL BASE_EMBEDDED { bool GenerateJumpTable(); bool GenerateSafepointTable(); + // Generates the custom OSR entrypoint and sets the osr_pc_offset. + void GenerateOsrPrologue(); + enum SafepointMode { RECORD_SIMPLE_SAFEPOINT, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 8d66085..07dbf13 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -32,6 +32,7 @@ #include "lithium-allocator-inl.h" #include "ia32/lithium-ia32.h" #include "ia32/lithium-codegen-ia32.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -483,6 +484,14 @@ LPlatformChunk* LChunkBuilder::Build() { USE(alignment_state_index); } + // If compiling for OSR, reserve space for the unoptimized frame, + // which will be subsumed into this frame. + if (graph()->has_osr()) { + for (int i = graph()->osr()->UnoptimizedFrameSlots(); i > 0; i--) { + chunk_->GetNextSpillIndex(false); + } + } + const ZoneList* blocks = graph()->blocks(); for (int i = 0; i < blocks->length(); i++) { HBasicBlock* next = NULL; @@ -2537,10 +2546,23 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { - int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width. - if (spill_index > LUnallocated::kMaxFixedSlotIndex) { - Abort(kTooManySpillSlotsNeededForOSR); - spill_index = 0; + // Use an index that corresponds to the location in the unoptimized frame, + // which the optimized frame will subsume. + int env_index = instr->index(); + int spill_index = 0; + if (instr->environment()->is_parameter_index(env_index)) { + spill_index = chunk()->GetParameterStackSlot(env_index); + } else { + spill_index = env_index - instr->environment()->first_local_index(); + if (spill_index > LUnallocated::kMaxFixedSlotIndex) { + Abort(kNotEnoughSpillSlotsForOsr); + spill_index = 0; + } + if (spill_index == 0) { + // The dynamic frame alignment state overwrites the first local. + // The first local is saved at the end of the unoptimized frame. + spill_index = graph()->osr()->UnoptimizedFrameSlots(); + } } return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index); } diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index f3c219e..1a5b302 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -1009,18 +1009,26 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); } - // If the result was -1 it means that we couldn't optimize the - // function. Just return and continue in the unoptimized version. - __ Ret(eq, v0, Operand(Smi::FromInt(-1))); - - // Untag the AST id and push it on the stack. - __ SmiUntag(v0); - __ push(v0); - - // Generate the code for doing the frame-to-frame translation using - // the deoptimizer infrastructure. - Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR); - generator.Generate(); + // If the code object is null, just return to the unoptimized code. + __ Ret(eq, v0, Operand(Smi::FromInt(0))); + + // Load deoptimization data from the code object. + // = [#deoptimization_data_offset] + __ lw(a1, MemOperand(v0, Code::kDeoptimizationDataOffset - kHeapObjectTag)); + + // Load the OSR entrypoint offset from the deoptimization data. + // = [#header_size + #osr_pc_offset] + __ lw(a1, MemOperand(a1, FixedArray::OffsetOfElementAt( + DeoptimizationInputData::kOsrPcOffsetIndex) - kHeapObjectTag)); + __ SmiUntag(a1); + + // Compute the target address = code_obj + header_size + osr_offset + // = + #header_size + + __ addu(v0, v0, a1); + __ addiu(ra, v0, Code::kHeaderSize - kHeapObjectTag); + + // And "return" to the OSR entry point of the function. + __ Ret(); } diff --git a/src/mips/deoptimizer-mips.cc b/src/mips/deoptimizer-mips.cc index bed6e12..16f75b8 100644 --- a/src/mips/deoptimizer-mips.cc +++ b/src/mips/deoptimizer-mips.cc @@ -160,169 +160,6 @@ Deoptimizer::InterruptPatchState Deoptimizer::GetInterruptPatchState( #endif // DEBUG -static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) { - ByteArray* translations = data->TranslationByteArray(); - int length = data->DeoptCount(); - for (int i = 0; i < length; i++) { - if (data->AstId(i) == ast_id) { - TranslationIterator it(translations, data->TranslationIndex(i)->value()); - int value = it.Next(); - ASSERT(Translation::BEGIN == static_cast(value)); - // Read the number of frames. - value = it.Next(); - if (value == 1) return i; - } - } - UNREACHABLE(); - return -1; -} - - -void Deoptimizer::DoComputeOsrOutputFrame() { - DeoptimizationInputData* data = DeoptimizationInputData::cast( - compiled_code_->deoptimization_data()); - unsigned ast_id = data->OsrAstId()->value(); - - int bailout_id = LookupBailoutId(data, BailoutId(ast_id)); - unsigned translation_index = data->TranslationIndex(bailout_id)->value(); - ByteArray* translations = data->TranslationByteArray(); - - TranslationIterator iterator(translations, translation_index); - Translation::Opcode opcode = - static_cast(iterator.Next()); - ASSERT(Translation::BEGIN == opcode); - USE(opcode); - int count = iterator.Next(); - iterator.Skip(1); // Drop JS frame count. - ASSERT(count == 1); - USE(count); - - opcode = static_cast(iterator.Next()); - USE(opcode); - ASSERT(Translation::JS_FRAME == opcode); - unsigned node_id = iterator.Next(); - USE(node_id); - ASSERT(node_id == ast_id); - int closure_id = iterator.Next(); - USE(closure_id); - ASSERT_EQ(Translation::kSelfLiteralId, closure_id); - unsigned height = iterator.Next(); - unsigned height_in_bytes = height * kPointerSize; - USE(height_in_bytes); - - unsigned fixed_size = ComputeFixedSize(function_); - unsigned input_frame_size = input_->GetFrameSize(); - ASSERT(fixed_size + height_in_bytes == input_frame_size); - - unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize; - unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value(); - unsigned outgoing_size = outgoing_height * kPointerSize; - unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size; - ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call. - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => node=%u, frame=%d->%d]\n", - ast_id, - input_frame_size, - output_frame_size); - } - - // There's only one output frame in the OSR case. - output_count_ = 1; - output_ = new FrameDescription*[1]; - output_[0] = new(output_frame_size) FrameDescription( - output_frame_size, function_); - output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); - - // Clear the incoming parameters in the optimized frame to avoid - // confusing the garbage collector. - unsigned output_offset = output_frame_size - kPointerSize; - int parameter_count = function_->shared()->formal_parameter_count() + 1; - for (int i = 0; i < parameter_count; ++i) { - output_[0]->SetFrameSlot(output_offset, 0); - output_offset -= kPointerSize; - } - - // Translate the incoming parameters. This may overwrite some of the - // incoming argument slots we've just cleared. - int input_offset = input_frame_size - kPointerSize; - bool ok = true; - int limit = input_offset - (parameter_count * kPointerSize); - while (ok && input_offset > limit) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // There are no translation commands for the caller's pc and fp, the - // context, and the function. Set them up explicitly. - for (int i = StandardFrameConstants::kCallerPCOffset; - ok && i >= StandardFrameConstants::kMarkerOffset; - i -= kPointerSize) { - uint32_t input_value = input_->GetFrameSlot(input_offset); - if (FLAG_trace_osr) { - const char* name = "UNKNOWN"; - switch (i) { - case StandardFrameConstants::kCallerPCOffset: - name = "caller's pc"; - break; - case StandardFrameConstants::kCallerFPOffset: - name = "fp"; - break; - case StandardFrameConstants::kContextOffset: - name = "context"; - break; - case StandardFrameConstants::kMarkerOffset: - name = "function"; - break; - } - PrintF(" [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n", - output_offset, - input_value, - input_offset, - name); - } - - output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset)); - input_offset -= kPointerSize; - output_offset -= kPointerSize; - } - - // Translate the rest of the frame. - while (ok && input_offset >= 0) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // If translation of any command failed, continue using the input frame. - if (!ok) { - delete output_[0]; - output_[0] = input_; - output_[0]->SetPc(reinterpret_cast(from_)); - } else { - // Set up the frame pointer and the context pointer. - output_[0]->SetRegister(fp.code(), input_->GetRegister(fp.code())); - output_[0]->SetRegister(cp.code(), input_->GetRegister(cp.code())); - - unsigned pc_offset = data->OsrPcOffset()->value(); - uint32_t pc = reinterpret_cast( - compiled_code_->entry() + pc_offset); - output_[0]->SetPc(pc); - } - Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR); - output_[0]->SetContinuation( - reinterpret_cast(continuation->entry())); - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", - ok ? "finished" : "aborted", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => pc=0x%0x]\n", output_[0]->GetPc()); - } -} - - void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { // Set the register values. The values are not important as there are no // callee saved registers in JavaScript frames, so all registers are @@ -537,10 +374,8 @@ void Deoptimizer::EntryGenerator::Generate() { } // Push state, pc, and continuation from the last output frame. - if (type() != OSR) { - __ lw(t2, MemOperand(a2, FrameDescription::state_offset())); - __ push(t2); - } + __ lw(t2, MemOperand(a2, FrameDescription::state_offset())); + __ push(t2); __ lw(t2, MemOperand(a2, FrameDescription::pc_offset())); __ push(t2); diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index d346428..ab713a3 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -31,6 +31,7 @@ #include "mips/lithium-gap-resolver-mips.h" #include "code-stubs.h" #include "stub-cache.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -247,6 +248,21 @@ bool LCodeGen::GeneratePrologue() { } +void LCodeGen::GenerateOsrPrologue() { + // Generate the OSR entry prologue at the first unknown OSR value, or if there + // are none, at the OSR entrypoint instruction. + if (osr_pc_offset_ >= 0) return; + + osr_pc_offset_ = masm()->pc_offset(); + + // Adjust the frame size, subsuming the unoptimized frame into the + // optimized frame. + int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots(); + ASSERT(slots >= 0); + __ Subu(sp, sp, Operand(slots * kPointerSize)); +} + + bool LCodeGen::GenerateBody() { ASSERT(is_generating()); bool emit_instructions = true; @@ -1071,8 +1087,7 @@ void LCodeGen::DoCallStub(LCallStub* instr) { void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { - // Record the address of the first unknown OSR value as the place to enter. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } @@ -5681,9 +5696,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { ASSERT(!environment->HasBeenRegistered()); RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); - // Normally we record the first unknown OSR value as the entrypoint to the OSR - // code, but if there were none, record the entrypoint here. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } diff --git a/src/mips/lithium-codegen-mips.h b/src/mips/lithium-codegen-mips.h index 13b5804..8437435 100644 --- a/src/mips/lithium-codegen-mips.h +++ b/src/mips/lithium-codegen-mips.h @@ -227,6 +227,9 @@ class LCodeGen V8_FINAL BASE_EMBEDDED { bool GenerateDeoptJumpTable(); bool GenerateSafepointTable(); + // Generates the custom OSR entrypoint and sets the osr_pc_offset. + void GenerateOsrPrologue(); + enum SafepointMode { RECORD_SIMPLE_SAFEPOINT, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index 695a058..9476f4d 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -30,6 +30,7 @@ #include "lithium-allocator-inl.h" #include "mips/lithium-mips.h" #include "mips/lithium-codegen-mips.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -438,6 +439,15 @@ LPlatformChunk* LChunkBuilder::Build() { chunk_ = new(zone()) LPlatformChunk(info(), graph()); LPhase phase("L_Building chunk", chunk_); status_ = BUILDING; + + // If compiling for OSR, reserve space for the unoptimized frame, + // which will be subsumed into this frame. + if (graph()->has_osr()) { + for (int i = graph()->osr()->UnoptimizedFrameSlots(); i > 0; i--) { + chunk_->GetNextSpillIndex(false); + } + } + const ZoneList* blocks = graph()->blocks(); for (int i = 0; i < blocks->length(); i++) { HBasicBlock* next = NULL; @@ -2341,10 +2351,18 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { - int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width. - if (spill_index > LUnallocated::kMaxFixedSlotIndex) { - Abort(kTooManySpillSlotsNeededForOSR); - spill_index = 0; + // Use an index that corresponds to the location in the unoptimized frame, + // which the optimized frame will subsume. + int env_index = instr->index(); + int spill_index = 0; + if (instr->environment()->is_parameter_index(env_index)) { + spill_index = chunk()->GetParameterStackSlot(env_index); + } else { + spill_index = env_index - instr->environment()->first_local_index(); + if (spill_index > LUnallocated::kMaxFixedSlotIndex) { + Abort(kTooManySpillSlotsNeededForOSR); + spill_index = 0; + } } return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index); } diff --git a/src/objects.cc b/src/objects.cc index 253405a..507fbfc 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -9492,11 +9492,25 @@ bool JSFunction::CompileLazy(Handle function, } +Handle JSFunction::CompileOsr(Handle function, + BailoutId osr_ast_id, + ClearExceptionFlag flag) { + CompilationInfoWithZone info(function); + info.SetOptimizing(osr_ast_id); + if (CompileLazyHelper(&info, flag)) { + // TODO(titzer): don't install the OSR code. + // ASSERT(function->code() != *info.code()); + return info.code(); + } else { + return Handle::null(); + } +} + + bool JSFunction::CompileOptimized(Handle function, - BailoutId osr_ast_id, ClearExceptionFlag flag) { CompilationInfoWithZone info(function); - info.SetOptimizing(osr_ast_id); + info.SetOptimizing(BailoutId::None()); return CompileLazyHelper(&info, flag); } diff --git a/src/objects.h b/src/objects.h index 82c4bf1..ccfc591 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1214,6 +1214,8 @@ class MaybeObject BASE_EMBEDDED { V(kNonSmiValue, "Non-smi value") \ V(kNotEnoughVirtualRegistersForValues, \ "not enough virtual registers for values") \ + V(kNotEnoughSpillSlotsForOsr, \ + "not enough spill slots for OSR") \ V(kNotEnoughVirtualRegistersRegalloc, \ "not enough virtual registers (regalloc)") \ V(kObjectFoundInSmiOnlyArray, "object found in smi-only array") \ @@ -6976,8 +6978,10 @@ class JSFunction: public JSObject { ClearExceptionFlag flag); static bool CompileLazy(Handle function, ClearExceptionFlag flag); + static Handle CompileOsr(Handle function, + BailoutId osr_ast_id, + ClearExceptionFlag flag); static bool CompileOptimized(Handle function, - BailoutId osr_ast_id, ClearExceptionFlag flag); // Tells whether or not the function is already marked for lazy diff --git a/src/runtime.cc b/src/runtime.cc index 1eff289..2c0cab0 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -8316,9 +8316,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { return function->code(); } function->shared()->code()->set_profiler_ticks(0); - if (JSFunction::CompileOptimized(function, - BailoutId::None(), - CLEAR_EXCEPTION)) { + if (JSFunction::CompileOptimized(function, CLEAR_EXCEPTION)) { return function->code(); } if (FLAG_trace_opt) { @@ -8413,6 +8411,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { JavaScriptFrame* frame = it.frame(); RUNTIME_ASSERT(frame->function()->IsJSFunction()); + ASSERT(frame->function() == *function); // Avoid doing too much work when running with --always-opt and keep // the optimized code around. @@ -8590,22 +8589,29 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) { // We're not prepared to handle a function with arguments object. ASSERT(!function->shared()->uses_arguments()); - // If the optimization attempt succeeded, return the AST id tagged as a - // smi. This tells the builtin that we need to translate the unoptimized - // frame to an optimized one. - BailoutId ast_id = + // If the optimization attempt succeeds, return the code object which + // the unoptimized code can jump into. + Handle code = (FLAG_concurrent_recompilation && FLAG_concurrent_osr) ? Compiler::CompileForConcurrentOSR(function) : Compiler::CompileForOnStackReplacement(function); - if (!ast_id.IsNone()) { - ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION); - return Smi::FromInt(ast_id.ToInt()); + if (!code.is_null()) { +#if DEBUG + ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); + DeoptimizationInputData* data = + DeoptimizationInputData::cast(code->deoptimization_data()); + ASSERT(!BailoutId(data->OsrAstId()->value()).IsNone()); +#endif + // TODO(titzer): this is a massive hack to make the deopt counts + // match. Fix heuristics for reenabling optimizations! + function->shared()->increment_deopt_count(); + return *code; } else { if (function->IsMarkedForLazyRecompilation() || function->IsMarkedForConcurrentRecompilation()) { function->ReplaceCode(function->shared()->code()); } - return Smi::FromInt(-1); + return NULL; } } diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 6c83dad..4730ed1 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -1418,22 +1418,29 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); } - // If the result was -1 it means that we couldn't optimize the - // function. Just return and continue in the unoptimized version. Label skip; - __ SmiCompare(rax, Smi::FromInt(-1)); + // If the code object is null, just return to the unoptimized code. + __ cmpq(rax, Immediate(0)); __ j(not_equal, &skip, Label::kNear); __ ret(0); __ bind(&skip); - // Untag the AST id and push it on the stack. - __ SmiToInteger32(rax, rax); - __ push(rax); - - // Generate the code for doing the frame-to-frame translation using - // the deoptimizer infrastructure. - Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR); - generator.Generate(); + + // Load deoptimization data from the code object. + __ movq(rbx, Operand(rax, Code::kDeoptimizationDataOffset - kHeapObjectTag)); + + // Load the OSR entrypoint offset from the deoptimization data. + __ SmiToInteger32(rbx, Operand(rbx, FixedArray::OffsetOfElementAt( + DeoptimizationInputData::kOsrPcOffsetIndex) - kHeapObjectTag)); + + // Compute the target address = code_obj + header_size + osr_offset + __ lea(rax, Operand(rax, rbx, times_1, Code::kHeaderSize - kHeapObjectTag)); + + // Overwrite the return address on the stack. + __ movq(Operand(rsp, 0), rax); + + // And "return" to the OSR entry point of the function. + __ ret(0); } diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc index bb8acce..303b756 100644 --- a/src/x64/deoptimizer-x64.cc +++ b/src/x64/deoptimizer-x64.cc @@ -163,173 +163,6 @@ Deoptimizer::InterruptPatchState Deoptimizer::GetInterruptPatchState( #endif // DEBUG -static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) { - ByteArray* translations = data->TranslationByteArray(); - int length = data->DeoptCount(); - for (int i = 0; i < length; i++) { - if (data->AstId(i) == ast_id) { - TranslationIterator it(translations, data->TranslationIndex(i)->value()); - int value = it.Next(); - ASSERT(Translation::BEGIN == static_cast(value)); - // Read the number of frames. - value = it.Next(); - if (value == 1) return i; - } - } - UNREACHABLE(); - return -1; -} - - -void Deoptimizer::DoComputeOsrOutputFrame() { - DeoptimizationInputData* data = DeoptimizationInputData::cast( - compiled_code_->deoptimization_data()); - unsigned ast_id = data->OsrAstId()->value(); - // TODO(kasperl): This should not be the bailout_id_. It should be - // the ast id. Confusing. - ASSERT(bailout_id_ == ast_id); - - int bailout_id = LookupBailoutId(data, BailoutId(ast_id)); - unsigned translation_index = data->TranslationIndex(bailout_id)->value(); - ByteArray* translations = data->TranslationByteArray(); - - TranslationIterator iterator(translations, translation_index); - Translation::Opcode opcode = - static_cast(iterator.Next()); - ASSERT(Translation::BEGIN == opcode); - USE(opcode); - int count = iterator.Next(); - iterator.Skip(1); // Drop JS frame count. - ASSERT(count == 1); - USE(count); - - opcode = static_cast(iterator.Next()); - USE(opcode); - ASSERT(Translation::JS_FRAME == opcode); - unsigned node_id = iterator.Next(); - USE(node_id); - ASSERT(node_id == ast_id); - int closure_id = iterator.Next(); - USE(closure_id); - ASSERT_EQ(Translation::kSelfLiteralId, closure_id); - unsigned height = iterator.Next(); - unsigned height_in_bytes = height * kPointerSize; - USE(height_in_bytes); - - unsigned fixed_size = ComputeFixedSize(function_); - unsigned input_frame_size = input_->GetFrameSize(); - ASSERT(fixed_size + height_in_bytes == input_frame_size); - - unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize; - unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value(); - unsigned outgoing_size = outgoing_height * kPointerSize; - unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size; - ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call. - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => node=%u, frame=%d->%d]\n", - ast_id, - input_frame_size, - output_frame_size); - } - - // There's only one output frame in the OSR case. - output_count_ = 1; - output_ = new FrameDescription*[1]; - output_[0] = new(output_frame_size) FrameDescription( - output_frame_size, function_); - output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); - - // Clear the incoming parameters in the optimized frame to avoid - // confusing the garbage collector. - unsigned output_offset = output_frame_size - kPointerSize; - int parameter_count = function_->shared()->formal_parameter_count() + 1; - for (int i = 0; i < parameter_count; ++i) { - output_[0]->SetFrameSlot(output_offset, 0); - output_offset -= kPointerSize; - } - - // Translate the incoming parameters. This may overwrite some of the - // incoming argument slots we've just cleared. - int input_offset = input_frame_size - kPointerSize; - bool ok = true; - int limit = input_offset - (parameter_count * kPointerSize); - while (ok && input_offset > limit) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // There are no translation commands for the caller's pc and fp, the - // context, and the function. Set them up explicitly. - for (int i = StandardFrameConstants::kCallerPCOffset; - ok && i >= StandardFrameConstants::kMarkerOffset; - i -= kPointerSize) { - intptr_t input_value = input_->GetFrameSlot(input_offset); - if (FLAG_trace_osr) { - const char* name = "UNKNOWN"; - switch (i) { - case StandardFrameConstants::kCallerPCOffset: - name = "caller's pc"; - break; - case StandardFrameConstants::kCallerFPOffset: - name = "fp"; - break; - case StandardFrameConstants::kContextOffset: - name = "context"; - break; - case StandardFrameConstants::kMarkerOffset: - name = "function"; - break; - } - PrintF(" [rsp + %d] <- 0x%08" V8PRIxPTR " ; [rsp + %d] " - "(fixed part - %s)\n", - output_offset, - input_value, - input_offset, - name); - } - output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset)); - input_offset -= kPointerSize; - output_offset -= kPointerSize; - } - - // Translate the rest of the frame. - while (ok && input_offset >= 0) { - ok = DoOsrTranslateCommand(&iterator, &input_offset); - } - - // If translation of any command failed, continue using the input frame. - if (!ok) { - delete output_[0]; - output_[0] = input_; - output_[0]->SetPc(reinterpret_cast(from_)); - } else { - // Set up the frame pointer and the context pointer. - output_[0]->SetRegister(rbp.code(), input_->GetRegister(rbp.code())); - output_[0]->SetRegister(rsi.code(), input_->GetRegister(rsi.code())); - - unsigned pc_offset = data->OsrPcOffset()->value(); - intptr_t pc = reinterpret_cast( - compiled_code_->entry() + pc_offset); - output_[0]->SetPc(pc); - } - Code* continuation = - function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR); - output_[0]->SetContinuation( - reinterpret_cast(continuation->entry())); - - if (FLAG_trace_osr) { - PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", - ok ? "finished" : "aborted", - reinterpret_cast(function_)); - PrintFunctionName(); - PrintF(" => pc=0x%0" V8PRIxPTR "]\n", output_[0]->GetPc()); - } -} - - void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { // Set the register values. The values are not important as there are no // callee saved registers in JavaScript frames, so all registers are @@ -526,9 +359,7 @@ void Deoptimizer::EntryGenerator::Generate() { } // Push state, pc, and continuation from the last output frame. - if (type() != OSR) { - __ push(Operand(rbx, FrameDescription::state_offset())); - } + __ push(Operand(rbx, FrameDescription::state_offset())); __ push(Operand(rbx, FrameDescription::pc_offset())); __ push(Operand(rbx, FrameDescription::continuation_offset())); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index f1430d7..f216019 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -32,6 +32,7 @@ #include "x64/lithium-codegen-x64.h" #include "code-stubs.h" #include "stub-cache.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -257,6 +258,21 @@ bool LCodeGen::GeneratePrologue() { } +void LCodeGen::GenerateOsrPrologue() { + // Generate the OSR entry prologue at the first unknown OSR value, or if there + // are none, at the OSR entrypoint instruction. + if (osr_pc_offset_ >= 0) return; + + osr_pc_offset_ = masm()->pc_offset(); + + // Adjust the frame size, subsuming the unoptimized frame into the + // optimized frame. + int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots(); + ASSERT(slots >= 0); + __ subq(rsp, Immediate(slots * kPointerSize)); +} + + bool LCodeGen::GenerateBody() { ASSERT(is_generating()); bool emit_instructions = true; @@ -979,8 +995,7 @@ void LCodeGen::DoCallStub(LCallStub* instr) { void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { - // Record the address of the first unknown OSR value as the place to enter. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } @@ -5457,9 +5472,7 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { ASSERT(!environment->HasBeenRegistered()); RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); - // Normally we record the first unknown OSR value as the entrypoint to the OSR - // code, but if there were none, record the entrypoint here. - if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset(); + GenerateOsrPrologue(); } diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index 65c6dca..ba2b260 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -191,6 +191,9 @@ class LCodeGen V8_FINAL BASE_EMBEDDED { bool GenerateJumpTable(); bool GenerateSafepointTable(); + // Generates the custom OSR entrypoint and sets the osr_pc_offset. + void GenerateOsrPrologue(); + enum SafepointMode { RECORD_SIMPLE_SAFEPOINT, RECORD_SAFEPOINT_WITH_REGISTERS diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 1b4332a..ac48b09 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -32,6 +32,7 @@ #include "lithium-allocator-inl.h" #include "x64/lithium-x64.h" #include "x64/lithium-codegen-x64.h" +#include "hydrogen-osr.h" namespace v8 { namespace internal { @@ -439,6 +440,15 @@ LPlatformChunk* LChunkBuilder::Build() { chunk_ = new(zone()) LPlatformChunk(info(), graph()); LPhase phase("L_Building chunk", chunk_); status_ = BUILDING; + + // If compiling for OSR, reserve space for the unoptimized frame, + // which will be subsumed into this frame. + if (graph()->has_osr()) { + for (int i = graph()->osr()->UnoptimizedFrameSlots(); i > 0; i--) { + chunk_->GetNextSpillIndex(false); + } + } + const ZoneList* blocks = graph()->blocks(); for (int i = 0; i < blocks->length(); i++) { HBasicBlock* next = NULL; @@ -2359,10 +2369,18 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { - int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width. - if (spill_index > LUnallocated::kMaxFixedSlotIndex) { - Abort(kTooManySpillSlotsNeededForOSR); - spill_index = 0; + // Use an index that corresponds to the location in the unoptimized frame, + // which the optimized frame will subsume. + int env_index = instr->index(); + int spill_index = 0; + if (instr->environment()->is_parameter_index(env_index)) { + spill_index = chunk()->GetParameterStackSlot(env_index); + } else { + spill_index = env_index - instr->environment()->first_local_index(); + if (spill_index > LUnallocated::kMaxFixedSlotIndex) { + Abort(kTooManySpillSlotsNeededForOSR); + spill_index = 0; + } } return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index); } diff --git a/test/mjsunit/compiler/osr-assert.js b/test/mjsunit/compiler/osr-assert.js new file mode 100644 index 0000000..94b901f --- /dev/null +++ b/test/mjsunit/compiler/osr-assert.js @@ -0,0 +1,41 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --use-osr + +function f(x, b, c) { + var outer = 1000000; + var a = 1; + while (outer > 0) { + a = a + 5; + assertEquals(b + 1, c); + outer--; + } + return a + 4; +} + +assertEquals(5000005, f(5, "122", "1221")); diff --git a/test/mjsunit/compiler/osr-sar.js b/test/mjsunit/compiler/osr-sar.js new file mode 100644 index 0000000..fd68b98 --- /dev/null +++ b/test/mjsunit/compiler/osr-sar.js @@ -0,0 +1,49 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function test() { + // Loop to force OSR. + var j = 0; + for (var i = 0; i < 80000; i++) { + j++; + } + + function SarShr(val) { + return val >> (-2 >>> 0); + } + + var K3 = 0x80000000; + assertEquals(-2, SarShr(K3 | 0)); + assertEquals(-2, SarShr(K3 | 0)); + %OptimizeFunctionOnNextCall(SarShr); + assertEquals(-2, SarShr(K3 | 0)); +} + +test(); +//test(); diff --git a/test/mjsunit/compiler/osr-uint32.js b/test/mjsunit/compiler/osr-uint32.js new file mode 100644 index 0000000..d6fcae5 --- /dev/null +++ b/test/mjsunit/compiler/osr-uint32.js @@ -0,0 +1,39 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Loop to force OSR. +var j = 0; +for (var i = 0; i < 80000; i++) { + j++; +} + +function SarShr(val) { + return val >> (-2 >>> 0); +} + +var K3 = 0x80000000; +assertEquals(-2, SarShr(K3 | 0)); diff --git a/test/mjsunit/compiler/osr-warm.js b/test/mjsunit/compiler/osr-warm.js new file mode 100644 index 0000000..65ada1e --- /dev/null +++ b/test/mjsunit/compiler/osr-warm.js @@ -0,0 +1,50 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --use-osr + +function f1(x) { + while (x > 0) { + x--; + } + return x; +} + +assertEquals(0, f1(1)); +assertEquals(0, f1(10000000)); + +function f2(x) { + var sum = 1; + while (x > 0) { + x--; + sum++; + } + return sum; +} + +assertEquals(2, f2(1)); +assertEquals(10000001, f2(10000000)); diff --git a/test/mjsunit/regress/regress-map-invalidation-2.js b/test/mjsunit/regress/regress-map-invalidation-2.js index 789c8bf..1f896a4 100644 --- a/test/mjsunit/regress/regress-map-invalidation-2.js +++ b/test/mjsunit/regress/regress-map-invalidation-2.js @@ -31,13 +31,13 @@ var c = { x: 2, y: 1 }; function g() { var outer = { foo: 1 }; - function f() { + function f(b, c) { var n = outer.foo; - for (var i = 0; i < 100000; i++) { + for (var i = 0; i < 10; i++) { n += c.x + outer.foo; } - var o2 = [{ x: 1.5, y: 1 }]; - return o2; + if (b) return [{ x: 1.5, y: 1 }]; + else return c; } // Clear type feedback from previous stress runs. %ClearFunctionTypeFeedback(f); @@ -45,7 +45,10 @@ function g() { } var fun = g(); -fun(); +fun(false, c); +fun(false, c); +fun(false, c); +%OptimizeFunctionOnNextCall(fun); +fun(false, c); +fun(true, c); assertOptimized(fun); -fun(); -