Support %_IsConstructCall in the Crankshaft pipeline.

Provide special case for f.bind(obj).

Review URL: http://codereview.chromium.org/6368138

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6671 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vegorov@chromium.org 2011-02-08 10:08:47 +00:00
parent fde8419697
commit 76cf30d9c8
16 changed files with 330 additions and 33 deletions

View File

@ -1077,6 +1077,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
} else if (v->IsTypeofIs()) {
HTypeofIs* typeof_is = HTypeofIs::cast(v);
return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
} else if (v->IsIsConstructCall()) {
return new LIsConstructCallAndBranch(TempRegister());
} else {
if (v->IsConstant()) {
if (HConstant::cast(v)->handle()->IsTrue()) {
@ -1890,6 +1892,12 @@ LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) {
return DefineSameAsFirst(new LTypeofIs(UseRegister(instr->value())));
}
LInstruction* LChunkBuilder::DoIsConstructCall(HIsConstructCall* instr) {
return DefineAsRegister(new LIsConstructCall());
}
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
HEnvironment* env = current_block_->last_environment();
ASSERT(env != NULL);

View File

@ -153,6 +153,8 @@ class LCodeGen;
V(Typeof) \
V(TypeofIs) \
V(TypeofIsAndBranch) \
V(IsConstructCall) \
V(IsConstructCallAndBranch) \
V(UnaryMathOperation) \
V(UnknownOSRValue) \
V(ValueOf)
@ -1716,6 +1718,24 @@ class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
};
class LIsConstructCall: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is-construct-call")
DECLARE_HYDROGEN_ACCESSOR(IsConstructCall)
};
class LIsConstructCallAndBranch: public LControlInstruction<0, 1> {
public:
explicit LIsConstructCallAndBranch(LOperand* temp) {
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
"is-construct-call-and-branch")
};
class LDeleteProperty: public LTemplateInstruction<1, 2, 0> {
public:
LDeleteProperty(LOperand* obj, LOperand* key) {

View File

@ -3778,6 +3778,55 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
}
void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) {
Register result = ToRegister(instr->result());
Label true_label;
Label false_label;
Label done;
EmitIsConstructCall(result, scratch0());
__ b(eq, &true_label);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ b(&done);
__ bind(&true_label);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ bind(&done);
}
void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
Register temp1 = ToRegister(instr->TempAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
EmitIsConstructCall(temp1, scratch0());
EmitBranch(true_block, false_block, eq);
}
void LCodeGen::EmitIsConstructCall(Register temp1, Register temp2) {
ASSERT(!temp1.is(temp2));
// Get the frame pointer for the calling frame.
__ ldr(temp1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
// Skip the arguments adaptor frame if it exists.
Label check_frame_marker;
__ ldr(temp2, MemOperand(temp1, StandardFrameConstants::kContextOffset));
__ cmp(temp2, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ b(ne, &check_frame_marker);
__ ldr(temp1, MemOperand(temp1, StandardFrameConstants::kCallerFPOffset));
// Check the marker in the calling frame.
__ bind(&check_frame_marker);
__ ldr(temp1, MemOperand(temp1, StandardFrameConstants::kMarkerOffset));
__ cmp(temp1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
}
void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
// No code for lazy bailout instruction. Used to capture environment after a
// call for populating the safepoint data with deoptimization data.

View File

@ -264,6 +264,10 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp1, Register temp2);
LChunk* const chunk_;
MacroAssembler* const masm_;
CompilationInfo* const info_;

View File

@ -113,6 +113,7 @@ class LChunkBuilder;
V(IsNull) \
V(IsObject) \
V(IsSmi) \
V(IsConstructCall) \
V(HasInstanceType) \
V(HasCachedArrayIndex) \
V(JSArrayLength) \
@ -2179,6 +2180,22 @@ class HIsSmi: public HUnaryPredicate {
};
class HIsConstructCall: public HInstruction {
public:
HIsConstructCall() {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
}
virtual bool EmitAtUses() const { return uses()->length() <= 1; }
DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is_construct_call")
protected:
virtual bool DataEquals(HValue* other) const { return true; }
};
class HHasInstanceType: public HUnaryPredicate {
public:
HHasInstanceType(HValue* value, InstanceType type)

View File

@ -5185,9 +5185,10 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
}
// Support for construct call checks.
// Support for construct call checks.
void HGraphBuilder::GenerateIsConstructCall(int argument_count, int ast_id) {
BAILOUT("inlined runtime function: IsConstructCall");
ASSERT(argument_count == 0);
ast_context()->ReturnInstruction(new HIsConstructCall, ast_id);
}

View File

@ -3587,6 +3587,53 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
}
void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) {
Register result = ToRegister(instr->result());
NearLabel true_label;
NearLabel false_label;
NearLabel done;
EmitIsConstructCall(result);
__ j(equal, &true_label);
__ mov(result, Factory::false_value());
__ jmp(&done);
__ bind(&true_label);
__ mov(result, Factory::true_value());
__ bind(&done);
}
void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
Register temp = ToRegister(instr->TempAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
EmitIsConstructCall(temp);
EmitBranch(true_block, false_block, equal);
}
void LCodeGen::EmitIsConstructCall(Register temp) {
// Get the frame pointer for the calling frame.
__ mov(temp, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
// Skip the arguments adaptor frame if it exists.
NearLabel check_frame_marker;
__ cmp(Operand(temp, StandardFrameConstants::kContextOffset),
Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ j(not_equal, &check_frame_marker);
__ mov(temp, Operand(temp, StandardFrameConstants::kCallerFPOffset));
// Check the marker in the calling frame.
__ bind(&check_frame_marker);
__ cmp(Operand(temp, StandardFrameConstants::kMarkerOffset),
Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
}
void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
// No code for lazy bailout instruction. Used to capture environment after a
// call for populating the safepoint data with deoptimization data.

View File

@ -229,6 +229,11 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
LChunk* const chunk_;
MacroAssembler* const masm_;
CompilationInfo* const info_;

View File

@ -1098,6 +1098,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
} else if (v->IsTypeofIs()) {
HTypeofIs* typeof_is = HTypeofIs::cast(v);
return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
} else if (v->IsIsConstructCall()) {
return new LIsConstructCallAndBranch(TempRegister());
} else {
if (v->IsConstant()) {
if (HConstant::cast(v)->handle()->IsTrue()) {
@ -1925,6 +1927,12 @@ LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) {
return DefineSameAsFirst(new LTypeofIs(UseRegister(instr->value())));
}
LInstruction* LChunkBuilder::DoIsConstructCall(HIsConstructCall* instr) {
return DefineAsRegister(new LIsConstructCall);
}
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
HEnvironment* env = current_block_->last_environment();
ASSERT(env != NULL);

View File

@ -112,6 +112,8 @@ class LCodeGen;
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
V(IsConstructCall) \
V(IsConstructCallAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
@ -755,6 +757,24 @@ class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> {
};
class LIsConstructCall: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is-construct-call")
DECLARE_HYDROGEN_ACCESSOR(IsConstructCall)
};
class LIsConstructCallAndBranch: public LControlInstruction<0, 1> {
public:
explicit LIsConstructCallAndBranch(LOperand* temp) {
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
"is-construct-call-and-branch")
};
class LClassOfTest: public LTemplateInstruction<1, 1, 1> {
public:
LClassOfTest(LOperand* value, LOperand* temp) {

View File

@ -6681,28 +6681,50 @@ static MaybeObject* Runtime_NewClosure(Arguments args) {
return *result;
}
static MaybeObject* Runtime_NewObjectFromBound(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 2);
// First argument is a function to use as a constructor.
CONVERT_ARG_CHECKED(JSFunction, function, 0);
// Second argument is either null or an array of bound arguments.
FixedArray* bound_args = NULL;
int bound_argc = 0;
if (!args[1]->IsNull()) {
CONVERT_ARG_CHECKED(JSArray, params, 1);
RUNTIME_ASSERT(params->HasFastElements());
FixedArray* fixed = FixedArray::cast(params->elements());
bound_args = FixedArray::cast(params->elements());
bound_argc = Smi::cast(params->length())->value();
}
int fixed_length = Smi::cast(params->length())->value();
SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
for (int i = 0; i < fixed_length; i++) {
Handle<Object> val = Handle<Object>(fixed->get(i));
// Find frame containing arguments passed to the caller.
JavaScriptFrameIterator it;
JavaScriptFrame* frame = it.frame();
ASSERT(!frame->is_optimized());
it.AdvanceToArgumentsFrame();
frame = it.frame();
int argc = frame->GetProvidedParametersCount();
// Prepend bound arguments to caller's arguments.
int total_argc = bound_argc + argc;
SmartPointer<Object**> param_data(NewArray<Object**>(total_argc));
for (int i = 0; i < bound_argc; i++) {
Handle<Object> val = Handle<Object>(bound_args->get(i));
param_data[i] = val.location();
}
for (int i = 0; i < argc; i++) {
Handle<Object> val = Handle<Object>(frame->GetParameter(i));
param_data[bound_argc + i] = val.location();
}
bool exception = false;
Handle<Object> result = Execution::New(
function, fixed_length, *param_data, &exception);
Handle<Object> result =
Execution::New(function, total_argc, *param_data, &exception);
if (exception) {
return Failure::Exception();
}
ASSERT(!result.is_null());
return *result;
}

View File

@ -1153,14 +1153,34 @@ function FunctionBind(this_arg) { // Length is 1.
}
// this_arg is not an argument that should be bound.
var argc_bound = (%_ArgumentsLength() || 1) - 1;
if (argc_bound > 0) {
var fn = this;
if (argc_bound == 0) {
var result = function() {
if (%_IsConstructCall()) {
// %NewObjectFromBound implicitly uses arguments passed to this
// function. We do not pass the arguments object explicitly to avoid
// materializing it and guarantee that this function will be optimized.
return %NewObjectFromBound(fn, null);
}
return fn.apply(this_arg, arguments);
};
} else {
var bound_args = new $Array(argc_bound);
for(var i = 0; i < argc_bound; i++) {
bound_args[i] = %_Arguments(i+1);
}
}
var fn = this;
var result = function() {
// If this is a construct call we use a special runtime method
// to generate the actual object using the bound function.
if (%_IsConstructCall()) {
// %NewObjectFromBound implicitly uses arguments passed to this
// function. We do not pass the arguments object explicitly to avoid
// materializing it and guarantee that this function will be optimized.
return %NewObjectFromBound(fn, bound_args);
}
// Combine the args we got from the bind call with the args
// given as argument to the invocation.
var argc = %_ArgumentsLength();
@ -1173,13 +1193,9 @@ function FunctionBind(this_arg) { // Length is 1.
for (var i = 0; i < argc; i++) {
args[argc_bound + i] = %_Arguments(i);
}
// If this is a construct call we use a special runtime method
// to generate the actual object using the bound function.
if (%_IsConstructCall()) {
return %NewObjectFromBound(fn, args);
}
return fn.apply(this_arg, args);
};
}
// We already have caller and arguments properties on functions,
// which are non-configurable. It therefore makes no sence to

View File

@ -2284,6 +2284,54 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
}
void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) {
Register result = ToRegister(instr->result());
NearLabel true_label;
NearLabel false_label;
NearLabel done;
EmitIsConstructCall(result);
__ j(equal, &true_label);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ jmp(&done);
__ bind(&true_label);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ bind(&done);
}
void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
Register temp = ToRegister(instr->TempAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
EmitIsConstructCall(temp);
EmitBranch(true_block, false_block, equal);
}
void LCodeGen::EmitIsConstructCall(Register temp) {
// Get the frame pointer for the calling frame.
__ movq(temp, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
// Skip the arguments adaptor frame if it exists.
NearLabel check_frame_marker;
__ SmiCompare(Operand(temp, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(not_equal, &check_frame_marker);
__ movq(temp, Operand(rax, StandardFrameConstants::kCallerFPOffset));
// Check the marker in the calling frame.
__ bind(&check_frame_marker);
__ SmiCompare(Operand(temp, StandardFrameConstants::kMarkerOffset),
Smi::FromInt(StackFrame::CONSTRUCT));
}
void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
Register input = ToRegister(instr->InputAt(0));
int true_block = chunk_->LookupDestination(instr->true_block_id());

View File

@ -221,6 +221,10 @@ class LCodeGen BASE_EMBEDDED {
Label* is_not_object,
Label* is_object);
// Emits optimized code for %_IsConstructCall().
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
LChunk* const chunk_;
MacroAssembler* const masm_;
CompilationInfo* const info_;

View File

@ -1082,6 +1082,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
} else if (v->IsTypeofIs()) {
HTypeofIs* typeof_is = HTypeofIs::cast(v);
return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
} else if (v->IsIsConstructCall()) {
return new LIsConstructCallAndBranch(TempRegister());
} else {
if (v->IsConstant()) {
if (HConstant::cast(v)->handle()->IsTrue()) {
@ -1775,6 +1777,12 @@ LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) {
return NULL;
}
LInstruction* LChunkBuilder::DoIsConstructCall(HIsConstructCall* instr) {
return DefineAsRegister(new LIsConstructCall);
}
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
HEnvironment* env = current_block_->last_environment();
ASSERT(env != NULL);

View File

@ -259,6 +259,8 @@ class LCodeGen;
V(Typeof) \
V(TypeofIs) \
V(TypeofIsAndBranch) \
V(IsConstructCall) \
V(IsConstructCallAndBranch) \
V(UnaryMathOperation) \
V(UnknownOSRValue) \
V(ValueOf)
@ -1763,6 +1765,24 @@ class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
};
class LIsConstructCall: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is-construct-call")
DECLARE_HYDROGEN_ACCESSOR(IsConstructCall)
};
class LIsConstructCallAndBranch: public LControlInstruction<0, 1> {
public:
explicit LIsConstructCallAndBranch(LOperand* temp) {
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
"is-construct-call-and-branch")
};
class LDeleteProperty: public LTemplateInstruction<1, 2, 0> {
public:
LDeleteProperty(LOperand* obj, LOperand* key) {