diff options
Diffstat (limited to 'src/jit/emitarm.h')
-rw-r--r-- | src/jit/emitarm.h | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/src/jit/emitarm.h b/src/jit/emitarm.h new file mode 100644 index 0000000000..031ad04105 --- /dev/null +++ b/src/jit/emitarm.h @@ -0,0 +1,493 @@ +// +// 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_ |