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:
parent
fde8419697
commit
76cf30d9c8
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user