/* * Copyright (c) 2005-2011 Imperas Software Ltd., www.imperas.com * * YOUR ACCESS TO THE INFORMATION IN THIS MODEL IS CONDITIONAL * UPON YOUR ACCEPTANCE THAT YOU WILL NOT USE OR PERMIT OTHERS * TO USE THE INFORMATION FOR THE PURPOSES OF DETERMINING WHETHER * IMPLEMENTATIONS OF THE ARM ARCHITECTURE INFRINGE ANY THIRD * PARTY PATENTS. * * THE LICENSE BELOW EXTENDS ONLY TO USE OF THE SOFTWARE FOR * MODELING PURPOSES AND SHALL NOT BE CONSTRUED AS GRANTING * A LICENSE TO CREATE A HARDWARE IMPLEMENTATION OF THE * FUNCTIONALITY OF THE SOFTWARE LICENSED HEREUNDER. * YOU MAY USE THE SOFTWARE SUBJECT TO THE LICENSE TERMS BELOW * PROVIDED THAT YOU ENSURE THAT THIS NOTICE IS REPLICATED UNMODIFIED * AND IN ITS ENTIRETY IN ALL DISTRIBUTIONS OF THE SOFTWARE, * MODIFIED OR UNMODIFIED, IN SOURCE CODE OR IN BINARY FORM. * * Licensed under an Imperas Modfied 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.ovpworld.org/licenses/OVP_MODIFIED_1.0_APACHE_OPEN_SOURCE_LICENSE_2.0.pdf * * 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. * */ // VMI header files #include "vmi/vmiMessage.h" // model header files #include "armConfig.h" #include "armDecode.h" #include "armEmit.h" #include "armExceptions.h" #include "armFunctions.h" #include "armMessage.h" #include "armMorph.h" #include "armMorphFunctions.h" #include "armRegisters.h" #include "armStructure.h" #include "armUtils.h" #include "armVariant.h" #include "armVM.h" #include "armVFP.h" // // Prefix for messages from this module // #define CPU_PREFIX "ARM_MORPH" //////////////////////////////////////////////////////////////////////////////// // EXCEPTIONS //////////////////////////////////////////////////////////////////////////////// // // Generic exception callback // #define EXCEPTION_FN(_NAME) void _NAME(armP arm, Uns32 thisPC) typedef EXCEPTION_FN((*exceptionFn)); // // Emit call to exception function // static void emitExceptionCall(armMorphStateP state, exceptionFn cb) { Uns32 bits = ARM_GPR_BITS; // emit processor argument armEmitArgProcessor(state); armEmitArgSimPC(state, bits); armEmitCall(state, (vmiCallFn)cb); // terminate the current block armEmitEndBlock(); } //////////////////////////////////////////////////////////////////////////////// // UNIMPLEMENTED/UNDEFINED INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Called for an unimplemented instruction // static void unimplemented(armP arm, Uns32 thisPC) { vmiMessage("F", CPU_PREFIX"_UII", SRCREF_FMT "unimplemented instruction", SRCREF_ARGS(arm, thisPC) ); } // // Default morpher callback for unimplemented instructions // static void emitUnimplemented(armMorphStateP state) { armEmitArgProcessor(state); armEmitArgSimPC(state, ARM_GPR_BITS); armEmitCall(state, (vmiCallFn)unimplemented); armEmitExit(); } // // Called for UsageFault exception // static void usageFault(armP arm, Uns32 thisPC) { vmiMessage("E", CPU_PREFIX"_UDI", SRCREF_FMT "usage fault - enable simulated exceptions " "to continue simulation if this behavior is desired", SRCREF_ARGS(arm, thisPC) ); } // // Emit code for a UsageFault exception // static void emitUsageFault(armMorphStateP state, Uns32 reason) { Uns32 bits = ARM_GPR_BITS; // update UFSR (CFSR) armEmitBinopRC(state, bits, vmi_OR, ARM_SCS_REG(SCS_ID(CFSR)), reason, 0); // handle UsageFault if(state->arm->simEx) { emitExceptionCall(state, armUsageFault); } else { armEmitArgProcessor(state); armEmitArgSimPC(state, bits); armEmitCall(state, (vmiCallFn)usageFault); armEmitExit(); } } // // Called for an instruction that isn't supported on this variant // static void notVariantMessage(armP arm, Uns32 thisPC) { vmiMessage("W", CPU_PREFIX"_NSV", SRCREF_FMT "not supported on this variant", SRCREF_ARGS(arm, thisPC) ); } // // Morpher callback for instructions that are not valid on this processor // variant // static ARM_MORPH_FN(emitNotVariant) { // report the offending instruction in verbose mode if(state->arm->verbose) { armEmitArgProcessor(state); armEmitArgSimPC(state, ARM_GPR_BITS); armEmitCall(state, (vmiCallFn)notVariantMessage); } // take UsageFault exception emitUsageFault(state, EXC_UNDEF_UNDEFINSTR); } // // Called for an undefined coprocessor instruction // static void undefinedCPMessage(armP arm, Uns32 thisPC) { vmiMessage("a", CPU_PREFIX"_UCA", SRCREF_FMT "unsupported coprocessor access", SRCREF_ARGS(arm, thisPC) ); } //////////////////////////////////////////////////////////////////////////////// // FLAGS //////////////////////////////////////////////////////////////////////////////// const static vmiFlags flagsCIn = { ARM_CF_CONST, { [vmi_CF] = VMI_NOFLAG_CONST, [vmi_PF] = VMI_NOFLAG_CONST, [vmi_ZF] = VMI_NOFLAG_CONST, [vmi_SF] = VMI_NOFLAG_CONST, [vmi_OF] = VMI_NOFLAG_CONST } }; const static vmiFlags flagsCOut = { ARM_CF_CONST, { [vmi_CF] = ARM_CF_CONST, [vmi_PF] = VMI_NOFLAG_CONST, [vmi_ZF] = VMI_NOFLAG_CONST, [vmi_SF] = VMI_NOFLAG_CONST, [vmi_OF] = VMI_NOFLAG_CONST } }; // Macro accessors for flags #define FLAGS_CIN &flagsCIn #define FLAGS_COUT &flagsCOut // // Return condition flags associated with an instruction, or null if no flags // are applicable // static vmiFlagsCP getFlagsOrNull(armMorphStateP state) { return state->info.f ? state->attrs->flagsRW : 0; } // // Return condition flags associated with an instruction, or FLAGS_CIN if no // flags are applicable // static vmiFlagsCP getFlagsOrCIn(armMorphStateP state, vmiReg rd) { if(!state->info.f) { return state->attrs->flagsR; } else if(!VMI_REG_EQUAL(rd, ARM_PC)) { return state->attrs->flagsRW; } else { return state->attrs->flagsR; } } // // If the instruction sets flags from the shifter output, return FLAGS_COUT // static vmiFlagsCP getShifterCOut(armMorphStateP state, vmiReg rd) { if(!state->info.f) { return FLAGS_CIN; } else if(!state->attrs->shiftCOut) { return FLAGS_CIN; } else if(!VMI_REG_EQUAL(rd, ARM_PC)) { return FLAGS_COUT; } else { return FLAGS_CIN; } } // // Do the flags specify that the shifter sets the carry out? // inline static Bool shifterSetsCOut(vmiFlagsCP flags) { return (flags==FLAGS_COUT); } //////////////////////////////////////////////////////////////////////////////// // REGISTER ACCESS UTILITIES //////////////////////////////////////////////////////////////////////////////// // // Register access macros // #define GET_RD(_S, _R) getRD(_S, _S->info._R) #define GET_RS(_S, _R) getRS(_S, _S->info._R) // VFP register access macros #define GET_VFP_REG(_S, _R, _SZ) getVFPReg((_S), (_S)->info._R, (_SZ)) #define GET_VFP_SREG(_S, _R) getVFPReg((_S), (_S)->info._R, 4) #define GET_VFP_DREG(_S, _R) getVFPReg((_S), (_S)->info._R, 8) #define GET_VFP_SCALAR(_S, _R, _IDX) getVFPReg((_S), (2*((_S)->info._R))+(_IDX), 4) // // Return vmiReg for GPR with the passed index // NOTE: PC is a temporary, others are true registers // static vmiReg getGPR(armMorphStateP state, Uns32 index) { return index==ARM_REG_PC ? ARM_PC : ARM_REG(index); } // // Return vmiReg for target GPR with the passed index // static vmiReg getRD(armMorphStateP state, Uns32 index) { return getGPR(state, index); } // // Return vmiReg for source GPR with the passed index // static vmiReg getRS(armMorphStateP state, Uns32 index) { vmiReg r = getGPR(state, index); // ensure PC is created on demand if required if(VMI_REG_EQUAL(r, ARM_PC)) { armEmitGetPC(state); } return r; } // // Return vmiReg object for low half of 32-bit register // inline static vmiReg getR32Lo(vmiReg reg32) { return reg32; } // // Return vmiReg object for high half of 32-bit register // inline static vmiReg getR32Hi(vmiReg reg32) { return VMI_REG_DELTA(reg32, sizeof(Uns16)); } // // Return vmiReg object for low half of 64-bit register // inline static vmiReg getR64Lo(vmiReg reg64) { return reg64; } // // Return vmiReg object for high half of 64-bit register // inline static vmiReg getR64Hi(vmiReg reg64) { return VMI_REG_DELTA(reg64, sizeof(Uns32)); } // // Return vmiReg for VFP register, addressed as a Single or Double precision // static vmiReg getVFPReg(armMorphStateP state, Uns32 regNum, Uns32 ebytes) { VMI_ASSERT(ebytes==2 || ebytes==4 || ebytes == 8, "Invalid VFP ebytes %d", ebytes); armP arm = state->arm; vmiReg reg = VMI_NOREG; Uns32 vfpRegisters = SCS_FIELD(arm, MVFR0, A_SIMD_Registers); Uns32 numRegs = (vfpRegisters == 1) ? ARM_VFP16_REG_NUM : ARM_VFP32_REG_NUM; if(!vfpRegisters) { // VFP Registers are not present emitUsageFault(state, EXC_UNDEF_UNDEFINSTR); } else if((regNum*ebytes) >= (numRegs*ARM_VFP_REG_BYTES)) { // Register number is out of range of the implemented number of registers emitUsageFault(state, EXC_UNDEF_UNDEFINSTR); } else { // get the indicated register switch (ebytes) { case 1: reg = ARM_BREG(regNum); break; case 2: reg = ARM_HREG(regNum); break; case 4: reg = ARM_WREG(regNum); break; case 8: reg = ARM_DREG(regNum); break; default: VMI_ABORT("Invalid ebytes %d", ebytes); } } return reg; } //////////////////////////////////////////////////////////////////////////////// // INSTRUCTION SKIPPING UTILITIES //////////////////////////////////////////////////////////////////////////////// // // Save the skip label on the info structure // inline static void setSkipLabel(armMorphStateP state, vmiLabelP label) { state->skipLabel = label; } // // Return the skip label on the info structure // inline static vmiLabelP getSkipLabel(armMorphStateP state) { return state->skipLabel; } // // Insert a label // inline static void emitLabel(vmiLabelP label) { if(label) { armEmitInsertLabel(label); } } //////////////////////////////////////////////////////////////////////////////// // MISCELLANEOUS UTILITIES //////////////////////////////////////////////////////////////////////////////// // // Return the version of the instruction set implemented by the processor // inline static Uns32 getInstructionVersion(armP arm) { return ARM_INSTRUCTION_VERSION(arm->configInfo.arch); } // // Return the constant value adjusted so that when added to the PC the result // is always word aligned // inline static Uns32 alignConstWithPC(armMorphStateP state, Uns32 c) { return c - (state->info.thisPC & 2); } //////////////////////////////////////////////////////////////////////////////// // TEMPORARIES //////////////////////////////////////////////////////////////////////////////// // // Get the current active temporary // inline static vmiReg getTemp(armMorphStateP state) { VMI_ASSERT(state->tempIdxtempIdx); } // // Allocate a new 32-bit temporary // inline static vmiReg newTemp32(armMorphStateP state) { vmiReg temp = getTemp(state); state->tempIdx++; return temp; } // // Free a 32-bit temporary // inline static void freeTemp32(armMorphStateP state) { VMI_ASSERT(state->tempIdx, "%s: temporary overflow", FUNC_NAME); state->tempIdx--; } // // Allocate a new 64-bit temporary // inline static vmiReg newTemp64(armMorphStateP state) { vmiReg temp = newTemp32(state); newTemp32(state); return temp; } // // Free a 64-bit temporary // inline static void freeTemp64(armMorphStateP state) { freeTemp32(state); freeTemp32(state); } // // Allocate a new 128-bit temporary // inline static vmiReg newTemp128(armMorphStateP state) { vmiReg temp = newTemp64(state); newTemp64(state); return temp; } // // Free a 128-bit temporary // inline static void freeTemp128(armMorphStateP state) { freeTemp64(state); freeTemp64(state); } //////////////////////////////////////////////////////////////////////////////// // TEMPORARY FLAGS //////////////////////////////////////////////////////////////////////////////// // // Return vmiFlags generating sign and overflow in temporary // static vmiFlags getSFOFFlags(vmiReg tf) { vmiFlags flags = VMI_NOFLAGS; // define overflow and sign flags vmiReg of = tf; vmiReg sf = VMI_REG_DELTA(of, 1); // define vmiFlags structure using the overflow and sign flags flags.f[vmi_SF] = sf; flags.f[vmi_OF] = of; // return vmiFlags structure return flags; } // // Return vmiFlags generating overflow in temporary // static vmiFlags getOFFlags(vmiReg tf) { vmiFlags flags = VMI_NOFLAGS; // define vmiFlags structure using the overflow flag flags.f[vmi_OF] = tf; // return vmiFlags structure return flags; } // // Return vmiFlags generating zero flag in temporary // static vmiFlags getZFFlags(vmiReg tf) { vmiFlags flags = VMI_NOFLAGS; // define vmiFlags structure using the carry flag flags.f[vmi_ZF] = tf; // return vmiFlags structure return flags; } // // Return vmiFlags generating and using carry in temporary // static vmiFlags getCFFlags(vmiReg tf) { vmiFlags flags = VMI_NOFLAGS; // define vmiFlags structure using the carry flag flags.cin = tf; flags.f[vmi_CF] = tf; // return vmiFlags structure return flags; } //////////////////////////////////////////////////////////////////////////////// // CONDITIONAL INSTRUCTION EXECUTION //////////////////////////////////////////////////////////////////////////////// // // For conditions, this enumeration describes the circumstances under which the // condition is satisfied // typedef enum armCondOpE { ACO_ALWAYS = 0, // condition always True ACO_FALSE = 1, // condition satisfied if flag unset ACO_TRUE = 2, // condition satisfied if flag set } armCondOp; // // For conditions, this structure describes a flag and a value for a match // typedef struct armCondS { vmiReg flag; armCondOp op; } armCond, *armCondP; // // Emit code to prepare a conditional operation and return an armCond structure // giving the offset of a flag to compare against // static armCond emitPrepareCondition(armMorphStateP state) { const static armCond condTable[ARM_C_LAST] = { [ARM_C_EQ] = {ARM_ZF_CONST, ACO_TRUE }, // ZF==1 [ARM_C_NE] = {ARM_ZF_CONST, ACO_FALSE }, // ZF==0 [ARM_C_CS] = {ARM_CF_CONST, ACO_TRUE }, // CF==1 [ARM_C_CC] = {ARM_CF_CONST, ACO_FALSE }, // CF==0 [ARM_C_MI] = {ARM_NF_CONST, ACO_TRUE }, // NF==1 [ARM_C_PL] = {ARM_NF_CONST, ACO_FALSE }, // NF==0 [ARM_C_VS] = {ARM_VF_CONST, ACO_TRUE }, // VF==1 [ARM_C_VC] = {ARM_VF_CONST, ACO_FALSE }, // VF==0 [ARM_C_HI] = {ARM_HI_CONST, ACO_TRUE }, // (CF==1) && (ZF==0) [ARM_C_LS] = {ARM_HI_CONST, ACO_FALSE }, // (CF==0) || (ZF==1) [ARM_C_GE] = {ARM_LT_CONST, ACO_FALSE }, // NF==VF [ARM_C_LT] = {ARM_LT_CONST, ACO_TRUE }, // NF!=VF [ARM_C_GT] = {ARM_LE_CONST, ACO_FALSE }, // (ZF==0) && (NF==VF) [ARM_C_LE] = {ARM_LE_CONST, ACO_TRUE }, // (ZF==1) || (NF!=VF) [ARM_C_AL] = {VMI_NOREG, ACO_ALWAYS}, // always [ARM_C_NV] = {VMI_NOREG, ACO_ALWAYS} // always (historically never) }; // get the table entry corresponding to the instruction condition armCond cond = condTable[state->info.cond]; vmiReg tf = cond.flag; armP arm = state->arm; switch(state->info.cond) { case ARM_C_AL: case ARM_C_NV: // unconditional execution break; case ARM_C_EQ: case ARM_C_NE: case ARM_C_CS: case ARM_C_CC: case ARM_C_MI: case ARM_C_PL: case ARM_C_VS: case ARM_C_VC: // basic flags, always valid break; case ARM_C_GT: // (ZF==0) && (NF==VF) case ARM_C_LE: // (ZF==1) || (NF!=VF) // derived LE flag if(!arm->validLE) { arm->validLE = True; armEmitBinopRRR(state, 8, vmi_XOR, tf, ARM_NF, ARM_VF, 0); armEmitBinopRR(state, 8, vmi_OR, tf, ARM_ZF, 0); } break; case ARM_C_GE: // NF==VF case ARM_C_LT: // NF!=VF // derived LT flag if(!arm->validLT) { arm->validLT = True; armEmitBinopRRR(state, 8, vmi_XOR, tf, ARM_NF, ARM_VF, 0); } break; case ARM_C_HI: // (CF==1) && (ZF==0) case ARM_C_LS: // (CF==0) || (ZF==1) // derived HI flag if(!arm->validHI) { arm->validHI = True; armEmitBinopRRR(state, 8, vmi_ANDN, tf, ARM_CF, ARM_ZF, 0); } break; default: // not reached VMI_ABORT("%s: unimplemented condition", FUNC_NAME); } // return the condition description return cond; } // // Create code to jump to a new label if the instruction is conditional // static vmiLabelP emitStartSkip(armMorphStateP state) { armCond cond = emitPrepareCondition(state); vmiLabelP doSkip = 0; if(cond.op!=ACO_ALWAYS) { doSkip = armEmitNewLabel(); armEmitCondJumpLabel(cond.flag, cond.op==ACO_FALSE, doSkip); } return doSkip; } // // Force all derived flag values to be regenerated // static void resetDerivedFlags(armMorphStateP state) { armP arm = state->arm; arm->validHI = False; arm->validLT = False; arm->validLE = False; } //////////////////////////////////////////////////////////////////////////////// // ARGUMENT GENERATION //////////////////////////////////////////////////////////////////////////////// // // Map from armShiftOp to vmiBinop // static vmiBinop mapShiftOp(armShiftOp so) { switch(so) { case ARM_SO_LSL: return vmi_SHL; case ARM_SO_LSR: return vmi_SHR; case ARM_SO_ASR: return vmi_SAR; case ARM_SO_ROR: return vmi_ROR; default: VMI_ABORT("%s: unimplemented case", FUNC_NAME); return 0; } } // // Generate register argument by shifting register 'ra' by constant, placing // result in register 't' // static void getShiftedRC( armMorphStateP state, vmiReg t, vmiReg ra, vmiFlagsCP shiftCOut ) { Uns32 shift = state->info.c; vmiBinop op = mapShiftOp(state->info.so); armEmitBinopRRC(state, ARM_GPR_BITS, op, t, ra, shift, shiftCOut); } // // Generate register argument by shifting register 'ra' by register 'rs', // placing result in register 't' // static void getShiftedRR( armMorphStateP state, vmiReg t, vmiReg ra, vmiReg rs, vmiFlagsCP shiftCOut ) { vmiBinop op = mapShiftOp(state->info.so); armEmitSetShiftMask(); armEmitBinopRRR(state, ARM_GPR_BITS, op, t, ra, rs, shiftCOut); } // // Generate register argument by rotate right of register 'ra' through carry // static void getExtendedR( armMorphStateP state, vmiReg t, vmiReg ra, vmiFlagsCP shiftCOut ) { armEmitBinopRRC(state, ARM_GPR_BITS, vmi_RCR, t, ra, 1, shiftCOut); } //////////////////////////////////////////////////////////////////////////////// // UNOPS //////////////////////////////////////////////////////////////////////////////// #define vmiFlagsCPU vmiFlagsCP __attribute__ ((unused)) // // Macro for emission of declarations for generic unop // #define EMIT_UNOP_DECLS(_S) \ vmiReg rd = GET_RD(_S, r1); \ vmiUnop op = _S->attrs->unop; \ vmiFlagsCP opFlags = getFlagsOrCIn(_S, rd); \ vmiFlagsCPU shiftCOut = getShifterCOut(_S, rd) // // Macro for emission of call implementing generic unop // #define EMIT_UNOP_CALL(_S, _F, _S1) \ _F(_S, ARM_GPR_BITS, op, rd, _S1, opFlags) // // Macro for emission of generic unop with unshifted argument // #define EMIT_UNOP_NO_SHIFT(_S, _F, _S1) \ EMIT_UNOP_DECLS(_S); \ EMIT_UNOP_CALL(_S, _F, _S1) // // Macro for emission of generic unop with shifted argument // #define EMIT_UNOP_SHIFT(_S, _T) \ EMIT_UNOP_DECLS(_S); \ _T; \ EMIT_UNOP_CALL(_S, armEmitUnopRR, rd); // // Unop with immediate // ARM_MORPH_FN(armEmitUnopI) { Uns32 c = state->info.c; // emit register/constant unop with unshifted argument EMIT_UNOP_NO_SHIFT(state, armEmitUnopRC, c); // set carry from bit 31 of rotated constant if constant rotation was // non-zero if(shifterSetsCOut(shiftCOut) && state->info.crotate) { armEmitMoveRC(state, 8, ARM_CF, (c & 0x80000000) ? 1 : 0); } } // // Unop with register // ARM_MORPH_FN(armEmitUnopR) { EMIT_UNOP_NO_SHIFT(state, armEmitUnopRR, GET_RS(state, r2)); } // // Unop with register shifted by immediate addressing mode // ARM_MORPH_FN(armEmitUnopRSI) { EMIT_UNOP_SHIFT( state, getShiftedRC(state, rd, GET_RS(state, r2), shiftCOut) ); } // // Unop with register shifted by register addressing mode // ARM_MORPH_FN(armEmitUnopRSR) { EMIT_UNOP_SHIFT( state, getShiftedRR(state, rd, GET_RS(state, r2), GET_RS(state, r3), shiftCOut) ); } // // Unop with register shifted by register addressing mode (Thumb variant) // ARM_MORPH_FN(armEmitUnopRSRT) { EMIT_UNOP_SHIFT( state, getShiftedRR(state, rd, GET_RS(state, r1), GET_RS(state, r2), shiftCOut) ); } // // Unop with rotate right with extend addressing mode // ARM_MORPH_FN(armEmitUnopRX) { EMIT_UNOP_SHIFT( state, getExtendedR(state, rd, GET_RS(state, r2), shiftCOut) ); } //////////////////////////////////////////////////////////////////////////////// // BINOPS //////////////////////////////////////////////////////////////////////////////// // // Macro for emission of declarations for generic binop // #define EMIT_BINOP_DECLS(_S, _S1) \ vmiReg rd = GET_RD(_S, r1); \ vmiReg rs1 = GET_RS(_S, _S1); \ vmiBinop op = _S->attrs->binop; \ vmiFlagsCP opFlags = getFlagsOrCIn(_S, rd); \ vmiFlagsCPU shiftCOut = getShifterCOut(_S, rd) // // Macro for emission of call implementing generic binop // #define EMIT_BINOP_CALL(_S, _F, _S2) \ _F(_S, ARM_GPR_BITS, op, rd, rs1, _S2, opFlags) // // Macro for emission of generic binop with unshifted argument // #define EMIT_BINOP_NO_SHIFT(_S, _F, _S1, _S2) \ EMIT_BINOP_DECLS(_S, _S1); \ EMIT_BINOP_CALL(_S, _F, _S2) // // Macro for emission of generic binop with shifted argument // #define EMIT_BINOP_SHIFT(_S, _S1, _RS2, _T) \ EMIT_BINOP_DECLS(_S, _S1); \ vmiReg _RS2 = newTemp32(_S); \ _T; \ EMIT_BINOP_CALL(_S, armEmitBinopRRR, _RS2); \ freeTemp32(_S) // // Binop with immediate // ARM_MORPH_FN(armEmitBinopI) { Uns32 c = state->info.c; // emit register/constant binop with unshifted argument EMIT_BINOP_NO_SHIFT(state, armEmitBinopRRC, r2, c); // set carry from bit 31 of rotated constant if constant rotation was // non-zero if(shifterSetsCOut(shiftCOut) && state->info.crotate) { armEmitMoveRC(state, 8, ARM_CF, (c & 0x80000000) ? 1 : 0); } } // // Binop with three registers // ARM_MORPH_FN(armEmitBinopR) { EMIT_BINOP_NO_SHIFT(state, armEmitBinopRRR, r2, GET_RS(state, r3)); } // // Binop with two registers (Thumb variant) // ARM_MORPH_FN(armEmitBinopRT) { EMIT_BINOP_NO_SHIFT(state, armEmitBinopRRR, r1, GET_RS(state, r2)); } // // Binop with register and immediate (Thumb variant) // ARM_MORPH_FN(armEmitBinopIT) { EMIT_BINOP_NO_SHIFT(state, armEmitBinopRRC, r1, state->info.c); } // // Binop with register, program counter and immediate (ADR) // ARM_MORPH_FN(armEmitBinopADR) { Uns32 c = state->attrs->negate ? -state->info.c : state->info.c; EMIT_BINOP_NO_SHIFT(state, armEmitBinopRRC, r2, alignConstWithPC(state, c)); } // // Binop with register shifted by immediate addressing mode // ARM_MORPH_FN(armEmitBinopRSI) { EMIT_BINOP_SHIFT( state, r2, rs2, getShiftedRC(state, rs2, GET_RS(state, r3), shiftCOut) ); } // // Binop with rotate right with extend addressing mode // ARM_MORPH_FN(armEmitBinopRX) { EMIT_BINOP_SHIFT( state, r2, rs2, getExtendedR(state, rs2, GET_RS(state, r3), shiftCOut) ); } //////////////////////////////////////////////////////////////////////////////// // CMPOPS //////////////////////////////////////////////////////////////////////////////// // // Macro for emission of declarations for generic cmpop // #define EMIT_CMPOP_DECLS(_S) \ vmiReg rd = VMI_NOREG; \ vmiReg rs1 = GET_RS(_S, r1); \ vmiBinop op = _S->attrs->binop; \ vmiFlagsCP opFlags = getFlagsOrCIn(_S, rd); \ vmiFlagsCPU shiftCOut = getShifterCOut(_S, rd) // // Macro for emission of call implementing generic cmpop // #define EMIT_CMPOP_CALL(_S, _F, _S2) \ _F(_S, ARM_GPR_BITS, op, rd, rs1, _S2, opFlags) // // Macro for emission of generic cmpop with unshifted argument // #define EMIT_CMPOP_NO_SHIFT(_S, _F, _S2) \ EMIT_CMPOP_DECLS(_S); \ EMIT_CMPOP_CALL(_S, _F, _S2) // // Macro for emission of generic cmpop with shifted argument // #define EMIT_CMPOP_SHIFT(_S, _RS2, _T) \ EMIT_CMPOP_DECLS(_S); \ vmiReg _RS2 = newTemp32(_S); \ _T; \ EMIT_CMPOP_CALL(_S, armEmitBinopRRR, _RS2); \ freeTemp32(_S) // // Cmpop with immediate // ARM_MORPH_FN(armEmitCmpopI) { Uns32 c = state->info.c; // emit register/constant cmpop with unshifted argument EMIT_CMPOP_NO_SHIFT(state, armEmitBinopRRC, c); // set carry from bit 31 of rotated constant if constant rotation was // non-zero if(shifterSetsCOut(shiftCOut) && state->info.crotate) { armEmitMoveRC(state, 8, ARM_CF, (c & 0x80000000) ? 1 : 0); } } // // Cmpop with register // ARM_MORPH_FN(armEmitCmpopR) { EMIT_CMPOP_NO_SHIFT(state, armEmitBinopRRR, GET_RS(state, r2)); } // // Cmpop with register shifted by immediate addressing mode // ARM_MORPH_FN(armEmitCmpopRSI) { EMIT_CMPOP_SHIFT( state, rs2, getShiftedRC(state, rs2, GET_RS(state, r2), shiftCOut) ); } // // Cmpop with rotate right with extend addressing mode // ARM_MORPH_FN(armEmitCmpopRX) { EMIT_CMPOP_SHIFT( state, rs2, getExtendedR(state, rs2, GET_RS(state, r2), shiftCOut) ); } //////////////////////////////////////////////////////////////////////////////// // MULTIPLY AND DIVIDE INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Emit code for MUL instruction // ARM_MORPH_FN(armEmitMUL) { vmiFlagsCP flags = getFlagsOrNull(state); vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); vmiReg rs = GET_RS(state, r3); armEmitBinopRRR(state, ARM_GPR_BITS, vmi_IMUL, rd, rm, rs, flags); } // // Emit code for SDIV and UDIV instructions // ARM_MORPH_FN(armEmitDIV) { vmiBinop op = state->attrs->binop; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); // record the target of the divide instruction (in case of exception) armEmitMoveRC(state, 8, ARM_DIVIDE_TARGET, state->info.r1); armEmitBinopRRR(state, ARM_GPR_BITS, op, rd, rn, rm, 0); } // // Emit code for MLA or MLS instructions // static void emitMLAMLS(armMorphStateP state, vmiBinop op) { vmiFlagsCP flags = getFlagsOrNull(state); vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); vmiReg rs = GET_RS(state, r3); vmiReg rn = GET_RS(state, r4); vmiReg t = VMI_REG_EQUAL(rd, rn) ? getTemp(state) : rd; armEmitBinopRRR(state, ARM_GPR_BITS, vmi_IMUL, t, rm, rs, 0); armEmitBinopRRR(state, ARM_GPR_BITS, op, rd, t, rn, flags); } // // MLA instruction // ARM_MORPH_FN(armEmitMLA) { emitMLAMLS(state, vmi_ADD); } // // MLS instruction // ARM_MORPH_FN(armEmitMLS) { emitMLAMLS(state, vmi_RSUB); } // // [SU]MULL instructions // ARM_MORPH_FN(armEmitMULL) { vmiBinop op = state->attrs->binop; vmiFlagsCP flags = getFlagsOrNull(state); vmiReg rdlo = GET_RD(state, r1); vmiReg rdhi = GET_RD(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg rs = GET_RS(state, r4); armEmitMulopRRR(state, ARM_GPR_BITS, op, rdhi, rdlo, rm, rs, flags); } // // [SU]MLAL instructions // ARM_MORPH_FN(armEmitMLAL) { Uns32 bits = ARM_GPR_BITS; vmiBinop op = state->attrs->binop; vmiFlagsCP flags = getFlagsOrNull(state); vmiReg rdlo = GET_RS(state, r1); vmiReg rdhi = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg rs = GET_RS(state, r4); vmiReg t = newTemp64(state); vmiReg t164lo = getR64Lo(t); vmiReg t164hi = getR64Hi(t); // perform initial multiply, result in temporary t164lo/t164hi armEmitMulopRRR(state, bits, op, t164hi, t164lo, rm, rs, 0); if(VMI_REG_EQUAL(rdhi, getR64Hi(rdlo))) { // rdlo/rdhi are an adjacent pair armEmitBinopRR(state, bits*2, vmi_ADD, rdlo, t164lo, flags); } else { // rdlo/rdhi are not an adjacent pair vmiReg t = newTemp64(state); vmiReg t264lo = getR64Lo(t); vmiReg t264hi = getR64Hi(t); // move rdlo/rdhi into adjacent pair armEmitMoveRR(state, bits, t264hi, rdhi); armEmitMoveRR(state, bits, t264lo, rdlo); // perform addition using adjacent pair armEmitBinopRR(state, bits*2, vmi_ADD, t264lo, t164lo, flags); // move to rdlo/rdhi from adjacent pair armEmitMoveRR(state, bits, rdhi, t264hi); armEmitMoveRR(state, bits, rdlo, t264lo); freeTemp64(state); } freeTemp64(state); } //////////////////////////////////////////////////////////////////////////////// // BRANCH INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Return link address from state // static Uns32 getStateLinkPC(armMorphStateP state) { return state->nextPC | (IN_THUMB_MODE(state->arm) ? 1 : 0); } // // Fill armJumpInfo structure with information about a jump to a target address // static void seedJumpInfo( armJumpInfoP ji, armMorphStateP state, Bool isLink, Bool isReturn, Bool isRelative ) { ji->linkReg = isLink ? ARM_LR : VMI_NOREG; ji->hint = isRelative ? vmi_JH_RELATIVE : vmi_JH_NONE; ji->hint |= isReturn ? vmi_JH_RETURN : isLink ? vmi_JH_CALL : vmi_JH_NONE; ji->linkPC = isLink ? getStateLinkPC(state) : 0; } // // Emit an explicit unconditional jump to target address // static void emitUncondJumpC(armMorphStateP state, Bool isLink) { // get information about the jump armJumpInfo ji; seedJumpInfo(&ji, state, isLink, False, True); // do the jump armEmitUncondJump(state, &ji); } // // Emit an explicit conditional jump to target address // static void emitCondJumpC( armMorphStateP state, vmiReg tf, Bool jumpIfTrue, Bool isLink ) { // get information about the jump armJumpInfo ji; seedJumpInfo(&ji, state, isLink, False, True); // do the jump armEmitCondJump(state, &ji, tf, jumpIfTrue); } // // Macro defining the body of a (possibly conditional) jump to target // #define COND_OR_UNCOND_BRANCH_BODY(_UNCOND_CB, _COND_CB) \ \ armCond cond = emitPrepareCondition(state); \ Bool isLink = state->attrs->isLink; \ \ if(cond.op==ACO_ALWAYS) { \ /* unconditional jump */ \ _UNCOND_CB(state, isLink); \ } else { \ /* conditional jump */ \ _COND_CB(state, cond.flag, cond.op==ACO_TRUE, isLink); \ } \ // // Emit conditional branch to constant address, possibly with link // ARM_MORPH_FN(armEmitBranchC) { COND_OR_UNCOND_BRANCH_BODY(emitUncondJumpC, emitCondJumpC); } // // Emit unconditional branch to register address, possibly with link // ARM_MORPH_FN(armEmitBranchR) { vmiReg ra = GET_RS(state, r1); Bool isLink = state->attrs->isLink; // switch mode if LSB of target address implies a different mode armEmitInterworkLSB(state, ra); // get information about the jump armJumpInfo ji; seedJumpInfo(&ji, state, isLink, state->info.r1==ARM_REG_LR, False); // do the jump armEmitUncondJumpReg(state, &ji, ra); } //////////////////////////////////////////////////////////////////////////////// // MISCELLANEOUS INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Called for an unimplemented instruction // static void ignoreBKPT(armP arm, Uns32 thisPC) { vmiMessage("W", CPU_PREFIX"_BII", SRCREF_FMT "BKPT instruction ignored in nopBKPT compatability mode", SRCREF_ARGS(arm, thisPC) ); } // // Emit call to perform CLZ // ARM_MORPH_FN(armEmitCLZ) { vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); armEmitUnopRR(state, ARM_GPR_BITS, vmi_CLZ, rd, rm, 0); } // // Emit call to perform BKPT // ARM_MORPH_FN(armEmitBKPT) { if(state->arm->compatMode==COMPAT_CODE_SOURCERY) { armEmitArgProcessor(state); armEmitArgSimPC(state, ARM_GPR_BITS); armEmitCall(state, (vmiCallFn)ignoreBKPT); } else { emitExceptionCall(state, armBKPT); } } // // Emit call to perform SWI // ARM_MORPH_FN(armEmitSWI) { emitExceptionCall(state, armSWI); } //////////////////////////////////////////////////////////////////////////////// // SPECIAL PURPOSE REGISTER ACCESS INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Emit code for MRS // ARM_MORPH_FN(armEmitMRS) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rs = VMI_NOREG; Bool privMode = !IN_USER_MODE(state->arm); armSysRegId SYSm = state->info.c; switch(SYSm>>3) { case 0: { // extract fields from PSR Uns32 mask = 0; // include exceptNum field if required if((SYSm&1) && privMode) { mask |= PSR_EXCEPT_NUM; } // include flags field if required if(!(SYSm&4)) { mask |= PSR_FLAGS; } // get masked result value if(mask) { armEmitArgProcessor(state); armEmitCallResult(state, (vmiCallFn)armReadCPSR, bits, rd); armEmitBinopRC(state, bits, vmi_AND, rd, mask, 0); rs = rd; } break; } case 1: // return SP_main or SP_process if(privMode) { // this code is mode specific armEmitValidateBlockMask(ARM_BM_USE_SP_PROCESS); // select source register if((SYSm&1) == USE_SP_PROCESS(state->arm)) { rs = ARM_SP; } else { rs = ARM_BANK_SP; } } break; case 2: // return PRIMASK, BASEPRI, FAULTMASK or CONTROL switch(SYSm&7) { case 0: rs = privMode ? ARM_PRIMASK : VMI_NOREG; break; case 1: rs = privMode ? ARM_BASEPRI : VMI_NOREG; break; case 2: rs = privMode ? ARM_BASEPRI : VMI_NOREG; break; case 3: rs = privMode ? ARM_FAULTMASK : VMI_NOREG; break; case 4: rs = ARM_CONTROL; break; default: break; } break; default: break; } // get the register armEmitMoveRR(state, bits, rd, rs); } // // Emit code for MSR // ARM_MORPH_FN(armEmitMSR) { Uns32 bits = ARM_GPR_BITS; vmiReg rs = GET_RD(state, r1); Bool privMode = !IN_USER_MODE(state->arm); armSysRegId SYSm = state->info.c; switch(SYSm>>3) { case 0: { // insert fields in PSR Uns32 wrMask = 0; // include flags field if required if(!(SYSm&4)) { armPSRBits bits = state->info.psrbits; if (bits==ARM_PSRBITS_ALL || bits==ARM_PSRBITS_FLAGS) { wrMask |= PSR_FLAGS; } if (bits==ARM_PSRBITS_ALL || bits==ARM_PSRBITS_GE) { if (DSP_PRESENT(state->arm)) { wrMask |= PSR_GE; } } } // set masked result value if(wrMask) { armEmitArgProcessor(state); armEmitArgReg(state, bits, rs); armEmitArgUns32(state, wrMask); armEmitCall(state, (vmiCallFn)armWriteCPSR); } break; } case 1: // set SP_main or SP_process if(privMode) { vmiReg rd; // this code is mode specific armEmitValidateBlockMask(ARM_BM_USE_SP_PROCESS); // select destination register if((SYSm&1) == USE_SP_PROCESS(state->arm)) { rd = ARM_SP; } else { rd = ARM_BANK_SP; } // set the register armEmitMoveRR(state, bits, rd, rs); } break; case 2: // assign PRIMASK, BASEPRI, FAULTMASK or CONTROL if(privMode) { switch(SYSm&7) { case 0: armEmitArgProcessor(state); armEmitArgReg(state, bits, rs); armEmitCall(state, (vmiCallFn)armWritePRIMASK); break; case 1: armEmitArgProcessor(state); armEmitArgReg(state, bits, rs); armEmitCall(state, (vmiCallFn)armWriteBASEPRI); break; case 2: armEmitArgProcessor(state); armEmitArgReg(state, bits, rs); armEmitCall(state, (vmiCallFn)armWriteBASEPRI_MAX); break; case 3: armEmitArgProcessor(state); armEmitArgReg(state, bits, rs); armEmitCall(state, (vmiCallFn)armWriteFAULTMASK); break; case 4: armEmitArgProcessor(state); armEmitArgReg(state, bits, rs); armEmitCall(state, (vmiCallFn)armWriteCONTROL); break; default: break; } } break; default: break; } } //////////////////////////////////////////////////////////////////////////////// // HINT INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Emit NOP // ARM_MORPH_FN(armEmitNOP) { // no action } // // Emit WFE // ARM_MORPH_FN(armEmitWFE) { vmiLabelP wait = armEmitNewLabel(); vmiLabelP done = armEmitNewLabel(); // jump to wait code if no event registered armEmitCondJumpLabel(ARM_EVENT, False, wait); // clear event register and finish armEmitMoveRC(state, 8, ARM_EVENT, 0); armEmitUncondJumpLabel(done); // here if halt is required armEmitInsertLabel(wait); // wait for event armEmitWait(state, AD_WFE); // here when done armEmitInsertLabel(done); } // // Emit WFI // ARM_MORPH_FN(armEmitWFI) { vmiLabelP noWait = armEmitNewLabel(); // don't stop if there are pending interrupts armEmitCompareRCJumpLabel(8, vmi_COND_NZ, ARM_PENDING, 0, noWait); // halt the processor at the end of this instruction armEmitWait(state, AD_WFI); // here if interrupt is currently pending emitLabel(noWait); } // // Emit SEV // ARM_MORPH_FN(armEmitSEV) { armEmitArgProcessor(state); armEmitCall(state, (vmiCallFn)armSEV); } //////////////////////////////////////////////////////////////////////////////// // LOAD AND STORE INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // This indicates that the register being loaded is also the base in an LDM // #define LDM_BASE_REG -1 // // If this is an STM with the base register in the list and also writeback, is // the value written the *final* value of the base register? // #define STM_WB_BASE_FINAL False // // Callback function for load/store // #define LOAD_STORE_FN(_NAME) void _NAME( \ armMorphStateP state, \ vmiReg base, \ Int32 offset \ ) typedef LOAD_STORE_FN((*loadStoreFn)); // // Callback function for one register of a load/store multiple // #define LOAD_STORE_M_FN(_NAME) void _NAME( \ armMorphStateP state, \ vmiReg base, \ Int32 offset, \ Uns32 r, \ Bool isWBNotFirst, \ Int32 frameDelta \ ) typedef LOAD_STORE_M_FN((*loadStoreMFn)); // // Should increment/decrement be performed before access? // inline static Bool doBefore(armMorphStateP state) { armIncDec incDec = state->info.incDec; return ((incDec==ARM_ID_IB) || (incDec==ARM_ID_DA)); } // // Does load/store multiple increment? // inline static Bool doIncrement(armMorphStateP state) { return state->info.incDec & ARM_ID_I; } // // Return the step to apply before load/store multiple // inline static Int32 getStepBefore(armMorphStateP state, Uns32 bytes) { return doBefore(state) ? bytes : 0; } // // Return the step to apply after load/store multiple // inline static Int32 getStepAfter(armMorphStateP state, Uns32 bytes) { return doBefore(state) ? 0 : bytes; } // // Return frame size for a load/store multiple // static Uns32 getFrameSize(armMorphStateP state) { Uns32 rList = state->info.rList; Uns32 mask; Uns32 r; Int32 size = 0; for(r=0, mask=1; rinfo.r1); return (isLoad && (state->info.rList & rmMask)); } // // Is the base register not the highest (first) register in the list? // inline static Bool baseIsNotFirst(Uns32 rList, Uns32 rBase) { return rList & ((1<info.rList; Uns32 rBase = state->info.r1; Bool baseLoaded = baseIsLoaded(state, isLoad); vmiReg base = GET_RS(state, r1); Bool increment = doIncrement(state); Int32 stepBefore = getStepBefore(state, ARM_GPR_BYTES); Int32 stepAfter = getStepAfter(state, ARM_GPR_BYTES); Uns32 frameSize = getFrameSize(state); Int32 offset = increment ? 0 : -frameSize; Int32 frameDelta = increment ? frameSize : -frameSize; Bool isNotFirst = state->info.wb && baseIsNotFirst(rList, rBase); Uns32 mask; Uns32 r; // load or store registers for(r=0, mask=1; rinfo.wb) { vmiReg rn = GET_RD(state, r1); armEmitBinopRRC(state, bits, vmi_ADD, rn, base, frameDelta, 0); } } // // Macro to emit one register load for LDM // #define EMIT_LDM_REG(_S, _BASE, _OFFSET, _R) { \ \ Uns32 bits = ARM_GPR_BITS; \ vmiReg rd = (_R==LDM_BASE_REG) ? getTemp(_S) : getRD(_S, _R); \ \ armEmitLoadRRO(_S, bits, _OFFSET, rd, _BASE, False, True); \ } // // Emit one register load for LDM // static LOAD_STORE_M_FN(emitLDMCB) { EMIT_LDM_REG(state, base, offset, r); } // // If the source register is the PC, adjust to a 12-byte offset if required // static vmiReg emitPCDelta(armMorphStateP state, Uns32 r, vmiReg rs) { if((r==ARM_REG_PC) && state->arm->configInfo.STRoffsetPC12) { vmiReg t = getTemp(state); armEmitBinopRRC(state, ARM_GPR_BITS, vmi_ADD, t, rs, 4, 0); return t; } else { return rs; } } // // Emit one register store for STM // static LOAD_STORE_M_FN(emitSTMCB) { Uns32 bits = ARM_GPR_BITS; vmiReg rs = getRS(state, r); // write the final value of the base register if it is in the register list if(STM_WB_BASE_FINAL && isWBNotFirst) { rs = getTemp(state); armEmitBinopRRC(state, bits, vmi_ADD, rs, base, frameDelta, 0); } // if source register is PC, allow for store offset adjustment rs = emitPCDelta(state, r, rs); // do the store armEmitStoreRRO(state, bits, offset, base, rs); } // // Emit code for LDM (1) // ARM_MORPH_FN(armEmitLDM1) { emitLoadStoreMultiple(state, emitLDMCB, True); } // // Emit code for STM (1) // ARM_MORPH_FN(armEmitSTM1) { emitLoadStoreMultiple(state, emitSTMCB, False); } // // Should the LDR instruction do writeback? // inline static Bool doLDRWriteBack(armMorphStateP state) { return (state->info.wb && (state->info.r1!=state->info.r2)); } // // Is translation required? (LDRT, STRT) // inline static Bool doTranslate(armMorphStateP state) { return (state->info.tl && IN_PRIV_MPU_MODE(state->arm)); } // // If this is an LDRT/STRT instruction and we are currently in privileged mode // with TLB enabled, emit code to switch to the user mode data memDomain // static void emitTranslateOn(armMorphStateP state) { if(doTranslate(state)) { armEmitArgProcessor(state); armEmitCall(state, (vmiCallFn)armVMSetUserPrivilegedModeDataDomain); } } // // If this is after a LDRT/STRT instruction and we are currently in privileged // mode with TLB enabled, emit code to switch back to the privileged mode data // memDomain. Note that this code is not executed if the prior access causes an // exception; in this case, mode is restored in armDataAbort. // static void emitTranslateOff(armMorphStateP state) { if(doTranslate(state)) { armEmitArgProcessor(state); armEmitCall(state, (vmiCallFn)armVMRestoreNormalDataDomain); } } // // Emit code for LDR variant // static LOAD_STORE_FN(emitLDR) { Uns32 memBits = state->info.sz*8; Bool isLong = (memBits==64); vmiReg rd = GET_RD(state, r1); Bool xs = state->info.xs; // start translation (LDRT, STRT) emitTranslateOn(state); // emit load if(isLong) { vmiReg rdH = GET_RD(state, r4); vmiReg rt = getTemp(state); armEmitLoadRRRO(state, memBits, offset, rd, rdH, base, rt, xs, False); } else { armEmitLoadRRO(state, memBits, offset, rd, base, xs, False); } // end translation (LDRT, STRT) emitTranslateOff(state); } // // Emit code for STR variant // static LOAD_STORE_FN(emitSTR) { Uns32 memBits = state->info.sz*8; Bool isLong = (memBits==64); Uns32 r1 = state->info.r1; vmiReg rs = getRS(state, r1); // if source register is PC, allow for store offset adjustment rs = emitPCDelta(state, r1, rs); // start translation (LDRT, STRT) emitTranslateOn(state); // emit store if(isLong) { Uns32 r4 = state->info.r4; vmiReg rsH = getRS(state, r4); armEmitStoreRRRO(state, memBits, offset, base, rs, rsH); } else { armEmitStoreRRO(state, memBits, offset, base, rs); } // end translation (LDRT, STRT) emitTranslateOff(state); } // // Emit code for LDR/STR with immediate offset // static void emitLDRSTRI(armMorphStateP state, loadStoreFn cb, Bool align) { Uns32 bits = ARM_GPR_BITS; vmiReg base = GET_RS(state, r2); Int32 offset = state->info.c; Int32 memOffset = state->info.pi ? 0 : offset; // align the constant if the base register is the program counter (has // effect for PC-relative Thumb load instructions only) if(align && VMI_REG_EQUAL(base, ARM_PC)) { memOffset = alignConstWithPC(state, memOffset); } // emit register load or store cb(state, base, memOffset); // do writeback if required if(doLDRWriteBack(state)) { vmiReg ra = GET_RD(state, r2); armEmitBinopRC(state, bits, vmi_ADD, ra, offset, 0); } } // // Emit code for LDR/STR variant // static void emitLDRSTRInt(armMorphStateP state, vmiReg offset, loadStoreFn cb) { Uns32 bits = ARM_GPR_BITS; vmiReg base = GET_RS(state, r2); vmiReg t = newTemp32(state); vmiReg memReg = state->info.pi ? base : t; vmiBinop op = state->info.u ? vmi_ADD : vmi_SUB; // calculate incremented address armEmitBinopRRR(state, bits, op, t, base, offset, 0); // emit register load cb(state, memReg, 0); // do writeback if required if(doLDRWriteBack(state)) { vmiReg ra = GET_RD(state, r2); armEmitMoveRR(state, bits, ra, t); } freeTemp32(state); } // // Emit code for LDR/STR with register offset // static void emitLDRSTRR(armMorphStateP state, loadStoreFn cb) { vmiReg rm = GET_RS(state, r3); emitLDRSTRInt(state, rm, cb); } // // Emit code for LDR/STR with scaled register offset (LSL, LSR, ASR, ROR) // static void emitLDRSTRRSI(armMorphStateP state, loadStoreFn cb) { vmiReg t = newTemp32(state); vmiReg rm = GET_RS(state, r3); getShiftedRC(state, t, rm, FLAGS_CIN); emitLDRSTRInt(state, t, cb); freeTemp32(state); } // // Emit code for LDR with immediate offset // ARM_MORPH_FN(armEmitLDRI) { emitLDRSTRI(state, emitLDR, True); } // // Emit code for LDR with register offset // ARM_MORPH_FN(armEmitLDRR) { emitLDRSTRR(state, emitLDR); } // // Emit code for LDR with scaled register offset (LSL, LSR, ASR, ROR) // ARM_MORPH_FN(armEmitLDRRSI) { emitLDRSTRRSI(state, emitLDR); } // // Emit code for STR with immediate offset // ARM_MORPH_FN(armEmitSTRI) { emitLDRSTRI(state, emitSTR, False); } // // Emit code for STR with register offset // ARM_MORPH_FN(armEmitSTRR) { emitLDRSTRR(state, emitSTR); } // // Emit code for STR with scaled register offset (LSL, LSR, ASR, ROR) // ARM_MORPH_FN(armEmitSTRRSI) { emitLDRSTRRSI(state, emitSTR); } //////////////////////////////////////////////////////////////////////////////// // DSP INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Minimum and maximum values for clamping // #define ARM_MIN 0x80000000 #define ARM_MAX 0x7fffffff // // Emit code for to perform rd = rs1 op rs2, clamping result and setting Q // flag if overflow // static void emitOpSetQClamp( armMorphStateP state, vmiBinop op, vmiReg rd, vmiReg rs1, vmiReg rs2 ) { Uns32 bits = ARM_GPR_BITS; vmiFlags flags = getSFOFFlags(getTemp(state)); vmiLabelP noOverflow = armEmitNewLabel(); // do the operation, setting flags armEmitBinopRRR(state, bits, op, rd, rs1, rs2, &flags); // skip clamping and Q flag update unless there was an overflow armEmitCondJumpLabel(flags.f[vmi_OF], False, noOverflow); // clamp depending on sign of result armEmitCondMoveRCC(state, bits, flags.f[vmi_SF], True, rd, ARM_MAX, ARM_MIN); // set the sticky Q flag armEmitMoveRC(state, 8, ARM_QF, 1); // here if the operation didn't overflow emitLabel(noOverflow); } // // Emit code to perform rd = rs1 op rs2, setting Q flag if overflow // static void emitOpSetQ( armMorphStateP state, Uns32 bits, vmiBinop op, vmiReg rd, vmiReg rs1, vmiReg rs2 ) { vmiReg tf = getTemp(state); vmiFlags flags = getOFFlags(tf); // do the operation, setting flags armEmitBinopRRR(state, bits, op, rd, rs1, rs2, &flags); // set the sticky Q flag of there was overflow armEmitBinopRR(state, 8, vmi_OR, ARM_QF, tf, 0); } // // Emit code for 16 x 16 = 32 multiply // static void emitMul1632( armMorphStateP state, vmiReg rd, vmiReg rs1, vmiReg rs2 ) { vmiReg rdlo = getR32Lo(rd); vmiReg rdhi = getR32Hi(rd); // do 16 x 16 = 32 multiply armEmitMulopRRR(state, ARM_GPR_BITS/2, vmi_IMUL, rdhi, rdlo, rs1, rs2, 0); } // // Emit code for QADD/QSUB // static void emitQADDSUB(armMorphStateP state, vmiBinop op) { vmiReg rd = GET_RD(state, r1); vmiReg rs1 = GET_RS(state, r2); vmiReg rs2 = GET_RS(state, r3); emitOpSetQClamp(state, op, rd, rs1, rs2); } // // Emit code for QDADD/QDSUB // static void emitQDADDSUB(armMorphStateP state, vmiBinop op) { vmiReg rd = GET_RD(state, r1); vmiReg rs1 = GET_RS(state, r2); vmiReg rs2 = GET_RS(state, r3); vmiReg t = newTemp32(state); emitOpSetQClamp(state, vmi_ADD, t, rs2, rs2); emitOpSetQClamp(state, op, rd, rs1, t); freeTemp32(state); } // // Emit code for QADD // ARM_MORPH_FN(armEmitQADD) { emitQADDSUB(state, vmi_ADD); } // // Emit code for QSUB // ARM_MORPH_FN(armEmitQSUB) { emitQADDSUB(state, vmi_SUB); } // // Emit code for QDADD // ARM_MORPH_FN(armEmitQDADD) { emitQDADDSUB(state, vmi_ADD); } // // Emit code for QDSUB // ARM_MORPH_FN(armEmitQDSUB) { emitQDADDSUB(state, vmi_SUB); } // // Emit code for SMLA // static void emitSMLA(armMorphStateP state, Uns32 xDelta, Uns32 yDelta) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rs1 = VMI_REG_DELTA(GET_RS(state, r2), xDelta); vmiReg rs2 = VMI_REG_DELTA(GET_RS(state, r3), yDelta); vmiReg rs3 = GET_RS(state, r4); vmiReg t = newTemp32(state); // do 16 x 16 = 32 multiply emitMul1632(state, t, rs1, rs2); // do the accumulate, setting Q flag on overflow emitOpSetQ(state, bits, vmi_ADD, rd, rs3, t); // free multiply result temporary freeTemp32(state); } // // Emit code for SMLABB // ARM_MORPH_FN(armEmitSMLABB) { emitSMLA(state, 0, 0); } // // Emit code for SMLABT // ARM_MORPH_FN(armEmitSMLABT) { emitSMLA(state, 0, 2); } // // Emit code for SMLATB // ARM_MORPH_FN(armEmitSMLATB) { emitSMLA(state, 2, 0); } // // Emit code for SMLATT // ARM_MORPH_FN(armEmitSMLATT) { emitSMLA(state, 2, 2); } // // Emit code for SMLAL // static void emitSMLAL(armMorphStateP state, Uns32 xDelta, Uns32 yDelta) { Uns32 bits = ARM_GPR_BITS; vmiReg rdlo = GET_RD(state, r1); vmiReg rdhi = GET_RD(state, r2); vmiReg rs1 = VMI_REG_DELTA(GET_RS(state, r3), xDelta); vmiReg rs2 = VMI_REG_DELTA(GET_RS(state, r4), yDelta); vmiReg t = newTemp64(state); vmiReg tlo32 = getR64Lo(t); vmiReg thi32 = getR64Hi(t); vmiFlags flags = getCFFlags(getTemp(state)); // do 16 x 16 = 32 multiply and extend to 64 bits emitMul1632(state, t, rs1, rs2); armEmitMoveExtendRR(state, bits*2, t, bits, tlo32, True); // do the accumulate armEmitBinopRR(state, bits, vmi_ADD, rdlo, tlo32, &flags); armEmitBinopRR(state, bits, vmi_ADC, rdhi, thi32, &flags); // free multiply result temporary freeTemp64(state); } // // Emit code for SMLABB // ARM_MORPH_FN(armEmitSMLALBB) { emitSMLAL(state, 0, 0); } // // Emit code for SMLABT // ARM_MORPH_FN(armEmitSMLALBT) { emitSMLAL(state, 0, 2); } // // Emit code for SMLATB // ARM_MORPH_FN(armEmitSMLALTB) { emitSMLAL(state, 2, 0); } // // Emit code for SMLATT // ARM_MORPH_FN(armEmitSMLALTT) { emitSMLAL(state, 2, 2); } // // Emit code for SMLAW // static void emitSMLAW(armMorphStateP state, Uns32 yDelta) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rs1 = GET_RS(state, r2); vmiReg rs2 = VMI_REG_DELTA(GET_RS(state, r3), yDelta); vmiReg rs3 = GET_RS(state, r4); vmiReg t = newTemp64(state); vmiReg tlo32 = getR64Lo(t); vmiReg thi32 = getR64Hi(t); vmiReg t48 = VMI_REG_DELTA(t, 2); // sign extend rs2 and place result in temporary armEmitMoveExtendRR(state, bits, t, bits/2, rs2, True); // do 32 x 32 = 64 multiply armEmitMulopRRR(state, bits, vmi_IMUL, thi32, tlo32, t, rs1, 0); // do the accumulate, setting Q flag on overflow emitOpSetQ(state, bits, vmi_ADD, rd, t48, rs3); // free multiply result temporary freeTemp64(state); } // // Emit code for SMLAWB // ARM_MORPH_FN(armEmitSMLAWB) { emitSMLAW(state, 0); } // // Emit code for SMLAWT // ARM_MORPH_FN(armEmitSMLAWT) { emitSMLAW(state, 2); } // // Emit code for emitSMUL // static void emitSMUL(armMorphStateP state, Uns32 xDelta, Uns32 yDelta) { vmiReg rd = GET_RD(state, r1); vmiReg rs1 = VMI_REG_DELTA(GET_RS(state, r2), xDelta); vmiReg rs2 = VMI_REG_DELTA(GET_RS(state, r3), yDelta); emitMul1632(state, rd, rs1, rs2); } // // Emit code for SMULBB // ARM_MORPH_FN(armEmitSMULBB) { emitSMUL(state, 0, 0); } // // Emit code for SMULBT // ARM_MORPH_FN(armEmitSMULBT) { emitSMUL(state, 0, 2); } // // Emit code for SMULTB // ARM_MORPH_FN(armEmitSMULTB) { emitSMUL(state, 2, 0); } // // Emit code for SMULTT // ARM_MORPH_FN(armEmitSMULTT) { emitSMUL(state, 2, 2); } // // Emit code for SMULW // static void emitSMULW(armMorphStateP state, Uns32 yDelta) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rs1 = GET_RS(state, r2); vmiReg rs2 = VMI_REG_DELTA(GET_RS(state, r3), yDelta); vmiReg t = newTemp64(state); vmiReg tlo32 = getR64Lo(t); vmiReg thi32 = getR64Hi(t); vmiReg t48 = VMI_REG_DELTA(t, 2); // sign extend rs2 and place result in temporary armEmitMoveExtendRR(state, bits, t, bits/2, rs2, True); // do 32 x 32 = 64 multiply armEmitMulopRRR(state, bits, vmi_IMUL, thi32, tlo32, t, rs1, 0); // assign the result armEmitMoveRR(state, bits, rd, t48); // free multiply result temporary freeTemp64(state); } // // Emit code for SMULWB // ARM_MORPH_FN(armEmitSMULWB) { emitSMULW(state, 0); } // // Emit code for SMULWT // ARM_MORPH_FN(armEmitSMULWT) { emitSMULW(state, 2); } //////////////////////////////////////////////////////////////////////////////// // COPROCESSOR INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Morpher callback for undefined coprocessor instructions // ARM_MORPH_FN(armEmitUsageFaultCP) { // generate assertion armEmitArgProcessor(state); armEmitArgSimPC(state, ARM_GPR_BITS); armEmitCall(state, (vmiCallFn)undefinedCPMessage); // take UndefinedInstruction exception emitUsageFault(state, EXC_UNDEF_NOCP); } //////////////////////////////////////////////////////////////////////////////// // MOVE INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // MOVW instruction // ARM_MORPH_FN(armEmitMOVW) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); armEmitMoveRC(state, bits, rd, state->info.c); } // // MOVT instruction // ARM_MORPH_FN(armEmitMOVT) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); if(!VMI_REG_EQUAL(rd, ARM_PC)) { vmiReg rdH = VMI_REG_DELTA(rd, 2); armEmitMoveRC(state, bits/2, rdH, state->info.c); } else { vmiReg rs = getRS(state, ARM_REG_PC); vmiReg tmp = newTemp32(state); vmiReg tmpH = VMI_REG_DELTA(tmp, 2); armEmitMoveRR(state, bits, tmp, rs); armEmitMoveRC(state, bits/2, tmpH, state->info.c); armEmitMoveRR(state, bits, rd, tmp); freeTemp32(state); } } //////////////////////////////////////////////////////////////////////////////// // MULTIPLY INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // [SU]MAAL instructions // ARM_MORPH_FN(armEmitMAAL) { Uns32 bits = ARM_GPR_BITS; vmiBinop op = state->attrs->binop; vmiReg rdlo = GET_RS(state, r1); vmiReg rdhi = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg rs = GET_RS(state, r4); vmiReg t = newTemp64(state); vmiReg t64lo = getR64Lo(t); vmiReg t64hi = getR64Hi(t); vmiFlags tf = getCFFlags(getTemp(state)); // perform initial multiply, result in temporary t64lo/t64hi armEmitMulopRRR(state, bits, op, t64hi, t64lo, rm, rs, 0); // add in rdhi armEmitBinopRR(state, bits, vmi_ADD, t64lo, rdhi, &tf); armEmitBinopRC(state, bits, vmi_ADC, t64hi, 0, &tf); // add in rdlo armEmitBinopRR(state, bits, vmi_ADD, t64lo, rdlo, &tf); armEmitBinopRC(state, bits, vmi_ADC, t64hi, 0, &tf); // move result to rdlo/rdhi armEmitMoveRR(state, bits, rdhi, t64hi); armEmitMoveRR(state, bits, rdlo, t64lo); freeTemp64(state); } //////////////////////////////////////////////////////////////////////////////// // SYNCHRONIZATION INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Generate exclusive access tag address ra+offset in register rtag // static void generateEATag( armMorphStateP state, Uns32 offset, vmiReg rtag, vmiReg ra ) { Uns32 bits = ARM_GPR_BITS; Uns32 mask = state->arm->exclusiveTagMask; armEmitBinopRRC(state, bits, vmi_ADD, rtag, ra, offset, 0); armEmitBinopRC(state, bits, vmi_AND, rtag, mask, 0); } // // Emit code to start exclusive access to address ra+offset // inline static void startEA(armMorphStateP state, Uns32 offset, vmiReg ra) { generateEATag(state, offset, ARM_EA_TAG, ra); } // // Validate the exclusive access and jump to label 'done' if it is invalid, // setting rd to 1 // static vmiLabelP validateEA( armMorphStateP state, Uns32 offset, vmiReg ra, vmiReg rd ) { Uns32 bits = ARM_GPR_BITS; Uns32 memBits = state->info.sz*8; vmiLabelP done = armEmitNewLabel(); vmiLabelP ok = armEmitNewLabel(); vmiReg t = getTemp(state); // generate any store exception prior to exclusive access tag check armEmitTryStoreRC(state, memBits, offset, ra); // generate exclusive access tag for this address generateEATag(state, offset, t, ra); // do load and store tags match? armEmitCompareRR(state, bits, vmi_COND_EQ, ARM_EA_TAG, t, t); // commit store if tags match armEmitCondJumpLabel(t, True, ok); // indicate store failed armEmitMoveRC(state, bits, rd, 1); // jump to instruction end armEmitUncondJumpLabel(done); // here to commit store armEmitInsertLabel(ok); return done; } // // Do actions required to terminate exclusive access // static void clearEA(armMorphStateP state) { // exclusiveTag becomes ARM_NO_TAG to indicate no active access armEmitMoveRC(state, ARM_GPR_BITS, ARM_EA_TAG, ARM_NO_TAG); } // // Do actions required to complete exclusive access // static void endEA(armMorphStateP state, vmiReg rd, vmiLabelP done) { // indicate store succeeded armEmitMoveRC(state, ARM_GPR_BITS, rd, 0); // insert target label for aborted stores armEmitInsertLabel(done); // terminate exclusive access clearEA(state); } // // Emit code for LDREX* // ARM_MORPH_FN(armEmitLDREX) { // indicate LDREX is now active at address r2+offset startEA(state, state->info.c, GET_RS(state, r2)); // emit load armEmitLDRI(state); } // // Emit code for STREX* // ARM_MORPH_FN(armEmitSTREX) { // validate STREX attempt at address r3+offset vmiReg rd = GET_RD(state, r1); vmiLabelP done = validateEA(state, state->info.c, GET_RS(state, r3), rd); // move down rt and rn so that they are in the required positions for a // normal store state->info.r1 = state->info.r2; state->info.r2 = state->info.r3; // emit store armEmitSTRI(state); // complete STREX attempt endEA(state, rd, done); } // // Emit code for CLREX // ARM_MORPH_FN(armEmitCLREX) { clearEA(state); } //////////////////////////////////////////////////////////////////////////////// // MISCELLANEOUS INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Function to set PRIMASK and/or FAULTMASK // static void setMasks(armP arm, armFlagAffect faff) { Bool updateState = False; // set PRIMASK if required if((faff&ARM_FAFF_I) && !arm->sregs.PRIMASK) { arm->sregs.PRIMASK = 1; updateState = True; } // set FAULTMASK if required if((faff&ARM_FAFF_F) && !arm->sregs.FAULTMASK && (arm->executionPriority>-1)) { arm->sregs.FAULTMASK = 1; updateState = True; } // update processor state if required if(updateState) { armRefreshExecutionPriority(arm); } } // // Function to clear PRIMASK and/or FAULTMASK // static void clearMasks(armP arm, armFlagAffect faff) { Bool updateState = False; // set PRIMASK if required if((faff&ARM_FAFF_I) && arm->sregs.PRIMASK) { arm->sregs.PRIMASK = 0; updateState = True; } // set FAULTMASK if required if((faff&ARM_FAFF_F) && arm->sregs.FAULTMASK) { arm->sregs.FAULTMASK = 0; updateState = True; } // update processor state if required if(updateState) { armRefreshExecutionPriority(arm); } } // // Emit code for CPS // ARM_MORPH_FN(armEmitCPS) { if(!IN_USER_MODE(state->arm)) { armFlagAffect faff = state->info.faff; Bool set = (state->info.fact==ARM_FACT_ID); // emit call to set or clear the masks armEmitArgProcessor(state); armEmitArgUns32(state, faff); armEmitCall(state, set ? (vmiCallFn)setMasks : (vmiCallFn)clearMasks); // terminate the current code block armEmitEndBlock(); } } //////////////////////////////////////////////////////////////////////////////// // BRANCH INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Emit code for CPZ/CBNZ // ARM_MORPH_FN(armEmitCBZ) { Uns32 bits = ARM_GPR_BITS; vmiReg rn = GET_RS(state, r1); vmiReg tf = getTemp(state); // do the comparison armEmitCompareRC(state, bits, vmi_COND_Z, rn, 0, tf); // get information about the jump armJumpInfo ji; seedJumpInfo(&ji, state, False, False, True); // do the jump armEmitCondJump(state, &ji, tf, state->attrs->jumpIfTrue); } // // Emit code for TBB/TBH // ARM_MORPH_FN(armEmitTB) { Uns32 bits = ARM_GPR_BITS; Uns32 sz = state->info.sz; Uns32 memBits = sz*8; vmiReg rn = GET_RS(state, r1); vmiReg rm = GET_RS(state, r2); vmiReg t1 = getTemp(state); Uns32 shift = 0; // convert from size to shift while(sz>1) { sz >>= 1; shift++; } // get offset, shifted if required armEmitBinopRRC(state, bits, vmi_SHL, t1, rm, shift, 0); // compose full address armEmitBinopRR(state, bits, vmi_ADD, t1, rn, 0); // load zero-extended offset from table and double it armEmitLoadRRO(state, memBits, 0, t1, t1, False, False); armEmitBinopRR(state, bits, vmi_ADD, t1, t1, 0); // add to effective PC to get target address armEmitBinopRR(state, bits, vmi_ADD, t1, getRS(state, ARM_REG_PC), 0); // get information about the jump armJumpInfo ji; seedJumpInfo(&ji, state, False, False, True); // do the jump armEmitUncondJumpReg(state, &ji, t1); } //////////////////////////////////////////////////////////////////////////////// // BASIC MEDIA INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Common routine for USAD8 and USADA8 // static void emitUSAD8Int(armMorphStateP state, vmiReg ra) { Uns32 bits = ARM_GPR_BITS; Uns32 partBits = 8; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = newTemp32(state); vmiReg t2 = newTemp32(state); vmiReg t3 = getTemp(state); Uns32 i; // clear result accumulators armEmitMoveRR(state, bits, t1, ra); for(i=0; i<(bits/partBits); i++) { // get zero-extended arguments in temporaries armEmitMoveExtendRR(state, bits, t2, partBits, rn, False); armEmitMoveExtendRR(state, bits, t3, partBits, rm, False); // perform subtraction armEmitBinopRR(state, bits, vmi_SUB, t2, t3, 0); // get absolute result armEmitUnopRR(state, bits, vmi_ABS, t2, t2, 0); // update accumulated result armEmitBinopRR(state, bits, vmi_ADD, t1, t2, 0); // step to next register pair rn = VMI_REG_DELTA(rn, 1); rm = VMI_REG_DELTA(rm, 1); } // assign result register armEmitMoveRR(state, bits, rd, t1); // free allocated temporaries freeTemp32(state); freeTemp32(state); } // // Common routine for SBFX and UBFX // static void emitBFXInt(armMorphStateP state, vmiBinop op) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); Uns32 lsb = state->info.c; Uns32 msb = lsb + state->info.w - 1; if(msbinfo.c; Int32 width = state->info.w; if(width<=0) { // no action } else if(width==bits) { // optimize to a move armEmitMoveRC(state, bits, rd, 0); } else { // general case Uns32 mask1 = ((1<info.c; Int32 width = state->info.w; if(width<=0) { // no action } else if(width==bits) { // optimize to a move armEmitMoveRR(state, bits, rd, rs); } else { // general case vmiReg t = getTemp(state); Uns32 mask1 = ((1<attrs->setGE) { armEmitBinopRC(state, ARM_GPR_BITS, vmi_AND, ARM_PSR, ~PSR_GE30, 0); } } // // Emit code to perform part of a parallel operation // static void emitParallelBinopInt( armMorphStateP state, Uns32 partBits, vmiBinop op, vmiReg rd, vmiReg rn, vmiReg rm, Uns32 delta1, Uns32 delta2, Uns32 apsrMask ) { Bool setGE = state->attrs->setGE; Bool sextend = state->attrs->sextend; Bool halve = state->attrs->halve; vmiFlags flags = VMI_NOFLAGS; vmiReg tf = newTemp32(state); vmiReg rnTmp; vmiReg rmTmp; vmiReg rdTmp; Uns32 opBits; // set up flags to generate CPSR greater-or-equal bits if required if(!(setGE || halve)) { // no action } else if(sextend) { flags.f[vmi_SF] = tf; } else { flags.f[vmi_CF] = flags.cin = tf; } // get shift to appropriate part of each register rd = VMI_REG_DELTA(rd, delta1); rn = VMI_REG_DELTA(rn, delta1); rm = VMI_REG_DELTA(rm, delta2); // argument format depends on whether sign extension is required if(sextend) { // sign extension required: perform operation on extended arguments opBits = ARM_GPR_BITS; rdTmp = newTemp32(state); rnTmp = rdTmp; rmTmp = getTemp(state); // sign extend arguments into temporaries armEmitMoveExtendRR(state, opBits, rnTmp, partBits, rn, True); armEmitMoveExtendRR(state, opBits, rmTmp, partBits, rm, True); } else { // use original unextended arguments opBits = partBits; rdTmp = rd; rnTmp = rn; rmTmp = rm; } // do the operation armEmitBinopRRR(state, opBits, op, rdTmp, rnTmp, rmTmp, &flags); // update CPSR greater-or-equal bits if required if(setGE) { vmiLabelP done = armEmitNewLabel(); // skip flag update if required armEmitCondJumpLabel(tf, (sextend || (op!=vmi_ADD)), done); // include apsrMask in CPSR register armEmitBinopRC(state, ARM_GPR_BITS, vmi_OR, ARM_PSR, apsrMask, 0); // jump to here if flag update is not required armEmitInsertLabel(done); } // halve results if required if(!halve) { // no action } else if(sextend) { armEmitBinopRC(state, opBits, vmi_SHR, rdTmp, 1, 0); } else { armEmitBinopRC(state, opBits, vmi_RCR, rdTmp, 1, &flags); } // write back temporary if required if(sextend) { armEmitMoveRR(state, partBits, rd, rdTmp); freeTemp32(state); } // free temporary flag freeTemp32(state); } // // Emit code for parallel add/subtract of 8-bit data // ARM_MORPH_FN(armEmitParallelBinop8) { Uns32 bits = ARM_GPR_BITS/4; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiBinop op = state->attrs->binop; // clear GE flags emitClearGEFlags(state); // perform the parallel operations emitParallelBinopInt(state, bits, op, rd, rn, rm, 0, 0, PSR_GE0); emitParallelBinopInt(state, bits, op, rd, rn, rm, 1, 1, PSR_GE1); emitParallelBinopInt(state, bits, op, rd, rn, rm, 2, 2, PSR_GE2); emitParallelBinopInt(state, bits, op, rd, rn, rm, 3, 3, PSR_GE3); } // // Emit code for parallel add/subtract of 16-bit data // ARM_MORPH_FN(armEmitParallelBinop16) { Uns32 bits = ARM_GPR_BITS/2; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); Bool exch = state->attrs->exchange; vmiBinop op1 = state->attrs->binop; vmiBinop op2 = state->attrs->binop2; vmiReg tmp = newTemp32(state); // save rm in temporary if exchange required and it is clobbered if(exch && VMI_REG_EQUAL(rd, rm)) { armEmitMoveRR(state, ARM_GPR_BITS, tmp, rm); rm = tmp; } // clear GE flags emitClearGEFlags(state); // perform the parallel operations emitParallelBinopInt(state, bits, op1, rd, rn, rm, 0, exch?2:0, PSR_GE10); emitParallelBinopInt(state, bits, op2, rd, rn, rm, 2, exch?0:2, PSR_GE32); // free temporary freeTemp32(state); } //////////////////////////////////////////////////////////////////////////////// // PACKING, UNPACKING, SATURATION AND REVERSAL INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Emit code for SSAT/SSAT16 // static void emitSSAT( armMorphStateP state, vmiBinop so, Uns32 bits, vmiReg rd, vmiReg rn ) { vmiReg t1 = newTemp32(state); vmiReg tf = getTemp(state); Uns32 shift = state->info.c; Uns32 width = state->info.w; vmiFlags flags = getZFFlags(tf); vmiLabelP done = armEmitNewLabel(); // create bitmasks Uns32 signMask = 1<<(width-1); Uns32 maxMask = signMask-1; Uns32 propMask = ~maxMask; // mask propMask to the operand width if(bits!=32) { propMask &= (1<info.c; Uns32 width = state->info.w; vmiLabelP done = armEmitNewLabel(); // create bitmasks Uns32 maxMask = (1<info.c/8; Uns32 delta1 = rorDelta1[dIndex]; // do the extension armEmitMoveExtendRR(state, bits, t1, 8, VMI_REG_DELTA(rm,delta1), sextend); } // // Extract word into temporary with sign extension // static void emitExtWord( armMorphStateP state, vmiReg rm, vmiReg t1, Bool sextend ) { Uns32 bits = ARM_GPR_BITS; Uns32 dIndex = state->info.c/8; Uns32 delta1 = rorDelta1[dIndex]; // if the word straddles the register boundary, handle it specially if(delta1==3) { armEmitBinopRRC(state, bits, vmi_ROR, t1, rm, 24, 0); rm = t1; delta1 = 0; } // do the extension armEmitMoveExtendRR(state, bits, t1, 16, VMI_REG_DELTA(rm,delta1), sextend); } // // Extract byte pair into temporaries with sign extension // static void emitExtBytePair( armMorphStateP state, vmiReg rm, vmiReg t1, vmiReg t2, Bool sextend ) { Uns32 bits = 16; Uns32 dIndex = state->info.c/8; Uns32 delta1 = rorDelta1[dIndex]; Uns32 delta2 = rorDelta2[dIndex]; // do the extension armEmitMoveExtendRR(state, bits, t1, 8, VMI_REG_DELTA(rm,delta1), sextend); armEmitMoveExtendRR(state, bits, t2, 8, VMI_REG_DELTA(rm,delta2), sextend); } // // Emit code for SXTAB/UXTAB // static void emitXTAB(armMorphStateP state, Bool sextend) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = getTemp(state); // extract extended byte into temporary emitExtByte(state, rm, t1, sextend); // create result from 32-bit addition armEmitBinopRRR(state, bits, vmi_ADD, rd, rn, t1, 0); } // // Emit code for SXTAH/UXTAH // static void emitXTAH(armMorphStateP state, Bool sextend) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = getTemp(state); // extract extended word into temporary emitExtWord(state, rm, t1, sextend); // create result from 32-bit addition armEmitBinopRRR(state, bits, vmi_ADD, rd, rn, t1, 0); } // // Emit code for SXTAB16/UXTAB16 // static void emitXTAB16(armMorphStateP state, Bool sextend) { Uns32 bits = 16; vmiReg rdL = GET_RD(state, r1); vmiReg rnL = GET_RS(state, r2); vmiReg rdH = VMI_REG_DELTA(rdL, 2); vmiReg rnH = VMI_REG_DELTA(rnL, 2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = newTemp32(state); vmiReg t2 = getTemp(state); // extract extended bytes into temporaries emitExtBytePair(state, rm, t1, t2, sextend); // create result from two 16-bit additions armEmitBinopRRR(state, bits, vmi_ADD, rdL, rnL, t1, 0); armEmitBinopRRR(state, bits, vmi_ADD, rdH, rnH, t2, 0); // free temporary freeTemp32(state); } // // Emit code for SXTB16/UXTB16 // static void emitXTB16(armMorphStateP state, Bool sextend) { Uns32 bits = 16; vmiReg rdL = GET_RD(state, r1); vmiReg rdH = VMI_REG_DELTA(rdL, 2); vmiReg rm = GET_RS(state, r2); vmiReg t1 = newTemp32(state); vmiReg t2 = getTemp(state); // extract extended bytes into temporaries emitExtBytePair(state, rm, t1, t2, sextend); // create result from two 16-bit moves armEmitMoveRR(state, bits, rdL, t1); armEmitMoveRR(state, bits, rdH, t2); // free temporary freeTemp32(state); } // // Emit code for SXTB/UXTB // static void emitXTB(armMorphStateP state, Bool sextend) { vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); emitExtByte(state, rm, rd, sextend); } // // Emit code for SXTH/UXTH // static void emitXTH(armMorphStateP state, Bool sextend) { vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); emitExtWord(state, rm, rd, sextend); } // // Emit code for PKHBT // ARM_MORPH_FN(armEmitPKHBT) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = newTemp32(state); vmiReg t2 = getTemp(state); Uns32 shift = state->info.c; vmiBinop so = mapShiftOp(state->info.so); // create lower half of the result armEmitBinopRRC(state, bits, vmi_AND, t1, rn, 0xffff, 0); // create upper half of the result armEmitBinopRRC(state, bits, so, t2, rm, shift, 0); // mask upper half of the result if required if(shift<16) { armEmitBinopRC(state, bits, vmi_AND, t2, 0xffff0000, 0); } // create combined result armEmitBinopRRR(state, bits, vmi_OR, rd, t1, t2, 0); // free temporary freeTemp32(state); } // // Emit code for PKHTB // ARM_MORPH_FN(armEmitPKHTB) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = newTemp32(state); vmiReg t2 = getTemp(state); Uns32 shift = state->info.c; vmiBinop so = mapShiftOp(state->info.so); // create upper half of the result armEmitBinopRRC(state, bits, vmi_AND, t1, rn, 0xffff0000, 0); // create lower half of the result armEmitBinopRRC(state, bits, so, t2, rm, shift, 0); armEmitBinopRC(state, bits, vmi_AND, t2, 0xffff, 0); // create combined result armEmitBinopRRR(state, bits, vmi_OR, rd, t1, t2, 0); // free temporary freeTemp32(state); } // // Emit code for SSAT // ARM_MORPH_FN(armEmitSSAT) { Uns32 bits = ARM_GPR_BITS; vmiBinop so = mapShiftOp(state->info.so); vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); emitSSAT(state, so, bits, rd, rn); } // // Emit code for SSAT16 // ARM_MORPH_FN(armEmitSSAT16) { Uns32 bits = ARM_GPR_BITS/2; vmiBinop so = vmi_SHR; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); emitSSAT(state, so, bits, VMI_REG_DELTA(rd, 0), VMI_REG_DELTA(rn, 0)); emitSSAT(state, so, bits, VMI_REG_DELTA(rd, 2), VMI_REG_DELTA(rn, 2)); } // // Emit code for USAT // ARM_MORPH_FN(armEmitUSAT) { Uns32 bits = ARM_GPR_BITS; vmiBinop so = mapShiftOp(state->info.so); vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); emitUSAT(state, so, bits, rd, rn); } // // Emit code for USAT16 // ARM_MORPH_FN(armEmitUSAT16) { Uns32 bits = ARM_GPR_BITS/2; vmiBinop so = vmi_SHR; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); emitUSAT(state, so, bits, VMI_REG_DELTA(rd, 0), VMI_REG_DELTA(rn, 0)); emitUSAT(state, so, bits, VMI_REG_DELTA(rd, 2), VMI_REG_DELTA(rn, 2)); } // // Emit code for SXTAB // ARM_MORPH_FN(armEmitSXTAB) { emitXTAB(state, True); } // // Emit code for UXTAB // ARM_MORPH_FN(armEmitUXTAB) { emitXTAB(state, False); } // // Emit code for SXTAB16 // ARM_MORPH_FN(armEmitSXTAB16) { emitXTAB16(state, True); } // // Emit code for UXTAB16 // ARM_MORPH_FN(armEmitUXTAB16) { emitXTAB16(state, False); } // // Emit code for SXTAH // ARM_MORPH_FN(armEmitSXTAH) { emitXTAH(state, True); } // // Emit code for UXTAH // ARM_MORPH_FN(armEmitUXTAH) { emitXTAH(state, False); } // // Emit code for SXTB // ARM_MORPH_FN(armEmitSXTB) { emitXTB(state, True); } // // Emit code for UXTB // ARM_MORPH_FN(armEmitUXTB) { emitXTB(state, False); } // // Emit code for SXTB16 // ARM_MORPH_FN(armEmitSXTB16) { emitXTB16(state, True); } // // Emit code for UXTB16 // ARM_MORPH_FN(armEmitUXTB16) { emitXTB16(state, False); } // // Emit code for SXTH // ARM_MORPH_FN(armEmitSXTH) { emitXTH(state, True); } // // Emit code for UXTH // ARM_MORPH_FN(armEmitUXTH) { emitXTH(state, False); } // // Emit code for SEL // ARM_MORPH_FN(armEmitSEL) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1 = newTemp32(state); vmiReg t2 = getTemp(state); // select GE bits from CPSR armEmitBinopRRC(state, bits, vmi_SHR, t1, ARM_PSR, 16, 0); armEmitBinopRC (state, bits, vmi_AND, t1, 0xf, 0); // convert from 0..15 to bitmask armEmitBinopRC(state, bits, vmi_IMUL, t1, 0x00204081, 0); armEmitBinopRC(state, bits, vmi_AND, t1, 0x01010101, 0); armEmitBinopRC(state, bits, vmi_IMUL, t1, 0xff, 0); // get components from each source armEmitBinopRRR(state, bits, vmi_AND, t2, rn, t1, 0); armEmitBinopRRR(state, bits, vmi_ANDN, t1, rm, t1, 0); // assign result register armEmitBinopRRR(state, bits, vmi_OR, rd, t1, t2, 0); // free temporary freeTemp32(state); } // // Emit code for REV // ARM_MORPH_FN(armEmitREV) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); armEmitUnopRR(state, bits, vmi_SWP, rd, rm, 0); } // // Emit code for REV16 // ARM_MORPH_FN(armEmitREV16) { Uns32 bits = 16; vmiReg rdL = GET_RD(state, r1); vmiReg rmL = GET_RS(state, r2); vmiReg rdH = VMI_REG_DELTA(rdL, 2); vmiReg rmH = VMI_REG_DELTA(rmL, 2); armEmitUnopRR(state, bits, vmi_SWP, rdL, rmL, 0); armEmitUnopRR(state, bits, vmi_SWP, rdH, rmH, 0); } // // Emit code for REVSH // ARM_MORPH_FN(armEmitREVSH) { Uns32 bits = 16; vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); armEmitUnopRR(state, bits, vmi_SWP, rd, rm, 0); armEmitMoveExtendRR(state, ARM_GPR_BITS, rd, bits, rd, True); } // // This array will hold bit-reversed byte values for RBIT // static Uns8 rbit8[256]; // // Return bit-reversed value // static Uns32 doRBIT(Uns32 value) { union {Uns32 u32; Uns8 u8[4];} u1 = {value}; union {Uns32 u32; Uns8 u8[4];} u2; // generate reversed result a byte at a time using the lookup table u2.u8[0] = rbit8[u1.u8[3]]; u2.u8[1] = rbit8[u1.u8[2]]; u2.u8[2] = rbit8[u1.u8[1]]; u2.u8[3] = rbit8[u1.u8[0]]; // return the reversed result return u2.u32; } // // Emit code for RBIT // ARM_MORPH_FN(armEmitRBIT) { Uns32 bits = ARM_GPR_BITS; vmiReg rd = GET_RD(state, r1); vmiReg rm = GET_RS(state, r2); static Bool init; // set up rbit8 table if required if(!init) { Uns32 i; for(i=0; i<256; i++) { Uns8 byte = i; Uns8 result = 0; Uns32 j; for(j=0; j<8; j++) { result = (result<<1) | (byte&1); byte >>= 1; } rbit8[i] = result; } init = True; } // emit embedded call to perform operation armEmitArgReg(state, bits, rm); armEmitCallResult(state, (vmiCallFn)doRBIT, bits, rd); } //////////////////////////////////////////////////////////////////////////////// // SIGNED MULTIPLY INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// // // Emit code to perform dual multiply, with results in t1 and t2 // static void emitDualMultiply( armMorphStateP state, vmiReg rnL, vmiReg rmL, vmiReg t1, vmiReg t2 ) { Uns32 bits = ARM_GPR_BITS; vmiReg rnH = VMI_REG_DELTA(rnL, 2); vmiReg rmH = VMI_REG_DELTA(rmL, 2); vmiReg t3 = getTemp(state); // exchange arguments if required if(state->attrs->exchange) { vmiReg tmp = rmL; rmL = rmH; rmH = tmp; } // do first multiply armEmitMoveExtendRR(state, bits, t1, 16, rnL, True); armEmitMoveExtendRR(state, bits, t3, 16, rmL, True); armEmitBinopRR(state, bits, vmi_IMUL, t1, t3, 0); // do second multiply armEmitMoveExtendRR(state, bits, t2, 16, rnH, True); armEmitMoveExtendRR(state, bits, t3, 16, rmH, True); armEmitBinopRR(state, bits, vmi_IMUL, t2, t3, 0); } // // Emit code for SMLAD/SMLSD // ARM_MORPH_FN(armEmitSMLXD) { Uns32 bits = ARM_GPR_BITS; vmiBinop op = state->attrs->binop; vmiReg rd = GET_RD(state, r1); vmiReg rnL = GET_RS(state, r2); vmiReg rmL = GET_RS(state, r3); vmiReg ra = GET_RS(state, r4); vmiReg t1 = newTemp32(state); vmiReg t2 = newTemp32(state); // do dual multiply, results in t1 and t2 emitDualMultiply(state, rnL, rmL, t1, t2); // combine results, setting CPSR.Q if overflow (ADD only, special case of // 0x8000*0x8000 + 0x8000*0x8000) if(op==vmi_ADD) { emitOpSetQ(state, bits, op, t1, t1, t2); } else { armEmitBinopRR(state, bits, op, t1, t2, 0); } // accumulate, setting CPSR.Q if overflow emitOpSetQ(state, bits, vmi_ADD, rd, ra, t1); // free temporaries freeTemp32(state); freeTemp32(state); } // // Emit code for SMUAD/SMUSD // ARM_MORPH_FN(armEmitSMUXD) { Uns32 bits = ARM_GPR_BITS; vmiBinop op = state->attrs->binop; vmiReg rd = GET_RD(state, r1); vmiReg rnL = GET_RS(state, r2); vmiReg rmL = GET_RS(state, r3); vmiReg t1 = newTemp32(state); vmiReg t2 = newTemp32(state); // do dual multiply, results in t1 and t2 emitDualMultiply(state, rnL, rmL, t1, t2); // generate result, setting CPSR.Q if overflow (ADD only) if(op==vmi_ADD) { emitOpSetQ(state, bits, op, rd, t1, t2); } else { armEmitBinopRRR(state, bits, op, rd, t1, t2, 0); } // free temporaries freeTemp32(state); freeTemp32(state); } // // Emit code for SMLALD/SMLSLD // ARM_MORPH_FN(armEmitSMLXLD) { Uns32 bits = ARM_GPR_BITS; vmiBinop op = state->attrs->binop; vmiReg rdLo = GET_RD(state, r1); vmiReg rdHi = GET_RD(state, r2); vmiReg rnL = GET_RS(state, r3); vmiReg rmL = GET_RS(state, r4); vmiReg t1 = newTemp64(state); vmiReg t2 = newTemp32(state); vmiReg tf = t2; vmiFlags flags = getCFFlags(tf); // do dual multiply, results in t1 and t2 emitDualMultiply(state, rnL, rmL, t1, t2); // extend results to 64 bits armEmitMoveExtendRR(state, 64, t1, bits, t1, True); armEmitMoveExtendRR(state, 64, t2, bits, t2, True); // add/subtract extended results armEmitBinopRR(state, 64, op, t1, t2, 0); // add total to accumulator armEmitBinopRR(state, bits, vmi_ADD, rdLo, VMI_REG_DELTA(t1, 0), &flags); armEmitBinopRR(state, bits, vmi_ADC, rdHi, VMI_REG_DELTA(t1, 4), &flags); // free temporaries freeTemp32(state); freeTemp64(state); } // // Emit code for SMMLX // ARM_MORPH_FN(armEmitSMMLX) { Uns32 bits = ARM_GPR_BITS; vmiBinop op = state->attrs->binop; vmiReg rd = GET_RD(state, r1); vmiReg rn = GET_RS(state, r2); vmiReg rm = GET_RS(state, r3); vmiReg t1L = newTemp64(state); vmiReg t1H = VMI_REG_DELTA(t1L, 4); // do the multiply armEmitMulopRRR(state, bits, vmi_IMUL, t1H, t1L, rn, rm, 0); // accumulate if required if(state->attrs->accumulate) { vmiReg t2L = getTemp(state); vmiReg t2H = VMI_REG_DELTA(t2L, 4); armEmitMoveRC(state, bits, t2L, 0); armEmitMoveRR(state, bits, t2H, GET_RS(state, r4)); armEmitBinopRR(state, 64, op, t1L, t2L, 0); } // round if required if(state->attrs->round) { armEmitBinopRC(state, 64, vmi_ADD, t1L, 0x80000000, 0); } // assign to result armEmitMoveRR(state, bits, rd, t1H); // free temporary freeTemp64(state); } //////////////////////////////////////////////////////////////////////////////// // VFP Utility functions //////////////////////////////////////////////////////////////////////////////// // // Emit NOCP Usage Fault if FPU is disabled // #define VFP_DISABLED(_S) emitUsageFault(state, EXC_UNDEF_NOCP); return False; // // This is the ARM CheckVFPEnabled psuedo-code primitive // static Bool checkVFPEnabled(armMorphStateP state) { armP arm = state->arm; Bool inUserMode = IN_USER_MODE(arm); Uns32 cp10Enable = SCS_FIELD(arm, CPACR, cp10); // this code block is dependent on the enable state of coprocessor 10 armEmitValidateBlockMask(ARM_BM_CP10); // check CPACR for permission to use cp10 (and cp11) in the current user/privilege mode if(inUserMode && !(cp10Enable&2)) { VFP_DISABLED(state); } else if(!inUserMode && !(cp10Enable&1)) { VFP_DISABLED(state); } return True; } // // This is the ARM ExecuteFPCheck primitive // static Bool executeFPCheck(armMorphStateP state) { armP arm = state->arm; VMI_ASSERT(FPU_PRESENT(arm), "FP instruction when no FPU present"); if (!checkVFPEnabled(state)) { // Access to CP10 not enabled return False; } else { Bool endMorphBlock = False; // this code block is dependent on the CONTROL.FPCA, FPCCR.ASPEN and FPCCR.LSPACT bits armEmitValidateBlockMask(ARM_BM_FPCA|ARM_BM_ASPEN|ARM_BM_LSPACT); // If FP lazy context save is enabled then save state if (SCS_FIELD(arm, FPCCR, LSPACT)) { // Preserve the FP state in the saved stack space armEmitArgProcessor(state); armEmitCall(state, (vmiCallFn)armPreserveFPState); // preserveFPState will clear LSPACT so should end block endMorphBlock = True; } // If ASPEN is on then check is FPCA is off. If it is, then this is the // first FP instruction in this context if (SCS_FIELD(arm, FPCCR, ASPEN) && !CONTROL_FIELD(arm, FPCA)) { Uns32 bits = ARM_GPR_BITS; // copy FPDSCR values to FPSCR armEmitArgProcessor(state); armEmitArgReg(state, bits, ARM_SCS_REG(SCS_ID(FPDSCR))); armEmitArgUns32(state, SCS_WRITE_MASK_FPDSCR); armEmitCall(state, (vmiCallFn)armWriteFPSCR); // Set CONTROL.FPCA bit armEmitBinopRC(state, 32, vmi_OR, ARM_CONTROL, CONTROL_FPCA, 0); // CONTROL.FPCA has changed so should end block endMorphBlock = True; } if (endMorphBlock) { // terminate the code block (CONTROL.FPCA or FPCCR.LSPACT has changed) armEmitEndBlock(); } } // VFP is available return True; } // // Load the Flt64 register with the floating point constant value of 2^n // static vmiReg getFPConstPower2Flt64(armMorphStateP state, Uns32 n) { Uns64 twoN = (1ULL << n); vmiReg r = newTemp64(state); union {Flt64 f64; Uns64 u64;} u = {f64:twoN}; armEmitMoveRC(state, 64, r, u.u64); return r; } // // Load the Flt80 register with the floating point constant value of 2^n // static vmiReg getFPConstPower2Flt80(armMorphStateP state, Uns32 n) { Uns64 twoN = (1ULL << n); vmiReg r = newTemp128(state); union {Flt80 f80; Uns64 u64; Uns16 u16[8];} u = {f80:twoN}; armEmitMoveRC(state, 64, r, u.u64); armEmitMoveRC(state, 16, VMI_REG_DELTA(r, 8), u.u16[4]); return r; } // // Get floating point operation type for ARM integer type from size of operand in bytes // static vmiFType bytesToIType(Uns32 ebytes, Bool isSigned) { switch(ebytes) { case 2: return isSigned ? vmi_FT_16_INT : vmi_FT_16_UNS; case 4: return isSigned ? vmi_FT_32_INT : vmi_FT_32_UNS; default: VMI_ABORT("%s: unimplemented size for floating point type: %d bytes", FUNC_NAME, ebytes); return 0; // Not reached } } // // Get floating point operation type for ARM floating point type from size of operand in bytes // static vmiFType bytesToFType(Uns32 ebytes) { switch(ebytes) { case 4: return vmi_FT_32_IEEE_754; case 8: return vmi_FT_64_IEEE_754; default: VMI_ABORT("%s: unimplemented size for floating point type: %d bytes", FUNC_NAME, ebytes); return 0; // Not reached } } // // This is FPNeg from psuedo-code // Negate the value in the register by toggling the sign bit // static void FPNeg(armMorphStateP state, vmiReg dest, vmiReg src, Uns32 size) { Uns64 signBit = 1ULL << (size-1); armEmitBinopRRC(state, size, vmi_XOR, dest, src, signBit, 0); } // // This is FixedToFP from psuedo-code // Note: fpscr_controlled selection is done automatically in armEmit... call // static void fixedToFP( armMorphStateP state, vmiReg result, Uns32 resultBytes, vmiReg operand, Uns32 operandBytes, Uns32 fracBits, Bool isSigned, Bool roundToNearest ) { vmiFPRC round = roundToNearest ? vmi_FPR_NEAREST : vmi_FPR_CURRENT; vmiFType operandType = bytesToIType(operandBytes, isSigned); VMI_ASSERT(operandBytes==4 || operandBytes==2, "operand size in bytes %d must be 2 or 4", operandBytes); VMI_ASSERT(fracBits <= operandBytes*8, "fracBits = %d must be <= %d", fracBits, operandBytes*8); if (fracBits) { // Fixed point to floating point - must scale by fracBits Bool singlePrec = (resultBytes == 4); vmiReg t = newTemp64(state); vmiReg power2 = getFPConstPower2Flt64(state, fracBits); // Convert integer to double precision floating point (rounding mode unused here) armEmitFConvertRR(state, vmi_FT_64_IEEE_754, t, operandType, operand, vmi_FPR_CURRENT); if (singlePrec) { // Get result / 2^fracBits (always exact, so no rounding) armEmitFBinopSimdRRR(state, vmi_FT_64_IEEE_754, 1, vmi_FDIV, t, t, power2); // Convert to single precision (may be rounded) armEmitFConvertRR(state, vmi_FT_32_IEEE_754, result, vmi_FT_64_IEEE_754, t, round); } else { // result = value / 2^fracBits (always exact, so no rounding) armEmitFBinopSimdRRR(state, vmi_FT_64_IEEE_754, 1, vmi_FDIV, result, t, power2); } } else { // Just convert integer to target floating point type (may be rounded) vmiFType resultType = bytesToFType(resultBytes); armEmitFConvertRR(state, resultType, result, operandType, operand, round); } } // // This is FPToFixed from psuedo-code // Note: fpscr_controlled selection is done automatically in armEmit... call // static void FPToFixed ( armMorphStateP state, vmiReg result, Uns32 resultBytes, vmiReg operand, Uns32 operandBytes, Uns32 fracBits, Bool isSigned, Bool roundTowardsZero ) { vmiFPRC round = roundTowardsZero ? vmi_FPR_ZERO : vmi_FPR_CURRENT; vmiFType opType = bytesToFType(operandBytes); VMI_ASSERT(operandBytes==4 || operandBytes==8, "operand size in bytes %d must be 8 or 4", operandBytes); VMI_ASSERT(resultBytes==4 || resultBytes==2, "result size in bytes %d must be 2 or 4", resultBytes); VMI_ASSERT(fracBits <= resultBytes*8, "fracBits = %d must be <= %d", fracBits, resultBytes*8); if (fracBits) { // Scale by fracBits vmiReg power2 = getFPConstPower2Flt80(state, fracBits); vmiReg t = newTemp128(state); // Convert operand to 80 bit fp value (rounding mode unused here) armEmitFConvertRR(state, vmi_FT_80_X87, t, opType, operand, vmi_FPR_CURRENT); // t = t * 2^fracBits (always bigger, so no rounding) armEmitFBinopSimdRRR(state, vmi_FT_80_X87, 1, vmi_FMUL, t, t, power2); // Use temporary as operand source opType = vmi_FT_80_X87; operand = t; } // convert to required type vmiFType resultType = bytesToIType(resultBytes, isSigned); armEmitFConvertRR(state, resultType, result, opType, operand, round); } // // Set FPSCR flags according to the vmiFPRelation value // static void setFPSCRFlags(armP arm, vmiFPRelation relation) { if (relation == vmi_FPRL_UNORDERED) { arm->sdfpAFlags.ZF = arm->sdfpAFlags.NF = 0; arm->sdfpAFlags.CF = arm->sdfpAFlags.VF = 1; } else if (relation == vmi_FPRL_EQUAL) { arm->sdfpAFlags.NF = arm->sdfpAFlags.VF = 0; arm->sdfpAFlags.ZF = arm->sdfpAFlags.CF = 1; } else if (relation == vmi_FPRL_LESS) { arm->sdfpAFlags.ZF = arm->sdfpAFlags.CF= arm->sdfpAFlags.VF = 0; arm->sdfpAFlags.NF = 1; } else if (relation == vmi_FPRL_GREATER) { arm->sdfpAFlags.ZF = arm->sdfpAFlags.NF= arm->sdfpAFlags.VF = 0; arm->sdfpAFlags.CF = 1; } else { VMI_ABORT("unsupported fp relation result 0x%x", relation); } } // // Assign APSR VCMPflags from FPSCR flags // static void getFPSCRFlags(armP arm) { arm->aflags = arm->sdfpAFlags; } //////////////////////////////////////////////////////////////////////////////// // VFP VLD and VST //////////////////////////////////////////////////////////////////////////////// // // Callback function for V load/store // #define V_LOAD_STORE_FN(_NAME) void _NAME( \ armMorphStateP state, \ Uns32 memBits, \ vmiReg base, \ Int32 offset, \ vmiReg rd \ ) typedef V_LOAD_STORE_FN((*VLoadStoreFn)); // // Swap rdH and rd if endian is big // static inline void endianSwapRegs(armMorphStateP state, vmiReg *rdH, vmiReg *rd) { memEndian endian = armGetEndian((vmiProcessorP)state->arm, False); if (endian == MEM_ENDIAN_BIG) { vmiReg t; t = *rdH; *rdH = *rd; *rd = t; } } // // Emit code to load a VFP register from memory address [base]+offset // static V_LOAD_STORE_FN(emitVLoad) { if (memBits == 2*ARM_GPR_BITS) { vmiReg rdH = getR64Hi(rd); endianSwapRegs(state, &rdH, &rd); armEmitLoadRRRO(state, memBits, offset, rd, rdH, base, getTemp(state), False, False); } else if (memBits <= ARM_GPR_BITS) { armEmitLoadRRO(state, memBits, offset, rd, base, False, False); } else { VMI_ABORT("Invalid memBits %d", memBits); } } // // Emit code to store a VFP register to memory address [base]+offset // When double word register adjust register depending on endian // static V_LOAD_STORE_FN(emitVStore) { if (memBits == 2*ARM_GPR_BITS) { vmiReg rdH = getR64Hi(rd); endianSwapRegs(state, &rdH, &rd); armEmitStoreRRRO(state, memBits, offset, base, rd, rdH); } else if (memBits <= ARM_GPR_BITS) { armEmitStoreRRO(state, memBits, offset, base, rd); } else { VMI_ABORT("Invalid memBits %d", memBits); } } // // Common code for a VLDR or VSTR instruction // static void emitVLoadStore(armMorphStateP state, VLoadStoreFn cb, Bool isLoad) { if(executeFPCheck(state)) { Uns32 ebytes = state->attrs->ebytes; Uns32 memBits = ebytes*8; vmiReg rd = GET_VFP_REG(state, r1, ebytes); vmiReg base = GET_RS(state, r2); Int32 offset = state->info.c; if (isLoad) { // For loads only: align the constant if the base register is the // program counter (has effect for PC-relative Thumb load instructions only) if(VMI_REG_EQUAL(base, ARM_PC)) { offset = alignConstWithPC(state, offset); } } (*cb) (state, memBits, base, offset, rd); } } // // Emit code for VLDR S or D, [RN, #imm] // ARM_MORPH_FN(armEmitVLDR) { emitVLoadStore(state, emitVLoad, True); } // // Emit code for VLDR S or D, [RN, #imm] // ARM_MORPH_FN(armEmitVSTR) { emitVLoadStore(state, emitVStore, False); } // // Common code for Load or Store Multiple using the passed call back function // static void emitVLoadStoreM(armMorphStateP state, VLoadStoreFn cb) { if(executeFPCheck(state)) { Uns32 ebytes = state->attrs->ebytes; Uns32 memBits = ebytes*8; vmiReg base = GET_RS(state, r1); Uns32 nregs = state->info.nregs; Bool increment = doIncrement(state); Uns32 frameSize = nregs * ebytes; Int32 frameDelta = increment ? frameSize : -frameSize; Int32 offset = increment ? 0 : -frameSize; Int32 stepBefore = getStepBefore(state, ebytes); Int32 stepAfter = getStepAfter (state, ebytes); Uns32 r; // load or store registers for(r=0; rinfo.wb) { armEmitBinopRC(state, ARM_GPR_BITS, vmi_ADD, base, frameDelta, 0); } } } // // Emit code for VLDM RN{!}, // ARM_MORPH_FN(armEmitVLDM) { emitVLoadStoreM(state, emitVLoad); } // // Emit code for VSTM RN{!}, // ARM_MORPH_FN(armEmitVSTM) { emitVLoadStoreM(state, emitVStore); } //////////////////////////////////////////////////////////////////////////////// // VFP VMOV, etc instructions //////////////////////////////////////////////////////////////////////////////// // // Emit code for VMRS // ARM_MORPH_FN(armEmitVMRS) { Uns32 bits = ARM_GPR_BITS; // FPSCR is accessible if cp10/11 access is enabled if(!executeFPCheck(state)) { // no action } else if(state->info.r1==ARM_REG_PC) { // assign CPSR flags from FPSCR flags armEmitArgProcessor(state); armEmitCall(state, (vmiCallFn)getFPSCRFlags); // terminate the code block (derived flags are invalid) armEmitEndBlock(); } else { vmiReg rd = GET_RD(state, r1); armEmitArgProcessor(state); armEmitCallResult(state, (vmiCallFn)armReadFPSCR, bits, rd); } } // // Emit code for VMSR // ARM_MORPH_FN(armEmitVMSR) { Uns32 bits = ARM_GPR_BITS; // FPSCR is accessible in user mode if(executeFPCheck(state)) { armEmitArgProcessor(state); armEmitArgReg(state, bits, GET_RS(state, r1)); armEmitArgUns32(state, FPSCR_MASK); armEmitCall(state, (vmiCallFn)armWriteFPSCR); // terminate the code block (block masks or floating point // mode may have changed) armEmitEndBlock(); } } // // Emit code for VMOV.F32 Sd, Sm // ARM_MORPH_FN(armEmitVMOVR_VFP) { if(executeFPCheck(state)) { vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rs = GET_VFP_SREG(state, r2); armEmitMoveRR(state, 32, rd, rs); } } // // Emit code for VMOV.F32 Sd, # or VMOV.F64 Dd, # // ARM_MORPH_FN(armEmitVMOVI_VFP) { if(executeFPCheck(state)) { vmiReg rd = GET_VFP_SREG(state, r1); Uns64 mi = state->info.sdfpMI.u64; armEmitMoveRC(state, 32, rd, mi); } } // // Emit code for VMOV RT, SN // ARM_MORPH_FN(armEmitVMOVRS) { if(executeFPCheck(state)) { vmiReg rd = GET_RD(state, r1); vmiReg rs = GET_VFP_SREG(state, r2); armEmitMoveRR(state, ARM_GPR_BITS, rd, rs); } } // // Emit code for VMOV SN, RT // ARM_MORPH_FN(armEmitVMOVSR) { if(executeFPCheck(state)) { vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rs = GET_RS(state, r2); armEmitMoveRR(state, ARM_GPR_BITS, rd, rs); } } // // Emit code for VMOV RT, RT2, DN // ARM_MORPH_FN(armEmitVMOVRRD) { if(executeFPCheck(state)) { vmiReg rdL = GET_RD(state, r1); vmiReg rdH = GET_RD(state, r2); vmiReg rsL = GET_VFP_DREG(state, r3); vmiReg rsH = getR64Hi(rsL); armEmitMoveRR(state, ARM_GPR_BITS, rdL, rsL); armEmitMoveRR(state, ARM_GPR_BITS, rdH, rsH); } } // // Emit code for VMOV DN, RT, RT2 // ARM_MORPH_FN(armEmitVMOVDRR) { if(executeFPCheck(state)) { vmiReg rdL = GET_VFP_DREG(state, r1); vmiReg rdH = getR64Hi(rdL); vmiReg rsL = GET_RS(state, r2); vmiReg rsH = GET_RS(state, r3); armEmitMoveRR(state, ARM_GPR_BITS, rdL, rsL); armEmitMoveRR(state, ARM_GPR_BITS, rdH, rsH); } } // // Emit code for VMOV RT, RT2, SM, SM1 // ARM_MORPH_FN(armEmitVMOVRRSS) { if(executeFPCheck(state)) { vmiReg rdL = GET_RD(state, r1); vmiReg rdH = GET_RD(state, r2); vmiReg rsL = GET_VFP_SREG(state, r3); vmiReg rsH = GET_VFP_SREG(state, r3+1); armEmitMoveRR(state, ARM_GPR_BITS, rdL, rsL); armEmitMoveRR(state, ARM_GPR_BITS, rdH, rsH); } } // // Emit code for VMOV SM, SM1, RT, RT2 // ARM_MORPH_FN(armEmitVMOVSSRR) { if(executeFPCheck(state)) { vmiReg rdL = GET_VFP_SREG(state, r1); vmiReg rdH = GET_VFP_SREG(state, r1+1); vmiReg rsL = GET_RS(state, r2); vmiReg rsH = GET_RS(state, r3); armEmitMoveRR(state, ARM_GPR_BITS, rdL, rsL); armEmitMoveRR(state, ARM_GPR_BITS, rdH, rsH); } } // // Emit code for VMOV DD[x], RT // ARM_MORPH_FN(armEmitVMOVZR) { if(executeFPCheck(state)) { Uns32 index = state->info.index; vmiReg rd = GET_VFP_SCALAR(state, r1, index); vmiReg rs = GET_RS(state, r2); armEmitMoveRR(state, ARM_GPR_BITS, rd, rs); } } // // Emit code for VMOV RT, DD[x] // ARM_MORPH_FN(armEmitVMOVRZ) { if(executeFPCheck(state)) { Uns32 index = state->info.index; vmiReg rd = GET_RS(state, r1); vmiReg rs = GET_VFP_SCALAR(state, r2, index); armEmitMoveRR(state, ARM_GPR_BITS, rd, rs); } } //////////////////////////////////////////////////////////////////////////////// // VFP Data-Processing Instructions //////////////////////////////////////////////////////////////////////////////// // // Emit code for VFP single precision floating point , with 3 regs // ARM_MORPH_FN(armEmitVFPBinop) { if(executeFPCheck(state)) { vmiReg r1 = GET_VFP_SREG(state, r1); vmiReg r2 = GET_VFP_SREG(state, r2); vmiReg r3 = GET_VFP_SREG(state, r3); vmiFBinop op = state->attrs->fbinop; Bool negate = state->attrs->negate; armEmitFBinopSimdRRR(state, vmi_FT_32_IEEE_754, 1, op, r1, r2, r3); // If negate attribute is selected negate the result if (negate) FPNeg(state, r1, r1, 32); } } // // Emit code for single precision VFP floating point , with 2 regs // ARM_MORPH_FN(armEmitVFPUnop) { if(executeFPCheck(state)) { vmiReg r1 = GET_VFP_SREG(state, r1); vmiReg r2 = GET_VFP_SREG(state, r2); vmiFBinop op = state->attrs->funop; armEmitFUnopSimdRR(state, vmi_FT_32_IEEE_754, 1, op, r1, r2); } } // // Emit code for VFP VABS Sd, Sm // Simply mask off sign bit // ARM_MORPH_FN(armEmitVABS_VFP) { if(executeFPCheck(state)) { vmiReg r1 = GET_VFP_SREG(state, r1); vmiReg r2 = GET_VFP_SREG(state, r2); armEmitBinopRRC(state, 32, vmi_ANDN, r1, r2, 0x80000000, 0); } } // // Emit code for VFP VNEG Sd, Sm or VNEG Dd, Dm // ARM_MORPH_FN(armEmitVNEG_VFP) { if(executeFPCheck(state)) { vmiReg r1 = GET_VFP_SREG(state, r1); vmiReg r2 = GET_VFP_SREG(state, r2); // Take negative value by simply toggling the sign bit FPNeg(state, r1, r2, 32); } } // // Emit code for VFP floating point Multiply (negate) accumulate/subtract instruction // ARM_MORPH_FN(armEmitVMulAcc_VFP) { if(executeFPCheck(state)) { vmiReg r1 = GET_VFP_SREG(state, r1); vmiReg r2 = GET_VFP_SREG(state, r2); vmiReg r3 = GET_VFP_SREG(state, r3); vmiFBinop op = state->attrs->fbinop; Bool negate = state->attrs->negate; vmiReg product = newTemp32(state); vmiReg acc = r1; VMI_ASSERT(op==vmi_FSUB || op==vmi_FADD, "Invalid fbinop"); // Perform Multiply to temporary armEmitFBinopSimdRRR(state, vmi_FT_32_IEEE_754, 1, vmi_FMUL, product, r2, r3); if (negate) { // Negate the accumulation value before adding/subtracting acc = newTemp64(state); FPNeg(state, acc, r1, 32); } // To implement subtraction, negate the value being subtracted and use vmi_FADD if (op == vmi_FSUB) FPNeg(state, product, product, 32); // Add the (possibly negated) product to/from the accumlate value (+/- r1) armEmitFBinopSimdRRR(state, vmi_FT_32_IEEE_754, 1, vmi_FADD, r1, acc, product); } } // // Emit code for VFP single precision fused multiply accumulate // ARM_MORPH_FN(armEmitVFusedMAC) { if(executeFPCheck(state)) { vmiReg rd = GET_VFP_SREG(state, r1); vmiReg addend = rd; vmiReg op1 = GET_VFP_SREG(state, r2); vmiReg op2 = GET_VFP_SREG(state, r3); Bool negate = state->attrs->negate; Bool subtract = state->attrs->subtract; if (subtract) { // Use negative of op1 vmiReg t = newTemp32(state); FPNeg(state, t, op1, 32); op1 = t; } if (negate) { // Use negative of r1 as addend vmiReg t = newTemp32(state); FPNeg(state, t, addend, 32); addend = t; } // Do rd = addend + (op1 * op2) with no rounding of intermediate results armEmitFTernopSimdRRRR(state, vmi_FT_32_IEEE_754, 1, vmi_FMADD, rd, op1, op2, addend, False); } } //////////////////////////////////////////////////////////////////////////////// // VFP VCMP Instructions //////////////////////////////////////////////////////////////////////////////// // // Common code to execute VFP VCMP instructions // static void emitVCmpVFP(armMorphStateP state, vmiReg rd, vmiReg rm) { Bool allowQNaN = state->attrs->allowQNaN; vmiReg relation = getTemp(state); // Compare the floating point operands, getting vmiFPRelation result in the register relation armEmitFCompareRR(state, vmi_FT_32_IEEE_754, relation, rd, rm, allowQNaN, True); // Set the FPSCR N, Z, C, V flags according to the result armEmitArgProcessor(state); armEmitArgReg(state, 8, relation); armEmitCall(state, (vmiCallFn) setFPSCRFlags); } // // Emit code for VFP VCMP/VCMPE instruction: VCMP{E} Sd, Sm // ARM_MORPH_FN(armEmitVCMP_VFP) { if(executeFPCheck(state)) { vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rm = GET_VFP_SREG(state, r2); emitVCmpVFP(state, rd, rm); } } // // Emit code for VFP VCMP/VCMPE immediate 0.0 instruction: VCMP{E} Sd, #0.0 // Note: VCMP instruction does not use short vectors // ARM_MORPH_FN(armEmitVCMP0_VFP) { if(executeFPCheck(state)) { vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rm = newTemp32(state); armEmitMoveRC(state, 32, rm, 0); emitVCmpVFP(state, rd, rm); } } //////////////////////////////////////////////////////////////////////////////// // VFP VCVT Instructions //////////////////////////////////////////////////////////////////////////////// // // Emit code for VFP Half to Single precision conversion: VCVTT.F32.F16 Sd, Sm or VCVTB.F32.F16 Sd, Sm // ARM_MORPH_FN(armEmitVCVT_SH_VFP) { if(executeFPCheck(state)) { Uns32 top = state->attrs->highhalf ? 1 : 0; vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rm = GET_VFP_SREG(state, r2); // point to top half of register when VCVTT if (top) rm = VMI_REG_DELTA(rm, 2); armEmitArgProcessor(state); // argument 1: processor armEmitArgReg(state, 16, rm); // argument 2: value in rm armEmitCallResult(state, (vmiCallFn)armFPHalfToSingle, 32, rd); } } // // Emit code for VFP Single to Half precision conversion: VCVTT.F16.F32 Sd, Sm or VCVTB.F16.F32 Sd, Sm // ARM_MORPH_FN(armEmitVCVT_HS_VFP) { if(executeFPCheck(state)) { Uns32 top = state->attrs->highhalf ? 1 : 0; vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rm = GET_VFP_SREG(state, r2); // point to top half of register when VCVTT if (top) rd = VMI_REG_DELTA(rd, 2); armEmitArgProcessor(state); // argument 1: processor armEmitArgReg(state, 32, rm);// argument 2: value armEmitCallResult(state, (vmiCallFn)armFPSingleToHalf, 16, rd); } } // // Emit code for VFP single precision Floating point to Fixed point (32 or 16 bit) conversion: VCVT // Note: r1 always equals r2 and the size of the fixed point value is defined by ebytes // Note: sextend indicates if 16 bit result should be sign extended // Note: roundFPSCR indicates if the normal rounding mode or round to nearest mode is used // ARM_MORPH_FN(armEmitVCVT_XF_VFP) { if(executeFPCheck(state)) { VMI_ASSERT(state->info.r1== state->info.r2, "r1 must be same as r2"); Uns32 fracBits = state->info.c; Uns32 sextend = state->attrs->sextend; vmiFPRC roundFPSCR = state->attrs->roundFPSCR; Uns32 fxBytes = state->attrs->ebytes; vmiReg rd = GET_VFP_SREG(state, r1); FPToFixed(state, rd, fxBytes, rd, 4, fracBits, sextend, !roundFPSCR); if (fxBytes != 4) { // sign extend to fill destination registeru armEmitMoveExtendRR(state, 32, rd, fxBytes*8, rd, sextend); } } } // // Emit code for VFP single precsion Floating point to signed or unsigned 32 bit Integer conversion: VCVT // ARM_MORPH_FN(armEmitVCVT_IF_VFP) { if(executeFPCheck(state)) { Uns32 sextend = state->attrs->sextend; vmiFPRC roundFPSCR = state->attrs->roundFPSCR; vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rm = GET_VFP_SREG(state, r2); FPToFixed(state, rd, 4, rm, 4, 0, sextend, !roundFPSCR); } } // // Emit code for VFP Fixed point to single precision Floating point conversion: VCVT // Note: r1 always equals r2 and the size of the fixed point value is defined by ebytes // ARM_MORPH_FN(armEmitVCVT_FX_VFP) { if(executeFPCheck(state)) { VMI_ASSERT(state->info.r1== state->info.r2, "r1 must be same as r2"); Uns32 fracBits = state->info.c; Uns32 sextend = state->attrs->sextend; vmiFPRC roundFPSCR = state->attrs->roundFPSCR; Uns32 fxBytes = state->attrs->ebytes; vmiReg rd = GET_VFP_SREG(state, r1); fixedToFP(state, rd, 4, rd, fxBytes, fracBits, sextend, !roundFPSCR); } } // // Emit code for VFP Integer to single precision Floating point conversion: VCVT // ARM_MORPH_FN(armEmitVCVT_FI_VFP) { if(executeFPCheck(state)) { Uns32 sextend = state->attrs->sextend; vmiFPRC roundFPSCR = state->attrs->roundFPSCR; vmiReg rd = GET_VFP_SREG(state, r1); vmiReg rm = GET_VFP_SREG(state, r2); fixedToFP(state, rd, 4, rm, 4, 0, sextend, !roundFPSCR); } } //////////////////////////////////////////////////////////////////////////////// // DEPRECATED DISASSEMBLY MODE //////////////////////////////////////////////////////////////////////////////// // // Disassembly mode: SWI 99/9999 terminates the test // #define ARM_SWI_EXIT_CODE_THUMB 99 #define ARM_SWI_EXIT_CODE_NORMAL 9999 // // Should morphing be disabled? (disassembly test mode only) // static Bool disableMorph(armMorphStateP state) { armP arm = state->arm; if(!ARM_DISASSEMBLE(arm)) { return False; } else if(state->info.type!=ARM_IT_SWI) { return True; } else if( ( IN_THUMB_MODE(arm) && (state->info.c==ARM_SWI_EXIT_CODE_THUMB)) || (!IN_THUMB_MODE(arm) && (state->info.c==ARM_SWI_EXIT_CODE_NORMAL)) ) { armEmitExit(); return True; } else { return True; } } //////////////////////////////////////////////////////////////////////////////// // MORPHER MAIN ROUTINES //////////////////////////////////////////////////////////////////////////////// // // Advance to the new IT state // static Uns8 itAdvance(Uns8 oldState) { Uns8 newState = (oldState & 0xe0) | ((oldState<<1) & 0x1f); return (newState & 0xf) ? newState : 0; } // // Emit code to update ITSTATE if required when the instruction completes (note // that jumps and branches take special action to reset ITSTATE elsewhere) // static void emitUpdateITState(armMorphStateP state) { armP arm = state->arm; if(state->info.it) { arm->itStateMT = state->info.it; armEmitMoveRC(state, 8, ARM_IT_STATE, arm->itStateMT); } else if(arm->itStateMT) { arm->itStateMT = itAdvance(arm->itStateMT); armEmitMoveRC(state, 8, ARM_IT_STATE, arm->itStateMT); } } // // Default morpher callback for implemented instructions // static void emitImplemented(armMorphStateP state) { // start code to skip this instruction conditionally unless it can be // emitted as a conditional jump setSkipLabel(state, !state->attrs->condJump ? emitStartSkip(state) : 0); // generate instruction code state->attrs->morphCB(state); // generate implicit jump to next instruction if required armEmitImplicitUncondJump(state); // insert conditional instruction skip target label emitLabel(getSkipLabel(state)); // update ITSTATE if required when the instruction completes emitUpdateITState(state); } // // Check whether the VFP floating-point type is not supported at the minimum required level // static Bool supportVFP(armMorphStateP state, armSDFPType dt, Uns32 minLevel) { armP arm = state->arm; Bool ok; VMI_ASSERT (state->attrs->iType==ARM_TY_VFP, "Called with non-VFP instruction"); if (!FPU_PRESENT(state->arm)) { ok = False; } else if (dt == ARM_SDFPT_F32) { ok = SCS_FIELD(arm, MVFR0, SinglePrecision) >= minLevel; } else if (dt == ARM_SDFPT_F64) { ok = SCS_FIELD(arm, MVFR0, DoublePrecision) >= minLevel; } else { ok = True; } return ok; } // // Determine whether a feature is supported by ISAR // static Bool supportedByISAR(armP arm, armMorphStateP state) { switch(state->info.isar) { case ARM_ISAR_DIV: return ARM_ISAR(0, Divide_instrs); case ARM_ISAR_BKPT: return ARM_ISAR(0, Debug_instrs); case ARM_ISAR_CBZ: return ARM_ISAR(0, CmpBranch_instrs); case ARM_ISAR_BFC: return ARM_ISAR(0, BitField_instrs); case ARM_ISAR_CLZ: return ARM_ISAR(0, BitCount_instrs); case ARM_ISAR_SWP: return ARM_ISAR(0, Swap_instrs); case ARM_ISAR_BXJ: return ARM_ISAR(1, Jazelle_instrs); case ARM_ISAR_BX: return ARM_ISAR(1, Interwork_instrs)>0; case ARM_ISAR_BLX: return ARM_ISAR(1, Interwork_instrs)>1; case ARM_ISAR_MOVT: return ARM_ISAR(1, Immediate_instrs); case ARM_ISAR_IT: return ARM_ISAR(1, IfThen_instrs); case ARM_ISAR_SXTB: return ARM_ISAR(1, Extend_instrs)>0; case ARM_ISAR_SXTAB: return ARM_ISAR(1, Extend_instrs)>1; case ARM_ISAR_SXTB16: return (ARM_ISAR(1, Extend_instrs)>1) && DSP_PRESENT(arm); case ARM_ISAR_SRS: return ARM_ISAR(1, Except_AR_instrs); case ARM_ISAR_LDM_UR: return ARM_ISAR(1, Except_instrs); case ARM_ISAR_SETEND: return ARM_ISAR(1, Endian_instrs); case ARM_ISAR_REV: return ARM_ISAR(2, Reversal_instrs)>0; case ARM_ISAR_RBIT: return ARM_ISAR(2, Reversal_instrs)>1; case ARM_ISAR_MRS_AR: return ARM_ISAR(2, PSR_AR_instrs); case ARM_ISAR_UMULL: return ARM_ISAR(2, MultU_instrs)>0; case ARM_ISAR_UMAAL: return ARM_ISAR(2, MultU_instrs)>1; case ARM_ISAR_SMULL: return ARM_ISAR(2, MultS_instrs)>0; case ARM_ISAR_SMLABB: return ARM_ISAR(2, MultS_instrs)>1; case ARM_ISAR_SMLAD: return ARM_ISAR(2, MultS_instrs)>2; case ARM_ISAR_MLA: return ARM_ISAR(2, Mult_instrs)>0; case ARM_ISAR_MLS: return ARM_ISAR(2, Mult_instrs)>1; case ARM_ISAR_PLD: return ARM_ISAR(2, MemHint_instrs)>0; case ARM_ISAR_PLI: return ARM_ISAR(2, MemHint_instrs)>2; case ARM_ISAR_LDRD: return ARM_ISAR(2, LoadStore_instrs); case ARM_ISAR_NOP: return ARM_ISAR(3, TrueNOP_instrs); case ARM_ISAR_MOVLL: return ARM_ISAR(3, ThumbCopy_instrs); case ARM_ISAR_TBB: return ARM_ISAR(3, TabBranch_instrs); case ARM_ISAR_LDREX: return ARM_ISAR(3, SynchPrim_instrs)>0; case ARM_ISAR_CLREX: return (ARM_ISAR(3, SynchPrim_instrs)>1) || (ARM_ISAR(4, SynchPrim_instrs_frac)==3); case ARM_ISAR_LDREXD: return ARM_ISAR(3, SynchPrim_instrs)>1; case ARM_ISAR_SVC: return ARM_ISAR(3, SVC_instrs); case ARM_ISAR_SSAT: return ARM_ISAR(3, SIMD_instrs)>0; case ARM_ISAR_PKHBT: return DSP_PRESENT(arm); case ARM_ISAR_QADD: return ARM_ISAR(3, Saturate_instrs); case ARM_ISAR_MRS_M: return ARM_ISAR(4, PSR_M_instrs); case ARM_ISAR_DMB: return ARM_ISAR(4, Barrier_instrs); case ARM_ISAR_LDRBT: return ARM_ISAR(4, Unpriv_instrs)>0; case ARM_ISAR_LDRHT: return ARM_ISAR(4, Unpriv_instrs)>1; case ARM_ISAR_VMRS: return FPU_PRESENT(arm); case ARM_ISAR_VFPV2: return supportVFP(state, state->info.dt1, 1); case ARM_ISAR_VFPV3: return supportVFP(state, state->info.dt1, 2); case ARM_ISAR_VFPFMAC: return FPU_PRESENT(arm) && SCS_FIELD(arm, MVFR1, VFP_FusedMAC); case ARM_ISAR_VFPSQRT: return SCS_FIELD(arm, MVFR0, SquareRoot) && supportVFP(state, state->info.dt1, 1); case ARM_ISAR_VFPDIV: return SCS_FIELD(arm, MVFR0, Divide) && supportVFP(state, state->info.dt1, 1); case ARM_ISAR_VFPCVT2: return supportVFP(state, state->info.dt1, 1) && supportVFP(state, state->info.dt2, 1); case ARM_ISAR_VFPCVT3: return supportVFP(state, state->info.dt1, 2) && supportVFP(state, state->info.dt2, 2); case ARM_ISAR_VFPHP: return FPU_PRESENT(arm) && SCS_FIELD(arm, MVFR1, VFP_HalfPrecision); default: VMI_ABORT("unimplemented case"); return False; } } // // Return a boolean indicating whether the processor supports the required // architecture // static Bool supportedOnVariant(armP arm, armMorphStateP state) { armArchitecture configVariant = arm->configInfo.arch; armArchitecture requiredVariant = state->info.support; if(ARM_INSTRUCTION_VERSION(requiredVariant) > getInstructionVersion(arm)) { return False; } else if(!ARM_SUPPORT(configVariant, requiredVariant & ~ARM_MASK_VERSION)) { return False; } else if(state->info.isar && SCS_USE_CPUID(arm)) { return supportedByISAR(arm, state); } else { return True; } } // // Create code for the ARM instruction at the simulated address referenced // by 'thisPC'. // VMI_MORPH_FN(armMorphInstruction) { armP arm = (armP)processor; armMorphState state; // seed morph-time ITSTATE if this is the first instruction in a code block if(firstInBlock) { arm->itStateMT = arm->itStateRT; } // get instruction and instruction type armDecode(arm, thisPC, &state.info); // get morpher attributes for the decoded instruction and initialize other // state fields state.attrs = &armMorphTable[state.info.type]; state.arm = arm; state.nextPC = state.info.thisPC + state.info.bytes; state.skipLabel = 0; state.tempIdx = 0; state.pcFetched = False; state.pcSet = ASPC_NA; state.pcImmediate = 0; state.setMode = False; // if this is the first instruction in the block, mark all derived flags as // invalid if(firstInBlock) { resetDerivedFlags(&state); } if(disableMorph(&state)) { // no action if in disassembly mode } else if(!supportedOnVariant(arm, &state)) { // instruction not supported on this variant emitNotVariant(&state); } else if(state.attrs->morphCB) { // translate the instruction emitImplemented(&state); } else if(state.info.type==ARM_IT_LAST) { // take UsageFault exception emitUsageFault(&state, EXC_UNDEF_UNDEFINSTR); } else { // here if no morph callback specified emitUnimplemented(&state); } } // // Snap instruction fetch addresses to the appropriate boundary // VMI_FETCH_SNAP_FN(armFetchSnap) { armP arm = (armP)processor; Uns32 snap = IN_THUMB_MODE(arm) ? 1 : 3; return thisPC & ~snap; }