Crankshaft support for polymorphic array handling
Review URL: http://codereview.chromium.org/7170012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8325 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
404434628b
commit
b789cb8c94
@ -1111,6 +1111,9 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
|
||||
HCompareSymbolEq* compare = HCompareSymbolEq::cast(v);
|
||||
return new LCmpSymbolEqAndBranch(UseRegisterAtStart(compare->left()),
|
||||
UseRegisterAtStart(compare->right()));
|
||||
} else if (v->IsCompareConstantEq()) {
|
||||
HCompareConstantEq* compare = HCompareConstantEq::cast(v);
|
||||
return new LCmpConstantEqAndBranch(UseRegisterAtStart(compare->value()));
|
||||
} else if (v->IsTypeofIs()) {
|
||||
HTypeofIs* typeof_is = HTypeofIs::cast(v);
|
||||
return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
|
||||
@ -1519,6 +1522,13 @@ LInstruction* LChunkBuilder::DoCompareSymbolEq(
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoCompareConstantEq(
|
||||
HCompareConstantEq* instr) {
|
||||
LOperand* left = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LCmpConstantEq(left));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
|
||||
ASSERT(instr->value()->representation().IsTagged());
|
||||
LOperand* value = UseRegisterAtStart(instr->value());
|
||||
@ -1603,6 +1613,12 @@ LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
|
||||
LOperand* object = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LElementsKind(object));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
|
||||
LOperand* object = UseRegister(instr->value());
|
||||
LValueOf* result = new LValueOf(object, TempRegister());
|
||||
|
@ -79,6 +79,8 @@ class LCodeGen;
|
||||
V(ClampTToUint8) \
|
||||
V(ClassOfTest) \
|
||||
V(ClassOfTestAndBranch) \
|
||||
V(CmpConstantEq) \
|
||||
V(CmpConstantEqAndBranch) \
|
||||
V(CmpID) \
|
||||
V(CmpIDAndBranch) \
|
||||
V(CmpJSObjectEq) \
|
||||
@ -95,6 +97,7 @@ class LCodeGen;
|
||||
V(Deoptimize) \
|
||||
V(DivI) \
|
||||
V(DoubleToI) \
|
||||
V(ElementsKind) \
|
||||
V(ExternalArrayLength) \
|
||||
V(FixedArrayLength) \
|
||||
V(FunctionLiteral) \
|
||||
@ -679,6 +682,29 @@ class LCmpSymbolEqAndBranch: public LControlInstruction<2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LCmpConstantEq: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LCmpConstantEq(LOperand* left) {
|
||||
inputs_[0] = left;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CmpConstantEq, "cmp-constant-eq")
|
||||
DECLARE_HYDROGEN_ACCESSOR(CompareConstantEq)
|
||||
};
|
||||
|
||||
|
||||
class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> {
|
||||
public:
|
||||
explicit LCmpConstantEqAndBranch(LOperand* left) {
|
||||
inputs_[0] = left;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CmpConstantEqAndBranch,
|
||||
"cmp-constant-eq-and-branch")
|
||||
DECLARE_HYDROGEN_ACCESSOR(CompareConstantEq)
|
||||
};
|
||||
|
||||
|
||||
class LIsNull: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LIsNull(LOperand* value) {
|
||||
@ -1062,6 +1088,17 @@ class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LElementsKind: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LElementsKind(LOperand* value) {
|
||||
inputs_[0] = value;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind")
|
||||
DECLARE_HYDROGEN_ACCESSOR(ElementsKind)
|
||||
};
|
||||
|
||||
|
||||
class LValueOf: public LTemplateInstruction<1, 1, 1> {
|
||||
public:
|
||||
LValueOf(LOperand* value, LOperand* temp) {
|
||||
|
@ -1370,6 +1370,20 @@ void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoElementsKind(LElementsKind* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
|
||||
// Load map into |result|.
|
||||
__ ldr(result, FieldMemOperand(input, HeapObject::kMapOffset));
|
||||
// Load the map's "bit field 2" into |result|. We only need the first byte,
|
||||
// but the following bit field extraction takes care of that anyway.
|
||||
__ ldr(result, FieldMemOperand(result, Map::kBitField2Offset));
|
||||
// Retrieve elements_kind from bit field 2.
|
||||
__ ubfx(result, result, Map::kElementsKindShift, Map::kElementsKindBitCount);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoValueOf(LValueOf* instr) {
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
@ -1744,6 +1758,27 @@ void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoCmpConstantEq(LCmpConstantEq* instr) {
|
||||
Register left = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
Label done;
|
||||
__ cmp(left, Operand(instr->hydrogen()->right()));
|
||||
__ LoadRoot(result, Heap::kTrueValueRootIndex, eq);
|
||||
__ LoadRoot(result, Heap::kFalseValueRootIndex, ne);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) {
|
||||
Register left = ToRegister(instr->InputAt(0));
|
||||
int true_block = chunk_->LookupDestination(instr->true_block_id());
|
||||
int false_block = chunk_->LookupDestination(instr->false_block_id());
|
||||
|
||||
__ cmp(left, Operand(instr->hydrogen()->right()));
|
||||
EmitBranch(true_block, false_block, eq);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoIsNull(LIsNull* instr) {
|
||||
Register reg = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
@ -2536,7 +2571,7 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
||||
|
||||
__ ldr(result, FieldMemOperand(input, JSObject::kElementsOffset));
|
||||
if (FLAG_debug_code) {
|
||||
Label done;
|
||||
Label done, fail;
|
||||
__ ldr(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
|
||||
__ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
|
||||
__ cmp(scratch, ip);
|
||||
@ -2544,11 +2579,18 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
||||
__ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
|
||||
__ cmp(scratch, ip);
|
||||
__ b(eq, &done);
|
||||
__ ldr(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
|
||||
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
|
||||
__ sub(scratch, scratch, Operand(FIRST_EXTERNAL_ARRAY_TYPE));
|
||||
__ cmp(scratch, Operand(kExternalArrayTypeCount));
|
||||
__ Check(cc, "Check for fast elements failed.");
|
||||
// |scratch| still contains |input|'s map.
|
||||
__ ldr(scratch, FieldMemOperand(scratch, Map::kBitField2Offset));
|
||||
__ ubfx(scratch, scratch, Map::kElementsKindShift,
|
||||
Map::kElementsKindBitCount);
|
||||
__ cmp(scratch, Operand(JSObject::FAST_ELEMENTS));
|
||||
__ b(eq, &done);
|
||||
__ cmp(scratch, Operand(JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND));
|
||||
__ b(lt, &fail);
|
||||
__ cmp(scratch, Operand(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND));
|
||||
__ b(le, &done);
|
||||
__ bind(&fail);
|
||||
__ Abort("Check for fast or external elements failed.");
|
||||
__ bind(&done);
|
||||
}
|
||||
}
|
||||
|
13
src/ast.cc
13
src/ast.cc
@ -607,6 +607,9 @@ void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
is_string_access_ = true;
|
||||
} else if (is_monomorphic_) {
|
||||
monomorphic_receiver_type_ = oracle->LoadMonomorphicReceiverType(this);
|
||||
} else if (oracle->LoadIsMegamorphicWithTypeInfo(this)) {
|
||||
receiver_types_ = new ZoneMapList(kMaxKeyedPolymorphism);
|
||||
oracle->CollectKeyedReceiverTypes(this->id(), receiver_types_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -622,8 +625,11 @@ void Assignment::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
ZoneMapList* types = oracle->StoreReceiverTypes(this, name);
|
||||
receiver_types_ = types;
|
||||
} else if (is_monomorphic_) {
|
||||
// Record receiver type for monomorphic keyed loads.
|
||||
// Record receiver type for monomorphic keyed stores.
|
||||
monomorphic_receiver_type_ = oracle->StoreMonomorphicReceiverType(this);
|
||||
} else if (oracle->StoreIsMegamorphicWithTypeInfo(this)) {
|
||||
receiver_types_ = new ZoneMapList(kMaxKeyedPolymorphism);
|
||||
oracle->CollectKeyedReceiverTypes(this->id(), receiver_types_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -631,8 +637,11 @@ void Assignment::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
void CountOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
is_monomorphic_ = oracle->StoreIsMonomorphicNormal(this);
|
||||
if (is_monomorphic_) {
|
||||
// Record receiver type for monomorphic keyed loads.
|
||||
// Record receiver type for monomorphic keyed stores.
|
||||
monomorphic_receiver_type_ = oracle->StoreMonomorphicReceiverType(this);
|
||||
} else if (oracle->StoreIsMegamorphicWithTypeInfo(this)) {
|
||||
receiver_types_ = new ZoneMapList(kMaxKeyedPolymorphism);
|
||||
oracle->CollectKeyedReceiverTypes(this->id(), receiver_types_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1405,7 +1405,8 @@ class CountOperation: public Expression {
|
||||
expression_(expr),
|
||||
pos_(pos),
|
||||
assignment_id_(GetNextId()),
|
||||
count_id_(GetNextId()) { }
|
||||
count_id_(GetNextId()),
|
||||
receiver_types_(NULL) { }
|
||||
|
||||
DECLARE_NODE_TYPE(CountOperation)
|
||||
|
||||
@ -1429,6 +1430,7 @@ class CountOperation: public Expression {
|
||||
virtual Handle<Map> GetMonomorphicReceiverType() {
|
||||
return monomorphic_receiver_type_;
|
||||
}
|
||||
virtual ZoneMapList* GetReceiverTypes() { return receiver_types_; }
|
||||
|
||||
// Bailout support.
|
||||
int AssignmentId() const { return assignment_id_; }
|
||||
@ -1443,6 +1445,7 @@ class CountOperation: public Expression {
|
||||
int assignment_id_;
|
||||
int count_id_;
|
||||
Handle<Map> monomorphic_receiver_type_;
|
||||
ZoneMapList* receiver_types_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -94,11 +94,13 @@ class LChunkBuilder;
|
||||
V(CompareJSObjectEq) \
|
||||
V(CompareMap) \
|
||||
V(CompareSymbolEq) \
|
||||
V(CompareConstantEq) \
|
||||
V(Constant) \
|
||||
V(Context) \
|
||||
V(DeleteProperty) \
|
||||
V(Deoptimize) \
|
||||
V(Div) \
|
||||
V(ElementsKind) \
|
||||
V(EnterInlined) \
|
||||
V(ExternalArrayLength) \
|
||||
V(FixedArrayLength) \
|
||||
@ -402,7 +404,7 @@ class HType {
|
||||
kBoolean = 0x85, // 0000 0000 1000 0101
|
||||
kNonPrimitive = 0x101, // 0000 0001 0000 0001
|
||||
kJSObject = 0x301, // 0000 0011 0000 0001
|
||||
kJSArray = 0x701, // 0000 0111 1000 0001
|
||||
kJSArray = 0x701, // 0000 0111 0000 0001
|
||||
kUninitialized = 0x1fff // 0001 1111 1111 1111
|
||||
};
|
||||
|
||||
@ -484,6 +486,10 @@ class HValue: public ZoneObject {
|
||||
GVN_FLAG_LIST(DECLARE_DO)
|
||||
#undef DECLARE_DO
|
||||
kFlexibleRepresentation,
|
||||
// Participate in Global Value Numbering, i.e. elimination of
|
||||
// unnecessary recomputations. If an instruction sets this flag, it must
|
||||
// implement DataEquals(), which will be used to determine if other
|
||||
// occurrences of the instruction are indeed the same.
|
||||
kUseGVN,
|
||||
kCanOverflow,
|
||||
kBailoutOnMinusZero,
|
||||
@ -1707,6 +1713,25 @@ class HExternalArrayLength: public HUnaryOperation {
|
||||
};
|
||||
|
||||
|
||||
class HElementsKind: public HUnaryOperation {
|
||||
public:
|
||||
explicit HElementsKind(HValue* value) : HUnaryOperation(value) {
|
||||
set_representation(Representation::Integer32());
|
||||
SetFlag(kUseGVN);
|
||||
SetFlag(kDependsOnMaps);
|
||||
}
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) const {
|
||||
return Representation::Tagged();
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(ElementsKind)
|
||||
|
||||
protected:
|
||||
virtual bool DataEquals(HValue* other) { return true; }
|
||||
};
|
||||
|
||||
|
||||
class HBitNot: public HUnaryOperation {
|
||||
public:
|
||||
explicit HBitNot(HValue* value) : HUnaryOperation(value) {
|
||||
@ -2589,6 +2614,43 @@ class HCompareSymbolEq: public HBinaryOperation {
|
||||
};
|
||||
|
||||
|
||||
class HCompareConstantEq: public HUnaryOperation {
|
||||
public:
|
||||
HCompareConstantEq(HValue* left, int right, Token::Value op)
|
||||
: HUnaryOperation(left), op_(op), right_(right) {
|
||||
ASSERT(op == Token::EQ_STRICT);
|
||||
set_representation(Representation::Tagged());
|
||||
SetFlag(kUseGVN);
|
||||
}
|
||||
|
||||
Token::Value op() const { return op_; }
|
||||
int right() const { return right_; }
|
||||
|
||||
virtual bool EmitAtUses() {
|
||||
return !HasSideEffects() && !HasMultipleUses();
|
||||
}
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) const {
|
||||
return Representation::Integer32();
|
||||
}
|
||||
|
||||
virtual HType CalculateInferredType() { return HType::Boolean(); }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CompareConstantEq);
|
||||
|
||||
protected:
|
||||
virtual bool DataEquals(HValue* other) {
|
||||
HCompareConstantEq* other_instr = HCompareConstantEq::cast(other);
|
||||
return (op_ == other_instr->op_ &&
|
||||
right_ == other_instr->right_);
|
||||
}
|
||||
|
||||
private:
|
||||
const Token::Value op_;
|
||||
const int right_;
|
||||
};
|
||||
|
||||
|
||||
class HUnaryPredicate: public HUnaryOperation {
|
||||
public:
|
||||
explicit HUnaryPredicate(HValue* value) : HUnaryOperation(value) {
|
||||
@ -2694,6 +2756,10 @@ class HHasInstanceType: public HUnaryPredicate {
|
||||
InstanceType from() { return from_; }
|
||||
InstanceType to() { return to_; }
|
||||
|
||||
virtual bool EmitAtUses() {
|
||||
return !HasSideEffects() && !HasMultipleUses();
|
||||
}
|
||||
|
||||
virtual void PrintDataTo(StringStream* stream);
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(HasInstanceType)
|
||||
|
449
src/hydrogen.cc
449
src/hydrogen.cc
@ -3444,7 +3444,16 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
|
||||
value = Pop();
|
||||
HValue* key = Pop();
|
||||
HValue* object = Pop();
|
||||
instr = BuildStoreKeyed(object, key, value, expr);
|
||||
bool has_side_effects = false;
|
||||
HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
|
||||
expr->position(),
|
||||
true, // is_store
|
||||
&has_side_effects);
|
||||
Push(value);
|
||||
ASSERT(has_side_effects); // Stores always have side effects.
|
||||
AddSimulate(expr->AssignmentId());
|
||||
ast_context()->ReturnValue(Pop());
|
||||
return;
|
||||
}
|
||||
Push(value);
|
||||
instr->set_position(expr->position());
|
||||
@ -3582,9 +3591,14 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
||||
HValue* obj = environment()->ExpressionStackAt(1);
|
||||
HValue* key = environment()->ExpressionStackAt(0);
|
||||
|
||||
HInstruction* load = BuildLoadKeyed(obj, key, prop);
|
||||
PushAndAdd(load);
|
||||
if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
|
||||
bool has_side_effects = false;
|
||||
HValue* load = HandleKeyedElementAccess(
|
||||
obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition,
|
||||
false, // is_store
|
||||
&has_side_effects);
|
||||
Push(load);
|
||||
if (has_side_effects) AddSimulate(expr->CompoundLoadId());
|
||||
|
||||
|
||||
CHECK_ALIVE(VisitForValue(expr->value()));
|
||||
HValue* right = Pop();
|
||||
@ -3595,12 +3609,16 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
||||
if (instr->HasSideEffects()) AddSimulate(operation->id());
|
||||
|
||||
expr->RecordTypeFeedback(oracle());
|
||||
HInstruction* store = BuildStoreKeyed(obj, key, instr, expr);
|
||||
AddInstruction(store);
|
||||
HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
|
||||
RelocInfo::kNoPosition,
|
||||
true, // is_store
|
||||
&has_side_effects);
|
||||
|
||||
// Drop the simulated receiver, key, and value. Return the value.
|
||||
Drop(3);
|
||||
Push(instr);
|
||||
if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
||||
ASSERT(has_side_effects); // Stores always have side effects.
|
||||
AddSimulate(expr->AssignmentId());
|
||||
ast_context()->ReturnValue(Pop());
|
||||
}
|
||||
|
||||
@ -3778,134 +3796,14 @@ HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object,
|
||||
HValue* key,
|
||||
Property* expr) {
|
||||
ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
Handle<Map> map = expr->GetMonomorphicReceiverType();
|
||||
ASSERT(map->has_fast_elements());
|
||||
AddInstruction(new(zone()) HCheckMap(object, map));
|
||||
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
|
||||
HLoadElements* elements = new(zone()) HLoadElements(object);
|
||||
HInstruction* length = NULL;
|
||||
HInstruction* checked_key = NULL;
|
||||
if (is_array) {
|
||||
length = AddInstruction(new(zone()) HJSArrayLength(object));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
AddInstruction(elements);
|
||||
} else {
|
||||
AddInstruction(elements);
|
||||
length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
}
|
||||
return new(zone()) HLoadKeyedFastElement(elements, checked_key);
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildLoadKeyedSpecializedArrayElement(
|
||||
HValue* object,
|
||||
HValue* key,
|
||||
Property* expr) {
|
||||
ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
Handle<Map> map = expr->GetMonomorphicReceiverType();
|
||||
ASSERT(!map->has_fast_elements());
|
||||
ASSERT(map->has_external_array_elements());
|
||||
AddInstruction(new(zone()) HCheckMap(object, map));
|
||||
HLoadElements* elements = new(zone()) HLoadElements(object);
|
||||
AddInstruction(elements);
|
||||
HInstruction* length = new(zone()) HExternalArrayLength(elements);
|
||||
AddInstruction(length);
|
||||
HInstruction* checked_key =
|
||||
AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
HLoadExternalArrayPointer* external_elements =
|
||||
new(zone()) HLoadExternalArrayPointer(elements);
|
||||
AddInstruction(external_elements);
|
||||
HLoadKeyedSpecializedArrayElement* pixel_array_value =
|
||||
new(zone()) HLoadKeyedSpecializedArrayElement(
|
||||
external_elements, checked_key, map->elements_kind());
|
||||
return pixel_array_value;
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildLoadKeyed(HValue* obj,
|
||||
HValue* key,
|
||||
Property* prop) {
|
||||
if (prop->IsMonomorphic()) {
|
||||
Handle<Map> receiver_type(prop->GetMonomorphicReceiverType());
|
||||
// An object has either fast elements or pixel array elements, but never
|
||||
// both. Pixel array maps that are assigned to pixel array elements are
|
||||
// always created with the fast elements flag cleared.
|
||||
if (receiver_type->has_external_array_elements()) {
|
||||
return BuildLoadKeyedSpecializedArrayElement(obj, key, prop);
|
||||
} else if (receiver_type->has_fast_elements()) {
|
||||
return BuildLoadKeyedFastElement(obj, key, prop);
|
||||
}
|
||||
}
|
||||
return BuildLoadKeyedGeneric(obj, key);
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
|
||||
HValue* key,
|
||||
HValue* value) {
|
||||
HValue* context = environment()->LookupContext();
|
||||
return new(zone()) HStoreKeyedGeneric(
|
||||
context,
|
||||
object,
|
||||
key,
|
||||
value,
|
||||
function_strict_mode());
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object,
|
||||
HValue* key,
|
||||
HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
|
||||
HValue* external_elements,
|
||||
HValue* checked_key,
|
||||
HValue* val,
|
||||
Expression* expr) {
|
||||
ASSERT(expr->IsMonomorphic());
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
Handle<Map> map = expr->GetMonomorphicReceiverType();
|
||||
ASSERT(map->has_fast_elements());
|
||||
AddInstruction(new(zone()) HCheckMap(object, map));
|
||||
HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
|
||||
AddInstruction(new(zone()) HCheckMap(
|
||||
elements, isolate()->factory()->fixed_array_map()));
|
||||
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
|
||||
HInstruction* length = NULL;
|
||||
if (is_array) {
|
||||
length = AddInstruction(new(zone()) HJSArrayLength(object));
|
||||
} else {
|
||||
length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
||||
}
|
||||
HInstruction* checked_key =
|
||||
AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
|
||||
HValue* object,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* expr) {
|
||||
ASSERT(expr->IsMonomorphic());
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
Handle<Map> map = expr->GetMonomorphicReceiverType();
|
||||
ASSERT(!map->has_fast_elements());
|
||||
ASSERT(map->has_external_array_elements());
|
||||
AddInstruction(new(zone()) HCheckMap(object, map));
|
||||
HLoadElements* elements = new(zone()) HLoadElements(object);
|
||||
AddInstruction(elements);
|
||||
HInstruction* length = AddInstruction(
|
||||
new(zone()) HExternalArrayLength(elements));
|
||||
HInstruction* checked_key =
|
||||
AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
HLoadExternalArrayPointer* external_elements =
|
||||
new(zone()) HLoadExternalArrayPointer(elements);
|
||||
AddInstruction(external_elements);
|
||||
JSObject::ElementsKind elements_kind = map->elements_kind();
|
||||
JSObject::ElementsKind elements_kind,
|
||||
bool is_store) {
|
||||
if (is_store) {
|
||||
ASSERT(val != NULL);
|
||||
switch (elements_kind) {
|
||||
case JSObject::EXTERNAL_PIXEL_ELEMENTS: {
|
||||
HClampToUint8* clamp = new(zone()) HClampToUint8(val);
|
||||
@ -3927,7 +3825,6 @@ HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
|
||||
case JSObject::EXTERNAL_FLOAT_ELEMENTS:
|
||||
case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
|
||||
break;
|
||||
|
||||
case JSObject::FAST_ELEMENTS:
|
||||
case JSObject::FAST_DOUBLE_ELEMENTS:
|
||||
case JSObject::DICTIONARY_ELEMENTS:
|
||||
@ -3936,34 +3833,239 @@ HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
|
||||
break;
|
||||
}
|
||||
return new(zone()) HStoreKeyedSpecializedArrayElement(
|
||||
external_elements,
|
||||
checked_key,
|
||||
val,
|
||||
map->elements_kind());
|
||||
external_elements, checked_key, val, elements_kind);
|
||||
} else {
|
||||
return new(zone()) HLoadKeyedSpecializedArrayElement(
|
||||
external_elements, checked_key, elements_kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildStoreKeyed(HValue* object,
|
||||
HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
|
||||
HValue* key,
|
||||
HValue* value,
|
||||
Expression* expr) {
|
||||
HValue* val,
|
||||
Expression* expr,
|
||||
bool is_store) {
|
||||
ASSERT(expr->IsMonomorphic());
|
||||
Handle<Map> map = expr->GetMonomorphicReceiverType();
|
||||
if (!map->has_fast_elements() && !map->has_external_array_elements()) {
|
||||
return is_store ? BuildStoreKeyedGeneric(object, key, val)
|
||||
: BuildLoadKeyedGeneric(object, key);
|
||||
}
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
AddInstruction(new(zone()) HCheckMap(object, map));
|
||||
HInstruction* elements = new(zone()) HLoadElements(object);
|
||||
HInstruction* length = NULL;
|
||||
HInstruction* checked_key = NULL;
|
||||
if (map->has_external_array_elements()) {
|
||||
AddInstruction(elements);
|
||||
length = AddInstruction(new(zone()) HExternalArrayLength(elements));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
HLoadExternalArrayPointer* external_elements =
|
||||
new(zone()) HLoadExternalArrayPointer(elements);
|
||||
AddInstruction(external_elements);
|
||||
return BuildExternalArrayElementAccess(external_elements, checked_key,
|
||||
val, map->elements_kind(), is_store);
|
||||
}
|
||||
ASSERT(map->has_fast_elements());
|
||||
if (map->instance_type() == JS_ARRAY_TYPE) {
|
||||
length = AddInstruction(new(zone()) HJSArrayLength(object));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
AddInstruction(elements);
|
||||
} else {
|
||||
AddInstruction(elements);
|
||||
length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
}
|
||||
if (is_store) {
|
||||
return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
|
||||
} else {
|
||||
return new(zone()) HLoadKeyedFastElement(elements, checked_key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* prop,
|
||||
int ast_id,
|
||||
int position,
|
||||
bool is_store,
|
||||
bool* has_side_effects) {
|
||||
*has_side_effects = false;
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
AddInstruction(HCheckInstanceType::NewIsSpecObject(object));
|
||||
ZoneMapList* maps = prop->GetReceiverTypes();
|
||||
bool todo_external_array = false;
|
||||
|
||||
static const int kNumElementTypes = JSObject::kElementsKindCount;
|
||||
bool type_todo[kNumElementTypes];
|
||||
for (int i = 0; i < kNumElementTypes; ++i) {
|
||||
type_todo[i] = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < maps->length(); ++i) {
|
||||
ASSERT(maps->at(i)->IsMap());
|
||||
type_todo[maps->at(i)->elements_kind()] = true;
|
||||
if (maps->at(i)->elements_kind()
|
||||
>= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
|
||||
todo_external_array = true;
|
||||
}
|
||||
}
|
||||
// We can't treat dictionary elements here (need to deopt instead).
|
||||
type_todo[JSObject::DICTIONARY_ELEMENTS] = false;
|
||||
// Support for FAST_DOUBLE_ELEMENTS isn't implemented yet, so we deopt.
|
||||
type_todo[JSObject::FAST_DOUBLE_ELEMENTS] = false;
|
||||
|
||||
HBasicBlock* join = graph()->CreateBasicBlock();
|
||||
|
||||
HInstruction* elements_kind_instr =
|
||||
AddInstruction(new(zone()) HElementsKind(object));
|
||||
HInstruction* elements = NULL;
|
||||
HLoadExternalArrayPointer* external_elements = NULL;
|
||||
HInstruction* checked_key = NULL;
|
||||
|
||||
// FAST_ELEMENTS is assumed to be the first case.
|
||||
STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0);
|
||||
|
||||
for (JSObject::ElementsKind elements_kind = JSObject::FAST_ELEMENTS;
|
||||
elements_kind <= JSObject::LAST_ELEMENTS_KIND;
|
||||
elements_kind = JSObject::ElementsKind(elements_kind + 1)) {
|
||||
// After having handled FAST_ELEMENTS in the first run of the loop, we
|
||||
// need to add some code that's executed for all other cases.
|
||||
if (elements_kind == 1 && todo_external_array) {
|
||||
elements = AddInstruction(new(zone()) HLoadElements(object));
|
||||
// We need to forcibly prevent some ElementsKind-dependent instructions
|
||||
// from being hoisted out of any loops they might occur in, because
|
||||
// the current loop-invariant-code-motion algorithm isn't clever enough
|
||||
// to deal with them properly.
|
||||
// There's some performance to be gained by developing a smarter
|
||||
// solution for this.
|
||||
elements->ClearFlag(HValue::kUseGVN);
|
||||
HInstruction* length =
|
||||
AddInstruction(new(zone()) HExternalArrayLength(elements));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
external_elements = new(zone()) HLoadExternalArrayPointer(elements);
|
||||
AddInstruction(external_elements);
|
||||
}
|
||||
if (type_todo[elements_kind]) {
|
||||
HBasicBlock* if_true = graph()->CreateBasicBlock();
|
||||
HBasicBlock* if_false = graph()->CreateBasicBlock();
|
||||
HCompareConstantEq* compare = new(zone()) HCompareConstantEq(
|
||||
elements_kind_instr,
|
||||
elements_kind,
|
||||
Token::EQ_STRICT);
|
||||
AddInstruction(compare);
|
||||
HTest* branch = new(zone()) HTest(compare, if_true, if_false);
|
||||
current_block()->Finish(branch);
|
||||
|
||||
set_current_block(if_true);
|
||||
HInstruction* access;
|
||||
if (elements_kind == JSObject::FAST_ELEMENTS) {
|
||||
HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
|
||||
HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
|
||||
HInstruction* typecheck =
|
||||
AddInstruction(new(zone()) HHasInstanceType(object, JS_ARRAY_TYPE));
|
||||
HTest* test = new(zone()) HTest(typecheck, if_jsarray, if_fastobject);
|
||||
current_block()->Finish(test);
|
||||
|
||||
set_current_block(if_jsarray);
|
||||
HInstruction* length = new(zone()) HJSArrayLength(object);
|
||||
AddInstruction(length);
|
||||
length->ClearFlag(HValue::kUseGVN);
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
elements = AddInstruction(new(zone()) HLoadElements(object));
|
||||
elements->ClearFlag(HValue::kUseGVN);
|
||||
if (is_store) {
|
||||
access = AddInstruction(
|
||||
new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
|
||||
} else {
|
||||
access = AddInstruction(
|
||||
new(zone()) HLoadKeyedFastElement(elements, checked_key));
|
||||
Push(access);
|
||||
}
|
||||
*has_side_effects |= access->HasSideEffects();
|
||||
if (position != -1) {
|
||||
access->set_position(position);
|
||||
}
|
||||
if_jsarray->Goto(join);
|
||||
|
||||
set_current_block(if_fastobject);
|
||||
elements = AddInstruction(new(zone()) HLoadElements(object));
|
||||
elements->ClearFlag(HValue::kUseGVN);
|
||||
length = AddInstruction(new(zone()) HFixedArrayLength(elements));
|
||||
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
|
||||
if (is_store) {
|
||||
access = AddInstruction(
|
||||
new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
|
||||
} else {
|
||||
access = AddInstruction(
|
||||
new(zone()) HLoadKeyedFastElement(elements, checked_key));
|
||||
}
|
||||
} else { // External array elements.
|
||||
access = AddInstruction(BuildExternalArrayElementAccess(
|
||||
external_elements, checked_key, val, elements_kind, is_store));
|
||||
}
|
||||
*has_side_effects |= access->HasSideEffects();
|
||||
access->set_position(position);
|
||||
if (!is_store) {
|
||||
Push(access);
|
||||
}
|
||||
current_block()->Goto(join);
|
||||
set_current_block(if_false);
|
||||
}
|
||||
}
|
||||
|
||||
// Deopt if none of the cases matched.
|
||||
current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
|
||||
join->SetJoinId(ast_id);
|
||||
set_current_block(join);
|
||||
return is_store ? NULL : Pop();
|
||||
}
|
||||
|
||||
|
||||
HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* expr,
|
||||
int ast_id,
|
||||
int position,
|
||||
bool is_store,
|
||||
bool* has_side_effects) {
|
||||
ASSERT(!expr->IsPropertyName());
|
||||
HInstruction* instr = NULL;
|
||||
if (expr->IsMonomorphic()) {
|
||||
Handle<Map> receiver_type(expr->GetMonomorphicReceiverType());
|
||||
// An object has either fast elements or external array elements, but
|
||||
// never both. Pixel array maps that are assigned to pixel array elements
|
||||
// are always created with the fast elements flag cleared.
|
||||
if (receiver_type->has_external_array_elements()) {
|
||||
return BuildStoreKeyedSpecializedArrayElement(object,
|
||||
instr = BuildMonomorphicElementAccess(obj, key, val, expr, is_store);
|
||||
} else if (expr->GetReceiverTypes() != NULL &&
|
||||
!expr->GetReceiverTypes()->is_empty()) {
|
||||
return HandlePolymorphicElementAccess(
|
||||
obj, key, val, expr, ast_id, position, is_store, has_side_effects);
|
||||
} else {
|
||||
if (is_store) {
|
||||
instr = BuildStoreKeyedGeneric(obj, key, val);
|
||||
} else {
|
||||
instr = BuildLoadKeyedGeneric(obj, key);
|
||||
}
|
||||
}
|
||||
instr->set_position(position);
|
||||
AddInstruction(instr);
|
||||
*has_side_effects = instr->HasSideEffects();
|
||||
return instr;
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
|
||||
HValue* key,
|
||||
HValue* value) {
|
||||
HValue* context = environment()->LookupContext();
|
||||
return new(zone()) HStoreKeyedGeneric(
|
||||
context,
|
||||
object,
|
||||
key,
|
||||
value,
|
||||
expr);
|
||||
} else if (receiver_type->has_fast_elements()) {
|
||||
return BuildStoreKeyedFastElement(object, key, value, expr);
|
||||
function_strict_mode());
|
||||
}
|
||||
}
|
||||
return BuildStoreKeyedGeneric(object, key, value);
|
||||
}
|
||||
|
||||
|
||||
bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
|
||||
VariableProxy* proxy = expr->obj()->AsVariableProxy();
|
||||
@ -4058,7 +4160,23 @@ void HGraphBuilder::VisitProperty(Property* expr) {
|
||||
|
||||
HValue* key = Pop();
|
||||
HValue* obj = Pop();
|
||||
instr = BuildLoadKeyed(obj, key, expr);
|
||||
|
||||
bool has_side_effects = false;
|
||||
HValue* load = HandleKeyedElementAccess(
|
||||
obj, key, NULL, expr, expr->id(), expr->position(),
|
||||
false, // is_store
|
||||
&has_side_effects);
|
||||
if (has_side_effects) {
|
||||
if (ast_context()->IsEffect()) {
|
||||
AddSimulate(expr->id());
|
||||
} else {
|
||||
Push(load);
|
||||
AddSimulate(expr->id());
|
||||
Drop(1);
|
||||
}
|
||||
}
|
||||
ast_context()->ReturnValue(load);
|
||||
return;
|
||||
}
|
||||
instr->set_position(expr->position());
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
@ -5082,16 +5200,22 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
||||
HValue* obj = environment()->ExpressionStackAt(1);
|
||||
HValue* key = environment()->ExpressionStackAt(0);
|
||||
|
||||
HInstruction* load = BuildLoadKeyed(obj, key, prop);
|
||||
PushAndAdd(load);
|
||||
if (load->HasSideEffects()) AddSimulate(expr->CountId());
|
||||
bool has_side_effects = false;
|
||||
HValue* load = HandleKeyedElementAccess(
|
||||
obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition,
|
||||
false, // is_store
|
||||
&has_side_effects);
|
||||
Push(load);
|
||||
if (has_side_effects) AddSimulate(expr->CountId());
|
||||
|
||||
after = BuildIncrement(returns_original_input, expr);
|
||||
input = Pop();
|
||||
|
||||
expr->RecordTypeFeedback(oracle());
|
||||
HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
|
||||
AddInstruction(store);
|
||||
HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
|
||||
RelocInfo::kNoPosition,
|
||||
true, // is_store
|
||||
&has_side_effects);
|
||||
|
||||
// Drop the key from the bailout environment. Overwrite the receiver
|
||||
// with the result of the operation, and the placeholder with the
|
||||
@ -5099,7 +5223,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
||||
Drop(1);
|
||||
environment()->SetExpressionStackAt(0, after);
|
||||
if (returns_original_input) environment()->SetExpressionStackAt(1, input);
|
||||
if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
||||
ASSERT(has_side_effects); // Stores always have side effects.
|
||||
AddSimulate(expr->AssignmentId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -898,18 +898,37 @@ class HGraphBuilder: public AstVisitor {
|
||||
LookupResult* result,
|
||||
bool smi_and_map_check);
|
||||
HInstruction* BuildLoadNamedGeneric(HValue* object, Property* expr);
|
||||
HInstruction* BuildLoadKeyedFastElement(HValue* object,
|
||||
HValue* key,
|
||||
Property* expr);
|
||||
HInstruction* BuildLoadKeyedSpecializedArrayElement(HValue* object,
|
||||
HValue* key,
|
||||
Property* expr);
|
||||
HInstruction* BuildLoadKeyedGeneric(HValue* object,
|
||||
HValue* key);
|
||||
HInstruction* BuildExternalArrayElementAccess(
|
||||
HValue* external_elements,
|
||||
HValue* checked_key,
|
||||
HValue* val,
|
||||
JSObject::ElementsKind elements_kind,
|
||||
bool is_store);
|
||||
|
||||
HInstruction* BuildLoadKeyed(HValue* obj,
|
||||
HInstruction* BuildMonomorphicElementAccess(HValue* object,
|
||||
HValue* key,
|
||||
Property* prop);
|
||||
HValue* val,
|
||||
Expression* expr,
|
||||
bool is_store);
|
||||
HValue* HandlePolymorphicElementAccess(HValue* object,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* prop,
|
||||
int ast_id,
|
||||
int position,
|
||||
bool is_store,
|
||||
bool* has_side_effects);
|
||||
|
||||
HValue* HandleKeyedElementAccess(HValue* obj,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* expr,
|
||||
int ast_id,
|
||||
int position,
|
||||
bool is_store,
|
||||
bool* has_side_effects);
|
||||
|
||||
HInstruction* BuildLoadNamed(HValue* object,
|
||||
Property* prop,
|
||||
@ -931,22 +950,6 @@ class HGraphBuilder: public AstVisitor {
|
||||
HValue* key,
|
||||
HValue* value);
|
||||
|
||||
HInstruction* BuildStoreKeyedFastElement(HValue* object,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* expr);
|
||||
|
||||
HInstruction* BuildStoreKeyedSpecializedArrayElement(
|
||||
HValue* object,
|
||||
HValue* key,
|
||||
HValue* val,
|
||||
Expression* expr);
|
||||
|
||||
HInstruction* BuildStoreKeyed(HValue* object,
|
||||
HValue* key,
|
||||
HValue* value,
|
||||
Expression* assignment);
|
||||
|
||||
HValue* BuildContextChainWalk(Variable* var);
|
||||
|
||||
void AddCheckConstantFunction(Call* expr,
|
||||
|
@ -1215,6 +1215,21 @@ void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoElementsKind(LElementsKind* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
|
||||
// Load map into |result|.
|
||||
__ mov(result, FieldOperand(input, HeapObject::kMapOffset));
|
||||
// Load the map's "bit field 2" into |result|. We only need the first byte,
|
||||
// but the following masking takes care of that anyway.
|
||||
__ mov(result, FieldOperand(result, Map::kBitField2Offset));
|
||||
// Retrieve elements_kind from bit field 2.
|
||||
__ and_(result, Map::kElementsKindMask);
|
||||
__ shr(result, Map::kElementsKindShift);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoValueOf(LValueOf* instr) {
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
@ -1583,6 +1598,29 @@ void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoCmpConstantEq(LCmpConstantEq* instr) {
|
||||
Register left = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
Label done;
|
||||
__ cmp(left, instr->hydrogen()->right());
|
||||
__ mov(result, factory()->true_value());
|
||||
__ j(equal, &done, Label::kNear);
|
||||
__ mov(result, factory()->false_value());
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) {
|
||||
Register left = ToRegister(instr->InputAt(0));
|
||||
int true_block = chunk_->LookupDestination(instr->true_block_id());
|
||||
int false_block = chunk_->LookupDestination(instr->false_block_id());
|
||||
|
||||
__ cmp(left, instr->hydrogen()->right());
|
||||
EmitBranch(true_block, false_block, equal);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoIsNull(LIsNull* instr) {
|
||||
Register reg = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
@ -2366,7 +2404,7 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
__ mov(result, FieldOperand(input, JSObject::kElementsOffset));
|
||||
if (FLAG_debug_code) {
|
||||
Label done;
|
||||
Label done, ok, fail;
|
||||
__ cmp(FieldOperand(result, HeapObject::kMapOffset),
|
||||
Immediate(factory()->fixed_array_map()));
|
||||
__ j(equal, &done, Label::kNear);
|
||||
@ -2376,11 +2414,19 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
||||
Register temp((result.is(eax)) ? ebx : eax);
|
||||
__ push(temp);
|
||||
__ mov(temp, FieldOperand(result, HeapObject::kMapOffset));
|
||||
__ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
|
||||
__ sub(Operand(temp), Immediate(FIRST_EXTERNAL_ARRAY_TYPE));
|
||||
__ cmp(Operand(temp), Immediate(kExternalArrayTypeCount));
|
||||
__ movzx_b(temp, FieldOperand(temp, Map::kBitField2Offset));
|
||||
__ and_(temp, Map::kElementsKindMask);
|
||||
__ shr(temp, Map::kElementsKindShift);
|
||||
__ cmp(temp, JSObject::FAST_ELEMENTS);
|
||||
__ j(equal, &ok, Label::kNear);
|
||||
__ cmp(temp, JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
|
||||
__ j(less, &fail, Label::kNear);
|
||||
__ cmp(temp, JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND);
|
||||
__ j(less_equal, &ok, Label::kNear);
|
||||
__ bind(&fail);
|
||||
__ Abort("Check for fast or external elements failed.");
|
||||
__ bind(&ok);
|
||||
__ pop(temp);
|
||||
__ Check(below, "Check for fast elements or pixel array failed.");
|
||||
__ bind(&done);
|
||||
}
|
||||
}
|
||||
|
@ -1114,6 +1114,9 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
|
||||
HCompareSymbolEq* compare = HCompareSymbolEq::cast(v);
|
||||
return new LCmpSymbolEqAndBranch(UseRegisterAtStart(compare->left()),
|
||||
UseRegisterAtStart(compare->right()));
|
||||
} else if (v->IsCompareConstantEq()) {
|
||||
HCompareConstantEq* compare = HCompareConstantEq::cast(v);
|
||||
return new LCmpConstantEqAndBranch(UseRegisterAtStart(compare->value()));
|
||||
} else if (v->IsTypeofIs()) {
|
||||
HTypeofIs* typeof_is = HTypeofIs::cast(v);
|
||||
return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
|
||||
@ -1543,6 +1546,13 @@ LInstruction* LChunkBuilder::DoCompareSymbolEq(
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoCompareConstantEq(
|
||||
HCompareConstantEq* instr) {
|
||||
LOperand* left = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LCmpConstantEq(left));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
|
||||
ASSERT(instr->value()->representation().IsTagged());
|
||||
LOperand* value = UseRegisterAtStart(instr->value());
|
||||
@ -1628,6 +1638,12 @@ LInstruction* LChunkBuilder::DoExternalArrayLength(
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
|
||||
LOperand* object = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LElementsKind(object));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
|
||||
LOperand* object = UseRegister(instr->value());
|
||||
LValueOf* result = new LValueOf(object, TempRegister());
|
||||
|
@ -77,10 +77,12 @@ class LCodeGen;
|
||||
V(CmpIDAndBranch) \
|
||||
V(CmpJSObjectEq) \
|
||||
V(CmpJSObjectEqAndBranch) \
|
||||
V(CmpMapAndBranch) \
|
||||
V(CmpSymbolEq) \
|
||||
V(CmpSymbolEqAndBranch) \
|
||||
V(CmpMapAndBranch) \
|
||||
V(CmpT) \
|
||||
V(CmpConstantEq) \
|
||||
V(CmpConstantEqAndBranch) \
|
||||
V(ConstantD) \
|
||||
V(ConstantI) \
|
||||
V(ConstantT) \
|
||||
@ -89,6 +91,7 @@ class LCodeGen;
|
||||
V(Deoptimize) \
|
||||
V(DivI) \
|
||||
V(DoubleToI) \
|
||||
V(ElementsKind) \
|
||||
V(ExternalArrayLength) \
|
||||
V(FixedArrayLength) \
|
||||
V(FunctionLiteral) \
|
||||
@ -664,6 +667,29 @@ class LCmpSymbolEqAndBranch: public LControlInstruction<2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LCmpConstantEq: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LCmpConstantEq(LOperand* left) {
|
||||
inputs_[0] = left;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CmpConstantEq, "cmp-constant-eq")
|
||||
DECLARE_HYDROGEN_ACCESSOR(CompareConstantEq)
|
||||
};
|
||||
|
||||
|
||||
class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> {
|
||||
public:
|
||||
explicit LCmpConstantEqAndBranch(LOperand* left) {
|
||||
inputs_[0] = left;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CmpConstantEqAndBranch,
|
||||
"cmp-constant-eq-and-branch")
|
||||
DECLARE_HYDROGEN_ACCESSOR(CompareConstantEq)
|
||||
};
|
||||
|
||||
|
||||
class LIsNull: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LIsNull(LOperand* value) {
|
||||
@ -1078,6 +1104,17 @@ class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LElementsKind: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LElementsKind(LOperand* value) {
|
||||
inputs_[0] = value;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind")
|
||||
DECLARE_HYDROGEN_ACCESSOR(ElementsKind)
|
||||
};
|
||||
|
||||
|
||||
class LValueOf: public LTemplateInstruction<1, 1, 1> {
|
||||
public:
|
||||
LValueOf(LOperand* value, LOperand* temp) {
|
||||
|
@ -1653,7 +1653,7 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
|
||||
|
||||
// If the maximum number of receiver maps has been exceeded, use the generic
|
||||
// version of the IC.
|
||||
if (target_receiver_maps.length() > KeyedIC::kMaxKeyedPolymorphism) {
|
||||
if (target_receiver_maps.length() > kMaxKeyedPolymorphism) {
|
||||
return generic_stub;
|
||||
}
|
||||
|
||||
|
2
src/ic.h
2
src/ic.h
@ -345,8 +345,6 @@ class KeyedIC: public IC {
|
||||
explicit KeyedIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {}
|
||||
virtual ~KeyedIC() {}
|
||||
|
||||
static const int kMaxKeyedPolymorphism = 4;
|
||||
|
||||
virtual MaybeObject* GetFastElementStubWithoutMapCheck(
|
||||
bool is_js_array) = 0;
|
||||
|
||||
|
@ -2060,6 +2060,8 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) {
|
||||
SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
|
||||
SMI_ACCESSORS(ByteArray, length, kLengthOffset)
|
||||
|
||||
// TODO(jkummerow): Investigate if it's possible to s/INT/SMI/ here (and
|
||||
// subsequently unify H{Fixed,External}ArrayLength).
|
||||
INT_ACCESSORS(ExternalArray, length, kLengthOffset)
|
||||
|
||||
|
||||
|
@ -4518,8 +4518,7 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey {
|
||||
|
||||
MapList* maps_; // weak.
|
||||
int code_flags_;
|
||||
static const int kDefaultListAllocationSize =
|
||||
KeyedIC::kMaxKeyedPolymorphism + 1;
|
||||
static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1;
|
||||
};
|
||||
|
||||
|
||||
@ -7152,7 +7151,7 @@ void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) {
|
||||
if (name != NULL) {
|
||||
PrintF(out, "extra_ic_state = %s\n", name);
|
||||
} else {
|
||||
PrintF(out, "etra_ic_state = %d\n", extra);
|
||||
PrintF(out, "extra_ic_state = %d\n", extra);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3758,7 +3758,6 @@ class Code: public HeapObject {
|
||||
static const int kOptimizableOffset = kKindSpecificFlagsOffset;
|
||||
static const int kStackSlotsOffset = kKindSpecificFlagsOffset;
|
||||
static const int kCheckTypeOffset = kKindSpecificFlagsOffset;
|
||||
static const int kExternalArrayTypeOffset = kKindSpecificFlagsOffset;
|
||||
|
||||
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
|
||||
static const int kUnaryOpTypeOffset = kStubMajorKeyOffset + 1;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "v8.h"
|
||||
|
||||
#include "ast.h"
|
||||
#include "code-stubs.h"
|
||||
#include "compiler.h"
|
||||
#include "ic.h"
|
||||
#include "macro-assembler.h"
|
||||
@ -88,6 +89,19 @@ bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) {
|
||||
}
|
||||
|
||||
|
||||
bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) {
|
||||
Handle<Object> map_or_code(GetInfo(expr->id()));
|
||||
if (map_or_code->IsCode()) {
|
||||
Handle<Code> code = Handle<Code>::cast(map_or_code);
|
||||
Builtins* builtins = Isolate::Current()->builtins();
|
||||
return code->is_keyed_load_stub() &&
|
||||
*code != builtins->builtin(Builtins::kKeyedLoadIC_Generic) &&
|
||||
code->ic_state() == MEGAMORPHIC;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) {
|
||||
Handle<Object> map_or_code(GetInfo(expr->id()));
|
||||
if (map_or_code->IsMap()) return true;
|
||||
@ -101,6 +115,19 @@ bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) {
|
||||
}
|
||||
|
||||
|
||||
bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) {
|
||||
Handle<Object> map_or_code(GetInfo(expr->id()));
|
||||
if (map_or_code->IsCode()) {
|
||||
Handle<Code> code = Handle<Code>::cast(map_or_code);
|
||||
Builtins* builtins = Isolate::Current()->builtins();
|
||||
return code->is_keyed_store_stub() &&
|
||||
*code != builtins->builtin(Builtins::kKeyedStoreIC_Generic) &&
|
||||
code->ic_state() == MEGAMORPHIC;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
|
||||
Handle<Object> value = GetInfo(expr->id());
|
||||
return value->IsMap() || value->IsSmi();
|
||||
@ -390,6 +417,27 @@ ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id,
|
||||
}
|
||||
|
||||
|
||||
void TypeFeedbackOracle::CollectKeyedReceiverTypes(
|
||||
unsigned ast_id,
|
||||
ZoneMapList* types) {
|
||||
Handle<Object> object = GetInfo(ast_id);
|
||||
if (!object->IsCode()) return;
|
||||
Handle<Code> code = Handle<Code>::cast(object);
|
||||
if (code->kind() == Code::KEYED_LOAD_IC ||
|
||||
code->kind() == Code::KEYED_STORE_IC) {
|
||||
AssertNoAllocation no_allocation;
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
|
||||
for (RelocIterator it(*code, mask); !it.done(); it.next()) {
|
||||
RelocInfo* info = it.rinfo();
|
||||
Object* object = info->target_object();
|
||||
if (object->IsMap()) {
|
||||
types->Add(Handle<Map>(Map::cast(object)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) {
|
||||
ASSERT(dictionary_->FindEntry(ast_id) == NumberDictionary::kNotFound);
|
||||
MaybeObject* maybe_result = dictionary_->AtNumberPut(ast_id, target);
|
||||
|
@ -36,6 +36,8 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
const int kMaxKeyedPolymorphism = 4;
|
||||
|
||||
// Unknown
|
||||
// | \____________
|
||||
// | |
|
||||
@ -216,7 +218,9 @@ class TypeFeedbackOracle BASE_EMBEDDED {
|
||||
TypeFeedbackOracle(Handle<Code> code, Handle<Context> global_context);
|
||||
|
||||
bool LoadIsMonomorphicNormal(Property* expr);
|
||||
bool LoadIsMegamorphicWithTypeInfo(Property* expr);
|
||||
bool StoreIsMonomorphicNormal(Expression* expr);
|
||||
bool StoreIsMegamorphicWithTypeInfo(Expression* expr);
|
||||
bool CallIsMonomorphic(Call* expr);
|
||||
|
||||
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
|
||||
@ -227,6 +231,8 @@ class TypeFeedbackOracle BASE_EMBEDDED {
|
||||
ZoneMapList* CallReceiverTypes(Call* expr,
|
||||
Handle<String> name,
|
||||
CallKind call_kind);
|
||||
void CollectKeyedReceiverTypes(unsigned ast_id,
|
||||
ZoneMapList* types);
|
||||
|
||||
CheckType GetCallCheckType(Call* expr);
|
||||
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
|
||||
|
@ -1216,6 +1216,20 @@ void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoElementsKind(LElementsKind* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
|
||||
// Load map into |result|.
|
||||
__ movq(result, FieldOperand(input, HeapObject::kMapOffset));
|
||||
// Load the map's "bit field 2" into |result|. We only need the first byte.
|
||||
__ movzxbq(result, FieldOperand(result, Map::kBitField2Offset));
|
||||
// Retrieve elements_kind from bit field 2.
|
||||
__ and_(result, Immediate(Map::kElementsKindMask));
|
||||
__ shr(result, Immediate(Map::kElementsKindShift));
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoValueOf(LValueOf* instr) {
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
@ -1594,6 +1608,29 @@ void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoCmpConstantEq(LCmpConstantEq* instr) {
|
||||
Register left = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
Label done;
|
||||
__ cmpq(left, Immediate(instr->hydrogen()->right()));
|
||||
__ LoadRoot(result, Heap::kTrueValueRootIndex);
|
||||
__ j(equal, &done, Label::kNear);
|
||||
__ LoadRoot(result, Heap::kFalseValueRootIndex);
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) {
|
||||
Register left = ToRegister(instr->InputAt(0));
|
||||
int true_block = chunk_->LookupDestination(instr->true_block_id());
|
||||
int false_block = chunk_->LookupDestination(instr->false_block_id());
|
||||
|
||||
__ cmpq(left, Immediate(instr->hydrogen()->right()));
|
||||
EmitBranch(true_block, false_block, equal);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoIsNull(LIsNull* instr) {
|
||||
Register reg = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
@ -2380,7 +2417,7 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
||||
Register input = ToRegister(instr->InputAt(0));
|
||||
__ movq(result, FieldOperand(input, JSObject::kElementsOffset));
|
||||
if (FLAG_debug_code) {
|
||||
Label done;
|
||||
Label done, ok, fail;
|
||||
__ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
|
||||
Heap::kFixedArrayMapRootIndex);
|
||||
__ j(equal, &done, Label::kNear);
|
||||
@ -2390,11 +2427,19 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
||||
Register temp((result.is(rax)) ? rbx : rax);
|
||||
__ push(temp);
|
||||
__ movq(temp, FieldOperand(result, HeapObject::kMapOffset));
|
||||
__ movzxbq(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
|
||||
__ subq(temp, Immediate(FIRST_EXTERNAL_ARRAY_TYPE));
|
||||
__ cmpq(temp, Immediate(kExternalArrayTypeCount));
|
||||
__ movzxbq(temp, FieldOperand(temp, Map::kBitField2Offset));
|
||||
__ and_(temp, Immediate(Map::kElementsKindMask));
|
||||
__ shr(temp, Immediate(Map::kElementsKindShift));
|
||||
__ cmpl(temp, Immediate(JSObject::FAST_ELEMENTS));
|
||||
__ j(equal, &ok, Label::kNear);
|
||||
__ cmpl(temp, Immediate(JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND));
|
||||
__ j(less, &fail, Label::kNear);
|
||||
__ cmpl(temp, Immediate(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND));
|
||||
__ j(less_equal, &ok, Label::kNear);
|
||||
__ bind(&fail);
|
||||
__ Abort("Check for fast or external elements failed");
|
||||
__ bind(&ok);
|
||||
__ pop(temp);
|
||||
__ Check(below, "Check for fast elements failed.");
|
||||
__ bind(&done);
|
||||
}
|
||||
}
|
||||
|
@ -1108,6 +1108,9 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
|
||||
HCompareSymbolEq* compare = HCompareSymbolEq::cast(v);
|
||||
return new LCmpSymbolEqAndBranch(UseRegisterAtStart(compare->left()),
|
||||
UseRegisterAtStart(compare->right()));
|
||||
} else if (v->IsCompareConstantEq()) {
|
||||
HCompareConstantEq* compare = HCompareConstantEq::cast(v);
|
||||
return new LCmpConstantEqAndBranch(UseRegisterAtStart(compare->value()));
|
||||
} else if (v->IsTypeofIs()) {
|
||||
HTypeofIs* typeof_is = HTypeofIs::cast(v);
|
||||
return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
|
||||
@ -1519,6 +1522,13 @@ LInstruction* LChunkBuilder::DoCompareSymbolEq(
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoCompareConstantEq(
|
||||
HCompareConstantEq* instr) {
|
||||
LOperand* left = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LCmpConstantEq(left));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
|
||||
ASSERT(instr->value()->representation().IsTagged());
|
||||
LOperand* value = UseRegisterAtStart(instr->value());
|
||||
@ -1601,6 +1611,12 @@ LInstruction* LChunkBuilder::DoExternalArrayLength(
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
|
||||
LOperand* object = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LElementsKind(object));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
|
||||
LOperand* object = UseRegister(instr->value());
|
||||
LValueOf* result = new LValueOf(object);
|
||||
|
@ -79,6 +79,8 @@ class LCodeGen;
|
||||
V(ClampTToUint8) \
|
||||
V(ClassOfTest) \
|
||||
V(ClassOfTestAndBranch) \
|
||||
V(CmpConstantEq) \
|
||||
V(CmpConstantEqAndBranch) \
|
||||
V(CmpID) \
|
||||
V(CmpIDAndBranch) \
|
||||
V(CmpJSObjectEq) \
|
||||
@ -95,6 +97,7 @@ class LCodeGen;
|
||||
V(Deoptimize) \
|
||||
V(DivI) \
|
||||
V(DoubleToI) \
|
||||
V(ElementsKind) \
|
||||
V(ExternalArrayLength) \
|
||||
V(FixedArrayLength) \
|
||||
V(FunctionLiteral) \
|
||||
@ -662,6 +665,29 @@ class LCmpSymbolEqAndBranch: public LControlInstruction<2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LCmpConstantEq: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LCmpConstantEq(LOperand* left) {
|
||||
inputs_[0] = left;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CmpConstantEq, "cmp-constant-eq")
|
||||
DECLARE_HYDROGEN_ACCESSOR(CompareConstantEq)
|
||||
};
|
||||
|
||||
|
||||
class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> {
|
||||
public:
|
||||
explicit LCmpConstantEqAndBranch(LOperand* left) {
|
||||
inputs_[0] = left;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(CmpConstantEqAndBranch,
|
||||
"cmp-constant-eq-and-branch")
|
||||
DECLARE_HYDROGEN_ACCESSOR(CompareConstantEq)
|
||||
};
|
||||
|
||||
|
||||
class LIsNull: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LIsNull(LOperand* value) {
|
||||
@ -1063,6 +1089,17 @@ class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LElementsKind: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LElementsKind(LOperand* value) {
|
||||
inputs_[0] = value;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind")
|
||||
DECLARE_HYDROGEN_ACCESSOR(ElementsKind)
|
||||
};
|
||||
|
||||
|
||||
class LValueOf: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LValueOf(LOperand* value) {
|
||||
|
Loading…
Reference in New Issue
Block a user