Commit b6009051 authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

ARM: Track Smis on top 4 stack positions and Smi loop variables.

Improve code generation for known smis and suspected Smis.
Review URL: http://codereview.chromium.org/2452002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4783 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 5b7e77ac
......@@ -109,21 +109,28 @@ void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
CodeGenState::CodeGenState(CodeGenerator* owner)
: owner_(owner),
true_target_(NULL),
false_target_(NULL),
previous_(NULL) {
owner_->set_state(this);
previous_(owner->state()) {
owner->set_state(this);
}
CodeGenState::CodeGenState(CodeGenerator* owner,
JumpTarget* true_target,
JumpTarget* false_target)
: owner_(owner),
ConditionCodeGenState::ConditionCodeGenState(CodeGenerator* owner,
JumpTarget* true_target,
JumpTarget* false_target)
: CodeGenState(owner),
true_target_(true_target),
false_target_(false_target),
previous_(owner->state()) {
owner_->set_state(this);
false_target_(false_target) {
owner->set_state(this);
}
TypeInfoCodeGenState::TypeInfoCodeGenState(CodeGenerator* owner,
Slot* slot,
TypeInfo type_info)
: CodeGenState(owner),
slot_(slot) {
owner->set_state(this);
old_type_info_ = owner->set_type_info(slot, type_info);
}
......@@ -133,6 +140,10 @@ CodeGenState::~CodeGenState() {
}
TypeInfoCodeGenState::~TypeInfoCodeGenState() {
owner()->set_type_info(slot_, old_type_info_);
}
// -------------------------------------------------------------------------
// CodeGenerator implementation
......@@ -145,6 +156,7 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm)
cc_reg_(al),
state_(NULL),
loop_nesting_(0),
type_info_(NULL),
function_return_is_shadowed_(false) {
}
......@@ -162,6 +174,11 @@ void CodeGenerator::Generate(CompilationInfo* info) {
// Initialize state.
info_ = info;
int slots = scope()->num_parameters() + scope()->num_stack_slots();
ScopedVector<TypeInfo> type_info_array(slots);
type_info_ = &type_info_array;
ASSERT(allocator_ == NULL);
RegisterAllocator register_allocator(this);
allocator_ = &register_allocator;
......@@ -393,6 +410,21 @@ void CodeGenerator::Generate(CompilationInfo* info) {
}
allocator_ = NULL;
type_info_ = NULL;
}
int CodeGenerator::NumberOfSlot(Slot* slot) {
if (slot == NULL) return kInvalidSlotNumber;
switch (slot->type()) {
case Slot::PARAMETER:
return slot->index();
case Slot::LOCAL:
return slot->index() + scope()->num_parameters();
default:
break;
}
return kInvalidSlotNumber;
}
......@@ -490,7 +522,7 @@ void CodeGenerator::LoadCondition(Expression* x,
ASSERT(!has_cc());
int original_height = frame_->height();
{ CodeGenState new_state(this, true_target, false_target);
{ ConditionCodeGenState new_state(this, true_target, false_target);
Visit(x);
// If we hit a stack overflow, we may not have actually visited
......@@ -791,63 +823,92 @@ void CodeGenerator::ToBoolean(JumpTarget* true_target,
void CodeGenerator::GenericBinaryOperation(Token::Value op,
OverwriteMode overwrite_mode,
GenerateInlineSmi inline_smi,
int constant_rhs) {
VirtualFrame::SpilledScope spilled_scope(frame_);
// sp[0] : y
// sp[1] : x
// result : r0
// top of virtual frame: y
// 2nd elt. on virtual frame : x
// result : top of virtual frame
// Stub is entered with a call: 'return address' is in lr.
switch (op) {
case Token::ADD:
case Token::SUB:
case Token::MUL:
case Token::DIV:
case Token::MOD:
if (inline_smi) {
JumpTarget done;
Register rhs = frame_->PopToRegister();
Register lhs = frame_->PopToRegister(rhs);
Register scratch = VirtualFrame::scratch0();
__ orr(scratch, rhs, Operand(lhs));
// Check they are both small and positive.
__ tst(scratch, Operand(kSmiTagMask | 0xc0000000));
ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
ASSERT_EQ(0, kSmiTag);
if (op == Token::ADD) {
__ add(r0, lhs, Operand(rhs), LeaveCC, eq);
} else {
__ sub(r0, lhs, Operand(rhs), LeaveCC, eq);
}
done.Branch(eq);
GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
frame_->SpillAll();
frame_->CallStub(&stub, 0);
done.Bind();
frame_->EmitPush(r0);
break;
} else {
// Fall through!
}
case Token::BIT_OR:
case Token::BIT_AND:
case Token::BIT_XOR:
case Token::SHL:
case Token::SHR:
case Token::SAR: {
frame_->EmitPop(r0); // r0 : y
frame_->EmitPop(r1); // r1 : x
GenericBinaryOpStub stub(op, overwrite_mode, r1, r0, constant_rhs);
frame_->CallStub(&stub, 0);
break;
}
case Token::COMMA:
frame_->EmitPop(r0);
// Simply discard left value.
frame_->Drop();
break;
default:
// Other cases should have been handled before this point.
UNREACHABLE();
break;
}
}
void CodeGenerator::VirtualFrameBinaryOperation(Token::Value op,
OverwriteMode overwrite_mode,
int constant_rhs) {
// top of virtual frame: y
// 2nd elt. on virtual frame : x
// result : top of virtual frame
// Stub is entered with a call: 'return address' is in lr.
switch (op) {
case Token::ADD: // fall through.
case Token::SUB: // fall through.
if (inline_smi) {
bool rhs_is_smi = frame_->KnownSmiAt(0);
bool lhs_is_smi = frame_->KnownSmiAt(1);
Register rhs = frame_->PopToRegister();
Register lhs = frame_->PopToRegister(rhs);
Register smi_test_reg;
Condition cond;
if (!rhs_is_smi || !lhs_is_smi) {
if (!rhs_is_smi) {
smi_test_reg = rhs;
} else if (!lhs_is_smi) {
smi_test_reg = lhs;
} else {
smi_test_reg = VirtualFrame::scratch0();
__ orr(smi_test_reg, rhs, Operand(lhs));
}
// Check they are both Smis.
__ tst(smi_test_reg, Operand(kSmiTagMask));
cond = eq;
} else {
cond = al;
}
ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
if (op == Token::BIT_OR) {
__ orr(r0, lhs, Operand(rhs), LeaveCC, cond);
} else if (op == Token::BIT_AND) {
__ and_(r0, lhs, Operand(rhs), LeaveCC, cond);
} else {
ASSERT(op == Token::BIT_XOR);
ASSERT_EQ(0, kSmiTag);
__ eor(r0, lhs, Operand(rhs), LeaveCC, cond);
}
if (cond != al) {
JumpTarget done;
done.Branch(cond);
GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
frame_->SpillAll();
frame_->CallStub(&stub, 0);
done.Bind();
}
frame_->EmitPush(r0);
break;
} else {
// Fall through!
}
case Token::MUL:
case Token::DIV:
case Token::MOD:
case Token::BIT_OR:
case Token::BIT_AND:
case Token::BIT_XOR:
case Token::SHL:
case Token::SHR:
case Token::SAR: {
......@@ -972,7 +1033,8 @@ void DeferredInlineSmiOperation::Generate() {
rhs = r1;
}
} else {
UNREACHABLE(); // Should have been handled in SmiOperation.
ASSERT(op_ == Token::SHL);
__ mov(r1, Operand(Smi::FromInt(value_)));
}
break;
}
......@@ -1020,6 +1082,8 @@ void CodeGenerator::SmiOperation(Token::Value op,
OverwriteMode mode) {
int int_value = Smi::cast(*value)->value();
bool both_sides_are_smi = frame_->KnownSmiAt(0);
bool something_to_inline;
switch (op) {
case Token::ADD:
......@@ -1030,7 +1094,10 @@ void CodeGenerator::SmiOperation(Token::Value op,
something_to_inline = true;
break;
}
case Token::SHL:
case Token::SHL: {
something_to_inline = (both_sides_are_smi || !reversed);
break;
}
case Token::SHR:
case Token::SAR: {
if (reversed) {
......@@ -1067,17 +1134,18 @@ void CodeGenerator::SmiOperation(Token::Value op,
// Push the rhs onto the virtual frame by putting it in a TOS register.
Register rhs = frame_->GetTOSRegister();
__ mov(rhs, Operand(value));
frame_->EmitPush(rhs);
VirtualFrameBinaryOperation(op, mode, int_value);
frame_->EmitPush(rhs, TypeInfo::Smi());
GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, int_value);
} else {
// Pop the rhs, then push lhs and rhs in the right order. Only performs
// at most one pop, the rest takes place in TOS registers.
Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
__ mov(lhs, Operand(value));
frame_->EmitPush(lhs);
frame_->EmitPush(rhs);
VirtualFrameBinaryOperation(op, mode, kUnknownIntValue);
frame_->EmitPush(lhs, TypeInfo::Smi());
TypeInfo t = both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Unknown();
frame_->EmitPush(rhs, t);
GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, kUnknownIntValue);
}
return;
}
......@@ -1097,8 +1165,10 @@ void CodeGenerator::SmiOperation(Token::Value op,
__ add(tos, tos, Operand(value), SetCC);
deferred->Branch(vs);
__ tst(tos, Operand(kSmiTagMask));
deferred->Branch(ne);
if (!both_sides_are_smi) {
__ tst(tos, Operand(kSmiTagMask));
deferred->Branch(ne);
}
deferred->BindExit();
frame_->EmitPush(tos);
break;
......@@ -1114,8 +1184,10 @@ void CodeGenerator::SmiOperation(Token::Value op,
__ sub(tos, tos, Operand(value), SetCC);
}
deferred->Branch(vs);
__ tst(tos, Operand(kSmiTagMask));
deferred->Branch(ne);
if (!both_sides_are_smi) {
__ tst(tos, Operand(kSmiTagMask));
deferred->Branch(ne);
}
deferred->BindExit();
frame_->EmitPush(tos);
break;
......@@ -1125,25 +1197,65 @@ void CodeGenerator::SmiOperation(Token::Value op,
case Token::BIT_OR:
case Token::BIT_XOR:
case Token::BIT_AND: {
DeferredCode* deferred =
new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
__ tst(tos, Operand(kSmiTagMask));
deferred->Branch(ne);
switch (op) {
case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
default: UNREACHABLE();
if (both_sides_are_smi) {
switch (op) {
case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
default: UNREACHABLE();
}
frame_->EmitPush(tos, TypeInfo::Smi());
} else {
DeferredCode* deferred =
new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
__ tst(tos, Operand(kSmiTagMask));
deferred->Branch(ne);
switch (op) {
case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
default: UNREACHABLE();
}
deferred->BindExit();
TypeInfo result_type =
(op == Token::BIT_AND) ? TypeInfo::Smi() : TypeInfo::Integer32();
frame_->EmitPush(tos, result_type);
}
deferred->BindExit();
frame_->EmitPush(tos);
break;
}
case Token::SHL:
if (reversed) {
ASSERT(both_sides_are_smi);
int max_shift = 0;
int max_result = int_value == 0 ? 1 : int_value;
while (Smi::IsValid(max_result << 1)) {
max_shift++;
max_result <<= 1;
}
DeferredCode* deferred =
new DeferredInlineSmiOperation(op, int_value, true, mode, tos);
// Mask off the last 5 bits of the shift operand (rhs). This is part
// of the definition of shift in JS and we know we have a Smi so we
// can safely do this. The masked version gets passed to the
// deferred code, but that makes no difference.
__ and_(tos, tos, Operand(Smi::FromInt(0x1f)));
__ cmp(tos, Operand(Smi::FromInt(max_shift)));
deferred->Branch(ge);
Register scratch = VirtualFrame::scratch0();
__ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Untag.
__ mov(tos, Operand(Smi::FromInt(int_value))); // Load constant.
__ mov(tos, Operand(tos, LSL, scratch)); // Shift constant.
deferred->BindExit();
TypeInfo result = TypeInfo::Integer32();
frame_->EmitPush(tos, result);
break;
}
// Fall through!
case Token::SHR:
case Token::SAR: {
ASSERT(!reversed);
TypeInfo result = TypeInfo::Integer32();
Register scratch = VirtualFrame::scratch0();
Register scratch2 = VirtualFrame::scratch1();
int shift_value = int_value & 0x1f; // least significant 5 bits
......@@ -1151,9 +1263,15 @@ void CodeGenerator::SmiOperation(Token::Value op,
new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
uint32_t problematic_mask = kSmiTagMask;
// For unsigned shift by zero all negative smis are problematic.
if (shift_value == 0 && op == Token::SHR) problematic_mask |= 0x80000000;
__ tst(tos, Operand(problematic_mask));
deferred->Branch(ne); // Go slow for problematic input.
bool skip_smi_test = both_sides_are_smi;
if (shift_value == 0 && op == Token::SHR) {
problematic_mask |= 0x80000000;
skip_smi_test = false;
}
if (!skip_smi_test) {
__ tst(tos, Operand(problematic_mask));
deferred->Branch(ne); // Go slow for problematic input.
}
switch (op) {
case Token::SHL: {
if (shift_value != 0) {
......@@ -1188,6 +1306,9 @@ void CodeGenerator::SmiOperation(Token::Value op,
// by 0 or 1 when handed a valid smi
__ tst(scratch, Operand(0xc0000000));
deferred->Branch(ne);
} else {
ASSERT(shift_value >= 2);
result = TypeInfo::Smi(); // SHR by at least 2 gives a Smi.
}
__ mov(tos, Operand(scratch, LSL, kSmiTagSize));
}
......@@ -1204,13 +1325,15 @@ void CodeGenerator::SmiOperation(Token::Value op,
__ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
// Put tag back.
__ mov(tos, Operand(tos, LSL, kSmiTagSize));
// SAR by at least 1 gives a Smi.
result = TypeInfo::Smi();
}
break;
}
default: UNREACHABLE();
}
deferred->BindExit();
frame_->EmitPush(tos);
frame_->EmitPush(tos, result);
break;
}
......@@ -1219,21 +1342,24 @@ void CodeGenerator::SmiOperation(Token::Value op,
ASSERT(int_value >= 2);
ASSERT(IsPowerOf2(int_value));
DeferredCode* deferred =
new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
unsigned mask = (0x80000000u | kSmiTagMask);
__ tst(tos, Operand(mask));
deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
mask = (int_value << kSmiTagSize) - 1;
__ and_(tos, tos, Operand(mask));
deferred->BindExit();
frame_->EmitPush(tos);
// Mod of positive power of 2 Smi gives a Smi if the lhs is an integer.
frame_->EmitPush(
tos,
both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Number());
break;
}
case Token::MUL: {
ASSERT(IsEasyToMultiplyBy(int_value));
DeferredCode* deferred =
new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
max_smi_that_wont_overflow <<= kSmiTagSize;
unsigned mask = 0x80000000u;
......@@ -1279,45 +1405,66 @@ void CodeGenerator::Comparison(Condition cc,
Register lhs;
Register rhs;
bool lhs_is_smi;
bool rhs_is_smi;
// We load the top two stack positions into registers chosen by the virtual
// frame. This should keep the register shuffling to a minimum.
// Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
if (cc == gt || cc == le) {
cc = ReverseCondition(cc);
lhs_is_smi = frame_->KnownSmiAt(0);
rhs_is_smi = frame_->KnownSmiAt(1);
lhs = frame_->PopToRegister();
rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
} else {
rhs_is_smi = frame_->KnownSmiAt(0);
lhs_is_smi = frame_->KnownSmiAt(1);
rhs = frame_->PopToRegister();
lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
}
bool both_sides_are_smi = (lhs_is_smi && rhs_is_smi);
ASSERT(rhs.is(r0) || rhs.is(r1));
ASSERT(lhs.is(r0) || lhs.is(r1));
// Now we have the two sides in r0 and r1. We flush any other registers
// because the stub doesn't know about register allocation.
frame_->SpillAll();
Register scratch = VirtualFrame::scratch0();
__ orr(scratch, lhs, Operand(rhs));
__ tst(scratch, Operand(kSmiTagMask));
JumpTarget smi;
smi.Branch(eq);
JumpTarget exit;
// Perform non-smi comparison by stub.
// CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
// We call with 0 args because there are 0 on the stack.
if (!rhs.is(r0)) {
__ Swap(rhs, lhs, ip);
}
if (!both_sides_are_smi) {
// Now we have the two sides in r0 and r1. We flush any other registers
// because the stub doesn't know about register allocation.
frame_->SpillAll();
Register scratch = VirtualFrame::scratch0();
Register smi_test_reg;
if (lhs_is_smi) {
smi_test_reg = rhs;
} else if (rhs_is_smi) {
smi_test_reg = lhs;
} else {
__ orr(scratch, lhs, Operand(rhs));
smi_test_reg = scratch;
}
__ tst(smi_test_reg, Operand(kSmiTagMask));
JumpTarget smi;
smi.Branch(eq);
CompareStub stub(cc, strict);
frame_->CallStub(&stub, 0);
__ cmp(r0, Operand(0));
JumpTarget exit;
exit.Jump();
// Perform non-smi comparison by stub.
// CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
// We call with 0 args because there are 0 on the stack.
if (!rhs.is(r0)) {
__ Swap(rhs, lhs, ip);
}
CompareStub stub(cc, strict);
frame_->CallStub(&stub, 0);
__ cmp(r0, Operand(0));
exit.Jump();
smi.Bind();
}
// Do smi comparisons by pointer comparison.
smi.Bind();
__ cmp(lhs, Operand(rhs));
exit.Bind();
......@@ -2090,6 +2237,17 @@ void CodeGenerator::VisitForStatement(ForStatement* node) {
node->break_target()->SetExpectedHeight();
IncrementLoopNesting();
// We know that the loop index is a smi if it is not modified in the
// loop body and it is checked against a constant limit in the loop
// condition. In this case, we reset the static type information of the
// loop index to smi before compiling the body, the update expression, and
// the bottom check of the loop condition.
TypeInfoCodeGenState type_info_scope(this,
node->is_fast_smi_loop() ?
node->loop_variable()->slot() :
NULL,
TypeInfo::Smi());
// If there is no update statement, label the top of the loop with the
// continue target, otherwise with the loop target.
JumpTarget loop(JumpTarget::BIDIRECTIONAL);
......@@ -2810,7 +2968,8 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
} else {
Register scratch = VirtualFrame::scratch0();
frame_->EmitPush(SlotOperand(slot, scratch));
TypeInfo info = type_info(slot);
frame_->EmitPush(SlotOperand(slot, scratch), info);
if (slot->var()->mode() == Variable::CONST) {
// Const slots may contain 'the hole' value (the constant hasn't been
// initialized yet) which needs to be converted into the 'undefined'
......@@ -3100,8 +3259,9 @@ void CodeGenerator::VisitLiteral(Literal* node) {
#endif
Comment cmnt(masm_, "[ Literal");
Register reg = frame_->GetTOSRegister();
bool is_smi = node->handle()->IsSmi();
__ mov(reg, Operand(node->handle()));
frame_->EmitPush(reg);
frame_->EmitPush(reg, is_smi ? TypeInfo::Smi() : TypeInfo::Unknown());
ASSERT_EQ(original_height + 1, frame_->height());
}
......@@ -3332,9 +3492,16 @@ void CodeGenerator::EmitSlotAssignment(Assignment* node) {
false,
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
GenerateInlineSmi inline_smi =
loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
if (literal != NULL) {
ASSERT(!literal->handle()->IsSmi());
inline_smi = DONT_GENERATE_INLINE_SMI;
}
Load(node->value());
VirtualFrameBinaryOperation(
node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
GenericBinaryOperation(node->binary_op(),
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
inline_smi);
}
} else {
Load(node->value());
......@@ -3425,9 +3592,16 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
false,
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
GenerateInlineSmi inline_smi =
loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
if (literal != NULL) {
ASSERT(!literal->handle()->IsSmi());
inline_smi = DONT_GENERATE_INLINE_SMI;
}
Load(node->value());
VirtualFrameBinaryOperation(
node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
GenericBinaryOperation(node->binary_op(),
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
inline_smi);
}
} else {
// For non-compound assignment just load the right-hand side.
......@@ -3532,9 +3706,16 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
false,
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
GenerateInlineSmi inline_smi =
loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
if (literal != NULL) {
ASSERT(!literal->handle()->IsSmi());
inline_smi = DONT_GENERATE_INLINE_SMI;
}
Load(node->value());
VirtualFrameBinaryOperation(
node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
GenericBinaryOperation(node->binary_op(),
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
inline_smi);
}
} else {
// For non-compound assignment just load the right-hand side.
......@@ -5086,9 +5267,36 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
Variable* var = node->expression()->AsVariableProxy()->AsVariable();
bool is_const = (var != NULL && var->mode() == Variable::CONST);
bool is_slot = (var != NULL && var->mode() == Variable::VAR);
if (!is_const && is_slot && type_info(var->slot()).IsSmi()) {
// The type info declares that this variable is always a Smi. That
// means it is a Smi both before and after the increment/decrement.
// Lets make use of that to make a very minimal count.
Reference target(this, node->expression(), !is_const);
ASSERT(!target.is_illegal());
target.GetValue(); // Pushes the value.
Register value = frame_->PopToRegister();
if (is_postfix) frame_->EmitPush(value);
if (is_increment) {
__ add(value, value, Operand(Smi::FromInt(1)));
} else {
__ sub(value, value, Operand(Smi::FromInt(1)));
}
frame_->EmitPush(value);
target.SetValue(NOT_CONST_INIT);
if (is_postfix) frame_->Pop();
ASSERT_EQ(original_height + 1, frame_->height());
return;
}
if (is_postfix) {
// If it's a postfix expression and its result is not ignored and the
// reference is non-trivial, then push a placeholder on the stack now
// to hold the result of the expression.
bool placeholder_pushed = false;
if (!is_slot && is_postfix) {
frame_->EmitPush(Operand(Smi::FromInt(0)));
placeholder_pushed = true;
}
// A constant reference is not saved to, so a constant reference is not a
......@@ -5097,12 +5305,11 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
if (target.is_illegal()) {
// Spoof the virtual frame to have the expected height (one higher
// than on entry).
if (!is_postfix) {
frame_->EmitPush(Operand(Smi::FromInt(0)));
}
if (!placeholder_pushed) frame_->EmitPush(Operand(Smi::FromInt(0)));
ASSERT_EQ(original_height + 1, frame_->height());
return;
}
// This pushes 0, 1 or 2 words on the object to be used later when updating
// the target. It also pushes the current value of the target.
target.GetValue();
......@@ -5110,16 +5317,21 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
JumpTarget slow;
JumpTarget exit;
// Check for smi operand.
Register value = frame_->PopToRegister();
__ tst(value, Operand(kSmiTagMask));
slow.Branch(ne);
// Postfix: Store the old value as the result.
if (is_postfix) {
if (placeholder_pushed) {
frame_->SetElementAt(value, target.size());
} else if (is_postfix) {
frame_->EmitPush(value);
__ mov(VirtualFrame::scratch0(), value);
value = VirtualFrame::scratch0();
}
// Check for smi operand.
__ tst(value, Operand(kSmiTagMask));
slow.Branch(ne);
// Perform optimistic increment/decrement.
if (is_increment) {
__ add(value, value, Operand(Smi::FromInt(1)), SetCC);
......@@ -5300,18 +5512,30 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
if (rliteral != NULL && rliteral->handle()->IsSmi()) {
VirtualFrame::RegisterAllocationScope scope(this);
Load(node->left());
if (frame_->KnownSmiAt(0)) overwrite_left = false;
SmiOperation(node->op(),
rliteral->handle(),
false,
overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
} else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
VirtualFrame::RegisterAllocationScope scope(this);
Load(node->right());
if (frame_->KnownSmiAt(0)) overwrite_right = false;
SmiOperation(node->op(),
lliteral->handle(),
true,
overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
GenerateInlineSmi inline_smi =
loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
if (lliteral != NULL) {
ASSERT(!lliteral->handle()->IsSmi());
inline_smi = DONT_GENERATE_INLINE_SMI;
}
if (rliteral != NULL) {
ASSERT(!rliteral->handle()->IsSmi());
inline_smi = DONT_GENERATE_INLINE_SMI;
}
VirtualFrame::RegisterAllocationScope scope(this);
OverwriteMode overwrite_mode = NO_OVERWRITE;
if (overwrite_left) {
......@@ -5321,7 +5545,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
}
Load(node->left());
Load(node->right());
VirtualFrameBinaryOperation(node->op(), overwrite_mode);
GenericBinaryOperation(node->op(), overwrite_mode, inline_smi);
}
}
ASSERT(!has_valid_frame() ||
......@@ -5813,6 +6037,7 @@ void CodeGenerator::EmitKeyedLoad() {
frame_->scratch0(), frame_->scratch1());
// Load the key and receiver from the stack.
bool key_is_known_smi = frame_->KnownSmiAt(0);
Register key = frame_->PopToRegister();
Register receiver = frame_->PopToRegister(key);
VirtualFrame::SpilledScope spilled(frame_);
......@@ -5835,18 +6060,21 @@ void CodeGenerator::EmitKeyedLoad() {
// Check the map. The null map used below is patched by the inline cache
// code.
__ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
// Check that the key is a smi.
if (!key_is_known_smi) {
__ tst(key, Operand(kSmiTagMask));
deferred->Branch(ne);
}
#ifdef DEBUG
Label check_inlined_codesize;
masm_->bind(&check_inlined_codesize);
Label check_inlined_codesize;
masm_->bind(&check_inlined_codesize);
#endif
__ mov(scratch2, Operand(Factory::null_value()));
__ cmp(scratch1, scratch2);
deferred->Branch(ne);
// Check that the key is a smi.
__ tst(key, Operand(kSmiTagMask));
deferred->Branch(ne);
// Get the elements array from the receiver and check that it
// is not a dictionary.
__ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
......
......@@ -43,6 +43,7 @@ class RegisterFile;
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
enum GenerateInlineSmi { DONT_GENERATE_INLINE_SMI, GENERATE_INLINE_SMI };
// -------------------------------------------------------------------------
......@@ -129,24 +130,55 @@ class CodeGenState BASE_EMBEDDED {
// leaves the code generator with a NULL state.
explicit CodeGenState(CodeGenerator* owner);
// Create a code generator state based on a code generator's current
// state. The new state has its own pair of branch labels.
CodeGenState(CodeGenerator* owner,
JumpTarget* true_target,
JumpTarget* false_target);
// Destroy a code generator state and restore the owning code generator's
// previous state.
~CodeGenState();
virtual ~CodeGenState();
virtual JumpTarget* true_target() const { return NULL; }
virtual JumpTarget* false_target() const { return NULL; }
JumpTarget* true_target() const { return true_target_; }
JumpTarget* false_target() const { return false_target_; }
protected:
inline CodeGenerator* owner() { return owner_; }
inline CodeGenState* previous() const { return previous_; }
private:
CodeGenerator* owner_;
CodeGenState* previous_;
};
class ConditionCodeGenState : public CodeGenState {
public:
// Create a code generator state based on a code generator's current
// state. The new state has its own pair of branch labels.
ConditionCodeGenState(CodeGenerator* owner,
JumpTarget* true_target,
JumpTarget* false_target);
virtual JumpTarget* true_target() const { return true_target_; }
virtual JumpTarget* false_target() const { return false_target_; }
private:
JumpTarget* true_target_;
JumpTarget* false_target_;
CodeGenState* previous_;
};
class TypeInfoCodeGenState : public CodeGenState {
public:
TypeInfoCodeGenState(CodeGenerator* owner,
Slot* slot_number,
TypeInfo info);
~TypeInfoCodeGenState();
virtual JumpTarget* true_target() const { return previous()->true_target(); }
virtual JumpTarget* false_target() const {
return previous()->false_target();
}
private:
Slot* slot_;
TypeInfo old_type_info_;
};
......@@ -216,6 +248,23 @@ class CodeGenerator: public AstVisitor {
CodeGenState* state() { return state_; }
void set_state(CodeGenState* state) { state_ = state; }
TypeInfo type_info(Slot* slot) {
int index = NumberOfSlot(slot);
if (index == kInvalidSlotNumber) return TypeInfo::Unknown();
return (*type_info_)[index];
}
TypeInfo set_type_info(Slot* slot, TypeInfo info) {
int index = NumberOfSlot(slot);
ASSERT(index >= kInvalidSlotNumber);
if (index != kInvalidSlotNumber) {
TypeInfo previous_value = (*type_info_)[index];
(*type_info_)[index] = info;
return previous_value;
}
return TypeInfo::Unknown();
}
void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
static const int kUnknownIntValue = -1;
......@@ -225,7 +274,7 @@ class CodeGenerator: public AstVisitor {
static int InlineRuntimeCallArgumentsCount(Handle<String> name);
// Constants related to patching of inlined load/store.
static const int kInlinedKeyedLoadInstructionsAfterPatch = 19;
static const int kInlinedKeyedLoadInstructionsAfterPatch = 17;
static const int kInlinedKeyedStoreInstructionsAfterPatch = 5;
private:
......@@ -239,6 +288,10 @@ class CodeGenerator: public AstVisitor {
// Generating deferred code.
void ProcessDeferred();
static const int kInvalidSlotNumber = -1;
int NumberOfSlot(Slot* slot);
// State
bool has_cc() const { return cc_reg_ != al; }
JumpTarget* true_target() const { return state_->true_target(); }
......@@ -351,10 +404,8 @@ class CodeGenerator: public AstVisitor {
void GenericBinaryOperation(Token::Value op,
OverwriteMode overwrite_mode,
GenerateInlineSmi inline_smi,
int known_rhs = kUnknownIntValue);
void VirtualFrameBinaryOperation(Token::Value op,
OverwriteMode overwrite_mode,
int known_rhs = kUnknownIntValue);
void Comparison(Condition cc,
Expression* left,
Expression* right,
......@@ -511,6 +562,8 @@ class CodeGenerator: public AstVisitor {
CodeGenState* state_;
int loop_nesting_;
Vector<TypeInfo>* type_info_;
// Jump targets
BreakTarget function_return_;
......
......@@ -50,6 +50,11 @@ void JumpTarget::DoJump() {
ASSERT(cgen()->HasValidEntryRegisters());
if (entry_frame_set_) {
if (entry_label_.is_bound()) {
// If we already bound and generated code at the destination then it
// is too late to ask for less optimistic type assumptions.
ASSERT(entry_frame_.IsCompatibleWith(cgen()->frame()));
}
// There already a frame expectation at the target.
cgen()->frame()->MergeTo(&entry_frame_);
cgen()->DeleteFrame();
......@@ -67,8 +72,12 @@ void JumpTarget::DoBranch(Condition cc, Hint ignored) {
ASSERT(cgen()->has_valid_frame());
if (entry_frame_set_) {
// Backward branch. We have an expected frame to merge to on the
// backward edge.
if (entry_label_.is_bound()) {
// If we already bound and generated code at the destination then it
// is too late to ask for less optimistic type assumptions.
ASSERT(entry_frame_.IsCompatibleWith(cgen()->frame()));
}
// We have an expected frame to merge to on the backward edge.
cgen()->frame()->MergeTo(&entry_frame_, cc);
} else {
// Clone the current frame to use as the expected one at the target.
......
......@@ -48,6 +48,12 @@ MemOperand VirtualFrame::Receiver() {
return ParameterAt(-1);
}
void VirtualFrame::Forget(int count) {
SpillAll();
LowerHeight(count);
}
} } // namespace v8::internal
#endif // V8_VIRTUAL_FRAME_ARM_INL_H_
......@@ -43,7 +43,7 @@ void VirtualFrame::PopToR1R0() {
// Shuffle things around so the top of stack is in r0 and r1.
MergeTOSTo(R0_R1_TOS);
// Pop the two registers off the stack so they are detached from the frame.
element_count_ -= 2;
LowerHeight(2);
top_of_stack_state_ = NO_TOS_REGISTERS;
}
......@@ -52,7 +52,7 @@ void VirtualFrame::PopToR1() {
// Shuffle things around so the top of stack is only in r1.
MergeTOSTo(R1_TOS);
// Pop the register off the stack so it is detached from the frame.
element_count_ -= 1;
LowerHeight(1);
top_of_stack_state_ = NO_TOS_REGISTERS;
}
......@@ -61,13 +61,22 @@ void VirtualFrame::PopToR0() {
// Shuffle things around so the top of stack only in r0.
MergeTOSTo(R0_TOS);
// Pop the register off the stack so it is detached from the frame.
element_count_ -= 1;
LowerHeight(1);
top_of_stack_state_ = NO_TOS_REGISTERS;
}
void VirtualFrame::MergeTo(const VirtualFrame* expected, Condition cond) {
if (Equals(expected)) return;
ASSERT(expected->IsCompatibleWith(this));
MergeTOSTo(expected->top_of_stack_state_, cond);
ASSERT(register_allocation_map_ == expected->register_allocation_map_);
}
void VirtualFrame::MergeTo(VirtualFrame* expected, Condition cond) {
if (Equals(expected)) return;
expected->tos_known_smi_map_ &= tos_known_smi_map_;
MergeTOSTo(expected->top_of_stack_state_, cond);
ASSERT(register_allocation_map_ == expected->register_allocation_map_);
}
......@@ -420,7 +429,7 @@ void VirtualFrame::Drop(int count) {
}
if (count == 0) return;
__ add(sp, sp, Operand(count * kPointerSize));
element_count_ -= count;
LowerHeight(count);
}
......@@ -430,7 +439,7 @@ void VirtualFrame::Pop() {
} else {
top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
}
element_count_--;
LowerHeight(1);
}
......@@ -442,7 +451,7 @@ void VirtualFrame::EmitPop(Register reg) {
__ mov(reg, kTopRegister[top_of_stack_state_]);
top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
}
element_count_--;
LowerHeight(1);
}
......@@ -550,7 +559,7 @@ void VirtualFrame::Dup() {
UNREACHABLE();
}
}
element_count_++;
RaiseHeight(1, tos_known_smi_map_ & 1);
}
......@@ -589,7 +598,7 @@ void VirtualFrame::Dup2() {
UNREACHABLE();
}
}
element_count_ += 2;
RaiseHeight(2, tos_known_smi_map_ & 3);
}
......@@ -597,7 +606,7 @@ Register VirtualFrame::PopToRegister(Register but_not_to_this_one) {
ASSERT(but_not_to_this_one.is(r0) ||
but_not_to_this_one.is(r1) ||
but_not_to_this_one.is(no_reg));
element_count_--;
LowerHeight(1);
if (top_of_stack_state_ == NO_TOS_REGISTERS) {
if (but_not_to_this_one.is(r0)) {
__ pop(r1);
......@@ -625,8 +634,8 @@ void VirtualFrame::EnsureOneFreeTOSRegister() {
}
void VirtualFrame::EmitPush(Register reg) {
element_count_++;
void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
RaiseHeight(1, info.IsSmi() ? 1 : 0);
if (reg.is(cp)) {
// If we are pushing cp then we are about to make a call and things have to
// be pushed to the physical stack. There's nothing to be gained my moving
......@@ -659,6 +668,9 @@ void VirtualFrame::EmitPush(Register reg) {
void VirtualFrame::SetElementAt(Register reg, int this_far_down) {
if (this_far_down < kTOSKnownSmiMapSize) {
tos_known_smi_map_ &= ~(1 << this_far_down);
}
if (this_far_down == 0) {
Pop();
Register dest = GetTOSRegister();
......@@ -699,8 +711,8 @@ Register VirtualFrame::GetTOSRegister() {
}
void VirtualFrame::EmitPush(Operand operand) {
element_count_++;
void VirtualFrame::EmitPush(Operand operand, TypeInfo info) {
RaiseHeight(1, info.IsSmi() ? 1 : 0);
if (SpilledScope::is_spilled()) {
__ mov(r0, operand);
__ push(r0);
......@@ -712,8 +724,8 @@ void VirtualFrame::EmitPush(Operand operand) {
}
void VirtualFrame::EmitPush(MemOperand operand) {
element_count_++;
void VirtualFrame::EmitPush(MemOperand operand, TypeInfo info) {
RaiseHeight(1, info.IsSmi() ? 1 : 0);
if (SpilledScope::is_spilled()) {
__ ldr(r0, operand);
__ push(r0);
......@@ -726,7 +738,7 @@ void VirtualFrame::EmitPush(MemOperand operand) {
void VirtualFrame::EmitPushRoot(Heap::RootListIndex index) {
element_count_++;
RaiseHeight(1, 0);
if (SpilledScope::is_spilled()) {
__ LoadRoot(r0, index);
__ push(r0);
......
......@@ -154,10 +154,7 @@ class VirtualFrame : public ZoneObject {
// Forget elements from the top of the frame to match an actual frame (eg,
// the frame after a runtime call). No code is emitted except to bring the
// frame to a spilled state.
void Forget(int count) {
SpillAll();
element_count_ -= count;
}
void Forget(int count);
// Spill all values from the frame to memory.
void SpillAll();
......@@ -184,8 +181,14 @@ class VirtualFrame : public ZoneObject {
// Make this virtual frame have a state identical to an expected virtual
// frame. As a side effect, code may be emitted to make this frame match
// the expected one.
void MergeTo(VirtualFrame* expected, Condition cond = al);
void MergeTo(const VirtualFrame* expected, Condition cond = al);
// Checks whether this frame can be branched to by the other frame.
bool IsCompatibleWith(const VirtualFrame* other) const {
return (tos_known_smi_map_ & (~other->tos_known_smi_map_)) == 0;
}
// Detach a frame from its code generator, perhaps temporarily. This
// tells the register allocator that it is free to use frame-internal
// registers. Used when the code generator's frame is switched from this
......@@ -234,6 +237,11 @@ class VirtualFrame : public ZoneObject {
return MemOperand(sp, adjusted_index * kPointerSize);
}
bool KnownSmiAt(int index) {
if (index >= kTOSKnownSmiMapSize) return false;
return (tos_known_smi_map_ & (1 << index)) != 0;
}
// A frame-allocated local as an assembly operand.
inline MemOperand LocalAt(int index);
......@@ -352,9 +360,9 @@ class VirtualFrame : public ZoneObject {
// Push an element on top of the expression stack and emit a
// corresponding push instruction.
void EmitPush(Register reg);
void EmitPush(Operand operand);
void EmitPush(MemOperand operand);
void EmitPush(Register reg, TypeInfo type_info = TypeInfo::Unknown());
void EmitPush(Operand operand, TypeInfo type_info = TypeInfo::Unknown());
void EmitPush(MemOperand operand, TypeInfo type_info = TypeInfo::Unknown());
void EmitPushRoot(Heap::RootListIndex index);
// Overwrite the nth thing on the stack. If the nth position is in a
......@@ -419,6 +427,8 @@ class VirtualFrame : public ZoneObject {
int element_count_;
TopOfStack top_of_stack_state_:3;
int register_allocation_map_:kNumberOfAllocatedRegisters;
static const int kTOSKnownSmiMapSize = 4;
unsigned tos_known_smi_map_:kTOSKnownSmiMapSize;
// The index of the element that is at the processor's stack pointer
// (the sp register). For now since everything is in memory it is given
......@@ -473,6 +483,25 @@ class VirtualFrame : public ZoneObject {
inline bool Equals(const VirtualFrame* other);
inline void LowerHeight(int count) {
element_count_ -= count;
if (count >= kTOSKnownSmiMapSize) {
tos_known_smi_map_ = 0;
} else {
tos_known_smi_map_ >>= count;
}
}
inline void RaiseHeight(int count, unsigned known_smi_map = 0) {
ASSERT(known_smi_map < (1u << count));
element_count_ += count;
if (count >= kTOSKnownSmiMapSize) {
tos_known_smi_map_ = known_smi_map;
} else {
tos_known_smi_map_ = ((tos_known_smi_map_ << count) | known_smi_map);
}
}
friend class JumpTarget;
};
......
......@@ -318,6 +318,9 @@ Variable* AssignedVariablesAnalyzer::FindSmiLoopVariable(ForStatement* stmt) {
Variable* loop_var = init->target()->AsVariableProxy()->AsVariable();
if (loop_var == NULL || !loop_var->IsStackAllocated()) return NULL;
// Don't try to get clever with const or dynamic variables.
if (loop_var->mode() != Variable::VAR) return NULL;
// The initial value has to be a smi.
Literal* init_lit = init->value()->AsLiteral();
if (init_lit == NULL || !init_lit->handle()->IsSmi()) return NULL;
......
......@@ -47,7 +47,7 @@ namespace internal {
class TypeInfo {
public:
TypeInfo() { }
TypeInfo() : type_(kUnknownType) { }
static inline TypeInfo Unknown();
// We know it's a primitive type.
......
......@@ -42,7 +42,8 @@ namespace internal {
VirtualFrame::VirtualFrame(InvalidVirtualFrameInitializer* dummy)
: element_count_(0),
top_of_stack_state_(NO_TOS_REGISTERS),
register_allocation_map_(0) { }
register_allocation_map_(0),
tos_known_smi_map_(0) { }
// On entry to a function, the virtual frame already contains the receiver,
......@@ -50,20 +51,23 @@ VirtualFrame::VirtualFrame(InvalidVirtualFrameInitializer* dummy)
VirtualFrame::VirtualFrame()
: element_count_(parameter_count() + 2),
top_of_stack_state_(NO_TOS_REGISTERS),
register_allocation_map_(0) { }
register_allocation_map_(0),
tos_known_smi_map_(0) { }
// When cloned, a frame is a deep copy of the original.
VirtualFrame::VirtualFrame(VirtualFrame* original)
: element_count_(original->element_count()),
top_of_stack_state_(original->top_of_stack_state_),
register_allocation_map_(original->register_allocation_map_) { }
register_allocation_map_(original->register_allocation_map_),
tos_known_smi_map_(0) { }
bool VirtualFrame::Equals(const VirtualFrame* other) {
ASSERT(element_count() == other->element_count());
if (top_of_stack_state_ != other->top_of_stack_state_) return false;
if (register_allocation_map_ != other->register_allocation_map_) return false;
if (tos_known_smi_map_ != other->tos_known_smi_map_) return false;
return true;
}
......
......@@ -36,7 +36,7 @@ namespace internal {
void VirtualFrame::Adjust(int count) {
ASSERT(count >= 0);
element_count_ += count;
RaiseHeight(count, 0);
}
......
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