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
parent 0b55f28e
......@@ -329,4 +329,73 @@ void CallFunctionStub::PrintName(StringStream* stream) {
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
......@@ -900,14 +900,56 @@ class KeyedStoreElementStub : public CodeStub {
class ToBooleanStub: public CodeStub {
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);
virtual int GetCodeKind() { return Code::TO_BOOLEAN_IC; }
virtual void PrintName(StringStream* stream);
private:
Register tos_;
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
......
......@@ -169,7 +169,8 @@ void BreakLocationIterator::Next() {
if ((code->is_inline_cache_stub() &&
!code->is_binary_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())) {
break_point_++;
return;
......
......@@ -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) {
Label false_result, true_result, not_string;
Label patch;
Factory* factory = masm->isolate()->factory();
const Register argument = eax;
const Register map = edx;
__ mov(eax, Operand(esp, 1 * kPointerSize));
if (!types_.IsEmpty()) {
__ mov(argument, Operand(esp, 1 * kPointerSize));
}
// undefined -> false
__ cmp(eax, factory->undefined_value());
__ j(equal, &false_result);
CheckOddball(masm, UNDEFINED, factory->undefined_value(), false, &patch);
// Boolean -> its value
__ cmp(eax, factory->false_value());
__ j(equal, &false_result);
__ 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);
CheckOddball(masm, BOOLEAN, factory->false_value(), false, &patch);
CheckOddball(masm, BOOLEAN, factory->true_value(), true, &patch);
// 'null' -> false.
__ cmp(eax, factory->null_value());
__ j(equal, &false_result, Label::kNear);
CheckOddball(masm, NULL_TYPE, factory->null_value(), false, &patch);
// Get the map of the heap object.
__ mov(map, FieldOperand(eax, HeapObject::kMapOffset));
bool need_map =
types_.Contains(UNDETECTABLE) |
types_.Contains(SPEC_OBJECT) |
types_.Contains(STRING) |
types_.Contains(HEAP_NUMBER) |
types_.Contains(INTERNAL_OBJECT);
// Undetectable -> false.
__ test_b(FieldOperand(map, Map::kBitFieldOffset),
1 << Map::kIsUndetectable);
__ j(not_zero, &false_result, Label::kNear);
if (types_.Contains(SMI)) {
// Smis: 0 -> false, all other -> true
Label not_smi;
__ 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.
__ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
__ j(above_equal, &true_result, Label::kNear);
if (need_map) {
__ mov(map, FieldOperand(argument, HeapObject::kMapOffset));
// String value -> false iff empty.
__ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
__ j(above_equal, &not_string, Label::kNear);
__ cmp(FieldOperand(eax, String::kLengthOffset), Immediate(0));
__ j(zero, &false_result, Label::kNear);
__ jmp(&true_result, Label::kNear);
// Everything with a map could be undetectable, so check this now.
__ test_b(FieldOperand(map, Map::kBitFieldOffset),
1 << Map::kIsUndetectable);
if (types_.Contains(UNDETECTABLE)) {
// Undetectable -> false.
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);
// HeapNumber -> false iff +0, -0, or NaN.
__ cmp(map, factory->heap_number_map());
__ j(not_equal, &true_result, Label::kNear);
__ fldz();
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ FCmp();
__ j(zero, &false_result, Label::kNear);
// Fall through to |true_result|.
// Return 1/0 for true/false in tos_.
__ bind(&true_result);
__ mov(tos_, 1);
__ ret(1 * kPointerSize);
__ bind(&false_result);
__ mov(tos_, 0);
__ ret(1 * kPointerSize);
if (types_.Contains(SPEC_OBJECT)) {
// spec object -> true.
Label not_js_object;
__ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
__ j(below, &not_js_object, Label::kNear);
__ Set(tos_, Immediate(1));
__ ret(1 * kPointerSize);
__ bind(&not_js_object);
} 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);
}
if (types_.Contains(STRING)) {
// String value -> false iff empty.
Label not_string;
__ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
__ j(above_equal, &not_string, Label::kNear);
__ mov(tos_, FieldOperand(argument, String::kLengthOffset));
__ 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);
}
......
......@@ -309,6 +309,7 @@ void IC::Clear(Address address) {
case Code::UNARY_OP_IC:
case Code::BINARY_OP_IC:
case Code::COMPARE_IC:
case Code::TO_BOOLEAN_IC:
// Clearing these is tricky and does not
// make any performance difference.
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,
Handle<Object> object,
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[] = {
#define ADDR(name) FUNCTION_ADDR(name),
IC_UTIL_LIST(ADDR)
......
......@@ -59,7 +59,8 @@ namespace internal {
ICU(StoreInterceptorProperty) \
ICU(UnaryOp_Patch) \
ICU(BinaryOp_Patch) \
ICU(CompareIC_Miss)
ICU(CompareIC_Miss) \
ICU(ToBoolean_Patch)
//
// IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC,
// and KeyedStoreIC.
......@@ -720,6 +721,15 @@ class CompareIC: public IC {
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.
void PatchInlinedSmiCode(Address address);
......
......@@ -1400,6 +1400,7 @@ void Logger::LogCodeObject(Object* object) {
case Code::UNARY_OP_IC: // fall through
case Code::BINARY_OP_IC: // fall through
case Code::COMPARE_IC: // fall through
case Code::TO_BOOLEAN_IC: // fall through
case Code::STUB:
description =
CodeStub::MajorName(CodeStub::GetMajorKey(code_object), true);
......
......@@ -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() {
if (!this->IsHeapObject()) return false;
uint32_t type = HeapObject::cast(this)->map()->instance_type();
......@@ -2757,7 +2763,8 @@ int Code::major_key() {
ASSERT(kind() == STUB ||
kind() == UNARY_OP_IC ||
kind() == BINARY_OP_IC ||
kind() == COMPARE_IC);
kind() == COMPARE_IC ||
kind() == TO_BOOLEAN_IC);
return READ_BYTE_FIELD(this, kStubMajorKeyOffset);
}
......@@ -2766,7 +2773,8 @@ void Code::set_major_key(int major) {
ASSERT(kind() == STUB ||
kind() == UNARY_OP_IC ||
kind() == BINARY_OP_IC ||
kind() == COMPARE_IC);
kind() == COMPARE_IC ||
kind() == TO_BOOLEAN_IC);
ASSERT(0 <= major && major < 256);
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() {
Kind kind = this->kind();
return kind >= FIRST_IC_KIND && kind <= LAST_IC_KIND;
......
......@@ -7209,6 +7209,7 @@ const char* Code::Kind2String(Kind kind) {
case UNARY_OP_IC: return "UNARY_OP_IC";
case BINARY_OP_IC: return "BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC";
case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
}
UNREACHABLE();
return NULL;
......
......@@ -790,6 +790,8 @@ class Object : public MaybeObject {
STRUCT_LIST(DECLARE_STRUCT_PREDICATE)
#undef DECLARE_STRUCT_PREDICATE
INLINE(bool IsSpecObject());
// Oddball testing.
INLINE(bool IsUndefined());
INLINE(bool IsNull());
......@@ -3480,13 +3482,14 @@ class Code: public HeapObject {
UNARY_OP_IC,
BINARY_OP_IC,
COMPARE_IC,
TO_BOOLEAN_IC,
// No more than 16 kinds. The value currently encoded in four bits in
// Flags.
// Pseudo-kinds.
REGEXP = BUILTIN,
FIRST_IC_KIND = LOAD_IC,
LAST_IC_KIND = COMPARE_IC
LAST_IC_KIND = TO_BOOLEAN_IC
};
enum {
......@@ -3552,13 +3555,10 @@ class Code: public HeapObject {
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
inline bool is_call_stub() { return kind() == CALL_IC; }
inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
inline bool is_unary_op_stub() {
return kind() == UNARY_OP_IC;
}
inline bool is_binary_op_stub() {
return kind() == BINARY_OP_IC;
}
inline bool is_unary_op_stub() { return kind() == UNARY_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_to_boolean_ic_stub() { return kind() == TO_BOOLEAN_IC; }
// [major_key]: For kind STUB or BINARY_OP_IC, the major key.
inline int major_key();
......@@ -3600,21 +3600,24 @@ class Code: public HeapObject {
inline CheckType check_type();
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 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 void set_binary_op_type(byte value);
inline byte binary_op_result_type();
inline void set_binary_op_result_type(byte value);
// [compare state]: For kind compare IC stubs, tells what state the
// stub is in.
// [compare state]: For kind COMPARE_IC, tells what state the stub is in.
inline byte compare_state();
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.
SafepointEntry GetSafepointEntry(Address pc);
......@@ -3756,9 +3759,10 @@ class Code: public HeapObject {
static const int kStackSlotsOffset = kKindSpecificFlagsOffset;
static const int kCheckTypeOffset = kKindSpecificFlagsOffset;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
static const int kUnaryOpTypeOffset = 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 kBinaryOpReturnTypeOffset = kBinaryOpTypeOffset + 1;
......
......@@ -1542,6 +1542,7 @@ static void ReportCodeKindStatistics() {
CASE(UNARY_OP_IC);
CASE(BINARY_OP_IC);
CASE(COMPARE_IC);
CASE(TO_BOOLEAN_IC);
}
}
......
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