Index: src/compiler/arm/code-generator-arm.cc |
diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..781252b75aadaf69557b7c948e51c049509bb181 |
--- /dev/null |
+++ b/src/compiler/arm/code-generator-arm.cc |
@@ -0,0 +1,828 @@ |
+// Copyright 2014 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/compiler/code-generator.h" |
+ |
+#include "src/arm/macro-assembler-arm.h" |
+#include "src/compiler/code-generator-impl.h" |
+#include "src/compiler/gap-resolver.h" |
+#include "src/compiler/node-matchers.h" |
+#include "src/compiler/node-properties-inl.h" |
+#include "src/scopes.h" |
+ |
+namespace v8 { |
+namespace internal { |
+namespace compiler { |
+ |
+#define __ masm()-> |
+ |
+ |
+#define kScratchReg r9 |
+ |
+ |
+// Adds Arm-specific methods to convert InstructionOperands. |
+class ArmOperandConverter : public InstructionOperandConverter { |
+ public: |
+ ArmOperandConverter(CodeGenerator* gen, Instruction* instr) |
+ : InstructionOperandConverter(gen, instr) {} |
+ |
+ SBit OutputSBit() const { |
+ switch (instr_->flags_mode()) { |
+ case kFlags_branch: |
+ case kFlags_set: |
+ return SetCC; |
+ case kFlags_none: |
+ return LeaveCC; |
+ } |
+ UNREACHABLE(); |
+ return LeaveCC; |
+ } |
+ |
+ Operand InputImmediate(int index) { |
+ Constant constant = ToConstant(instr_->InputAt(index)); |
+ switch (constant.type()) { |
+ case Constant::kInt32: |
+ return Operand(constant.ToInt32()); |
+ case Constant::kFloat64: |
+ return Operand( |
+ isolate()->factory()->NewNumber(constant.ToFloat64(), TENURED)); |
+ case Constant::kInt64: |
+ case Constant::kExternalReference: |
+ case Constant::kHeapObject: |
+ break; |
+ } |
+ UNREACHABLE(); |
+ return Operand::Zero(); |
+ } |
+ |
+ Operand InputOperand2(int first_index) { |
+ const int index = first_index; |
+ switch (AddressingModeField::decode(instr_->opcode())) { |
+ case kMode_None: |
+ case kMode_Offset_RI: |
+ case kMode_Offset_RR: |
+ break; |
+ case kMode_Operand2_I: |
+ return InputImmediate(index + 0); |
+ case kMode_Operand2_R: |
+ return Operand(InputRegister(index + 0)); |
+ case kMode_Operand2_R_ASR_I: |
+ return Operand(InputRegister(index + 0), ASR, InputInt5(index + 1)); |
+ case kMode_Operand2_R_ASR_R: |
+ return Operand(InputRegister(index + 0), ASR, InputRegister(index + 1)); |
+ case kMode_Operand2_R_LSL_I: |
+ return Operand(InputRegister(index + 0), LSL, InputInt5(index + 1)); |
+ case kMode_Operand2_R_LSL_R: |
+ return Operand(InputRegister(index + 0), LSL, InputRegister(index + 1)); |
+ case kMode_Operand2_R_LSR_I: |
+ return Operand(InputRegister(index + 0), LSR, InputInt5(index + 1)); |
+ case kMode_Operand2_R_LSR_R: |
+ return Operand(InputRegister(index + 0), LSR, InputRegister(index + 1)); |
+ } |
+ UNREACHABLE(); |
+ return Operand::Zero(); |
+ } |
+ |
+ MemOperand InputOffset(int* first_index) { |
+ const int index = *first_index; |
+ switch (AddressingModeField::decode(instr_->opcode())) { |
+ case kMode_None: |
+ case kMode_Operand2_I: |
+ case kMode_Operand2_R: |
+ case kMode_Operand2_R_ASR_I: |
+ case kMode_Operand2_R_ASR_R: |
+ case kMode_Operand2_R_LSL_I: |
+ case kMode_Operand2_R_LSL_R: |
+ case kMode_Operand2_R_LSR_I: |
+ case kMode_Operand2_R_LSR_R: |
+ break; |
+ case kMode_Offset_RI: |
+ *first_index += 2; |
+ return MemOperand(InputRegister(index + 0), InputInt32(index + 1)); |
+ case kMode_Offset_RR: |
+ *first_index += 2; |
+ return MemOperand(InputRegister(index + 0), InputRegister(index + 1)); |
+ } |
+ UNREACHABLE(); |
+ return MemOperand(r0); |
+ } |
+ |
+ MemOperand InputOffset() { |
+ int index = 0; |
+ return InputOffset(&index); |
+ } |
+ |
+ MemOperand ToMemOperand(InstructionOperand* op) const { |
+ ASSERT(op != NULL); |
+ ASSERT(!op->IsRegister()); |
+ ASSERT(!op->IsDoubleRegister()); |
+ ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot()); |
+ // The linkage computes where all spill slots are located. |
+ FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), 0); |
+ return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset()); |
+ } |
+}; |
+ |
+ |
+// Assembles an instruction after register allocation, producing machine code. |
+void CodeGenerator::AssembleArchInstruction(Instruction* instr) { |
+ ArmOperandConverter i(this, instr); |
+ |
+ switch (ArchOpcodeField::decode(instr->opcode())) { |
+ case kArchJmp: |
+ __ b(code_->GetLabel(i.InputBlock(0))); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArchNop: |
+ // don't emit code for nops. |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArchRet: |
+ AssembleReturn(); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArchDeoptimize: { |
+ int deoptimization_id = MiscField::decode(instr->opcode()); |
+ BuildTranslation(instr, deoptimization_id); |
+ |
+ Address deopt_entry = Deoptimizer::GetDeoptimizationEntry( |
+ isolate(), deoptimization_id, Deoptimizer::LAZY); |
+ __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmAdd: |
+ __ add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmAnd: |
+ __ and_(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmBic: |
+ __ bic(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmMul: |
+ __ mul(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmMla: |
+ __ mla(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), |
+ i.InputRegister(2), i.OutputSBit()); |
+ break; |
+ case kArmMls: { |
+ CpuFeatureScope scope(masm(), MLS); |
+ __ mls(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), |
+ i.InputRegister(2)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmSdiv: { |
+ CpuFeatureScope scope(masm(), SUDIV); |
+ __ sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmUdiv: { |
+ CpuFeatureScope scope(masm(), SUDIV); |
+ __ udiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmMov: |
+ __ Move(i.OutputRegister(), i.InputOperand2(0)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmMvn: |
+ __ mvn(i.OutputRegister(), i.InputOperand2(0), i.OutputSBit()); |
+ break; |
+ case kArmOrr: |
+ __ orr(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmEor: |
+ __ eor(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmSub: |
+ __ sub(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmRsb: |
+ __ rsb(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), |
+ i.OutputSBit()); |
+ break; |
+ case kArmBfc: { |
+ CpuFeatureScope scope(masm(), ARMv7); |
+ __ bfc(i.OutputRegister(), i.InputInt8(1), i.InputInt8(2)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmUbfx: { |
+ CpuFeatureScope scope(masm(), ARMv7); |
+ __ ubfx(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), |
+ i.InputInt8(2)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmCallCodeObject: { |
+ if (instr->InputAt(0)->IsImmediate()) { |
+ Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0)); |
+ __ Call(code, RelocInfo::CODE_TARGET); |
+ RecordSafepoint(instr->pointer_map(), Safepoint::kSimple, 0, |
+ Safepoint::kNoLazyDeopt); |
+ } else { |
+ Register reg = i.InputRegister(0); |
+ int entry = Code::kHeaderSize - kHeapObjectTag; |
+ __ ldr(reg, MemOperand(reg, entry)); |
+ __ Call(reg); |
+ RecordSafepoint(instr->pointer_map(), Safepoint::kSimple, 0, |
+ Safepoint::kNoLazyDeopt); |
+ } |
+ bool lazy_deopt = (MiscField::decode(instr->opcode()) == 1); |
+ if (lazy_deopt) { |
+ RecordLazyDeoptimizationEntry(instr); |
+ } |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmCallJSFunction: { |
+ Register func = i.InputRegister(0); |
+ |
+ // TODO(jarin) The load of the context should be separated from the call. |
+ __ ldr(cp, FieldMemOperand(func, JSFunction::kContextOffset)); |
+ __ ldr(ip, FieldMemOperand(func, JSFunction::kCodeEntryOffset)); |
+ __ Call(ip); |
+ |
+ RecordSafepoint(instr->pointer_map(), Safepoint::kSimple, 0, |
+ Safepoint::kNoLazyDeopt); |
+ RecordLazyDeoptimizationEntry(instr); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmCallAddress: { |
+ DirectCEntryStub stub(isolate()); |
+ stub.GenerateCall(masm(), i.InputRegister(0)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmPush: |
+ __ Push(i.InputRegister(0)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmDrop: { |
+ int words = MiscField::decode(instr->opcode()); |
+ __ Drop(words); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmCmp: |
+ __ cmp(i.InputRegister(0), i.InputOperand2(1)); |
+ ASSERT_EQ(SetCC, i.OutputSBit()); |
+ break; |
+ case kArmCmn: |
+ __ cmn(i.InputRegister(0), i.InputOperand2(1)); |
+ ASSERT_EQ(SetCC, i.OutputSBit()); |
+ break; |
+ case kArmTst: |
+ __ tst(i.InputRegister(0), i.InputOperand2(1)); |
+ ASSERT_EQ(SetCC, i.OutputSBit()); |
+ break; |
+ case kArmTeq: |
+ __ teq(i.InputRegister(0), i.InputOperand2(1)); |
+ ASSERT_EQ(SetCC, i.OutputSBit()); |
+ break; |
+ case kArmVcmpF64: |
+ __ VFPCompareAndSetFlags(i.InputDoubleRegister(0), |
+ i.InputDoubleRegister(1)); |
+ ASSERT_EQ(SetCC, i.OutputSBit()); |
+ break; |
+ case kArmVaddF64: |
+ __ vadd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
+ i.InputDoubleRegister(1)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmVsubF64: |
+ __ vsub(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
+ i.InputDoubleRegister(1)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmVmulF64: |
+ __ vmul(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
+ i.InputDoubleRegister(1)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmVmlaF64: |
+ __ vmla(i.OutputDoubleRegister(), i.InputDoubleRegister(1), |
+ i.InputDoubleRegister(2)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmVmlsF64: |
+ __ vmls(i.OutputDoubleRegister(), i.InputDoubleRegister(1), |
+ i.InputDoubleRegister(2)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmVdivF64: |
+ __ vdiv(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
+ i.InputDoubleRegister(1)); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmVmodF64: { |
+ // TODO(bmeurer): We should really get rid of this special instruction, |
+ // and generate a CallAddress instruction instead. |
+ FrameScope scope(masm(), StackFrame::MANUAL); |
+ __ PrepareCallCFunction(0, 2, kScratchReg); |
+ __ MovToFloatParameters(i.InputDoubleRegister(0), |
+ i.InputDoubleRegister(1)); |
+ __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()), |
+ 0, 2); |
+ // Move the result in the double result register. |
+ __ MovFromFloatResult(i.OutputDoubleRegister()); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmVnegF64: |
+ __ vneg(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); |
+ break; |
+ case kArmVcvtF64S32: { |
+ SwVfpRegister scratch = kScratchDoubleReg.low(); |
+ __ vmov(scratch, i.InputRegister(0)); |
+ __ vcvt_f64_s32(i.OutputDoubleRegister(), scratch); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmVcvtF64U32: { |
+ SwVfpRegister scratch = kScratchDoubleReg.low(); |
+ __ vmov(scratch, i.InputRegister(0)); |
+ __ vcvt_f64_u32(i.OutputDoubleRegister(), scratch); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmVcvtS32F64: { |
+ SwVfpRegister scratch = kScratchDoubleReg.low(); |
+ __ vcvt_s32_f64(scratch, i.InputDoubleRegister(0)); |
+ __ vmov(i.OutputRegister(), scratch); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmVcvtU32F64: { |
+ SwVfpRegister scratch = kScratchDoubleReg.low(); |
+ __ vcvt_u32_f64(scratch, i.InputDoubleRegister(0)); |
+ __ vmov(i.OutputRegister(), scratch); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmLoadWord8: |
+ __ ldrb(i.OutputRegister(), i.InputOffset()); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmStoreWord8: { |
+ int index = 0; |
+ MemOperand operand = i.InputOffset(&index); |
+ __ strb(i.InputRegister(index), operand); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmLoadWord16: |
+ __ ldrh(i.OutputRegister(), i.InputOffset()); |
+ break; |
+ case kArmStoreWord16: { |
+ int index = 0; |
+ MemOperand operand = i.InputOffset(&index); |
+ __ strh(i.InputRegister(index), operand); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmLoadWord32: |
+ __ ldr(i.OutputRegister(), i.InputOffset()); |
+ break; |
+ case kArmStoreWord32: { |
+ int index = 0; |
+ MemOperand operand = i.InputOffset(&index); |
+ __ str(i.InputRegister(index), operand); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmFloat64Load: |
+ __ vldr(i.OutputDoubleRegister(), i.InputOffset()); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ case kArmFloat64Store: { |
+ int index = 0; |
+ MemOperand operand = i.InputOffset(&index); |
+ __ vstr(i.InputDoubleRegister(index), operand); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ case kArmStoreWriteBarrier: { |
+ Register object = i.InputRegister(0); |
+ Register index = i.InputRegister(1); |
+ Register value = i.InputRegister(2); |
+ __ add(index, object, index); |
+ __ str(value, MemOperand(index)); |
+ SaveFPRegsMode mode = |
+ frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs; |
+ LinkRegisterStatus lr_status = kLRHasNotBeenSaved; |
+ __ RecordWrite(object, index, value, lr_status, mode); |
+ ASSERT_EQ(LeaveCC, i.OutputSBit()); |
+ break; |
+ } |
+ } |
+} |
+ |
+ |
+// Assembles branches after an instruction. |
+void CodeGenerator::AssembleArchBranch(Instruction* instr, |
+ FlagsCondition condition) { |
+ ArmOperandConverter i(this, instr); |
+ Label done; |
+ |
+ // Emit a branch. The true and false targets are always the last two inputs |
+ // to the instruction. |
+ BasicBlock* tblock = i.InputBlock(instr->InputCount() - 2); |
+ BasicBlock* fblock = i.InputBlock(instr->InputCount() - 1); |
+ bool fallthru = IsNextInAssemblyOrder(fblock); |
+ Label* tlabel = code()->GetLabel(tblock); |
+ Label* flabel = fallthru ? &done : code()->GetLabel(fblock); |
+ switch (condition) { |
+ case kUnorderedEqual: |
+ __ b(vs, flabel); |
+ // Fall through. |
+ case kEqual: |
+ __ b(eq, tlabel); |
+ break; |
+ case kUnorderedNotEqual: |
+ __ b(vs, tlabel); |
+ // Fall through. |
+ case kNotEqual: |
+ __ b(ne, tlabel); |
+ break; |
+ case kSignedLessThan: |
+ __ b(lt, tlabel); |
+ break; |
+ case kSignedGreaterThanOrEqual: |
+ __ b(ge, tlabel); |
+ break; |
+ case kSignedLessThanOrEqual: |
+ __ b(le, tlabel); |
+ break; |
+ case kSignedGreaterThan: |
+ __ b(gt, tlabel); |
+ break; |
+ case kUnorderedLessThan: |
+ __ b(vs, flabel); |
+ // Fall through. |
+ case kUnsignedLessThan: |
+ __ b(lo, tlabel); |
+ break; |
+ case kUnorderedGreaterThanOrEqual: |
+ __ b(vs, tlabel); |
+ // Fall through. |
+ case kUnsignedGreaterThanOrEqual: |
+ __ b(hs, tlabel); |
+ break; |
+ case kUnorderedLessThanOrEqual: |
+ __ b(vs, flabel); |
+ // Fall through. |
+ case kUnsignedLessThanOrEqual: |
+ __ b(ls, tlabel); |
+ break; |
+ case kUnorderedGreaterThan: |
+ __ b(vs, tlabel); |
+ // Fall through. |
+ case kUnsignedGreaterThan: |
+ __ b(hi, tlabel); |
+ break; |
+ } |
+ if (!fallthru) __ b(flabel); // no fallthru to flabel. |
+ __ bind(&done); |
+} |
+ |
+ |
+// Assembles boolean materializations after an instruction. |
+void CodeGenerator::AssembleArchBoolean(Instruction* instr, |
+ FlagsCondition condition) { |
+ ArmOperandConverter i(this, instr); |
+ Label done; |
+ |
+ // Materialize a full 32-bit 1 or 0 value. |
+ Label check; |
+ Register reg = i.OutputRegister(); |
+ Condition cc = kNoCondition; |
+ switch (condition) { |
+ case kUnorderedEqual: |
+ __ b(vc, &check); |
+ __ mov(reg, Operand(0)); |
+ __ b(&done); |
+ // Fall through. |
+ case kEqual: |
+ cc = eq; |
+ break; |
+ case kUnorderedNotEqual: |
+ __ b(vc, &check); |
+ __ mov(reg, Operand(1)); |
+ __ b(&done); |
+ // Fall through. |
+ case kNotEqual: |
+ cc = ne; |
+ break; |
+ case kSignedLessThan: |
+ cc = lt; |
+ break; |
+ case kSignedGreaterThanOrEqual: |
+ cc = ge; |
+ break; |
+ case kSignedLessThanOrEqual: |
+ cc = le; |
+ break; |
+ case kSignedGreaterThan: |
+ cc = gt; |
+ break; |
+ case kUnorderedLessThan: |
+ __ b(vc, &check); |
+ __ mov(reg, Operand(0)); |
+ __ b(&done); |
+ // Fall through. |
+ case kUnsignedLessThan: |
+ cc = lo; |
+ break; |
+ case kUnorderedGreaterThanOrEqual: |
+ __ b(vc, &check); |
+ __ mov(reg, Operand(1)); |
+ __ b(&done); |
+ // Fall through. |
+ case kUnsignedGreaterThanOrEqual: |
+ cc = hs; |
+ break; |
+ case kUnorderedLessThanOrEqual: |
+ __ b(vc, &check); |
+ __ mov(reg, Operand(0)); |
+ __ b(&done); |
+ // Fall through. |
+ case kUnsignedLessThanOrEqual: |
+ cc = ls; |
+ break; |
+ case kUnorderedGreaterThan: |
+ __ b(vc, &check); |
+ __ mov(reg, Operand(1)); |
+ __ b(&done); |
+ // Fall through. |
+ case kUnsignedGreaterThan: |
+ cc = hi; |
+ break; |
+ } |
+ __ bind(&check); |
+ __ mov(reg, Operand(0)); |
+ __ mov(reg, Operand(1), LeaveCC, cc); |
+ __ bind(&done); |
+} |
+ |
+ |
+void CodeGenerator::AssemblePrologue() { |
+ CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); |
+ if (descriptor->kind() == CallDescriptor::kCallAddress) { |
+ __ Push(lr, fp); |
+ __ mov(fp, sp); |
+ const RegList saves = descriptor->CalleeSavedRegisters(); |
+ if (saves != 0) { // Save callee-saved registers. |
+ int register_save_area_size = 0; |
+ for (int i = Register::kNumRegisters - 1; i >= 0; i--) { |
+ if (!((1 << i) & saves)) continue; |
+ register_save_area_size += kPointerSize; |
+ } |
+ frame()->SetRegisterSaveAreaSize(register_save_area_size); |
+ __ stm(db_w, sp, saves); |
+ } |
+ } else if (descriptor->IsJSFunctionCall()) { |
+ CompilationInfo* info = linkage()->info(); |
+ __ Prologue(info->IsCodePreAgingActive()); |
+ frame()->SetRegisterSaveAreaSize( |
+ StandardFrameConstants::kFixedFrameSizeFromFp); |
+ |
+ // Sloppy mode functions and builtins need to replace the receiver with the |
+ // global proxy when called as functions (without an explicit receiver |
+ // object). |
+ // TODO(mstarzinger/verwaest): Should this be moved back into the CallIC? |
+ if (info->strict_mode() == SLOPPY && !info->is_native()) { |
+ Label ok; |
+ // +2 for return address and saved frame pointer. |
+ int receiver_slot = info->scope()->num_parameters() + 2; |
+ __ ldr(r2, MemOperand(fp, receiver_slot * kPointerSize)); |
+ __ CompareRoot(r2, Heap::kUndefinedValueRootIndex); |
+ __ b(ne, &ok); |
+ __ ldr(r2, GlobalObjectOperand()); |
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalProxyOffset)); |
+ __ str(r2, MemOperand(fp, receiver_slot * kPointerSize)); |
+ __ bind(&ok); |
+ } |
+ |
+ } else { |
+ __ StubPrologue(); |
+ frame()->SetRegisterSaveAreaSize( |
+ StandardFrameConstants::kFixedFrameSizeFromFp); |
+ } |
+ int stack_slots = frame()->GetSpillSlotCount(); |
+ if (stack_slots > 0) { |
+ __ sub(sp, sp, Operand(stack_slots * kPointerSize)); |
+ } |
+} |
+ |
+ |
+void CodeGenerator::AssembleReturn() { |
+ CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); |
+ if (descriptor->kind() == CallDescriptor::kCallAddress) { |
+ if (frame()->GetRegisterSaveAreaSize() > 0) { |
+ // Remove this frame's spill slots first. |
+ int stack_slots = frame()->GetSpillSlotCount(); |
+ if (stack_slots > 0) { |
+ __ add(sp, sp, Operand(stack_slots * kPointerSize)); |
+ } |
+ // Restore registers. |
+ const RegList saves = descriptor->CalleeSavedRegisters(); |
+ if (saves != 0) { |
+ __ ldm(ia_w, sp, saves); |
+ } |
+ } |
+ __ mov(sp, fp); |
+ __ ldm(ia_w, sp, fp.bit() | lr.bit()); |
+ __ Ret(); |
+ } else { |
+ __ mov(sp, fp); |
+ __ ldm(ia_w, sp, fp.bit() | lr.bit()); |
+ int pop_count = |
+ descriptor->IsJSFunctionCall() ? descriptor->ParameterCount() : 0; |
+ __ Drop(pop_count); |
+ __ Ret(); |
+ } |
+} |
+ |
+ |
+void CodeGenerator::AssembleMove(InstructionOperand* source, |
+ InstructionOperand* destination) { |
+ ArmOperandConverter g(this, NULL); |
+ // Dispatch on the source and destination operand kinds. Not all |
+ // combinations are possible. |
+ if (source->IsRegister()) { |
+ ASSERT(destination->IsRegister() || destination->IsStackSlot()); |
+ Register src = g.ToRegister(source); |
+ if (destination->IsRegister()) { |
+ __ mov(g.ToRegister(destination), src); |
+ } else { |
+ __ str(src, g.ToMemOperand(destination)); |
+ } |
+ } else if (source->IsStackSlot()) { |
+ ASSERT(destination->IsRegister() || destination->IsStackSlot()); |
+ MemOperand src = g.ToMemOperand(source); |
+ if (destination->IsRegister()) { |
+ __ ldr(g.ToRegister(destination), src); |
+ } else { |
+ Register temp = kScratchReg; |
+ __ ldr(temp, src); |
+ __ str(temp, g.ToMemOperand(destination)); |
+ } |
+ } else if (source->IsConstant()) { |
+ if (destination->IsRegister() || destination->IsStackSlot()) { |
+ Register dst = |
+ destination->IsRegister() ? g.ToRegister(destination) : kScratchReg; |
+ Constant src = g.ToConstant(source); |
+ switch (src.type()) { |
+ case Constant::kInt32: |
+ __ mov(dst, Operand(src.ToInt32())); |
+ break; |
+ case Constant::kInt64: |
+ UNREACHABLE(); |
+ break; |
+ case Constant::kFloat64: |
+ __ Move(dst, |
+ isolate()->factory()->NewNumber(src.ToFloat64(), TENURED)); |
+ break; |
+ case Constant::kExternalReference: |
+ __ mov(dst, Operand(src.ToExternalReference())); |
+ break; |
+ case Constant::kHeapObject: |
+ __ Move(dst, src.ToHeapObject()); |
+ break; |
+ } |
+ if (destination->IsStackSlot()) __ str(dst, g.ToMemOperand(destination)); |
+ } else if (destination->IsDoubleRegister()) { |
+ DwVfpRegister result = g.ToDoubleRegister(destination); |
+ __ vmov(result, g.ToDouble(source)); |
+ } else { |
+ ASSERT(destination->IsDoubleStackSlot()); |
+ DwVfpRegister temp = kScratchDoubleReg; |
+ __ vmov(temp, g.ToDouble(source)); |
+ __ vstr(temp, g.ToMemOperand(destination)); |
+ } |
+ } else if (source->IsDoubleRegister()) { |
+ DwVfpRegister src = g.ToDoubleRegister(source); |
+ if (destination->IsDoubleRegister()) { |
+ DwVfpRegister dst = g.ToDoubleRegister(destination); |
+ __ Move(dst, src); |
+ } else { |
+ ASSERT(destination->IsDoubleStackSlot()); |
+ __ vstr(src, g.ToMemOperand(destination)); |
+ } |
+ } else if (source->IsDoubleStackSlot()) { |
+ ASSERT(destination->IsDoubleRegister() || destination->IsDoubleStackSlot()); |
+ MemOperand src = g.ToMemOperand(source); |
+ if (destination->IsDoubleRegister()) { |
+ __ vldr(g.ToDoubleRegister(destination), src); |
+ } else { |
+ DwVfpRegister temp = kScratchDoubleReg; |
+ __ vldr(temp, src); |
+ __ vstr(temp, g.ToMemOperand(destination)); |
+ } |
+ } else { |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void CodeGenerator::AssembleSwap(InstructionOperand* source, |
+ InstructionOperand* destination) { |
+ ArmOperandConverter g(this, NULL); |
+ // Dispatch on the source and destination operand kinds. Not all |
+ // combinations are possible. |
+ if (source->IsRegister()) { |
+ // Register-register. |
+ Register temp = kScratchReg; |
+ Register src = g.ToRegister(source); |
+ if (destination->IsRegister()) { |
+ Register dst = g.ToRegister(destination); |
+ __ Move(temp, src); |
+ __ Move(src, dst); |
+ __ Move(dst, temp); |
+ } else { |
+ ASSERT(destination->IsStackSlot()); |
+ MemOperand dst = g.ToMemOperand(destination); |
+ __ mov(temp, src); |
+ __ ldr(src, dst); |
+ __ str(temp, dst); |
+ } |
+ } else if (source->IsStackSlot()) { |
+ ASSERT(destination->IsStackSlot()); |
+ Register temp_0 = kScratchReg; |
+ SwVfpRegister temp_1 = kScratchDoubleReg.low(); |
+ MemOperand src = g.ToMemOperand(source); |
+ MemOperand dst = g.ToMemOperand(destination); |
+ __ ldr(temp_0, src); |
+ __ vldr(temp_1, dst); |
+ __ str(temp_0, dst); |
+ __ vstr(temp_1, src); |
+ } else if (source->IsDoubleRegister()) { |
+ DwVfpRegister temp = kScratchDoubleReg; |
+ DwVfpRegister src = g.ToDoubleRegister(source); |
+ if (destination->IsDoubleRegister()) { |
+ DwVfpRegister dst = g.ToDoubleRegister(destination); |
+ __ Move(temp, src); |
+ __ Move(src, dst); |
+ __ Move(src, temp); |
+ } else { |
+ ASSERT(destination->IsDoubleStackSlot()); |
+ MemOperand dst = g.ToMemOperand(destination); |
+ __ Move(temp, src); |
+ __ vldr(src, dst); |
+ __ vstr(temp, dst); |
+ } |
+ } else if (source->IsDoubleStackSlot()) { |
+ ASSERT(destination->IsDoubleStackSlot()); |
+ Register temp_0 = kScratchReg; |
+ DwVfpRegister temp_1 = kScratchDoubleReg; |
+ MemOperand src0 = g.ToMemOperand(source); |
+ MemOperand src1(src0.rn(), src0.offset() + kPointerSize); |
+ MemOperand dst0 = g.ToMemOperand(destination); |
+ MemOperand dst1(dst0.rn(), dst0.offset() + kPointerSize); |
+ __ vldr(temp_1, dst0); // Save destination in temp_1. |
+ __ ldr(temp_0, src0); // Then use temp_0 to copy source to destination. |
+ __ str(temp_0, dst0); |
+ __ ldr(temp_0, src1); |
+ __ str(temp_0, dst1); |
+ __ vstr(temp_1, src0); |
+ } else { |
+ // No other combinations are possible. |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void CodeGenerator::AddNopForSmiCodeInlining() { |
+ // On 32-bit ARM we do not insert nops for inlined Smi code. |
+ UNREACHABLE(); |
+} |
+ |
+#ifdef DEBUG |
+ |
+// Checks whether the code between start_pc and end_pc is a no-op. |
+bool CodeGenerator::IsNopForSmiCodeInlining(Handle<Code> code, int start_pc, |
+ int end_pc) { |
+ return false; |
+} |
+ |
+#endif // DEBUG |
+ |
+#undef __ |
+} |
+} |
+} // namespace v8::internal::compiler |