Index: src/x64/codegen-x64.cc |
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc |
index b1f296c2abd21cd7577708001730551203658b95..284d9bee9eda3cad8991744dafe352264bbfbceb 100644 |
--- a/src/x64/codegen-x64.cc |
+++ b/src/x64/codegen-x64.cc |
@@ -341,27 +341,26 @@ void CodeGenerator::VisitThisFunction(ThisFunction* a) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* a) { |
- UNIMPLEMENTED(); |
-} |
+void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { |
+ UNIMPLEMENTED();} |
void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* a) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
@@ -369,7 +368,7 @@ void CodeGenerator::GenerateLog(ZoneList<Expression*>* a) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
@@ -381,11 +380,11 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
-void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* a) { |
+void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) { |
UNIMPLEMENTED(); |
} |
@@ -398,6 +397,483 @@ void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* a) { |
// Stub classes have public member named masm, not masm_. |
#define __ ACCESS_MASM(masm) |
+class ToBooleanStub: public CodeStub { |
+ public: |
+ ToBooleanStub() { } |
+ |
+ void Generate(MacroAssembler* masm); |
+ |
+ private: |
+ Major MajorKey() { return ToBoolean; } |
+ int MinorKey() { return 0; } |
+}; |
+ |
+ |
+void ToBooleanStub::Generate(MacroAssembler* masm) { |
+ Label false_result, true_result, not_string; |
+ __ movq(rax, Operand(rsp, 1 * kPointerSize)); |
+ |
+ // 'null' => false. |
+ __ movq(kScratchRegister, Factory::null_value(), RelocInfo::EMBEDDED_OBJECT); |
+ __ cmpq(rax, kScratchRegister); |
+ __ j(equal, &false_result); |
+ |
+ // Get the map and type of the heap object. |
+ __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); |
+ __ movzxbq(rcx, FieldOperand(rdx, Map::kInstanceTypeOffset)); |
+ |
+ // Undetectable => false. |
+ __ movzxbq(rbx, FieldOperand(rdx, Map::kBitFieldOffset)); |
+ __ and_(rbx, Immediate(1 << Map::kIsUndetectable)); |
+ __ j(not_zero, &false_result); |
+ |
+ // JavaScript object => true. |
+ __ cmpq(rcx, Immediate(FIRST_JS_OBJECT_TYPE)); |
+ __ j(above_equal, &true_result); |
+ |
+ // String value => false iff empty. |
+ __ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE)); |
+ __ j(above_equal, ¬_string); |
+ __ and_(rcx, Immediate(kStringSizeMask)); |
+ __ cmpq(rcx, Immediate(kShortStringTag)); |
+ __ j(not_equal, &true_result); // Empty string is always short. |
+ __ movq(rdx, FieldOperand(rax, String::kLengthOffset)); |
+ __ shr(rdx, Immediate(String::kShortLengthShift)); |
+ __ j(zero, &false_result); |
+ __ jmp(&true_result); |
+ |
+ __ bind(¬_string); |
+ // HeapNumber => false iff +0, -0, or NaN. |
+ __ movq(kScratchRegister, |
+ Factory::heap_number_map(), |
+ RelocInfo::EMBEDDED_OBJECT); |
+ __ cmpq(rdx, kScratchRegister); |
+ __ j(not_equal, &true_result); |
+ // TODO(x64): Don't use fp stack, use MMX registers? |
+ __ fldz(); // Load zero onto fp stack |
+ // Load heap-number double value onto fp stack |
+ __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset)); |
+ __ fucompp(); // Compare and pop both values. |
+ __ movq(kScratchRegister, rax); |
+ __ fnstsw_ax(); // Store fp status word in ax, no checking for exceptions. |
+ __ testb(rax, Immediate(0x08)); // Test FP condition flag C3. |
+ __ movq(rax, kScratchRegister); |
+ __ j(zero, &false_result); |
+ // Fall through to |true_result|. |
+ |
+ // Return 1/0 for true/false in rax. |
+ __ bind(&true_result); |
+ __ movq(rax, Immediate(1)); |
+ __ ret(1 * kPointerSize); |
+ __ bind(&false_result); |
+ __ xor_(rax, rax); |
+ __ ret(1 * kPointerSize); |
+} |
+ |
+ |
+// 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"; |
+ } |
+} |
+ |
+ |
+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 GenericBinaryOpStub::Generate(MacroAssembler* masm) { |
+} |
+ |
+ |
+void UnarySubStub::Generate(MacroAssembler* masm) { |
+} |
+ |
+class CompareStub: public CodeStub { |
+ public: |
+ CompareStub(Condition cc, bool strict) : cc_(cc), strict_(strict) { } |
+ |
+ void Generate(MacroAssembler* masm); |
+ |
+ private: |
+ Condition cc_; |
+ bool strict_; |
+ |
+ Major MajorKey() { return Compare; } |
+ |
+ int MinorKey() { |
+ // Encode the three parameters in a unique 16 bit value. |
+ ASSERT(static_cast<int>(cc_) < (1 << 15)); |
+ return (static_cast<int>(cc_) << 1) | (strict_ ? 1 : 0); |
+ } |
+ |
+#ifdef DEBUG |
+ void Print() { |
+ PrintF("CompareStub (cc %d), (strict %s)\n", |
+ static_cast<int>(cc_), |
+ strict_ ? "true" : "false"); |
+ } |
+#endif |
+}; |
+ |
+ |
+void CompareStub::Generate(MacroAssembler* masm) { |
+} |
+ |
+ |
+void StackCheckStub::Generate(MacroAssembler* masm) { |
+} |
+ |
+ |
+class CallFunctionStub: public CodeStub { |
+ public: |
+ CallFunctionStub(int argc, InLoopFlag in_loop) |
+ : argc_(argc), in_loop_(in_loop) { } |
+ |
+ void Generate(MacroAssembler* masm); |
+ |
+ private: |
+ int argc_; |
+ InLoopFlag in_loop_; |
+ |
+#ifdef DEBUG |
+ void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); } |
+#endif |
+ |
+ Major MajorKey() { return CallFunction; } |
+ int MinorKey() { return argc_; } |
+ InLoopFlag InLoop() { return in_loop_; } |
+}; |
+ |
+ |
+void CallFunctionStub::Generate(MacroAssembler* masm) { |
+} |
+ |
+ |
+void InstanceofStub::Generate(MacroAssembler* masm) { |
+} |
+ |
+ |
+ |
+void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { |
+ // The displacement is used for skipping the return address and the |
+ // frame pointer on the stack. It is the offset of the last |
+ // parameter (if any) relative to the frame pointer. |
+ static const int kDisplacement = 2 * kPointerSize; |
+ |
+ // Check if the calling frame is an arguments adaptor frame. |
+ Label runtime; |
+ __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
+ __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset)); |
+ __ cmpq(rcx, Immediate(ArgumentsAdaptorFrame::SENTINEL)); |
+ __ j(not_equal, &runtime); |
+ // Value in rcx is Smi encoded. |
+ |
+ // Patch the arguments.length and the parameters pointer. |
+ __ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
+ __ movq(Operand(rsp, 1 * kPointerSize), rcx); |
+ __ lea(rdx, Operand(rdx, rcx, kTimes4, kDisplacement)); |
+ __ movq(Operand(rsp, 2 * kPointerSize), rdx); |
+ |
+ // Do the runtime call to allocate the arguments object. |
+ __ bind(&runtime); |
+ __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3); |
+} |
+ |
+void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { |
+ // The key is in rdx and the parameter count is in rax. |
+ |
+ // The displacement is used for skipping the frame pointer on the |
+ // stack. It is the offset of the last parameter (if any) relative |
+ // to the frame pointer. |
+ static const int kDisplacement = 1 * kPointerSize; |
+ |
+ // Check that the key is a smi. |
+ Label slow; |
+ __ testl(rdx, Immediate(kSmiTagMask)); |
+ __ j(not_zero, &slow); |
+ |
+ // Check if the calling frame is an arguments adaptor frame. |
+ Label adaptor; |
+ __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
+ __ movq(rcx, Operand(rbx, StandardFrameConstants::kContextOffset)); |
+ __ cmpq(rcx, Immediate(ArgumentsAdaptorFrame::SENTINEL)); |
+ __ j(equal, &adaptor); |
+ |
+ // Check index against formal parameters count limit passed in |
+ // through register rax. Use unsigned comparison to get negative |
+ // check for free. |
+ __ cmpq(rdx, rax); |
+ __ j(above_equal, &slow); |
+ |
+ // Read the argument from the stack and return it. |
+ // Shifting code depends on SmiEncoding being equivalent to left shift: |
+ // we multiply by four to get pointer alignment. |
+ ASSERT(kSmiTagSize == 1 && kSmiTag == 0); |
+ __ lea(rbx, Operand(rbp, rax, kTimes4, 0)); |
+ __ neg(rdx); |
+ __ movq(rax, Operand(rbx, rdx, kTimes4, kDisplacement)); |
+ __ Ret(); |
+ |
+ // Arguments adaptor case: Check index against actual arguments |
+ // limit found in the arguments adaptor frame. Use unsigned |
+ // comparison to get negative check for free. |
+ __ bind(&adaptor); |
+ __ movq(rcx, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
+ __ cmpq(rdx, rcx); |
+ __ j(above_equal, &slow); |
+ |
+ // Read the argument from the stack and return it. |
+ // Shifting code depends on SmiEncoding being equivalent to left shift: |
+ // we multiply by four to get pointer alignment. |
+ ASSERT(kSmiTagSize == 1 && kSmiTag == 0); |
+ __ lea(rbx, Operand(rbx, rcx, kTimes4, 0)); |
+ __ neg(rdx); |
+ __ movq(rax, Operand(rbx, rdx, kTimes4, kDisplacement)); |
+ __ Ret(); |
+ |
+ // Slow-case: Handle non-smi or out-of-bounds access to arguments |
+ // by calling the runtime system. |
+ __ bind(&slow); |
+ __ pop(rbx); // Return address. |
+ __ push(rdx); |
+ __ push(rbx); |
+ __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); |
+} |
+ |
+ |
+void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) { |
+ // Check if the calling frame is an arguments adaptor frame. |
+ Label adaptor; |
+ __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
+ __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset)); |
+ __ cmpq(rcx, Immediate(ArgumentsAdaptorFrame::SENTINEL)); |
+ __ j(equal, &adaptor); |
+ |
+ // Nothing to do: The formal number of parameters has already been |
+ // passed in register rax by calling function. Just return it. |
+ __ ret(0); |
+ |
+ // Arguments adaptor case: Read the arguments length from the |
+ // adaptor frame and return it. |
+ __ bind(&adaptor); |
+ __ movq(rax, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
+ __ ret(0); |
+} |
+ |
+ |
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { |
// Check that stack should contain frame pointer, code pointer, state and |
// return address in that order. |
@@ -743,7 +1219,6 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { |
__ ret(0); |
} |
- |
#undef __ |
} } // namespace v8::internal |