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
}
-// 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, ¬_smi, Label::kNear);
+ // argument contains the correct return value already
+ if (!tos_.is(argument)) {
+ __ mov(tos_, argument);
+ }
+ __ ret(1 * kPointerSize);
+ __ bind(¬_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, ¬_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, ¬_undetectable, Label::kNear);
+ __ Set(tos_, Immediate(0));
+ __ ret(1 * kPointerSize);
+ __ bind(¬_undetectable);
+ } else {
+ // We've seen an undetectable value for the first time -> patch.
+ __ j(not_zero, &patch, Label::kNear);
+ }
+ }
- __ bind(¬_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, ¬_js_object, Label::kNear);
+ __ Set(tos_, Immediate(1));
+ __ ret(1 * kPointerSize);
+ __ bind(¬_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, ¬_string, Label::kNear);
+ __ mov(tos_, FieldOperand(argument, String::kLengthOffset));
+ __ ret(1 * kPointerSize); // the string length is OK as the return value
+ __ bind(¬_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, ¬_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(¬_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);
}
STRUCT_LIST(DECLARE_STRUCT_PREDICATE)
#undef DECLARE_STRUCT_PREDICATE
+ INLINE(bool IsSpecObject());
+
// Oddball testing.
INLINE(bool IsUndefined());
INLINE(bool IsNull());
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 {
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();
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);
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;