Use untagged int32 values in evaluation of side-effect free expressions.
Review URL: http://codereview.chromium.org/975001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4148 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
9202e05016
commit
4751f36492
11
src/ast.h
11
src/ast.h
@ -269,8 +269,18 @@ class Expression: public AstNode {
|
||||
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 Expression: public AstNode {
|
||||
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> {};
|
||||
};
|
||||
|
||||
|
||||
|
@ -153,7 +153,7 @@ DEFINE_bool(always_fast_compiler, false,
|
||||
"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")
|
||||
|
||||
|
@ -145,6 +145,16 @@ class FrameElement BASE_EMBEDDED {
|
||||
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());
|
||||
uint32_t reg = DataField::decode(value_);
|
||||
@ -255,8 +265,9 @@ class FrameElement BASE_EMBEDDED {
|
||||
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;
|
||||
};
|
||||
|
@ -314,6 +314,12 @@ Operand::Operand(Register reg) {
|
||||
}
|
||||
|
||||
|
||||
Operand::Operand(XMMRegister xmm_reg) {
|
||||
Register reg = { xmm_reg.code() };
|
||||
set_modrm(3, reg);
|
||||
}
|
||||
|
||||
|
||||
Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
|
||||
// [disp/r]
|
||||
set_modrm(0, ebp);
|
||||
|
@ -241,6 +241,9 @@ class Operand BASE_EMBEDDED {
|
||||
// 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 @@ class Assembler : public Malloced {
|
||||
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);
|
||||
|
||||
|
@ -112,6 +112,8 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm)
|
||||
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 @@ Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
|
||||
// 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
|
||||
@ -481,64 +483,157 @@ void CodeGenerator::LoadAndSpill(Expression* expression) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
#ifdef DEBUG
|
||||
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);
|
||||
JumpTarget done;
|
||||
|
||||
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();
|
||||
// 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 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();
|
||||
}
|
||||
|
||||
} 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();
|
||||
}
|
||||
|
||||
} 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();
|
||||
}
|
||||
loaded.Bind();
|
||||
}
|
||||
}
|
||||
|
||||
done.Bind();
|
||||
ASSERT(has_valid_frame());
|
||||
ASSERT(frame_->height() == original_height + 1);
|
||||
}
|
||||
@ -4312,7 +4407,7 @@ Result CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
|
||||
|
||||
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::VisitFunctionLiteral(FunctionLiteral* node) {
|
||||
|
||||
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::VisitFunctionBoilerplateLiteral(
|
||||
|
||||
void CodeGenerator::VisitConditional(Conditional* node) {
|
||||
Comment cmnt(masm_, "[ Conditional");
|
||||
ASSERT(!in_safe_int32_mode());
|
||||
JumpTarget then;
|
||||
JumpTarget else_;
|
||||
JumpTarget exit;
|
||||
@ -4503,6 +4600,7 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
|
||||
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::StoreToSlot(Slot* slot, InitState init_state) {
|
||||
}
|
||||
|
||||
|
||||
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 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
|
||||
Visit(expr);
|
||||
} else {
|
||||
ASSERT(var->is_global());
|
||||
ASSERT(!in_safe_int32_mode());
|
||||
Reference ref(this, node);
|
||||
ref.GetValue();
|
||||
}
|
||||
@ -4694,7 +4803,11 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
|
||||
|
||||
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 DeferredRegExpLiteral::Generate() {
|
||||
|
||||
|
||||
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::VisitRegExpLiteral(RegExpLiteral* node) {
|
||||
|
||||
|
||||
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::VisitObjectLiteral(ObjectLiteral* node) {
|
||||
|
||||
|
||||
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::VisitArrayLiteral(ArrayLiteral* node) {
|
||||
|
||||
|
||||
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::EmitKeyedPropertyAssignment(Assignment* node) {
|
||||
|
||||
|
||||
void CodeGenerator::VisitAssignment(Assignment* node) {
|
||||
ASSERT(!in_safe_int32_mode());
|
||||
#ifdef DEBUG
|
||||
int original_height = frame()->height();
|
||||
#endif
|
||||
@ -5213,6 +5331,7 @@ void CodeGenerator::VisitAssignment(Assignment* node) {
|
||||
|
||||
|
||||
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::VisitThrow(Throw* node) {
|
||||
|
||||
|
||||
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::VisitProperty(Property* node) {
|
||||
|
||||
|
||||
void CodeGenerator::VisitCall(Call* node) {
|
||||
ASSERT(!in_safe_int32_mode());
|
||||
Comment cmnt(masm_, "[ Call");
|
||||
|
||||
Expression* function = node->expression();
|
||||
@ -5443,6 +5564,7 @@ void CodeGenerator::VisitCall(Call* node) {
|
||||
|
||||
|
||||
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::GenerateMathSqrt(ZoneList<Expression*>* args) {
|
||||
|
||||
|
||||
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
|
||||
ASSERT(!in_safe_int32_mode());
|
||||
if (CheckForInlineRuntimeCall(node)) {
|
||||
return;
|
||||
}
|
||||
@ -6496,64 +6619,100 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
|
||||
}
|
||||
|
||||
} 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;
|
||||
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);
|
||||
|
||||
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);
|
||||
} 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;
|
||||
}
|
||||
|
||||
GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
|
||||
Result answer = frame_->CallStub(&stub, &operand);
|
||||
continue_label.Jump(&answer);
|
||||
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);
|
||||
|
||||
smi_label.Bind(&answer);
|
||||
answer.ToRegister();
|
||||
frame_->Spill(answer.reg());
|
||||
__ not_(answer.reg());
|
||||
__ and_(answer.reg(), ~kSmiTagMask); // Remove inverted smi-tag.
|
||||
GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
|
||||
Result answer = frame_->CallStub(&stub, &operand);
|
||||
continue_label.Jump(&answer);
|
||||
|
||||
continue_label.Bind(&answer);
|
||||
frame_->Push(&answer);
|
||||
break;
|
||||
}
|
||||
smi_label.Bind(&answer);
|
||||
answer.ToRegister();
|
||||
frame_->Spill(answer.reg());
|
||||
__ not_(answer.reg());
|
||||
__ and_(answer.reg(), ~kSmiTagMask); // Remove inverted smi-tag.
|
||||
|
||||
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);
|
||||
continue_label.Bind(&answer);
|
||||
frame_->Push(&answer);
|
||||
break;
|
||||
}
|
||||
|
||||
frame_->Push(&operand);
|
||||
Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
|
||||
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;
|
||||
}
|
||||
continue_label.Bind(&answer);
|
||||
frame_->Push(&answer);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// NOT, DELETE, TYPEOF, and VOID are handled outside the
|
||||
// switch.
|
||||
UNREACHABLE();
|
||||
default:
|
||||
// NOT, DELETE, TYPEOF, and VOID are handled outside the
|
||||
// switch.
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6646,6 +6805,7 @@ void DeferredPostfixCountOperation::Generate() {
|
||||
|
||||
|
||||
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::VisitCountOperation(CountOperation* node) {
|
||||
}
|
||||
|
||||
|
||||
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 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
|
||||
// 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 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
|
||||
}
|
||||
|
||||
} 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 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
|
||||
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::VisitBinaryOperation(BinaryOperation* node) {
|
||||
|
||||
|
||||
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;
|
||||
|
@ -357,6 +357,23 @@ class CodeGenerator: public AstVisitor {
|
||||
// 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_; }
|
||||
void IncrementLoopNesting() { loop_nesting_++; }
|
||||
@ -413,7 +430,7 @@ class CodeGenerator: public AstVisitor {
|
||||
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 @@ class CodeGenerator: public AstVisitor {
|
||||
// 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 @@ class CodeGenerator: public AstVisitor {
|
||||
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 @@ class CodeGenerator: public AstVisitor {
|
||||
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
|
||||
|
@ -42,7 +42,33 @@ void Result::ToRegister() {
|
||||
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 @@ void Result::ToRegister(Register target) {
|
||||
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());
|
||||
|
@ -775,6 +775,89 @@ void VirtualFrame::StoreToFrameSlotAt(int index) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
ASSERT(cgen()->HasValidEntryRegisters());
|
||||
// Grow the expression stack by handler size less one (the return
|
||||
@ -1060,6 +1143,7 @@ Result VirtualFrame::Pop() {
|
||||
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 @@ Result VirtualFrame::Pop() {
|
||||
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 @@ Result VirtualFrame::Pop() {
|
||||
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 @@ Result VirtualFrame::Pop() {
|
||||
// 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 @@ Result VirtualFrame::Pop() {
|
||||
__ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1161,6 +1252,12 @@ void VirtualFrame::EmitPush(Immediate immediate, NumberInfo info) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
ASSERT(expr->IsTrivial());
|
||||
|
||||
|
@ -242,6 +242,11 @@ class VirtualFrame: public ZoneObject {
|
||||
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
|
||||
// from it again.
|
||||
@ -282,6 +287,11 @@ class VirtualFrame: public ZoneObject {
|
||||
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
|
||||
// trying to read from it again.
|
||||
@ -399,6 +409,8 @@ class VirtualFrame: public ZoneObject {
|
||||
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 @@ class VirtualFrame: public ZoneObject {
|
||||
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();
|
||||
}
|
||||
|
||||
@ -422,6 +438,14 @@ class VirtualFrame: public ZoneObject {
|
||||
// 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 @@ class VirtualFrame: public ZoneObject {
|
||||
// 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);
|
||||
|
@ -71,6 +71,7 @@ class Result BASE_EMBEDDED {
|
||||
explicit Result(Handle<Object> value) {
|
||||
value_ = TypeField::encode(CONSTANT)
|
||||
| NumberInfoField::encode(NumberInfo::Uninitialized().ToInt())
|
||||
| IsUntaggedInt32Field::encode(false)
|
||||
| DataField::encode(ConstantList()->length());
|
||||
ConstantList()->Add(value);
|
||||
}
|
||||
@ -112,6 +113,19 @@ class Result BASE_EMBEDDED {
|
||||
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());
|
||||
uint32_t reg = DataField::decode(value_);
|
||||
@ -140,7 +154,8 @@ class Result BASE_EMBEDDED {
|
||||
|
||||
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;
|
||||
|
||||
|
@ -266,7 +266,14 @@ void AstOptimizer::VisitLiteral(Literal* node) {
|
||||
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 @@ void AstOptimizer::VisitAssignment(Assignment* node) {
|
||||
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 @@ void AstOptimizer::VisitUnaryOperation(UnaryOperation* node) {
|
||||
// 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 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
|
||||
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 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
|
||||
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();
|
||||
|
@ -163,6 +163,9 @@ void VirtualFrame::SpillElementAt(int index) {
|
||||
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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user