Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(144)

Unified Diff: src/x64/codegen-x64.cc

Issue 146081: X64: Implemented LikelySmiBinaryOperation (Closed)
Patch Set: Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/x64/codegen-x64.cc
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index bf454e21494f1a062b5cae21521d4aaf012f7849..bfa3911a4378cb67507f27c9bc807294239e2ce1 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -37,6 +37,7 @@
#include "parser.h"
#include "register-allocator-inl.h"
#include "scopes.h"
+#include "macro-assembler.h"
William Hesse 2009/06/24 11:39:35 Surely this was already included through a depende
// TODO(X64): Remove compiler.h when compiler test is removed.
#include "compiler.h"
@@ -1336,7 +1337,6 @@ void CodeGenerator::VisitCall(Call* node) {
// Call the function.
CallWithArguments(args, node->position());
}
-
} else {
// ----------------------------------
// JavaScript example: 'foo(1, 2, 3)' // foo is not global
@@ -1404,12 +1404,10 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
// Prepare stack for calling JS runtime function.
frame_->Push(node->name());
// Push the builtins object found in the current global object.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ movq(temp.reg(), GlobalObject());
- __ movq(temp.reg(),
- FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset));
- frame_->Push(&temp);
+ __ movq(kScratchRegister, GlobalObject());
+ __ movq(kScratchRegister,
+ FieldOperand(kScratchRegister, GlobalObject::kBuiltinsOffset));
+ frame_->Push(kScratchRegister);
William Hesse 2009/06/24 11:39:35 This must be reversed again - we cannot push kScra
}
// Push the arguments ("left-to-right").
@@ -2297,6 +2295,37 @@ class GenericBinaryOpStub: public CodeStub {
};
+class DeferredInlineBinaryOperation: public DeferredCode {
+ public:
+ DeferredInlineBinaryOperation(Token::Value op,
+ Register dst,
+ Register left,
+ Register right,
+ OverwriteMode mode)
+ : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
+ set_comment("[ DeferredInlineBinaryOperation");
+ }
+
+ virtual void Generate();
+
+ private:
+ Token::Value op_;
+ Register dst_;
+ Register left_;
+ Register right_;
+ OverwriteMode mode_;
+};
+
+
+void DeferredInlineBinaryOperation::Generate() {
+ __ push(left_);
+ __ push(right_);
+ GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED);
+ __ CallStub(&stub);
+ if (!dst_.is(rax)) __ movq(dst_, rax);
+}
+
+
void CodeGenerator::GenericBinaryOperation(Token::Value op,
SmiAnalysis* type,
OverwriteMode overwrite_mode) {
@@ -2623,6 +2652,328 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
ASSERT(!operand->is_valid());
}
+void CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
+ Result* left,
+ Result* right,
+ OverwriteMode overwrite_mode) {
+ // Special handling of div and mod because they use fixed registers.
+ if (op == Token::DIV || op == Token::MOD) {
+ // We need rax as the quotient register, rdx as the remainder
+ // register, neither left nor right in rax or rdx, and left copied
+ // to rax.
+ Result quotient;
+ Result remainder;
+ bool left_is_in_rax = false;
+ // Step 1: get rax for quotient.
+ if ((left->is_register() && left->reg().is(rax)) ||
+ (right->is_register() && right->reg().is(rax))) {
+ // One or both is in rax. Use a fresh non-rdx register for
+ // them.
+ Result fresh = allocator_->Allocate();
+ ASSERT(fresh.is_valid());
+ if (fresh.reg().is(rdx)) {
+ remainder = fresh;
+ fresh = allocator_->Allocate();
+ ASSERT(fresh.is_valid());
+ }
+ if (left->is_register() && left->reg().is(rax)) {
+ quotient = *left;
+ *left = fresh;
+ left_is_in_rax = true;
+ }
+ if (right->is_register() && right->reg().is(rax)) {
+ quotient = *right;
+ *right = fresh;
+ }
+ __ movq(fresh.reg(), rax);
+ } else {
+ // Neither left nor right is in rax.
+ quotient = allocator_->Allocate(rax);
+ }
+ ASSERT(quotient.is_register() && quotient.reg().is(rax));
+ ASSERT(!(left->is_register() && left->reg().is(rax)));
+ ASSERT(!(right->is_register() && right->reg().is(rax)));
+
+ // Step 2: get rdx for remainder if necessary.
+ if (!remainder.is_valid()) {
+ if ((left->is_register() && left->reg().is(rdx)) ||
+ (right->is_register() && right->reg().is(rdx))) {
+ Result fresh = allocator_->Allocate();
+ ASSERT(fresh.is_valid());
+ if (left->is_register() && left->reg().is(rdx)) {
+ remainder = *left;
+ *left = fresh;
+ }
+ if (right->is_register() && right->reg().is(rdx)) {
+ remainder = *right;
+ *right = fresh;
+ }
+ __ movq(fresh.reg(), rdx);
+ } else {
+ // Neither left nor right is in rdx.
+ remainder = allocator_->Allocate(rdx);
+ }
+ }
+ ASSERT(remainder.is_register() && remainder.reg().is(rdx));
+ ASSERT(!(left->is_register() && left->reg().is(rdx)));
+ ASSERT(!(right->is_register() && right->reg().is(rdx)));
+
+ left->ToRegister();
+ right->ToRegister();
+ frame_->Spill(rax);
+ frame_->Spill(rdx);
+
+ // Check that left and right are smi tagged.
+ DeferredInlineBinaryOperation* deferred =
+ new DeferredInlineBinaryOperation(op,
+ (op == Token::DIV) ? rax : rdx,
+ left->reg(),
+ right->reg(),
+ overwrite_mode);
+ if (left->reg().is(right->reg())) {
+ __ testl(left->reg(), Immediate(kSmiTagMask));
+ } else {
+ // Use the quotient register as a scratch for the tag check.
+ if (!left_is_in_rax) __ movq(rax, left->reg());
+ left_is_in_rax = false; // About to destroy the value in rax.
+ __ or_(rax, right->reg());
+ ASSERT(kSmiTag == 0); // Adjust test if not the case.
+ __ testl(rax, Immediate(kSmiTagMask));
+ }
+ deferred->Branch(not_zero);
+
+ if (!left_is_in_rax) __ movq(rax, left->reg());
+ // Sign extend rax into rdx:rax.
+ __ cqo();
+ // Check for 0 divisor.
+ __ testq(right->reg(), right->reg());
+ deferred->Branch(zero);
+ // Divide rdx:rax by the right operand.
+ __ idiv(right->reg());
+
+ // Complete the operation.
+ if (op == Token::DIV) {
+ // Check for negative zero result. If result is zero, and divisor
+ // is negative, return a floating point negative zero. The
+ // virtual frame is unchanged in this block, so local control flow
+ // can use a Label rather than a JumpTarget.
+ Label non_zero_result;
+ __ testq(left->reg(), left->reg());
+ __ j(not_zero, &non_zero_result);
+ __ testq(right->reg(), right->reg());
+ deferred->Branch(negative);
+ __ bind(&non_zero_result);
+ // Check for the corner case of dividing the most negative smi by
+ // -1. We cannot use the overflow flag, since it is not set by
+ // idiv instruction.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ cmpq(rax, Immediate(0x40000000));
+ deferred->Branch(equal);
+ // Check that the remainder is zero.
+ __ testq(rdx, rdx);
+ deferred->Branch(not_zero);
+ // Tag the result and store it in the quotient register.
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case
+ __ lea(rax, Operand(rax, rax, times_1, kSmiTag));
+ deferred->BindExit();
+ left->Unuse();
+ right->Unuse();
+ frame_->Push(&quotient);
+ } else {
+ ASSERT(op == Token::MOD);
+ // Check for a negative zero result. If the result is zero, and
+ // the dividend is negative, return a floating point negative
+ // zero. The frame is unchanged in this block, so local control
+ // flow can use a Label rather than a JumpTarget.
+ Label non_zero_result;
+ __ testq(rdx, rdx);
+ __ j(not_zero, &non_zero_result);
+ __ testq(left->reg(), left->reg());
+ deferred->Branch(negative);
+ __ bind(&non_zero_result);
+ deferred->BindExit();
+ left->Unuse();
+ right->Unuse();
+ frame_->Push(&remainder);
+ }
+ return;
+ }
+
+ // Special handling of shift operations because they use fixed
+ // registers.
+ if (op == Token::SHL || op == Token::SHR || op == Token::SAR) {
+ // Move left out of rcx if necessary.
+ if (left->is_register() && left->reg().is(rcx)) {
+ *left = allocator_->Allocate();
+ ASSERT(left->is_valid());
+ __ movq(left->reg(), rcx);
+ }
+ right->ToRegister(rcx);
+ left->ToRegister();
+ ASSERT(left->is_register() && !left->reg().is(rcx));
+ ASSERT(right->is_register() && right->reg().is(rcx));
+
+ // We will modify right, it must be spilled.
+ frame_->Spill(rcx);
+
+ // Use a fresh answer register to avoid spilling the left operand.
+ Result answer = allocator_->Allocate();
+ ASSERT(answer.is_valid());
+ // Check that both operands are smis using the answer register as a
+ // temporary.
+ DeferredInlineBinaryOperation* deferred =
+ new DeferredInlineBinaryOperation(op,
+ answer.reg(),
+ left->reg(),
+ rcx,
+ overwrite_mode);
+ __ movq(answer.reg(), left->reg());
+ __ or_(answer.reg(), rcx);
+ __ testl(answer.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+
+ // Untag both operands.
+ __ movq(answer.reg(), left->reg());
+ __ sar(answer.reg(), Immediate(kSmiTagSize));
+ __ sar(rcx, Immediate(kSmiTagSize));
+ // Perform the operation.
+ switch (op) {
+ case Token::SAR:
+ __ sar(answer.reg());
+ // No checks of result necessary
+ break;
+ case Token::SHR: {
+ Label result_ok;
+ __ shr(answer.reg());
+ // Check that the *unsigned* result fits in a smi. Neither of
+ // the two high-order bits can be set:
+ // * 0x80000000: high bit would be lost when smi tagging.
+ // * 0x40000000: this number would convert to negative when smi
+ // tagging.
+ // These two cases can only happen with shifts by 0 or 1 when
+ // handed a valid smi. If the answer cannot be represented by a
+ // smi, restore the left and right arguments, and jump to slow
+ // case. The low bit of the left argument may be lost, but only
+ // in a case where it is dropped anyway.
+ __ testl(answer.reg(), Immediate(0xc0000000));
+ __ j(zero, &result_ok);
+ ASSERT(kSmiTag == 0);
+ __ shl(rcx, Immediate(kSmiTagSize));
+ deferred->Jump();
+ __ bind(&result_ok);
+ break;
+ }
+ case Token::SHL: {
+ Label result_ok;
+ __ shl(answer.reg());
+ // Check that the *signed* result fits in a smi.
+ __ cmpq(answer.reg(), Immediate(0xc0000000));
+ __ j(positive, &result_ok);
+ ASSERT(kSmiTag == 0);
+ __ shl(rcx, Immediate(kSmiTagSize));
+ deferred->Jump();
+ __ bind(&result_ok);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ // Smi-tag the result in answer.
+ ASSERT(kSmiTagSize == 1); // Adjust code if not the case.
+ __ lea(answer.reg(),
+ Operand(answer.reg(), answer.reg(), times_1, kSmiTag));
+ deferred->BindExit();
+ left->Unuse();
+ right->Unuse();
+ frame_->Push(&answer);
+ return;
+ }
+
+ // Handle the other binary operations.
+ left->ToRegister();
+ right->ToRegister();
+ // A newly allocated register answer is used to hold the answer. The
+ // registers containing left and right are not modified so they don't
+ // need to be spilled in the fast case.
+ Result answer = allocator_->Allocate();
+ ASSERT(answer.is_valid());
+
+ // Perform the smi tag check.
+ DeferredInlineBinaryOperation* deferred =
+ new DeferredInlineBinaryOperation(op,
+ answer.reg(),
+ left->reg(),
+ right->reg(),
+ overwrite_mode);
+ if (left->reg().is(right->reg())) {
+ __ testl(left->reg(), Immediate(kSmiTagMask));
+ } else {
+ __ movq(answer.reg(), left->reg());
+ __ or_(answer.reg(), right->reg());
+ ASSERT(kSmiTag == 0); // Adjust test if not the case.
+ __ testl(answer.reg(), Immediate(kSmiTagMask));
+ }
+ deferred->Branch(not_zero);
+ __ movq(answer.reg(), left->reg());
+ switch (op) {
+ case Token::ADD:
+ __ addq(answer.reg(), right->reg()); // Add optimistically.
William Hesse 2009/06/24 11:39:35 I think we want addl here.
+ deferred->Branch(overflow);
+ break;
+
+ case Token::SUB:
+ __ subq(answer.reg(), right->reg()); // Subtract optimistically.
William Hesse 2009/06/24 11:39:35 subl
+ deferred->Branch(overflow);
+ break;
+
+ case Token::MUL: {
+ // If the smi tag is 0 we can just leave the tag on one operand.
+ ASSERT(kSmiTag == 0); // Adjust code below if not the case.
+ // Remove smi tag from the left operand (but keep sign).
+ // Left-hand operand has been copied into answer.
+ __ sar(answer.reg(), Immediate(kSmiTagSize));
+ // Do multiplication of smis, leaving result in answer.
+ __ imull(answer.reg(), right->reg());
+ // Go slow on overflows.
+ deferred->Branch(overflow);
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case. The frame is unchanged
+ // in this block, so local control flow can use a Label rather
+ // than a JumpTarget.
+ Label non_zero_result;
+ __ testq(answer.reg(), answer.reg());
+ __ j(not_zero, &non_zero_result);
+ __ movq(answer.reg(), left->reg());
+ __ or_(answer.reg(), right->reg());
+ deferred->Branch(negative);
+ __ xor_(answer.reg(), answer.reg()); // Positive 0 is correct.
+ __ bind(&non_zero_result);
+ break;
+ }
+
+ case Token::BIT_OR:
+ __ or_(answer.reg(), right->reg());
+ break;
+
+ case Token::BIT_AND:
+ __ and_(answer.reg(), right->reg());
+ break;
+
+ case Token::BIT_XOR:
+ __ xor_(answer.reg(), right->reg());
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+ deferred->BindExit();
+ left->Unuse();
+ right->Unuse();
+ frame_->Push(&answer);
+}
+
+
#undef __
#define __ ACCESS_MASM(masm)
@@ -2683,9 +3034,9 @@ void Reference::GetValue(TypeofState typeof_state) {
? RelocInfo::CODE_TARGET_CONTEXT
: RelocInfo::CODE_TARGET;
Result answer = cgen_->frame()->CallLoadIC(mode);
- // A test eax instruction following the call signals that the
+ // A test rax instruction following the call signals that the
// inobject property case was inlined. Ensure that there is not
- // a test eax instruction here.
+ // a test rax instruction here.
__ nop();
cgen_->frame()->Push(&answer);
} else {
@@ -2870,18 +3221,92 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
- return false; // UNIMPLEMENTED.
-}
+ // TODO(X64): This method is identical to the ia32 version.
+ // Either find a reason to change it, or move it somewhere where it can be
+ // shared. (Notice: It assumes that a Smi can fit in an int).
+ Object* answer_object = Heap::undefined_value();
+ switch (op) {
+ case Token::ADD:
+ if (Smi::IsValid(left + right)) {
+ answer_object = Smi::FromInt(left + right);
+ }
+ break;
+ case Token::SUB:
+ if (Smi::IsValid(left - right)) {
+ answer_object = Smi::FromInt(left - right);
+ }
+ break;
+ case Token::MUL: {
+ double answer = static_cast<double>(left) * right;
+ if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
+ // If the product is zero and the non-zero factor is negative,
+ // the spec requires us to return floating point negative zero.
+ if (answer != 0 || (left >= 0 && right >= 0)) {
+ answer_object = Smi::FromInt(static_cast<int>(answer));
+ }
+ }
+ }
+ break;
+ case Token::DIV:
+ case Token::MOD:
+ break;
+ case Token::BIT_OR:
+ answer_object = Smi::FromInt(left | right);
+ break;
+ case Token::BIT_AND:
+ answer_object = Smi::FromInt(left & right);
+ break;
+ case Token::BIT_XOR:
+ answer_object = Smi::FromInt(left ^ right);
+ break;
-void CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
- Result* left,
- Result* right,
- OverwriteMode overwrite_mode) {
- UNIMPLEMENTED();
+ case Token::SHL: {
+ int shift_amount = right & 0x1F;
+ if (Smi::IsValid(left << shift_amount)) {
+ answer_object = Smi::FromInt(left << shift_amount);
+ }
+ break;
+ }
+ case Token::SHR: {
+ int shift_amount = right & 0x1F;
+ unsigned int unsigned_left = left;
+ unsigned_left >>= shift_amount;
+ if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
+ answer_object = Smi::FromInt(unsigned_left);
+ }
+ break;
+ }
+ case Token::SAR: {
+ int shift_amount = right & 0x1F;
+ unsigned int unsigned_left = left;
+ if (left < 0) {
+ // Perform arithmetic shift of a negative number by
+ // complementing number, logical shifting, complementing again.
+ unsigned_left = ~unsigned_left;
+ unsigned_left >>= shift_amount;
+ unsigned_left = ~unsigned_left;
+ } else {
+ unsigned_left >>= shift_amount;
+ }
+ ASSERT(Smi::IsValid(unsigned_left)); // Converted to signed.
+ answer_object = Smi::FromInt(unsigned_left); // Converted to signed.
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ if (answer_object == Heap::undefined_value()) {
+ return false;
+ }
+ frame_->Push(Handle<Object>(answer_object));
+ return true;
}
+
+
// End of CodeGenerator implementation.
void UnarySubStub::Generate(MacroAssembler* masm) {
@@ -3569,7 +3994,7 @@ void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
__ bind(&test_other);
__ testl(rax, Immediate(kSmiTagMask));
- __ j(zero, &done); // argument in eax is OK
+ __ j(zero, &done); // argument in rax is OK
__ movq(kScratchRegister,
Factory::heap_number_map(),
RelocInfo::EMBEDDED_OBJECT);
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698