Implement inlining of constructor calls.
R=vegorov@chromium.org,kmillikin@chromium.org Review URL: https://chromiumcodereview.appspot.com/9304001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10849 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
a81b05af93
commit
fb8eb04bfd
@ -978,6 +978,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
NullCallWrapper(), CALL_AS_METHOD);
|
||||
}
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
if (!is_api_function && !count_constructions) {
|
||||
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
|
||||
}
|
||||
|
||||
// Restore context from the frame.
|
||||
// r0: result
|
||||
// sp[0]: receiver
|
||||
@ -1740,7 +1745,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
__ bind(&invoke);
|
||||
__ Call(r3);
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
|
||||
|
||||
// Exit frame and return.
|
||||
LeaveArgumentsAdaptorFrame(masm);
|
||||
__ Jump(lr);
|
||||
|
@ -351,7 +351,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
@ -373,16 +372,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -392,7 +388,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
@ -404,7 +399,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// A marker value is used in place of the context.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t context = reinterpret_cast<intptr_t>(
|
||||
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
output_frame->SetFrameSlot(output_offset, context);
|
||||
@ -415,7 +409,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -425,7 +418,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -445,6 +437,119 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" translating construct stub => height=%d\n", height_in_bytes);
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = 7 * kPointerSize;
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::CONSTRUCT);
|
||||
|
||||
// Construct stub can not be topmost or bottommost.
|
||||
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
|
||||
ASSERT(output_[frame_index] == NULL);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address of the frame is computed from the previous
|
||||
// frame's top and this frame's size.
|
||||
uint32_t top_address;
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
|
||||
top_address + output_offset, output_offset, callers_pc);
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
output_frame->SetFp(fp_value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
|
||||
fp_value, output_offset, value);
|
||||
}
|
||||
|
||||
// The context can be gotten from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
value = output_[frame_index - 1]->GetContext();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// A marker value is used in place of the function.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::CONSTRUCT));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function (construct sentinel)\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n",
|
||||
top_address + output_offset, output_offset, value, height - 1);
|
||||
}
|
||||
|
||||
// Constructor function being invoked by the stub.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; constructor function\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// The newly allocated object was passed as receiver in the artificial
|
||||
// constructor stub environment created by HEnvironment::CopyForInlining().
|
||||
output_offset -= kPointerSize;
|
||||
value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; allocated receiver\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
ASSERT(0 == output_offset);
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
|
||||
uint32_t pc = reinterpret_cast<uint32_t>(
|
||||
construct_stub->instruction_start() +
|
||||
isolate_->heap()->construct_stub_deopt_pc_offset()->value());
|
||||
output_frame->SetPc(pc);
|
||||
}
|
||||
|
||||
|
||||
// This code is very similar to ia32 code, but relies on register names (fp, sp)
|
||||
// and how the frame is laid out.
|
||||
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
@ -557,9 +662,8 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
value = reinterpret_cast<intptr_t>(function->context());
|
||||
}
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (is_topmost) {
|
||||
output_frame->SetRegister(cp.code(), value);
|
||||
}
|
||||
output_frame->SetContext(value);
|
||||
if (is_topmost) output_frame->SetRegister(cp.code(), value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
|
@ -2382,6 +2382,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
CallConstructStub stub(flags);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
|
||||
context()->Plug(r0);
|
||||
}
|
||||
|
||||
|
@ -995,10 +995,11 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
LEnvironment* outer =
|
||||
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
|
||||
int ast_id = hydrogen_env->ast_id();
|
||||
ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
|
||||
ASSERT(ast_id != AstNode::kNoNumber ||
|
||||
hydrogen_env->frame_type() != JS_FUNCTION);
|
||||
int value_count = hydrogen_env->length();
|
||||
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
|
||||
hydrogen_env->is_arguments_adaptor(),
|
||||
hydrogen_env->frame_type(),
|
||||
ast_id,
|
||||
hydrogen_env->parameter_count(),
|
||||
argument_count_,
|
||||
@ -1020,7 +1021,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
result->AddValue(op, value->representation());
|
||||
}
|
||||
|
||||
if (!hydrogen_env->is_arguments_adaptor()) {
|
||||
if (hydrogen_env->frame_type() == JS_FUNCTION) {
|
||||
*argument_index_accumulator = argument_index;
|
||||
}
|
||||
|
||||
@ -2093,6 +2094,12 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
LAllocateObject* result = new LAllocateObject();
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) {
|
||||
return MarkAsCall(DefineFixed(new LFastLiteral, r0), instr);
|
||||
}
|
||||
@ -2245,7 +2252,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
instr->arguments_count(),
|
||||
instr->function(),
|
||||
undefined,
|
||||
instr->call_kind());
|
||||
instr->call_kind(),
|
||||
instr->is_construct());
|
||||
current_block_->UpdateEnvironment(inner);
|
||||
chunk_->AddInlinedClosure(instr->closure());
|
||||
return NULL;
|
||||
|
@ -49,6 +49,7 @@ class LCodeGen;
|
||||
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -1922,6 +1923,13 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LFastLiteral: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal")
|
||||
|
@ -479,10 +479,18 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
|
||||
|
||||
WriteTranslation(environment->outer(), translation);
|
||||
int closure_id = DefineDeoptimizationLiteral(environment->closure());
|
||||
if (environment->is_arguments_adaptor()) {
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
} else {
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
switch (environment->frame_type()) {
|
||||
case JS_FUNCTION:
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
break;
|
||||
case JS_CONSTRUCT:
|
||||
translation->BeginConstructStubFrame(closure_id, translation_size);
|
||||
break;
|
||||
case ARGUMENTS_ADAPTOR:
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
for (int i = 0; i < translation_size; ++i) {
|
||||
LOperand* value = environment->values()->at(i);
|
||||
@ -619,7 +627,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment,
|
||||
int jsframe_count = 0;
|
||||
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
|
||||
++frame_count;
|
||||
if (!e->is_arguments_adaptor()) {
|
||||
if (e->frame_type() == JS_FUNCTION) {
|
||||
++jsframe_count;
|
||||
}
|
||||
}
|
||||
@ -4322,6 +4330,44 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr);
|
||||
|
||||
// TODO(mstarzinger): Implement inlined version instead of jumping to
|
||||
// deferred runtime call.
|
||||
__ jmp(deferred->entry());
|
||||
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ mov(result, Operand(0));
|
||||
|
||||
PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
|
||||
__ LoadHeapObject(r0, constructor);
|
||||
__ push(r0);
|
||||
CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr);
|
||||
__ StoreToSafepointRegisterSlot(r0, result);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
|
||||
Heap* heap = isolate()->heap();
|
||||
ElementsKind boilerplate_elements_kind =
|
||||
|
@ -116,6 +116,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredStackCheck(LStackCheck* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -596,6 +596,14 @@ void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle,
|
||||
}
|
||||
|
||||
|
||||
void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
is_monomorphic_ = oracle->CallNewIsMonomorphic(this);
|
||||
if (is_monomorphic_) {
|
||||
target_ = oracle->GetCallNewTarget(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CompareOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
TypeInfo info = oracle->CompareType(this);
|
||||
if (info.IsSmi()) {
|
||||
|
16
src/ast.h
16
src/ast.h
@ -1528,6 +1528,13 @@ class CallNew: public Expression {
|
||||
ZoneList<Expression*>* arguments() const { return arguments_; }
|
||||
virtual int position() const { return pos_; }
|
||||
|
||||
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
|
||||
virtual bool IsMonomorphic() { return is_monomorphic_; }
|
||||
Handle<JSFunction> target() { return target_; }
|
||||
|
||||
// Bailout support.
|
||||
int ReturnId() const { return return_id_; }
|
||||
|
||||
protected:
|
||||
template<class> friend class AstNodeFactory;
|
||||
|
||||
@ -1538,12 +1545,19 @@ class CallNew: public Expression {
|
||||
: Expression(isolate),
|
||||
expression_(expression),
|
||||
arguments_(arguments),
|
||||
pos_(pos) { }
|
||||
pos_(pos),
|
||||
is_monomorphic_(false),
|
||||
return_id_(GetNextId(isolate)) { }
|
||||
|
||||
private:
|
||||
Expression* expression_;
|
||||
ZoneList<Expression*>* arguments_;
|
||||
int pos_;
|
||||
|
||||
bool is_monomorphic_;
|
||||
Handle<JSFunction> target_;
|
||||
|
||||
int return_id_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -170,8 +170,16 @@ DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
|
||||
deoptimizer->output_[frame_index - 1]->GetFrameType() ==
|
||||
StackFrame::ARGUMENTS_ADAPTOR;
|
||||
|
||||
DeoptimizedFrameInfo* info =
|
||||
new DeoptimizedFrameInfo(deoptimizer, frame_index, has_arguments_adaptor);
|
||||
int construct_offset = has_arguments_adaptor ? 2 : 1;
|
||||
bool has_construct_stub =
|
||||
frame_index >= construct_offset &&
|
||||
deoptimizer->output_[frame_index - construct_offset]->GetFrameType() ==
|
||||
StackFrame::CONSTRUCT;
|
||||
|
||||
DeoptimizedFrameInfo* info = new DeoptimizedFrameInfo(deoptimizer,
|
||||
frame_index,
|
||||
has_arguments_adaptor,
|
||||
has_construct_stub);
|
||||
isolate->deoptimizer_data()->deoptimized_frame_info_ = info;
|
||||
|
||||
// Get the "simulated" top and size for the requested frame.
|
||||
@ -570,6 +578,9 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
DoComputeArgumentsAdaptorFrame(&iterator, i);
|
||||
break;
|
||||
case Translation::CONSTRUCT_STUB_FRAME:
|
||||
DoComputeConstructStubFrame(&iterator, i);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@ -686,6 +697,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
|
||||
case Translation::BEGIN:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::CONSTRUCT_STUB_FRAME:
|
||||
case Translation::DUPLICATE:
|
||||
UNREACHABLE();
|
||||
return;
|
||||
@ -873,6 +885,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
|
||||
case Translation::BEGIN:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::CONSTRUCT_STUB_FRAME:
|
||||
case Translation::DUPLICATE:
|
||||
UNREACHABLE(); // Malformed input.
|
||||
return false;
|
||||
@ -1206,7 +1219,8 @@ FrameDescription::FrameDescription(uint32_t frame_size,
|
||||
function_(function),
|
||||
top_(kZapUint32),
|
||||
pc_(kZapUint32),
|
||||
fp_(kZapUint32) {
|
||||
fp_(kZapUint32),
|
||||
context_(kZapUint32) {
|
||||
// Zap all the registers.
|
||||
for (int r = 0; r < Register::kNumRegisters; r++) {
|
||||
SetRegister(r, kZapUint32);
|
||||
@ -1320,6 +1334,13 @@ Handle<ByteArray> TranslationBuffer::CreateByteArray() {
|
||||
}
|
||||
|
||||
|
||||
void Translation::BeginConstructStubFrame(int literal_id, unsigned height) {
|
||||
buffer_->Add(CONSTRUCT_STUB_FRAME);
|
||||
buffer_->Add(literal_id);
|
||||
buffer_->Add(height);
|
||||
}
|
||||
|
||||
|
||||
void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
|
||||
buffer_->Add(ARGUMENTS_ADAPTOR_FRAME);
|
||||
buffer_->Add(literal_id);
|
||||
@ -1402,6 +1423,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
|
||||
return 1;
|
||||
case BEGIN:
|
||||
case ARGUMENTS_ADAPTOR_FRAME:
|
||||
case CONSTRUCT_STUB_FRAME:
|
||||
return 2;
|
||||
case JS_FRAME:
|
||||
return 3;
|
||||
@ -1421,6 +1443,8 @@ const char* Translation::StringFor(Opcode opcode) {
|
||||
return "JS_FRAME";
|
||||
case ARGUMENTS_ADAPTOR_FRAME:
|
||||
return "ARGUMENTS_ADAPTOR_FRAME";
|
||||
case CONSTRUCT_STUB_FRAME:
|
||||
return "CONSTRUCT_STUB_FRAME";
|
||||
case REGISTER:
|
||||
return "REGISTER";
|
||||
case INT32_REGISTER:
|
||||
@ -1476,6 +1500,7 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
|
||||
case Translation::BEGIN:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::CONSTRUCT_STUB_FRAME:
|
||||
// Peeled off before getting here.
|
||||
break;
|
||||
|
||||
@ -1598,10 +1623,13 @@ Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments(
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
|
||||
DeoptimizedFrameInfo::DeoptimizedFrameInfo(
|
||||
Deoptimizer* deoptimizer, int frame_index, bool has_arguments_adaptor) {
|
||||
DeoptimizedFrameInfo::DeoptimizedFrameInfo(Deoptimizer* deoptimizer,
|
||||
int frame_index,
|
||||
bool has_arguments_adaptor,
|
||||
bool has_construct_stub) {
|
||||
FrameDescription* output_frame = deoptimizer->output_[frame_index];
|
||||
SetFunction(output_frame->GetFunction());
|
||||
function_ = output_frame->GetFunction();
|
||||
has_construct_stub_ = has_construct_stub;
|
||||
expression_count_ = output_frame->GetExpressionCount();
|
||||
expression_stack_ = new Object*[expression_count_];
|
||||
// Get the source position using the unoptimized code.
|
||||
|
@ -283,6 +283,8 @@ class Deoptimizer : public Malloced {
|
||||
void DoComputeJSFrame(TranslationIterator* iterator, int frame_index);
|
||||
void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
int frame_index);
|
||||
void DoComputeConstructStubFrame(TranslationIterator* iterator,
|
||||
int frame_index);
|
||||
void DoTranslateCommand(TranslationIterator* iterator,
|
||||
int frame_index,
|
||||
unsigned output_offset);
|
||||
@ -431,6 +433,9 @@ class FrameDescription {
|
||||
intptr_t GetFp() const { return fp_; }
|
||||
void SetFp(intptr_t fp) { fp_ = fp; }
|
||||
|
||||
intptr_t GetContext() const { return context_; }
|
||||
void SetContext(intptr_t context) { context_ = context; }
|
||||
|
||||
Smi* GetState() const { return state_; }
|
||||
void SetState(Smi* state) { state_ = state; }
|
||||
|
||||
@ -492,6 +497,7 @@ class FrameDescription {
|
||||
intptr_t top_;
|
||||
intptr_t pc_;
|
||||
intptr_t fp_;
|
||||
intptr_t context_;
|
||||
StackFrame::Type type_;
|
||||
Smi* state_;
|
||||
#ifdef DEBUG
|
||||
@ -556,6 +562,7 @@ class Translation BASE_EMBEDDED {
|
||||
enum Opcode {
|
||||
BEGIN,
|
||||
JS_FRAME,
|
||||
CONSTRUCT_STUB_FRAME,
|
||||
ARGUMENTS_ADAPTOR_FRAME,
|
||||
REGISTER,
|
||||
INT32_REGISTER,
|
||||
@ -584,6 +591,7 @@ class Translation BASE_EMBEDDED {
|
||||
// Commands.
|
||||
void BeginJSFrame(int node_id, int literal_id, unsigned height);
|
||||
void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
|
||||
void BeginConstructStubFrame(int literal_id, unsigned height);
|
||||
void StoreRegister(Register reg);
|
||||
void StoreInt32Register(Register reg);
|
||||
void StoreDoubleRegister(DoubleRegister reg);
|
||||
@ -716,7 +724,8 @@ class DeoptimizedFrameInfo : public Malloced {
|
||||
public:
|
||||
DeoptimizedFrameInfo(Deoptimizer* deoptimizer,
|
||||
int frame_index,
|
||||
bool has_arguments_adaptor);
|
||||
bool has_arguments_adaptor,
|
||||
bool has_construct_stub);
|
||||
virtual ~DeoptimizedFrameInfo();
|
||||
|
||||
// GC support.
|
||||
@ -733,6 +742,12 @@ class DeoptimizedFrameInfo : public Malloced {
|
||||
return function_;
|
||||
}
|
||||
|
||||
// Check if this frame is preceded by construct stub frame. The bottom-most
|
||||
// inlined frame might still be called by an uninlined construct stub.
|
||||
bool HasConstructStub() {
|
||||
return has_construct_stub_;
|
||||
}
|
||||
|
||||
// Get an incoming argument.
|
||||
Object* GetParameter(int index) {
|
||||
ASSERT(0 <= index && index < parameters_count());
|
||||
@ -750,11 +765,6 @@ class DeoptimizedFrameInfo : public Malloced {
|
||||
}
|
||||
|
||||
private:
|
||||
// Set the frame function.
|
||||
void SetFunction(JSFunction* function) {
|
||||
function_ = function;
|
||||
}
|
||||
|
||||
// Set an incoming argument.
|
||||
void SetParameter(int index, Object* obj) {
|
||||
ASSERT(0 <= index && index < parameters_count());
|
||||
@ -768,6 +778,7 @@ class DeoptimizedFrameInfo : public Malloced {
|
||||
}
|
||||
|
||||
JSFunction* function_;
|
||||
bool has_construct_stub_;
|
||||
int parameters_count_;
|
||||
int expression_count_;
|
||||
Object** parameters_;
|
||||
|
@ -167,6 +167,7 @@ DEFINE_bool(use_osr, true, "use on-stack replacement")
|
||||
DEFINE_bool(trace_osr, false, "trace on-stack replacement")
|
||||
DEFINE_int(stress_runs, 0, "number of stress runs")
|
||||
DEFINE_bool(optimize_closures, true, "optimize closures")
|
||||
DEFINE_bool(inline_construct, false, "inline constructor calls")
|
||||
DEFINE_int(loop_weight, 1, "loop weight for representation inference")
|
||||
|
||||
DEFINE_bool(optimize_for_in, true,
|
||||
|
@ -191,7 +191,7 @@ inline bool StandardFrame::IsArgumentsAdaptorFrame(Address fp) {
|
||||
inline bool StandardFrame::IsConstructFrame(Address fp) {
|
||||
Object* marker =
|
||||
Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset);
|
||||
return marker == Smi::FromInt(CONSTRUCT);
|
||||
return marker == Smi::FromInt(StackFrame::CONSTRUCT);
|
||||
}
|
||||
|
||||
|
||||
|
@ -843,14 +843,11 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
|
||||
// We create the summary in reverse order because the frames
|
||||
// in the deoptimization translation are ordered bottom-to-top.
|
||||
bool is_constructor = IsConstructor();
|
||||
int i = jsframe_count;
|
||||
while (i > 0) {
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
if (opcode == Translation::JS_FRAME) {
|
||||
// We don't inline constructor calls, so only the first, outermost
|
||||
// frame can be a constructor frame in case of inlining.
|
||||
bool is_constructor = (i == jsframe_count) && IsConstructor();
|
||||
|
||||
i--;
|
||||
int ast_id = it.Next();
|
||||
int function_id = it.Next();
|
||||
@ -900,11 +897,18 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
|
||||
FrameSummary summary(receiver, function, code, pc_offset, is_constructor);
|
||||
frames->Add(summary);
|
||||
is_constructor = false;
|
||||
} else if (opcode == Translation::CONSTRUCT_STUB_FRAME) {
|
||||
// The next encountered JS_FRAME will be marked as a constructor call.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
ASSERT(!is_constructor);
|
||||
is_constructor = true;
|
||||
} else {
|
||||
// Skip over operands to advance to the next opcode.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
}
|
||||
}
|
||||
ASSERT(!is_constructor);
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,7 +150,8 @@ namespace internal {
|
||||
V(Script, empty_script, EmptyScript) \
|
||||
V(Smi, real_stack_limit, RealStackLimit) \
|
||||
V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames) \
|
||||
V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset)
|
||||
V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset) \
|
||||
V(Smi, construct_stub_deopt_pc_offset, ConstructStubDeoptPCOffset)
|
||||
|
||||
#define ROOT_LIST(V) \
|
||||
STRONG_ROOT_LIST(V) \
|
||||
@ -1567,6 +1568,11 @@ class Heap {
|
||||
set_arguments_adaptor_deopt_pc_offset(Smi::FromInt(pc_offset));
|
||||
}
|
||||
|
||||
void SetConstructStubDeoptPCOffset(int pc_offset) {
|
||||
ASSERT(construct_stub_deopt_pc_offset() == Smi::FromInt(0));
|
||||
set_construct_stub_deopt_pc_offset(Smi::FromInt(pc_offset));
|
||||
}
|
||||
|
||||
private:
|
||||
Heap();
|
||||
|
||||
|
@ -1923,6 +1923,11 @@ HType HStringCharFromCode::CalculateInferredType() {
|
||||
}
|
||||
|
||||
|
||||
HType HAllocateObject::CalculateInferredType() {
|
||||
return HType::JSObject();
|
||||
}
|
||||
|
||||
|
||||
HType HFastLiteral::CalculateInferredType() {
|
||||
// TODO(mstarzinger): Be smarter, could also be JSArray here.
|
||||
return HType::JSObject();
|
||||
|
@ -62,6 +62,7 @@ class LChunkBuilder;
|
||||
V(AbnormalExit) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(Add) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -1376,11 +1377,13 @@ class HEnterInlined: public HTemplateInstruction<0> {
|
||||
HEnterInlined(Handle<JSFunction> closure,
|
||||
int arguments_count,
|
||||
FunctionLiteral* function,
|
||||
CallKind call_kind)
|
||||
CallKind call_kind,
|
||||
bool is_construct)
|
||||
: closure_(closure),
|
||||
arguments_count_(arguments_count),
|
||||
function_(function),
|
||||
call_kind_(call_kind) {
|
||||
call_kind_(call_kind),
|
||||
is_construct_(is_construct) {
|
||||
}
|
||||
|
||||
virtual void PrintDataTo(StringStream* stream);
|
||||
@ -1389,6 +1392,7 @@ class HEnterInlined: public HTemplateInstruction<0> {
|
||||
int arguments_count() const { return arguments_count_; }
|
||||
FunctionLiteral* function() const { return function_; }
|
||||
CallKind call_kind() const { return call_kind_; }
|
||||
bool is_construct() const { return is_construct_; }
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) {
|
||||
return Representation::None();
|
||||
@ -1401,6 +1405,7 @@ class HEnterInlined: public HTemplateInstruction<0> {
|
||||
int arguments_count_;
|
||||
FunctionLiteral* function_;
|
||||
CallKind call_kind_;
|
||||
bool is_construct_;
|
||||
};
|
||||
|
||||
|
||||
@ -4358,6 +4363,29 @@ class HStringLength: public HUnaryOperation {
|
||||
};
|
||||
|
||||
|
||||
class HAllocateObject: public HTemplateInstruction<1> {
|
||||
public:
|
||||
HAllocateObject(HValue* context, Handle<JSFunction> constructor)
|
||||
: constructor_(constructor) {
|
||||
SetOperandAt(0, context);
|
||||
set_representation(Representation::Tagged());
|
||||
}
|
||||
|
||||
HValue* context() { return OperandAt(0); }
|
||||
Handle<JSFunction> constructor() { return constructor_; }
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) {
|
||||
return Representation::Tagged();
|
||||
}
|
||||
virtual HType CalculateInferredType();
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject)
|
||||
|
||||
private:
|
||||
Handle<JSFunction> constructor_;
|
||||
};
|
||||
|
||||
|
||||
template <int V>
|
||||
class HMaterializedLiteral: public HTemplateInstruction<V> {
|
||||
public:
|
||||
|
290
src/hydrogen.cc
290
src/hydrogen.cc
@ -600,7 +600,7 @@ HConstant* HGraph::GetConstantHole() {
|
||||
HGraphBuilder::HGraphBuilder(CompilationInfo* info,
|
||||
TypeFeedbackOracle* oracle)
|
||||
: function_state_(NULL),
|
||||
initial_function_state_(this, info, oracle, false),
|
||||
initial_function_state_(this, info, oracle, NORMAL_RETURN),
|
||||
ast_context_(NULL),
|
||||
break_scope_(NULL),
|
||||
graph_(NULL),
|
||||
@ -2156,12 +2156,12 @@ void HGraph::ComputeMinusZeroChecks() {
|
||||
FunctionState::FunctionState(HGraphBuilder* owner,
|
||||
CompilationInfo* info,
|
||||
TypeFeedbackOracle* oracle,
|
||||
bool drop_extra)
|
||||
ReturnHandlingFlag return_handling)
|
||||
: owner_(owner),
|
||||
compilation_info_(info),
|
||||
oracle_(oracle),
|
||||
call_context_(NULL),
|
||||
drop_extra_(drop_extra),
|
||||
return_handling_(return_handling),
|
||||
function_return_(NULL),
|
||||
test_context_(NULL),
|
||||
outer_(owner->function_state()) {
|
||||
@ -2204,7 +2204,7 @@ AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
|
||||
for_typeof_(false) {
|
||||
owner->set_ast_context(this); // Push.
|
||||
#ifdef DEBUG
|
||||
ASSERT(!owner->environment()->is_arguments_adaptor());
|
||||
ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
|
||||
original_length_ = owner->environment()->length();
|
||||
#endif
|
||||
}
|
||||
@ -2219,7 +2219,7 @@ EffectContext::~EffectContext() {
|
||||
ASSERT(owner()->HasStackOverflow() ||
|
||||
owner()->current_block() == NULL ||
|
||||
(owner()->environment()->length() == original_length_ &&
|
||||
!owner()->environment()->is_arguments_adaptor()));
|
||||
owner()->environment()->frame_type() == JS_FUNCTION));
|
||||
}
|
||||
|
||||
|
||||
@ -2227,7 +2227,7 @@ ValueContext::~ValueContext() {
|
||||
ASSERT(owner()->HasStackOverflow() ||
|
||||
owner()->current_block() == NULL ||
|
||||
(owner()->environment()->length() == original_length_ + 1 &&
|
||||
!owner()->environment()->is_arguments_adaptor()));
|
||||
owner()->environment()->frame_type() == JS_FUNCTION));
|
||||
}
|
||||
|
||||
|
||||
@ -2590,8 +2590,8 @@ void HGraphBuilder::PushAndAdd(HInstruction* instr) {
|
||||
}
|
||||
|
||||
|
||||
template <int V>
|
||||
HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) {
|
||||
template <class Instruction>
|
||||
HInstruction* HGraphBuilder::PreProcessCall(Instruction* call) {
|
||||
int count = call->argument_count();
|
||||
ZoneList<HValue*> arguments(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@ -2819,7 +2819,38 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
||||
CHECK_ALIVE(VisitForValue(stmt->expression()));
|
||||
HValue* result = environment()->Pop();
|
||||
current_block()->FinishExit(new(zone()) HReturn(result));
|
||||
set_current_block(NULL);
|
||||
} else if (function_state()->is_construct()) {
|
||||
// Return from an inlined construct call. In a test context the return
|
||||
// value will always evaluate to true, in a value context the return value
|
||||
// needs to be a JSObject.
|
||||
if (context->IsTest()) {
|
||||
TestContext* test = TestContext::cast(context);
|
||||
CHECK_ALIVE(VisitForEffect(stmt->expression()));
|
||||
current_block()->Goto(test->if_true(), function_state()->drop_extra());
|
||||
} else if (context->IsEffect()) {
|
||||
CHECK_ALIVE(VisitForEffect(stmt->expression()));
|
||||
current_block()->Goto(function_return(), function_state()->drop_extra());
|
||||
} else {
|
||||
ASSERT(context->IsValue());
|
||||
CHECK_ALIVE(VisitForValue(stmt->expression()));
|
||||
HValue* return_value = Pop();
|
||||
HValue* receiver = environment()->Lookup(0);
|
||||
HHasInstanceTypeAndBranch* typecheck =
|
||||
new(zone()) HHasInstanceTypeAndBranch(return_value,
|
||||
FIRST_SPEC_OBJECT_TYPE,
|
||||
LAST_SPEC_OBJECT_TYPE);
|
||||
HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
|
||||
HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
|
||||
typecheck->SetSuccessorAt(0, if_spec_object);
|
||||
typecheck->SetSuccessorAt(1, not_spec_object);
|
||||
current_block()->Finish(typecheck);
|
||||
if_spec_object->AddLeaveInlined(return_value,
|
||||
function_return(),
|
||||
function_state()->drop_extra());
|
||||
not_spec_object->AddLeaveInlined(receiver,
|
||||
function_return(),
|
||||
function_state()->drop_extra());
|
||||
}
|
||||
} else {
|
||||
// Return from an inlined function, visit the subexpression in the
|
||||
// expression context of the call.
|
||||
@ -2834,13 +2865,13 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
||||
} else {
|
||||
ASSERT(context->IsValue());
|
||||
CHECK_ALIVE(VisitForValue(stmt->expression()));
|
||||
HValue* return_value = environment()->Pop();
|
||||
HValue* return_value = Pop();
|
||||
current_block()->AddLeaveInlined(return_value,
|
||||
function_return(),
|
||||
function_state()->drop_extra());
|
||||
}
|
||||
set_current_block(NULL);
|
||||
}
|
||||
set_current_block(NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -5001,7 +5032,7 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
|
||||
PrintF("Trying to inline the polymorphic call to %s\n",
|
||||
*name->ToCString());
|
||||
}
|
||||
if (FLAG_polymorphic_inlining && TryInline(expr)) {
|
||||
if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
|
||||
// Trying to inline will signal that we should bailout from the
|
||||
// entire compilation by setting stack overflow on the visitor.
|
||||
if (HasStackOverflow()) return;
|
||||
@ -5071,19 +5102,18 @@ void HGraphBuilder::TraceInline(Handle<JSFunction> target,
|
||||
}
|
||||
|
||||
|
||||
bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
bool HGraphBuilder::TryInline(CallKind call_kind,
|
||||
Handle<JSFunction> target,
|
||||
ZoneList<Expression*>* arguments,
|
||||
HValue* receiver,
|
||||
int ast_id,
|
||||
int return_id,
|
||||
ReturnHandlingFlag return_handling) {
|
||||
if (!FLAG_use_inlining) return false;
|
||||
|
||||
// The function call we are inlining is a method call if the call
|
||||
// is a property call.
|
||||
CallKind call_kind = (expr->expression()->AsProperty() == NULL)
|
||||
? CALL_AS_FUNCTION
|
||||
: CALL_AS_METHOD;
|
||||
|
||||
// Precondition: call is monomorphic and we have found a target with the
|
||||
// appropriate arity.
|
||||
Handle<JSFunction> caller = info()->closure();
|
||||
Handle<JSFunction> target = expr->target();
|
||||
Handle<SharedFunctionInfo> target_shared(target->shared());
|
||||
|
||||
// Do a quick check on source code length to avoid parsing large
|
||||
@ -5131,7 +5161,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
TraceInline(target, caller, "inline depth limit reached");
|
||||
return false;
|
||||
}
|
||||
if (!env->outer()->is_arguments_adaptor()) {
|
||||
if (env->outer()->frame_type() == JS_FUNCTION) {
|
||||
current_level++;
|
||||
}
|
||||
env = env->outer();
|
||||
@ -5239,16 +5269,17 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
isolate());
|
||||
// The function state is new-allocated because we need to delete it
|
||||
// in two different places.
|
||||
FunctionState* target_state =
|
||||
new FunctionState(this, &target_info, &target_oracle, drop_extra);
|
||||
FunctionState* target_state = new FunctionState(
|
||||
this, &target_info, &target_oracle, return_handling);
|
||||
|
||||
HConstant* undefined = graph()->GetConstantUndefined();
|
||||
HEnvironment* inner_env =
|
||||
environment()->CopyForInlining(target,
|
||||
expr->arguments()->length(),
|
||||
arguments->length(),
|
||||
function,
|
||||
undefined,
|
||||
call_kind);
|
||||
call_kind,
|
||||
function_state()->is_construct());
|
||||
#ifdef V8_TARGET_ARCH_IA32
|
||||
// IA32 only, overwrite the caller's context in the deoptimization
|
||||
// environment with the correct one.
|
||||
@ -5262,12 +5293,13 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
#endif
|
||||
HBasicBlock* body_entry = CreateBasicBlock(inner_env);
|
||||
current_block()->Goto(body_entry);
|
||||
body_entry->SetJoinId(expr->ReturnId());
|
||||
body_entry->SetJoinId(return_id);
|
||||
set_current_block(body_entry);
|
||||
AddInstruction(new(zone()) HEnterInlined(target,
|
||||
expr->arguments()->length(),
|
||||
arguments->length(),
|
||||
function,
|
||||
call_kind));
|
||||
call_kind,
|
||||
function_state()->is_construct()));
|
||||
VisitDeclarations(target_info.scope()->declarations());
|
||||
VisitStatements(function->body());
|
||||
if (HasStackOverflow()) {
|
||||
@ -5286,32 +5318,27 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
TraceInline(target, caller, NULL);
|
||||
|
||||
if (current_block() != NULL) {
|
||||
// Add a return of undefined if control can fall off the body. In a
|
||||
// test context, undefined is false.
|
||||
if (inlined_test_context() == NULL) {
|
||||
// Add default return value (i.e. undefined for normals calls or the newly
|
||||
// allocated receiver for construct calls) if control can fall off the
|
||||
// body. In a test context, undefined is false and any JSObject is true.
|
||||
if (call_context()->IsValue()) {
|
||||
ASSERT(function_return() != NULL);
|
||||
ASSERT(call_context()->IsEffect() || call_context()->IsValue());
|
||||
if (call_context()->IsEffect()) {
|
||||
current_block()->Goto(function_return(), drop_extra);
|
||||
} else {
|
||||
current_block()->AddLeaveInlined(undefined,
|
||||
function_return(),
|
||||
drop_extra);
|
||||
}
|
||||
HValue* return_value = function_state()->is_construct()
|
||||
? receiver
|
||||
: undefined;
|
||||
current_block()->AddLeaveInlined(return_value,
|
||||
function_return(),
|
||||
function_state()->drop_extra());
|
||||
} else if (call_context()->IsEffect()) {
|
||||
ASSERT(function_return() != NULL);
|
||||
current_block()->Goto(function_return(), function_state()->drop_extra());
|
||||
} else {
|
||||
// The graph builder assumes control can reach both branches of a
|
||||
// test, so we materialize the undefined value and test it rather than
|
||||
// simply jumping to the false target.
|
||||
//
|
||||
// TODO(3168478): refactor to avoid this.
|
||||
ASSERT(call_context()->IsTest());
|
||||
HBasicBlock* empty_true = graph()->CreateBasicBlock();
|
||||
HBasicBlock* empty_false = graph()->CreateBasicBlock();
|
||||
HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false);
|
||||
current_block()->Finish(test);
|
||||
|
||||
empty_true->Goto(inlined_test_context()->if_true(), drop_extra);
|
||||
empty_false->Goto(inlined_test_context()->if_false(), drop_extra);
|
||||
ASSERT(inlined_test_context() != NULL);
|
||||
HBasicBlock* target = function_state()->is_construct()
|
||||
? inlined_test_context()->if_true()
|
||||
: inlined_test_context()->if_false();
|
||||
current_block()->Goto(target, function_state()->drop_extra());
|
||||
}
|
||||
}
|
||||
|
||||
@ -5327,12 +5354,12 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
|
||||
// Forward to the real test context.
|
||||
if (if_true->HasPredecessor()) {
|
||||
if_true->SetJoinId(expr->id());
|
||||
if_true->SetJoinId(ast_id);
|
||||
HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
|
||||
if_true->Goto(true_target, function_state()->drop_extra());
|
||||
}
|
||||
if (if_false->HasPredecessor()) {
|
||||
if_false->SetJoinId(expr->id());
|
||||
if_false->SetJoinId(ast_id);
|
||||
HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
|
||||
if_false->Goto(false_target, function_state()->drop_extra());
|
||||
}
|
||||
@ -5340,7 +5367,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
return true;
|
||||
|
||||
} else if (function_return()->HasPredecessor()) {
|
||||
function_return()->SetJoinId(expr->id());
|
||||
function_return()->SetJoinId(ast_id);
|
||||
set_current_block(function_return());
|
||||
} else {
|
||||
set_current_block(NULL);
|
||||
@ -5350,6 +5377,34 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
}
|
||||
|
||||
|
||||
bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
|
||||
// The function call we are inlining is a method call if the call
|
||||
// is a property call.
|
||||
CallKind call_kind = (expr->expression()->AsProperty() == NULL)
|
||||
? CALL_AS_FUNCTION
|
||||
: CALL_AS_METHOD;
|
||||
|
||||
return TryInline(call_kind,
|
||||
expr->target(),
|
||||
expr->arguments(),
|
||||
NULL,
|
||||
expr->id(),
|
||||
expr->ReturnId(),
|
||||
drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
|
||||
}
|
||||
|
||||
|
||||
bool HGraphBuilder::TryInlineConstruct(CallNew* expr, HValue* receiver) {
|
||||
return TryInline(CALL_AS_FUNCTION,
|
||||
expr->target(),
|
||||
expr->arguments(),
|
||||
receiver,
|
||||
expr->id(),
|
||||
expr->ReturnId(),
|
||||
CONSTRUCT_CALL_RETURN);
|
||||
}
|
||||
|
||||
|
||||
bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) {
|
||||
if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
|
||||
BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
|
||||
@ -5679,7 +5734,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
||||
} else {
|
||||
AddCheckConstantFunction(expr, receiver, receiver_map, true);
|
||||
|
||||
if (TryInline(expr)) return;
|
||||
if (TryInlineCall(expr)) return;
|
||||
call = PreProcessCall(
|
||||
new(zone()) HCallConstantFunction(expr->target(),
|
||||
argument_count));
|
||||
@ -5743,7 +5798,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (TryInline(expr)) return;
|
||||
if (TryInlineCall(expr)) return;
|
||||
call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
|
||||
argument_count));
|
||||
} else {
|
||||
@ -5779,7 +5834,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryInline(expr, true)) { // Drop function from environment.
|
||||
if (TryInlineCall(expr, true)) { // Drop function from environment.
|
||||
return;
|
||||
} else {
|
||||
call = PreProcessCall(new(zone()) HInvokeFunction(context,
|
||||
@ -5809,25 +5864,64 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
||||
}
|
||||
|
||||
|
||||
// Checks whether allocation using the given constructor can be inlined.
|
||||
static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
||||
return constructor->has_initial_map() &&
|
||||
constructor->initial_map()->instance_type() == JS_OBJECT_TYPE;
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitCallNew(CallNew* expr) {
|
||||
ASSERT(!HasStackOverflow());
|
||||
ASSERT(current_block() != NULL);
|
||||
ASSERT(current_block()->HasPredecessor());
|
||||
// The constructor function is also used as the receiver argument to the
|
||||
// JS construct call builtin.
|
||||
HValue* constructor = NULL;
|
||||
CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
|
||||
CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
||||
|
||||
expr->RecordTypeFeedback(oracle());
|
||||
int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
||||
HValue* context = environment()->LookupContext();
|
||||
|
||||
// The constructor is both an operand to the instruction and an argument
|
||||
// to the construct call.
|
||||
int arg_count = expr->arguments()->length() + 1; // Plus constructor.
|
||||
HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
|
||||
call->set_position(expr->position());
|
||||
Drop(arg_count);
|
||||
return ast_context()->ReturnInstruction(call, expr->id());
|
||||
if (FLAG_inline_construct &&
|
||||
expr->IsMonomorphic() &&
|
||||
IsAllocationInlineable(expr->target())) {
|
||||
// The constructor function is on the stack in the unoptimized code
|
||||
// during evaluation of the arguments.
|
||||
CHECK_ALIVE(VisitForValue(expr->expression()));
|
||||
HValue* function = Top();
|
||||
CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
||||
Handle<JSFunction> constructor = expr->target();
|
||||
AddInstruction(new(zone()) HCheckFunction(function, constructor));
|
||||
|
||||
// Replace the constructor function with a newly allocated receiver.
|
||||
HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
|
||||
// Index of the receiver from the top of the expression stack.
|
||||
const int receiver_index = argument_count - 1;
|
||||
AddInstruction(receiver);
|
||||
ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
|
||||
environment()->SetExpressionStackAt(receiver_index, receiver);
|
||||
|
||||
if (TryInlineConstruct(expr, receiver)) return;
|
||||
|
||||
// TODO(mstarzinger): For now we remove the previous HAllocateObject and
|
||||
// add HPushArgument for the arguments in case inlining failed. What we
|
||||
// actually should do is emit HInvokeFunction on the constructor instead
|
||||
// of using HCallNew as a fallback.
|
||||
receiver->DeleteAndReplaceWith(NULL);
|
||||
environment()->SetExpressionStackAt(receiver_index, function);
|
||||
HInstruction* call = PreProcessCall(
|
||||
new(zone()) HCallNew(context, function, argument_count));
|
||||
call->set_position(expr->position());
|
||||
return ast_context()->ReturnInstruction(call, expr->id());
|
||||
} else {
|
||||
// The constructor function is both an operand to the instruction and an
|
||||
// argument to the construct call.
|
||||
HValue* constructor = NULL;
|
||||
CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
|
||||
CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
||||
HInstruction* call =
|
||||
new(zone()) HCallNew(context, constructor, argument_count);
|
||||
Drop(argument_count);
|
||||
call->set_position(expr->position());
|
||||
return ast_context()->ReturnInstruction(call, expr->id());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6952,10 +7046,11 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
|
||||
void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
|
||||
ASSERT(call->arguments()->length() == 0);
|
||||
if (function_state()->outer() != NULL) {
|
||||
// We are generating graph for inlined function. Currently
|
||||
// constructor inlining is not supported and we can just return
|
||||
// false from %_IsConstructCall().
|
||||
return ast_context()->ReturnValue(graph()->GetConstantFalse());
|
||||
// We are generating graph for inlined function.
|
||||
HValue* value = function_state()->is_construct()
|
||||
? graph()->GetConstantTrue()
|
||||
: graph()->GetConstantFalse();
|
||||
return ast_context()->ReturnValue(value);
|
||||
} else {
|
||||
return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
|
||||
call->id());
|
||||
@ -7343,14 +7438,14 @@ HEnvironment::HEnvironment(HEnvironment* outer,
|
||||
: closure_(closure),
|
||||
values_(0),
|
||||
assigned_variables_(4),
|
||||
frame_type_(JS_FUNCTION),
|
||||
parameter_count_(0),
|
||||
specials_count_(1),
|
||||
local_count_(0),
|
||||
outer_(outer),
|
||||
pop_count_(0),
|
||||
push_count_(0),
|
||||
ast_id_(AstNode::kNoNumber),
|
||||
arguments_adaptor_(false) {
|
||||
ast_id_(AstNode::kNoNumber) {
|
||||
Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
|
||||
}
|
||||
|
||||
@ -7358,31 +7453,32 @@ HEnvironment::HEnvironment(HEnvironment* outer,
|
||||
HEnvironment::HEnvironment(const HEnvironment* other)
|
||||
: values_(0),
|
||||
assigned_variables_(0),
|
||||
frame_type_(JS_FUNCTION),
|
||||
parameter_count_(0),
|
||||
specials_count_(1),
|
||||
local_count_(0),
|
||||
outer_(NULL),
|
||||
pop_count_(0),
|
||||
push_count_(0),
|
||||
ast_id_(other->ast_id()),
|
||||
arguments_adaptor_(false) {
|
||||
ast_id_(other->ast_id()) {
|
||||
Initialize(other);
|
||||
}
|
||||
|
||||
|
||||
HEnvironment::HEnvironment(HEnvironment* outer,
|
||||
Handle<JSFunction> closure,
|
||||
FrameType frame_type,
|
||||
int arguments)
|
||||
: closure_(closure),
|
||||
values_(arguments),
|
||||
assigned_variables_(0),
|
||||
frame_type_(frame_type),
|
||||
parameter_count_(arguments),
|
||||
local_count_(0),
|
||||
outer_(outer),
|
||||
pop_count_(0),
|
||||
push_count_(0),
|
||||
ast_id_(AstNode::kNoNumber),
|
||||
arguments_adaptor_(true) {
|
||||
ast_id_(AstNode::kNoNumber) {
|
||||
}
|
||||
|
||||
|
||||
@ -7403,13 +7499,13 @@ void HEnvironment::Initialize(const HEnvironment* other) {
|
||||
closure_ = other->closure();
|
||||
values_.AddAll(other->values_);
|
||||
assigned_variables_.AddAll(other->assigned_variables_);
|
||||
frame_type_ = other->frame_type_;
|
||||
parameter_count_ = other->parameter_count_;
|
||||
local_count_ = other->local_count_;
|
||||
if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
|
||||
pop_count_ = other->pop_count_;
|
||||
push_count_ = other->push_count_;
|
||||
ast_id_ = other->ast_id_;
|
||||
arguments_adaptor_ = other->arguments_adaptor_;
|
||||
}
|
||||
|
||||
|
||||
@ -7510,13 +7606,28 @@ HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
|
||||
}
|
||||
|
||||
|
||||
HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
|
||||
Handle<JSFunction> target,
|
||||
FrameType frame_type,
|
||||
int arguments) const {
|
||||
HEnvironment* new_env = new(closure()->GetIsolate()->zone())
|
||||
HEnvironment(outer, target, frame_type, arguments + 1);
|
||||
for (int i = 0; i <= arguments; ++i) { // Include receiver.
|
||||
new_env->Push(ExpressionStackAt(arguments - i));
|
||||
}
|
||||
new_env->ClearHistory();
|
||||
return new_env;
|
||||
}
|
||||
|
||||
|
||||
HEnvironment* HEnvironment::CopyForInlining(
|
||||
Handle<JSFunction> target,
|
||||
int arguments,
|
||||
FunctionLiteral* function,
|
||||
HConstant* undefined,
|
||||
CallKind call_kind) const {
|
||||
ASSERT(!is_arguments_adaptor());
|
||||
CallKind call_kind,
|
||||
bool is_construct) const {
|
||||
ASSERT(frame_type() == JS_FUNCTION);
|
||||
|
||||
Zone* zone = closure()->GetIsolate()->zone();
|
||||
|
||||
@ -7527,13 +7638,16 @@ HEnvironment* HEnvironment::CopyForInlining(
|
||||
outer->Drop(arguments + 1); // Including receiver.
|
||||
outer->ClearHistory();
|
||||
|
||||
if (is_construct) {
|
||||
// Create artificial constructor stub environment. The receiver should
|
||||
// actually be the constructor function, but we pass the newly allocated
|
||||
// object instead, DoComputeConstructStubFrame() relies on that.
|
||||
outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
|
||||
}
|
||||
|
||||
if (arity != arguments) {
|
||||
// Create artificial arguments adaptation environment.
|
||||
outer = new(zone) HEnvironment(outer, target, arguments + 1);
|
||||
for (int i = 0; i <= arguments; ++i) { // Include receiver.
|
||||
outer->Push(ExpressionStackAt(arguments - i));
|
||||
}
|
||||
outer->ClearHistory();
|
||||
outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
|
||||
}
|
||||
|
||||
HEnvironment* inner =
|
||||
|
@ -361,19 +361,19 @@ class HGraph: public ZoneObject {
|
||||
Zone* HBasicBlock::zone() { return graph_->zone(); }
|
||||
|
||||
|
||||
// Type of stack frame an environment might refer to.
|
||||
enum FrameType { JS_FUNCTION, JS_CONSTRUCT, ARGUMENTS_ADAPTOR };
|
||||
|
||||
|
||||
class HEnvironment: public ZoneObject {
|
||||
public:
|
||||
HEnvironment(HEnvironment* outer,
|
||||
Scope* scope,
|
||||
Handle<JSFunction> closure);
|
||||
|
||||
bool is_arguments_adaptor() const {
|
||||
return arguments_adaptor_;
|
||||
}
|
||||
|
||||
HEnvironment* DiscardInlined(bool drop_extra) {
|
||||
HEnvironment* outer = outer_->is_arguments_adaptor() ?
|
||||
outer_->outer_ : outer_;
|
||||
HEnvironment* outer = outer_;
|
||||
while (outer->frame_type() != JS_FUNCTION) outer = outer->outer_;
|
||||
if (drop_extra) outer->Drop(1);
|
||||
return outer;
|
||||
}
|
||||
@ -384,6 +384,7 @@ class HEnvironment: public ZoneObject {
|
||||
const ZoneList<int>* assigned_variables() const {
|
||||
return &assigned_variables_;
|
||||
}
|
||||
FrameType frame_type() const { return frame_type_; }
|
||||
int parameter_count() const { return parameter_count_; }
|
||||
int specials_count() const { return specials_count_; }
|
||||
int local_count() const { return local_count_; }
|
||||
@ -469,7 +470,8 @@ class HEnvironment: public ZoneObject {
|
||||
int arguments,
|
||||
FunctionLiteral* function,
|
||||
HConstant* undefined,
|
||||
CallKind call_kind) const;
|
||||
CallKind call_kind,
|
||||
bool is_construct) const;
|
||||
|
||||
void AddIncomingEdge(HBasicBlock* block, HEnvironment* other);
|
||||
|
||||
@ -490,9 +492,17 @@ class HEnvironment: public ZoneObject {
|
||||
private:
|
||||
explicit HEnvironment(const HEnvironment* other);
|
||||
|
||||
// Create an argument adaptor environment.
|
||||
HEnvironment(HEnvironment* outer, Handle<JSFunction> closure, int arguments);
|
||||
HEnvironment(HEnvironment* outer,
|
||||
Handle<JSFunction> closure,
|
||||
FrameType frame_type,
|
||||
int arguments);
|
||||
|
||||
// Create an artificial stub environment (e.g. for argument adaptor or
|
||||
// constructor stub).
|
||||
HEnvironment* CreateStubEnvironment(HEnvironment* outer,
|
||||
Handle<JSFunction> target,
|
||||
FrameType frame_type,
|
||||
int arguments) const;
|
||||
|
||||
// True if index is included in the expression stack part of the environment.
|
||||
bool HasExpressionAt(int index) const;
|
||||
@ -515,6 +525,7 @@ class HEnvironment: public ZoneObject {
|
||||
// Value array [parameters] [specials] [locals] [temporaries].
|
||||
ZoneList<HValue*> values_;
|
||||
ZoneList<int> assigned_variables_;
|
||||
FrameType frame_type_;
|
||||
int parameter_count_;
|
||||
int specials_count_;
|
||||
int local_count_;
|
||||
@ -522,7 +533,6 @@ class HEnvironment: public ZoneObject {
|
||||
int pop_count_;
|
||||
int push_count_;
|
||||
int ast_id_;
|
||||
bool arguments_adaptor_;
|
||||
};
|
||||
|
||||
|
||||
@ -650,18 +660,27 @@ class TestContext: public AstContext {
|
||||
};
|
||||
|
||||
|
||||
enum ReturnHandlingFlag {
|
||||
NORMAL_RETURN,
|
||||
DROP_EXTRA_ON_RETURN,
|
||||
CONSTRUCT_CALL_RETURN
|
||||
};
|
||||
|
||||
|
||||
class FunctionState {
|
||||
public:
|
||||
|
||||
FunctionState(HGraphBuilder* owner,
|
||||
CompilationInfo* info,
|
||||
TypeFeedbackOracle* oracle,
|
||||
bool drop_extra);
|
||||
ReturnHandlingFlag return_handling);
|
||||
~FunctionState();
|
||||
|
||||
CompilationInfo* compilation_info() { return compilation_info_; }
|
||||
TypeFeedbackOracle* oracle() { return oracle_; }
|
||||
AstContext* call_context() { return call_context_; }
|
||||
bool drop_extra() { return drop_extra_; }
|
||||
bool drop_extra() { return return_handling_ == DROP_EXTRA_ON_RETURN; }
|
||||
bool is_construct() { return return_handling_ == CONSTRUCT_CALL_RETURN; }
|
||||
HBasicBlock* function_return() { return function_return_; }
|
||||
TestContext* test_context() { return test_context_; }
|
||||
void ClearInlinedTestContext() {
|
||||
@ -681,11 +700,13 @@ class FunctionState {
|
||||
// inlined. NULL when not inlining.
|
||||
AstContext* call_context_;
|
||||
|
||||
// Indicate if we have to drop an extra value from the environment on
|
||||
// return from inlined functions.
|
||||
bool drop_extra_;
|
||||
// Indicate whether we have to perform special handling on return from
|
||||
// inlined functions.
|
||||
// - DROP_EXTRA_ON_RETURN: Drop an extra value from the environment.
|
||||
// - CONSTRUCT_CALL_RETURN: Either use allocated receiver or return value.
|
||||
ReturnHandlingFlag return_handling_;
|
||||
|
||||
// When inlining in an effect of value context, this is the return block.
|
||||
// When inlining in an effect or value context, this is the return block.
|
||||
// It is NULL otherwise. When inlining in a test context, there are a
|
||||
// pair of return blocks in the context. When not inlining, there is no
|
||||
// local return point.
|
||||
@ -825,7 +846,6 @@ class HGraphBuilder: public AstVisitor {
|
||||
CompilationInfo* info() const {
|
||||
return function_state()->compilation_info();
|
||||
}
|
||||
|
||||
AstContext* call_context() const {
|
||||
return function_state()->call_context();
|
||||
}
|
||||
@ -922,7 +942,7 @@ class HGraphBuilder: public AstVisitor {
|
||||
|
||||
// Remove the arguments from the bailout environment and emit instructions
|
||||
// to push them as outgoing parameters.
|
||||
template <int V> HInstruction* PreProcessCall(HCall<V>* call);
|
||||
template <class Instruction> HInstruction* PreProcessCall(Instruction* call);
|
||||
|
||||
void TraceRepresentation(Token::Value op,
|
||||
TypeInfo info,
|
||||
@ -954,11 +974,20 @@ class HGraphBuilder: public AstVisitor {
|
||||
// Try to optimize fun.apply(receiver, arguments) pattern.
|
||||
bool TryCallApply(Call* expr);
|
||||
|
||||
bool TryInline(Call* expr, bool drop_extra = false);
|
||||
bool TryInline(CallKind call_kind,
|
||||
Handle<JSFunction> target,
|
||||
ZoneList<Expression*>* arguments,
|
||||
HValue* receiver,
|
||||
int ast_id,
|
||||
int return_id,
|
||||
ReturnHandlingFlag return_handling);
|
||||
|
||||
bool TryInlineCall(Call* expr, bool drop_extra = false);
|
||||
bool TryInlineConstruct(CallNew* expr, HValue* receiver);
|
||||
bool TryInlineBuiltinMethodCall(Call* expr,
|
||||
HValue* receiver,
|
||||
Handle<Map> receiver_map,
|
||||
CheckType check_type);
|
||||
HValue* receiver,
|
||||
Handle<Map> receiver_map,
|
||||
CheckType check_type);
|
||||
bool TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra);
|
||||
|
||||
// If --trace-inlining, print a line of the inlining trace. Inlining
|
||||
|
@ -324,6 +324,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
NullCallWrapper(), CALL_AS_METHOD);
|
||||
}
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
if (!is_api_function && !count_constructions) {
|
||||
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
|
||||
}
|
||||
|
||||
// Restore context from the frame.
|
||||
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
@ -1639,7 +1644,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
__ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ call(edx);
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
|
||||
|
||||
// Leave frame and return.
|
||||
LeaveArgumentsAdaptorFrame(masm);
|
||||
__ ret(0);
|
||||
|
@ -467,7 +467,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
@ -489,16 +488,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -508,7 +504,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
@ -520,7 +515,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// A marker value is used in place of the context.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t context = reinterpret_cast<intptr_t>(
|
||||
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
output_frame->SetFrameSlot(output_offset, context);
|
||||
@ -531,7 +525,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -541,7 +534,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -561,6 +553,110 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" translating construct stub => height=%d\n", height_in_bytes);
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = 6 * kPointerSize;
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::CONSTRUCT);
|
||||
|
||||
// Construct stub can not be topmost or bottommost.
|
||||
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
|
||||
ASSERT(output_[frame_index] == NULL);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address of the frame is computed from the previous
|
||||
// frame's top and this frame's size.
|
||||
uint32_t top_address;
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
|
||||
top_address + output_offset, output_offset, callers_pc);
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
output_frame->SetFp(fp_value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
|
||||
fp_value, output_offset, value);
|
||||
}
|
||||
|
||||
// The context can be gotten from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
value = output_[frame_index - 1]->GetContext();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// A marker value is used in place of the function.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::CONSTRUCT));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function (construct sentinel)\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n",
|
||||
top_address + output_offset, output_offset, value, height - 1);
|
||||
}
|
||||
|
||||
// The newly allocated object was passed as receiver in the artificial
|
||||
// constructor stub environment created by HEnvironment::CopyForInlining().
|
||||
output_offset -= kPointerSize;
|
||||
value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; allocated receiver\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
ASSERT(0 == output_offset);
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
|
||||
uint32_t pc = reinterpret_cast<uint32_t>(
|
||||
construct_stub->instruction_start() +
|
||||
isolate_->heap()->construct_stub_deopt_pc_offset()->value());
|
||||
output_frame->SetPc(pc);
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
int node_id = iterator->Next();
|
||||
@ -672,6 +768,7 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
value = reinterpret_cast<uint32_t>(function->context());
|
||||
}
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
output_frame->SetContext(value);
|
||||
if (is_topmost) output_frame->SetRegister(esi.code(), value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n",
|
||||
|
@ -2400,6 +2400,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
CallConstructStub stub(flags);
|
||||
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
|
||||
context()->Plug(eax);
|
||||
}
|
||||
|
||||
|
@ -394,10 +394,18 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
|
||||
|
||||
WriteTranslation(environment->outer(), translation);
|
||||
int closure_id = DefineDeoptimizationLiteral(environment->closure());
|
||||
if (environment->is_arguments_adaptor()) {
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
} else {
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
switch (environment->frame_type()) {
|
||||
case JS_FUNCTION:
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
break;
|
||||
case JS_CONSTRUCT:
|
||||
translation->BeginConstructStubFrame(closure_id, translation_size);
|
||||
break;
|
||||
case ARGUMENTS_ADAPTOR:
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
for (int i = 0; i < translation_size; ++i) {
|
||||
LOperand* value = environment->values()->at(i);
|
||||
@ -550,7 +558,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(
|
||||
int jsframe_count = 0;
|
||||
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
|
||||
++frame_count;
|
||||
if (!e->is_arguments_adaptor()) {
|
||||
if (e->frame_type() == JS_FUNCTION) {
|
||||
++jsframe_count;
|
||||
}
|
||||
}
|
||||
@ -4194,6 +4202,43 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr);
|
||||
|
||||
// TODO(mstarzinger): Implement inlined version instead of jumping to
|
||||
// deferred runtime call.
|
||||
__ jmp(deferred->entry());
|
||||
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ Set(result, Immediate(0));
|
||||
|
||||
PushSafepointRegistersScope scope(this);
|
||||
__ PushHeapObject(constructor);
|
||||
CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr, instr->context());
|
||||
__ StoreToSafepointRegisterSlot(result, eax);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
|
||||
ASSERT(ToRegister(instr->context()).is(esi));
|
||||
Heap* heap = isolate()->heap();
|
||||
|
@ -107,6 +107,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredStackCheck(LStackCheck* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -1005,11 +1005,12 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
LEnvironment* outer =
|
||||
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
|
||||
int ast_id = hydrogen_env->ast_id();
|
||||
ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
|
||||
ASSERT(ast_id != AstNode::kNoNumber ||
|
||||
hydrogen_env->frame_type() != JS_FUNCTION);
|
||||
int value_count = hydrogen_env->length();
|
||||
LEnvironment* result =
|
||||
new(zone()) LEnvironment(hydrogen_env->closure(),
|
||||
hydrogen_env->is_arguments_adaptor(),
|
||||
hydrogen_env->frame_type(),
|
||||
ast_id,
|
||||
hydrogen_env->parameter_count(),
|
||||
argument_count_,
|
||||
@ -1031,7 +1032,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
result->AddValue(op, value->representation());
|
||||
}
|
||||
|
||||
if (!hydrogen_env->is_arguments_adaptor()) {
|
||||
if (hydrogen_env->frame_type() == JS_FUNCTION) {
|
||||
*argument_index_accumulator = argument_index;
|
||||
}
|
||||
|
||||
@ -2213,6 +2214,13 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
LOperand* context = UseFixed(instr->context(), esi);
|
||||
LAllocateObject* result = new(zone()) LAllocateObject(context);
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) {
|
||||
LOperand* context = UseFixed(instr->context(), esi);
|
||||
return MarkAsCall(
|
||||
@ -2385,7 +2393,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
instr->arguments_count(),
|
||||
instr->function(),
|
||||
undefined,
|
||||
instr->call_kind());
|
||||
instr->call_kind(),
|
||||
instr->is_construct());
|
||||
current_block_->UpdateEnvironment(inner);
|
||||
chunk_->AddInlinedClosure(instr->closure());
|
||||
return NULL;
|
||||
|
@ -43,6 +43,7 @@ class LCodeGen;
|
||||
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -1995,6 +1996,19 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
LAllocateObject(LOperand* context) {
|
||||
inputs_[0] = context;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
|
||||
LOperand* context() { return inputs_[0]; }
|
||||
};
|
||||
|
||||
|
||||
class LFastLiteral: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LFastLiteral(LOperand* context) {
|
||||
|
@ -438,14 +438,14 @@ class LPointerMap: public ZoneObject {
|
||||
class LEnvironment: public ZoneObject {
|
||||
public:
|
||||
LEnvironment(Handle<JSFunction> closure,
|
||||
bool is_arguments_adaptor,
|
||||
FrameType frame_type,
|
||||
int ast_id,
|
||||
int parameter_count,
|
||||
int argument_count,
|
||||
int value_count,
|
||||
LEnvironment* outer)
|
||||
: closure_(closure),
|
||||
is_arguments_adaptor_(is_arguments_adaptor),
|
||||
frame_type_(frame_type),
|
||||
arguments_stack_height_(argument_count),
|
||||
deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
|
||||
translation_index_(-1),
|
||||
@ -459,6 +459,7 @@ class LEnvironment: public ZoneObject {
|
||||
outer_(outer) { }
|
||||
|
||||
Handle<JSFunction> closure() const { return closure_; }
|
||||
FrameType frame_type() const { return frame_type_; }
|
||||
int arguments_stack_height() const { return arguments_stack_height_; }
|
||||
int deoptimization_index() const { return deoptimization_index_; }
|
||||
int translation_index() const { return translation_index_; }
|
||||
@ -503,11 +504,9 @@ class LEnvironment: public ZoneObject {
|
||||
|
||||
void PrintTo(StringStream* stream);
|
||||
|
||||
bool is_arguments_adaptor() const { return is_arguments_adaptor_; }
|
||||
|
||||
private:
|
||||
Handle<JSFunction> closure_;
|
||||
bool is_arguments_adaptor_;
|
||||
FrameType frame_type_;
|
||||
int arguments_stack_height_;
|
||||
int deoptimization_index_;
|
||||
int translation_index_;
|
||||
|
@ -2444,6 +2444,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
CallConstructStub stub(flags);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
|
||||
context()->Plug(v0);
|
||||
}
|
||||
|
||||
|
@ -4222,6 +4222,44 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr);
|
||||
|
||||
// TODO(mstarzinger): Implement inlined version instead of jumping to
|
||||
// deferred runtime call.
|
||||
__ jmp(deferred->entry());
|
||||
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ mov(result, zero_reg);
|
||||
|
||||
PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
|
||||
__ LoadHeapObject(a0, constructor);
|
||||
__ push(a0);
|
||||
CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr);
|
||||
__ StoreToSafepointRegisterSlot(v0, result);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
|
||||
Heap* heap = isolate()->heap();
|
||||
ElementsKind boilerplate_elements_kind =
|
||||
|
@ -112,6 +112,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredStackCheck(LStackCheck* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -2098,6 +2098,12 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
LAllocateObject* result = new LAllocateObject();
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) {
|
||||
return MarkAsCall(DefineFixed(new LFastLiteral, v0), instr);
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class LCodeGen;
|
||||
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -1917,6 +1918,13 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LFastLiteral: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal")
|
||||
|
@ -8257,9 +8257,15 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME: {
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::CONSTRUCT_STUB_FRAME: {
|
||||
int function_id = iterator.Next();
|
||||
JSFunction* function =
|
||||
JSFunction::cast(LiteralArray()->get(function_id));
|
||||
unsigned height = iterator.Next();
|
||||
PrintF(out, "{arguments adaptor, height=%d}", height);
|
||||
PrintF(out, "{function=");
|
||||
function->PrintName(out);
|
||||
PrintF(out, ", height=%u}", height);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -882,14 +882,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_IsConstructCall) {
|
||||
NoHandleAllocation ha;
|
||||
ASSERT(args.length() == 0);
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
return isolate->heap()->ToBoolean(it.frame()->IsConstructor());
|
||||
}
|
||||
|
||||
|
||||
// Recursively traverses hidden prototypes if property is not found
|
||||
static void GetOwnPropertyImplementation(JSObject* obj,
|
||||
String* name,
|
||||
@ -10640,6 +10632,7 @@ class FrameInspector {
|
||||
frame, inlined_jsframe_index, isolate);
|
||||
}
|
||||
has_adapted_arguments_ = frame_->has_adapted_arguments();
|
||||
is_bottommost_ = inlined_jsframe_index == 0;
|
||||
is_optimized_ = frame_->is_optimized();
|
||||
}
|
||||
|
||||
@ -10677,6 +10670,11 @@ class FrameInspector {
|
||||
? deoptimized_frame_->GetSourcePosition()
|
||||
: frame_->LookupCode()->SourcePosition(frame_->pc());
|
||||
}
|
||||
bool IsConstructor() {
|
||||
return is_optimized_ && !is_bottommost_
|
||||
? deoptimized_frame_->HasConstructStub()
|
||||
: frame_->IsConstructor();
|
||||
}
|
||||
|
||||
// To inspect all the provided arguments the frame might need to be
|
||||
// replaced with the arguments frame.
|
||||
@ -10692,6 +10690,7 @@ class FrameInspector {
|
||||
DeoptimizedFrameInfo* deoptimized_frame_;
|
||||
Isolate* isolate_;
|
||||
bool is_optimized_;
|
||||
bool is_bottommost_;
|
||||
bool has_adapted_arguments_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameInspector);
|
||||
@ -10721,6 +10720,16 @@ static SaveContext* FindSavedContextForFrame(Isolate* isolate,
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_IsConstructCall) {
|
||||
NoHandleAllocation ha;
|
||||
ASSERT(args.length() == 0);
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
FrameInspector frame_inspector(frame, frame->GetInlineCount() - 1, isolate);
|
||||
return isolate->heap()->ToBoolean(frame_inspector.IsConstructor());
|
||||
}
|
||||
|
||||
|
||||
// Return an array with frame details
|
||||
// args[0]: number: break id
|
||||
// args[1]: number: frame index
|
||||
@ -10785,9 +10794,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
// Find source position in unoptimized code.
|
||||
int position = frame_inspector.GetSourcePosition();
|
||||
|
||||
// Check for constructor frame. Inlined frames cannot be construct calls.
|
||||
bool inlined_frame = is_optimized && inlined_jsframe_index != 0;
|
||||
bool constructor = !inlined_frame && it.frame()->IsConstructor();
|
||||
// Check for constructor frame.
|
||||
bool constructor = frame_inspector.IsConstructor();
|
||||
|
||||
// Get scope info and read from it for local variable information.
|
||||
Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
|
||||
|
@ -256,6 +256,11 @@ Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) {
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(CallNew* expr) {
|
||||
return Handle<JSFunction>::cast(GetInfo(expr->id()));
|
||||
}
|
||||
|
||||
|
||||
bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
|
||||
return *GetInfo(expr->id()) ==
|
||||
isolate_->builtins()->builtin(id);
|
||||
|
@ -267,6 +267,7 @@ class TypeFeedbackOracle BASE_EMBEDDED {
|
||||
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
|
||||
|
||||
Handle<JSFunction> GetCallTarget(Call* expr);
|
||||
Handle<JSFunction> GetCallNewTarget(CallNew* expr);
|
||||
|
||||
bool LoadIsBuiltin(Property* expr, Builtins::Name id);
|
||||
|
||||
|
@ -329,6 +329,14 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
NullCallWrapper(), CALL_AS_METHOD);
|
||||
}
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
// TODO(849): Once Generate_StringConstructCode doesn't reuse this
|
||||
// generator, we can drop the third condition below!
|
||||
if (!is_api_function && !count_constructions &&
|
||||
masm->isolate()->heap()->construct_stub_deopt_pc_offset() == 0) {
|
||||
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
|
||||
}
|
||||
|
||||
// Restore context from the frame.
|
||||
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
@ -1538,7 +1546,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
__ bind(&invoke);
|
||||
__ call(rdx);
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
|
||||
|
||||
// Leave frame and return.
|
||||
LeaveArgumentsAdaptorFrame(masm);
|
||||
__ ret(0);
|
||||
|
@ -347,7 +347,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
@ -369,16 +368,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -389,7 +385,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
@ -402,7 +397,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// A marker value is used in place of the context.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t context = reinterpret_cast<intptr_t>(
|
||||
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
output_frame->SetFrameSlot(output_offset, context);
|
||||
@ -414,7 +408,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -425,7 +418,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -446,6 +438,116 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" translating construct stub => height=%d\n", height_in_bytes);
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = 6 * kPointerSize;
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::CONSTRUCT);
|
||||
|
||||
// Construct stub can not be topmost or bottommost.
|
||||
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
|
||||
ASSERT(output_[frame_index] == NULL);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address of the frame is computed from the previous
|
||||
// frame's top and this frame's size.
|
||||
intptr_t top_address;
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; caller's pc\n",
|
||||
top_address + output_offset, output_offset, callers_pc);
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
output_frame->SetFp(fp_value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; caller's fp\n",
|
||||
fp_value, output_offset, value);
|
||||
}
|
||||
|
||||
// The context can be gotten from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
value = output_[frame_index - 1]->GetContext();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; context\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// A marker value is used in place of the function.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::CONSTRUCT));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; function (construct sentinel)\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; argc (%d)\n",
|
||||
top_address + output_offset, output_offset, value, height - 1);
|
||||
}
|
||||
|
||||
// The newly allocated object was passed as receiver in the artificial
|
||||
// constructor stub environment created by HEnvironment::CopyForInlining().
|
||||
output_offset -= kPointerSize;
|
||||
value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; allocated receiver\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
ASSERT(0 == output_offset);
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
|
||||
intptr_t pc = reinterpret_cast<intptr_t>(
|
||||
construct_stub->instruction_start() +
|
||||
isolate_->heap()->construct_stub_deopt_pc_offset()->value());
|
||||
output_frame->SetPc(pc);
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
int node_id = iterator->Next();
|
||||
@ -555,6 +657,7 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
value = reinterpret_cast<intptr_t>(function->context());
|
||||
}
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
output_frame->SetContext(value);
|
||||
if (is_topmost) output_frame->SetRegister(rsi.code(), value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
|
@ -2280,6 +2280,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
CallConstructStub stub(flags);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
|
||||
context()->Plug(rax);
|
||||
}
|
||||
|
||||
|
@ -368,10 +368,18 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
|
||||
|
||||
WriteTranslation(environment->outer(), translation);
|
||||
int closure_id = DefineDeoptimizationLiteral(environment->closure());
|
||||
if (environment->is_arguments_adaptor()) {
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
} else {
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
switch (environment->frame_type()) {
|
||||
case JS_FUNCTION:
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
break;
|
||||
case JS_CONSTRUCT:
|
||||
translation->BeginConstructStubFrame(closure_id, translation_size);
|
||||
break;
|
||||
case ARGUMENTS_ADAPTOR:
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
for (int i = 0; i < translation_size; ++i) {
|
||||
LOperand* value = environment->values()->at(i);
|
||||
@ -511,7 +519,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment,
|
||||
int jsframe_count = 0;
|
||||
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
|
||||
++frame_count;
|
||||
if (!e->is_arguments_adaptor()) {
|
||||
if (e->frame_type() == JS_FUNCTION) {
|
||||
++jsframe_count;
|
||||
}
|
||||
}
|
||||
@ -3925,6 +3933,43 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr);
|
||||
|
||||
// TODO(mstarzinger): Implement inlined version instead of jumping to
|
||||
// deferred runtime call.
|
||||
__ jmp(deferred->entry());
|
||||
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ Set(result, 0);
|
||||
|
||||
PushSafepointRegistersScope scope(this);
|
||||
__ PushHeapObject(constructor);
|
||||
CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr);
|
||||
__ StoreToSafepointRegisterSlot(result, rax);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
|
||||
Heap* heap = isolate()->heap();
|
||||
ElementsKind boilerplate_elements_kind =
|
||||
|
@ -99,6 +99,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredStackCheck(LStackCheck* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -990,10 +990,11 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
LEnvironment* outer =
|
||||
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
|
||||
int ast_id = hydrogen_env->ast_id();
|
||||
ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
|
||||
ASSERT(ast_id != AstNode::kNoNumber ||
|
||||
hydrogen_env->frame_type() != JS_FUNCTION);
|
||||
int value_count = hydrogen_env->length();
|
||||
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
|
||||
hydrogen_env->is_arguments_adaptor(),
|
||||
hydrogen_env->frame_type(),
|
||||
ast_id,
|
||||
hydrogen_env->parameter_count(),
|
||||
argument_count_,
|
||||
@ -1015,7 +1016,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
result->AddValue(op, value->representation());
|
||||
}
|
||||
|
||||
if (!hydrogen_env->is_arguments_adaptor()) {
|
||||
if (hydrogen_env->frame_type() == JS_FUNCTION) {
|
||||
*argument_index_accumulator = argument_index;
|
||||
}
|
||||
|
||||
@ -2094,6 +2095,12 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
LAllocateObject* result = new LAllocateObject();
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) {
|
||||
return MarkAsCall(DefineFixed(new LFastLiteral, rax), instr);
|
||||
}
|
||||
@ -2246,7 +2253,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
instr->arguments_count(),
|
||||
instr->function(),
|
||||
undefined,
|
||||
instr->call_kind());
|
||||
instr->call_kind(),
|
||||
instr->is_construct());
|
||||
current_block_->UpdateEnvironment(inner);
|
||||
chunk_->AddInlinedClosure(instr->closure());
|
||||
return NULL;
|
||||
|
@ -49,6 +49,7 @@ class LCodeGen;
|
||||
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -1910,6 +1911,13 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LFastLiteral: public LTemplateInstruction<1, 0, 0> {
|
||||
public:
|
||||
DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal")
|
||||
|
170
test/mjsunit/compiler/inline-construct.js
Normal file
170
test/mjsunit/compiler/inline-construct.js
Normal file
@ -0,0 +1,170 @@
|
||||
// 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 --inline-construct
|
||||
|
||||
// Test inlining of constructor calls.
|
||||
|
||||
function TestInlinedConstructor(closure) {
|
||||
var result;
|
||||
var counter = { value:0 };
|
||||
result = closure(11, 12, counter);
|
||||
assertEquals(23, result);
|
||||
assertEquals(1, counter.value);
|
||||
result = closure(23, 19, counter);
|
||||
assertEquals(42, result);
|
||||
assertEquals(2, counter.value);
|
||||
%OptimizeFunctionOnNextCall(closure);
|
||||
result = closure(1, 42, counter)
|
||||
assertEquals(43, result);
|
||||
assertEquals(3, counter.value);
|
||||
result = closure("foo", "bar", counter)
|
||||
assertEquals("foobar", result)
|
||||
assertEquals(4, counter.value);
|
||||
}
|
||||
|
||||
|
||||
// Test constructor returning nothing in all contexts.
|
||||
function c1(a, b, counter) {
|
||||
this.x = a + b;
|
||||
counter.value++;
|
||||
}
|
||||
function c1_value_context(a, b, counter) {
|
||||
var obj = new c1(a, b, counter);
|
||||
return obj.x;
|
||||
}
|
||||
function c1_test_context(a, b, counter) {
|
||||
if (!new c1(a, b, counter)) {
|
||||
assertUnreachable("should not happen");
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
function c1_effect_context(a, b, counter) {
|
||||
new c1(a, b, counter);
|
||||
return a + b;
|
||||
}
|
||||
TestInlinedConstructor(c1_value_context);
|
||||
TestInlinedConstructor(c1_test_context);
|
||||
TestInlinedConstructor(c1_effect_context);
|
||||
|
||||
|
||||
// Test constructor returning an object in all contexts.
|
||||
function c2(a, b, counter) {
|
||||
var obj = new Object();
|
||||
obj.x = a + b;
|
||||
counter.value++;
|
||||
return obj;
|
||||
}
|
||||
function c2_value_context(a, b, counter) {
|
||||
var obj = new c2(a, b, counter);
|
||||
return obj.x;
|
||||
}
|
||||
function c2_test_context(a, b, counter) {
|
||||
if (!new c2(a, b, counter)) {
|
||||
assertUnreachable("should not happen");
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
function c2_effect_context(a, b, counter) {
|
||||
new c2(a, b, counter);
|
||||
return a + b;
|
||||
}
|
||||
TestInlinedConstructor(c2_value_context);
|
||||
TestInlinedConstructor(c2_test_context);
|
||||
TestInlinedConstructor(c2_effect_context);
|
||||
|
||||
|
||||
// Test constructor returning a primitive value in all contexts.
|
||||
function c3(a, b, counter) {
|
||||
this.x = a + b;
|
||||
counter.value++;
|
||||
return "not an object";
|
||||
}
|
||||
function c3_value_context(a, b, counter) {
|
||||
var obj = new c3(a, b, counter);
|
||||
return obj.x;
|
||||
}
|
||||
function c3_test_context(a, b, counter) {
|
||||
if (!new c3(a, b, counter)) {
|
||||
assertUnreachable("should not happen");
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
function c3_effect_context(a, b, counter) {
|
||||
new c3(a, b, counter);
|
||||
return a + b;
|
||||
}
|
||||
TestInlinedConstructor(c3_value_context);
|
||||
TestInlinedConstructor(c3_test_context);
|
||||
TestInlinedConstructor(c3_effect_context);
|
||||
|
||||
|
||||
// Test constructor called with too many arguments.
|
||||
function c_too_many(a, b) {
|
||||
this.x = a + b;
|
||||
}
|
||||
function f_too_many(a, b, c) {
|
||||
var obj = new c_too_many(a, b, c);
|
||||
return obj.x;
|
||||
}
|
||||
assertEquals(23, f_too_many(11, 12, 1));
|
||||
assertEquals(42, f_too_many(23, 19, 1));
|
||||
%OptimizeFunctionOnNextCall(f_too_many);
|
||||
assertEquals(43, f_too_many(1, 42, 1));
|
||||
assertEquals("foobar", f_too_many("foo", "bar", "baz"))
|
||||
|
||||
|
||||
// Test constructor called with too few arguments.
|
||||
function c_too_few(a, b) {
|
||||
assertSame(undefined, b);
|
||||
this.x = a + 1;
|
||||
}
|
||||
function f_too_few(a) {
|
||||
var obj = new c_too_few(a);
|
||||
return obj.x;
|
||||
}
|
||||
assertEquals(12, f_too_few(11));
|
||||
assertEquals(24, f_too_few(23));
|
||||
%OptimizeFunctionOnNextCall(f_too_few);
|
||||
assertEquals(2, f_too_few(1));
|
||||
assertEquals("foo1", f_too_few("foo"))
|
||||
|
||||
|
||||
// Test constructor that cannot be inlined.
|
||||
function c_unsupported_syntax(a, b, counter) {
|
||||
try {
|
||||
this.x = a + b;
|
||||
counter.value++;
|
||||
} catch(e) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
function f_unsupported_syntax(a, b, counter) {
|
||||
var obj = new c_unsupported_syntax(a, b, counter);
|
||||
return obj.x;
|
||||
}
|
||||
TestInlinedConstructor(f_unsupported_syntax);
|
@ -25,7 +25,7 @@
|
||||
// (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: --expose-debug-as debug --allow-natives-syntax
|
||||
// Flags: --expose-debug-as debug --expose-gc --allow-natives-syntax --inline-construct
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -140,7 +140,13 @@ function listener(event, exec_state, event_data, data) {
|
||||
}
|
||||
|
||||
// Check for construct call.
|
||||
assertEquals(testingConstructCall && i == 4, frame.isConstructCall());
|
||||
if (i == 4) {
|
||||
assertEquals(testingConstructCall, frame.isConstructCall());
|
||||
} else if (i == 2) {
|
||||
assertTrue(frame.isConstructCall());
|
||||
} else {
|
||||
assertFalse(frame.isConstructCall());
|
||||
}
|
||||
|
||||
// When function f is optimized (1 means YES, see runtime.cc) we
|
||||
// expect an optimized frame for f with g1, g2 and g3 inlined.
|
||||
@ -204,7 +210,7 @@ function g1(i, x3, y3, z3) {
|
||||
var b3 = input[i].b;
|
||||
a3 = a3 + a3 / 100;
|
||||
b3 = b3 + b3 / 100;
|
||||
g2(i - 1, a3, b3);
|
||||
new g2(i - 1, a3, b3);
|
||||
};
|
||||
|
||||
function f(i, x4, y4) {
|
||||
@ -222,8 +228,11 @@ testingConstructCall = true;
|
||||
new f(input.length - 1, 11.11, 12.12);
|
||||
new f(input.length - 1, 11.11, 12.12, "");
|
||||
|
||||
// Make sure that the debug event listener vas invoked.
|
||||
// Make sure that the debug event listener was invoked.
|
||||
assertFalse(exception, "exception in listener " + exception)
|
||||
assertTrue(listenerComplete);
|
||||
|
||||
//Throw away type information for next run.
|
||||
gc();
|
||||
|
||||
Debug.setListener(null);
|
||||
|
@ -25,7 +25,7 @@
|
||||
// (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: --expose-debug-as debug --allow-natives-syntax
|
||||
// Flags: --expose-debug-as debug --expose-gc --allow-natives-syntax --inline-construct
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -130,7 +130,13 @@ function listener(event, exec_state, event_data, data) {
|
||||
}
|
||||
|
||||
// Check for construct call.
|
||||
assertEquals(testingConstructCall && i == 4, frame.isConstructCall());
|
||||
if (i == 4) {
|
||||
assertEquals(testingConstructCall, frame.isConstructCall());
|
||||
} else if (i == 2) {
|
||||
assertTrue(frame.isConstructCall());
|
||||
} else {
|
||||
assertFalse(frame.isConstructCall());
|
||||
}
|
||||
|
||||
// When function f is optimized (1 means YES, see runtime.cc) we
|
||||
// expect an optimized frame for f with g1, g2 and g3 inlined.
|
||||
@ -185,7 +191,7 @@ function g2(i) {
|
||||
function g1(i, x3, y3, z3) {
|
||||
var a3 = expected[i].locals.a3;
|
||||
var b3 = expected[i].locals.b3;
|
||||
g2(i - 1, a3, b3);
|
||||
new g2(i - 1, a3, b3);
|
||||
}
|
||||
|
||||
function f(i, x4, y4) {
|
||||
@ -201,8 +207,11 @@ testingConstructCall = true;
|
||||
new f(expected.length - 1, 11, 12);
|
||||
new f(expected.length - 1, 11, 12, 0);
|
||||
|
||||
// Make sure that the debug event listener vas invoked.
|
||||
// Make sure that the debug event listener was invoked.
|
||||
assertFalse(exception, "exception in listener " + exception)
|
||||
assertTrue(listenerComplete);
|
||||
|
||||
// Throw away type information for next run.
|
||||
gc();
|
||||
|
||||
Debug.setListener(null);
|
||||
|
@ -126,19 +126,39 @@ invoke(h3, [8, 6, 4]);
|
||||
// Check that %_IsConstructCall returns correct value when inlined
|
||||
var NON_CONSTRUCT_MARKER = {};
|
||||
var CONSTRUCT_MARKER = {};
|
||||
function baz(x) {
|
||||
function baz1(x) {
|
||||
return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
|
||||
}
|
||||
|
||||
function bar(x, y, z) {
|
||||
var non_construct = baz(0); /* baz should be inlined */
|
||||
function bar1(x, y, z) {
|
||||
var non_construct = baz1(0); /* baz should be inlined */
|
||||
assertSame(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var non_construct = baz(); /* baz should be inlined */
|
||||
var non_construct = baz1(); /* baz should be inlined */
|
||||
assertSame(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var non_construct = baz(0, 0); /* baz should be inlined */
|
||||
var non_construct = baz1(0, 0); /* baz should be inlined */
|
||||
assertSame(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var construct = new baz(0);
|
||||
var construct = new baz1(0);
|
||||
assertSame(construct, CONSTRUCT_MARKER);
|
||||
var construct = new baz1(0, 0);
|
||||
assertSame(construct, CONSTRUCT_MARKER);
|
||||
}
|
||||
|
||||
invoke(bar, [1, 2, 3]);
|
||||
function baz2(x) {
|
||||
return (!%IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
|
||||
}
|
||||
|
||||
function bar2(x, y, z) {
|
||||
var non_construct = baz2(0); /* baz should be inlined */
|
||||
assertSame(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var non_construct = baz2(); /* baz should be inlined */
|
||||
assertSame(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var non_construct = baz2(0, 0); /* baz should be inlined */
|
||||
assertSame(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var construct = new baz2(0);
|
||||
assertSame(construct, CONSTRUCT_MARKER);
|
||||
var construct = new baz2(0, 0);
|
||||
assertSame(construct, CONSTRUCT_MARKER);
|
||||
}
|
||||
|
||||
invoke(bar1, [1, 2, 3]);
|
||||
invoke(bar2, [1, 2, 3]);
|
||||
|
Loading…
Reference in New Issue
Block a user