Commit 4751f364 authored by whesse@chromium.org's avatar whesse@chromium.org

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
parent 9202e050
......@@ -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);
......
This diff is collapsed.
......@@ -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, &not_smi);
__ SmiUntag(fresh_reg);
__ jmp(&done);
__ bind(&not_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());
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment