Implement a type recording ToBoolean IC.

The IC records the set of types it has seen, e.g. {String} or {Boolean,
Undefined}, etc.  Note that in theory this could lead to a large number of
different ToBoolean ICs (512, to be exact, because we distinguish 9 types),
but in practice only a small handful of them are actually generated.

Currently the type recording part is only implemented on ia32, other platforms
continue to work like they did before, though.

Removed some dead code on the way.
Review URL: http://codereview.chromium.org/7473028

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8716 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
svenpanne@chromium.org 2011-07-21 13:51:04 +00:00
parent 0b55f28e34
commit f7138b1427
11 changed files with 334 additions and 74 deletions

View File

@ -329,4 +329,73 @@ void CallFunctionStub::PrintName(StringStream* stream) {
stream->Add("CallFunctionStub_Args%d%s%s", argc_, in_loop_name, flags_name); stream->Add("CallFunctionStub_Args%d%s%s", argc_, in_loop_name, flags_name);
} }
void ToBooleanStub::PrintName(StringStream* stream) {
stream->Add("ToBooleanStub_");
types_.Print(stream);
}
void ToBooleanStub::Types::Print(StringStream* stream) {
if (IsEmpty()) stream->Add("None");
if (Contains(UNDEFINED)) stream->Add("Undefined");
if (Contains(BOOLEAN)) stream->Add("Bool");
if (Contains(SMI)) stream->Add("Smi");
if (Contains(NULL_TYPE)) stream->Add("Null");
if (Contains(UNDETECTABLE)) stream->Add("Undetectable");
if (Contains(SPEC_OBJECT)) stream->Add("SpecObject");
if (Contains(STRING)) stream->Add("String");
if (Contains(HEAP_NUMBER)) stream->Add("HeapNumber");
if (Contains(INTERNAL_OBJECT)) stream->Add("InternalObject");
}
void ToBooleanStub::Types::TraceTransition(Types to) {
if (!FLAG_trace_ic) return;
char buffer[100];
NoAllocationStringAllocator allocator(buffer,
static_cast<unsigned>(sizeof(buffer)));
StringStream stream(&allocator);
stream.Add("[ToBooleanIC (");
Print(&stream);
stream.Add("->");
to.Print(&stream);
stream.Add(")]\n");
stream.OutputToStdOut();
}
bool ToBooleanStub::Types::Record(Handle<Object> object) {
if (object->IsUndefined()) {
Add(UNDEFINED);
return false;
} else if (object->IsBoolean()) {
Add(BOOLEAN);
return object->IsTrue();
} else if (object->IsNull()) {
Add(NULL_TYPE);
return false;
} else if (object->IsSmi()) {
Add(SMI);
return Smi::cast(*object)->value() != 0;
} else if (object->IsUndetectableObject()) {
Add(UNDETECTABLE);
return false;
} else if (object->IsSpecObject()) {
Add(SPEC_OBJECT);
return true;
} else if (object->IsString()) {
Add(STRING);
return String::cast(*object)->length() != 0;
} else if (object->IsHeapNumber()) {
Add(HEAP_NUMBER);
double value = HeapNumber::cast(*object)->value();
return value != 0 && !isnan(value);
} else {
Add(INTERNAL_OBJECT);
return true;
}
}
} } // namespace v8::internal } } // namespace v8::internal

View File

@ -900,14 +900,56 @@ class KeyedStoreElementStub : public CodeStub {
class ToBooleanStub: public CodeStub { class ToBooleanStub: public CodeStub {
public: public:
explicit ToBooleanStub(Register tos) : tos_(tos) { } enum Type {
UNDEFINED,
BOOLEAN,
NULL_TYPE,
SMI,
UNDETECTABLE,
SPEC_OBJECT,
STRING,
HEAP_NUMBER,
INTERNAL_OBJECT,
NUMBER_OF_TYPES
};
class Types {
public:
Types() {}
explicit Types(int bits) : set_(bits) {}
bool IsEmpty() const { return set_.IsEmpty(); }
bool Contains(Type type) const { return set_.Contains(type); }
void Add(Type type) { set_.Add(type); }
int ToInt() const { return set_.ToIntegral(); }
void Print(StringStream* stream);
void TraceTransition(Types to);
bool Record(Handle<Object> object);
private:
EnumSet<Type> set_;
};
explicit ToBooleanStub(Register tos, Types types = Types())
: tos_(tos), types_(types) { }
void Generate(MacroAssembler* masm); void Generate(MacroAssembler* masm);
virtual int GetCodeKind() { return Code::TO_BOOLEAN_IC; }
virtual void PrintName(StringStream* stream);
private: private:
Register tos_;
Major MajorKey() { return ToBoolean; } Major MajorKey() { return ToBoolean; }
int MinorKey() { return tos_.code(); } int MinorKey() { return (tos_.code() << NUMBER_OF_TYPES) | types_.ToInt(); }
void CheckOddball(MacroAssembler* masm,
Type type,
Handle<Object> value,
bool result,
Label* patch);
void GenerateTypeTransition(MacroAssembler* masm);
Register tos_;
Types types_;
}; };
} } // namespace v8::internal } } // namespace v8::internal

View File

@ -169,7 +169,8 @@ void BreakLocationIterator::Next() {
if ((code->is_inline_cache_stub() && if ((code->is_inline_cache_stub() &&
!code->is_binary_op_stub() && !code->is_binary_op_stub() &&
!code->is_unary_op_stub() && !code->is_unary_op_stub() &&
!code->is_compare_ic_stub()) || !code->is_compare_ic_stub() &&
!code->is_to_boolean_ic_stub()) ||
RelocInfo::IsConstructCall(rmode())) { RelocInfo::IsConstructCall(rmode())) {
break_point_++; break_point_++;
return; return;

View File

@ -236,69 +236,163 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
} }
// The stub returns zero for false, and a non-zero value for true. // The stub expects its argument on the stack and returns its result in tos_:
// zero for false, and a non-zero value for true.
void ToBooleanStub::Generate(MacroAssembler* masm) { void ToBooleanStub::Generate(MacroAssembler* masm) {
Label false_result, true_result, not_string; Label patch;
Factory* factory = masm->isolate()->factory(); Factory* factory = masm->isolate()->factory();
const Register argument = eax;
const Register map = edx; const Register map = edx;
__ mov(eax, Operand(esp, 1 * kPointerSize)); if (!types_.IsEmpty()) {
__ mov(argument, Operand(esp, 1 * kPointerSize));
}
// undefined -> false // undefined -> false
__ cmp(eax, factory->undefined_value()); CheckOddball(masm, UNDEFINED, factory->undefined_value(), false, &patch);
__ j(equal, &false_result);
// Boolean -> its value // Boolean -> its value
__ cmp(eax, factory->false_value()); CheckOddball(masm, BOOLEAN, factory->false_value(), false, &patch);
__ j(equal, &false_result); CheckOddball(masm, BOOLEAN, factory->true_value(), true, &patch);
__ cmp(eax, factory->true_value());
__ j(equal, &true_result);
// Smis: 0 -> false, all other -> true
__ test(eax, Operand(eax));
__ j(zero, &false_result);
__ JumpIfSmi(eax, &true_result);
// 'null' -> false. // 'null' -> false.
__ cmp(eax, factory->null_value()); CheckOddball(masm, NULL_TYPE, factory->null_value(), false, &patch);
__ j(equal, &false_result, Label::kNear);
// Get the map of the heap object. bool need_map =
__ mov(map, FieldOperand(eax, HeapObject::kMapOffset)); types_.Contains(UNDETECTABLE) |
types_.Contains(SPEC_OBJECT) |
types_.Contains(STRING) |
types_.Contains(HEAP_NUMBER) |
types_.Contains(INTERNAL_OBJECT);
// Undetectable -> false. if (types_.Contains(SMI)) {
__ test_b(FieldOperand(map, Map::kBitFieldOffset), // Smis: 0 -> false, all other -> true
1 << Map::kIsUndetectable); Label not_smi;
__ j(not_zero, &false_result, Label::kNear); __ JumpIfNotSmi(argument, &not_smi, Label::kNear);
// argument contains the correct return value already
if (!tos_.is(argument)) {
__ mov(tos_, argument);
}
__ ret(1 * kPointerSize);
__ bind(&not_smi);
} else if (need_map) {
// If we need a map later and have a Smi -> patch.
__ JumpIfSmi(argument, &patch, Label::kNear);
}
// JavaScript object -> true. if (need_map) {
__ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); __ mov(map, FieldOperand(argument, HeapObject::kMapOffset));
__ j(above_equal, &true_result, Label::kNear);
// String value -> false iff empty. // Everything with a map could be undetectable, so check this now.
__ CmpInstanceType(map, FIRST_NONSTRING_TYPE); __ test_b(FieldOperand(map, Map::kBitFieldOffset),
__ j(above_equal, &not_string, Label::kNear); 1 << Map::kIsUndetectable);
__ cmp(FieldOperand(eax, String::kLengthOffset), Immediate(0)); if (types_.Contains(UNDETECTABLE)) {
__ j(zero, &false_result, Label::kNear); // Undetectable -> false.
__ jmp(&true_result, Label::kNear); Label not_undetectable;
__ j(zero, &not_undetectable, Label::kNear);
__ Set(tos_, Immediate(0));
__ ret(1 * kPointerSize);
__ bind(&not_undetectable);
} else {
// We've seen an undetectable value for the first time -> patch.
__ j(not_zero, &patch, Label::kNear);
}
}
__ bind(&not_string); if (types_.Contains(SPEC_OBJECT)) {
// HeapNumber -> false iff +0, -0, or NaN. // spec object -> true.
__ cmp(map, factory->heap_number_map()); Label not_js_object;
__ j(not_equal, &true_result, Label::kNear); __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
__ fldz(); __ j(below, &not_js_object, Label::kNear);
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ Set(tos_, Immediate(1));
__ FCmp(); __ ret(1 * kPointerSize);
__ j(zero, &false_result, Label::kNear); __ bind(&not_js_object);
// Fall through to |true_result|. } else if (types_.Contains(INTERNAL_OBJECT)) {
// We've seen a spec object for the first time -> patch.
__ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
__ j(above_equal, &patch, Label::kNear);
}
// Return 1/0 for true/false in tos_. if (types_.Contains(STRING)) {
__ bind(&true_result); // String value -> false iff empty.
__ mov(tos_, 1); Label not_string;
__ ret(1 * kPointerSize); __ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
__ bind(&false_result); __ j(above_equal, &not_string, Label::kNear);
__ mov(tos_, 0); __ mov(tos_, FieldOperand(argument, String::kLengthOffset));
__ ret(1 * kPointerSize); __ ret(1 * kPointerSize); // the string length is OK as the return value
__ bind(&not_string);
} else if (types_.Contains(INTERNAL_OBJECT)) {
// We've seen a string for the first time -> patch
__ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
__ j(below, &patch, Label::kNear);
}
if (types_.Contains(HEAP_NUMBER)) {
// heap number -> false iff +0, -0, or NaN.
Label not_heap_number, false_result;
__ cmp(map, factory->heap_number_map());
__ j(not_equal, &not_heap_number, Label::kNear);
__ fldz();
__ fld_d(FieldOperand(argument, HeapNumber::kValueOffset));
__ FCmp();
__ j(zero, &false_result, Label::kNear);
__ Set(tos_, Immediate(1));
__ ret(1 * kPointerSize);
__ bind(&false_result);
__ Set(tos_, Immediate(0));
__ ret(1 * kPointerSize);
__ bind(&not_heap_number);
} else if (types_.Contains(INTERNAL_OBJECT)) {
// We've seen a heap number for the first time -> patch
__ cmp(map, factory->heap_number_map());
__ j(equal, &patch, Label::kNear);
}
if (types_.Contains(INTERNAL_OBJECT)) {
// internal objects -> true
__ Set(tos_, Immediate(1));
__ ret(1 * kPointerSize);
}
__ bind(&patch);
GenerateTypeTransition(masm);
}
void ToBooleanStub::CheckOddball(MacroAssembler* masm,
Type type,
Handle<Object> value,
bool result,
Label* patch) {
const Register argument = eax;
if (types_.Contains(type)) {
// If we see an expected oddball, return its ToBoolean value tos_.
Label different_value;
__ cmp(argument, value);
__ j(not_equal, &different_value, Label::kNear);
__ Set(tos_, Immediate(result ? 1 : 0));
__ ret(1 * kPointerSize);
__ bind(&different_value);
} else if (types_.Contains(INTERNAL_OBJECT)) {
// If we see an unexpected oddball and handle internal objects, we must
// patch because the code for internal objects doesn't handle it explictly.
__ cmp(argument, value);
__ j(equal, patch);
}
}
void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) {
__ pop(ecx); // Get return address, operand is now on top of stack.
__ push(Immediate(Smi::FromInt(tos_.code())));
__ push(Immediate(Smi::FromInt(types_.ToInt())));
__ push(ecx); // Push return address.
// Patch the caller to an appropriate specialized stub and return the
// operation result to the caller of the stub.
__ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()),
3,
1);
} }

View File

@ -309,6 +309,7 @@ void IC::Clear(Address address) {
case Code::UNARY_OP_IC: case Code::UNARY_OP_IC:
case Code::BINARY_OP_IC: case Code::BINARY_OP_IC:
case Code::COMPARE_IC: case Code::COMPARE_IC:
case Code::TO_BOOLEAN_IC:
// Clearing these is tricky and does not // Clearing these is tricky and does not
// make any performance difference. // make any performance difference.
return; return;
@ -842,14 +843,6 @@ MaybeObject* KeyedCallIC::LoadFunction(State state,
} }
#ifdef DEBUG
#define TRACE_IC_NAMED(msg, name) \
if (FLAG_trace_ic) PrintF(msg, *(name)->ToCString())
#else
#define TRACE_IC_NAMED(msg, name)
#endif
MaybeObject* LoadIC::Load(State state, MaybeObject* LoadIC::Load(State state,
Handle<Object> object, Handle<Object> object,
Handle<String> name) { Handle<String> name) {
@ -2506,6 +2499,31 @@ RUNTIME_FUNCTION(Code*, CompareIC_Miss) {
} }
RUNTIME_FUNCTION(MaybeObject*, ToBoolean_Patch) {
ASSERT(args.length() == 3);
HandleScope scope(isolate);
Handle<Object> object = args.at<Object>(0);
Register tos = Register::from_code(args.smi_at(1));
ToBooleanStub::Types old_types(args.smi_at(2));
ToBooleanStub::Types new_types(old_types);
bool to_boolean_value = new_types.Record(object);
old_types.TraceTransition(new_types);
ToBooleanStub stub(tos, new_types);
Handle<Code> code = stub.GetCode();
ToBooleanIC ic(isolate);
ic.patch(*code);
return Smi::FromInt(to_boolean_value ? 1 : 0);
}
void ToBooleanIC::patch(Code* code) {
set_target(code);
}
static const Address IC_utilities[] = { static const Address IC_utilities[] = {
#define ADDR(name) FUNCTION_ADDR(name), #define ADDR(name) FUNCTION_ADDR(name),
IC_UTIL_LIST(ADDR) IC_UTIL_LIST(ADDR)

View File

@ -59,7 +59,8 @@ namespace internal {
ICU(StoreInterceptorProperty) \ ICU(StoreInterceptorProperty) \
ICU(UnaryOp_Patch) \ ICU(UnaryOp_Patch) \
ICU(BinaryOp_Patch) \ ICU(BinaryOp_Patch) \
ICU(CompareIC_Miss) ICU(CompareIC_Miss) \
ICU(ToBoolean_Patch)
// //
// IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC, // IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC,
// and KeyedStoreIC. // and KeyedStoreIC.
@ -720,6 +721,15 @@ class CompareIC: public IC {
Token::Value op_; Token::Value op_;
}; };
class ToBooleanIC: public IC {
public:
explicit ToBooleanIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
void patch(Code* code);
};
// Helper for BinaryOpIC and CompareIC. // Helper for BinaryOpIC and CompareIC.
void PatchInlinedSmiCode(Address address); void PatchInlinedSmiCode(Address address);

View File

@ -1400,6 +1400,7 @@ void Logger::LogCodeObject(Object* object) {
case Code::UNARY_OP_IC: // fall through case Code::UNARY_OP_IC: // fall through
case Code::BINARY_OP_IC: // fall through case Code::BINARY_OP_IC: // fall through
case Code::COMPARE_IC: // fall through case Code::COMPARE_IC: // fall through
case Code::TO_BOOLEAN_IC: // fall through
case Code::STUB: case Code::STUB:
description = description =
CodeStub::MajorName(CodeStub::GetMajorKey(code_object), true); CodeStub::MajorName(CodeStub::GetMajorKey(code_object), true);

View File

@ -158,6 +158,12 @@ bool Object::IsString() {
} }
bool Object::IsSpecObject() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() >= FIRST_SPEC_OBJECT_TYPE;
}
bool Object::IsSymbol() { bool Object::IsSymbol() {
if (!this->IsHeapObject()) return false; if (!this->IsHeapObject()) return false;
uint32_t type = HeapObject::cast(this)->map()->instance_type(); uint32_t type = HeapObject::cast(this)->map()->instance_type();
@ -2757,7 +2763,8 @@ int Code::major_key() {
ASSERT(kind() == STUB || ASSERT(kind() == STUB ||
kind() == UNARY_OP_IC || kind() == UNARY_OP_IC ||
kind() == BINARY_OP_IC || kind() == BINARY_OP_IC ||
kind() == COMPARE_IC); kind() == COMPARE_IC ||
kind() == TO_BOOLEAN_IC);
return READ_BYTE_FIELD(this, kStubMajorKeyOffset); return READ_BYTE_FIELD(this, kStubMajorKeyOffset);
} }
@ -2766,7 +2773,8 @@ void Code::set_major_key(int major) {
ASSERT(kind() == STUB || ASSERT(kind() == STUB ||
kind() == UNARY_OP_IC || kind() == UNARY_OP_IC ||
kind() == BINARY_OP_IC || kind() == BINARY_OP_IC ||
kind() == COMPARE_IC); kind() == COMPARE_IC ||
kind() == TO_BOOLEAN_IC);
ASSERT(0 <= major && major < 256); ASSERT(0 <= major && major < 256);
WRITE_BYTE_FIELD(this, kStubMajorKeyOffset, major); WRITE_BYTE_FIELD(this, kStubMajorKeyOffset, major);
} }
@ -2908,6 +2916,17 @@ void Code::set_compare_state(byte value) {
} }
byte Code::to_boolean_state() {
ASSERT(is_to_boolean_ic_stub());
return READ_BYTE_FIELD(this, kToBooleanTypeOffset);
}
void Code::set_to_boolean_state(byte value) {
ASSERT(is_to_boolean_ic_stub());
WRITE_BYTE_FIELD(this, kToBooleanTypeOffset, value);
}
bool Code::is_inline_cache_stub() { bool Code::is_inline_cache_stub() {
Kind kind = this->kind(); Kind kind = this->kind();
return kind >= FIRST_IC_KIND && kind <= LAST_IC_KIND; return kind >= FIRST_IC_KIND && kind <= LAST_IC_KIND;

View File

@ -7209,6 +7209,7 @@ const char* Code::Kind2String(Kind kind) {
case UNARY_OP_IC: return "UNARY_OP_IC"; case UNARY_OP_IC: return "UNARY_OP_IC";
case BINARY_OP_IC: return "BINARY_OP_IC"; case BINARY_OP_IC: return "BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC"; case COMPARE_IC: return "COMPARE_IC";
case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
} }
UNREACHABLE(); UNREACHABLE();
return NULL; return NULL;

View File

@ -790,6 +790,8 @@ class Object : public MaybeObject {
STRUCT_LIST(DECLARE_STRUCT_PREDICATE) STRUCT_LIST(DECLARE_STRUCT_PREDICATE)
#undef DECLARE_STRUCT_PREDICATE #undef DECLARE_STRUCT_PREDICATE
INLINE(bool IsSpecObject());
// Oddball testing. // Oddball testing.
INLINE(bool IsUndefined()); INLINE(bool IsUndefined());
INLINE(bool IsNull()); INLINE(bool IsNull());
@ -3480,13 +3482,14 @@ class Code: public HeapObject {
UNARY_OP_IC, UNARY_OP_IC,
BINARY_OP_IC, BINARY_OP_IC,
COMPARE_IC, COMPARE_IC,
TO_BOOLEAN_IC,
// No more than 16 kinds. The value currently encoded in four bits in // No more than 16 kinds. The value currently encoded in four bits in
// Flags. // Flags.
// Pseudo-kinds. // Pseudo-kinds.
REGEXP = BUILTIN, REGEXP = BUILTIN,
FIRST_IC_KIND = LOAD_IC, FIRST_IC_KIND = LOAD_IC,
LAST_IC_KIND = COMPARE_IC LAST_IC_KIND = TO_BOOLEAN_IC
}; };
enum { enum {
@ -3552,13 +3555,10 @@ class Code: public HeapObject {
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; } inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
inline bool is_call_stub() { return kind() == CALL_IC; } inline bool is_call_stub() { return kind() == CALL_IC; }
inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; } inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
inline bool is_unary_op_stub() { inline bool is_unary_op_stub() { return kind() == UNARY_OP_IC; }
return kind() == UNARY_OP_IC; inline bool is_binary_op_stub() { return kind() == BINARY_OP_IC; }
}
inline bool is_binary_op_stub() {
return kind() == BINARY_OP_IC;
}
inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; } inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; }
inline bool is_to_boolean_ic_stub() { return kind() == TO_BOOLEAN_IC; }
// [major_key]: For kind STUB or BINARY_OP_IC, the major key. // [major_key]: For kind STUB or BINARY_OP_IC, the major key.
inline int major_key(); inline int major_key();
@ -3600,21 +3600,24 @@ class Code: public HeapObject {
inline CheckType check_type(); inline CheckType check_type();
inline void set_check_type(CheckType value); inline void set_check_type(CheckType value);
// [type-recording unary op type]: For all UNARY_OP_IC. // [type-recording unary op type]: For kind UNARY_OP_IC.
inline byte unary_op_type(); inline byte unary_op_type();
inline void set_unary_op_type(byte value); inline void set_unary_op_type(byte value);
// [type-recording binary op type]: For all TYPE_RECORDING_BINARY_OP_IC. // [type-recording binary op type]: For kind BINARY_OP_IC.
inline byte binary_op_type(); inline byte binary_op_type();
inline void set_binary_op_type(byte value); inline void set_binary_op_type(byte value);
inline byte binary_op_result_type(); inline byte binary_op_result_type();
inline void set_binary_op_result_type(byte value); inline void set_binary_op_result_type(byte value);
// [compare state]: For kind compare IC stubs, tells what state the // [compare state]: For kind COMPARE_IC, tells what state the stub is in.
// stub is in.
inline byte compare_state(); inline byte compare_state();
inline void set_compare_state(byte value); inline void set_compare_state(byte value);
// [to_boolean_foo]: For kind TO_BOOLEAN_IC tells what state the stub is in.
inline byte to_boolean_state();
inline void set_to_boolean_state(byte value);
// Get the safepoint entry for the given pc. // Get the safepoint entry for the given pc.
SafepointEntry GetSafepointEntry(Address pc); SafepointEntry GetSafepointEntry(Address pc);
@ -3756,9 +3759,10 @@ class Code: public HeapObject {
static const int kStackSlotsOffset = kKindSpecificFlagsOffset; static const int kStackSlotsOffset = kKindSpecificFlagsOffset;
static const int kCheckTypeOffset = kKindSpecificFlagsOffset; static const int kCheckTypeOffset = kKindSpecificFlagsOffset;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
static const int kUnaryOpTypeOffset = kStubMajorKeyOffset + 1; static const int kUnaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1; static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
static const int kToBooleanTypeOffset = kStubMajorKeyOffset + 1;
static const int kHasDeoptimizationSupportOffset = kOptimizableOffset + 1; static const int kHasDeoptimizationSupportOffset = kOptimizableOffset + 1;
static const int kBinaryOpReturnTypeOffset = kBinaryOpTypeOffset + 1; static const int kBinaryOpReturnTypeOffset = kBinaryOpTypeOffset + 1;

View File

@ -1542,6 +1542,7 @@ static void ReportCodeKindStatistics() {
CASE(UNARY_OP_IC); CASE(UNARY_OP_IC);
CASE(BINARY_OP_IC); CASE(BINARY_OP_IC);
CASE(COMPARE_IC); CASE(COMPARE_IC);
CASE(TO_BOOLEAN_IC);
} }
} }