Commit c10267f1 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

Refactor HCheckInstanceType to allow mask/tag tests.

This allows us to get rid of totally fake LAST_STRING_TYPE and makes
it possible to test for symbols.

I considered splitting HCheckInstanceType into two instructions, but
it seems nice to be able to hide the instance type implementation
details from the hydrogen level.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7840 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 4912c037
...@@ -3869,22 +3869,41 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) { ...@@ -3869,22 +3869,41 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0)); Register input = ToRegister(instr->InputAt(0));
Register scratch = scratch0(); Register scratch = scratch0();
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset)); __ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
__ cmp(scratch, Operand(first));
// If there is only one type in the interval check for equality. if (instr->hydrogen()->is_interval_check()) {
if (first == last) { InstanceType first;
DeoptimizeIf(ne, instr->environment()); InstanceType last;
instr->hydrogen()->GetCheckInterval(&first, &last);
__ cmp(scratch, Operand(first));
// If there is only one type in the interval check for equality.
if (first == last) {
DeoptimizeIf(ne, instr->environment());
} else {
DeoptimizeIf(lo, instr->environment());
// Omit check for the last type.
if (last != LAST_TYPE) {
__ cmp(scratch, Operand(last));
DeoptimizeIf(hi, instr->environment());
}
}
} else { } else {
DeoptimizeIf(lo, instr->environment()); uint8_t mask;
// Omit check for the last type. uint8_t tag;
if (last != LAST_TYPE) { instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
__ cmp(scratch, Operand(last));
DeoptimizeIf(hi, instr->environment()); if (IsPowerOf2(mask)) {
ASSERT(tag == 0 || IsPowerOf2(tag));
__ tst(scratch, Operand(mask));
DeoptimizeIf(tag == 0 ? ne : eq, instr->environment());
} else {
__ and_(scratch, scratch, Operand(mask));
__ cmp(scratch, Operand(tag));
DeoptimizeIf(ne, instr->environment());
} }
} }
} }
......
...@@ -751,10 +751,38 @@ void HChange::PrintDataTo(StringStream* stream) { ...@@ -751,10 +751,38 @@ void HChange::PrintDataTo(StringStream* stream) {
} }
HCheckInstanceType* HCheckInstanceType::NewIsJSObjectOrJSFunction( void HCheckInstanceType::GetCheckInterval(InstanceType* first,
HValue* value) { InstanceType* last) {
STATIC_ASSERT((LAST_JS_OBJECT_TYPE + 1) == JS_FUNCTION_TYPE); ASSERT(is_interval_check());
return new HCheckInstanceType(value, FIRST_JS_OBJECT_TYPE, JS_FUNCTION_TYPE); switch (check_) {
case IS_JS_OBJECT_OR_JS_FUNCTION:
STATIC_ASSERT((LAST_JS_OBJECT_TYPE + 1) == JS_FUNCTION_TYPE);
*first = FIRST_JS_OBJECT_TYPE;
*last = JS_FUNCTION_TYPE;
return;
case IS_JS_ARRAY:
*first = *last = JS_ARRAY_TYPE;
return;
default:
UNREACHABLE();
}
}
void HCheckInstanceType::GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag) {
ASSERT(!is_interval_check());
switch (check_) {
case IS_STRING:
*mask = kIsNotStringMask;
*tag = kStringTag;
return;
case IS_SYMBOL:
*mask = kIsSymbolMask;
*tag = kSymbolTag;
return;
default:
UNREACHABLE();
}
} }
......
...@@ -1757,19 +1757,17 @@ class HCheckFunction: public HUnaryOperation { ...@@ -1757,19 +1757,17 @@ class HCheckFunction: public HUnaryOperation {
class HCheckInstanceType: public HUnaryOperation { class HCheckInstanceType: public HUnaryOperation {
public: public:
// Check that the instance type is in the range [first, last] where static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value) {
// both first and last are included. return new HCheckInstanceType(value, IS_JS_OBJECT_OR_JS_FUNCTION);
HCheckInstanceType(HValue* value, InstanceType first, InstanceType last) }
: HUnaryOperation(value), first_(first), last_(last) { static HCheckInstanceType* NewIsJSArray(HValue* value) {
ASSERT(first <= last); return new HCheckInstanceType(value, IS_JS_ARRAY);
set_representation(Representation::Tagged()); }
SetFlag(kUseGVN); static HCheckInstanceType* NewIsString(HValue* value) {
if ((FIRST_STRING_TYPE < first && last <= LAST_STRING_TYPE) || return new HCheckInstanceType(value, IS_STRING);
(FIRST_STRING_TYPE <= first && last < LAST_STRING_TYPE)) { }
// A particular string instance type can change because of GC or static HCheckInstanceType* NewIsSymbol(HValue* value) {
// externalization, but the value still remains a string. return new HCheckInstanceType(value, IS_SYMBOL);
SetFlag(kDependsOnMaps);
}
} }
virtual bool IsCheckInstruction() const { return true; } virtual bool IsCheckInstruction() const { return true; }
...@@ -1785,17 +1783,15 @@ class HCheckInstanceType: public HUnaryOperation { ...@@ -1785,17 +1783,15 @@ class HCheckInstanceType: public HUnaryOperation {
virtual HValue* Canonicalize() { virtual HValue* Canonicalize() {
if (!value()->type().IsUninitialized() && if (!value()->type().IsUninitialized() &&
value()->type().IsString() && value()->type().IsString() &&
first() == FIRST_STRING_TYPE && check_ == IS_STRING) {
last() == LAST_STRING_TYPE) {
return NULL; return NULL;
} }
return this; return this;
} }
static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value); bool is_interval_check() const { return check_ <= LAST_INTERVAL_CHECK; }
void GetCheckInterval(InstanceType* first, InstanceType* last);
InstanceType first() const { return first_; } void GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag);
InstanceType last() const { return last_; }
DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType) DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType)
...@@ -1805,12 +1801,25 @@ class HCheckInstanceType: public HUnaryOperation { ...@@ -1805,12 +1801,25 @@ class HCheckInstanceType: public HUnaryOperation {
// with a larger range. // with a larger range.
virtual bool DataEquals(HValue* other) { virtual bool DataEquals(HValue* other) {
HCheckInstanceType* b = HCheckInstanceType::cast(other); HCheckInstanceType* b = HCheckInstanceType::cast(other);
return (first_ == b->first()) && (last_ == b->last()); return check_ == b->check_;
} }
private: private:
InstanceType first_; enum Check {
InstanceType last_; IS_JS_OBJECT_OR_JS_FUNCTION,
IS_JS_ARRAY,
IS_STRING,
IS_SYMBOL,
LAST_INTERVAL_CHECK = IS_JS_ARRAY
};
HCheckInstanceType(HValue* value, Check check)
: HUnaryOperation(value), check_(check) {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
}
const Check check_;
}; };
......
...@@ -3796,17 +3796,13 @@ void HGraphBuilder::VisitProperty(Property* expr) { ...@@ -3796,17 +3796,13 @@ void HGraphBuilder::VisitProperty(Property* expr) {
if (expr->IsArrayLength()) { if (expr->IsArrayLength()) {
HValue* array = Pop(); HValue* array = Pop();
AddInstruction(new(zone()) HCheckNonSmi(array)); AddInstruction(new(zone()) HCheckNonSmi(array));
AddInstruction(new(zone()) HCheckInstanceType(array, AddInstruction(HCheckInstanceType::NewIsJSArray(array));
JS_ARRAY_TYPE,
JS_ARRAY_TYPE));
instr = new(zone()) HJSArrayLength(array); instr = new(zone()) HJSArrayLength(array);
} else if (expr->IsStringLength()) { } else if (expr->IsStringLength()) {
HValue* string = Pop(); HValue* string = Pop();
AddInstruction(new(zone()) HCheckNonSmi(string)); AddInstruction(new(zone()) HCheckNonSmi(string));
AddInstruction(new(zone()) HCheckInstanceType(string, AddInstruction(HCheckInstanceType::NewIsString(string));
FIRST_STRING_TYPE,
LAST_STRING_TYPE));
instr = new(zone()) HStringLength(string); instr = new(zone()) HStringLength(string);
} else if (expr->IsStringAccess()) { } else if (expr->IsStringAccess()) {
CHECK_ALIVE(VisitForValue(expr->key())); CHECK_ALIVE(VisitForValue(expr->key()));
...@@ -4853,8 +4849,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4853,8 +4849,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string, HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HValue* index) { HValue* index) {
AddInstruction(new(zone()) HCheckNonSmi(string)); AddInstruction(new(zone()) HCheckNonSmi(string));
AddInstruction(new(zone()) HCheckInstanceType( AddInstruction(HCheckInstanceType::NewIsString(string));
string, FIRST_STRING_TYPE, LAST_STRING_TYPE));
HStringLength* length = new(zone()) HStringLength(string); HStringLength* length = new(zone()) HStringLength(string);
AddInstruction(length); AddInstruction(length);
AddInstruction(new(zone()) HBoundsCheck(index, length)); AddInstruction(new(zone()) HBoundsCheck(index, length));
...@@ -4894,11 +4889,9 @@ HInstruction* HGraphBuilder::BuildBinaryOperation( ...@@ -4894,11 +4889,9 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
case Token::ADD: case Token::ADD:
if (info.IsString()) { if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left)); AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(new(zone()) HCheckInstanceType(left, FIRST_STRING_TYPE, AddInstruction(HCheckInstanceType::NewIsString(left));
LAST_STRING_TYPE));
AddInstruction(new(zone()) HCheckNonSmi(right)); AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(new(zone()) HCheckInstanceType(right, FIRST_STRING_TYPE, AddInstruction(HCheckInstanceType::NewIsString(right));
LAST_STRING_TYPE));
return new(zone()) HStringAdd(left, right); return new(zone()) HStringAdd(left, right);
} else { } else {
return new(zone()) HAdd(left, right); return new(zone()) HAdd(left, right);
......
...@@ -3829,29 +3829,43 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) { ...@@ -3829,29 +3829,43 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0)); Register input = ToRegister(instr->InputAt(0));
Register temp = ToRegister(instr->TempAt(0)); Register temp = ToRegister(instr->TempAt(0));
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ mov(temp, FieldOperand(input, HeapObject::kMapOffset)); __ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
// If there is only one type in the interval check for equality. if (instr->hydrogen()->is_interval_check()) {
if (first == last) { InstanceType first;
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset), InstanceType last;
static_cast<int8_t>(first)); instr->hydrogen()->GetCheckInterval(&first, &last);
DeoptimizeIf(not_equal, instr->environment());
} else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) {
// String has a dedicated bit in instance type.
__ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), kIsNotStringMask);
DeoptimizeIf(not_zero, instr->environment());
} else {
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset), __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first)); static_cast<int8_t>(first));
DeoptimizeIf(below, instr->environment());
// Omit check for the last type. // If there is only one type in the interval check for equality.
if (last != LAST_TYPE) { if (first == last) {
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset), DeoptimizeIf(not_equal, instr->environment());
static_cast<int8_t>(last)); } else {
DeoptimizeIf(above, instr->environment()); DeoptimizeIf(below, instr->environment());
// Omit check for the last type.
if (last != LAST_TYPE) {
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(last));
DeoptimizeIf(above, instr->environment());
}
}
} else {
uint8_t mask;
uint8_t tag;
instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
if (IsPowerOf2(mask)) {
ASSERT(tag == 0 || IsPowerOf2(tag));
__ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), mask);
DeoptimizeIf(tag == 0 ? not_zero : zero, instr->environment());
} else {
__ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
__ and_(temp, mask);
__ cmpb(Operand(temp), tag);
DeoptimizeIf(not_equal, instr->environment());
} }
} }
} }
......
...@@ -414,7 +414,6 @@ static const char* TypeToString(InstanceType type) { ...@@ -414,7 +414,6 @@ static const char* TypeToString(InstanceType type) {
case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT"; case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY"; case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
case PROXY_TYPE: return "PROXY"; case PROXY_TYPE: return "PROXY";
case LAST_STRING_TYPE: return "LAST_STRING_TYPE";
case JS_MESSAGE_OBJECT_TYPE: return "JS_MESSAGE_OBJECT_TYPE"; case JS_MESSAGE_OBJECT_TYPE: return "JS_MESSAGE_OBJECT_TYPE";
#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME; #define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
STRUCT_LIST(MAKE_STRUCT_CASE) STRUCT_LIST(MAKE_STRUCT_CASE)
......
...@@ -485,7 +485,6 @@ const uint32_t kShortcutTypeTag = kConsStringTag; ...@@ -485,7 +485,6 @@ const uint32_t kShortcutTypeTag = kConsStringTag;
enum InstanceType { enum InstanceType {
// String types. // String types.
// FIRST_STRING_TYPE
SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag, SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag,
ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag, ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag,
CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag, CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag,
...@@ -568,8 +567,6 @@ enum InstanceType { ...@@ -568,8 +567,6 @@ enum InstanceType {
LAST_TYPE = JS_FUNCTION_TYPE, LAST_TYPE = JS_FUNCTION_TYPE,
INVALID_TYPE = FIRST_TYPE - 1, INVALID_TYPE = FIRST_TYPE - 1,
FIRST_NONSTRING_TYPE = MAP_TYPE, FIRST_NONSTRING_TYPE = MAP_TYPE,
FIRST_STRING_TYPE = FIRST_TYPE,
LAST_STRING_TYPE = FIRST_NONSTRING_TYPE - 1,
// Boundaries for testing for an external array. // Boundaries for testing for an external array.
FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE, FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE,
LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_PIXEL_ARRAY_TYPE, LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_PIXEL_ARRAY_TYPE,
......
...@@ -3650,30 +3650,45 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) { ...@@ -3650,30 +3650,45 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0)); Register input = ToRegister(instr->InputAt(0));
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset)); __ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset));
// If there is only one type in the interval check for equality. if (instr->hydrogen()->is_interval_check()) {
if (first == last) { InstanceType first;
InstanceType last;
instr->hydrogen()->GetCheckInterval(&first, &last);
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset), __ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
Immediate(static_cast<int8_t>(first))); Immediate(static_cast<int8_t>(first)));
DeoptimizeIf(not_equal, instr->environment());
} else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) { // If there is only one type in the interval check for equality.
// String has a dedicated bit in instance type. if (first == last) {
__ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset), DeoptimizeIf(not_equal, instr->environment());
Immediate(kIsNotStringMask)); } else {
DeoptimizeIf(not_zero, instr->environment()); DeoptimizeIf(below, instr->environment());
// Omit check for the last type.
if (last != LAST_TYPE) {
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
Immediate(static_cast<int8_t>(last)));
DeoptimizeIf(above, instr->environment());
}
}
} else { } else {
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset), uint8_t mask;
Immediate(static_cast<int8_t>(first))); uint8_t tag;
DeoptimizeIf(below, instr->environment()); instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
// Omit check for the last type.
if (last != LAST_TYPE) { if (IsPowerOf2(mask)) {
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset), ASSERT(tag == 0 || IsPowerOf2(tag));
Immediate(static_cast<int8_t>(last))); __ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
DeoptimizeIf(above, instr->environment()); Immediate(mask));
DeoptimizeIf(tag == 0 ? not_zero : zero, instr->environment());
} else {
__ movzxbl(kScratchRegister,
FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
__ andb(kScratchRegister, Immediate(mask));
__ cmpb(kScratchRegister, Immediate(tag));
DeoptimizeIf(not_equal, instr->environment());
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment