| Index: src/x64/codegen-x64.cc
|
| diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
|
| index e4cb00baa2454ec63f3b0be26f748857286c5e76..512e3349716781a5c385b101e7aaad647ed72c68 100644
|
| --- a/src/x64/codegen-x64.cc
|
| +++ b/src/x64/codegen-x64.cc
|
| @@ -143,9 +143,9 @@ void CodeGenerator::TestCodeGenerator() {
|
| "(function(){"
|
| " function test_if_then_else(x, y, z){"
|
| " if (x) {"
|
| - " x = y;"
|
| + " x = y + 2;"
|
| " } else {"
|
| - " x = z;"
|
| + " x = z + 2;"
|
| " }"
|
| " return x;"
|
| " }"
|
| @@ -211,7 +211,7 @@ void CodeGenerator::TestCodeGenerator() {
|
| &pending_exceptions);
|
| // Function compiles and runs, but returns a JSFunction object.
|
| CHECK(result->IsSmi());
|
| - CHECK_EQ(47, Smi::cast(*result)->value());
|
| + CHECK_EQ(49, Smi::cast(*result)->value());
|
| }
|
|
|
|
|
| @@ -1352,10 +1352,170 @@ void CodeGenerator::VisitCountOperation(CountOperation* a) {
|
| UNIMPLEMENTED();
|
| }
|
|
|
| -void CodeGenerator::VisitBinaryOperation(BinaryOperation* a) {
|
| - UNIMPLEMENTED();
|
| +void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
|
| + // TODO(X64): This code was copied verbatim from codegen-ia32.
|
| + // Either find a reason to change it or move it to a shared location.
|
| +
|
| + // Note that due to an optimization in comparison operations (typeof
|
| + // compared to a string literal), we can evaluate a binary expression such
|
| + // as AND or OR and not leave a value on the frame or in the cc register.
|
| + Comment cmnt(masm_, "[ BinaryOperation");
|
| + Token::Value op = node->op();
|
| +
|
| + // According to ECMA-262 section 11.11, page 58, the binary logical
|
| + // operators must yield the result of one of the two expressions
|
| + // before any ToBoolean() conversions. This means that the value
|
| + // produced by a && or || operator is not necessarily a boolean.
|
| +
|
| + // NOTE: If the left hand side produces a materialized value (not
|
| + // control flow), we force the right hand side to do the same. This
|
| + // 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) {
|
| + JumpTarget is_true;
|
| + ControlDestination dest(&is_true, destination()->false_target(), true);
|
| + LoadCondition(node->left(), NOT_INSIDE_TYPEOF, &dest, false);
|
| +
|
| + if (dest.false_was_fall_through()) {
|
| + // The current false target was used as the fall-through. If
|
| + // there are no dangling jumps to is_true then the left
|
| + // subexpression was unconditionally false. Otherwise we have
|
| + // paths where we do have to evaluate the right subexpression.
|
| + if (is_true.is_linked()) {
|
| + // We need to compile the right subexpression. If the jump to
|
| + // the current false target was a forward jump then we have a
|
| + // valid frame, we have just bound the false target, and we
|
| + // have to jump around the code for the right subexpression.
|
| + if (has_valid_frame()) {
|
| + destination()->false_target()->Unuse();
|
| + destination()->false_target()->Jump();
|
| + }
|
| + is_true.Bind();
|
| + // The left subexpression compiled to control flow, so the
|
| + // right one is free to do so as well.
|
| + LoadCondition(node->right(), NOT_INSIDE_TYPEOF, destination(), false);
|
| + } else {
|
| + // We have actually just jumped to or bound the current false
|
| + // target but the current control destination is not marked as
|
| + // used.
|
| + destination()->Use(false);
|
| + }
|
| +
|
| + } else if (dest.is_used()) {
|
| + // The left subexpression compiled to control flow (and is_true
|
| + // was just bound), so the right is free to do so as well.
|
| + LoadCondition(node->right(), NOT_INSIDE_TYPEOF, destination(), false);
|
| +
|
| + } else {
|
| + // We have a materialized value on the frame, so we exit with
|
| + // one on all paths. There are possibly also jumps to is_true
|
| + // from nested subexpressions.
|
| + JumpTarget pop_and_continue;
|
| + JumpTarget exit;
|
| +
|
| + // Avoid popping the result if it converts to 'false' using the
|
| + // standard ToBoolean() conversion as described in ECMA-262,
|
| + // section 9.2, page 30.
|
| + //
|
| + // Duplicate the TOS value. The duplicate will be popped by
|
| + // ToBoolean.
|
| + frame_->Dup();
|
| + ControlDestination dest(&pop_and_continue, &exit, true);
|
| + ToBoolean(&dest);
|
| +
|
| + // Pop the result of evaluating the first part.
|
| + frame_->Drop();
|
| +
|
| + // Compile right side expression.
|
| + is_true.Bind();
|
| + Load(node->right());
|
| +
|
| + // Exit (always with a materialized value).
|
| + exit.Bind();
|
| + }
|
| +
|
| + } else if (op == Token::OR) {
|
| + JumpTarget is_false;
|
| + ControlDestination dest(destination()->true_target(), &is_false, false);
|
| + LoadCondition(node->left(), NOT_INSIDE_TYPEOF, &dest, false);
|
| +
|
| + if (dest.true_was_fall_through()) {
|
| + // The current true target was used as the fall-through. If
|
| + // there are no dangling jumps to is_false then the left
|
| + // subexpression was unconditionally true. Otherwise we have
|
| + // paths where we do have to evaluate the right subexpression.
|
| + if (is_false.is_linked()) {
|
| + // We need to compile the right subexpression. If the jump to
|
| + // the current true target was a forward jump then we have a
|
| + // valid frame, we have just bound the true target, and we
|
| + // have to jump around the code for the right subexpression.
|
| + if (has_valid_frame()) {
|
| + destination()->true_target()->Unuse();
|
| + destination()->true_target()->Jump();
|
| + }
|
| + is_false.Bind();
|
| + // The left subexpression compiled to control flow, so the
|
| + // right one is free to do so as well.
|
| + LoadCondition(node->right(), NOT_INSIDE_TYPEOF, destination(), false);
|
| + } else {
|
| + // We have just jumped to or bound the current true target but
|
| + // the current control destination is not marked as used.
|
| + destination()->Use(true);
|
| + }
|
| +
|
| + } else if (dest.is_used()) {
|
| + // The left subexpression compiled to control flow (and is_false
|
| + // was just bound), so the right is free to do so as well.
|
| + LoadCondition(node->right(), NOT_INSIDE_TYPEOF, destination(), false);
|
| +
|
| + } else {
|
| + // We have a materialized value on the frame, so we exit with
|
| + // one on all paths. There are possibly also jumps to is_false
|
| + // from nested subexpressions.
|
| + JumpTarget pop_and_continue;
|
| + JumpTarget exit;
|
| +
|
| + // Avoid popping the result if it converts to 'true' using the
|
| + // standard ToBoolean() conversion as described in ECMA-262,
|
| + // section 9.2, page 30.
|
| + //
|
| + // Duplicate the TOS value. The duplicate will be popped by
|
| + // ToBoolean.
|
| + frame_->Dup();
|
| + ControlDestination dest(&exit, &pop_and_continue, false);
|
| + ToBoolean(&dest);
|
| +
|
| + // Pop the result of evaluating the first part.
|
| + frame_->Drop();
|
| +
|
| + // Compile right side expression.
|
| + is_false.Bind();
|
| + Load(node->right());
|
| +
|
| + // Exit (always with a materialized value).
|
| + exit.Bind();
|
| + }
|
| +
|
| + } else {
|
| + // NOTE: The code below assumes that the slow cases (calls to runtime)
|
| + // never return a constant/immutable object.
|
| + OverwriteMode overwrite_mode = NO_OVERWRITE;
|
| + if (node->left()->AsBinaryOperation() != NULL &&
|
| + node->left()->AsBinaryOperation()->ResultOverwriteAllowed()) {
|
| + overwrite_mode = OVERWRITE_LEFT;
|
| + } else if (node->right()->AsBinaryOperation() != NULL &&
|
| + node->right()->AsBinaryOperation()->ResultOverwriteAllowed()) {
|
| + overwrite_mode = OVERWRITE_RIGHT;
|
| + }
|
| +
|
| + Load(node->left());
|
| + Load(node->right());
|
| + GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
|
| + }
|
| }
|
|
|
| +
|
| +
|
| void CodeGenerator::VisitCompareOperation(CompareOperation* a) {
|
| UNIMPLEMENTED();
|
| }
|
| @@ -1947,6 +2107,216 @@ void CodeGenerator::LoadGlobalReceiver() {
|
| frame_->Push(&temp);
|
| }
|
|
|
| +
|
| +// Flag that indicates whether or not the code that handles smi arguments
|
| +// should be placed in the stub, inlined, or omitted entirely.
|
| +enum GenericBinaryFlags {
|
| + SMI_CODE_IN_STUB,
|
| + SMI_CODE_INLINED
|
| +};
|
| +
|
| +
|
| +class FloatingPointHelper : public AllStatic {
|
| + public:
|
| + // Code pattern for loading a floating point value. Input value must
|
| + // be either a smi or a heap number object (fp value). Requirements:
|
| + // operand in src register. Returns operand as floating point number
|
| + // in XMM register
|
| + static void LoadFloatOperand(MacroAssembler* masm,
|
| + Register src,
|
| + XMMRegister dst);
|
| + // Code pattern for loading floating point values. Input values must
|
| + // be either smi or heap number objects (fp values). Requirements:
|
| + // operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as
|
| + // floating point numbers in XMM registers.
|
| + static void LoadFloatOperands(MacroAssembler* masm,
|
| + XMMRegister dst1,
|
| + XMMRegister dst2);
|
| +
|
| + // Code pattern for loading floating point values onto the fp stack.
|
| + // Input values must be either smi or heap number objects (fp values).
|
| + // Requirements:
|
| + // operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as
|
| + // floating point numbers on fp stack.
|
| + static void LoadFloatOperands(MacroAssembler* masm);
|
| +
|
| + // Code pattern for loading a floating point value and converting it
|
| + // to a 32 bit integer. Input value must be either a smi or a heap number
|
| + // object.
|
| + // Returns operands as 32-bit sign extended integers in a general purpose
|
| + // registers.
|
| + static void LoadInt32Operand(MacroAssembler* masm,
|
| + const Operand& src,
|
| + Register dst);
|
| +
|
| + // Test if operands are smi or number objects (fp). Requirements:
|
| + // operand_1 in eax, operand_2 in edx; falls through on float
|
| + // operands, jumps to the non_float label otherwise.
|
| + static void CheckFloatOperands(MacroAssembler* masm,
|
| + Label* non_float);
|
| + // Allocate a heap number in new space with undefined value.
|
| + // Returns tagged pointer in result, or jumps to need_gc if new space is full.
|
| + static void AllocateHeapNumber(MacroAssembler* masm,
|
| + Label* need_gc,
|
| + Register scratch,
|
| + Register result);
|
| +};
|
| +
|
| +
|
| +class GenericBinaryOpStub: public CodeStub {
|
| + public:
|
| + GenericBinaryOpStub(Token::Value op,
|
| + OverwriteMode mode,
|
| + GenericBinaryFlags flags)
|
| + : op_(op), mode_(mode), flags_(flags) {
|
| + ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
|
| + }
|
| +
|
| + void GenerateSmiCode(MacroAssembler* masm, Label* slow);
|
| +
|
| + private:
|
| + Token::Value op_;
|
| + OverwriteMode mode_;
|
| + GenericBinaryFlags flags_;
|
| +
|
| + const char* GetName();
|
| +
|
| +#ifdef DEBUG
|
| + void Print() {
|
| + PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
|
| + Token::String(op_),
|
| + static_cast<int>(mode_),
|
| + static_cast<int>(flags_));
|
| + }
|
| +#endif
|
| +
|
| + // Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
|
| + class ModeBits: public BitField<OverwriteMode, 0, 2> {};
|
| + class OpBits: public BitField<Token::Value, 2, 13> {};
|
| + class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
|
| +
|
| + Major MajorKey() { return GenericBinaryOp; }
|
| + int MinorKey() {
|
| + // Encode the parameters in a unique 16 bit value.
|
| + return OpBits::encode(op_)
|
| + | ModeBits::encode(mode_)
|
| + | FlagBits::encode(flags_);
|
| + }
|
| + void Generate(MacroAssembler* masm);
|
| +};
|
| +
|
| +
|
| +void CodeGenerator::GenericBinaryOperation(Token::Value op,
|
| + SmiAnalysis* type,
|
| + OverwriteMode overwrite_mode) {
|
| + Comment cmnt(masm_, "[ BinaryOperation");
|
| + Comment cmnt_token(masm_, Token::String(op));
|
| +
|
| + if (op == Token::COMMA) {
|
| + // Simply discard left value.
|
| + frame_->Nip(1);
|
| + return;
|
| + }
|
| +
|
| + // Set the flags based on the operation, type and loop nesting level.
|
| + GenericBinaryFlags flags;
|
| + switch (op) {
|
| + case Token::BIT_OR:
|
| + case Token::BIT_AND:
|
| + case Token::BIT_XOR:
|
| + case Token::SHL:
|
| + case Token::SHR:
|
| + case Token::SAR:
|
| + // Bit operations always assume they likely operate on Smis. Still only
|
| + // generate the inline Smi check code if this operation is part of a loop.
|
| + flags = (loop_nesting() > 0)
|
| + ? SMI_CODE_INLINED
|
| + : SMI_CODE_IN_STUB;
|
| + break;
|
| +
|
| + default:
|
| + // By default only inline the Smi check code for likely smis if this
|
| + // operation is part of a loop.
|
| + flags = ((loop_nesting() > 0) && type->IsLikelySmi())
|
| + ? SMI_CODE_INLINED
|
| + : SMI_CODE_IN_STUB;
|
| + break;
|
| + }
|
| +
|
| + Result right = frame_->Pop();
|
| + Result left = frame_->Pop();
|
| +
|
| + if (op == Token::ADD) {
|
| + bool left_is_string = left.static_type().is_jsstring();
|
| + bool right_is_string = right.static_type().is_jsstring();
|
| + if (left_is_string || right_is_string) {
|
| + frame_->Push(&left);
|
| + frame_->Push(&right);
|
| + Result answer;
|
| + if (left_is_string) {
|
| + if (right_is_string) {
|
| + // TODO(lrn): if (left.is_constant() && right.is_constant())
|
| + // -- do a compile time cons, if allocation during codegen is allowed.
|
| + answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
|
| + } else {
|
| + answer =
|
| + frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
|
| + }
|
| + } else if (right_is_string) {
|
| + answer =
|
| + frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
|
| + }
|
| + answer.set_static_type(StaticType::jsstring());
|
| + frame_->Push(&answer);
|
| + return;
|
| + }
|
| + // Neither operand is known to be a string.
|
| + }
|
| +
|
| + bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
|
| + bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
|
| + bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
|
| + bool right_is_non_smi = right.is_constant() && !right.handle()->IsSmi();
|
| + bool generate_no_smi_code = false; // No smi code at all, inline or in stub.
|
| +
|
| + if (left_is_smi && right_is_smi) {
|
| + // Compute the constant result at compile time, and leave it on the frame.
|
| + int left_int = Smi::cast(*left.handle())->value();
|
| + int right_int = Smi::cast(*right.handle())->value();
|
| + if (FoldConstantSmis(op, left_int, right_int)) return;
|
| + }
|
| +
|
| + if (left_is_non_smi || right_is_non_smi) {
|
| + // Set flag so that we go straight to the slow case, with no smi code.
|
| + generate_no_smi_code = true;
|
| + } else if (right_is_smi) {
|
| + ConstantSmiBinaryOperation(op, &left, right.handle(),
|
| + type, false, overwrite_mode);
|
| + return;
|
| + } else if (left_is_smi) {
|
| + ConstantSmiBinaryOperation(op, &right, left.handle(),
|
| + type, true, overwrite_mode);
|
| + return;
|
| + }
|
| +
|
| + if (flags == SMI_CODE_INLINED && !generate_no_smi_code) {
|
| + LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
|
| + } else {
|
| + frame_->Push(&left);
|
| + frame_->Push(&right);
|
| + // If we know the arguments aren't smis, use the binary operation stub
|
| + // that does not check for the fast smi case.
|
| + // The same stub is used for NO_SMI_CODE and SMI_CODE_INLINED.
|
| + if (generate_no_smi_code) {
|
| + flags = SMI_CODE_INLINED;
|
| + }
|
| + GenericBinaryOpStub stub(op, overwrite_mode, flags);
|
| + Result answer = frame_->CallStub(&stub, 2);
|
| + frame_->Push(&answer);
|
| + }
|
| +}
|
| +
|
| +
|
| // Emit a LoadIC call to get the value from receiver and leave it in
|
| // dst. The receiver register is restored after the call.
|
| class DeferredReferenceGetNamedValue: public DeferredCode {
|
| @@ -1992,6 +2362,176 @@ void DeferredReferenceGetNamedValue::Generate() {
|
| }
|
|
|
|
|
| +
|
| +
|
| +// The result of src + value is in dst. It either overflowed or was not
|
| +// smi tagged. Undo the speculative addition and call the appropriate
|
| +// specialized stub for add. The result is left in dst.
|
| +class DeferredInlineSmiAdd: public DeferredCode {
|
| + public:
|
| + DeferredInlineSmiAdd(Register dst,
|
| + Smi* value,
|
| + OverwriteMode overwrite_mode)
|
| + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
|
| + set_comment("[ DeferredInlineSmiAdd");
|
| + }
|
| +
|
| + virtual void Generate();
|
| +
|
| + private:
|
| + Register dst_;
|
| + Smi* value_;
|
| + OverwriteMode overwrite_mode_;
|
| +};
|
| +
|
| +
|
| +void DeferredInlineSmiAdd::Generate() {
|
| + // Undo the optimistic add operation and call the shared stub.
|
| + __ subq(dst_, Immediate(value_));
|
| + __ push(dst_);
|
| + __ push(Immediate(value_));
|
| + GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(rax)) __ movq(dst_, rax);
|
| +}
|
| +
|
| +
|
| +// The result of value + src is in dst. It either overflowed or was not
|
| +// smi tagged. Undo the speculative addition and call the appropriate
|
| +// specialized stub for add. The result is left in dst.
|
| +class DeferredInlineSmiAddReversed: public DeferredCode {
|
| + public:
|
| + DeferredInlineSmiAddReversed(Register dst,
|
| + Smi* value,
|
| + OverwriteMode overwrite_mode)
|
| + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
|
| + set_comment("[ DeferredInlineSmiAddReversed");
|
| + }
|
| +
|
| + virtual void Generate();
|
| +
|
| + private:
|
| + Register dst_;
|
| + Smi* value_;
|
| + OverwriteMode overwrite_mode_;
|
| +};
|
| +
|
| +
|
| +void DeferredInlineSmiAddReversed::Generate() {
|
| + // Undo the optimistic add operation and call the shared stub.
|
| + __ subq(dst_, Immediate(value_));
|
| + __ push(Immediate(value_));
|
| + __ push(dst_);
|
| + GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(rax)) __ movq(dst_, rax);
|
| +}
|
| +
|
| +
|
| +// The result of src - value is in dst. It either overflowed or was not
|
| +// smi tagged. Undo the speculative subtraction and call the
|
| +// appropriate specialized stub for subtract. The result is left in
|
| +// dst.
|
| +class DeferredInlineSmiSub: public DeferredCode {
|
| + public:
|
| + DeferredInlineSmiSub(Register dst,
|
| + Smi* value,
|
| + OverwriteMode overwrite_mode)
|
| + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
|
| + set_comment("[ DeferredInlineSmiSub");
|
| + }
|
| +
|
| + virtual void Generate();
|
| +
|
| + private:
|
| + Register dst_;
|
| + Smi* value_;
|
| + OverwriteMode overwrite_mode_;
|
| +};
|
| +
|
| +
|
| +void DeferredInlineSmiSub::Generate() {
|
| + // Undo the optimistic sub operation and call the shared stub.
|
| + __ addq(dst_, Immediate(value_));
|
| + __ push(dst_);
|
| + __ push(Immediate(value_));
|
| + GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(rax)) __ movq(dst_, rax);
|
| +}
|
| +
|
| +
|
| +void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
|
| + Result* operand,
|
| + Handle<Object> value,
|
| + SmiAnalysis* type,
|
| + bool reversed,
|
| + OverwriteMode overwrite_mode) {
|
| + // NOTE: This is an attempt to inline (a bit) more of the code for
|
| + // some possible smi operations (like + and -) when (at least) one
|
| + // of the operands is a constant smi.
|
| + // Consumes the argument "operand".
|
| +
|
| + // TODO(199): Optimize some special cases of operations involving a
|
| + // smi literal (multiply by 2, shift by 0, etc.).
|
| + if (IsUnsafeSmi(value)) {
|
| + Result unsafe_operand(value);
|
| + if (reversed) {
|
| + LikelySmiBinaryOperation(op, &unsafe_operand, operand,
|
| + overwrite_mode);
|
| + } else {
|
| + LikelySmiBinaryOperation(op, operand, &unsafe_operand,
|
| + overwrite_mode);
|
| + }
|
| + ASSERT(!operand->is_valid());
|
| + return;
|
| + }
|
| +
|
| + // Get the literal value.
|
| + Smi* smi_value = Smi::cast(*value);
|
| +
|
| + switch (op) {
|
| + case Token::ADD: {
|
| + operand->ToRegister();
|
| + frame_->Spill(operand->reg());
|
| +
|
| + // Optimistically add. Call the specialized add stub if the
|
| + // result is not a smi or overflows.
|
| + DeferredCode* deferred = NULL;
|
| + if (reversed) {
|
| + deferred = new DeferredInlineSmiAddReversed(operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| + } else {
|
| + deferred = new DeferredInlineSmiAdd(operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| + }
|
| + __ movq(kScratchRegister, value, RelocInfo::NONE);
|
| + __ addl(operand->reg(), kScratchRegister);
|
| + deferred->Branch(overflow);
|
| + __ testl(operand->reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(not_zero);
|
| + deferred->BindExit();
|
| + frame_->Push(operand);
|
| + break;
|
| + }
|
| + // TODO(X64): Move other implementations from ia32 to here.
|
| + default: {
|
| + Result constant_operand(value);
|
| + if (reversed) {
|
| + LikelySmiBinaryOperation(op, &constant_operand, operand,
|
| + overwrite_mode);
|
| + } else {
|
| + LikelySmiBinaryOperation(op, operand, &constant_operand,
|
| + overwrite_mode);
|
| + }
|
| + break;
|
| + }
|
| + }
|
| + ASSERT(!operand->is_valid());
|
| +}
|
| +
|
| #undef __
|
| #define __ ACCESS_MASM(masm)
|
|
|
| @@ -2238,234 +2778,23 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
|
| }
|
|
|
|
|
| -// Flag that indicates whether or not the code that handles smi arguments
|
| -// should be placed in the stub, inlined, or omitted entirely.
|
| -enum GenericBinaryFlags {
|
| - SMI_CODE_IN_STUB,
|
| - SMI_CODE_INLINED
|
| -};
|
| -
|
| -
|
| -class GenericBinaryOpStub: public CodeStub {
|
| - public:
|
| - GenericBinaryOpStub(Token::Value op,
|
| - OverwriteMode mode,
|
| - GenericBinaryFlags flags)
|
| - : op_(op), mode_(mode), flags_(flags) {
|
| - ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
|
| - }
|
| -
|
| - void GenerateSmiCode(MacroAssembler* masm, Label* slow);
|
| -
|
| - private:
|
| - Token::Value op_;
|
| - OverwriteMode mode_;
|
| - GenericBinaryFlags flags_;
|
| -
|
| - const char* GetName();
|
| -
|
| -#ifdef DEBUG
|
| - void Print() {
|
| - PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
|
| - Token::String(op_),
|
| - static_cast<int>(mode_),
|
| - static_cast<int>(flags_));
|
| - }
|
| -#endif
|
| -
|
| - // Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
|
| - class ModeBits: public BitField<OverwriteMode, 0, 2> {};
|
| - class OpBits: public BitField<Token::Value, 2, 13> {};
|
| - class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
|
|
|
| - Major MajorKey() { return GenericBinaryOp; }
|
| - int MinorKey() {
|
| - // Encode the parameters in a unique 16 bit value.
|
| - return OpBits::encode(op_)
|
| - | ModeBits::encode(mode_)
|
| - | FlagBits::encode(flags_);
|
| - }
|
| - void Generate(MacroAssembler* masm);
|
| -};
|
| -
|
| -
|
| -const char* GenericBinaryOpStub::GetName() {
|
| - switch (op_) {
|
| - case Token::ADD: return "GenericBinaryOpStub_ADD";
|
| - case Token::SUB: return "GenericBinaryOpStub_SUB";
|
| - case Token::MUL: return "GenericBinaryOpStub_MUL";
|
| - case Token::DIV: return "GenericBinaryOpStub_DIV";
|
| - case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
|
| - case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
|
| - case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
|
| - case Token::SAR: return "GenericBinaryOpStub_SAR";
|
| - case Token::SHL: return "GenericBinaryOpStub_SHL";
|
| - case Token::SHR: return "GenericBinaryOpStub_SHR";
|
| - default: return "GenericBinaryOpStub";
|
| - }
|
| +bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
|
| + return false; // UNIMPLEMENTED.
|
| }
|
|
|
| -
|
| -void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
|
| - // Perform fast-case smi code for the operation (rax <op> rbx) and
|
| - // leave result in register rax.
|
| -
|
| - // Prepare the smi check of both operands by or'ing them together
|
| - // before checking against the smi mask.
|
| - __ movq(rcx, rbx);
|
| - __ or_(rcx, rax);
|
| -
|
| - switch (op_) {
|
| - case Token::ADD:
|
| - __ addl(rax, rbx); // add optimistically
|
| - __ j(overflow, slow);
|
| - __ movsxlq(rax, rax); // Sign extend eax into rax.
|
| - break;
|
| -
|
| - case Token::SUB:
|
| - __ subl(rax, rbx); // subtract optimistically
|
| - __ j(overflow, slow);
|
| - __ movsxlq(rax, rax); // Sign extend eax into rax.
|
| - break;
|
| -
|
| - case Token::DIV:
|
| - case Token::MOD:
|
| - // Sign extend rax into rdx:rax
|
| - // (also sign extends eax into edx if eax is Smi).
|
| - __ cqo();
|
| - // Check for 0 divisor.
|
| - __ testq(rbx, rbx);
|
| - __ j(zero, slow);
|
| - break;
|
| -
|
| - default:
|
| - // Fall-through to smi check.
|
| - break;
|
| - }
|
| -
|
| - // Perform the actual smi check.
|
| - ASSERT(kSmiTag == 0); // adjust zero check if not the case
|
| - __ testl(rcx, Immediate(kSmiTagMask));
|
| - __ j(not_zero, slow);
|
| -
|
| - switch (op_) {
|
| - case Token::ADD:
|
| - case Token::SUB:
|
| - // Do nothing here.
|
| - 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 tag from one of the operands (but keep sign).
|
| - __ sar(rax, Immediate(kSmiTagSize));
|
| - // Do multiplication.
|
| - __ imull(rax, rbx); // multiplication of smis; result in eax
|
| - // Go slow on overflows.
|
| - __ j(overflow, slow);
|
| - // Check for negative zero result.
|
| - __ movsxlq(rax, rax); // Sign extend eax into rax.
|
| - __ NegativeZeroTest(rax, rcx, slow); // use rcx = x | y
|
| - break;
|
| -
|
| - case Token::DIV:
|
| - // Divide rdx:rax by rbx (where rdx:rax is equivalent to the smi in eax).
|
| - __ idiv(rbx);
|
| - // Check that the remainder is zero.
|
| - __ testq(rdx, rdx);
|
| - __ j(not_zero, slow);
|
| - // 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);
|
| - // TODO(X64): TODO(Smi): Smi implementation dependent constant.
|
| - // Value is Smi::fromInt(-(1<<31)) / Smi::fromInt(-1)
|
| - __ cmpq(rax, Immediate(0x40000000));
|
| - __ j(equal, slow);
|
| - // Check for negative zero result.
|
| - __ NegativeZeroTest(rax, rcx, slow); // use ecx = x | y
|
| - // Tag the result and store it in register rax.
|
| - ASSERT(kSmiTagSize == kTimes2); // adjust code if not the case
|
| - __ lea(rax, Operand(rax, rax, kTimes1, kSmiTag));
|
| - break;
|
| -
|
| - case Token::MOD:
|
| - // Divide rdx:rax by rbx.
|
| - __ idiv(rbx);
|
| - // Check for negative zero result.
|
| - __ NegativeZeroTest(rdx, rcx, slow); // use ecx = x | y
|
| - // Move remainder to register rax.
|
| - __ movq(rax, rdx);
|
| - break;
|
| -
|
| - case Token::BIT_OR:
|
| - __ or_(rax, rbx);
|
| - break;
|
| -
|
| - case Token::BIT_AND:
|
| - __ and_(rax, rbx);
|
| - break;
|
| -
|
| - case Token::BIT_XOR:
|
| - __ xor_(rax, rbx);
|
| - break;
|
| -
|
| - case Token::SHL:
|
| - case Token::SHR:
|
| - case Token::SAR:
|
| - // Move the second operand into register ecx.
|
| - __ movq(rcx, rbx);
|
| - // Remove tags from operands (but keep sign).
|
| - __ sar(rax, Immediate(kSmiTagSize));
|
| - __ sar(rcx, Immediate(kSmiTagSize));
|
| - // Perform the operation.
|
| - switch (op_) {
|
| - case Token::SAR:
|
| - __ sar(rax);
|
| - // No checks of result necessary
|
| - break;
|
| - case Token::SHR:
|
| - __ shrl(rax); // ecx is implicit shift register
|
| - // 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.
|
| - __ testq(rax, Immediate(0xc0000000));
|
| - __ j(not_zero, slow);
|
| - break;
|
| - case Token::SHL:
|
| - __ shll(rax);
|
| - // TODO(Smi): Significant change if Smi changes.
|
| - // Check that the *signed* result fits in a smi.
|
| - // It does, if the 30th and 31st bits are equal, since then
|
| - // shifting the SmiTag in at the bottom doesn't change the sign.
|
| - ASSERT(kSmiTagSize == 1);
|
| - __ cmpl(rax, Immediate(0xc0000000));
|
| - __ j(sign, slow);
|
| - __ movsxlq(rax, rax); // Extend new sign of eax into rax.
|
| - break;
|
| - default:
|
| - UNREACHABLE();
|
| - }
|
| - // Tag the result and store it in register eax.
|
| - ASSERT(kSmiTagSize == kTimes2); // adjust code if not the case
|
| - __ lea(rax, Operand(rax, rax, kTimes1, kSmiTag));
|
| - break;
|
| -
|
| - default:
|
| - UNREACHABLE();
|
| - break;
|
| - }
|
| +void CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
|
| + Result* left,
|
| + Result* right,
|
| + OverwriteMode overwrite_mode) {
|
| + UNIMPLEMENTED();
|
| }
|
|
|
|
|
| -void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
|
| -}
|
| -
|
| +// End of CodeGenerator implementation.
|
|
|
| void UnarySubStub::Generate(MacroAssembler* masm) {
|
| + UNIMPLEMENTED();
|
| }
|
|
|
| class CompareStub: public CodeStub {
|
| @@ -3033,6 +3362,547 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
| __ ret(0);
|
| }
|
|
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Implementation of stubs.
|
| +
|
| +// Stub classes have public member named masm, not masm_.
|
| +
|
| +
|
| +void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
|
| + Label* need_gc,
|
| + Register scratch,
|
| + Register result) {
|
| + ExternalReference allocation_top =
|
| + ExternalReference::new_space_allocation_top_address();
|
| + ExternalReference allocation_limit =
|
| + ExternalReference::new_space_allocation_limit_address();
|
| + __ movq(scratch, allocation_top); // scratch: address of allocation top.
|
| + __ movq(result, Operand(scratch, 0));
|
| + __ addq(result, Immediate(HeapNumber::kSize)); // New top.
|
| + __ movq(kScratchRegister, allocation_limit);
|
| + __ cmpq(result, Operand(kScratchRegister, 0));
|
| + __ j(above, need_gc);
|
| +
|
| + __ movq(Operand(scratch, 0), result); // store new top
|
| + __ addq(result, Immediate(kHeapObjectTag - HeapNumber::kSize));
|
| + __ movq(kScratchRegister,
|
| + Factory::heap_number_map(),
|
| + RelocInfo::EMBEDDED_OBJECT);
|
| + __ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
|
| + // Tag old top and use as result.
|
| +}
|
| +
|
| +
|
| +
|
| +void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
|
| + Register src,
|
| + XMMRegister dst) {
|
| + Label load_smi, done;
|
| +
|
| + __ testl(src, Immediate(kSmiTagMask));
|
| + __ j(zero, &load_smi);
|
| + __ movsd(dst, FieldOperand(src, HeapNumber::kValueOffset));
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&load_smi);
|
| + __ sar(src, Immediate(kSmiTagSize));
|
| + __ cvtlsi2sd(dst, src);
|
| +
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
|
| + XMMRegister dst1,
|
| + XMMRegister dst2) {
|
| + __ movq(kScratchRegister, Operand(rsp, 2 * kPointerSize));
|
| + LoadFloatOperand(masm, kScratchRegister, dst1);
|
| + __ movq(kScratchRegister, Operand(rsp, 1 * kPointerSize));
|
| + LoadFloatOperand(masm, kScratchRegister, dst2);
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::LoadInt32Operand(MacroAssembler* masm,
|
| + const Operand& src,
|
| + Register dst) {
|
| + // TODO(X64): Convert number operands to int32 values.
|
| + // Don't convert a Smi to a double first.
|
| + UNIMPLEMENTED();
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm) {
|
| + Label load_smi_1, load_smi_2, done_load_1, done;
|
| + __ movq(kScratchRegister, Operand(rsp, 2 * kPointerSize));
|
| + __ testl(kScratchRegister, Immediate(kSmiTagMask));
|
| + __ j(zero, &load_smi_1);
|
| + __ fld_d(FieldOperand(kScratchRegister, HeapNumber::kValueOffset));
|
| + __ bind(&done_load_1);
|
| +
|
| + __ movq(kScratchRegister, Operand(rsp, 1 * kPointerSize));
|
| + __ testl(kScratchRegister, Immediate(kSmiTagMask));
|
| + __ j(zero, &load_smi_2);
|
| + __ fld_d(FieldOperand(kScratchRegister, HeapNumber::kValueOffset));
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&load_smi_1);
|
| + __ sar(kScratchRegister, Immediate(kSmiTagSize));
|
| + __ push(kScratchRegister);
|
| + __ fild_s(Operand(rsp, 0));
|
| + __ pop(kScratchRegister);
|
| + __ jmp(&done_load_1);
|
| +
|
| + __ bind(&load_smi_2);
|
| + __ sar(kScratchRegister, Immediate(kSmiTagSize));
|
| + __ push(kScratchRegister);
|
| + __ fild_s(Operand(rsp, 0));
|
| + __ pop(kScratchRegister);
|
| +
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
|
| + Label* non_float) {
|
| + Label test_other, done;
|
| + // Test if both operands are floats or smi -> scratch=k_is_float;
|
| + // Otherwise scratch = k_not_float.
|
| + __ testl(rdx, Immediate(kSmiTagMask));
|
| + __ j(zero, &test_other); // argument in rdx is OK
|
| + __ movq(kScratchRegister,
|
| + Factory::heap_number_map(),
|
| + RelocInfo::EMBEDDED_OBJECT);
|
| + __ cmpq(kScratchRegister, FieldOperand(rdx, HeapObject::kMapOffset));
|
| + __ j(not_equal, non_float); // argument in rdx is not a number -> NaN
|
| +
|
| + __ bind(&test_other);
|
| + __ testl(rax, Immediate(kSmiTagMask));
|
| + __ j(zero, &done); // argument in eax is OK
|
| + __ movq(kScratchRegister,
|
| + Factory::heap_number_map(),
|
| + RelocInfo::EMBEDDED_OBJECT);
|
| + __ cmpq(kScratchRegister, FieldOperand(rax, HeapObject::kMapOffset));
|
| + __ j(not_equal, non_float); // argument in rax is not a number -> NaN
|
| +
|
| + // Fall-through: Both operands are numbers.
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| +const char* GenericBinaryOpStub::GetName() {
|
| + switch (op_) {
|
| + case Token::ADD: return "GenericBinaryOpStub_ADD";
|
| + case Token::SUB: return "GenericBinaryOpStub_SUB";
|
| + case Token::MUL: return "GenericBinaryOpStub_MUL";
|
| + case Token::DIV: return "GenericBinaryOpStub_DIV";
|
| + case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
|
| + case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
|
| + case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
|
| + case Token::SAR: return "GenericBinaryOpStub_SAR";
|
| + case Token::SHL: return "GenericBinaryOpStub_SHL";
|
| + case Token::SHR: return "GenericBinaryOpStub_SHR";
|
| + default: return "GenericBinaryOpStub";
|
| + }
|
| +}
|
| +
|
| +void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
|
| + // Perform fast-case smi code for the operation (rax <op> rbx) and
|
| + // leave result in register rax.
|
| +
|
| + // Prepare the smi check of both operands by or'ing them together
|
| + // before checking against the smi mask.
|
| + __ movq(rcx, rbx);
|
| + __ or_(rcx, rax);
|
| +
|
| + switch (op_) {
|
| + case Token::ADD:
|
| + __ addl(rax, rbx); // add optimistically
|
| + __ j(overflow, slow);
|
| + __ movsxlq(rax, rax); // Sign extend eax into rax.
|
| + break;
|
| +
|
| + case Token::SUB:
|
| + __ subl(rax, rbx); // subtract optimistically
|
| + __ j(overflow, slow);
|
| + __ movsxlq(rax, rax); // Sign extend eax into rax.
|
| + break;
|
| +
|
| + case Token::DIV:
|
| + case Token::MOD:
|
| + // Sign extend rax into rdx:rax
|
| + // (also sign extends eax into edx if eax is Smi).
|
| + __ cqo();
|
| + // Check for 0 divisor.
|
| + __ testq(rbx, rbx);
|
| + __ j(zero, slow);
|
| + break;
|
| +
|
| + default:
|
| + // Fall-through to smi check.
|
| + break;
|
| + }
|
| +
|
| + // Perform the actual smi check.
|
| + ASSERT(kSmiTag == 0); // adjust zero check if not the case
|
| + __ testl(rcx, Immediate(kSmiTagMask));
|
| + __ j(not_zero, slow);
|
| +
|
| + switch (op_) {
|
| + case Token::ADD:
|
| + case Token::SUB:
|
| + // Do nothing here.
|
| + 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 tag from one of the operands (but keep sign).
|
| + __ sar(rax, Immediate(kSmiTagSize));
|
| + // Do multiplication.
|
| + __ imull(rax, rbx); // multiplication of smis; result in eax
|
| + // Go slow on overflows.
|
| + __ j(overflow, slow);
|
| + // Check for negative zero result.
|
| + __ movsxlq(rax, rax); // Sign extend eax into rax.
|
| + __ NegativeZeroTest(rax, rcx, slow); // use rcx = x | y
|
| + break;
|
| +
|
| + case Token::DIV:
|
| + // Divide rdx:rax by rbx (where rdx:rax is equivalent to the smi in eax).
|
| + __ idiv(rbx);
|
| + // Check that the remainder is zero.
|
| + __ testq(rdx, rdx);
|
| + __ j(not_zero, slow);
|
| + // 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);
|
| + // TODO(X64): TODO(Smi): Smi implementation dependent constant.
|
| + // Value is Smi::fromInt(-(1<<31)) / Smi::fromInt(-1)
|
| + __ cmpq(rax, Immediate(0x40000000));
|
| + __ j(equal, slow);
|
| + // Check for negative zero result.
|
| + __ NegativeZeroTest(rax, rcx, slow); // use ecx = x | y
|
| + // Tag the result and store it in register rax.
|
| + ASSERT(kSmiTagSize == kTimes2); // adjust code if not the case
|
| + __ lea(rax, Operand(rax, rax, kTimes1, kSmiTag));
|
| + break;
|
| +
|
| + case Token::MOD:
|
| + // Divide rdx:rax by rbx.
|
| + __ idiv(rbx);
|
| + // Check for negative zero result.
|
| + __ NegativeZeroTest(rdx, rcx, slow); // use ecx = x | y
|
| + // Move remainder to register rax.
|
| + __ movq(rax, rdx);
|
| + break;
|
| +
|
| + case Token::BIT_OR:
|
| + __ or_(rax, rbx);
|
| + break;
|
| +
|
| + case Token::BIT_AND:
|
| + __ and_(rax, rbx);
|
| + break;
|
| +
|
| + case Token::BIT_XOR:
|
| + ASSERT_EQ(0, kSmiTag);
|
| + __ xor_(rax, rbx);
|
| + break;
|
| +
|
| + case Token::SHL:
|
| + case Token::SHR:
|
| + case Token::SAR:
|
| + // Move the second operand into register ecx.
|
| + __ movq(rcx, rbx);
|
| + // Remove tags from operands (but keep sign).
|
| + __ sar(rax, Immediate(kSmiTagSize));
|
| + __ sar(rcx, Immediate(kSmiTagSize));
|
| + // Perform the operation.
|
| + switch (op_) {
|
| + case Token::SAR:
|
| + __ sar(rax);
|
| + // No checks of result necessary
|
| + break;
|
| + case Token::SHR:
|
| + __ shrl(rax); // rcx is implicit shift register
|
| + // 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.
|
| + __ testq(rax, Immediate(0xc0000000));
|
| + __ j(not_zero, slow);
|
| + break;
|
| + case Token::SHL:
|
| + __ shll(rax);
|
| + // TODO(Smi): Significant change if Smi changes.
|
| + // Check that the *signed* result fits in a smi.
|
| + // It does, if the 30th and 31st bits are equal, since then
|
| + // shifting the SmiTag in at the bottom doesn't change the sign.
|
| + ASSERT(kSmiTagSize == 1);
|
| + __ cmpl(rax, Immediate(0xc0000000));
|
| + __ j(sign, slow);
|
| + __ movsxlq(rax, rax); // Extend new sign of eax into rax.
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + // Tag the result and store it in register eax.
|
| + ASSERT(kSmiTagSize == kTimes2); // adjust code if not the case
|
| + __ lea(rax, Operand(rax, rax, kTimes1, kSmiTag));
|
| + break;
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| +}
|
| +
|
| +
|
| +void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
|
| + Label call_runtime;
|
| +
|
| + if (flags_ == SMI_CODE_IN_STUB) {
|
| + // The fast case smi code wasn't inlined in the stub caller
|
| + // code. Generate it here to speed up common operations.
|
| + Label slow;
|
| + __ movq(rbx, Operand(rsp, 1 * kPointerSize)); // get y
|
| + __ movq(rax, Operand(rsp, 2 * kPointerSize)); // get x
|
| + GenerateSmiCode(masm, &slow);
|
| + __ ret(2 * kPointerSize); // remove both operands
|
| +
|
| + // Too bad. The fast case smi code didn't succeed.
|
| + __ bind(&slow);
|
| + }
|
| +
|
| + // Setup registers.
|
| + __ movq(rax, Operand(rsp, 1 * kPointerSize)); // get y
|
| + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // get x
|
| +
|
| + // Floating point case.
|
| + switch (op_) {
|
| + case Token::ADD:
|
| + case Token::SUB:
|
| + case Token::MUL:
|
| + case Token::DIV: {
|
| + // rax: y
|
| + // rdx: x
|
| + FloatingPointHelper::CheckFloatOperands(masm, &call_runtime);
|
| + // Fast-case: Both operands are numbers.
|
| + // Allocate a heap number, if needed.
|
| + Label skip_allocation;
|
| + switch (mode_) {
|
| + case OVERWRITE_LEFT:
|
| + __ movq(rax, rdx);
|
| + // Fall through!
|
| + case OVERWRITE_RIGHT:
|
| + // If the argument in rax is already an object, we skip the
|
| + // allocation of a heap number.
|
| + __ testl(rax, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &skip_allocation);
|
| + // Fall through!
|
| + case NO_OVERWRITE:
|
| + FloatingPointHelper::AllocateHeapNumber(masm,
|
| + &call_runtime,
|
| + rcx,
|
| + rax);
|
| + __ bind(&skip_allocation);
|
| + break;
|
| + default: UNREACHABLE();
|
| + }
|
| + // xmm4 and xmm5 are volatile XMM registers.
|
| + FloatingPointHelper::LoadFloatOperands(masm, xmm4, xmm5);
|
| +
|
| + switch (op_) {
|
| + case Token::ADD: __ addsd(xmm4, xmm5); break;
|
| + case Token::SUB: __ subsd(xmm4, xmm5); break;
|
| + case Token::MUL: __ mulsd(xmm4, xmm5); break;
|
| + case Token::DIV: __ divsd(xmm4, xmm5); break;
|
| + default: UNREACHABLE();
|
| + }
|
| + __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm4);
|
| + __ ret(2 * kPointerSize);
|
| + }
|
| + case Token::MOD: {
|
| + // For MOD we go directly to runtime in the non-smi case.
|
| + break;
|
| + }
|
| + case Token::BIT_OR:
|
| + case Token::BIT_AND:
|
| + case Token::BIT_XOR:
|
| + case Token::SAR:
|
| + case Token::SHL:
|
| + case Token::SHR: {
|
| + FloatingPointHelper::CheckFloatOperands(masm, &call_runtime);
|
| + // TODO(X64): Don't convert a Smi to float and then back to int32
|
| + // afterwards.
|
| + FloatingPointHelper::LoadFloatOperands(masm);
|
| +
|
| + Label skip_allocation, non_smi_result, operand_conversion_failure;
|
| +
|
| + // Reserve space for converted numbers.
|
| + __ subq(rsp, Immediate(2 * kPointerSize));
|
| +
|
| + bool use_sse3 = CpuFeatures::IsSupported(CpuFeatures::SSE3);
|
| + if (use_sse3) {
|
| + // Truncate the operands to 32-bit integers and check for
|
| + // exceptions in doing so.
|
| + CpuFeatures::Scope scope(CpuFeatures::SSE3);
|
| + __ fisttp_s(Operand(rsp, 0 * kPointerSize));
|
| + __ fisttp_s(Operand(rsp, 1 * kPointerSize));
|
| + __ fnstsw_ax();
|
| + __ testl(rax, Immediate(1));
|
| + __ j(not_zero, &operand_conversion_failure);
|
| + } else {
|
| + // Check if right operand is int32.
|
| + __ fist_s(Operand(rsp, 0 * kPointerSize));
|
| + __ fild_s(Operand(rsp, 0 * kPointerSize));
|
| + __ fucompp();
|
| + __ fnstsw_ax();
|
| + __ sahf(); // TODO(X64): Not available.
|
| + __ j(not_zero, &operand_conversion_failure);
|
| + __ j(parity_even, &operand_conversion_failure);
|
| +
|
| + // Check if left operand is int32.
|
| + __ fist_s(Operand(rsp, 1 * kPointerSize));
|
| + __ fild_s(Operand(rsp, 1 * kPointerSize));
|
| + __ fucompp();
|
| + __ fnstsw_ax();
|
| + __ sahf(); // TODO(X64): Not available. Test bits in ax directly
|
| + __ j(not_zero, &operand_conversion_failure);
|
| + __ j(parity_even, &operand_conversion_failure);
|
| + }
|
| +
|
| + // Get int32 operands and perform bitop.
|
| + __ pop(rcx);
|
| + __ pop(rax);
|
| + switch (op_) {
|
| + case Token::BIT_OR: __ or_(rax, rcx); break;
|
| + case Token::BIT_AND: __ and_(rax, rcx); break;
|
| + case Token::BIT_XOR: __ xor_(rax, rcx); break;
|
| + case Token::SAR: __ sar(rax); break;
|
| + case Token::SHL: __ shl(rax); break;
|
| + case Token::SHR: __ shr(rax); break;
|
| + default: UNREACHABLE();
|
| + }
|
| + if (op_ == Token::SHR) {
|
| + // Check if result is non-negative and fits in a smi.
|
| + __ testl(rax, Immediate(0xc0000000));
|
| + __ j(not_zero, &non_smi_result);
|
| + } else {
|
| + // Check if result fits in a smi.
|
| + __ cmpl(rax, Immediate(0xc0000000));
|
| + __ j(negative, &non_smi_result);
|
| + }
|
| + // Tag smi result and return.
|
| + ASSERT(kSmiTagSize == kTimes2); // adjust code if not the case
|
| + __ lea(rax, Operand(rax, rax, kTimes1, kSmiTag));
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + // All ops except SHR return a signed int32 that we load in a HeapNumber.
|
| + if (op_ != Token::SHR) {
|
| + __ bind(&non_smi_result);
|
| + // Allocate a heap number if needed.
|
| + __ movsxlq(rbx, rax); // rbx: sign extended 32-bit result
|
| + switch (mode_) {
|
| + case OVERWRITE_LEFT:
|
| + case OVERWRITE_RIGHT:
|
| + // If the operand was an object, we skip the
|
| + // allocation of a heap number.
|
| + __ movq(rax, Operand(rsp, mode_ == OVERWRITE_RIGHT ?
|
| + 1 * kPointerSize : 2 * kPointerSize));
|
| + __ testl(rax, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &skip_allocation);
|
| + // Fall through!
|
| + case NO_OVERWRITE:
|
| + FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime,
|
| + rcx, rax);
|
| + __ bind(&skip_allocation);
|
| + break;
|
| + default: UNREACHABLE();
|
| + }
|
| + // Store the result in the HeapNumber and return.
|
| + __ movq(Operand(rsp, 1 * kPointerSize), rbx);
|
| + __ fild_s(Operand(rsp, 1 * kPointerSize));
|
| + __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset));
|
| + __ ret(2 * kPointerSize);
|
| + }
|
| +
|
| + // Clear the FPU exception flag and reset the stack before calling
|
| + // the runtime system.
|
| + __ bind(&operand_conversion_failure);
|
| + __ addq(rsp, Immediate(2 * kPointerSize));
|
| + if (use_sse3) {
|
| + // If we've used the SSE3 instructions for truncating the
|
| + // floating point values to integers and it failed, we have a
|
| + // pending #IA exception. Clear it.
|
| + __ fnclex();
|
| + } else {
|
| + // The non-SSE3 variant does early bailout if the right
|
| + // operand isn't a 32-bit integer, so we may have a single
|
| + // value on the FPU stack we need to get rid of.
|
| + __ ffree(0);
|
| + }
|
| +
|
| + // SHR should return uint32 - go to runtime for non-smi/negative result.
|
| + if (op_ == Token::SHR) {
|
| + __ bind(&non_smi_result);
|
| + }
|
| + __ movq(rax, Operand(rsp, 1 * kPointerSize));
|
| + __ movq(rdx, Operand(rsp, 2 * kPointerSize));
|
| + break;
|
| + }
|
| + default: UNREACHABLE(); break;
|
| + }
|
| +
|
| + // If all else fails, use the runtime system to get the correct
|
| + // result.
|
| + __ bind(&call_runtime);
|
| + // Disable builtin-calls until JS builtins can compile and run.
|
| + __ Abort("Disabled until builtins compile and run.");
|
| + switch (op_) {
|
| + case Token::ADD:
|
| + __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SUB:
|
| + __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
|
| + break;
|
| + case Token::MUL:
|
| + __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
|
| + break;
|
| + case Token::DIV:
|
| + __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
|
| + break;
|
| + case Token::MOD:
|
| + __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
|
| + break;
|
| + case Token::BIT_OR:
|
| + __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
|
| + break;
|
| + case Token::BIT_AND:
|
| + __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
|
| + break;
|
| + case Token::BIT_XOR:
|
| + __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SAR:
|
| + __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SHL:
|
| + __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SHR:
|
| + __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| #undef __
|
|
|
| } } // namespace v8::internal
|
|
|