| 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
|
|
|