Type recording for unary ops

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7707 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1c950e04
......@@ -55,6 +55,17 @@ static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
Register rhs);
// Check if the operand is a heap number.
static void EmitCheckForHeapNumber(MacroAssembler* masm, Register operand,
Register scratch1, Register scratch2,
Label* not_a_heap_number) {
__ ldr(scratch1, FieldMemOperand(operand, HeapObject::kMapOffset));
__ LoadRoot(scratch2, Heap::kHeapNumberMapRootIndex);
__ cmp(scratch1, scratch2);
__ b(ne, not_a_heap_number);
}
void ToNumberStub::Generate(MacroAssembler* masm) {
// The ToNumber stub takes one argument in eax.
Label check_heap_number, call_builtin;
......@@ -63,10 +74,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
__ Ret();
__ bind(&check_heap_number);
__ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
__ cmp(r1, ip);
__ b(ne, &call_builtin);
EmitCheckForHeapNumber(masm, r0, r1, ip, &call_builtin);
__ Ret();
__ bind(&call_builtin);
......@@ -1674,6 +1682,302 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
}
Handle<Code> GetTypeRecordingUnaryOpStub(int key,
TRUnaryOpIC::TypeInfo type_info) {
TypeRecordingUnaryOpStub stub(key, type_info);
return stub.GetCode();
}
const char* TypeRecordingUnaryOpStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
kMaxNameLength);
if (name_ == NULL) return "OOM";
const char* op_name = Token::Name(op_);
const char* overwrite_name;
switch (mode_) {
case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
"TypeRecordingUnaryOpStub_%s_%s_%s",
op_name,
overwrite_name,
TRUnaryOpIC::GetName(operand_type_));
return name_;
}
// TODO(svenpanne): Use virtual functions instead of switch.
void TypeRecordingUnaryOpStub::Generate(MacroAssembler* masm) {
switch (operand_type_) {
case TRUnaryOpIC::UNINITIALIZED:
GenerateTypeTransition(masm);
break;
case TRUnaryOpIC::SMI:
GenerateSmiStub(masm);
break;
case TRUnaryOpIC::HEAP_NUMBER:
GenerateHeapNumberStub(masm);
break;
case TRUnaryOpIC::GENERIC:
GenerateGenericStub(masm);
break;
}
}
void TypeRecordingUnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
// Prepare to push argument.
__ mov(r3, Operand(r0));
// Push this stub's key. Although the operation and the type info are
// encoded into the key, the encoding is opaque, so push them too.
__ mov(r2, Operand(Smi::FromInt(MinorKey())));
__ mov(r1, Operand(Smi::FromInt(op_)));
__ mov(r0, Operand(Smi::FromInt(operand_type_)));
__ Push(r3, r2, r1, r0);
__ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kTypeRecordingUnaryOp_Patch),
masm->isolate()),
4,
1);
}
// TODO(svenpanne): Use virtual functions instead of switch.
void TypeRecordingUnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
switch (op_) {
case Token::SUB:
GenerateSmiStubSub(masm);
break;
case Token::BIT_NOT:
GenerateSmiStubBitNot(masm);
break;
default:
UNREACHABLE();
}
}
void TypeRecordingUnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
Label non_smi, slow;
GenerateSmiCodeSub(masm, &non_smi, &slow);
__ bind(&non_smi);
__ bind(&slow);
GenerateTypeTransition(masm);
}
void TypeRecordingUnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
Label non_smi;
GenerateSmiCodeBitNot(masm, &non_smi);
__ bind(&non_smi);
GenerateTypeTransition(masm);
}
void TypeRecordingUnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
Label* non_smi,
Label* slow) {
__ JumpIfNotSmi(r0, non_smi);
// The result of negating zero or the smallest negative smi is not a smi.
__ bic(ip, r0, Operand(0x80000000), SetCC);
__ b(eq, slow);
// Return '0 - value'.
__ rsb(r0, r0, Operand(0, RelocInfo::NONE));
__ Ret();
}
void TypeRecordingUnaryOpStub::GenerateSmiCodeBitNot(MacroAssembler* masm,
Label* non_smi) {
__ JumpIfNotSmi(r0, non_smi);
// Flip bits and revert inverted smi-tag.
__ mvn(r0, Operand(r0));
__ bic(r0, r0, Operand(kSmiTagMask));
__ Ret();
}
// TODO(svenpanne): Use virtual functions instead of switch.
void TypeRecordingUnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
switch (op_) {
case Token::SUB:
GenerateHeapNumberStubSub(masm);
break;
case Token::BIT_NOT:
GenerateHeapNumberStubBitNot(masm);
break;
default:
UNREACHABLE();
}
}
void TypeRecordingUnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
Label non_smi, slow;
GenerateSmiCodeSub(masm, &non_smi, &slow);
__ bind(&non_smi);
GenerateHeapNumberCodeSub(masm, &slow);
__ bind(&slow);
GenerateTypeTransition(masm);
}
void TypeRecordingUnaryOpStub::GenerateHeapNumberStubBitNot(
MacroAssembler* masm) {
Label non_smi, slow;
GenerateSmiCodeBitNot(masm, &non_smi);
__ bind(&non_smi);
GenerateHeapNumberCodeBitNot(masm, &slow);
__ bind(&slow);
GenerateTypeTransition(masm);
}
void TypeRecordingUnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
Label* slow) {
EmitCheckForHeapNumber(masm, r0, r1, r6, slow);
// r0 is a heap number. Get a new heap number in r1.
if (mode_ == UNARY_OVERWRITE) {
__ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
__ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
__ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
} else {
Label slow_allocate_heapnumber, heapnumber_allocated;
__ AllocateHeapNumber(r1, r2, r3, r6, &slow_allocate_heapnumber);
__ jmp(&heapnumber_allocated);
__ bind(&slow_allocate_heapnumber);
__ EnterInternalFrame();
__ push(r0);
__ CallRuntime(Runtime::kNumberAlloc, 0);
__ mov(r1, Operand(r0));
__ pop(r0);
__ LeaveInternalFrame();
__ bind(&heapnumber_allocated);
__ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
__ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
__ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
__ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
__ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
__ mov(r0, Operand(r1));
}
__ Ret();
}
void TypeRecordingUnaryOpStub::GenerateHeapNumberCodeBitNot(
MacroAssembler* masm, Label* slow) {
EmitCheckForHeapNumber(masm, r0, r1, r6, slow);
// Convert the heap number is r0 to an untagged integer in r1.
__ ConvertToInt32(r0, r1, r2, r3, d0, slow);
// Do the bitwise operation and check if the result fits in a smi.
Label try_float;
__ mvn(r1, Operand(r1));
__ add(r2, r1, Operand(0x40000000), SetCC);
__ b(mi, &try_float);
// Tag the result as a smi and we're done.
__ mov(r0, Operand(r1, LSL, kSmiTagSize));
__ Ret();
// Try to store the result in a heap number.
__ bind(&try_float);
if (mode_ == UNARY_NO_OVERWRITE) {
Label slow_allocate_heapnumber, heapnumber_allocated;
__ AllocateHeapNumber(r0, r2, r3, r6, &slow_allocate_heapnumber);
__ jmp(&heapnumber_allocated);
__ bind(&slow_allocate_heapnumber);
__ EnterInternalFrame();
__ push(r1);
__ CallRuntime(Runtime::kNumberAlloc, 0);
__ pop(r1);
__ LeaveInternalFrame();
__ bind(&heapnumber_allocated);
}
if (CpuFeatures::IsSupported(VFP3)) {
// Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
CpuFeatures::Scope scope(VFP3);
__ vmov(s0, r1);
__ vcvt_f64_s32(d0, s0);
__ sub(r2, r0, Operand(kHeapObjectTag));
__ vstr(d0, r2, HeapNumber::kValueOffset);
__ Ret();
} else {
// WriteInt32ToHeapNumberStub does not trigger GC, so we do not
// have to set up a frame.
WriteInt32ToHeapNumberStub stub(r1, r0, r2);
__ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
}
}
// TODO(svenpanne): Use virtual functions instead of switch.
void TypeRecordingUnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
switch (op_) {
case Token::SUB:
GenerateGenericStubSub(masm);
break;
case Token::BIT_NOT:
GenerateGenericStubBitNot(masm);
break;
default:
UNREACHABLE();
}
}
void TypeRecordingUnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
Label non_smi, slow;
GenerateSmiCodeSub(masm, &non_smi, &slow);
__ bind(&non_smi);
GenerateHeapNumberCodeSub(masm, &slow);
__ bind(&slow);
GenerateGenericCodeFallback(masm);
}
void TypeRecordingUnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
Label non_smi, slow;
GenerateSmiCodeBitNot(masm, &non_smi);
__ bind(&non_smi);
GenerateHeapNumberCodeBitNot(masm, &slow);
__ bind(&slow);
GenerateGenericCodeFallback(masm);
}
void TypeRecordingUnaryOpStub::GenerateGenericCodeFallback(
MacroAssembler* masm) {
// Handle the slow case by jumping to the JavaScript builtin.
__ push(r0);
switch (op_) {
case Token::SUB:
__ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
break;
case Token::BIT_NOT:
__ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
break;
default:
UNREACHABLE();
}
}
Handle<Code> GetTypeRecordingBinaryOpStub(int key,
TRBinaryOpIC::TypeInfo type_info,
TRBinaryOpIC::TypeInfo result_type_info) {
......
......@@ -71,6 +71,92 @@ class ToBooleanStub: public CodeStub {
};
class TypeRecordingUnaryOpStub: public CodeStub {
public:
TypeRecordingUnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
operand_type_(TRUnaryOpIC::UNINITIALIZED),
name_(NULL) {
}
TypeRecordingUnaryOpStub(
int key,
TRUnaryOpIC::TypeInfo operand_type)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
operand_type_(operand_type),
name_(NULL) {
}
private:
Token::Value op_;
UnaryOverwriteMode mode_;
// Operand type information determined at runtime.
TRUnaryOpIC::TypeInfo operand_type_;
char* name_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("TypeRecordingUnaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
TRUnaryOpIC::GetName(operand_type_));
}
#endif
class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
class OpBits: public BitField<Token::Value, 1, 7> {};
class OperandTypeInfoBits: public BitField<TRUnaryOpIC::TypeInfo, 8, 3> {};
Major MajorKey() { return TypeRecordingUnaryOp; }
int MinorKey() {
return ModeBits::encode(mode_)
| OpBits::encode(op_)
| OperandTypeInfoBits::encode(operand_type_);
}
// Note: A lot of the helper functions below will vanish when we use virtual
// function instead of switch more often.
void Generate(MacroAssembler* masm);
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateSmiStub(MacroAssembler* masm);
void GenerateSmiStubSub(MacroAssembler* masm);
void GenerateSmiStubBitNot(MacroAssembler* masm);
void GenerateSmiCodeSub(MacroAssembler* masm, Label* non_smi, Label* slow);
void GenerateSmiCodeBitNot(MacroAssembler* masm, Label* slow);
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateHeapNumberStubSub(MacroAssembler* masm);
void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateGenericStubSub(MacroAssembler* masm);
void GenerateGenericStubBitNot(MacroAssembler* masm);
void GenerateGenericCodeFallback(MacroAssembler* masm);
virtual int GetCodeKind() { return Code::TYPE_RECORDING_UNARY_OP_IC; }
virtual InlineCacheState GetICState() {
return TRUnaryOpIC::ToState(operand_type_);
}
virtual void FinishCode(Code* code) {
code->set_type_recording_unary_op_type(operand_type_);
}
};
class TypeRecordingBinaryOpStub: public CodeStub {
public:
TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
......
......@@ -3789,48 +3789,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
break;
}
case Token::SUB: {
Comment cmt(masm_, "[ UnaryOperation (SUB)");
bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode overwrite =
can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register r0.
VisitForAccumulatorValue(expr->expression());
__ CallStub(&stub);
context()->Plug(r0);
case Token::SUB:
EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
break;
}
case Token::BIT_NOT: {
Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
// The generic unary operation stub expects the argument to be
// in the accumulator register r0.
VisitForAccumulatorValue(expr->expression());
Label done;
bool inline_smi_code = ShouldInlineSmiCase(expr->op());
if (inline_smi_code) {
Label call_stub;
__ JumpIfNotSmi(r0, &call_stub);
__ mvn(r0, Operand(r0));
// Bit-clear inverted smi-tag.
__ bic(r0, r0, Operand(kSmiTagMask));
__ b(&done);
__ bind(&call_stub);
}
bool overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOpFlags flags = inline_smi_code
? NO_UNARY_SMI_CODE_IN_STUB
: NO_UNARY_FLAGS;
UnaryOverwriteMode mode =
overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
__ CallStub(&stub);
__ bind(&done);
context()->Plug(r0);
case Token::BIT_NOT:
EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
break;
}
default:
UNREACHABLE();
......@@ -3838,6 +3803,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
const char* comment) {
// TODO(svenpanne): Allowing format strings in Comment would be nice here...
Comment cmt(masm_, comment);
bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode overwrite =
can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
TypeRecordingUnaryOpStub stub(expr->op(), overwrite);
// TypeRecordingGenericUnaryOpStub expects the argument to be in the
// accumulator register r0.
VisitForAccumulatorValue(expr->expression());
SetSourcePosition(expr->position());
EmitCallIC(stub.GetCode(), NULL, expr->id());
context()->Plug(r0);
}
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
SetSourcePosition(expr->position());
......
......@@ -1392,8 +1392,8 @@ class CallRuntime: public Expression {
class UnaryOperation: public Expression {
public:
UnaryOperation(Token::Value op, Expression* expression)
: op_(op), expression_(expression) {
UnaryOperation(Token::Value op, Expression* expression, int pos)
: op_(op), expression_(expression), pos_(pos) {
ASSERT(Token::IsUnaryOp(op));
}
......@@ -1405,10 +1405,12 @@ class UnaryOperation: public Expression {
Token::Value op() const { return op_; }
Expression* expression() const { return expression_; }
virtual int position() const { return pos_; }
private:
Token::Value op_;
Expression* expression_;
int pos_;
};
......
......@@ -37,6 +37,7 @@ namespace internal {
// as only the stubs up to and including Instanceof allows nested stub calls.
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
V(TypeRecordingUnaryOp) \
V(TypeRecordingBinaryOp) \
V(StringAdd) \
V(SubString) \
......
......@@ -536,6 +536,9 @@ class FullCodeGenerator: public AstVisitor {
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
void EmitUnaryOperation(UnaryOperation* expr, const char* comment);
// Handles the shortcutted logical binary operations in VisitBinaryOperation.
void EmitLogicalOperation(BinaryOperation* expr);
......
This diff is collapsed.
......@@ -72,6 +72,94 @@ class ToBooleanStub: public CodeStub {
};
class TypeRecordingUnaryOpStub: public CodeStub {
public:
TypeRecordingUnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
operand_type_(TRUnaryOpIC::UNINITIALIZED),
name_(NULL) {
}
TypeRecordingUnaryOpStub(
int key,
TRUnaryOpIC::TypeInfo operand_type)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
operand_type_(operand_type),
name_(NULL) {
}
private:
Token::Value op_;
UnaryOverwriteMode mode_;
// Operand type information determined at runtime.
TRUnaryOpIC::TypeInfo operand_type_;
char* name_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("TypeRecordingUnaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
TRUnaryOpIC::GetName(operand_type_));
}
#endif
class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
class OpBits: public BitField<Token::Value, 1, 7> {};
class OperandTypeInfoBits: public BitField<TRUnaryOpIC::TypeInfo, 8, 3> {};
Major MajorKey() { return TypeRecordingUnaryOp; }
int MinorKey() {
return ModeBits::encode(mode_)
| OpBits::encode(op_)
| OperandTypeInfoBits::encode(operand_type_);
}
// Note: A lot of the helper functions below will vanish when we use virtual
// function instead of switch more often.
void Generate(MacroAssembler* masm);
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateSmiStub(MacroAssembler* masm);
void GenerateSmiStubSub(MacroAssembler* masm);
void GenerateSmiStubBitNot(MacroAssembler* masm);
void GenerateSmiCodeSub(MacroAssembler* masm, NearLabel* non_smi, Label* undo,
Label* slow);
void GenerateSmiCodeBitNot(MacroAssembler* masm, NearLabel* non_smi);
void GenerateSmiCodeUndo(MacroAssembler* masm);
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateHeapNumberStubSub(MacroAssembler* masm);
void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateGenericStubSub(MacroAssembler* masm);
void GenerateGenericStubBitNot(MacroAssembler* masm);
void GenerateGenericCodeFallback(MacroAssembler* masm);
virtual int GetCodeKind() { return Code::TYPE_RECORDING_UNARY_OP_IC; }
virtual InlineCacheState GetICState() {
return TRUnaryOpIC::ToState(operand_type_);
}
virtual void FinishCode(Code* code) {
code->set_type_recording_unary_op_type(operand_type_);
}
};
class TypeRecordingBinaryOpStub: public CodeStub {
public:
TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
......
......@@ -3734,48 +3734,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
break;
}
case Token::SUB: {
Comment cmt(masm_, "[ UnaryOperation (SUB)");
bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode overwrite =
can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register eax.
VisitForAccumulatorValue(expr->expression());
__ CallStub(&stub);
context()->Plug(eax);
case Token::SUB:
EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
break;
}
case Token::BIT_NOT: {
Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
// The generic unary operation stub expects the argument to be
// in the accumulator register eax.
VisitForAccumulatorValue(expr->expression());
Label done;
bool inline_smi_case = ShouldInlineSmiCase(expr->op());
if (inline_smi_case) {
NearLabel call_stub;
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &call_stub);
__ lea(eax, Operand(eax, kSmiTagMask));
__ not_(eax);
__ jmp(&done);
__ bind(&call_stub);
}
bool overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode mode =
overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
UnaryOpFlags flags = inline_smi_case
? NO_UNARY_SMI_CODE_IN_STUB
: NO_UNARY_FLAGS;
GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
__ CallStub(&stub);
__ bind(&done);
context()->Plug(eax);
case Token::BIT_NOT:
EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
break;
}
default:
UNREACHABLE();
......@@ -3783,6 +3748,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
const char* comment) {
// TODO(svenpanne): Allowing format strings in Comment would be nice here...
Comment cmt(masm_, comment);
bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode overwrite =
can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
TypeRecordingUnaryOpStub stub(expr->op(), overwrite);
// TypeRecordingUnaryOpStub expects the argument to be in the
// accumulator register eax.
VisitForAccumulatorValue(expr->expression());
SetSourcePosition(expr->position());
EmitCallIC(stub.GetCode(), NULL, expr->id());
context()->Plug(eax);
}
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
SetSourcePosition(expr->position());
......
......@@ -282,6 +282,7 @@ void IC::Clear(Address address) {
return KeyedStoreIC::Clear(address, target);
case Code::CALL_IC: return CallIC::Clear(address, target);
case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
case Code::TYPE_RECORDING_UNARY_OP_IC:
case Code::TYPE_RECORDING_BINARY_OP_IC:
case Code::COMPARE_IC:
// Clearing these is tricky and does not
......@@ -1807,6 +1808,55 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Miss) {
}
void TRUnaryOpIC::patch(Code* code) {
set_target(code);
}
const char* TRUnaryOpIC::GetName(TypeInfo type_info) {
switch (type_info) {
case UNINITIALIZED: return "Uninitialized";
case SMI: return "Smi";
case HEAP_NUMBER: return "HeapNumbers";
case GENERIC: return "Generic";
default: return "Invalid";
}
}
TRUnaryOpIC::State TRUnaryOpIC::ToState(TypeInfo type_info) {
switch (type_info) {
case UNINITIALIZED:
return ::v8::internal::UNINITIALIZED;
case SMI:
case HEAP_NUMBER:
return MONOMORPHIC;
case GENERIC:
return MEGAMORPHIC;
}
UNREACHABLE();
return ::v8::internal::UNINITIALIZED;
}
TRUnaryOpIC::TypeInfo TRUnaryOpIC::GetTypeInfo(Handle<Object> operand) {
::v8::internal::TypeInfo operand_type =
::v8::internal::TypeInfo::TypeFromValue(operand);
if (operand_type.IsSmi()) {
return SMI;
} else if (operand_type.IsNumber()) {
return HEAP_NUMBER;
} else {
return GENERIC;
}
}
TRUnaryOpIC::TypeInfo TRUnaryOpIC::JoinTypes(TRUnaryOpIC::TypeInfo x,
TRUnaryOpIC::TypeInfo y) {
return x >= y ? x : y;
}
void TRBinaryOpIC::patch(Code* code) {
set_target(code);
}
......@@ -1898,6 +1948,62 @@ TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
}
// defined in code-stubs-<arch>.cc
// Only needed to remove dependency of ic.cc on code-stubs-<arch>.h.
Handle<Code> GetTypeRecordingUnaryOpStub(int key,
TRUnaryOpIC::TypeInfo type_info);
RUNTIME_FUNCTION(MaybeObject*, TypeRecordingUnaryOp_Patch) {
ASSERT(args.length() == 4);
HandleScope scope(isolate);
Handle<Object> operand = args.at<Object>(0);
int key = Smi::cast(args[1])->value();
Token::Value op = static_cast<Token::Value>(Smi::cast(args[2])->value());
TRUnaryOpIC::TypeInfo previous_type =
static_cast<TRUnaryOpIC::TypeInfo>(Smi::cast(args[3])->value());
TRUnaryOpIC::TypeInfo type = TRUnaryOpIC::GetTypeInfo(operand);
type = TRUnaryOpIC::JoinTypes(type, previous_type);
Handle<Code> code = GetTypeRecordingUnaryOpStub(key, type);
if (!code.is_null()) {
if (FLAG_trace_ic) {
PrintF("[TypeRecordingUnaryOpIC (%s->%s)#%s]\n",
TRUnaryOpIC::GetName(previous_type),
TRUnaryOpIC::GetName(type),
Token::Name(op));
}
TRUnaryOpIC ic(isolate);
ic.patch(*code);
}
Handle<JSBuiltinsObject> builtins = Handle<JSBuiltinsObject>(
isolate->thread_local_top()->context_->builtins(), isolate);
Object* builtin = NULL; // Initialization calms down the compiler.
switch (op) {
case Token::SUB:
builtin = builtins->javascript_builtin(Builtins::UNARY_MINUS);
break;
case Token::BIT_NOT:
builtin = builtins->javascript_builtin(Builtins::BIT_NOT);
break;
default:
UNREACHABLE();
}
Handle<JSFunction> builtin_function(JSFunction::cast(builtin), isolate);
bool caught_exception;
Handle<Object> result = Execution::Call(builtin_function, operand, 0, NULL,
&caught_exception);
if (caught_exception) {
return Failure::Exception();
}
return *result;
}
// defined in code-stubs-<arch>.cc
// Only needed to remove dependency of ic.cc on code-stubs-<arch>.h.
Handle<Code> GetTypeRecordingBinaryOpStub(int key,
......
......@@ -53,6 +53,7 @@ namespace internal {
ICU(LoadPropertyWithInterceptorForCall) \
ICU(KeyedLoadPropertyWithInterceptor) \
ICU(StoreInterceptorProperty) \
ICU(TypeRecordingUnaryOp_Patch) \
ICU(TypeRecordingBinaryOp_Patch) \
ICU(CompareIC_Miss)
//
......@@ -529,6 +530,32 @@ class KeyedStoreIC: public IC {
};
class TRUnaryOpIC: public IC {
public:
// sorted: increasingly more unspecific (ignoring UNINITIALIZED)
// TODO(svenpanne) Using enums+switch is an antipattern, use a class instead.
enum TypeInfo {
UNINITIALIZED,
SMI,
HEAP_NUMBER,
GENERIC
};
explicit TRUnaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
void patch(Code* code);
static const char* GetName(TypeInfo type_info);
static State ToState(TypeInfo type_info);
static TypeInfo GetTypeInfo(Handle<Object> operand);
static TypeInfo JoinTypes(TypeInfo x, TypeInfo y);
};
// Type Recording BinaryOpIC, that records the types of the inputs and outputs.
class TRBinaryOpIC: public IC {
public:
......
......@@ -1316,6 +1316,7 @@ void Logger::LogCodeObject(Object* object) {
case Code::FUNCTION:
case Code::OPTIMIZED_FUNCTION:
return; // We log this later using LogCompiledFunctions.
case Code::TYPE_RECORDING_UNARY_OP_IC: // fall through
case Code::TYPE_RECORDING_BINARY_OP_IC: // fall through
case Code::COMPARE_IC: // fall through
case Code::STUB:
......
......@@ -2609,6 +2609,7 @@ int Code::major_key() {
void Code::set_major_key(int major) {
ASSERT(kind() == STUB ||
kind() == TYPE_RECORDING_UNARY_OP_IC ||
kind() == TYPE_RECORDING_BINARY_OP_IC ||
kind() == COMPARE_IC);
ASSERT(0 <= major && major < 256);
......@@ -2717,6 +2718,18 @@ void Code::set_external_array_type(ExternalArrayType value) {
}
byte Code::type_recording_unary_op_type() {
ASSERT(is_type_recording_unary_op_stub());
return READ_BYTE_FIELD(this, kUnaryOpTypeOffset);
}
void Code::set_type_recording_unary_op_type(byte value) {
ASSERT(is_type_recording_unary_op_stub());
WRITE_BYTE_FIELD(this, kUnaryOpTypeOffset, value);
}
byte Code::type_recording_binary_op_type() {
ASSERT(is_type_recording_binary_op_stub());
return READ_BYTE_FIELD(this, kBinaryOpTypeOffset);
......
......@@ -6516,6 +6516,7 @@ const char* Code::Kind2String(Kind kind) {
case KEYED_EXTERNAL_ARRAY_STORE_IC: return "KEYED_EXTERNAL_ARRAY_STORE_IC";
case CALL_IC: return "CALL_IC";
case KEYED_CALL_IC: return "KEYED_CALL_IC";
case TYPE_RECORDING_UNARY_OP_IC: return "TYPE_RECORDING_UNARY_OP_IC";
case TYPE_RECORDING_BINARY_OP_IC: return "TYPE_RECORDING_BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC";
}
......
......@@ -3276,6 +3276,7 @@ class Code: public HeapObject {
STORE_IC,
KEYED_STORE_IC,
KEYED_EXTERNAL_ARRAY_STORE_IC,
TYPE_RECORDING_UNARY_OP_IC,
TYPE_RECORDING_BINARY_OP_IC,
COMPARE_IC,
// No more than 16 kinds. The value currently encoded in four bits in
......@@ -3344,6 +3345,9 @@ 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_type_recording_unary_op_stub() {
return kind() == TYPE_RECORDING_UNARY_OP_IC;
}
inline bool is_type_recording_binary_op_stub() {
return kind() == TYPE_RECORDING_BINARY_OP_IC;
}
......@@ -3401,6 +3405,10 @@ class Code: public HeapObject {
inline ExternalArrayType external_array_type();
inline void set_external_array_type(ExternalArrayType value);
// [type-recording unary op type]: For all TYPE_RECORDING_UNARY_OP_IC.
inline byte type_recording_unary_op_type();
inline void set_type_recording_unary_op_type(byte value);
// [type-recording binary op type]: For all TYPE_RECORDING_BINARY_OP_IC.
inline byte type_recording_binary_op_type();
inline void set_type_recording_binary_op_type(byte value);
......@@ -3552,6 +3560,7 @@ class Code: public HeapObject {
static const int kExternalArrayTypeOffset = kKindSpecificFlagsOffset;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
static const int kUnaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kHasDeoptimizationSupportOffset = kOptimizableOffset + 1;
......
......@@ -2484,7 +2484,7 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) {
x = NewCompareNode(cmp, x, y, position);
if (cmp != op) {
// The comparison was negated - add a NOT.
x = new(zone()) UnaryOperation(Token::NOT, x);
x = new(zone()) UnaryOperation(Token::NOT, x, position);
}
} else {
......@@ -2534,6 +2534,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
Token::Value op = peek();
if (Token::IsUnaryOp(op)) {
op = Next();
int position = scanner().location().beg_pos;
Expression* expression = ParseUnaryExpression(CHECK_OK);
// Compute some expressions involving only number literals.
......@@ -2561,7 +2562,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
}
}
return new(zone()) UnaryOperation(op, expression);
return new(zone()) UnaryOperation(op, expression, position);
} else if (Token::IsCountOp(op)) {
op = Next();
......
......@@ -1570,6 +1570,7 @@ static void ReportCodeKindStatistics() {
CASE(KEYED_EXTERNAL_ARRAY_STORE_IC);
CASE(CALL_IC);
CASE(KEYED_CALL_IC);
CASE(TYPE_RECORDING_UNARY_OP_IC);
CASE(TYPE_RECORDING_BINARY_OP_IC);
CASE(COMPARE_IC);
}
......
......@@ -239,6 +239,25 @@ TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
}
TypeInfo TypeFeedbackOracle::UnaryType(UnaryOperation* expr) {
Handle<Object> object = GetInfo(expr->position());
TypeInfo unknown = TypeInfo::Unknown();
if (!object->IsCode()) return unknown;
Handle<Code> code = Handle<Code>::cast(object);
ASSERT(code->is_type_recording_unary_op_stub());
TRUnaryOpIC::TypeInfo type = static_cast<TRUnaryOpIC::TypeInfo>(
code->type_recording_unary_op_type());
switch (type) {
case TRUnaryOpIC::SMI:
return TypeInfo::Smi();
case TRUnaryOpIC::HEAP_NUMBER:
return TypeInfo::Double();
default:
return unknown;
}
}
TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
Handle<Object> object = GetInfo(expr->id());
TypeInfo unknown = TypeInfo::Unknown();
......
......@@ -227,6 +227,7 @@ enum StringStubFeedback {
// Forward declarations.
class Assignment;
class UnaryOperation;
class BinaryOperation;
class Call;
class CompareOperation;
......@@ -258,6 +259,7 @@ class TypeFeedbackOracle BASE_EMBEDDED {
bool LoadIsBuiltin(Property* expr, Builtins::Name id);
// Get type information for arithmetic operations and compares.
TypeInfo UnaryType(UnaryOperation* expr);
TypeInfo BinaryType(BinaryOperation* expr);
TypeInfo CompareType(CompareOperation* expr);
TypeInfo SwitchType(CaseClause* clause);
......
This diff is collapsed.
......@@ -71,6 +71,93 @@ class ToBooleanStub: public CodeStub {
};
class TypeRecordingUnaryOpStub: public CodeStub {
public:
TypeRecordingUnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
operand_type_(TRUnaryOpIC::UNINITIALIZED),
name_(NULL) {
}
TypeRecordingUnaryOpStub(
int key,
TRUnaryOpIC::TypeInfo operand_type)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
operand_type_(operand_type),
name_(NULL) {
}
private:
Token::Value op_;
UnaryOverwriteMode mode_;
// Operand type information determined at runtime.
TRUnaryOpIC::TypeInfo operand_type_;
char* name_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("TypeRecordingUnaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
TRUnaryOpIC::GetName(operand_type_));
}
#endif
class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
class OpBits: public BitField<Token::Value, 1, 7> {};
class OperandTypeInfoBits: public BitField<TRUnaryOpIC::TypeInfo, 8, 3> {};
Major MajorKey() { return TypeRecordingUnaryOp; }
int MinorKey() {
return ModeBits::encode(mode_)
| OpBits::encode(op_)
| OperandTypeInfoBits::encode(operand_type_);
}
// Note: A lot of the helper functions below will vanish when we use virtual
// function instead of switch more often.
void Generate(MacroAssembler* masm);
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateSmiStub(MacroAssembler* masm);
void GenerateSmiStubSub(MacroAssembler* masm);
void GenerateSmiStubBitNot(MacroAssembler* masm);
void GenerateSmiCodeSub(MacroAssembler* masm, NearLabel* non_smi,
Label* slow);
void GenerateSmiCodeBitNot(MacroAssembler* masm, NearLabel* non_smi);
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateHeapNumberStubSub(MacroAssembler* masm);
void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateGenericStubSub(MacroAssembler* masm);
void GenerateGenericStubBitNot(MacroAssembler* masm);
void GenerateGenericCodeFallback(MacroAssembler* masm);
virtual int GetCodeKind() { return Code::TYPE_RECORDING_UNARY_OP_IC; }
virtual InlineCacheState GetICState() {
return TRUnaryOpIC::ToState(operand_type_);
}
virtual void FinishCode(Code* code) {
code->set_type_recording_unary_op_type(operand_type_);
}
};
class TypeRecordingBinaryOpStub: public CodeStub {
public:
TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
......
......@@ -3714,46 +3714,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
break;
}
case Token::SUB: {
Comment cmt(masm_, "[ UnaryOperation (SUB)");
bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode overwrite =
can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register rax.
VisitForAccumulatorValue(expr->expression());
__ CallStub(&stub);
context()->Plug(rax);
case Token::SUB:
EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
break;
}
case Token::BIT_NOT: {
Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
// The generic unary operation stub expects the argument to be
// in the accumulator register rax.
VisitForAccumulatorValue(expr->expression());
Label done;
bool inline_smi_case = ShouldInlineSmiCase(expr->op());
if (inline_smi_case) {
Label call_stub;
__ JumpIfNotSmi(rax, &call_stub);
__ SmiNot(rax, rax);
__ jmp(&done);
__ bind(&call_stub);
}
bool overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode mode =
overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
UnaryOpFlags flags = inline_smi_case
? NO_UNARY_SMI_CODE_IN_STUB
: NO_UNARY_FLAGS;
GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
__ CallStub(&stub);
__ bind(&done);
context()->Plug(rax);
case Token::BIT_NOT:
EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
break;
}
default:
UNREACHABLE();
......@@ -3761,6 +3728,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
const char* comment) {
// TODO(svenpanne): Allowing format strings in Comment would be nice here...
Comment cmt(masm_, comment);
bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
UnaryOverwriteMode overwrite =
can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
TypeRecordingUnaryOpStub stub(expr->op(), overwrite);
// TypeRecordingUnaryOpStub expects the argument to be in the
// accumulator register rax.
VisitForAccumulatorValue(expr->expression());
SetSourcePosition(expr->position());
EmitCallIC(stub.GetCode(), NULL, expr->id());
context()->Plug(rax);
}
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
SetSourcePosition(expr->position());
......
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