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
This commit is contained in:
parent
01e913e874
commit
49d9555a97
@ -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.
|
||||
// <deopt_data> = <code>[#deoptimization_data_offset]
|
||||
__ ldr(r1, MemOperand(r0, Code::kDeoptimizationDataOffset - kHeapObjectTag));
|
||||
|
||||
// Load the OSR entrypoint offset from the deoptimization data.
|
||||
// <osr_offset> = <deopt_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
|
||||
// <entry_addr> = <code_obj> + #header_size + <osr_offset>
|
||||
__ add(r0, r0, Operand::SmiUntag(r1));
|
||||
__ add(lr, r0, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// And "return" to the OSR entry point of the function.
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<Translation::Opcode>(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<Translation::Opcode>(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<Translation::Opcode>(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<intptr_t>(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<uint32_t>(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<uint32_t>(
|
||||
compiled_code_->entry() + pc_offset);
|
||||
output_[0]->SetPc(pc);
|
||||
}
|
||||
Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR);
|
||||
output_[0]->SetContinuation(
|
||||
reinterpret_cast<uint32_t>(continuation->entry()));
|
||||
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
|
||||
ok ? "finished" : "aborted",
|
||||
reinterpret_cast<intptr_t>(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::pc_offset()));
|
||||
__ push(r6);
|
||||
__ ldr(r6, MemOperand(r2, FrameDescription::continuation_offset()));
|
||||
|
@ -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<HBasicBlock*>* blocks = graph()->blocks();
|
||||
for (int i = 0; i < blocks->length(); i++) {
|
||||
HBasicBlock* next = NULL;
|
||||
@ -2439,11 +2449,19 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
|
||||
int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<JSFunction> function = info->closure();
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
Handle<FixedArray> 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<SharedFunctionInfo> shared = info->shared_info();
|
||||
Handle<JSFunction> 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> 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<JSFunction> function) {
|
||||
Handle<Code> Compiler::CompileForOnStackReplacement(
|
||||
Handle<JSFunction> 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<Code> unoptimized(function->shared()->code(), isolate);
|
||||
|
||||
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
|
||||
@ -1162,12 +1164,9 @@ BailoutId Compiler::CompileForOnStackReplacement(Handle<JSFunction> function) {
|
||||
PrintF("]\n");
|
||||
}
|
||||
|
||||
if (!IsSuitableForOnStackReplacement(isolate, function, unoptimized)) {
|
||||
return BailoutId::None();
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -1176,33 +1175,33 @@ BailoutId Compiler::CompileForOnStackReplacement(Handle<JSFunction> function) {
|
||||
PrintF("]\n");
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Attempt OSR compilation.
|
||||
Handle<Code> result = JSFunction::CompileOsr(
|
||||
function, ast_id, CLEAR_EXCEPTION);
|
||||
|
||||
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;
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[OSR - optimization failed for ");
|
||||
PrintF("[OSR - attempt failed for ");
|
||||
function->PrintName();
|
||||
PrintF("]\n");
|
||||
}
|
||||
}
|
||||
return BailoutId::None();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
|
||||
BailoutId Compiler::CompileForConcurrentOSR(Handle<JSFunction> function) {
|
||||
Handle<Code> Compiler::CompileForConcurrentOSR(Handle<JSFunction> function) {
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<Code> unoptimized(function->shared()->code(), isolate);
|
||||
|
||||
@ -1216,7 +1215,7 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle<JSFunction> function) {
|
||||
function->PrintName();
|
||||
PrintF("]\n");
|
||||
}
|
||||
return BailoutId::None();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
OptimizingCompiler* compiler = isolate->optimizing_compiler_thread()->
|
||||
@ -1230,8 +1229,7 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle<JSFunction> 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<JSFunction> function) {
|
||||
function->PrintName();
|
||||
PrintF("]\n");
|
||||
}
|
||||
return BailoutId::None();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
Handle<Code> result = compiler->info()->code();
|
||||
|
||||
DeoptimizationInputData* data = DeoptimizationInputData::cast(
|
||||
function->code()->deoptimization_data());
|
||||
// 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 ast_id;
|
||||
return result;
|
||||
}
|
||||
return BailoutId::None();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
if (!IsSuitableForOnStackReplacement(isolate, function, unoptimized)) {
|
||||
@ -1266,13 +1269,13 @@ BailoutId Compiler::CompileForConcurrentOSR(Handle<JSFunction> function) {
|
||||
PrintF(" is unsuitable, restoring interrupt calls]\n");
|
||||
}
|
||||
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
|
||||
return BailoutId::None();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
if (!RecompileConcurrent(function, pc_offset)) {
|
||||
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
|
||||
}
|
||||
return BailoutId::None();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<JSFunction> function);
|
||||
static Handle<Code> CompileForOnStackReplacement(Handle<JSFunction> function);
|
||||
|
||||
static BailoutId CompileForConcurrentOSR(Handle<JSFunction> function);
|
||||
static Handle<Code> CompileForConcurrentOSR(Handle<JSFunction> function);
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
static bool MakeCodeForLiveEdit(CompilationInfo* info);
|
||||
|
@ -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<Code*>(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<Object*>(input_value);
|
||||
|
||||
Translation::Opcode opcode =
|
||||
static_cast<Translation::Opcode>(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<int32_t>(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<Object*>(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<int32_t>(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<uint64_t, double>(double_value);
|
||||
int32_t lower = static_cast<int32_t>(int_value);
|
||||
int32_t upper = static_cast<int32_t>(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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(" ");
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -80,7 +80,8 @@ HBasicBlock* HOsrBuilder::BuildPossibleOsrLoopEntry(
|
||||
osr_values_ = new(zone) ZoneList<HUnknownOSRValue*>(length, zone);
|
||||
|
||||
for (int i = 0; i < first_expression_index; ++i) {
|
||||
HUnknownOSRValue* osr_value = builder_->Add<HUnknownOSRValue>();
|
||||
HUnknownOSRValue* osr_value
|
||||
= builder_->Add<HUnknownOSRValue>(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>();
|
||||
HUnknownOSRValue* osr_value
|
||||
= builder_->Add<HUnknownOSRValue>(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<HSimulate>(osr_entry_id);
|
||||
builder_->Add<HOsrEntry>(osr_entry_id);
|
||||
HContext* context = builder_->Add<HContext>();
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<Translation::Opcode>(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<Translation::Opcode>(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<Translation::Opcode>(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<intptr_t>(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<uint32_t>(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<uint32_t>(
|
||||
compiled_code_->entry() + pc_offset);
|
||||
output_[0]->SetPc(pc);
|
||||
}
|
||||
Code* continuation =
|
||||
function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
|
||||
output_[0]->SetContinuation(
|
||||
reinterpret_cast<uint32_t>(continuation->entry()));
|
||||
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
|
||||
ok ? "finished" : "aborted",
|
||||
reinterpret_cast<intptr_t>(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,7 +425,6 @@ 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()),
|
||||
@ -623,15 +436,6 @@ void Deoptimizer::EntryGenerator::Generate() {
|
||||
__ 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);
|
||||
}
|
||||
|
||||
// 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::pc_offset()));
|
||||
__ push(Operand(ebx, FrameDescription::continuation_offset()));
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<HBasicBlock*>* blocks = graph()->blocks();
|
||||
for (int i = 0; i < blocks->length(); i++) {
|
||||
HBasicBlock* next = NULL;
|
||||
@ -2537,11 +2546,24 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
|
||||
int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
// If the code object is null, just return to the unoptimized code.
|
||||
__ Ret(eq, v0, Operand(Smi::FromInt(0)));
|
||||
|
||||
// Untag the AST id and push it on the stack.
|
||||
__ SmiUntag(v0);
|
||||
__ push(v0);
|
||||
// Load deoptimization data from the code object.
|
||||
// <deopt_data> = <code>[#deoptimization_data_offset]
|
||||
__ lw(a1, MemOperand(v0, Code::kDeoptimizationDataOffset - kHeapObjectTag));
|
||||
|
||||
// Generate the code for doing the frame-to-frame translation using
|
||||
// the deoptimizer infrastructure.
|
||||
Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR);
|
||||
generator.Generate();
|
||||
// Load the OSR entrypoint offset from the deoptimization data.
|
||||
// <osr_offset> = <deopt_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
|
||||
// <entry_addr> = <code_obj> + #header_size + <osr_offset>
|
||||
__ addu(v0, v0, a1);
|
||||
__ addiu(ra, v0, Code::kHeaderSize - kHeapObjectTag);
|
||||
|
||||
// And "return" to the OSR entry point of the function.
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<Translation::Opcode>(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<Translation::Opcode>(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<Translation::Opcode>(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<intptr_t>(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<uint32_t>(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<uint32_t>(
|
||||
compiled_code_->entry() + pc_offset);
|
||||
output_[0]->SetPc(pc);
|
||||
}
|
||||
Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR);
|
||||
output_[0]->SetContinuation(
|
||||
reinterpret_cast<uint32_t>(continuation->entry()));
|
||||
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
|
||||
ok ? "finished" : "aborted",
|
||||
reinterpret_cast<intptr_t>(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::pc_offset()));
|
||||
__ push(t2);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<HBasicBlock*>* blocks = graph()->blocks();
|
||||
for (int i = 0; i < blocks->length(); i++) {
|
||||
HBasicBlock* next = NULL;
|
||||
@ -2341,11 +2351,19 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
|
||||
int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -9492,11 +9492,25 @@ bool JSFunction::CompileLazy(Handle<JSFunction> function,
|
||||
}
|
||||
|
||||
|
||||
bool JSFunction::CompileOptimized(Handle<JSFunction> function,
|
||||
Handle<Code> JSFunction::CompileOsr(Handle<JSFunction> 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<Code>::null();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool JSFunction::CompileOptimized(Handle<JSFunction> function,
|
||||
ClearExceptionFlag flag) {
|
||||
CompilationInfoWithZone info(function);
|
||||
info.SetOptimizing(BailoutId::None());
|
||||
return CompileLazyHelper(&info, flag);
|
||||
}
|
||||
|
||||
|
@ -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,9 +6978,11 @@ class JSFunction: public JSObject {
|
||||
ClearExceptionFlag flag);
|
||||
static bool CompileLazy(Handle<JSFunction> function,
|
||||
ClearExceptionFlag flag);
|
||||
static bool CompileOptimized(Handle<JSFunction> function,
|
||||
static Handle<Code> CompileOsr(Handle<JSFunction> function,
|
||||
BailoutId osr_ast_id,
|
||||
ClearExceptionFlag flag);
|
||||
static bool CompileOptimized(Handle<JSFunction> function,
|
||||
ClearExceptionFlag flag);
|
||||
|
||||
// Tells whether or not the function is already marked for lazy
|
||||
// recompilation.
|
||||
|
@ -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> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<Translation::Opcode>(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<Translation::Opcode>(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<Translation::Opcode>(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<intptr_t>(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<intptr_t>(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<intptr_t>(
|
||||
compiled_code_->entry() + pc_offset);
|
||||
output_[0]->SetPc(pc);
|
||||
}
|
||||
Code* continuation =
|
||||
function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
|
||||
output_[0]->SetContinuation(
|
||||
reinterpret_cast<intptr_t>(continuation->entry()));
|
||||
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
|
||||
ok ? "finished" : "aborted",
|
||||
reinterpret_cast<intptr_t>(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::pc_offset()));
|
||||
__ push(Operand(rbx, FrameDescription::continuation_offset()));
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<HBasicBlock*>* blocks = graph()->blocks();
|
||||
for (int i = 0; i < blocks->length(); i++) {
|
||||
HBasicBlock* next = NULL;
|
||||
@ -2359,11 +2369,19 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
|
||||
int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
41
test/mjsunit/compiler/osr-assert.js
Normal file
41
test/mjsunit/compiler/osr-assert.js
Normal file
@ -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"));
|
49
test/mjsunit/compiler/osr-sar.js
Normal file
49
test/mjsunit/compiler/osr-sar.js
Normal file
@ -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();
|
39
test/mjsunit/compiler/osr-uint32.js
Normal file
39
test/mjsunit/compiler/osr-uint32.js
Normal file
@ -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));
|
50
test/mjsunit/compiler/osr-warm.js
Normal file
50
test/mjsunit/compiler/osr-warm.js
Normal file
@ -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));
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user