// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // #if defined(_TARGET_ARM_) /************************************************************************/ /* Routines that compute the size of / encode instructions */ /************************************************************************/ struct CnsVal { int cnsVal; #ifdef RELOC_SUPPORT bool cnsReloc; #endif }; insSize emitInsSize (insFormat insFmt); BYTE * emitOutputAM (BYTE *dst, instrDesc *id, size_t code, CnsVal * addc = NULL); BYTE * emitOutputSV (BYTE *dst, instrDesc *id, size_t code, CnsVal * addc = NULL); BYTE * emitOutputCV (BYTE *dst, instrDesc *id, size_t code, CnsVal * addc = NULL); BYTE * emitOutputR (BYTE *dst, instrDesc *id); BYTE * emitOutputRI (BYTE *dst, instrDesc *id); BYTE * emitOutputRR (BYTE *dst, instrDesc *id); BYTE * emitOutputIV (BYTE *dst, instrDesc *id); #ifdef FEATURE_ITINSTRUCTION BYTE * emitOutputIT (BYTE *dst, instruction ins, insFormat fmt, ssize_t condcode); #endif // FEATURE_ITINSTRUCTION BYTE * emitOutputNOP (BYTE *dst, instruction ins, insFormat fmt); BYTE * emitOutputLJ (insGroup *ig, BYTE *dst, instrDesc *id); BYTE * emitOutputShortBranch(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, instrDescJmp* id); static unsigned emitOutput_Thumb1Instr(BYTE *dst, ssize_t code); static unsigned emitOutput_Thumb2Instr(BYTE *dst, ssize_t code); /************************************************************************/ /* Debug-only routines to display instructions */ /************************************************************************/ #ifdef DEBUG const char * emitFPregName (unsigned reg, bool varName = true); void emitDispInst (instruction ins, insFlags flags); void emitDispReloc (int value, bool addComma); void emitDispImm (int imm, bool addComma, bool alwaysHex = false); void emitDispCond (int cond); void emitDispShiftOpts(insOpts opt); void emitDispRegmask (int imm, bool encodedPC_LR); void emitDispRegRange(regNumber reg, int len, emitAttr attr); void emitDispReg (regNumber reg, emitAttr attr, bool addComma); void emitDispFloatReg(regNumber reg, emitAttr attr, bool addComma); void emitDispAddrR (regNumber reg, emitAttr attr); void emitDispAddrRI (regNumber reg, int imm, emitAttr attr); void emitDispAddrRR (regNumber reg1, regNumber reg2, emitAttr attr); void emitDispAddrRRI (regNumber reg1, regNumber reg2, int imm, emitAttr attr); void emitDispAddrPUW (regNumber reg, int imm, insOpts opt, emitAttr attr); void emitDispGC (emitAttr attr); void emitDispInsHelp (instrDesc *id, bool isNew, bool doffs, bool asmfm, unsigned offs = 0, BYTE * code = 0, size_t sz = 0, insGroup *ig = NULL); void emitDispIns (instrDesc *id, bool isNew, bool doffs, bool asmfm, unsigned offs = 0, BYTE * code = 0, size_t sz = 0, insGroup *ig = NULL); #endif // DEBUG /************************************************************************/ /* Private members that deal with target-dependent instr. descriptors */ /************************************************************************/ private: instrDesc *emitNewInstrAmd (emitAttr attr, int dsp); instrDesc *emitNewInstrAmdCns (emitAttr attr, int dsp, int cns); instrDesc *emitNewInstrCallDir (int argCnt, VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, emitAttr retSize); instrDesc *emitNewInstrCallInd( int argCnt, ssize_t disp, VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, emitAttr retSize); void emitGetInsCns (instrDesc *id, CnsVal *cv); int emitGetInsAmdCns(instrDesc *id, CnsVal *cv); void emitGetInsDcmCns(instrDesc *id, CnsVal *cv); int emitGetInsAmdAny(instrDesc *id); /************************************************************************/ /* Private helpers for instruction output */ /************************************************************************/ private: bool emitInsIsCompare(instruction ins); bool emitInsIsLoad (instruction ins); bool emitInsIsStore (instruction ins); bool emitInsIsLoadOrStore(instruction ins); /***************************************************************************** * * Convert between an index scale in bytes to a smaller encoding used for * storage in instruction descriptors. */ inline emitter::opSize emitEncodeScale(size_t scale) { assert(scale == 1 || scale == 2 || scale == 4 || scale == 8); return emitSizeEncode[scale-1]; } inline emitAttr emitDecodeScale(unsigned ensz) { assert(ensz < 4); return emitter::emitSizeDecode[ensz]; } static bool isModImmConst (int imm); static int encodeModImmConst (int imm); static int insUnscaleImm (int imm, emitAttr size); /************************************************************************/ /* Public inline informational methods */ /************************************************************************/ public: inline static bool isLowRegister (regNumber reg) { return (reg <= REG_R7); } inline static bool isGeneralRegister (regNumber reg) { return (reg <= REG_R15); } inline static bool isFloatReg (regNumber reg) { return (reg >= REG_F0 && reg <= REG_F31); } inline static bool isDoubleReg (regNumber reg) { return isFloatReg(reg) && ((reg % 2) == 0); } inline static bool insSetsFlags (insFlags flags) { return (flags != INS_FLAGS_NOT_SET); } inline static bool insDoesNotSetFlags(insFlags flags) { return (flags != INS_FLAGS_SET); } inline static insFlags insMustSetFlags (insFlags flags) { return (flags == INS_FLAGS_SET) ? INS_FLAGS_SET : INS_FLAGS_NOT_SET; } inline static insFlags insMustNotSetFlags(insFlags flags) { return (flags == INS_FLAGS_NOT_SET) ? INS_FLAGS_NOT_SET : INS_FLAGS_SET; } inline static bool insOptsNone (insOpts opt) { return (opt == INS_OPTS_NONE); } inline static bool insOptAnyInc (insOpts opt) { return (opt == INS_OPTS_LDST_PRE_DEC) || (opt == INS_OPTS_LDST_POST_INC); } inline static bool insOptsPreDec (insOpts opt) { return (opt == INS_OPTS_LDST_PRE_DEC); } inline static bool insOptsPostInc (insOpts opt) { return (opt == INS_OPTS_LDST_POST_INC); } inline static bool insOptAnyShift (insOpts opt) { return ((opt >= INS_OPTS_RRX) && (opt <= INS_OPTS_ROR) ); } inline static bool insOptsRRX (insOpts opt) { return (opt == INS_OPTS_RRX); } inline static bool insOptsLSL (insOpts opt) { return (opt == INS_OPTS_LSL); } inline static bool insOptsLSR (insOpts opt) { return (opt == INS_OPTS_LSR); } inline static bool insOptsASR (insOpts opt) { return (opt == INS_OPTS_ASR); } inline static bool insOptsROR (insOpts opt) { return (opt == INS_OPTS_ROR); } /************************************************************************/ /* The public entry points to output instructions */ /************************************************************************/ public: static bool emitIns_valid_imm_for_alu(int imm); static bool emitIns_valid_imm_for_mov(int imm); static bool emitIns_valid_imm_for_small_mov(regNumber reg, int imm, insFlags flags); static bool emitIns_valid_imm_for_add(int imm, insFlags flags); static bool emitIns_valid_imm_for_add_sp(int imm); #ifdef ARM_HAZARD_AVOIDANCE bool emitKraitHazardActive(instrDesc * id); #endif void emitIns (instruction ins); void emitIns_I (instruction ins, emitAttr attr, ssize_t imm); void emitIns_R (instruction ins, emitAttr attr, regNumber reg); void emitIns_R_I (instruction ins, emitAttr attr, regNumber reg, ssize_t imm, insFlags flags = INS_FLAGS_DONT_CARE); void emitIns_R_R (instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insFlags flags = INS_FLAGS_DONT_CARE); void emitIns_R_I_I (instruction ins, emitAttr attr, regNumber reg1, int imm1, int imm2, insFlags flags = INS_FLAGS_DONT_CARE); void emitIns_R_R_I (instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int imm, insFlags flags = INS_FLAGS_DONT_CARE, insOpts opt = INS_OPTS_NONE); void emitIns_R_R_R (instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, insFlags flags = INS_FLAGS_DONT_CARE); void emitIns_R_R_I_I(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int imm1, int imm2, insFlags flags = INS_FLAGS_DONT_CARE); void emitIns_R_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, int imm, insFlags flags = INS_FLAGS_DONT_CARE, insOpts opt = INS_OPTS_NONE); void emitIns_R_R_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, regNumber reg4); void emitIns_C (instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, int offs); void emitIns_S (instruction ins, emitAttr attr, int varx, int offs); void emitIns_genStackOffset(regNumber r, int varx, int offs); void emitIns_S_R (instruction ins, emitAttr attr, regNumber ireg, int varx, int offs); void emitIns_R_S (instruction ins, emitAttr attr, regNumber ireg, int varx, int offs); void emitIns_S_I (instruction ins, emitAttr attr, int varx, int offs, int val); void emitIns_R_C (instruction ins, emitAttr attr, regNumber reg, CORINFO_FIELD_HANDLE fldHnd, int offs); void emitIns_C_R (instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fldHnd, regNumber reg, int offs); void emitIns_C_I (instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, ssize_t offs, ssize_t val); void emitIns_R_L (instruction ins, emitAttr attr, BasicBlock * dst, regNumber reg); void emitIns_R_D (instruction ins, emitAttr attr, unsigned offs, regNumber reg); void emitIns_J_R (instruction ins, emitAttr attr, BasicBlock *dst, regNumber reg); void emitIns_I_AR (instruction ins, emitAttr attr, int val, regNumber reg, int offs, int memCookie = 0, void * clsCookie = NULL); void emitIns_R_AR (instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs, int memCookie = 0, void * clsCookie = NULL); void emitIns_R_AI (instruction ins, emitAttr attr, regNumber ireg, ssize_t disp); void emitIns_AR_R (instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs, int memCookie = 0, void * clsCookie = NULL); void emitIns_R_ARR (instruction ins, emitAttr attr, regNumber ireg, regNumber reg, regNumber rg2, int disp); void emitIns_ARR_R (instruction ins, emitAttr attr, regNumber ireg, regNumber reg, regNumber rg2, int disp); void emitIns_R_ARX (instruction ins, emitAttr attr, regNumber ireg, regNumber reg, regNumber rg2, unsigned mul, int disp); enum EmitCallType { // I have included here, but commented out, all the values used by the x86 emitter. // However, ARM has a much reduced instruction set, and so the ARM emitter only // supports a subset of the x86 variants. By leaving them commented out, it becomes // a compile time error if code tries to use them (and hopefully see this comment // and know why they are unavailible on ARM), while making it easier to stay // in-sync with x86 and possibly add them back in if needed. EC_FUNC_TOKEN, // Direct call to a helper/static/nonvirtual/global method // EC_FUNC_TOKEN_INDIR, // Indirect call to a helper/static/nonvirtual/global method EC_FUNC_ADDR, // Direct call to an absolute address // EC_FUNC_VIRTUAL, // Call to a virtual method (using the vtable) EC_INDIR_R, // Indirect call via register // EC_INDIR_SR, // Indirect call via stack-reference (local var) // EC_INDIR_C, // Indirect call via static class var // EC_INDIR_ARD, // Indirect call via an addressing mode EC_COUNT }; void emitIns_Call (EmitCallType callType, CORINFO_METHOD_HANDLE methHnd, // used for pretty printing INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) // used to report call sites to the EE void* addr, ssize_t argSize, emitAttr retSize, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, IL_OFFSETX ilOffset = BAD_IL_OFFSET, regNumber ireg = REG_NA, regNumber xreg = REG_NA, unsigned xmul = 0, int disp = 0, bool isJump = false, bool isNoGC = false, bool isProfLeaveCB = false); /***************************************************************************** * * Given an instrDesc, return true if it's a conditional jump. */ inline bool emitIsCondJump(instrDesc *jmp) { return (jmp->idInsFmt() == IF_T2_J1) || (jmp->idInsFmt() == IF_T1_K) || (jmp->idInsFmt() == IF_LARGEJMP); } /***************************************************************************** * * Given an instrDesc, return true if it's a comapre and jump. */ inline bool emitIsCmpJump(instrDesc *jmp) { return (jmp->idInsFmt() == IF_T1_I); } /***************************************************************************** * * Given a instrDesc, return true if it's an unconditional jump. */ inline bool emitIsUncondJump(instrDesc *jmp) { return (jmp->idInsFmt() == IF_T2_J2) || (jmp->idInsFmt() == IF_T1_M); } /***************************************************************************** * * Given a instrDesc, return true if it's a load label instruction. */ inline bool emitIsLoadLabel(instrDesc *jmp) { return (jmp->idInsFmt() == IF_T2_M1) || (jmp->idInsFmt() == IF_T1_J3) || (jmp->idInsFmt() == IF_T2_N1); } #endif // _TARGET_ARM_