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) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0));
Register scratch = scratch0();
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
__ cmp(scratch, Operand(first));
// If there is only one type in the interval check for equality.
if (first == last) {
DeoptimizeIf(ne, instr->environment());
if (instr->hydrogen()->is_interval_check()) {
InstanceType first;
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 {
DeoptimizeIf(lo, instr->environment());
// Omit check for the last type.
if (last != LAST_TYPE) {
__ cmp(scratch, Operand(last));
DeoptimizeIf(hi, instr->environment());
uint8_t mask;
uint8_t tag;
instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
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) {
}
HCheckInstanceType* HCheckInstanceType::NewIsJSObjectOrJSFunction(
HValue* value) {
STATIC_ASSERT((LAST_JS_OBJECT_TYPE + 1) == JS_FUNCTION_TYPE);
return new HCheckInstanceType(value, FIRST_JS_OBJECT_TYPE, JS_FUNCTION_TYPE);
void HCheckInstanceType::GetCheckInterval(InstanceType* first,
InstanceType* last) {
ASSERT(is_interval_check());
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 {
class HCheckInstanceType: public HUnaryOperation {
public:
// Check that the instance type is in the range [first, last] where
// both first and last are included.
HCheckInstanceType(HValue* value, InstanceType first, InstanceType last)
: HUnaryOperation(value), first_(first), last_(last) {
ASSERT(first <= last);
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
if ((FIRST_STRING_TYPE < first && last <= LAST_STRING_TYPE) ||
(FIRST_STRING_TYPE <= first && last < LAST_STRING_TYPE)) {
// A particular string instance type can change because of GC or
// externalization, but the value still remains a string.
SetFlag(kDependsOnMaps);
}
static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value) {
return new HCheckInstanceType(value, IS_JS_OBJECT_OR_JS_FUNCTION);
}
static HCheckInstanceType* NewIsJSArray(HValue* value) {
return new HCheckInstanceType(value, IS_JS_ARRAY);
}
static HCheckInstanceType* NewIsString(HValue* value) {
return new HCheckInstanceType(value, IS_STRING);
}
static HCheckInstanceType* NewIsSymbol(HValue* value) {
return new HCheckInstanceType(value, IS_SYMBOL);
}
virtual bool IsCheckInstruction() const { return true; }
......@@ -1785,17 +1783,15 @@ class HCheckInstanceType: public HUnaryOperation {
virtual HValue* Canonicalize() {
if (!value()->type().IsUninitialized() &&
value()->type().IsString() &&
first() == FIRST_STRING_TYPE &&
last() == LAST_STRING_TYPE) {
check_ == IS_STRING) {
return NULL;
}
return this;
}
static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value);
InstanceType first() const { return first_; }
InstanceType last() const { return last_; }
bool is_interval_check() const { return check_ <= LAST_INTERVAL_CHECK; }
void GetCheckInterval(InstanceType* first, InstanceType* last);
void GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag);
DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType)
......@@ -1805,12 +1801,25 @@ class HCheckInstanceType: public HUnaryOperation {
// with a larger range.
virtual bool DataEquals(HValue* other) {
HCheckInstanceType* b = HCheckInstanceType::cast(other);
return (first_ == b->first()) && (last_ == b->last());
return check_ == b->check_;
}
private:
InstanceType first_;
InstanceType last_;
enum Check {
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) {
if (expr->IsArrayLength()) {
HValue* array = Pop();
AddInstruction(new(zone()) HCheckNonSmi(array));
AddInstruction(new(zone()) HCheckInstanceType(array,
JS_ARRAY_TYPE,
JS_ARRAY_TYPE));
AddInstruction(HCheckInstanceType::NewIsJSArray(array));
instr = new(zone()) HJSArrayLength(array);
} else if (expr->IsStringLength()) {
HValue* string = Pop();
AddInstruction(new(zone()) HCheckNonSmi(string));
AddInstruction(new(zone()) HCheckInstanceType(string,
FIRST_STRING_TYPE,
LAST_STRING_TYPE));
AddInstruction(HCheckInstanceType::NewIsString(string));
instr = new(zone()) HStringLength(string);
} else if (expr->IsStringAccess()) {
CHECK_ALIVE(VisitForValue(expr->key()));
......@@ -4853,8 +4849,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HValue* index) {
AddInstruction(new(zone()) HCheckNonSmi(string));
AddInstruction(new(zone()) HCheckInstanceType(
string, FIRST_STRING_TYPE, LAST_STRING_TYPE));
AddInstruction(HCheckInstanceType::NewIsString(string));
HStringLength* length = new(zone()) HStringLength(string);
AddInstruction(length);
AddInstruction(new(zone()) HBoundsCheck(index, length));
......@@ -4894,11 +4889,9 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
case Token::ADD:
if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(new(zone()) HCheckInstanceType(left, FIRST_STRING_TYPE,
LAST_STRING_TYPE));
AddInstruction(HCheckInstanceType::NewIsString(left));
AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(new(zone()) HCheckInstanceType(right, FIRST_STRING_TYPE,
LAST_STRING_TYPE));
AddInstruction(HCheckInstanceType::NewIsString(right));
return new(zone()) HStringAdd(left, right);
} else {
return new(zone()) HAdd(left, right);
......
......@@ -3829,29 +3829,43 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0));
Register temp = ToRegister(instr->TempAt(0));
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
// If there is only one type in the interval check for equality.
if (first == last) {
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
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 {
if (instr->hydrogen()->is_interval_check()) {
InstanceType first;
InstanceType last;
instr->hydrogen()->GetCheckInterval(&first, &last);
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
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());
// If there is only one type in the interval check for equality.
if (first == last) {
DeoptimizeIf(not_equal, instr->environment());
} else {
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) {
case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
case PROXY_TYPE: return "PROXY";
case LAST_STRING_TYPE: return "LAST_STRING_TYPE";
case JS_MESSAGE_OBJECT_TYPE: return "JS_MESSAGE_OBJECT_TYPE";
#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
STRUCT_LIST(MAKE_STRUCT_CASE)
......
......@@ -485,7 +485,6 @@ const uint32_t kShortcutTypeTag = kConsStringTag;
enum InstanceType {
// String types.
// FIRST_STRING_TYPE
SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag,
ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag,
CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag,
......@@ -568,8 +567,6 @@ enum InstanceType {
LAST_TYPE = JS_FUNCTION_TYPE,
INVALID_TYPE = FIRST_TYPE - 1,
FIRST_NONSTRING_TYPE = MAP_TYPE,
FIRST_STRING_TYPE = FIRST_TYPE,
LAST_STRING_TYPE = FIRST_NONSTRING_TYPE - 1,
// Boundaries for testing for an external array.
FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE,
LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_PIXEL_ARRAY_TYPE,
......
......@@ -3650,30 +3650,45 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0));
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset));
// If there is only one type in the interval check for equality.
if (first == last) {
if (instr->hydrogen()->is_interval_check()) {
InstanceType first;
InstanceType last;
instr->hydrogen()->GetCheckInterval(&first, &last);
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
Immediate(static_cast<int8_t>(first)));
DeoptimizeIf(not_equal, instr->environment());
} else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) {
// String has a dedicated bit in instance type.
__ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
Immediate(kIsNotStringMask));
DeoptimizeIf(not_zero, instr->environment());
// If there is only one type in the interval check for equality.
if (first == last) {
DeoptimizeIf(not_equal, instr->environment());
} else {
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 {
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
Immediate(static_cast<int8_t>(first)));
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());
uint8_t mask;
uint8_t tag;
instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
if (IsPowerOf2(mask)) {
ASSERT(tag == 0 || IsPowerOf2(tag));
__ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
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