| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <math.h> // for double sqrt(double) |
| |
| |
| /* |
| * This file is included by Codegen-armv5te-vfp.c, and implements architecture |
| * variant-specific code. |
| */ |
| |
| /* |
| * Determine the initial instruction set to be used for this trace. |
| * Later components may decide to change this. |
| */ |
| JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit) |
| { |
| return DALVIK_JIT_THUMB2; |
| } |
| |
| /* |
| * Jump to the out-of-line handler in ARM mode to finish executing the |
| * remaining of more complex instructions. |
| */ |
| static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode) |
| { |
| /* |
| * NOTE - In practice BLX only needs one operand, but since the assembler |
| * may abort itself and retry due to other out-of-range conditions we |
| * cannot really use operand[0] to store the absolute target address since |
| * it may get clobbered by the final relative offset. Therefore, |
| * we fake BLX_1 is a two operand instruction and the absolute target |
| * address is stored in operand[1]. |
| */ |
| clobberHandlerRegs(cUnit); |
| newLIR2(cUnit, kThumbBlx1, |
| (int) gDvmJit.codeCache + templateEntryOffsets[opCode], |
| (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); |
| newLIR2(cUnit, kThumbBlx2, |
| (int) gDvmJit.codeCache + templateEntryOffsets[opCode], |
| (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); |
| } |
| |
| void *dvmCompilerGetInterpretTemplate() |
| { |
| return (void*) ((int)gDvmJit.codeCache + |
| templateEntryOffsets[TEMPLATE_INTERPRET]); |
| } |
| |
| /* Architecture-specific initializations and checks go here */ |
| static bool compilerArchVariantInit(void) |
| { |
| /* First, declare dvmCompiler_TEMPLATE_XXX for each template */ |
| #define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X(); |
| #include "../../../template/armv5te-vfp/TemplateOpList.h" |
| #undef JIT_TEMPLATE |
| |
| int i = 0; |
| extern void dvmCompilerTemplateStart(void); |
| |
| /* |
| * Then, populate the templateEntryOffsets array with the offsets from the |
| * the dvmCompilerTemplateStart symbol for each template. |
| */ |
| #define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \ |
| (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart; |
| #include "../../../template/armv5te-vfp/TemplateOpList.h" |
| #undef JIT_TEMPLATE |
| |
| /* Codegen-specific assumptions */ |
| assert(offsetof(ClassObject, vtable) < 128 && |
| (offsetof(ClassObject, vtable) & 0x3) == 0); |
| assert(offsetof(ArrayObject, length) < 128 && |
| (offsetof(ArrayObject, length) & 0x3) == 0); |
| assert(offsetof(ArrayObject, contents) < 256); |
| |
| /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ |
| assert(sizeof(StackSaveArea) < 236); |
| |
| /* |
| * EA is calculated by doing "Rn + imm5 << 2", and there are 5 entry points |
| * that codegen may access, make sure that the offset from the top of the |
| * struct is less than 108. |
| */ |
| assert(offsetof(InterpState, jitToInterpEntries) < 108); |
| return true; |
| } |
| |
| static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir) |
| { |
| ArmLIR *branch; |
| DecodedInstruction *dInsn = &mir->dalvikInsn; |
| RegLocation rlSrc = getSrcLocWide(cUnit, mir, 0, 1); |
| RegLocation rlDest = inlinedTargetWide(cUnit, mir, true); |
| rlSrc = loadValueWide(cUnit, rlSrc, kFPReg); |
| RegLocation rlResult = evalLoc(cUnit, rlDest, kFPReg, true); |
| newLIR2(cUnit, kThumb2Vsqrtd, S2D(rlResult.lowReg, rlResult.highReg), |
| S2D(rlSrc.lowReg, rlSrc.highReg)); |
| newLIR2(cUnit, kThumb2Vcmpd, S2D(rlResult.lowReg, rlResult.highReg), |
| S2D(rlResult.lowReg, rlResult.highReg)); |
| newLIR0(cUnit, kThumb2Fmstat); |
| branch = newLIR2(cUnit, kThumbBCond, 0, kArmCondEq); |
| clobberCallRegs(cUnit); |
| loadConstant(cUnit, r2, (int)sqrt); |
| newLIR3(cUnit, kThumb2Fmrrd, r0, r1, S2D(rlSrc.lowReg, rlSrc.highReg)); |
| newLIR1(cUnit, kThumbBlxR, r2); |
| newLIR3(cUnit, kThumb2Fmdrr, S2D(rlResult.lowReg, rlResult.highReg), |
| r0, r1); |
| ArmLIR *label = newLIR0(cUnit, kArmPseudoTargetLabel); |
| label->defMask = ENCODE_ALL; |
| branch->generic.target = (LIR *)label; |
| storeValueWide(cUnit, rlDest, rlResult); |
| return true; |
| } |
| |
| static bool handleArithOpFloat(CompilationUnit *cUnit, MIR *mir, |
| RegLocation rlDest, RegLocation rlSrc1, |
| RegLocation rlSrc2) |
| { |
| int op = kThumbBkpt; |
| RegLocation rlResult; |
| |
| /* |
| * Don't attempt to optimize register usage since these opcodes call out to |
| * the handlers. |
| */ |
| switch (mir->dalvikInsn.opCode) { |
| case OP_ADD_FLOAT_2ADDR: |
| case OP_ADD_FLOAT: |
| op = kThumb2Vadds; |
| break; |
| case OP_SUB_FLOAT_2ADDR: |
| case OP_SUB_FLOAT: |
| op = kThumb2Vsubs; |
| break; |
| case OP_DIV_FLOAT_2ADDR: |
| case OP_DIV_FLOAT: |
| op = kThumb2Vdivs; |
| break; |
| case OP_MUL_FLOAT_2ADDR: |
| case OP_MUL_FLOAT: |
| op = kThumb2Vmuls; |
| break; |
| case OP_REM_FLOAT_2ADDR: |
| case OP_REM_FLOAT: |
| case OP_NEG_FLOAT: { |
| return handleArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, |
| rlSrc2); |
| } |
| default: |
| return true; |
| } |
| rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg); |
| rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg); |
| rlResult = evalLoc(cUnit, rlDest, kFPReg, true); |
| newLIR3(cUnit, op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg); |
| storeValue(cUnit, rlDest, rlResult); |
| return false; |
| } |
| |
| static bool handleArithOpDouble(CompilationUnit *cUnit, MIR *mir, |
| RegLocation rlDest, RegLocation rlSrc1, |
| RegLocation rlSrc2) |
| { |
| int op = kThumbBkpt; |
| RegLocation rlResult; |
| |
| switch (mir->dalvikInsn.opCode) { |
| case OP_ADD_DOUBLE_2ADDR: |
| case OP_ADD_DOUBLE: |
| op = kThumb2Vaddd; |
| break; |
| case OP_SUB_DOUBLE_2ADDR: |
| case OP_SUB_DOUBLE: |
| op = kThumb2Vsubd; |
| break; |
| case OP_DIV_DOUBLE_2ADDR: |
| case OP_DIV_DOUBLE: |
| op = kThumb2Vdivd; |
| break; |
| case OP_MUL_DOUBLE_2ADDR: |
| case OP_MUL_DOUBLE: |
| op = kThumb2Vmuld; |
| break; |
| case OP_REM_DOUBLE_2ADDR: |
| case OP_REM_DOUBLE: |
| case OP_NEG_DOUBLE: { |
| return handleArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, |
| rlSrc2); |
| } |
| default: |
| return true; |
| } |
| |
| rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg); |
| assert(rlSrc1.wide); |
| rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg); |
| assert(rlSrc2.wide); |
| rlResult = evalLoc(cUnit, rlDest, kFPReg, true); |
| assert(rlDest.wide); |
| assert(rlResult.wide); |
| newLIR3(cUnit, op, S2D(rlResult.lowReg, rlResult.highReg), |
| S2D(rlSrc1.lowReg, rlSrc1.highReg), |
| S2D(rlSrc2.lowReg, rlSrc2.highReg)); |
| storeValueWide(cUnit, rlDest, rlResult); |
| return false; |
| } |
| |
| static bool handleConversion(CompilationUnit *cUnit, MIR *mir) |
| { |
| OpCode opCode = mir->dalvikInsn.opCode; |
| int op = kThumbBkpt; |
| bool longSrc = false; |
| bool longDest = false; |
| int srcReg; |
| RegLocation rlSrc; |
| RegLocation rlDest; |
| RegLocation rlResult; |
| |
| switch (opCode) { |
| case OP_INT_TO_FLOAT: |
| longSrc = false; |
| longDest = false; |
| op = kThumb2VcvtIF; |
| break; |
| case OP_FLOAT_TO_INT: |
| longSrc = false; |
| longDest = false; |
| op = kThumb2VcvtFI; |
| break; |
| case OP_DOUBLE_TO_FLOAT: |
| longSrc = true; |
| longDest = false; |
| op = kThumb2VcvtDF; |
| break; |
| case OP_FLOAT_TO_DOUBLE: |
| longSrc = false; |
| longDest = true; |
| op = kThumb2VcvtFd; |
| break; |
| case OP_INT_TO_DOUBLE: |
| longSrc = false; |
| longDest = true; |
| op = kThumb2VcvtID; |
| break; |
| case OP_DOUBLE_TO_INT: |
| longSrc = true; |
| longDest = false; |
| op = kThumb2VcvtDI; |
| break; |
| case OP_LONG_TO_DOUBLE: |
| case OP_FLOAT_TO_LONG: |
| case OP_LONG_TO_FLOAT: |
| case OP_DOUBLE_TO_LONG: |
| return handleConversionPortable(cUnit, mir); |
| default: |
| return true; |
| } |
| if (longSrc) { |
| rlSrc = getSrcLocWide(cUnit, mir, 0, 1); |
| rlSrc = loadValueWide(cUnit, rlSrc, kFPReg); |
| srcReg = S2D(rlSrc.lowReg, rlSrc.highReg); |
| } else { |
| rlSrc = getSrcLoc(cUnit, mir, 0); |
| rlSrc = loadValue(cUnit, rlSrc, kFPReg); |
| srcReg = rlSrc.lowReg; |
| } |
| if (longDest) { |
| rlDest = getDestLocWide(cUnit, mir, 0, 1); |
| rlResult = evalLoc(cUnit, rlDest, kFPReg, true); |
| newLIR2(cUnit, op, S2D(rlResult.lowReg, rlResult.highReg), srcReg); |
| storeValueWide(cUnit, rlDest, rlResult); |
| } else { |
| rlDest = getDestLoc(cUnit, mir, 0); |
| rlResult = evalLoc(cUnit, rlDest, kFPReg, true); |
| newLIR2(cUnit, op, rlResult.lowReg, srcReg); |
| storeValue(cUnit, rlDest, rlResult); |
| } |
| return false; |
| } |
| |
| static bool handleCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest, |
| RegLocation rlSrc1, RegLocation rlSrc2) |
| { |
| bool isDouble; |
| int defaultResult; |
| bool ltNaNBias; |
| RegLocation rlResult; |
| |
| switch(mir->dalvikInsn.opCode) { |
| case OP_CMPL_FLOAT: |
| isDouble = false; |
| defaultResult = -1; |
| break; |
| case OP_CMPG_FLOAT: |
| isDouble = false; |
| defaultResult = 1; |
| break; |
| case OP_CMPL_DOUBLE: |
| isDouble = true; |
| defaultResult = -1; |
| break; |
| case OP_CMPG_DOUBLE: |
| isDouble = true; |
| defaultResult = 1; |
| break; |
| default: |
| return true; |
| } |
| if (isDouble) { |
| rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg); |
| rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg); |
| clobberSReg(cUnit, rlDest.sRegLow); |
| rlResult = evalLoc(cUnit, rlDest, kCoreReg, true); |
| loadConstant(cUnit, rlResult.lowReg, defaultResult); |
| newLIR2(cUnit, kThumb2Vcmpd, S2D(rlSrc1.lowReg, r1Src2.highReg), |
| S2D(rlSrc2.lowReg, rlSrc2.highReg)); |
| } else { |
| rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg); |
| rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg); |
| clobberSReg(cUnit, rlDest.sRegLow); |
| rlResult = evalLoc(cUnit, rlDest, kCoreReg, true); |
| loadConstant(cUnit, rlResult.lowReg, defaultResult); |
| newLIR2(cUnit, kThumb2Vcmps, rlSrc1.lowReg, rlSrc2.lowReg); |
| } |
| assert(!FPREG(rlResult.lowReg)); |
| newLIR0(cUnit, kThumb2Fmstat); |
| genIT(cUnit, (defaultResult == -1) ? kArmCondGt : kArmCondMi, ""); |
| newLIR2(cUnit, kThumb2MovImmShift, rlResult.lowReg, |
| modifiedImmediate(-defaultResult)); // Must not alter ccodes |
| genIT(cUnit, kArmCondEq, ""); |
| loadConstant(cUnit, rlResult.lowReg, 0); |
| storeValue(cUnit, rlDest, rlResult); |
| return false; |
| } |