| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | // |
| 6 | // This class contains all the data & functionality for code generation |
| 7 | // of a method, except for the target-specific elements, which are |
| 8 | // primarily in the Target class. |
| 9 | // |
| 10 | |
| 11 | #ifndef _CODEGEN_H_ |
| 12 | #define _CODEGEN_H_ |
| 13 | #include "compiler.h" // temporary?? |
| 14 | #include "codegeninterface.h" |
| 15 | #include "regset.h" |
| 16 | #include "jitgcinfo.h" |
| 17 | |
| 18 | #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_) |
| 19 | #define FOREACH_REGISTER_FILE(file) \ |
| 20 | for ((file) = &(this->intRegState); (file) != NULL; \ |
| 21 | (file) = ((file) == &(this->intRegState)) ? &(this->floatRegState) : NULL) |
| 22 | #else |
| 23 | #define FOREACH_REGISTER_FILE(file) (file) = &(this->intRegState); |
| 24 | #endif |
| 25 | |
| 26 | class CodeGen : public CodeGenInterface |
| 27 | { |
| 28 | friend class emitter; |
| 29 | friend class DisAssembler; |
| 30 | |
| 31 | public: |
| 32 | // This could use further abstraction |
| 33 | CodeGen(Compiler* theCompiler); |
| 34 | |
| 35 | virtual void genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode); |
| 36 | // TODO-Cleanup: Abstract out the part of this that finds the addressing mode, and |
| 37 | // move it to Lower |
| 38 | virtual bool genCreateAddrMode(GenTree* addr, |
| 39 | bool fold, |
| 40 | bool* revPtr, |
| 41 | GenTree** rv1Ptr, |
| 42 | GenTree** rv2Ptr, |
| 43 | #if SCALED_ADDR_MODES |
| 44 | unsigned* mulPtr, |
| 45 | #endif // SCALED_ADDR_MODES |
| 46 | ssize_t* cnsPtr); |
| 47 | |
| 48 | private: |
| 49 | #if defined(_TARGET_XARCH_) |
| 50 | // Bit masks used in negating a float or double number. |
| 51 | // This is to avoid creating more than one data constant for these bitmasks when a |
| 52 | // method has more than one GT_NEG operation on floating point values. |
| 53 | CORINFO_FIELD_HANDLE negBitmaskFlt; |
| 54 | CORINFO_FIELD_HANDLE negBitmaskDbl; |
| 55 | |
| 56 | // Bit masks used in computing Math.Abs() of a float or double number. |
| 57 | CORINFO_FIELD_HANDLE absBitmaskFlt; |
| 58 | CORINFO_FIELD_HANDLE absBitmaskDbl; |
| 59 | |
| 60 | // Bit mask used in U8 -> double conversion to adjust the result. |
| 61 | CORINFO_FIELD_HANDLE u8ToDblBitmask; |
| 62 | |
| 63 | // Generates SSE2 code for the given tree as "Operand BitWiseOp BitMask" |
| 64 | void genSSE2BitwiseOp(GenTree* treeNode); |
| 65 | |
| 66 | // Generates SSE41 code for the given tree as a round operation |
| 67 | void genSSE41RoundOp(GenTreeOp* treeNode); |
| 68 | #endif // defined(_TARGET_XARCH_) |
| 69 | |
| 70 | void genPrepForCompiler(); |
| 71 | |
| 72 | void genPrepForEHCodegen(); |
| 73 | |
| 74 | inline RegState* regStateForType(var_types t) |
| 75 | { |
| 76 | return varTypeIsFloating(t) ? &floatRegState : &intRegState; |
| 77 | } |
| 78 | inline RegState* regStateForReg(regNumber reg) |
| 79 | { |
| 80 | return genIsValidFloatReg(reg) ? &floatRegState : &intRegState; |
| 81 | } |
| 82 | |
| 83 | regNumber genFramePointerReg() |
| 84 | { |
| 85 | if (isFramePointerUsed()) |
| 86 | { |
| 87 | return REG_FPBASE; |
| 88 | } |
| 89 | else |
| 90 | { |
| 91 | return REG_SPBASE; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | enum CompareKind |
| 96 | { |
| 97 | CK_SIGNED, |
| 98 | CK_UNSIGNED, |
| 99 | CK_LOGICAL |
| 100 | }; |
| 101 | static emitJumpKind genJumpKindForOper(genTreeOps cmp, CompareKind compareKind); |
| 102 | |
| 103 | // For a given compare oper tree, returns the conditions to use with jmp/set in 'jmpKind' array. |
| 104 | // The corresponding elements of jmpToTrueLabel indicate whether the target of the jump is to the |
| 105 | // 'true' label or a 'false' label. |
| 106 | // |
| 107 | // 'true' label corresponds to jump target of the current basic block i.e. the target to |
| 108 | // branch to on compare condition being true. 'false' label corresponds to the target to |
| 109 | // branch to on condition being false. |
| 110 | static void genJumpKindsForTree(GenTree* cmpTree, emitJumpKind jmpKind[2], bool jmpToTrueLabel[2]); |
| 111 | |
| 112 | static bool genShouldRoundFP(); |
| 113 | |
| 114 | GenTreeIndir indirForm(var_types type, GenTree* base); |
| 115 | |
| 116 | GenTreeIntCon intForm(var_types type, ssize_t value); |
| 117 | |
| 118 | void genRangeCheck(GenTree* node); |
| 119 | |
| 120 | void genLockedInstructions(GenTreeOp* node); |
| 121 | #ifdef _TARGET_XARCH_ |
| 122 | void genCodeForLockAdd(GenTreeOp* node); |
| 123 | #endif |
| 124 | |
| 125 | #ifdef REG_OPT_RSVD |
| 126 | // On some targets such as the ARM we may need to have an extra reserved register |
| 127 | // that is used when addressing stack based locals and stack based temps. |
| 128 | // This method returns the regNumber that should be used when an extra register |
| 129 | // is needed to access the stack based locals and stack based temps. |
| 130 | // |
| 131 | regNumber rsGetRsvdReg() |
| 132 | { |
| 133 | // We should have already added this register to the mask |
| 134 | // of reserved registers in regSet.rdMaskResvd |
| 135 | noway_assert((regSet.rsMaskResvd & RBM_OPT_RSVD) != 0); |
| 136 | |
| 137 | return REG_OPT_RSVD; |
| 138 | } |
| 139 | #endif // REG_OPT_RSVD |
| 140 | |
| 141 | //------------------------------------------------------------------------- |
| 142 | |
| 143 | bool genUseBlockInit; // true if we plan to block-initialize the local stack frame |
| 144 | unsigned genInitStkLclCnt; // The count of local variables that we need to zero init |
| 145 | |
| 146 | // Keeps track of how many bytes we've pushed on the processor's stack. |
| 147 | // |
| 148 | unsigned genStackLevel; |
| 149 | |
| 150 | void SubtractStackLevel(unsigned adjustment) |
| 151 | { |
| 152 | assert(genStackLevel >= adjustment); |
| 153 | unsigned newStackLevel = genStackLevel - adjustment; |
| 154 | if (genStackLevel != newStackLevel) |
| 155 | { |
| 156 | JITDUMP("Adjusting stack level from %d to %d\n" , genStackLevel, newStackLevel); |
| 157 | } |
| 158 | genStackLevel = newStackLevel; |
| 159 | } |
| 160 | |
| 161 | void AddStackLevel(unsigned adjustment) |
| 162 | { |
| 163 | unsigned newStackLevel = genStackLevel + adjustment; |
| 164 | if (genStackLevel != newStackLevel) |
| 165 | { |
| 166 | JITDUMP("Adjusting stack level from %d to %d\n" , genStackLevel, newStackLevel); |
| 167 | } |
| 168 | genStackLevel = newStackLevel; |
| 169 | } |
| 170 | |
| 171 | void SetStackLevel(unsigned newStackLevel) |
| 172 | { |
| 173 | if (genStackLevel != newStackLevel) |
| 174 | { |
| 175 | JITDUMP("Setting stack level from %d to %d\n" , genStackLevel, newStackLevel); |
| 176 | } |
| 177 | genStackLevel = newStackLevel; |
| 178 | } |
| 179 | |
| 180 | #if STACK_PROBES |
| 181 | // Stack Probes |
| 182 | bool genNeedPrologStackProbe; |
| 183 | |
| 184 | void genGenerateStackProbe(); |
| 185 | #endif |
| 186 | |
| 187 | //------------------------------------------------------------------------- |
| 188 | |
| 189 | void genReportEH(); |
| 190 | |
| 191 | // Allocates storage for the GC info, writes the GC info into that storage, records the address of the |
| 192 | // GC info of the method with the EE, and returns a pointer to the "info" portion (just post-header) of |
| 193 | // the GC info. Requires "codeSize" to be the size of the generated code, "prologSize" and "epilogSize" |
| 194 | // to be the sizes of the prolog and epilog, respectively. In DEBUG, makes a check involving the |
| 195 | // "codePtr", assumed to be a pointer to the start of the generated code. |
| 196 | CLANG_FORMAT_COMMENT_ANCHOR; |
| 197 | |
| 198 | #ifdef JIT32_GCENCODER |
| 199 | void* genCreateAndStoreGCInfo(unsigned codeSize, unsigned prologSize, unsigned epilogSize DEBUGARG(void* codePtr)); |
| 200 | void* genCreateAndStoreGCInfoJIT32(unsigned codeSize, |
| 201 | unsigned prologSize, |
| 202 | unsigned epilogSize DEBUGARG(void* codePtr)); |
| 203 | #else // !JIT32_GCENCODER |
| 204 | void genCreateAndStoreGCInfo(unsigned codeSize, unsigned prologSize, unsigned epilogSize DEBUGARG(void* codePtr)); |
| 205 | void genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUGARG(void* codePtr)); |
| 206 | #endif // !JIT32_GCENCODER |
| 207 | |
| 208 | /************************************************************************** |
| 209 | * PROTECTED |
| 210 | *************************************************************************/ |
| 211 | |
| 212 | protected: |
| 213 | // the current (pending) label ref, a label which has been referenced but not yet seen |
| 214 | BasicBlock* genPendingCallLabel; |
| 215 | |
| 216 | #ifdef DEBUG |
| 217 | // Last instr we have displayed for dspInstrs |
| 218 | unsigned genCurDispOffset; |
| 219 | |
| 220 | static const char* genInsName(instruction ins); |
| 221 | #endif // DEBUG |
| 222 | |
| 223 | //------------------------------------------------------------------------- |
| 224 | |
| 225 | // JIT-time constants for use in multi-dimensional array code generation. |
| 226 | unsigned genOffsetOfMDArrayLowerBound(var_types elemType, unsigned rank, unsigned dimension); |
| 227 | unsigned genOffsetOfMDArrayDimensionSize(var_types elemType, unsigned rank, unsigned dimension); |
| 228 | |
| 229 | #ifdef DEBUG |
| 230 | static const char* genSizeStr(emitAttr size); |
| 231 | #endif // DEBUG |
| 232 | |
| 233 | void genCodeForBBlist(); |
| 234 | |
| 235 | public: |
| 236 | void genSpillVar(GenTree* tree); |
| 237 | |
| 238 | protected: |
| 239 | void genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTarget = REG_NA); |
| 240 | |
| 241 | void genGCWriteBarrier(GenTree* tgt, GCInfo::WriteBarrierForm wbf); |
| 242 | |
| 243 | BasicBlock* genCreateTempLabel(); |
| 244 | |
| 245 | void genDefineTempLabel(BasicBlock* label); |
| 246 | |
| 247 | void genAdjustSP(target_ssize_t delta); |
| 248 | |
| 249 | void genAdjustStackLevel(BasicBlock* block); |
| 250 | |
| 251 | void genExitCode(BasicBlock* block); |
| 252 | |
| 253 | void genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKind, GenTree* failBlk = nullptr); |
| 254 | |
| 255 | void genCheckOverflow(GenTree* tree); |
| 256 | |
| 257 | //------------------------------------------------------------------------- |
| 258 | // |
| 259 | // Prolog/epilog generation |
| 260 | // |
| 261 | //------------------------------------------------------------------------- |
| 262 | |
| 263 | // |
| 264 | // Prolog functions and data (there are a few exceptions for more generally used things) |
| 265 | // |
| 266 | |
| 267 | void genEstablishFramePointer(int delta, bool reportUnwindData); |
| 268 | void genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbered, RegState* regState); |
| 269 | void genEnregisterIncomingStackArgs(); |
| 270 | void genCheckUseBlockInit(); |
| 271 | #if defined(UNIX_AMD64_ABI) && defined(FEATURE_SIMD) |
| 272 | void genClearStackVec3ArgUpperBits(); |
| 273 | #endif // UNIX_AMD64_ABI && FEATURE_SIMD |
| 274 | |
| 275 | #if defined(_TARGET_ARM64_) |
| 276 | bool genInstrWithConstant(instruction ins, |
| 277 | emitAttr attr, |
| 278 | regNumber reg1, |
| 279 | regNumber reg2, |
| 280 | ssize_t imm, |
| 281 | regNumber tmpReg, |
| 282 | bool inUnwindRegion = false); |
| 283 | |
| 284 | void genStackPointerAdjustment(ssize_t spAdjustment, regNumber tmpReg, bool* pTmpRegIsZero); |
| 285 | |
| 286 | void genPrologSaveRegPair(regNumber reg1, |
| 287 | regNumber reg2, |
| 288 | int spOffset, |
| 289 | int spDelta, |
| 290 | bool lastSavedWasPreviousPair, |
| 291 | regNumber tmpReg, |
| 292 | bool* pTmpRegIsZero); |
| 293 | |
| 294 | void genPrologSaveReg(regNumber reg1, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero); |
| 295 | |
| 296 | void genEpilogRestoreRegPair( |
| 297 | regNumber reg1, regNumber reg2, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero); |
| 298 | |
| 299 | void genEpilogRestoreReg(regNumber reg1, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero); |
| 300 | |
| 301 | #ifdef DEBUG |
| 302 | static void genCheckSPOffset(bool isRegsCountOdd, int spOffset, int slotSize); |
| 303 | #endif // DEBUG |
| 304 | |
| 305 | // A simple struct to keep register pairs for prolog and epilog. |
| 306 | struct RegPair |
| 307 | { |
| 308 | regNumber reg1; |
| 309 | regNumber reg2; |
| 310 | |
| 311 | RegPair(regNumber reg1) : reg1(reg1), reg2(REG_NA) |
| 312 | { |
| 313 | } |
| 314 | |
| 315 | RegPair(regNumber reg1, regNumber reg2) : reg1(reg1), reg2(reg2) |
| 316 | { |
| 317 | assert(reg2 == REG_NEXT(reg1)); |
| 318 | } |
| 319 | }; |
| 320 | |
| 321 | static void (regMaskTP regsMask, ArrayStack<RegPair>* regStack); |
| 322 | |
| 323 | static int genGetSlotSizeForRegsInMask(regMaskTP regsMask); |
| 324 | |
| 325 | int genSaveCalleeSavedRegisterGroup(regMaskTP regsMask, |
| 326 | int spDelta, |
| 327 | int spOffset DEBUGARG(bool isRegsToSaveCountOdd)); |
| 328 | int genRestoreCalleeSavedRegisterGroup(regMaskTP regsMask, |
| 329 | int spDelta, |
| 330 | int spOffset DEBUGARG(bool isRegsToRestoreCountOdd)); |
| 331 | |
| 332 | void genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowestCalleeSavedOffset, int spDelta); |
| 333 | void genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, int lowestCalleeSavedOffset, int spDelta); |
| 334 | |
| 335 | void genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroed); |
| 336 | #else |
| 337 | void genPushCalleeSavedRegisters(); |
| 338 | #endif |
| 339 | |
| 340 | void genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pInitRegZeroed, regMaskTP maskArgRegsLiveIn); |
| 341 | |
| 342 | #if defined(_TARGET_ARM_) |
| 343 | |
| 344 | void genPushFltRegs(regMaskTP regMask); |
| 345 | void genPopFltRegs(regMaskTP regMask); |
| 346 | regMaskTP genStackAllocRegisterMask(unsigned frameSize, regMaskTP maskCalleeSavedFloat); |
| 347 | |
| 348 | regMaskTP genJmpCallArgMask(); |
| 349 | |
| 350 | void genFreeLclFrame(unsigned frameSize, |
| 351 | /* IN OUT */ bool* pUnwindStarted, |
| 352 | bool jmpEpilog); |
| 353 | |
| 354 | void genMov32RelocatableDisplacement(BasicBlock* block, regNumber reg); |
| 355 | void genMov32RelocatableDataLabel(unsigned value, regNumber reg); |
| 356 | void genMov32RelocatableImmediate(emitAttr size, BYTE* addr, regNumber reg); |
| 357 | |
| 358 | bool genUsedPopToReturn; // True if we use the pop into PC to return, |
| 359 | // False if we didn't and must branch to LR to return. |
| 360 | |
| 361 | // A set of information that is used by funclet prolog and epilog generation. It is collected once, before |
| 362 | // funclet prologs and epilogs are generated, and used by all funclet prologs and epilogs, which must all be the |
| 363 | // same. |
| 364 | struct FuncletFrameInfoDsc |
| 365 | { |
| 366 | regMaskTP fiSaveRegs; // Set of registers saved in the funclet prolog (includes LR) |
| 367 | unsigned fiFunctionCallerSPtoFPdelta; // Delta between caller SP and the frame pointer |
| 368 | unsigned fiSpDelta; // Stack pointer delta |
| 369 | unsigned fiPSP_slot_SP_offset; // PSP slot offset from SP |
| 370 | int fiPSP_slot_CallerSP_offset; // PSP slot offset from Caller SP |
| 371 | }; |
| 372 | |
| 373 | FuncletFrameInfoDsc genFuncletInfo; |
| 374 | |
| 375 | #elif defined(_TARGET_ARM64_) |
| 376 | |
| 377 | // A set of information that is used by funclet prolog and epilog generation. It is collected once, before |
| 378 | // funclet prologs and epilogs are generated, and used by all funclet prologs and epilogs, which must all be the |
| 379 | // same. |
| 380 | struct FuncletFrameInfoDsc |
| 381 | { |
| 382 | regMaskTP fiSaveRegs; // Set of callee-saved registers saved in the funclet prolog (includes LR) |
| 383 | int fiFunction_CallerSP_to_FP_delta; // Delta between caller SP and the frame pointer in the parent function |
| 384 | // (negative) |
| 385 | int fiSP_to_FPLR_save_delta; // FP/LR register save offset from SP (positive) |
| 386 | int fiSP_to_PSP_slot_delta; // PSP slot offset from SP (positive) |
| 387 | int fiSP_to_CalleeSave_delta; // First callee-saved register slot offset from SP (positive) |
| 388 | int fiCallerSP_to_PSP_slot_delta; // PSP slot offset from Caller SP (negative) |
| 389 | int fiFrameType; // Funclet frame types are numbered. See genFuncletProlog() for details. |
| 390 | int fiSpDelta1; // Stack pointer delta 1 (negative) |
| 391 | int fiSpDelta2; // Stack pointer delta 2 (negative) |
| 392 | }; |
| 393 | |
| 394 | FuncletFrameInfoDsc genFuncletInfo; |
| 395 | |
| 396 | #elif defined(_TARGET_AMD64_) |
| 397 | |
| 398 | // A set of information that is used by funclet prolog and epilog generation. It is collected once, before |
| 399 | // funclet prologs and epilogs are generated, and used by all funclet prologs and epilogs, which must all be the |
| 400 | // same. |
| 401 | struct FuncletFrameInfoDsc |
| 402 | { |
| 403 | unsigned fiFunction_InitialSP_to_FP_delta; // Delta between Initial-SP and the frame pointer |
| 404 | unsigned fiSpDelta; // Stack pointer delta |
| 405 | int fiPSP_slot_InitialSP_offset; // PSP slot offset from Initial-SP |
| 406 | }; |
| 407 | |
| 408 | FuncletFrameInfoDsc genFuncletInfo; |
| 409 | |
| 410 | #endif // _TARGET_AMD64_ |
| 411 | |
| 412 | #if defined(_TARGET_XARCH_) |
| 413 | |
| 414 | // Save/Restore callee saved float regs to stack |
| 415 | void genPreserveCalleeSavedFltRegs(unsigned lclFrameSize); |
| 416 | void genRestoreCalleeSavedFltRegs(unsigned lclFrameSize); |
| 417 | // Generate VZeroupper instruction to avoid AVX/SSE transition penalty |
| 418 | void genVzeroupperIfNeeded(bool check256bitOnly = true); |
| 419 | |
| 420 | #endif // _TARGET_XARCH_ |
| 421 | |
| 422 | void genZeroInitFltRegs(const regMaskTP& initFltRegs, const regMaskTP& initDblRegs, const regNumber& initReg); |
| 423 | |
| 424 | regNumber genGetZeroReg(regNumber initReg, bool* pInitRegZeroed); |
| 425 | |
| 426 | void genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, bool* pInitRegZeroed); |
| 427 | |
| 428 | void genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed); |
| 429 | |
| 430 | void genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed); |
| 431 | |
| 432 | void genFinalizeFrame(); |
| 433 | |
| 434 | #ifdef PROFILING_SUPPORTED |
| 435 | void genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed); |
| 436 | void genProfilingLeaveCallback(unsigned helper = CORINFO_HELP_PROF_FCN_LEAVE); |
| 437 | #endif // PROFILING_SUPPORTED |
| 438 | |
| 439 | void genPrologPadForReJit(); |
| 440 | |
| 441 | // clang-format off |
| 442 | void genEmitCall(int callType, |
| 443 | CORINFO_METHOD_HANDLE methHnd, |
| 444 | INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) |
| 445 | void* addr |
| 446 | X86_ARG(int argSize), |
| 447 | emitAttr retSize |
| 448 | MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), |
| 449 | IL_OFFSETX ilOffset, |
| 450 | regNumber base = REG_NA, |
| 451 | bool isJump = false); |
| 452 | // clang-format on |
| 453 | |
| 454 | // clang-format off |
| 455 | void genEmitCall(int callType, |
| 456 | CORINFO_METHOD_HANDLE methHnd, |
| 457 | INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) |
| 458 | GenTreeIndir* indir |
| 459 | X86_ARG(int argSize), |
| 460 | emitAttr retSize |
| 461 | MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), |
| 462 | IL_OFFSETX ilOffset); |
| 463 | // clang-format on |
| 464 | |
| 465 | // |
| 466 | // Epilog functions |
| 467 | // |
| 468 | CLANG_FORMAT_COMMENT_ANCHOR; |
| 469 | |
| 470 | #if defined(_TARGET_ARM_) |
| 471 | bool genCanUsePopToReturn(regMaskTP maskPopRegsInt, bool jmpEpilog); |
| 472 | #endif |
| 473 | |
| 474 | #if defined(_TARGET_ARM64_) |
| 475 | |
| 476 | void genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog); |
| 477 | |
| 478 | #else // !defined(_TARGET_ARM64_) |
| 479 | |
| 480 | void genPopCalleeSavedRegisters(bool jmpEpilog = false); |
| 481 | |
| 482 | #endif // !defined(_TARGET_ARM64_) |
| 483 | |
| 484 | // |
| 485 | // Common or driving functions |
| 486 | // |
| 487 | |
| 488 | void genReserveProlog(BasicBlock* block); // currently unused |
| 489 | void genReserveEpilog(BasicBlock* block); |
| 490 | void genFnProlog(); |
| 491 | void genFnEpilog(BasicBlock* block); |
| 492 | |
| 493 | #if FEATURE_EH_FUNCLETS |
| 494 | |
| 495 | void genReserveFuncletProlog(BasicBlock* block); |
| 496 | void genReserveFuncletEpilog(BasicBlock* block); |
| 497 | void genFuncletProlog(BasicBlock* block); |
| 498 | void genFuncletEpilog(); |
| 499 | void genCaptureFuncletPrologEpilogInfo(); |
| 500 | |
| 501 | void genSetPSPSym(regNumber initReg, bool* pInitRegZeroed); |
| 502 | |
| 503 | void genUpdateCurrentFunclet(BasicBlock* block); |
| 504 | #if defined(_TARGET_ARM_) |
| 505 | void genInsertNopForUnwinder(BasicBlock* block); |
| 506 | #endif |
| 507 | |
| 508 | #else // FEATURE_EH_FUNCLETS |
| 509 | |
| 510 | // This is a no-op when there are no funclets! |
| 511 | void genUpdateCurrentFunclet(BasicBlock* block) |
| 512 | { |
| 513 | return; |
| 514 | } |
| 515 | |
| 516 | #if defined(_TARGET_ARM_) |
| 517 | void genInsertNopForUnwinder(BasicBlock* block) |
| 518 | { |
| 519 | return; |
| 520 | } |
| 521 | #endif |
| 522 | |
| 523 | #endif // FEATURE_EH_FUNCLETS |
| 524 | |
| 525 | void genGeneratePrologsAndEpilogs(); |
| 526 | |
| 527 | #if defined(DEBUG) && defined(_TARGET_ARM64_) |
| 528 | void genArm64EmitterUnitTests(); |
| 529 | #endif |
| 530 | |
| 531 | #if defined(DEBUG) && defined(LATE_DISASM) && defined(_TARGET_AMD64_) |
| 532 | void genAmd64EmitterUnitTests(); |
| 533 | #endif |
| 534 | |
| 535 | //------------------------------------------------------------------------- |
| 536 | // |
| 537 | // End prolog/epilog generation |
| 538 | // |
| 539 | //------------------------------------------------------------------------- |
| 540 | |
| 541 | void genSinglePush(); |
| 542 | void genSinglePop(); |
| 543 | regMaskTP genPushRegs(regMaskTP regs, regMaskTP* byrefRegs, regMaskTP* noRefRegs); |
| 544 | void genPopRegs(regMaskTP regs, regMaskTP byrefRegs, regMaskTP noRefRegs); |
| 545 | |
| 546 | /* |
| 547 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 548 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 549 | XX XX |
| 550 | XX Debugging Support XX |
| 551 | XX XX |
| 552 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 553 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 554 | */ |
| 555 | |
| 556 | #ifdef DEBUG |
| 557 | void genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMapping); |
| 558 | void genIPmappingListDisp(); |
| 559 | #endif // DEBUG |
| 560 | |
| 561 | void genIPmappingAdd(IL_OFFSETX offset, bool isLabel); |
| 562 | void genIPmappingAddToFront(IL_OFFSETX offset); |
| 563 | void genIPmappingGen(); |
| 564 | |
| 565 | void genEnsureCodeEmitted(IL_OFFSETX offsx); |
| 566 | |
| 567 | //------------------------------------------------------------------------- |
| 568 | // scope info for the variables |
| 569 | |
| 570 | void genSetScopeInfo(unsigned which, |
| 571 | UNATIVE_OFFSET startOffs, |
| 572 | UNATIVE_OFFSET length, |
| 573 | unsigned varNum, |
| 574 | unsigned LVnum, |
| 575 | bool avail, |
| 576 | Compiler::siVarLoc& loc); |
| 577 | |
| 578 | void genSetScopeInfo(); |
| 579 | |
| 580 | protected: |
| 581 | /* |
| 582 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 583 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 584 | XX XX |
| 585 | XX ScopeInfo XX |
| 586 | XX XX |
| 587 | XX Keeps track of the scopes during code-generation. XX |
| 588 | XX This is used to translate the local-variable debugging information XX |
| 589 | XX from IL offsets to native code offsets. XX |
| 590 | XX XX |
| 591 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 592 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 593 | */ |
| 594 | |
| 595 | /*****************************************************************************/ |
| 596 | /***************************************************************************** |
| 597 | * ScopeInfo |
| 598 | * |
| 599 | * This class is called during code gen at block-boundaries, and when the |
| 600 | * set of live variables changes. It keeps track of the scope of the variables |
| 601 | * in terms of the native code PC. |
| 602 | */ |
| 603 | |
| 604 | public: |
| 605 | void siInit(); |
| 606 | |
| 607 | void siBeginBlock(BasicBlock* block); |
| 608 | |
| 609 | void siEndBlock(BasicBlock* block); |
| 610 | |
| 611 | virtual void siUpdate(); |
| 612 | |
| 613 | void siCheckVarScope(unsigned varNum, IL_OFFSET offs); |
| 614 | |
| 615 | void siCloseAllOpenScopes(); |
| 616 | |
| 617 | #ifdef DEBUG |
| 618 | void siDispOpenScopes(); |
| 619 | #endif |
| 620 | |
| 621 | /************************************************************************** |
| 622 | * PROTECTED |
| 623 | *************************************************************************/ |
| 624 | |
| 625 | protected: |
| 626 | struct siScope |
| 627 | { |
| 628 | emitLocation scStartLoc; // emitter location of start of scope |
| 629 | emitLocation scEndLoc; // emitter location of end of scope |
| 630 | |
| 631 | unsigned scVarNum; // index into lvaTable |
| 632 | unsigned scLVnum; // 'which' in eeGetLVinfo() |
| 633 | |
| 634 | unsigned scStackLevel; // Only for stk-vars |
| 635 | bool scAvailable : 1; // It has a home / Home recycled - TODO-Cleanup: it appears this is unused (always true) |
| 636 | |
| 637 | siScope* scPrev; |
| 638 | siScope* scNext; |
| 639 | }; |
| 640 | |
| 641 | siScope siOpenScopeList, siScopeList, *siOpenScopeLast, *siScopeLast; |
| 642 | |
| 643 | unsigned siScopeCnt; |
| 644 | |
| 645 | VARSET_TP siLastLife; // Life at last call to siUpdate() |
| 646 | |
| 647 | // Tracks the last entry for each tracked register variable |
| 648 | |
| 649 | siScope** siLatestTrackedScopes; |
| 650 | |
| 651 | IL_OFFSET siLastEndOffs; // IL offset of the (exclusive) end of the last block processed |
| 652 | |
| 653 | #if FEATURE_EH_FUNCLETS |
| 654 | bool siInFuncletRegion; // Have we seen the start of the funclet region? |
| 655 | #endif // FEATURE_EH_FUNCLETS |
| 656 | |
| 657 | // Functions |
| 658 | |
| 659 | siScope* siNewScope(unsigned LVnum, unsigned varNum); |
| 660 | |
| 661 | void siRemoveFromOpenScopeList(siScope* scope); |
| 662 | |
| 663 | void siEndTrackedScope(unsigned varIndex); |
| 664 | |
| 665 | void siEndScope(unsigned varNum); |
| 666 | |
| 667 | void siEndScope(siScope* scope); |
| 668 | |
| 669 | #ifdef DEBUG |
| 670 | bool siVerifyLocalVarTab(); |
| 671 | #endif |
| 672 | |
| 673 | #ifdef LATE_DISASM |
| 674 | public: |
| 675 | /* virtual */ |
| 676 | const char* siRegVarName(size_t offs, size_t size, unsigned reg); |
| 677 | |
| 678 | /* virtual */ |
| 679 | const char* siStackVarName(size_t offs, size_t size, unsigned reg, unsigned stkOffs); |
| 680 | #endif // LATE_DISASM |
| 681 | |
| 682 | public: |
| 683 | /* |
| 684 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 685 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 686 | XX XX |
| 687 | XX PrologScopeInfo XX |
| 688 | XX XX |
| 689 | XX We need special handling in the prolog block, as the parameter variables XX |
| 690 | XX may not be in the same position described by genLclVarTable - they all XX |
| 691 | XX start out on the stack XX |
| 692 | XX XX |
| 693 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 694 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 695 | */ |
| 696 | |
| 697 | public: |
| 698 | void psiBegProlog(); |
| 699 | |
| 700 | void psiAdjustStackLevel(unsigned size); |
| 701 | |
| 702 | void psiMoveESPtoEBP(); |
| 703 | |
| 704 | void psiMoveToReg(unsigned varNum, regNumber reg = REG_NA, regNumber otherReg = REG_NA); |
| 705 | |
| 706 | void psiMoveToStack(unsigned varNum); |
| 707 | |
| 708 | void psiEndProlog(); |
| 709 | |
| 710 | /************************************************************************** |
| 711 | * PROTECTED |
| 712 | *************************************************************************/ |
| 713 | |
| 714 | protected: |
| 715 | struct psiScope |
| 716 | { |
| 717 | emitLocation scStartLoc; // emitter location of start of scope |
| 718 | emitLocation scEndLoc; // emitter location of end of scope |
| 719 | |
| 720 | unsigned scSlotNum; // index into lclVarTab |
| 721 | unsigned scLVnum; // 'which' in eeGetLVinfo() |
| 722 | |
| 723 | bool scRegister; |
| 724 | |
| 725 | union { |
| 726 | struct |
| 727 | { |
| 728 | regNumberSmall scRegNum; |
| 729 | |
| 730 | // Used for: |
| 731 | // - "other half" of long var on architectures with 32 bit size registers - x86. |
| 732 | // - for System V structs it stores the second register |
| 733 | // used to pass a register passed struct. |
| 734 | regNumberSmall scOtherReg; |
| 735 | } u1; |
| 736 | |
| 737 | struct |
| 738 | { |
| 739 | regNumberSmall scBaseReg; |
| 740 | NATIVE_OFFSET scOffset; |
| 741 | } u2; |
| 742 | }; |
| 743 | |
| 744 | psiScope* scPrev; |
| 745 | psiScope* scNext; |
| 746 | }; |
| 747 | |
| 748 | psiScope psiOpenScopeList, psiScopeList, *psiOpenScopeLast, *psiScopeLast; |
| 749 | |
| 750 | unsigned psiScopeCnt; |
| 751 | |
| 752 | // Implementation Functions |
| 753 | |
| 754 | psiScope* psiNewPrologScope(unsigned LVnum, unsigned slotNum); |
| 755 | |
| 756 | void psiEndPrologScope(psiScope* scope); |
| 757 | |
| 758 | void psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc1); |
| 759 | |
| 760 | /***************************************************************************** |
| 761 | * TrnslLocalVarInfo |
| 762 | * |
| 763 | * This struct holds the LocalVarInfo in terms of the generated native code |
| 764 | * after a call to genSetScopeInfo() |
| 765 | */ |
| 766 | |
| 767 | #ifdef DEBUG |
| 768 | |
| 769 | struct TrnslLocalVarInfo |
| 770 | { |
| 771 | unsigned tlviVarNum; |
| 772 | unsigned tlviLVnum; |
| 773 | VarName tlviName; |
| 774 | UNATIVE_OFFSET tlviStartPC; |
| 775 | size_t tlviLength; |
| 776 | bool tlviAvailable; |
| 777 | Compiler::siVarLoc tlviVarLoc; |
| 778 | }; |
| 779 | |
| 780 | // Array of scopes of LocalVars in terms of native code |
| 781 | |
| 782 | TrnslLocalVarInfo* genTrnslLocalVarInfo; |
| 783 | unsigned genTrnslLocalVarCount; |
| 784 | #endif |
| 785 | |
| 786 | void genSetRegToConst(regNumber targetReg, var_types targetType, GenTree* tree); |
| 787 | void genCodeForTreeNode(GenTree* treeNode); |
| 788 | void genCodeForBinary(GenTreeOp* treeNode); |
| 789 | |
| 790 | #if defined(_TARGET_X86_) |
| 791 | void genCodeForLongUMod(GenTreeOp* node); |
| 792 | #endif // _TARGET_X86_ |
| 793 | |
| 794 | void genCodeForDivMod(GenTreeOp* treeNode); |
| 795 | void genCodeForMul(GenTreeOp* treeNode); |
| 796 | void genCodeForMulHi(GenTreeOp* treeNode); |
| 797 | void genLeaInstruction(GenTreeAddrMode* lea); |
| 798 | void genSetRegToCond(regNumber dstReg, GenTree* tree); |
| 799 | |
| 800 | #if defined(_TARGET_ARMARCH_) |
| 801 | void genScaledAdd(emitAttr attr, regNumber targetReg, regNumber baseReg, regNumber indexReg, int scale); |
| 802 | #endif // _TARGET_ARMARCH_ |
| 803 | |
| 804 | #if defined(_TARGET_ARM_) |
| 805 | void genCodeForMulLong(GenTreeMultiRegOp* treeNode); |
| 806 | #endif // _TARGET_ARM_ |
| 807 | |
| 808 | #if !defined(_TARGET_64BIT_) |
| 809 | void genLongToIntCast(GenTree* treeNode); |
| 810 | #endif |
| 811 | |
| 812 | struct GenIntCastDesc |
| 813 | { |
| 814 | enum CheckKind |
| 815 | { |
| 816 | CHECK_NONE, |
| 817 | CHECK_SMALL_INT_RANGE, |
| 818 | CHECK_POSITIVE, |
| 819 | #ifdef _TARGET_64BIT_ |
| 820 | CHECK_UINT_RANGE, |
| 821 | CHECK_POSITIVE_INT_RANGE, |
| 822 | CHECK_INT_RANGE, |
| 823 | #endif |
| 824 | }; |
| 825 | |
| 826 | enum ExtendKind |
| 827 | { |
| 828 | COPY, |
| 829 | ZERO_EXTEND_SMALL_INT, |
| 830 | SIGN_EXTEND_SMALL_INT, |
| 831 | #ifdef _TARGET_64BIT_ |
| 832 | ZERO_EXTEND_INT, |
| 833 | SIGN_EXTEND_INT, |
| 834 | #endif |
| 835 | }; |
| 836 | |
| 837 | private: |
| 838 | CheckKind m_checkKind; |
| 839 | unsigned m_checkSrcSize; |
| 840 | int m_checkSmallIntMin; |
| 841 | int m_checkSmallIntMax; |
| 842 | ExtendKind m_extendKind; |
| 843 | unsigned m_extendSrcSize; |
| 844 | |
| 845 | public: |
| 846 | GenIntCastDesc(GenTreeCast* cast); |
| 847 | |
| 848 | CheckKind CheckKind() const |
| 849 | { |
| 850 | return m_checkKind; |
| 851 | } |
| 852 | |
| 853 | unsigned CheckSrcSize() const |
| 854 | { |
| 855 | assert(m_checkKind != CHECK_NONE); |
| 856 | return m_checkSrcSize; |
| 857 | } |
| 858 | |
| 859 | int CheckSmallIntMin() const |
| 860 | { |
| 861 | assert(m_checkKind == CHECK_SMALL_INT_RANGE); |
| 862 | return m_checkSmallIntMin; |
| 863 | } |
| 864 | |
| 865 | int CheckSmallIntMax() const |
| 866 | { |
| 867 | assert(m_checkKind == CHECK_SMALL_INT_RANGE); |
| 868 | return m_checkSmallIntMax; |
| 869 | } |
| 870 | |
| 871 | ExtendKind ExtendKind() const |
| 872 | { |
| 873 | return m_extendKind; |
| 874 | } |
| 875 | |
| 876 | unsigned ExtendSrcSize() const |
| 877 | { |
| 878 | return m_extendSrcSize; |
| 879 | } |
| 880 | }; |
| 881 | |
| 882 | void genIntCastOverflowCheck(GenTreeCast* cast, const GenIntCastDesc& desc, regNumber reg); |
| 883 | void genIntToIntCast(GenTreeCast* cast); |
| 884 | void genFloatToFloatCast(GenTree* treeNode); |
| 885 | void genFloatToIntCast(GenTree* treeNode); |
| 886 | void genIntToFloatCast(GenTree* treeNode); |
| 887 | void genCkfinite(GenTree* treeNode); |
| 888 | void genCodeForCompare(GenTreeOp* tree); |
| 889 | void genIntrinsic(GenTree* treeNode); |
| 890 | void genPutArgStk(GenTreePutArgStk* treeNode); |
| 891 | void genPutArgReg(GenTreeOp* tree); |
| 892 | #if FEATURE_ARG_SPLIT |
| 893 | void genPutArgSplit(GenTreePutArgSplit* treeNode); |
| 894 | #endif // FEATURE_ARG_SPLIT |
| 895 | |
| 896 | #if defined(_TARGET_XARCH_) |
| 897 | unsigned getBaseVarForPutArgStk(GenTree* treeNode); |
| 898 | #endif // _TARGET_XARCH_ |
| 899 | |
| 900 | unsigned getFirstArgWithStackSlot(); |
| 901 | |
| 902 | void genCompareFloat(GenTree* treeNode); |
| 903 | void genCompareInt(GenTree* treeNode); |
| 904 | |
| 905 | #ifdef FEATURE_SIMD |
| 906 | enum SIMDScalarMoveType{ |
| 907 | SMT_ZeroInitUpper, // zero initlaize target upper bits |
| 908 | SMT_ZeroInitUpper_SrcHasUpperZeros, // zero initialize target upper bits; source upper bits are known to be zero |
| 909 | SMT_PreserveUpper // preserve target upper bits |
| 910 | }; |
| 911 | |
| 912 | #ifdef _TARGET_ARM64_ |
| 913 | insOpts genGetSimdInsOpt(emitAttr size, var_types elementType); |
| 914 | #endif |
| 915 | instruction getOpForSIMDIntrinsic(SIMDIntrinsicID intrinsicId, var_types baseType, unsigned* ival = nullptr); |
| 916 | void genSIMDScalarMove( |
| 917 | var_types targetType, var_types type, regNumber target, regNumber src, SIMDScalarMoveType moveType); |
| 918 | void genSIMDZero(var_types targetType, var_types baseType, regNumber targetReg); |
| 919 | void genSIMDIntrinsicInit(GenTreeSIMD* simdNode); |
| 920 | void genSIMDIntrinsicInitN(GenTreeSIMD* simdNode); |
| 921 | void genSIMDIntrinsicUnOp(GenTreeSIMD* simdNode); |
| 922 | void genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode); |
| 923 | void genSIMDIntrinsicRelOp(GenTreeSIMD* simdNode); |
| 924 | void genSIMDIntrinsicDotProduct(GenTreeSIMD* simdNode); |
| 925 | void genSIMDIntrinsicSetItem(GenTreeSIMD* simdNode); |
| 926 | void genSIMDIntrinsicGetItem(GenTreeSIMD* simdNode); |
| 927 | void genSIMDIntrinsicShuffleSSE2(GenTreeSIMD* simdNode); |
| 928 | void genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode); |
| 929 | void genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode); |
| 930 | void genSIMDLo64BitConvert(SIMDIntrinsicID intrinsicID, |
| 931 | var_types simdType, |
| 932 | var_types baseType, |
| 933 | regNumber tmpReg, |
| 934 | regNumber tmpIntReg, |
| 935 | regNumber targetReg); |
| 936 | void genSIMDIntrinsic32BitConvert(GenTreeSIMD* simdNode); |
| 937 | void genSIMDIntrinsic64BitConvert(GenTreeSIMD* simdNode); |
| 938 | void genSIMDIntrinsicNarrow(GenTreeSIMD* simdNode); |
| 939 | void (GenTreeSIMD* simdNode, regNumber srcReg, regNumber tgtReg); |
| 940 | void genSIMDIntrinsicWiden(GenTreeSIMD* simdNode); |
| 941 | void genSIMDIntrinsic(GenTreeSIMD* simdNode); |
| 942 | |
| 943 | // TYP_SIMD12 (i.e Vector3 of size 12 bytes) is not a hardware supported size and requires |
| 944 | // two reads/writes on 64-bit targets. These routines abstract reading/writing of Vector3 |
| 945 | // values through an indirection. Note that Vector3 locals allocated on stack would have |
| 946 | // their size rounded to TARGET_POINTER_SIZE (which is 8 bytes on 64-bit targets) and hence |
| 947 | // Vector3 locals could be treated as TYP_SIMD16 while reading/writing. |
| 948 | void genStoreIndTypeSIMD12(GenTree* treeNode); |
| 949 | void genLoadIndTypeSIMD12(GenTree* treeNode); |
| 950 | void genStoreLclTypeSIMD12(GenTree* treeNode); |
| 951 | void genLoadLclTypeSIMD12(GenTree* treeNode); |
| 952 | #ifdef _TARGET_X86_ |
| 953 | void genStoreSIMD12ToStack(regNumber operandReg, regNumber tmpReg); |
| 954 | void genPutArgStkSIMD12(GenTree* treeNode); |
| 955 | #endif // _TARGET_X86_ |
| 956 | #endif // FEATURE_SIMD |
| 957 | |
| 958 | #ifdef FEATURE_HW_INTRINSICS |
| 959 | void genHWIntrinsic(GenTreeHWIntrinsic* node); |
| 960 | #if defined(_TARGET_XARCH_) |
| 961 | void genHWIntrinsic_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr); |
| 962 | void genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, int8_t ival); |
| 963 | void genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr); |
| 964 | void genHWIntrinsic_R_R_RM( |
| 965 | GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, GenTree* op2); |
| 966 | void genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, int8_t ival); |
| 967 | void genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins); |
| 968 | void genHWIntrinsic_R_R_R_RM( |
| 969 | instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, GenTree* op3); |
| 970 | void genBaseIntrinsic(GenTreeHWIntrinsic* node); |
| 971 | void genSSEIntrinsic(GenTreeHWIntrinsic* node); |
| 972 | void genSSE2Intrinsic(GenTreeHWIntrinsic* node); |
| 973 | void genSSE41Intrinsic(GenTreeHWIntrinsic* node); |
| 974 | void genSSE42Intrinsic(GenTreeHWIntrinsic* node); |
| 975 | void genAvxOrAvx2Intrinsic(GenTreeHWIntrinsic* node); |
| 976 | void genAESIntrinsic(GenTreeHWIntrinsic* node); |
| 977 | void genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node); |
| 978 | void genFMAIntrinsic(GenTreeHWIntrinsic* node); |
| 979 | void genLZCNTIntrinsic(GenTreeHWIntrinsic* node); |
| 980 | void genPCLMULQDQIntrinsic(GenTreeHWIntrinsic* node); |
| 981 | void genPOPCNTIntrinsic(GenTreeHWIntrinsic* node); |
| 982 | void genXCNTIntrinsic(GenTreeHWIntrinsic* node, instruction ins); |
| 983 | template <typename HWIntrinsicSwitchCaseBody> |
| 984 | void genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsic, |
| 985 | regNumber nonConstImmReg, |
| 986 | regNumber baseReg, |
| 987 | regNumber offsReg, |
| 988 | HWIntrinsicSwitchCaseBody emitSwCase); |
| 989 | #endif // defined(_TARGET_XARCH_) |
| 990 | #if defined(_TARGET_ARM64_) |
| 991 | instruction getOpForHWIntrinsic(GenTreeHWIntrinsic* node, var_types instrType); |
| 992 | void genHWIntrinsicUnaryOp(GenTreeHWIntrinsic* node); |
| 993 | void genHWIntrinsicCrcOp(GenTreeHWIntrinsic* node); |
| 994 | void genHWIntrinsicSimdBinaryOp(GenTreeHWIntrinsic* node); |
| 995 | void (GenTreeHWIntrinsic* node); |
| 996 | void genHWIntrinsicSimdInsertOp(GenTreeHWIntrinsic* node); |
| 997 | void genHWIntrinsicSimdSelectOp(GenTreeHWIntrinsic* node); |
| 998 | void genHWIntrinsicSimdSetAllOp(GenTreeHWIntrinsic* node); |
| 999 | void genHWIntrinsicSimdUnaryOp(GenTreeHWIntrinsic* node); |
| 1000 | void genHWIntrinsicSimdBinaryRMWOp(GenTreeHWIntrinsic* node); |
| 1001 | void genHWIntrinsicSimdTernaryRMWOp(GenTreeHWIntrinsic* node); |
| 1002 | void genHWIntrinsicShaHashOp(GenTreeHWIntrinsic* node); |
| 1003 | void genHWIntrinsicShaRotateOp(GenTreeHWIntrinsic* node); |
| 1004 | template <typename HWIntrinsicSwitchCaseBody> |
| 1005 | void genHWIntrinsicSwitchTable(regNumber swReg, regNumber tmpReg, int swMax, HWIntrinsicSwitchCaseBody emitSwCase); |
| 1006 | #endif // defined(_TARGET_XARCH_) |
| 1007 | #endif // FEATURE_HW_INTRINSICS |
| 1008 | |
| 1009 | #if !defined(_TARGET_64BIT_) |
| 1010 | |
| 1011 | // CodeGen for Long Ints |
| 1012 | |
| 1013 | void genStoreLongLclVar(GenTree* treeNode); |
| 1014 | |
| 1015 | #endif // !defined(_TARGET_64BIT_) |
| 1016 | |
| 1017 | void genProduceReg(GenTree* tree); |
| 1018 | void genUnspillRegIfNeeded(GenTree* tree); |
| 1019 | regNumber genConsumeReg(GenTree* tree); |
| 1020 | void genCopyRegIfNeeded(GenTree* tree, regNumber needReg); |
| 1021 | void genConsumeRegAndCopy(GenTree* tree, regNumber needReg); |
| 1022 | |
| 1023 | void genConsumeIfReg(GenTree* tree) |
| 1024 | { |
| 1025 | if (!tree->isContained()) |
| 1026 | { |
| 1027 | (void)genConsumeReg(tree); |
| 1028 | } |
| 1029 | } |
| 1030 | |
| 1031 | void genRegCopy(GenTree* tree); |
| 1032 | void genTransferRegGCState(regNumber dst, regNumber src); |
| 1033 | void genConsumeAddress(GenTree* addr); |
| 1034 | void genConsumeAddrMode(GenTreeAddrMode* mode); |
| 1035 | void genSetBlockSize(GenTreeBlk* blkNode, regNumber sizeReg); |
| 1036 | void genConsumeBlockSrc(GenTreeBlk* blkNode); |
| 1037 | void genSetBlockSrc(GenTreeBlk* blkNode, regNumber srcReg); |
| 1038 | void genConsumeBlockOp(GenTreeBlk* blkNode, regNumber dstReg, regNumber srcReg, regNumber sizeReg); |
| 1039 | |
| 1040 | #ifdef FEATURE_PUT_STRUCT_ARG_STK |
| 1041 | void genConsumePutStructArgStk(GenTreePutArgStk* putArgStkNode, |
| 1042 | regNumber dstReg, |
| 1043 | regNumber srcReg, |
| 1044 | regNumber sizeReg); |
| 1045 | #endif // FEATURE_PUT_STRUCT_ARG_STK |
| 1046 | #if FEATURE_ARG_SPLIT |
| 1047 | void genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode); |
| 1048 | #endif // FEATURE_ARG_SPLIT |
| 1049 | |
| 1050 | void genConsumeRegs(GenTree* tree); |
| 1051 | void genConsumeOperands(GenTreeOp* tree); |
| 1052 | void genEmitGSCookieCheck(bool pushReg); |
| 1053 | void genSetRegToIcon(regNumber reg, ssize_t val, var_types type = TYP_INT, insFlags flags = INS_FLAGS_DONT_CARE); |
| 1054 | void genCodeForShift(GenTree* tree); |
| 1055 | |
| 1056 | #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) |
| 1057 | void genCodeForShiftLong(GenTree* tree); |
| 1058 | #endif |
| 1059 | |
| 1060 | #ifdef _TARGET_XARCH_ |
| 1061 | void genCodeForShiftRMW(GenTreeStoreInd* storeInd); |
| 1062 | void genCodeForBT(GenTreeOp* bt); |
| 1063 | #endif // _TARGET_XARCH_ |
| 1064 | |
| 1065 | void genCodeForCast(GenTreeOp* tree); |
| 1066 | void genCodeForLclAddr(GenTree* tree); |
| 1067 | void genCodeForIndexAddr(GenTreeIndexAddr* tree); |
| 1068 | void genCodeForIndir(GenTreeIndir* tree); |
| 1069 | void genCodeForNegNot(GenTree* tree); |
| 1070 | void genCodeForBswap(GenTree* tree); |
| 1071 | void genCodeForLclVar(GenTreeLclVar* tree); |
| 1072 | void genCodeForLclFld(GenTreeLclFld* tree); |
| 1073 | void genCodeForStoreLclFld(GenTreeLclFld* tree); |
| 1074 | void genCodeForStoreLclVar(GenTreeLclVar* tree); |
| 1075 | void genCodeForReturnTrap(GenTreeOp* tree); |
| 1076 | void genCodeForJcc(GenTreeCC* tree); |
| 1077 | void genCodeForSetcc(GenTreeCC* setcc); |
| 1078 | void genCodeForStoreInd(GenTreeStoreInd* tree); |
| 1079 | void genCodeForSwap(GenTreeOp* tree); |
| 1080 | void genCodeForCpObj(GenTreeObj* cpObjNode); |
| 1081 | void genCodeForCpBlk(GenTreeBlk* cpBlkNode); |
| 1082 | void genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode); |
| 1083 | void genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode); |
| 1084 | void genCodeForPhysReg(GenTreePhysReg* tree); |
| 1085 | void genCodeForNullCheck(GenTreeOp* tree); |
| 1086 | void genCodeForCmpXchg(GenTreeCmpXchg* tree); |
| 1087 | |
| 1088 | void genAlignStackBeforeCall(GenTreePutArgStk* putArgStk); |
| 1089 | void genAlignStackBeforeCall(GenTreeCall* call); |
| 1090 | void genRemoveAlignmentAfterCall(GenTreeCall* call, unsigned bias = 0); |
| 1091 | |
| 1092 | #if defined(UNIX_X86_ABI) |
| 1093 | |
| 1094 | unsigned curNestedAlignment; // Keep track of alignment adjustment required during codegen. |
| 1095 | unsigned maxNestedAlignment; // The maximum amount of alignment adjustment required. |
| 1096 | |
| 1097 | void SubtractNestedAlignment(unsigned adjustment) |
| 1098 | { |
| 1099 | assert(curNestedAlignment >= adjustment); |
| 1100 | unsigned newNestedAlignment = curNestedAlignment - adjustment; |
| 1101 | if (curNestedAlignment != newNestedAlignment) |
| 1102 | { |
| 1103 | JITDUMP("Adjusting stack nested alignment from %d to %d\n" , curNestedAlignment, newNestedAlignment); |
| 1104 | } |
| 1105 | curNestedAlignment = newNestedAlignment; |
| 1106 | } |
| 1107 | |
| 1108 | void AddNestedAlignment(unsigned adjustment) |
| 1109 | { |
| 1110 | unsigned newNestedAlignment = curNestedAlignment + adjustment; |
| 1111 | if (curNestedAlignment != newNestedAlignment) |
| 1112 | { |
| 1113 | JITDUMP("Adjusting stack nested alignment from %d to %d\n" , curNestedAlignment, newNestedAlignment); |
| 1114 | } |
| 1115 | curNestedAlignment = newNestedAlignment; |
| 1116 | |
| 1117 | if (curNestedAlignment > maxNestedAlignment) |
| 1118 | { |
| 1119 | JITDUMP("Max stack nested alignment changed from %d to %d\n" , maxNestedAlignment, curNestedAlignment); |
| 1120 | maxNestedAlignment = curNestedAlignment; |
| 1121 | } |
| 1122 | } |
| 1123 | |
| 1124 | #endif |
| 1125 | |
| 1126 | #ifndef _TARGET_X86_ |
| 1127 | void genPutArgStkFieldList(GenTreePutArgStk* putArgStk, unsigned outArgVarNum); |
| 1128 | #endif // !_TARGET_X86_ |
| 1129 | |
| 1130 | #ifdef FEATURE_PUT_STRUCT_ARG_STK |
| 1131 | #ifdef _TARGET_X86_ |
| 1132 | bool genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk); |
| 1133 | void genPushReg(var_types type, regNumber srcReg); |
| 1134 | void genPutArgStkFieldList(GenTreePutArgStk* putArgStk); |
| 1135 | #endif // _TARGET_X86_ |
| 1136 | |
| 1137 | void genPutStructArgStk(GenTreePutArgStk* treeNode); |
| 1138 | |
| 1139 | unsigned genMove8IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); |
| 1140 | unsigned genMove4IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); |
| 1141 | unsigned genMove2IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); |
| 1142 | unsigned genMove1IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); |
| 1143 | void genStructPutArgRepMovs(GenTreePutArgStk* putArgStkNode); |
| 1144 | void genStructPutArgUnroll(GenTreePutArgStk* putArgStkNode); |
| 1145 | void genStoreRegToStackArg(var_types type, regNumber reg, int offset); |
| 1146 | #endif // FEATURE_PUT_STRUCT_ARG_STK |
| 1147 | |
| 1148 | void genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset); |
| 1149 | void genCodeForStoreOffset(instruction ins, emitAttr size, regNumber src, GenTree* base, unsigned offset); |
| 1150 | |
| 1151 | #ifdef _TARGET_ARM64_ |
| 1152 | void genCodeForLoadPairOffset(regNumber dst, regNumber dst2, GenTree* base, unsigned offset); |
| 1153 | void genCodeForStorePairOffset(regNumber src, regNumber src2, GenTree* base, unsigned offset); |
| 1154 | #endif // _TARGET_ARM64_ |
| 1155 | |
| 1156 | void genCodeForStoreBlk(GenTreeBlk* storeBlkNode); |
| 1157 | void genCodeForInitBlk(GenTreeBlk* initBlkNode); |
| 1158 | void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode); |
| 1159 | void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode); |
| 1160 | void genJumpTable(GenTree* tree); |
| 1161 | void genTableBasedSwitch(GenTree* tree); |
| 1162 | void genCodeForArrIndex(GenTreeArrIndex* treeNode); |
| 1163 | void genCodeForArrOffset(GenTreeArrOffs* treeNode); |
| 1164 | instruction genGetInsForOper(genTreeOps oper, var_types type); |
| 1165 | bool genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarrierForm, GenTree* addr, GenTree* data); |
| 1166 | void genCallInstruction(GenTreeCall* call); |
| 1167 | void genJmpMethod(GenTree* jmp); |
| 1168 | BasicBlock* genCallFinally(BasicBlock* block); |
| 1169 | void genCodeForJumpTrue(GenTree* tree); |
| 1170 | #ifdef _TARGET_ARM64_ |
| 1171 | void genCodeForJumpCompare(GenTreeOp* tree); |
| 1172 | #endif // _TARGET_ARM64_ |
| 1173 | |
| 1174 | #if FEATURE_EH_FUNCLETS |
| 1175 | void genEHCatchRet(BasicBlock* block); |
| 1176 | #else // !FEATURE_EH_FUNCLETS |
| 1177 | void genEHFinallyOrFilterRet(BasicBlock* block); |
| 1178 | #endif // !FEATURE_EH_FUNCLETS |
| 1179 | |
| 1180 | void genMultiRegCallStoreToLocal(GenTree* treeNode); |
| 1181 | |
| 1182 | // Deals with codegen for muti-register struct returns. |
| 1183 | bool isStructReturn(GenTree* treeNode); |
| 1184 | void genStructReturn(GenTree* treeNode); |
| 1185 | |
| 1186 | #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) |
| 1187 | void genLongReturn(GenTree* treeNode); |
| 1188 | #endif // _TARGET_X86_ || _TARGET_ARM_ |
| 1189 | |
| 1190 | #if defined(_TARGET_X86_) |
| 1191 | void genFloatReturn(GenTree* treeNode); |
| 1192 | #endif // _TARGET_X86_ |
| 1193 | |
| 1194 | #if defined(_TARGET_ARM64_) |
| 1195 | void genSimpleReturn(GenTree* treeNode); |
| 1196 | #endif // _TARGET_ARM64_ |
| 1197 | |
| 1198 | void genReturn(GenTree* treeNode); |
| 1199 | |
| 1200 | void genLclHeap(GenTree* tree); |
| 1201 | |
| 1202 | bool genIsRegCandidateLocal(GenTree* tree) |
| 1203 | { |
| 1204 | if (!tree->IsLocal()) |
| 1205 | { |
| 1206 | return false; |
| 1207 | } |
| 1208 | const LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]; |
| 1209 | return (varDsc->lvIsRegCandidate()); |
| 1210 | } |
| 1211 | |
| 1212 | #ifdef FEATURE_PUT_STRUCT_ARG_STK |
| 1213 | #ifdef _TARGET_X86_ |
| 1214 | bool m_pushStkArg; |
| 1215 | #else // !_TARGET_X86_ |
| 1216 | unsigned m_stkArgVarNum; |
| 1217 | unsigned m_stkArgOffset; |
| 1218 | #endif // !_TARGET_X86_ |
| 1219 | #endif // !FEATURE_PUT_STRUCT_ARG_STK |
| 1220 | |
| 1221 | #if defined(DEBUG) && defined(_TARGET_XARCH_) |
| 1222 | void genStackPointerCheck(bool doStackPointerCheck, unsigned lvaStackPointerVar); |
| 1223 | #endif // defined(DEBUG) && defined(_TARGET_XARCH_) |
| 1224 | |
| 1225 | #ifdef DEBUG |
| 1226 | GenTree* lastConsumedNode; |
| 1227 | void genNumberOperandUse(GenTree* const operand, int& useNum) const; |
| 1228 | void genCheckConsumeNode(GenTree* const node); |
| 1229 | #else // !DEBUG |
| 1230 | inline void genCheckConsumeNode(GenTree* treeNode) |
| 1231 | { |
| 1232 | } |
| 1233 | #endif // DEBUG |
| 1234 | |
| 1235 | /* |
| 1236 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1237 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1238 | XX XX |
| 1239 | XX Instruction XX |
| 1240 | XX XX |
| 1241 | XX The interface to generate a machine-instruction. XX |
| 1242 | XX Currently specific to x86 XX |
| 1243 | XX TODO-Cleanup: Consider factoring this out of CodeGen XX |
| 1244 | XX XX |
| 1245 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1246 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1247 | */ |
| 1248 | |
| 1249 | public: |
| 1250 | void instInit(); |
| 1251 | |
| 1252 | void instGen(instruction ins); |
| 1253 | #ifdef _TARGET_XARCH_ |
| 1254 | void instNop(unsigned size); |
| 1255 | #endif |
| 1256 | |
| 1257 | void inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock); |
| 1258 | |
| 1259 | void inst_SET(emitJumpKind condition, regNumber reg); |
| 1260 | |
| 1261 | void inst_RV(instruction ins, regNumber reg, var_types type, emitAttr size = EA_UNKNOWN); |
| 1262 | |
| 1263 | void inst_RV_RV(instruction ins, |
| 1264 | regNumber reg1, |
| 1265 | regNumber reg2, |
| 1266 | var_types type = TYP_I_IMPL, |
| 1267 | emitAttr size = EA_UNKNOWN, |
| 1268 | insFlags flags = INS_FLAGS_DONT_CARE); |
| 1269 | |
| 1270 | void inst_RV_RV_RV(instruction ins, |
| 1271 | regNumber reg1, |
| 1272 | regNumber reg2, |
| 1273 | regNumber reg3, |
| 1274 | emitAttr size, |
| 1275 | insFlags flags = INS_FLAGS_DONT_CARE); |
| 1276 | |
| 1277 | void inst_IV(instruction ins, int val); |
| 1278 | void inst_IV_handle(instruction ins, int val); |
| 1279 | |
| 1280 | void inst_RV_IV( |
| 1281 | instruction ins, regNumber reg, target_ssize_t val, emitAttr size, insFlags flags = INS_FLAGS_DONT_CARE); |
| 1282 | |
| 1283 | void inst_ST_RV(instruction ins, TempDsc* tmp, unsigned ofs, regNumber reg, var_types type); |
| 1284 | void inst_ST_IV(instruction ins, TempDsc* tmp, unsigned ofs, int val, var_types type); |
| 1285 | |
| 1286 | void inst_SA_RV(instruction ins, unsigned ofs, regNumber reg, var_types type); |
| 1287 | void inst_SA_IV(instruction ins, unsigned ofs, int val, var_types type); |
| 1288 | |
| 1289 | void inst_RV_ST( |
| 1290 | instruction ins, regNumber reg, TempDsc* tmp, unsigned ofs, var_types type, emitAttr size = EA_UNKNOWN); |
| 1291 | void inst_FS_ST(instruction ins, emitAttr size, TempDsc* tmp, unsigned ofs); |
| 1292 | |
| 1293 | void inst_TT(instruction ins, GenTree* tree, unsigned offs = 0, int shfv = 0, emitAttr size = EA_UNKNOWN); |
| 1294 | |
| 1295 | void inst_TT_RV(instruction ins, |
| 1296 | GenTree* tree, |
| 1297 | regNumber reg, |
| 1298 | unsigned offs = 0, |
| 1299 | emitAttr size = EA_UNKNOWN, |
| 1300 | insFlags flags = INS_FLAGS_DONT_CARE); |
| 1301 | |
| 1302 | void inst_RV_TT(instruction ins, |
| 1303 | regNumber reg, |
| 1304 | GenTree* tree, |
| 1305 | unsigned offs = 0, |
| 1306 | emitAttr size = EA_UNKNOWN, |
| 1307 | insFlags flags = INS_FLAGS_DONT_CARE); |
| 1308 | |
| 1309 | void inst_FS_TT(instruction ins, GenTree* tree); |
| 1310 | |
| 1311 | void inst_RV_SH(instruction ins, emitAttr size, regNumber reg, unsigned val, insFlags flags = INS_FLAGS_DONT_CARE); |
| 1312 | |
| 1313 | void inst_TT_SH(instruction ins, GenTree* tree, unsigned val, unsigned offs = 0); |
| 1314 | |
| 1315 | void inst_RV_CL(instruction ins, regNumber reg, var_types type = TYP_I_IMPL); |
| 1316 | |
| 1317 | void inst_TT_CL(instruction ins, GenTree* tree, unsigned offs = 0); |
| 1318 | |
| 1319 | #if defined(_TARGET_XARCH_) |
| 1320 | void inst_RV_RV_IV(instruction ins, emitAttr size, regNumber reg1, regNumber reg2, unsigned ival); |
| 1321 | #endif |
| 1322 | |
| 1323 | void inst_RV_RR(instruction ins, emitAttr size, regNumber reg1, regNumber reg2); |
| 1324 | |
| 1325 | void inst_RV_ST(instruction ins, emitAttr size, regNumber reg, GenTree* tree); |
| 1326 | |
| 1327 | void inst_mov_RV_ST(regNumber reg, GenTree* tree); |
| 1328 | |
| 1329 | void inst_set_SV_var(GenTree* tree); |
| 1330 | |
| 1331 | #ifdef _TARGET_ARM_ |
| 1332 | bool arm_Valid_Imm_For_Instr(instruction ins, target_ssize_t imm, insFlags flags); |
| 1333 | bool arm_Valid_Disp_For_LdSt(target_ssize_t disp, var_types type); |
| 1334 | bool arm_Valid_Imm_For_Alu(target_ssize_t imm); |
| 1335 | bool arm_Valid_Imm_For_Mov(target_ssize_t imm); |
| 1336 | bool arm_Valid_Imm_For_Small_Mov(regNumber reg, target_ssize_t imm, insFlags flags); |
| 1337 | bool arm_Valid_Imm_For_Add(target_ssize_t imm, insFlags flag); |
| 1338 | bool arm_Valid_Imm_For_Add_SP(target_ssize_t imm); |
| 1339 | bool arm_Valid_Imm_For_BL(ssize_t addr); |
| 1340 | |
| 1341 | bool ins_Writes_Dest(instruction ins); |
| 1342 | #endif |
| 1343 | |
| 1344 | bool isMoveIns(instruction ins); |
| 1345 | instruction ins_Move_Extend(var_types srcType, bool srcInReg); |
| 1346 | |
| 1347 | instruction ins_Copy(var_types dstType); |
| 1348 | instruction ins_CopyIntToFloat(var_types srcType, var_types dstTyp); |
| 1349 | instruction ins_CopyFloatToInt(var_types srcType, var_types dstTyp); |
| 1350 | static instruction ins_FloatStore(var_types type = TYP_DOUBLE); |
| 1351 | static instruction ins_FloatCopy(var_types type = TYP_DOUBLE); |
| 1352 | instruction ins_FloatConv(var_types to, var_types from); |
| 1353 | instruction ins_FloatCompare(var_types type); |
| 1354 | instruction ins_MathOp(genTreeOps oper, var_types type); |
| 1355 | instruction ins_FloatSqrt(var_types type); |
| 1356 | |
| 1357 | void instGen_Return(unsigned stkArgSize); |
| 1358 | |
| 1359 | #ifdef _TARGET_ARM64_ |
| 1360 | void instGen_MemoryBarrier(insBarrier barrierType = INS_BARRIER_ISH); |
| 1361 | #else |
| 1362 | void instGen_MemoryBarrier(); |
| 1363 | #endif |
| 1364 | |
| 1365 | void instGen_Set_Reg_To_Zero(emitAttr size, regNumber reg, insFlags flags = INS_FLAGS_DONT_CARE); |
| 1366 | |
| 1367 | void instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, insFlags flags = INS_FLAGS_DONT_CARE); |
| 1368 | |
| 1369 | void instGen_Compare_Reg_To_Zero(emitAttr size, regNumber reg); |
| 1370 | |
| 1371 | void instGen_Compare_Reg_To_Reg(emitAttr size, regNumber reg1, regNumber reg2); |
| 1372 | |
| 1373 | void instGen_Compare_Reg_To_Imm(emitAttr size, regNumber reg, target_ssize_t imm); |
| 1374 | |
| 1375 | void instGen_Load_Reg_From_Lcl(var_types srcType, regNumber dstReg, int varNum, int offs); |
| 1376 | |
| 1377 | void instGen_Store_Reg_Into_Lcl(var_types dstType, regNumber srcReg, int varNum, int offs); |
| 1378 | |
| 1379 | void instGen_Store_Imm_Into_Lcl( |
| 1380 | var_types dstType, emitAttr sizeAttr, ssize_t imm, int varNum, int offs, regNumber regToUse = REG_NA); |
| 1381 | |
| 1382 | #ifdef DEBUG |
| 1383 | void __cdecl instDisp(instruction ins, bool noNL, const char* fmt, ...); |
| 1384 | #endif |
| 1385 | |
| 1386 | #ifdef _TARGET_XARCH_ |
| 1387 | instruction genMapShiftInsToShiftByConstantIns(instruction ins, int shiftByValue); |
| 1388 | #endif // _TARGET_XARCH_ |
| 1389 | }; |
| 1390 | |
| 1391 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1392 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1393 | XX XX |
| 1394 | XX Instruction XX |
| 1395 | XX Inline functions XX |
| 1396 | XX XX |
| 1397 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1398 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| 1399 | */ |
| 1400 | |
| 1401 | #ifdef _TARGET_XARCH_ |
| 1402 | /***************************************************************************** |
| 1403 | * |
| 1404 | * Generate a floating-point instruction that has one operand given by |
| 1405 | * a tree (which has been made addressable). |
| 1406 | */ |
| 1407 | |
| 1408 | inline void CodeGen::inst_FS_TT(instruction ins, GenTree* tree) |
| 1409 | { |
| 1410 | assert(instIsFP(ins)); |
| 1411 | |
| 1412 | assert(varTypeIsFloating(tree->gtType)); |
| 1413 | |
| 1414 | inst_TT(ins, tree, 0); |
| 1415 | } |
| 1416 | #endif |
| 1417 | |
| 1418 | /***************************************************************************** |
| 1419 | * |
| 1420 | * Generate a "shift reg, cl" instruction. |
| 1421 | */ |
| 1422 | |
| 1423 | inline void CodeGen::inst_RV_CL(instruction ins, regNumber reg, var_types type) |
| 1424 | { |
| 1425 | inst_RV(ins, reg, type); |
| 1426 | } |
| 1427 | |
| 1428 | #endif // _CODEGEN_H_ |
| 1429 | |