Revision: 4148
Author: [email protected]
Date: Tue Mar 16 09:07:19 2010
Log: Use untagged int32 values in evaluation of side-effect free
expressions.
Review URL: http://codereview.chromium.org/975001
http://code.google.com/p/v8/source/detail?r=4148
Modified:
/branches/bleeding_edge/src/ast.h
/branches/bleeding_edge/src/flag-definitions.h
/branches/bleeding_edge/src/frame-element.h
/branches/bleeding_edge/src/ia32/assembler-ia32-inl.h
/branches/bleeding_edge/src/ia32/assembler-ia32.h
/branches/bleeding_edge/src/ia32/codegen-ia32.cc
/branches/bleeding_edge/src/ia32/codegen-ia32.h
/branches/bleeding_edge/src/ia32/register-allocator-ia32.cc
/branches/bleeding_edge/src/ia32/virtual-frame-ia32.cc
/branches/bleeding_edge/src/ia32/virtual-frame-ia32.h
/branches/bleeding_edge/src/register-allocator.h
/branches/bleeding_edge/src/rewriter.cc
/branches/bleeding_edge/src/virtual-frame.cc
=======================================
--- /branches/bleeding_edge/src/ast.h Tue Mar 16 03:54:02 2010
+++ /branches/bleeding_edge/src/ast.h Tue Mar 16 09:07:19 2010
@@ -269,8 +269,18 @@
bitfields_ |= ToInt32Field::encode(to_int32);
}
+ // How many bitwise logical or shift operators are used in this
expression?
+ int num_bit_ops() { return NumBitOpsField::decode(bitfields_); }
+ void set_num_bit_ops(int num_bit_ops) {
+ bitfields_ &= ~NumBitOpsField::mask();
+ num_bit_ops = Min(num_bit_ops, kMaxNumBitOps);
+ bitfields_ |= NumBitOpsField::encode(num_bit_ops);
+ }
+
private:
+ static const int kMaxNumBitOps = (1 << 5) - 1;
+
uint32_t bitfields_;
StaticType type_;
@@ -281,6 +291,7 @@
class SideEffectFreeField : public BitField<bool, 0, 1> {};
class NoNegativeZeroField : public BitField<bool, 1, 1> {};
class ToInt32Field : public BitField<bool, 2, 1> {};
+ class NumBitOpsField : public BitField<int, 3, 5> {};
};
=======================================
--- /branches/bleeding_edge/src/flag-definitions.h Mon Mar 8 07:28:57 2010
+++ /branches/bleeding_edge/src/flag-definitions.h Tue Mar 16 09:07:19 2010
@@ -153,7 +153,7 @@
"try to use the speculative optimizing backend for all code")
DEFINE_bool(trace_bailout, false,
"print reasons for falling back to using the classic V8
backend")
-DEFINE_bool(safe_int32_compiler, false,
+DEFINE_bool(safe_int32_compiler, true,
"enable optimized side-effect-free int32 expressions.")
DEFINE_bool(use_flow_graph, false, "perform flow-graph based
optimizations")
=======================================
--- /branches/bleeding_edge/src/frame-element.h Mon Mar 8 22:38:33 2010
+++ /branches/bleeding_edge/src/frame-element.h Tue Mar 16 09:07:19 2010
@@ -144,6 +144,16 @@
bool is_copied() const { return CopiedField::decode(value_); }
void set_copied() { value_ = value_ | CopiedField::encode(true); }
void clear_copied() { value_ = value_ & ~CopiedField::mask(); }
+
+ // An untagged int32 FrameElement represents a signed int32
+ // on the stack. These are only allowed in a side-effect-free
+ // int32 calculation, and if a non-int32 input shows up or an overflow
+ // occurs, we bail out and drop all the int32 values.
+ void set_untagged_int32(bool value) {
+ value_ &= ~UntaggedInt32Field::mask();
+ value_ |= UntaggedInt32Field::encode(value);
+ }
+ bool is_untagged_int32() const { return
UntaggedInt32Field::decode(value_); }
Register reg() const {
ASSERT(is_register());
@@ -255,8 +265,9 @@
class TypeField: public BitField<Type, 0, 3> {};
class CopiedField: public BitField<bool, 3, 1> {};
class SyncedField: public BitField<bool, 4, 1> {};
- class NumberInfoField: public BitField<int, 5, 4> {};
- class DataField: public BitField<uint32_t, 9, 32 - 9> {};
+ class UntaggedInt32Field: public BitField<bool, 5, 1> {};
+ class NumberInfoField: public BitField<int, 6, 4> {};
+ class DataField: public BitField<uint32_t, 10, 32 - 10> {};
friend class VirtualFrame;
};
=======================================
--- /branches/bleeding_edge/src/ia32/assembler-ia32-inl.h Thu Nov 12
05:55:21 2009
+++ /branches/bleeding_edge/src/ia32/assembler-ia32-inl.h Tue Mar 16
09:07:19 2010
@@ -312,6 +312,12 @@
// reg
set_modrm(3, reg);
}
+
+
+Operand::Operand(XMMRegister xmm_reg) {
+ Register reg = { xmm_reg.code() };
+ set_modrm(3, reg);
+}
Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
=======================================
--- /branches/bleeding_edge/src/ia32/assembler-ia32.h Fri Mar 12 00:36:01
2010
+++ /branches/bleeding_edge/src/ia32/assembler-ia32.h Tue Mar 16 09:07:19
2010
@@ -241,6 +241,9 @@
// reg
INLINE(explicit Operand(Register reg));
+ // XMM reg
+ INLINE(explicit Operand(XMMRegister xmm_reg));
+
// [disp/r]
INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode));
// disp only must always be relocated
@@ -709,6 +712,7 @@
void fistp_s(const Operand& adr);
void fistp_d(const Operand& adr);
+ // The fisttp instructions require SSE3.
void fisttp_s(const Operand& adr);
void fisttp_d(const Operand& adr);
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Mar 16 09:03:40
2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Mar 16 09:07:19
2010
@@ -112,6 +112,8 @@
allocator_(NULL),
state_(NULL),
loop_nesting_(0),
+ in_safe_int32_mode_(false),
+ safe_int32_mode_enabled_(true),
function_return_is_shadowed_(false),
in_spilled_code_(false) {
}
@@ -437,14 +439,14 @@
// frame. If the expression is boolean-valued it may be compiled (or
// partially compiled) into control flow to the control destination.
// If force_control is true, control flow is forced.
-void CodeGenerator::LoadCondition(Expression* x,
+void CodeGenerator::LoadCondition(Expression* expr,
ControlDestination* dest,
bool force_control) {
ASSERT(!in_spilled_code());
int original_height = frame_->height();
{ CodeGenState new_state(this, dest);
- Visit(x);
+ Visit(expr);
// If we hit a stack overflow, we may not have actually visited
// the expression. In that case, we ensure that we have a
@@ -479,6 +481,78 @@
frame_->SpillAll();
set_in_spilled_code(true);
}
+
+
+void CodeGenerator::LoadInSafeInt32Mode(Expression* expr,
+ BreakTarget* unsafe_bailout) {
+ set_unsafe_bailout(unsafe_bailout);
+ set_in_safe_int32_mode(true);
+ Load(expr);
+ Result value = frame_->Pop();
+ ASSERT(frame_->HasNoUntaggedInt32Elements());
+ ConvertInt32ResultToNumber(&value);
+ set_in_safe_int32_mode(false);
+ set_unsafe_bailout(NULL);
+ frame_->Push(&value);
+}
+
+
+void CodeGenerator::LoadWithSafeInt32ModeDisabled(Expression* expr) {
+ set_safe_int32_mode_enabled(false);
+ Load(expr);
+ set_safe_int32_mode_enabled(true);
+}
+
+
+void CodeGenerator::ConvertInt32ResultToNumber(Result* value) {
+ ASSERT(value->is_untagged_int32());
+ if (value->is_register()) {
+ Register val = value->reg();
+ JumpTarget done;
+ __ add(val, Operand(val));
+ done.Branch(no_overflow, value);
+ __ sar(val, 1);
+ // If there was an overflow, bits 30 and 31 of the original number
disagree.
+ __ xor_(val, 0x80000000u);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope fscope(SSE2);
+ __ cvtsi2sd(xmm0, Operand(val));
+ } else {
+ // Move val to ST[0] in the FPU
+ // Push and pop are safe with respect to the virtual frame because
+ // all synced elements are below the actual stack pointer.
+ __ push(val);
+ __ fild_s(Operand(esp, 0));
+ __ pop(val);
+ }
+ Result scratch = allocator_->Allocate();
+ ASSERT(scratch.is_register());
+ Label allocation_failed;
+ __ AllocateHeapNumber(val, scratch.reg(),
+ no_reg, &allocation_failed);
+ VirtualFrame* clone = new VirtualFrame(frame_);
+ scratch.Unuse();
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope fscope(SSE2);
+ __ movdbl(FieldOperand(val, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ fstp_d(FieldOperand(val, HeapNumber::kValueOffset));
+ }
+ done.Jump(value);
+
+ // Establish the virtual frame, cloned from where AllocateHeapNumber
+ // jumped to allocation_failed.
+ RegisterFile empty_regs;
+ SetFrame(clone, &empty_regs);
+ __ bind(&allocation_failed);
+ unsafe_bailout_->Jump();
+
+ done.Bind(value);
+ } else {
+ ASSERT(value->is_constant());
+ }
+ value->set_untagged_int32(false);
+}
void CodeGenerator::Load(Expression* expr) {
@@ -486,59 +560,80 @@
int original_height = frame_->height();
#endif
ASSERT(!in_spilled_code());
- JumpTarget true_target;
- JumpTarget false_target;
- ControlDestination dest(&true_target, &false_target, true);
- LoadCondition(expr, &dest, false);
-
- if (dest.false_was_fall_through()) {
- // The false target was just bound.
- JumpTarget loaded;
- frame_->Push(Factory::false_value());
- // There may be dangling jumps to the true target.
- if (true_target.is_linked()) {
- loaded.Jump();
- true_target.Bind();
- frame_->Push(Factory::true_value());
- loaded.Bind();
- }
-
- } else if (dest.is_used()) {
- // There is true, and possibly false, control flow (with true as
- // the fall through).
- JumpTarget loaded;
- frame_->Push(Factory::true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- false_target.Bind();
- frame_->Push(Factory::false_value());
- loaded.Bind();
- }
-
+ JumpTarget done;
+
+ // If the expression should be a side-effect-free 32-bit int computation,
+ // compile that SafeInt32 path, and a bailout path.
+ if (!in_safe_int32_mode() &&
+ safe_int32_mode_enabled() &&
+ expr->side_effect_free() &&
+ expr->num_bit_ops() > 2 &&
+ CpuFeatures::IsSupported(SSE2)) {
+ BreakTarget unsafe_bailout;
+ unsafe_bailout.set_expected_height(frame_->height());
+ LoadInSafeInt32Mode(expr, &unsafe_bailout);
+ done.Jump();
+
+ if (unsafe_bailout.is_linked()) {
+ unsafe_bailout.Bind();
+ LoadWithSafeInt32ModeDisabled(expr);
+ }
} else {
- // We have a valid value on top of the frame, but we still may
- // have dangling jumps to the true and false targets from nested
- // subexpressions (eg, the left subexpressions of the
- // short-circuited boolean operators).
- ASSERT(has_valid_frame());
- if (true_target.is_linked() || false_target.is_linked()) {
+ JumpTarget true_target;
+ JumpTarget false_target;
+
+ ControlDestination dest(&true_target, &false_target, true);
+ LoadCondition(expr, &dest, false);
+
+ if (dest.false_was_fall_through()) {
+ // The false target was just bound.
JumpTarget loaded;
- loaded.Jump(); // Don't lose the current TOS.
+ frame_->Push(Factory::false_value());
+ // There may be dangling jumps to the true target.
if (true_target.is_linked()) {
+ loaded.Jump();
true_target.Bind();
frame_->Push(Factory::true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- }
- }
+ loaded.Bind();
+ }
+
+ } else if (dest.is_used()) {
+ // There is true, and possibly false, control flow (with true as
+ // the fall through).
+ JumpTarget loaded;
+ frame_->Push(Factory::true_value());
if (false_target.is_linked()) {
+ loaded.Jump();
false_target.Bind();
frame_->Push(Factory::false_value());
- }
- loaded.Bind();
+ loaded.Bind();
+ }
+
+ } else {
+ // We have a valid value on top of the frame, but we still may
+ // have dangling jumps to the true and false targets from nested
+ // subexpressions (eg, the left subexpressions of the
+ // short-circuited boolean operators).
+ ASSERT(has_valid_frame());
+ if (true_target.is_linked() || false_target.is_linked()) {
+ JumpTarget loaded;
+ loaded.Jump(); // Don't lose the current TOS.
+ if (true_target.is_linked()) {
+ true_target.Bind();
+ frame_->Push(Factory::true_value());
+ if (false_target.is_linked()) {
+ loaded.Jump();
+ }
+ }
+ if (false_target.is_linked()) {
+ false_target.Bind();
+ frame_->Push(Factory::false_value());
+ }
+ loaded.Bind();
+ }
}
}
-
+ done.Bind();
ASSERT(has_valid_frame());
ASSERT(frame_->height() == original_height + 1);
}
@@ -4312,7 +4407,7 @@
void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
Comment cmnt(masm_, "[ FunctionLiteral");
-
+ ASSERT(!in_safe_int32_mode());
// Build the function boilerplate and instantiate it.
Handle<JSFunction> boilerplate =
Compiler::BuildBoilerplate(node, script(), this);
@@ -4325,6 +4420,7 @@
void CodeGenerator::VisitFunctionBoilerplateLiteral(
FunctionBoilerplateLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
Result result = InstantiateBoilerplate(node->boilerplate());
frame()->Push(&result);
@@ -4333,6 +4429,7 @@
void CodeGenerator::VisitConditional(Conditional* node) {
Comment cmnt(masm_, "[ Conditional");
+ ASSERT(!in_safe_int32_mode());
JumpTarget then;
JumpTarget else_;
JumpTarget exit;
@@ -4503,6 +4600,7 @@
Slot* slot,
TypeofState typeof_state,
JumpTarget* slow) {
+ ASSERT(!in_safe_int32_mode());
// Check that no extension objects have been created by calls to
// eval from the current scope to the global scope.
Register context = esi;
@@ -4671,10 +4769,20 @@
}
-void CodeGenerator::VisitSlot(Slot* node) {
+void CodeGenerator::VisitSlot(Slot* slot) {
Comment cmnt(masm_, "[ Slot");
- Result result = LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
- frame()->Push(&result);
+ if (in_safe_int32_mode()) {
+ if ((slot->type() == Slot::LOCAL && !slot->is_arguments())) {
+ frame()->UntaggedPushLocalAt(slot->index());
+ } else if (slot->type() == Slot::PARAMETER) {
+ frame()->UntaggedPushParameterAt(slot->index());
+ } else {
+ UNREACHABLE();
+ }
+ } else {
+ Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
+ frame()->Push(&result);
+ }
}
@@ -4686,6 +4794,7 @@
Visit(expr);
} else {
ASSERT(var->is_global());
+ ASSERT(!in_safe_int32_mode());
Reference ref(this, node);
ref.GetValue();
}
@@ -4694,7 +4803,11 @@
void CodeGenerator::VisitLiteral(Literal* node) {
Comment cmnt(masm_, "[ Literal");
- frame_->Push(node->handle());
+ if (in_safe_int32_mode()) {
+ frame_->PushUntaggedElement(node->handle());
+ } else {
+ frame_->Push(node->handle());
+ }
}
@@ -4768,6 +4881,7 @@
void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ RegExp Literal");
// Retrieve the literals array and check the allocated entry. Begin
@@ -4804,6 +4918,7 @@
void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ ObjectLiteral");
// Load a writable copy of the function of this activation in a
@@ -4888,6 +5003,7 @@
void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ ArrayLiteral");
// Load a writable copy of the function of this activation in a
@@ -4959,6 +5075,7 @@
void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ ASSERT(!in_safe_int32_mode());
ASSERT(!in_spilled_code());
// Call runtime routine to allocate the catch extension object and
// assign the exception value to the catch variable.
@@ -5178,6 +5295,7 @@
void CodeGenerator::VisitAssignment(Assignment* node) {
+ ASSERT(!in_safe_int32_mode());
#ifdef DEBUG
int original_height = frame()->height();
#endif
@@ -5213,6 +5331,7 @@
void CodeGenerator::VisitThrow(Throw* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ Throw");
Load(node->exception());
Result result = frame_->CallRuntime(Runtime::kThrow, 1);
@@ -5221,6 +5340,7 @@
void CodeGenerator::VisitProperty(Property* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ Property");
Reference property(this, node);
property.GetValue();
@@ -5228,6 +5348,7 @@
void CodeGenerator::VisitCall(Call* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ Call");
Expression* function = node->expression();
@@ -5443,6 +5564,7 @@
void CodeGenerator::VisitCallNew(CallNew* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ CallNew");
// According to ECMA-262, section 11.2.2, page 44, the function
@@ -6370,6 +6492,7 @@
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
+ ASSERT(!in_safe_int32_mode());
if (CheckForInlineRuntimeCall(node)) {
return;
}
@@ -6496,64 +6619,100 @@
}
} else {
- Load(node->expression());
- bool overwrite =
- (node->expression()->AsBinaryOperation() != NULL &&
-
node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
- switch (op) {
- case Token::SUB: {
- GenericUnaryOpStub stub(Token::SUB, overwrite);
- Result operand = frame_->Pop();
- Result answer = frame_->CallStub(&stub, &operand);
- frame_->Push(&answer);
- break;
- }
-
- case Token::BIT_NOT: {
- // Smi check.
- JumpTarget smi_label;
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- smi_label.Branch(zero, &operand, taken);
-
- GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
- Result answer = frame_->CallStub(&stub, &operand);
- continue_label.Jump(&answer);
-
- smi_label.Bind(&answer);
- answer.ToRegister();
- frame_->Spill(answer.reg());
- __ not_(answer.reg());
- __ and_(answer.reg(), ~kSmiTagMask); // Remove inverted smi-tag.
-
- continue_label.Bind(&answer);
- frame_->Push(&answer);
- break;
- }
-
- case Token::ADD: {
- // Smi check.
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- continue_label.Branch(zero, &operand, taken);
-
- frame_->Push(&operand);
- Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
+ if (in_safe_int32_mode()) {
+ Visit(node->expression());
+ Result value = frame_->Pop();
+ ASSERT(value.is_untagged_int32());
+ // Registers containing an int32 value are not multiply used.
+ ASSERT(!value.is_register() || !frame_->is_used(value.reg()));
+ value.ToRegister();
+ switch (op) {
+ case Token::SUB: {
+ __ neg(value.reg());
+ if (node->no_negative_zero()) {
+ // -MIN_INT is MIN_INT with the overflow flag set.
+ unsafe_bailout_->Branch(overflow);
+ } else {
+ // MIN_INT and 0 both have bad negations. They both have 31
zeros.
+ __ test(value.reg(), Immediate(0x7FFFFFFF));
+ unsafe_bailout_->Branch(zero);
+ }
+ break;
+ }
+ case Token::BIT_NOT: {
+ __ not_(value.reg());
+ break;
+ }
+ case Token::ADD: {
+ // Unary plus has no effect on int32 values.
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ frame_->Push(&value);
+
+ } else {
+ Load(node->expression());
+ bool overwrite =
+ (node->expression()->AsBinaryOperation() != NULL &&
+
node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ switch (op) {
+ case Token::SUB: {
+ GenericUnaryOpStub stub(Token::SUB, overwrite);
+ Result operand = frame_->Pop();
+ Result answer = frame_->CallStub(&stub, &operand);
+ frame_->Push(&answer);
+ break;
+ }
+
+ case Token::BIT_NOT: {
+ // Smi check.
+ JumpTarget smi_label;
+ JumpTarget continue_label;
+ Result operand = frame_->Pop();
+ operand.ToRegister();
+ __ test(operand.reg(), Immediate(kSmiTagMask));
+ smi_label.Branch(zero, &operand, taken);
+
+ GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
+ Result answer = frame_->CallStub(&stub, &operand);
+ continue_label.Jump(&answer);
+
+ smi_label.Bind(&answer);
+ answer.ToRegister();
+ frame_->Spill(answer.reg());
+ __ not_(answer.reg());
+ __ and_(answer.reg(), ~kSmiTagMask); // Remove inverted smi-tag.
+
+ continue_label.Bind(&answer);
+ frame_->Push(&answer);
+ break;
+ }
+
+ case Token::ADD: {
+ // Smi check.
+ JumpTarget continue_label;
+ Result operand = frame_->Pop();
+ operand.ToRegister();
+ __ test(operand.reg(), Immediate(kSmiTagMask));
+ continue_label.Branch(zero, &operand, taken);
+
+ frame_->Push(&operand);
+ Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
CALL_FUNCTION, 1);
- continue_label.Bind(&answer);
- frame_->Push(&answer);
- break;
- }
-
- default:
- // NOT, DELETE, TYPEOF, and VOID are handled outside the
- // switch.
- UNREACHABLE();
+ continue_label.Bind(&answer);
+ frame_->Push(&answer);
+ break;
+ }
+
+ default:
+ // NOT, DELETE, TYPEOF, and VOID are handled outside the
+ // switch.
+ UNREACHABLE();
+ }
}
}
}
@@ -6646,6 +6805,7 @@
void CodeGenerator::VisitCountOperation(CountOperation* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ CountOperation");
bool is_postfix = node->is_postfix();
@@ -6759,6 +6919,166 @@
}
+void CodeGenerator::Int32BinaryOperation(BinaryOperation* node) {
+ Token::Value op = node->op();
+ Comment cmnt(masm_, "[ Int32BinaryOperation");
+ ASSERT(in_safe_int32_mode());
+ ASSERT(safe_int32_mode_enabled());
+ ASSERT(FLAG_safe_int32_compiler);
+
+ if (op == Token::COMMA) {
+ // Discard left value.
+ frame_->Nip(1);
+ return;
+ }
+
+ Result right = frame_->Pop();
+ Result left = frame_->Pop();
+
+ ASSERT(right.is_untagged_int32());
+ ASSERT(left.is_untagged_int32());
+ // Registers containing an int32 value are not multiply used.
+ ASSERT(!left.is_register() || !frame_->is_used(left.reg()));
+ ASSERT(!right.is_register() || !frame_->is_used(right.reg()));
+
+ switch (op) {
+ case Token::COMMA:
+ case Token::OR:
+ case Token::AND:
+ UNREACHABLE();
+ break;
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ left.ToRegister();
+ right.ToRegister();
+ if (op == Token::BIT_OR) {
+ __ or_(left.reg(), Operand(right.reg()));
+ } else if (op == Token::BIT_XOR) {
+ __ xor_(left.reg(), Operand(right.reg()));
+ } else {
+ ASSERT(op == Token::BIT_AND);
+ __ and_(left.reg(), Operand(right.reg()));
+ }
+ frame_->Push(&left);
+ right.Unuse();
+ break;
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ bool test_shr_overflow = false;
+ left.ToRegister();
+ if (right.is_constant()) {
+ ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
+ int shift_amount = NumberToInt32(*right.handle()) & 0x1F;
+ if (op == Token::SAR) {
+ __ sar(left.reg(), shift_amount);
+ } else if (op == Token::SHL) {
+ __ shl(left.reg(), shift_amount);
+ } else {
+ ASSERT(op == Token::SHR);
+ __ shr(left.reg(), shift_amount);
+ if (shift_amount == 0) test_shr_overflow = true;
+ }
+ } else {
+ // Move right to ecx
+ if (left.is_register() && left.reg().is(ecx)) {
+ right.ToRegister();
+ __ xchg(left.reg(), right.reg());
+ left = right; // Left is unused here, copy of right unused by
Push.
+ } else {
+ right.ToRegister(ecx);
+ left.ToRegister();
+ }
+ if (op == Token::SAR) {
+ __ sar_cl(left.reg());
+ } else if (op == Token::SHL) {
+ __ shl_cl(left.reg());
+ } else {
+ ASSERT(op == Token::SHR);
+ __ shr_cl(left.reg());
+ test_shr_overflow = true;
+ }
+ }
+ {
+ Register left_reg = left.reg();
+ frame_->Push(&left);
+ right.Unuse();
+ if (test_shr_overflow && !node->to_int32()) {
+ // Uint32 results with top bit set are not Int32 values.
+ // If they will be forced to Int32, skip the test.
+ // Test is needed because shr with shift amount 0 does not set
flags.
+ __ test(left_reg, Operand(left_reg));
+ unsafe_bailout_->Branch(sign);
+ }
+ }
+ break;
+ }
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ left.ToRegister();
+ right.ToRegister();
+ if (op == Token::ADD) {
+ __ add(left.reg(), Operand(right.reg()));
+ } else if (op == Token::SUB) {
+ __ sub(left.reg(), Operand(right.reg()));
+ } else {
+ ASSERT(op == Token::MUL);
+ // We have statically verified that a negative zero can be ignored.
+ __ imul(left.reg(), Operand(right.reg()));
+ }
+ right.Unuse();
+ frame_->Push(&left);
+ if (!node->to_int32()) {
+ // If ToInt32 is called on the result of ADD, SUB, or MUL, we don't
+ // care about overflows.
+ unsafe_bailout_->Branch(overflow);
+ }
+ break;
+ case Token::DIV:
+ case Token::MOD: {
+ if (right.is_register() && (right.reg().is(eax) ||
right.reg().is(edx))) {
+ if (left.is_register() && left.reg().is(edi)) {
+ right.ToRegister(ebx);
+ } else {
+ right.ToRegister(edi);
+ }
+ }
+ left.ToRegister(eax);
+ Result edx_reg = allocator_->Allocate(edx);
+ right.ToRegister();
+ // The results are unused here because BreakTarget::Branch cannot
handle
+ // live results.
+ Register right_reg = right.reg();
+ left.Unuse();
+ right.Unuse();
+ edx_reg.Unuse();
+ __ cmp(right_reg, 0);
+ // Ensure divisor is positive: no chance of non-int32 or -0 result.
+ unsafe_bailout_->Branch(less_equal);
+ __ cdq(); // Sign-extend eax into edx:eax
+ __ idiv(right_reg);
+ if (op == Token::MOD) {
+ Result edx_result(edx, NumberInfo::Integer32());
+ edx_result.set_untagged_int32(true);
+ frame_->Push(&edx_result);
+ } else {
+ ASSERT(op == Token::DIV);
+ __ test(edx, Operand(edx));
+ unsafe_bailout_->Branch(not_equal);
+ Result eax_result(eax, NumberInfo::Integer32());
+ eax_result.set_untagged_int32(true);
+ frame_->Push(&eax_result);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
Comment cmnt(masm_, "[ BinaryOperation");
Token::Value op = node->op();
@@ -6773,6 +7093,7 @@
// is necessary because we assume that if we get control flow on the
// last path out of an expression we got it on all paths.
if (op == Token::AND) {
+ ASSERT(!in_safe_int32_mode());
JumpTarget is_true;
ControlDestination dest(&is_true, destination()->false_target(), true);
LoadCondition(node->left(), &dest, false);
@@ -6836,6 +7157,7 @@
}
} else if (op == Token::OR) {
+ ASSERT(!in_safe_int32_mode());
JumpTarget is_false;
ControlDestination dest(destination()->true_target(), &is_false,
false);
LoadCondition(node->left(), &dest, false);
@@ -6897,6 +7219,10 @@
exit.Bind();
}
+ } else if (in_safe_int32_mode()) {
+ Visit(node->left());
+ Visit(node->right());
+ Int32BinaryOperation(node);
} else {
// NOTE: The code below assumes that the slow cases (calls to runtime)
// never return a constant/immutable object.
@@ -6925,11 +7251,13 @@
void CodeGenerator::VisitThisFunction(ThisFunction* node) {
+ ASSERT(!in_safe_int32_mode());
frame_->PushFunction();
}
void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ CompareOperation");
bool left_already_loaded = false;
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.h Mon Mar 15 07:03:36 2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.h Tue Mar 16 09:07:19 2010
@@ -356,6 +356,23 @@
// State
ControlDestination* destination() const { return state_->destination(); }
+
+ // Control of side-effect-free int32 expression compilation.
+ bool in_safe_int32_mode() { return in_safe_int32_mode_; }
+ void set_in_safe_int32_mode(bool value) { in_safe_int32_mode_ = value; }
+ bool safe_int32_mode_enabled() {
+ return FLAG_safe_int32_compiler && safe_int32_mode_enabled_;
+ }
+ void set_safe_int32_mode_enabled(bool value) {
+ safe_int32_mode_enabled_ = value;
+ }
+ void set_unsafe_bailout(BreakTarget* unsafe_bailout) {
+ unsafe_bailout_ = unsafe_bailout;
+ }
+
+ // Take the Result that is an untagged int32, and convert it to a tagged
+ // Smi or HeapNumber. Remove the untagged_int32 flag from the result.
+ void ConvertInt32ResultToNumber(Result* value);
// Track loop nesting level.
int loop_nesting() const { return loop_nesting_; }
@@ -413,7 +430,7 @@
return ContextOperand(esi, Context::GLOBAL_INDEX);
}
- void LoadCondition(Expression* x,
+ void LoadCondition(Expression* expr,
ControlDestination* destination,
bool force_control);
void Load(Expression* expr);
@@ -425,6 +442,11 @@
// temporarily while the code generator is being transformed.
void LoadAndSpill(Expression* expression);
+ // Evaluate an expression and place its value on top of the frame,
+ // using, or not using, the side-effect-free expression compiler.
+ void LoadInSafeInt32Mode(Expression* expr, BreakTarget* unsafe_bailout);
+ void LoadWithSafeInt32ModeDisabled(Expression* expr);
+
// Read a value from a slot and leave it on top of the expression stack.
Result LoadFromSlot(Slot* slot, TypeofState typeof_state);
Result LoadFromSlotCheckForArguments(Slot* slot, TypeofState
typeof_state);
@@ -496,6 +518,12 @@
OverwriteMode overwrite_mode,
bool no_negative_zero);
+
+ // Emit code to perform a binary operation on two untagged int32 values.
+ // The values are on top of the frame, and the result is pushed on the
frame.
+ void Int32BinaryOperation(BinaryOperation* node);
+
+
void Comparison(AstNode* node,
Condition cc,
bool strict,
@@ -642,10 +670,14 @@
RegisterAllocator* allocator_;
CodeGenState* state_;
int loop_nesting_;
+ bool in_safe_int32_mode_;
+ bool safe_int32_mode_enabled_;
// Jump targets.
// The target of the return from the function.
BreakTarget function_return_;
+ // The target of the bailout from a side-effect-free int32 subexpression.
+ BreakTarget* unsafe_bailout_;
// True if the function return is shadowed (ie, jumping to the target
// function_return_ does not jump to the true function return, but rather
=======================================
--- /branches/bleeding_edge/src/ia32/register-allocator-ia32.cc Fri Mar 5
15:54:13 2010
+++ /branches/bleeding_edge/src/ia32/register-allocator-ia32.cc Tue Mar 16
09:07:19 2010
@@ -42,7 +42,33 @@
if (is_constant()) {
Result fresh = CodeGeneratorScope::Current()->allocator()->Allocate();
ASSERT(fresh.is_valid());
- if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
+ if (is_untagged_int32()) {
+ fresh.set_untagged_int32(true);
+ if (handle()->IsSmi()) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(),
+ Immediate(Smi::cast(*handle())->value()));
+ } else if (handle()->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(*handle())->value();
+ int32_t value = DoubleToInt32(double_value);
+ if (double_value == 0 && signbit(double_value)) {
+ // Negative zero must not be converted to an int32 unless
+ // the context allows it.
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+
CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ } else if (double_value == value) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(), Immediate(value));
+ } else {
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+
CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ }
+ } else {
+ // Constant is not a number. This was not predicted by AST
analysis.
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ }
+ } else if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
} else {
CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
@@ -65,13 +91,38 @@
CodeGeneratorScope::Current()->masm()->mov(fresh.reg(), reg());
} else {
ASSERT(is_constant());
- if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
- CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(),
handle());
+ if (is_untagged_int32()) {
+ if (handle()->IsSmi()) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(),
+ Immediate(Smi::cast(*handle())->value()));
+ } else {
+ ASSERT(handle()->IsHeapNumber());
+ double double_value = HeapNumber::cast(*handle())->value();
+ int32_t value = DoubleToInt32(double_value);
+ if (double_value == 0 && signbit(double_value)) {
+ // Negative zero must not be converted to an int32 unless
+ // the context allows it.
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+
CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ } else if (double_value == value) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(), Immediate(value));
+ } else {
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+
CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ }
+ }
} else {
- CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
- Immediate(handle()));
+ if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
+ CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(),
handle());
+ } else {
+ CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
+ Immediate(handle()));
+ }
}
}
+ fresh.set_untagged_int32(is_untagged_int32());
*this = fresh;
} else if (is_register() && reg().is(target)) {
ASSERT(CodeGeneratorScope::Current()->has_valid_frame());
=======================================
--- /branches/bleeding_edge/src/ia32/virtual-frame-ia32.cc Thu Mar 11
02:28:40 2010
+++ /branches/bleeding_edge/src/ia32/virtual-frame-ia32.cc Tue Mar 16
09:07:19 2010
@@ -773,6 +773,89 @@
elements_[index].clear_sync();
}
}
+
+
+void VirtualFrame::UntaggedPushFrameSlotAt(int index) {
+ ASSERT(index >= 0);
+ ASSERT(index <= element_count());
+ FrameElement original = elements_[index];
+ if (original.is_copy()) {
+ original = elements_[original.index()];
+ index = original.index();
+ }
+
+ switch (original.type()) {
+ case FrameElement::MEMORY:
+ case FrameElement::REGISTER: {
+ Label done;
+ // Emit code to load the original element's data into a register.
+ // Push that register as a FrameElement on top of the frame.
+ Result fresh = cgen()->allocator()->Allocate();
+ ASSERT(fresh.is_valid());
+ Register fresh_reg = fresh.reg();
+ FrameElement new_element =
+ FrameElement::RegisterElement(fresh_reg,
+ FrameElement::NOT_SYNCED,
+ original.number_info());
+ new_element.set_untagged_int32(true);
+ Use(fresh_reg, element_count());
+ fresh.Unuse(); // BreakTarget does not handle a live Result well.
+ elements_.Add(new_element);
+ if (original.is_register()) {
+ __ mov(fresh_reg, original.reg());
+ } else {
+ ASSERT(original.is_memory());
+ __ mov(fresh_reg, Operand(ebp, fp_relative(index)));
+ }
+ // Now convert the value to int32, or bail out.
+ if (original.number_info().IsSmi()) {
+ __ SmiUntag(fresh_reg);
+ // Pushing the element is completely done.
+ } else {
+ __ test(fresh_reg, Immediate(kSmiTagMask));
+ Label not_smi;
+ __ j(not_zero, ¬_smi);
+ __ SmiUntag(fresh_reg);
+ __ jmp(&done);
+
+ __ bind(¬_smi);
+ if (!original.number_info().IsNumber()) {
+ __ cmp(FieldOperand(fresh_reg, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ cgen()->unsafe_bailout_->Branch(not_equal);
+ }
+
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ UNREACHABLE();
+ } else {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ movdbl(xmm0, FieldOperand(fresh_reg,
HeapNumber::kValueOffset));
+ __ cvttsd2si(fresh_reg, Operand(xmm0));
+ __ cvtsi2sd(xmm1, Operand(fresh_reg));
+ __ ucomisd(xmm0, xmm1);
+ cgen()->unsafe_bailout_->Branch(not_equal);
+ cgen()->unsafe_bailout_->Branch(parity_even); // NaN.
+ // Test for negative zero.
+ __ test(fresh_reg, Operand(fresh_reg));
+ __ j(not_zero, &done);
+ __ movmskpd(fresh_reg, xmm0);
+ __ and_(fresh_reg, 0x1);
+ cgen()->unsafe_bailout_->Branch(not_equal);
+ }
+ __ bind(&done);
+ }
+ break;
+ }
+ case FrameElement::CONSTANT:
+ elements_.Add(CopyElementAt(index));
+ elements_[element_count() - 1].set_untagged_int32(true);
+ break;
+ case FrameElement::COPY:
+ case FrameElement::INVALID:
+ UNREACHABLE();
+ break;
+ }
+}
void VirtualFrame::PushTryHandler(HandlerType type) {
@@ -1060,6 +1143,7 @@
FrameElement element = elements_.RemoveLast();
int index = element_count();
ASSERT(element.is_valid());
+ ASSERT(element.is_untagged_int32() == cgen()->in_safe_int32_mode());
// Get number type information of the result.
NumberInfo info;
@@ -1077,6 +1161,7 @@
ASSERT(temp.is_valid());
__ pop(temp.reg());
temp.set_number_info(info);
+ temp.set_untagged_int32(element.is_untagged_int32());
return temp;
}
@@ -1089,6 +1174,7 @@
if (element.is_register()) {
Unuse(element.reg());
} else if (element.is_copy()) {
+ ASSERT(!element.is_untagged_int32());
ASSERT(element.index() < index);
index = element.index();
element = elements_[index];
@@ -1100,6 +1186,7 @@
// Memory elements could only be the backing store of a copy.
// Allocate the original to a register.
ASSERT(index <= stack_pointer_);
+ ASSERT(!element.is_untagged_int32());
Result temp = cgen()->allocator()->Allocate();
ASSERT(temp.is_valid());
Use(temp.reg(), index);
@@ -1113,10 +1200,14 @@
__ mov(temp.reg(), Operand(ebp, fp_relative(index)));
return Result(temp.reg(), info);
} else if (element.is_register()) {
- return Result(element.reg(), info);
+ Result return_value(element.reg(), info);
+ return_value.set_untagged_int32(element.is_untagged_int32());
+ return return_value;
} else {
ASSERT(element.is_constant());
- return Result(element.handle());
+ Result return_value(element.handle());
+ return_value.set_untagged_int32(element.is_untagged_int32());
+ return return_value;
}
}
@@ -1159,6 +1250,12 @@
stack_pointer_++;
__ push(immediate);
}
+
+
+void VirtualFrame::PushUntaggedElement(Handle<Object> value) {
+ elements_.Add(FrameElement::ConstantElement(value,
FrameElement::NOT_SYNCED));
+ elements_[element_count() - 1].set_untagged_int32(true);
+}
void VirtualFrame::Push(Expression* expr) {
=======================================
--- /branches/bleeding_edge/src/ia32/virtual-frame-ia32.h Thu Mar 11
02:28:40 2010
+++ /branches/bleeding_edge/src/ia32/virtual-frame-ia32.h Tue Mar 16
09:07:19 2010
@@ -241,6 +241,11 @@
void PushLocalAt(int index) {
PushFrameSlotAt(local0_index() + index);
}
+
+ // Push a copy of the value of a local frame slot on top of the frame.
+ void UntaggedPushLocalAt(int index) {
+ UntaggedPushFrameSlotAt(local0_index() + index);
+ }
// Push the value of a local frame slot on top of the frame and
invalidate
// the local slot. The slot should be written to before trying to read
@@ -281,6 +286,11 @@
void PushParameterAt(int index) {
PushFrameSlotAt(param0_index() + index);
}
+
+ // Push a copy of the value of a parameter frame slot on top of the
frame.
+ void UntaggedPushParameterAt(int index) {
+ UntaggedPushFrameSlotAt(param0_index() + index);
+ }
// Push the value of a paramter frame slot on top of the frame and
// invalidate the parameter slot. The slot should be written to before
@@ -399,6 +409,8 @@
inline void Push(Handle<Object> value);
inline void Push(Smi* value);
+ void PushUntaggedElement(Handle<Object> value);
+
// Pushing a result invalidates it (its contents become owned by the
// frame).
void Push(Result* result) {
@@ -410,6 +422,10 @@
ASSERT(result->is_constant());
Push(result->handle());
}
+ if (cgen()->in_safe_int32_mode()) {
+ ASSERT(result->is_untagged_int32());
+ elements_[element_count() - 1].set_untagged_int32(true);
+ }
result->Unuse();
}
@@ -421,6 +437,14 @@
// of the frame, leaving the previous top-of-frame value on top of
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
inline void Nip(int num_dropped);
+
+ // Check that the frame has no elements containing untagged int32
elements.
+ bool HasNoUntaggedInt32Elements() {
+ for (int i = 0; i < element_count(); ++i) {
+ if (elements_[i].is_untagged_int32()) return false;
+ }
+ return true;
+ }
// Update the type information of a local variable frame element
directly.
inline void SetTypeForLocalAt(int index, NumberInfo info);
@@ -533,6 +557,11 @@
// the frame.
inline void PushFrameSlotAt(int index);
+ // Push a copy of a frame slot (typically a local or parameter) on top of
+ // the frame, at an untagged int32 value. Bails out if the value is not
+ // an int32.
+ void UntaggedPushFrameSlotAt(int index);
+
// Push a the value of a frame slot (typically a local or parameter) on
// top of the frame and invalidate the slot.
void TakeFrameSlotAt(int index);
=======================================
--- /branches/bleeding_edge/src/register-allocator.h Fri Mar 5 15:54:13
2010
+++ /branches/bleeding_edge/src/register-allocator.h Tue Mar 16 09:07:19
2010
@@ -71,6 +71,7 @@
explicit Result(Handle<Object> value) {
value_ = TypeField::encode(CONSTANT)
| NumberInfoField::encode(NumberInfo::Uninitialized().ToInt())
+ | IsUntaggedInt32Field::encode(false)
| DataField::encode(ConstantList()->length());
ConstantList()->Add(value);
}
@@ -111,6 +112,19 @@
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
bool is_constant() const { return type() == CONSTANT; }
+
+ // An untagged int32 Result contains a signed int32 in a register
+ // or as a constant. These are only allowed in a side-effect-free
+ // int32 calculation, and if a non-int32 input shows up or an overflow
+ // occurs, we bail out and drop all the int32 values. Constants are
+ // not converted to int32 until they are loaded into a register.
+ bool is_untagged_int32() const {
+ return IsUntaggedInt32Field::decode(value_);
+ }
+ void set_untagged_int32(bool value) {
+ value_ &= ~IsUntaggedInt32Field::mask();
+ value_ |= IsUntaggedInt32Field::encode(value);
+ }
Register reg() const {
ASSERT(is_register());
@@ -140,7 +154,8 @@
class TypeField: public BitField<Type, 0, 2> {};
class NumberInfoField : public BitField<int, 2, 4> {};
- class DataField: public BitField<uint32_t, 6, 32 - 6> {};
+ class IsUntaggedInt32Field : public BitField<bool, 6, 1> {};
+ class DataField: public BitField<uint32_t, 7, 32 - 7> {};
inline void CopyTo(Result* destination) const;
=======================================
--- /branches/bleeding_edge/src/rewriter.cc Mon Mar 15 07:03:36 2010
+++ /branches/bleeding_edge/src/rewriter.cc Tue Mar 16 09:07:19 2010
@@ -266,7 +266,14 @@
func_name_inferrer_.PushName(lit_str);
}
} else if (literal->IsHeapNumber()) {
- node->set_side_effect_free(true);
+ if (node->to_int32()) {
+ // Any HeapNumber has an int32 value if it is the input to a bit op.
+ node->set_side_effect_free(true);
+ } else {
+ double double_value = HeapNumber::cast(*literal)->value();
+ int32_t int32_value = DoubleToInt32(double_value);
+ node->set_side_effect_free(double_value == int32_value);
+ }
}
}
@@ -320,6 +327,7 @@
node->type()->SetAsLikelySmiIfUnknown();
node->target()->type()->SetAsLikelySmiIfUnknown();
node->value()->type()->SetAsLikelySmiIfUnknown();
+ node->value()->set_to_int32(true);
node->value()->set_no_negative_zero(true);
break;
case Token::ASSIGN_ADD:
@@ -438,9 +446,9 @@
// Fall through.
case Token::ADD:
case Token::SUB:
- case Token::NOT:
node->set_side_effect_free(node->expression()->side_effect_free());
break;
+ case Token::NOT:
case Token::DELETE:
case Token::TYPEOF:
case Token::VOID:
@@ -553,6 +561,9 @@
case Token::SHL:
case Token::SAR:
case Token::SHR:
+ // Add one to the number of bit operations in this expression.
+ node->set_num_bit_ops(1);
+ // Fall through.
case Token::ADD:
case Token::SUB:
case Token::MUL:
@@ -560,6 +571,12 @@
case Token::MOD:
node->set_side_effect_free(node->left()->side_effect_free() &&
node->right()->side_effect_free());
+ node->set_num_bit_ops(node->num_bit_ops() +
+ node->left()->num_bit_ops() +
+ node->right()->num_bit_ops());
+ if (!node->no_negative_zero() && node->op() == Token::MUL) {
+ node->set_side_effect_free(false);
+ }
break;
default:
UNREACHABLE();
=======================================
--- /branches/bleeding_edge/src/virtual-frame.cc Fri Mar 5 15:54:13 2010
+++ /branches/bleeding_edge/src/virtual-frame.cc Tue Mar 16 09:07:19 2010
@@ -163,6 +163,9 @@
if (elements_[index].is_copied()) {
new_element.set_copied();
}
+ if (elements_[index].is_untagged_int32()) {
+ new_element.set_untagged_int32(true);
+ }
if (elements_[index].is_register()) {
Unuse(elements_[index].reg());
}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev