1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
5 // This class contains all the data & functionality for code generation
6 // of a method, except for the target-specific elements, which are
7 // primarily in the Target class.
12 #include "codegeninterface.h"
13 #include "compiler.h" // temporary??
15 #include "jitgcinfo.h"
17 class CodeGen final : public CodeGenInterface
20 friend class DisAssembler;
23 // This could use further abstraction
24 CodeGen(Compiler* theCompiler);
26 virtual void genGenerateCode(void** codePtr, uint32_t* nativeSizeOfCode);
28 void genGenerateMachineCode();
29 void genEmitMachineCode();
30 void genEmitUnwindDebugGCandEH();
32 // TODO-Cleanup: Abstract out the part of this that finds the addressing mode, and
34 virtual bool genCreateAddrMode(
35 GenTree* addr, bool fold, bool* revPtr, GenTree** rv1Ptr, GenTree** rv2Ptr, unsigned* mulPtr, ssize_t* cnsPtr);
38 #if defined(TARGET_XARCH)
39 // Bit masks used in negating a float or double number.
40 // This is to avoid creating more than one data constant for these bitmasks when a
41 // method has more than one GT_NEG operation on floating point values.
42 CORINFO_FIELD_HANDLE negBitmaskFlt;
43 CORINFO_FIELD_HANDLE negBitmaskDbl;
45 // Bit masks used in computing Math.Abs() of a float or double number.
46 CORINFO_FIELD_HANDLE absBitmaskFlt;
47 CORINFO_FIELD_HANDLE absBitmaskDbl;
49 // Bit mask used in zeroing the 3rd element of a SIMD12
50 CORINFO_FIELD_HANDLE zroSimd12Elm3;
52 // Bit mask used in U8 -> double conversion to adjust the result.
53 CORINFO_FIELD_HANDLE u8ToDblBitmask;
55 // Generates SSE2 code for the given tree as "Operand BitWiseOp BitMask"
56 void genSSE2BitwiseOp(GenTree* treeNode);
58 // Generates SSE41 code for the given tree as a round operation
59 void genSSE41RoundOp(GenTreeOp* treeNode);
61 instruction simdAlignedMovIns()
63 // We use movaps when non-VEX because it is a smaller instruction;
64 // however the VEX version vmovaps would be used which is the same size as vmovdqa;
65 // also vmovdqa has more available CPU ports on older processors so we switch to that
66 return compiler->canUseVexEncoding() ? INS_movdqa : INS_movaps;
68 instruction simdUnalignedMovIns()
70 // We use movups when non-VEX because it is a smaller instruction;
71 // however the VEX version vmovups would be used which is the same size as vmovdqu;
72 // but vmovdqu has more available CPU ports on older processors so we switch to that
73 return compiler->canUseVexEncoding() ? INS_movdqu : INS_movups;
75 #endif // defined(TARGET_XARCH)
77 void genPrepForCompiler();
79 void genMarkLabelsForCodegen();
81 regNumber genFramePointerReg()
83 if (isFramePointerUsed())
93 static bool genShouldRoundFP();
95 static GenTreeIndir indirForm(var_types type, GenTree* base);
96 static GenTreeStoreInd storeIndirForm(var_types type, GenTree* base, GenTree* data);
98 GenTreeIntCon intForm(var_types type, ssize_t value);
100 void genRangeCheck(GenTree* node);
102 void genLockedInstructions(GenTreeOp* node);
104 void genCodeForLockAdd(GenTreeOp* node);
108 // On some targets such as the ARM we may need to have an extra reserved register
109 // that is used when addressing stack based locals and stack based temps.
110 // This method returns the regNumber that should be used when an extra register
111 // is needed to access the stack based locals and stack based temps.
113 regNumber rsGetRsvdReg()
115 // We should have already added this register to the mask
116 // of reserved registers in regSet.rdMaskResvd
117 noway_assert((regSet.rsMaskResvd & RBM_OPT_RSVD) != 0);
121 #endif // REG_OPT_RSVD
123 //-------------------------------------------------------------------------
125 bool genUseBlockInit; // true if we plan to block-initialize the local stack frame
126 unsigned genInitStkLclCnt; // The count of local variables that we need to zero init
128 void SubtractStackLevel(unsigned adjustment)
130 assert(genStackLevel >= adjustment);
131 unsigned newStackLevel = genStackLevel - adjustment;
132 if (genStackLevel != newStackLevel)
134 JITDUMP("Adjusting stack level from %d to %d\n", genStackLevel, newStackLevel);
136 genStackLevel = newStackLevel;
139 void AddStackLevel(unsigned adjustment)
141 unsigned newStackLevel = genStackLevel + adjustment;
142 if (genStackLevel != newStackLevel)
144 JITDUMP("Adjusting stack level from %d to %d\n", genStackLevel, newStackLevel);
146 genStackLevel = newStackLevel;
149 void SetStackLevel(unsigned newStackLevel)
151 if (genStackLevel != newStackLevel)
153 JITDUMP("Setting stack level from %d to %d\n", genStackLevel, newStackLevel);
155 genStackLevel = newStackLevel;
158 //-------------------------------------------------------------------------
162 // Allocates storage for the GC info, writes the GC info into that storage, records the address of the
163 // GC info of the method with the EE, and returns a pointer to the "info" portion (just post-header) of
164 // the GC info. Requires "codeSize" to be the size of the generated code, "prologSize" and "epilogSize"
165 // to be the sizes of the prolog and epilog, respectively. In DEBUG, makes a check involving the
166 // "codePtr", assumed to be a pointer to the start of the generated code.
167 CLANG_FORMAT_COMMENT_ANCHOR;
169 #ifdef JIT32_GCENCODER
170 void* genCreateAndStoreGCInfo(unsigned codeSize, unsigned prologSize, unsigned epilogSize DEBUGARG(void* codePtr));
171 void* genCreateAndStoreGCInfoJIT32(unsigned codeSize,
173 unsigned epilogSize DEBUGARG(void* codePtr));
174 #else // !JIT32_GCENCODER
175 void genCreateAndStoreGCInfo(unsigned codeSize, unsigned prologSize, unsigned epilogSize DEBUGARG(void* codePtr));
176 void genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUGARG(void* codePtr));
177 #endif // !JIT32_GCENCODER
179 /**************************************************************************
181 *************************************************************************/
184 // the current (pending) label ref, a label which has been referenced but not yet seen
185 BasicBlock* genPendingCallLabel;
188 uint32_t* nativeSizeOfCode;
193 // Last instr we have displayed for dspInstrs
194 unsigned genCurDispOffset;
196 static const char* genInsName(instruction ins);
197 const char* genInsDisplayName(emitter::instrDesc* id);
199 static const char* genSizeStr(emitAttr size);
201 void genInitialize();
203 void genInitializeRegisterState();
205 void genCodeForBBlist();
208 void genSpillVar(GenTree* tree);
211 void genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTarget = REG_NA);
213 void genGCWriteBarrier(GenTreeStoreInd* store, GCInfo::WriteBarrierForm wbf);
215 BasicBlock* genCreateTempLabel();
218 void genLogLabel(BasicBlock* bb);
221 void genDefineTempLabel(BasicBlock* label);
222 void genDefineInlineTempLabel(BasicBlock* label);
224 void genAdjustStackLevel(BasicBlock* block);
226 void genExitCode(BasicBlock* block);
228 void genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKind, BasicBlock* failBlk = nullptr);
230 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
231 void genJumpToThrowHlpBlk_la(SpecialCodeKind codeKind,
234 BasicBlock* failBlk = nullptr,
235 regNumber reg2 = REG_R0);
237 void genCheckOverflow(GenTree* tree);
240 //-------------------------------------------------------------------------
242 // Prolog/epilog generation
244 //-------------------------------------------------------------------------
250 // Prolog functions and data (there are a few exceptions for more generally used things)
253 void genEstablishFramePointer(int delta, bool reportUnwindData);
254 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
255 void genFnPrologCalleeRegArgs();
257 void genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbered, RegState* regState);
259 void genEnregisterIncomingStackArgs();
260 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
261 void genEnregisterOSRArgsAndLocals(regNumber initReg, bool* pInitRegZeroed);
263 void genEnregisterOSRArgsAndLocals();
265 void genCheckUseBlockInit();
266 #if defined(UNIX_AMD64_ABI) && defined(FEATURE_SIMD)
267 void genClearStackVec3ArgUpperBits();
268 #endif // UNIX_AMD64_ABI && FEATURE_SIMD
270 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
271 bool genInstrWithConstant(instruction ins,
277 bool inUnwindRegion = false);
279 void genStackPointerAdjustment(ssize_t spAdjustment, regNumber tmpReg, bool* pTmpRegIsZero, bool reportUnwindData);
281 void genPrologSaveRegPair(regNumber reg1,
285 bool useSaveNextPair,
287 bool* pTmpRegIsZero);
289 void genPrologSaveReg(regNumber reg1, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero);
291 void genEpilogRestoreRegPair(regNumber reg1,
295 bool useSaveNextPair,
297 bool* pTmpRegIsZero);
299 void genEpilogRestoreReg(regNumber reg1, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero);
301 // A simple struct to keep register pairs for prolog and epilog.
306 bool useSaveNextPair;
308 RegPair(regNumber reg1) : reg1(reg1), reg2(REG_NA), useSaveNextPair(false)
312 RegPair(regNumber reg1, regNumber reg2) : reg1(reg1), reg2(reg2), useSaveNextPair(false)
314 assert(reg2 == REG_NEXT(reg1));
318 static void genBuildRegPairsStack(regMaskTP regsMask, ArrayStack<RegPair>* regStack);
319 static void genSetUseSaveNextPairs(ArrayStack<RegPair>* regStack);
321 static int genGetSlotSizeForRegsInMask(regMaskTP regsMask);
323 void genSaveCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, int spOffset);
324 void genRestoreCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, int spOffset);
326 void genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowestCalleeSavedOffset, int spDelta);
327 void genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, int lowestCalleeSavedOffset, int spDelta);
329 void genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroed);
332 void genPushCalleeSavedRegisters();
335 #if defined(TARGET_AMD64)
336 void genOSRRecordTier0CalleeSavedRegistersAndFrame();
337 void genOSRSaveRemainingCalleeSavedRegisters();
338 #endif // TARGET_AMD64
340 void genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pInitRegZeroed, regMaskTP maskArgRegsLiveIn);
342 void genPoisonFrame(regMaskTP bbRegLiveIn);
344 #if defined(TARGET_ARM)
346 bool genInstrWithConstant(
347 instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm, insFlags flags, regNumber tmpReg);
349 bool genStackPointerAdjustment(ssize_t spAdjustment, regNumber tmpReg);
351 void genPushFltRegs(regMaskTP regMask);
352 void genPopFltRegs(regMaskTP regMask);
353 regMaskTP genStackAllocRegisterMask(unsigned frameSize, regMaskTP maskCalleeSavedFloat);
355 regMaskTP genJmpCallArgMask();
357 void genFreeLclFrame(unsigned frameSize,
358 /* IN OUT */ bool* pUnwindStarted);
360 void genMov32RelocatableDisplacement(BasicBlock* block, regNumber reg);
361 void genMov32RelocatableDataLabel(unsigned value, regNumber reg);
362 void genMov32RelocatableImmediate(emitAttr size, BYTE* addr, regNumber reg);
364 bool genUsedPopToReturn; // True if we use the pop into PC to return,
365 // False if we didn't and must branch to LR to return.
367 // A set of information that is used by funclet prolog and epilog generation. It is collected once, before
368 // funclet prologs and epilogs are generated, and used by all funclet prologs and epilogs, which must all be the
370 struct FuncletFrameInfoDsc
372 regMaskTP fiSaveRegs; // Set of registers saved in the funclet prolog (includes LR)
373 unsigned fiFunctionCallerSPtoFPdelta; // Delta between caller SP and the frame pointer
374 unsigned fiSpDelta; // Stack pointer delta
375 unsigned fiPSP_slot_SP_offset; // PSP slot offset from SP
376 int fiPSP_slot_CallerSP_offset; // PSP slot offset from Caller SP
379 FuncletFrameInfoDsc genFuncletInfo;
381 #elif defined(TARGET_ARM64)
383 // A set of information that is used by funclet prolog and epilog generation. It is collected once, before
384 // funclet prologs and epilogs are generated, and used by all funclet prologs and epilogs, which must all be the
386 struct FuncletFrameInfoDsc
388 regMaskTP fiSaveRegs; // Set of callee-saved registers saved in the funclet prolog (includes LR)
389 int fiFunction_CallerSP_to_FP_delta; // Delta between caller SP and the frame pointer in the parent function
391 int fiSP_to_FPLR_save_delta; // FP/LR register save offset from SP (positive)
392 int fiSP_to_PSP_slot_delta; // PSP slot offset from SP (positive)
393 int fiSP_to_CalleeSave_delta; // First callee-saved register slot offset from SP (positive)
394 int fiCallerSP_to_PSP_slot_delta; // PSP slot offset from Caller SP (negative)
395 int fiFrameType; // Funclet frame types are numbered. See genFuncletProlog() for details.
396 int fiSpDelta1; // Stack pointer delta 1 (negative)
397 int fiSpDelta2; // Stack pointer delta 2 (negative)
400 FuncletFrameInfoDsc genFuncletInfo;
402 #elif defined(TARGET_AMD64)
404 // A set of information that is used by funclet prolog and epilog generation. It is collected once, before
405 // funclet prologs and epilogs are generated, and used by all funclet prologs and epilogs, which must all be the
407 struct FuncletFrameInfoDsc
409 unsigned fiFunction_InitialSP_to_FP_delta; // Delta between Initial-SP and the frame pointer
410 unsigned fiSpDelta; // Stack pointer delta
411 int fiPSP_slot_InitialSP_offset; // PSP slot offset from Initial-SP
414 FuncletFrameInfoDsc genFuncletInfo;
416 #elif defined(TARGET_LOONGARCH64)
418 // A set of information that is used by funclet prolog and epilog generation.
419 // It is collected once, before funclet prologs and epilogs are generated,
420 // and used by all funclet prologs and epilogs, which must all be the same.
421 struct FuncletFrameInfoDsc
423 regMaskTP fiSaveRegs; // Set of callee-saved registers saved in the funclet prolog (includes RA)
424 int fiFunction_CallerSP_to_FP_delta; // Delta between caller SP and the frame pointer in the parent function
426 int fiSP_to_CalleeSaved_delta; // CalleeSaved register save offset from SP (positive)
427 int fiCalleeSavedPadding; // CalleeSaved offset padding (positive)
428 int fiSP_to_PSP_slot_delta; // PSP slot offset from SP (positive)
429 int fiCallerSP_to_PSP_slot_delta; // PSP slot offset from Caller SP (negative)
430 int fiSpDelta; // Stack pointer delta (negative)
433 FuncletFrameInfoDsc genFuncletInfo;
435 #elif defined(TARGET_RISCV64)
437 // A set of information that is used by funclet prolog and epilog generation.
438 // It is collected once, before funclet prologs and epilogs are generated,
439 // and used by all funclet prologs and epilogs, which must all be the same.
440 struct FuncletFrameInfoDsc
442 regMaskTP fiSaveRegs; // Set of callee-saved registers saved in the funclet prolog (includes RA)
443 int fiFunction_CallerSP_to_FP_delta; // Delta between caller SP and the frame pointer in the parent function
445 int fiSP_to_FPRA_save_delta; // FP/RA register save offset from SP (positive)
446 int fiSP_to_PSP_slot_delta; // PSP slot offset from SP (positive)
447 int fiCallerSP_to_PSP_slot_delta; // PSP slot offset from Caller SP (negative)
448 int fiFrameType; // Funclet frame types are numbered. See genFuncletProlog() for details.
449 int fiSpDelta1; // Stack pointer delta 1 (negative)
452 FuncletFrameInfoDsc genFuncletInfo;
454 #endif // TARGET_ARM, TARGET_ARM64, TARGET_AMD64, TARGET_LOONGARCH64, TARGET_RISCV64
456 #if defined(TARGET_XARCH)
458 // Save/Restore callee saved float regs to stack
459 void genPreserveCalleeSavedFltRegs(unsigned lclFrameSize);
460 void genRestoreCalleeSavedFltRegs(unsigned lclFrameSize);
461 // Generate VZeroupper instruction to avoid AVX/SSE transition penalty
462 void genVzeroupperIfNeeded(bool check256bitOnly = true);
464 #endif // TARGET_XARCH
466 void genZeroInitFltRegs(const regMaskTP& initFltRegs, const regMaskTP& initDblRegs, const regNumber& initReg);
468 regNumber genGetZeroReg(regNumber initReg, bool* pInitRegZeroed);
470 void genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, bool* pInitRegZeroed);
471 void genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNumber initReg, bool* pInitRegZeroed);
473 void genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed);
475 void genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed);
477 void genFinalizeFrame();
479 #ifdef PROFILING_SUPPORTED
480 void genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed);
481 void genProfilingLeaveCallback(unsigned helper);
482 #endif // PROFILING_SUPPORTED
485 void genEmitCall(int callType,
486 CORINFO_METHOD_HANDLE methHnd,
487 INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
489 X86_ARG(int argSize),
491 MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
498 void genEmitCallIndir(int callType,
499 CORINFO_METHOD_HANDLE methHnd,
500 INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
502 X86_ARG(int argSize),
504 MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
512 CLANG_FORMAT_COMMENT_ANCHOR;
514 #if defined(TARGET_ARM)
515 bool genCanUsePopToReturn(regMaskTP maskPopRegsInt, bool jmpEpilog);
518 #if defined(TARGET_ARM64)
520 void genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog);
522 #else // !defined(TARGET_ARM64)
524 void genPopCalleeSavedRegisters(bool jmpEpilog = false);
526 #if defined(TARGET_XARCH)
527 unsigned genPopCalleeSavedRegistersFromMask(regMaskTP rsPopRegs);
528 #endif // !defined(TARGET_XARCH)
530 #endif // !defined(TARGET_ARM64)
533 // Common or driving functions
536 void genReserveProlog(BasicBlock* block); // currently unused
537 void genReserveEpilog(BasicBlock* block);
539 void genFnEpilog(BasicBlock* block);
541 #if defined(FEATURE_EH_FUNCLETS)
543 void genReserveFuncletProlog(BasicBlock* block);
544 void genReserveFuncletEpilog(BasicBlock* block);
545 void genFuncletProlog(BasicBlock* block);
546 void genFuncletEpilog();
547 void genCaptureFuncletPrologEpilogInfo();
549 /*-----------------------------------------------------------------------------
551 * Set the main function PSPSym value in the frame.
552 * Funclets use different code to load the PSP sym and save it in their frame.
553 * See the document "CLR ABI.md" for a full description of the PSPSym.
554 * The PSPSym section of that document is copied here.
556 ***********************************
557 * The name PSPSym stands for Previous Stack Pointer Symbol. It is how a funclet
558 * accesses locals from the main function body.
560 * First, two definitions.
562 * Caller-SP is the value of the stack pointer in a function's caller before the call
563 * instruction is executed. That is, when function A calls function B, Caller-SP for B
564 * is the value of the stack pointer immediately before the call instruction in A
565 * (calling B) was executed. Note that this definition holds for both AMD64, which
566 * pushes the return value when a call instruction is executed, and for ARM, which
567 * doesn't. For AMD64, Caller-SP is the address above the call return address.
569 * Initial-SP is the initial value of the stack pointer after the fixed-size portion of
570 * the frame has been allocated. That is, before any "alloca"-type allocations.
572 * The PSPSym is a pointer-sized local variable in the frame of the main function and
573 * of each funclet. The value stored in PSPSym is the value of Initial-SP/Caller-SP
574 * for the main function. The stack offset of the PSPSym is reported to the VM in the
575 * GC information header. The value reported in the GC information is the offset of the
576 * PSPSym from Initial-SP/Caller-SP. (Note that both the value stored, and the way the
577 * value is reported to the VM, differs between architectures. In particular, note that
578 * most things in the GC information header are reported as offsets relative to Caller-SP,
579 * but PSPSym on AMD64 is one (maybe the only) exception.)
581 * The VM uses the PSPSym to find other locals it cares about (such as the generics context
582 * in a funclet frame). The JIT uses it to re-establish the frame pointer register, so that
583 * the frame pointer is the same value in a funclet as it is in the main function body.
585 * When a funclet is called, it is passed the Establisher Frame Pointer. For AMD64 this is
586 * true for all funclets and it is passed as the first argument in RCX, but for ARM this is
587 * only true for first pass funclets (currently just filters) and it is passed as the second
588 * argument in R1. The Establisher Frame Pointer is a stack pointer of an interesting "parent"
589 * frame in the exception processing system. For the CLR, it points either to the main function
590 * frame or a dynamically enclosing funclet frame from the same function, for the funclet being
591 * invoked. The value of the Establisher Frame Pointer is Initial-SP on AMD64, Caller-SP on ARM.
593 * Using the establisher frame, the funclet wants to load the value of the PSPSym. Since we
594 * don't know if the Establisher Frame is from the main function or a funclet, we design the
595 * main function and funclet frame layouts to place the PSPSym at an identical, small, constant
596 * offset from the Establisher Frame in each case. (This is also required because we only report
597 * a single offset to the PSPSym in the GC information, and that offset must be valid for the main
598 * function and all of its funclets). Then, the funclet uses this known offset to compute the
599 * PSPSym address and read its value. From this, it can compute the value of the frame pointer
600 * (which is a constant offset from the PSPSym value) and set the frame register to be the same
601 * as the parent function. Also, the funclet writes the value of the PSPSym to its own frame's
602 * PSPSym. This "copying" of the PSPSym happens for every funclet invocation, in particular,
603 * for every nested funclet invocation.
605 * On ARM, for all second pass funclets (finally, fault, catch, and filter-handler) the VM
606 * restores all non-volatile registers to their values within the parent frame. This includes
607 * the frame register (R11). Thus, the PSPSym is not used to recompute the frame pointer register
608 * in this case, though the PSPSym is copied to the funclet's frame, as for all funclets.
610 * Catch, Filter, and Filter-handlers also get an Exception object (GC ref) as an argument
611 * (REG_EXCEPTION_OBJECT). On AMD64 it is the second argument and thus passed in RDX. On
612 * ARM this is the first argument and passed in R0.
614 * (Note that the JIT64 source code contains a comment that says, "The current CLR doesn't always
615 * pass the correct establisher frame to the funclet. Funclet may receive establisher frame of
616 * funclet when expecting that of original routine." It indicates this is the reason that a PSPSym
617 * is required in all funclets as well as the main function, whereas if the establisher frame was
618 * correctly reported, the PSPSym could be omitted in some cases.)
619 ***********************************
621 void genSetPSPSym(regNumber initReg, bool* pInitRegZeroed);
623 void genUpdateCurrentFunclet(BasicBlock* block);
624 #if defined(TARGET_ARM)
625 void genInsertNopForUnwinder(BasicBlock* block);
628 #else // !FEATURE_EH_FUNCLETS
630 // This is a no-op when there are no funclets!
631 void genUpdateCurrentFunclet(BasicBlock* block)
636 #endif // !FEATURE_EH_FUNCLETS
638 void genGeneratePrologsAndEpilogs();
640 #if defined(DEBUG) && defined(TARGET_ARM64)
641 void genArm64EmitterUnitTests();
644 #if defined(DEBUG) && defined(TARGET_LOONGARCH64)
645 void genLoongArch64EmitterUnitTests();
648 #if defined(DEBUG) && defined(LATE_DISASM) && defined(TARGET_AMD64)
649 void genAmd64EmitterUnitTests();
653 virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value);
654 virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const;
655 bool genSaveFpLrWithAllCalleeSavedRegisters;
656 bool genForceFuncletFrameType5;
657 #endif // TARGET_ARM64
659 //-------------------------------------------------------------------------
661 // End prolog/epilog generation
663 //-------------------------------------------------------------------------
665 void genSinglePush();
667 regMaskTP genPushRegs(regMaskTP regs, regMaskTP* byrefRegs, regMaskTP* noRefRegs);
668 void genPopRegs(regMaskTP regs, regMaskTP byrefRegs, regMaskTP noRefRegs);
671 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
672 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
674 XX Debugging Support XX
676 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
677 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
681 void genIPmappingDisp(unsigned mappingNum, const IPmappingDsc* ipMapping);
682 void genIPmappingListDisp();
685 void genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
686 void genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
687 void genIPmappingGen();
688 void genAddRichIPMappingHere(const DebugInfo& di);
690 void genReportRichDebugInfo();
692 void genRecordRichDebugInfoInlineTree(InlineContext* context, ICorDebugInfo::InlineTreeNode* tree);
695 void genReportRichDebugInfoToFile();
696 void genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* context, bool* first);
699 void genEnsureCodeEmitted(const DebugInfo& di);
701 //-------------------------------------------------------------------------
702 // scope info for the variables
704 void genSetScopeInfo(unsigned which,
705 UNATIVE_OFFSET startOffs,
706 UNATIVE_OFFSET length,
712 void genSetScopeInfo();
713 // Send VariableLiveRanges as debug info to the debugger
714 void genSetScopeInfoUsingVariableRanges();
718 void checkICodeDebugInfo();
720 // The logic used to report debug info on debug code is the same for ScopeInfo and
722 void siBeginBlock(BasicBlock* block);
723 void siEndBlock(BasicBlock* block);
725 // VariableLiveRange and siScope needs this method to report variables on debug code
726 void siOpenScopesForNonTrackedVars(const BasicBlock* block, unsigned int lastBlockILEndOffset);
729 #if defined(FEATURE_EH_FUNCLETS)
730 bool siInFuncletRegion; // Have we seen the start of the funclet region?
731 #endif // FEATURE_EH_FUNCLETS
733 IL_OFFSET siLastEndOffs; // IL offset of the (exclusive) end of the last block processed
736 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
737 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
739 XX PrologScopeInfo XX
741 XX We need special handling in the prolog block, as the parameter variables XX
742 XX may not be in the same position described by genLclVarTable - they all XX
743 XX start out on the stack XX
745 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
746 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
753 NATIVE_OFFSET psiGetVarStackOffset(const LclVarDsc* lclVarDsc) const;
755 /*****************************************************************************
758 * This struct holds the LocalVarInfo in terms of the generated native code
759 * after a call to genSetScopeInfo()
765 struct TrnslLocalVarInfo
770 UNATIVE_OFFSET tlviStartPC;
776 // Array of scopes of LocalVars in terms of native code
778 TrnslLocalVarInfo* genTrnslLocalVarInfo;
779 unsigned genTrnslLocalVarCount;
782 void genSetRegToConst(regNumber targetReg, var_types targetType, GenTree* tree);
783 #if defined(FEATURE_SIMD)
784 void genSetRegToConst(regNumber targetReg, var_types targetType, simd_t* val);
786 void genCodeForTreeNode(GenTree* treeNode);
787 void genCodeForBinary(GenTreeOp* treeNode);
789 #if defined(TARGET_X86)
790 void genCodeForLongUMod(GenTreeOp* node);
793 void genCodeForDivMod(GenTreeOp* treeNode);
794 void genCodeForMul(GenTreeOp* treeNode);
795 void genCodeForIncSaturate(GenTree* treeNode);
796 void genCodeForMulHi(GenTreeOp* treeNode);
797 void genLeaInstruction(GenTreeAddrMode* lea);
798 void genSetRegToCond(regNumber dstReg, GenTree* tree);
800 #if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
801 void genScaledAdd(emitAttr attr,
805 int scale RISCV64_ARG(regNumber scaleTempReg));
806 #endif // TARGET_ARMARCH || TARGET_LOONGARCH64 || TARGET_RISCV64
808 #if defined(TARGET_ARMARCH)
809 void genCodeForMulLong(GenTreeOp* mul);
810 #endif // TARGET_ARMARCH
812 #if !defined(TARGET_64BIT)
813 void genLongToIntCast(GenTree* treeNode);
816 // Generate code for a GT_BITCAST that is not contained.
817 void genCodeForBitCast(GenTreeOp* treeNode);
819 // Generate the instruction to move a value between register files
820 void genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg);
823 struct GenIntCastDesc
828 CHECK_SMALL_INT_RANGE,
832 CHECK_POSITIVE_INT_RANGE,
840 ZERO_EXTEND_SMALL_INT,
841 SIGN_EXTEND_SMALL_INT,
846 LOAD_ZERO_EXTEND_SMALL_INT,
847 LOAD_SIGN_EXTEND_SMALL_INT,
849 LOAD_ZERO_EXTEND_INT,
850 LOAD_SIGN_EXTEND_INT,
856 CheckKind m_checkKind;
857 unsigned m_checkSrcSize;
858 int m_checkSmallIntMin;
859 int m_checkSmallIntMax;
860 ExtendKind m_extendKind;
861 unsigned m_extendSrcSize;
864 GenIntCastDesc(GenTreeCast* cast);
866 CheckKind CheckKind() const
871 unsigned CheckSrcSize() const
873 assert(m_checkKind != CHECK_NONE);
874 return m_checkSrcSize;
877 int CheckSmallIntMin() const
879 assert(m_checkKind == CHECK_SMALL_INT_RANGE);
880 return m_checkSmallIntMin;
883 int CheckSmallIntMax() const
885 assert(m_checkKind == CHECK_SMALL_INT_RANGE);
886 return m_checkSmallIntMax;
889 ExtendKind ExtendKind() const
894 unsigned ExtendSrcSize() const
896 return m_extendSrcSize;
901 void genIntCastOverflowCheck(GenTreeCast* cast, const GenIntCastDesc& desc, regNumber reg);
902 void genIntToIntCast(GenTreeCast* cast);
903 void genFloatToFloatCast(GenTree* treeNode);
904 void genFloatToIntCast(GenTree* treeNode);
905 void genIntToFloatCast(GenTree* treeNode);
906 void genCkfinite(GenTree* treeNode);
907 void genCodeForCompare(GenTreeOp* tree);
909 void genCodeForCCMP(GenTreeCCMP* ccmp);
911 void genCodeForSelect(GenTreeOp* select);
912 void genIntrinsic(GenTreeIntrinsic* treeNode);
913 void genPutArgStk(GenTreePutArgStk* treeNode);
914 void genPutArgReg(GenTreeOp* tree);
915 #if FEATURE_ARG_SPLIT
916 void genPutArgSplit(GenTreePutArgSplit* treeNode);
917 #endif // FEATURE_ARG_SPLIT
919 #if defined(TARGET_XARCH)
920 unsigned getBaseVarForPutArgStk(GenTree* treeNode);
921 #endif // TARGET_XARCH
923 unsigned getFirstArgWithStackSlot();
925 void genCompareFloat(GenTree* treeNode);
926 void genCompareInt(GenTree* treeNode);
928 bool genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opType);
929 GenTree* genTryFindFlagsConsumer(GenTree* flagsProducer, GenCondition** condition);
934 insOpts genGetSimdInsOpt(emitAttr size, var_types elementType);
936 void genSimdUpperSave(GenTreeIntrinsic* node);
937 void genSimdUpperRestore(GenTreeIntrinsic* node);
939 void genSimd12UpperClear(regNumber tgtReg);
941 // TYP_SIMD12 (i.e Vector3 of size 12 bytes) is not a hardware supported size and requires
942 // two reads/writes on 64-bit targets. These routines abstract reading/writing of Vector3
943 // values through an indirection. Note that Vector3 locals allocated on stack would have
944 // their size rounded to TARGET_POINTER_SIZE (which is 8 bytes on 64-bit targets) and hence
945 // Vector3 locals could be treated as TYP_SIMD16 while reading/writing.
946 void genStoreIndTypeSimd12(GenTreeStoreInd* treeNode);
947 void genLoadIndTypeSimd12(GenTreeIndir* treeNode);
948 void genStoreLclTypeSimd12(GenTreeLclVarCommon* treeNode);
949 void genLoadLclTypeSimd12(GenTreeLclVarCommon* treeNode);
951 void genEmitStoreLclTypeSimd12(GenTree* store, unsigned lclNum, unsigned offset);
952 void genEmitLoadLclTypeSimd12(regNumber tgtReg, unsigned lclNum, unsigned offset);
953 #endif // TARGET_XARCH
955 void genStoreSimd12ToStack(regNumber dataReg, regNumber tmpReg);
956 void genPutArgStkSimd12(GenTreePutArgStk* treeNode);
958 #endif // FEATURE_SIMD
960 #ifdef FEATURE_HW_INTRINSICS
961 void genHWIntrinsic(GenTreeHWIntrinsic* node);
962 #if defined(TARGET_XARCH)
963 void genHWIntrinsic_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber reg, GenTree* rmOp);
964 void genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival);
965 void genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr);
966 void genHWIntrinsic_R_R_RM(
967 GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, GenTree* op2);
968 void genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival);
969 void genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr);
970 void genHWIntrinsic_R_R_R_RM(
971 instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, GenTree* op3);
972 void genHWIntrinsic_R_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival);
973 void genBaseIntrinsic(GenTreeHWIntrinsic* node);
974 void genX86BaseIntrinsic(GenTreeHWIntrinsic* node);
975 void genSSEIntrinsic(GenTreeHWIntrinsic* node);
976 void genSSE2Intrinsic(GenTreeHWIntrinsic* node);
977 void genSSE41Intrinsic(GenTreeHWIntrinsic* node);
978 void genSSE42Intrinsic(GenTreeHWIntrinsic* node);
979 void genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node);
980 void genAESIntrinsic(GenTreeHWIntrinsic* node);
981 void genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node);
982 void genFMAIntrinsic(GenTreeHWIntrinsic* node);
983 void genPermuteVar2x(GenTreeHWIntrinsic* node);
984 void genLZCNTIntrinsic(GenTreeHWIntrinsic* node);
985 void genPCLMULQDQIntrinsic(GenTreeHWIntrinsic* node);
986 void genPOPCNTIntrinsic(GenTreeHWIntrinsic* node);
987 void genXCNTIntrinsic(GenTreeHWIntrinsic* node, instruction ins);
988 void genX86SerializeIntrinsic(GenTreeHWIntrinsic* node);
989 template <typename HWIntrinsicSwitchCaseBody>
990 void genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsic,
991 regNumber nonConstImmReg,
994 HWIntrinsicSwitchCaseBody emitSwCase);
995 #endif // defined(TARGET_XARCH)
998 class HWIntrinsicImmOpHelper final
1001 HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTree* immOp, GenTreeHWIntrinsic* intrin);
1006 // Returns true after the last call to EmitCaseEnd() (i.e. this signals that code generation is done).
1009 return (immValue > immUpperBound);
1012 // Returns a value of the immediate operand that should be used for a case.
1013 int ImmValue() const
1019 // Returns true if immOp is non contained immediate (i.e. the value of the immediate operand is enregistered in
1021 bool NonConstImmOp() const
1023 return nonConstImmReg != REG_NA;
1026 // Returns true if a non constant immediate operand can be either 0 or 1.
1027 bool TestImmOpZeroOrOne() const
1029 assert(NonConstImmOp());
1030 return (immLowerBound == 0) && (immUpperBound == 1);
1033 emitter* GetEmitter() const
1035 return codeGen->GetEmitter();
1038 CodeGen* const codeGen;
1039 BasicBlock* endLabel;
1040 BasicBlock* nonZeroLabel;
1044 regNumber nonConstImmReg;
1045 regNumber branchTargetReg;
1047 #endif // TARGET_ARM64
1049 #endif // FEATURE_HW_INTRINSICS
1051 #if !defined(TARGET_64BIT)
1053 // CodeGen for Long Ints
1055 void genStoreLongLclVar(GenTree* treeNode);
1057 #endif // !defined(TARGET_64BIT)
1059 //-------------------------------------------------------------------------
1060 // genUpdateLifeStore: Do liveness udpate after tree store instructions
1061 // were emitted, update result var's home if it was stored on stack.
1064 // tree - Gentree node
1065 // targetReg - of the tree
1066 // varDsc - result value's variable
1070 __forceinline void genUpdateLifeStore(GenTree* tree, regNumber targetReg, LclVarDsc* varDsc)
1072 if (targetReg != REG_NA)
1074 genProduceReg(tree);
1078 genUpdateLife(tree);
1079 varDsc->SetRegNum(REG_STK);
1083 // Do liveness update for register produced by the current node in codegen after
1084 // code has been emitted for it.
1085 void genProduceReg(GenTree* tree);
1086 void genSpillLocal(unsigned varNum, var_types type, GenTreeLclVar* lclNode, regNumber regNum);
1087 void genUnspillLocal(
1088 unsigned varNum, var_types type, GenTreeLclVar* lclNode, regNumber regNum, bool reSpill, bool isLastUse);
1089 void genUnspillRegIfNeeded(GenTree* tree);
1090 void genUnspillRegIfNeeded(GenTree* tree, unsigned multiRegIndex);
1091 regNumber genConsumeReg(GenTree* tree);
1092 regNumber genConsumeReg(GenTree* tree, unsigned multiRegIndex);
1093 void genCopyRegIfNeeded(GenTree* tree, regNumber needReg);
1094 void genConsumeRegAndCopy(GenTree* tree, regNumber needReg);
1096 void genConsumeIfReg(GenTree* tree)
1098 if (!tree->isContained())
1100 (void)genConsumeReg(tree);
1104 void genRegCopy(GenTree* tree);
1105 regNumber genRegCopy(GenTree* tree, unsigned multiRegIndex);
1106 void genTransferRegGCState(regNumber dst, regNumber src);
1107 void genConsumeAddress(GenTree* addr);
1108 void genConsumeAddrMode(GenTreeAddrMode* mode);
1109 void genSetBlockSize(GenTreeBlk* blkNode, regNumber sizeReg);
1110 void genConsumeBlockSrc(GenTreeBlk* blkNode);
1111 void genSetBlockSrc(GenTreeBlk* blkNode, regNumber srcReg);
1112 void genConsumeBlockOp(GenTreeBlk* blkNode, regNumber dstReg, regNumber srcReg, regNumber sizeReg);
1114 #ifdef FEATURE_PUT_STRUCT_ARG_STK
1115 void genConsumePutStructArgStk(GenTreePutArgStk* putArgStkNode,
1119 #endif // FEATURE_PUT_STRUCT_ARG_STK
1120 #if FEATURE_ARG_SPLIT
1121 void genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode);
1122 #endif // FEATURE_ARG_SPLIT
1124 void genConsumeRegs(GenTree* tree);
1125 void genConsumeOperands(GenTreeOp* tree);
1126 #if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
1127 void genConsumeMultiOpOperands(GenTreeMultiOp* tree);
1129 void genEmitGSCookieCheck(bool pushReg);
1130 void genCodeForShift(GenTree* tree);
1132 #if defined(TARGET_X86) || defined(TARGET_ARM)
1133 void genCodeForShiftLong(GenTree* tree);
1137 void genCodeForShiftRMW(GenTreeStoreInd* storeInd);
1138 #endif // TARGET_XARCH
1140 void genCodeForCast(GenTreeOp* tree);
1141 void genCodeForLclAddr(GenTreeLclFld* lclAddrNode);
1142 void genCodeForIndexAddr(GenTreeIndexAddr* tree);
1143 void genCodeForIndir(GenTreeIndir* tree);
1144 void genCodeForNegNot(GenTree* tree);
1145 void genCodeForBswap(GenTree* tree);
1146 bool genCanOmitNormalizationForBswap16(GenTree* tree);
1147 void genCodeForLclVar(GenTreeLclVar* tree);
1148 void genCodeForLclFld(GenTreeLclFld* tree);
1149 void genCodeForStoreLclFld(GenTreeLclFld* tree);
1150 void genCodeForStoreLclVar(GenTreeLclVar* tree);
1151 void genCodeForReturnTrap(GenTreeOp* tree);
1152 void genCodeForStoreInd(GenTreeStoreInd* tree);
1153 void genCodeForSwap(GenTreeOp* tree);
1154 void genCodeForCpObj(GenTreeBlk* cpObjNode);
1155 void genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode);
1156 void genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode);
1158 void genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode);
1160 void genCodeForPhysReg(GenTreePhysReg* tree);
1161 void genCodeForNullCheck(GenTreeIndir* tree);
1162 void genCodeForCmpXchg(GenTreeCmpXchg* tree);
1163 void genCodeForReuseVal(GenTree* treeNode);
1165 void genAlignStackBeforeCall(GenTreePutArgStk* putArgStk);
1166 void genAlignStackBeforeCall(GenTreeCall* call);
1167 void genRemoveAlignmentAfterCall(GenTreeCall* call, unsigned bias = 0);
1169 #if defined(UNIX_X86_ABI)
1171 unsigned curNestedAlignment; // Keep track of alignment adjustment required during codegen.
1172 unsigned maxNestedAlignment; // The maximum amount of alignment adjustment required.
1174 void SubtractNestedAlignment(unsigned adjustment)
1176 assert(curNestedAlignment >= adjustment);
1177 unsigned newNestedAlignment = curNestedAlignment - adjustment;
1178 if (curNestedAlignment != newNestedAlignment)
1180 JITDUMP("Adjusting stack nested alignment from %d to %d\n", curNestedAlignment, newNestedAlignment);
1182 curNestedAlignment = newNestedAlignment;
1185 void AddNestedAlignment(unsigned adjustment)
1187 unsigned newNestedAlignment = curNestedAlignment + adjustment;
1188 if (curNestedAlignment != newNestedAlignment)
1190 JITDUMP("Adjusting stack nested alignment from %d to %d\n", curNestedAlignment, newNestedAlignment);
1192 curNestedAlignment = newNestedAlignment;
1194 if (curNestedAlignment > maxNestedAlignment)
1196 JITDUMP("Max stack nested alignment changed from %d to %d\n", maxNestedAlignment, curNestedAlignment);
1197 maxNestedAlignment = curNestedAlignment;
1204 void genPutArgStkFieldList(GenTreePutArgStk* putArgStk, unsigned outArgVarNum);
1205 #endif // !TARGET_X86
1207 #ifdef FEATURE_PUT_STRUCT_ARG_STK
1209 bool genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk);
1210 void genPushReg(var_types type, regNumber srcReg);
1211 void genPutArgStkFieldList(GenTreePutArgStk* putArgStk);
1212 #endif // TARGET_X86
1214 void genPutStructArgStk(GenTreePutArgStk* treeNode);
1216 unsigned genMove8IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
1217 unsigned genMove4IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
1218 unsigned genMove2IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
1219 unsigned genMove1IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
1220 void genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset);
1221 void genStoreRegToStackArg(var_types type, regNumber reg, int offset);
1222 void genStructPutArgRepMovs(GenTreePutArgStk* putArgStkNode);
1223 void genStructPutArgUnroll(GenTreePutArgStk* putArgStkNode);
1225 void genStructPutArgPush(GenTreePutArgStk* putArgStkNode);
1227 void genStructPutArgPartialRepMovs(GenTreePutArgStk* putArgStkNode);
1229 #endif // FEATURE_PUT_STRUCT_ARG_STK
1231 void genCodeForStoreBlk(GenTreeBlk* storeBlkNode);
1233 void genCodeForInitBlkHelper(GenTreeBlk* initBlkNode);
1235 void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode);
1236 void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode);
1237 void genJumpTable(GenTree* tree);
1238 void genTableBasedSwitch(GenTree* tree);
1239 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1240 instruction genGetInsForOper(GenTree* treeNode);
1242 instruction genGetInsForOper(genTreeOps oper, var_types type);
1244 bool genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarrierForm, GenTree* addr, GenTree* data);
1245 GenTree* getCallTarget(const GenTreeCall* call, CORINFO_METHOD_HANDLE* methHnd);
1246 regNumber getCallIndirectionCellReg(GenTreeCall* call);
1247 void genCall(GenTreeCall* call);
1248 void genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackArgBytes));
1249 void genDefinePendingCallLabel(GenTreeCall* call);
1250 void genJmpMethod(GenTree* jmp);
1251 BasicBlock* genCallFinally(BasicBlock* block);
1252 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1253 // TODO: refactor for LA.
1254 void genCodeForJumpCompare(GenTreeOpCC* tree);
1256 #if defined(TARGET_ARM64)
1257 void genCodeForJumpCompare(GenTreeOpCC* tree);
1258 void genCodeForBfiz(GenTreeOp* tree);
1259 #endif // TARGET_ARM64
1261 #if defined(FEATURE_EH_FUNCLETS)
1262 void genEHCatchRet(BasicBlock* block);
1263 #else // !FEATURE_EH_FUNCLETS
1264 void genEHFinallyOrFilterRet(BasicBlock* block);
1265 #endif // !FEATURE_EH_FUNCLETS
1267 void genMultiRegStoreToSIMDLocal(GenTreeLclVar* lclNode);
1268 void genMultiRegStoreToLocal(GenTreeLclVar* lclNode);
1270 // Codegen for multi-register struct returns.
1271 bool isStructReturn(GenTree* treeNode);
1273 void genSIMDSplitReturn(GenTree* src, ReturnTypeDesc* retTypeDesc);
1275 void genStructReturn(GenTree* treeNode);
1277 #if defined(TARGET_X86) || defined(TARGET_ARM)
1278 void genLongReturn(GenTree* treeNode);
1279 #endif // TARGET_X86 || TARGET_ARM
1281 #if defined(TARGET_X86)
1282 void genFloatReturn(GenTree* treeNode);
1283 #endif // TARGET_X86
1285 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1286 void genSimpleReturn(GenTree* treeNode);
1287 #endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64
1289 void genReturn(GenTree* treeNode);
1292 void genStackPointerConstantAdjustment(ssize_t spDelta, bool trackSpAdjustments);
1293 void genStackPointerConstantAdjustmentWithProbe(ssize_t spDelta, bool trackSpAdjustments);
1294 target_ssize_t genStackPointerConstantAdjustmentLoopWithProbe(ssize_t spDelta, bool trackSpAdjustments);
1295 void genStackPointerDynamicAdjustmentWithProbe(regNumber regSpDelta);
1296 #else // !TARGET_XARCH
1297 void genStackPointerConstantAdjustment(ssize_t spDelta, regNumber regTmp);
1298 void genStackPointerConstantAdjustmentWithProbe(ssize_t spDelta, regNumber regTmp);
1299 target_ssize_t genStackPointerConstantAdjustmentLoopWithProbe(ssize_t spDelta, regNumber regTmp);
1300 #endif // !TARGET_XARCH
1302 void genLclHeap(GenTree* tree);
1303 void genCodeForMemmove(GenTreeBlk* tree);
1305 bool genIsRegCandidateLocal(GenTree* tree)
1307 if (!tree->IsLocal())
1311 return compiler->lvaGetDesc(tree->AsLclVarCommon())->lvIsRegCandidate();
1314 #ifdef FEATURE_PUT_STRUCT_ARG_STK
1317 #else // !TARGET_X86
1318 unsigned m_stkArgVarNum;
1319 unsigned m_stkArgOffset;
1320 #endif // !TARGET_X86
1321 #endif // !FEATURE_PUT_STRUCT_ARG_STK
1323 #if defined(DEBUG) && defined(TARGET_XARCH)
1324 void genStackPointerCheck(bool doStackPointerCheck,
1325 unsigned lvaStackPointerVar,
1327 regNumber regTmp = REG_NA);
1328 #endif // defined(DEBUG) && defined(TARGET_XARCH)
1331 GenTree* lastConsumedNode;
1332 void genNumberOperandUse(GenTree* const operand, int& useNum) const;
1333 void genCheckConsumeNode(GenTree* const node);
1335 inline void genCheckConsumeNode(GenTree* treeNode)
1341 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1342 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1346 XX The interface to generate a machine-instruction. XX
1347 XX Currently specific to x86 XX
1348 XX TODO-Cleanup: Consider factoring this out of CodeGen XX
1350 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1351 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1355 void instGen(instruction ins);
1356 #if defined(TARGET_XARCH)
1357 void inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock, bool isRemovableJmpCandidate = false);
1359 void inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock);
1362 void inst_SET(emitJumpKind condition, regNumber reg);
1364 void inst_RV(instruction ins, regNumber reg, var_types type, emitAttr size = EA_UNKNOWN);
1366 void inst_Mov(var_types dstType,
1370 emitAttr size = EA_UNKNOWN,
1371 insFlags flags = INS_FLAGS_DONT_CARE);
1373 void inst_Mov_Extend(var_types srcType,
1378 emitAttr size = EA_UNKNOWN,
1379 insFlags flags = INS_FLAGS_DONT_CARE);
1381 void inst_RV_RV(instruction ins,
1384 var_types type = TYP_I_IMPL,
1385 emitAttr size = EA_UNKNOWN,
1386 insFlags flags = INS_FLAGS_DONT_CARE);
1388 void inst_RV_RV_RV(instruction ins,
1393 insFlags flags = INS_FLAGS_DONT_CARE);
1395 void inst_IV(instruction ins, cnsval_ssize_t val);
1396 void inst_IV_handle(instruction ins, cnsval_ssize_t val);
1399 instruction ins, regNumber reg, target_ssize_t val, emitAttr size, insFlags flags = INS_FLAGS_DONT_CARE);
1401 void inst_ST_RV(instruction ins, TempDsc* tmp, unsigned ofs, regNumber reg, var_types type);
1403 void inst_FS_ST(instruction ins, emitAttr size, TempDsc* tmp, unsigned ofs);
1405 void inst_TT_RV(instruction ins, emitAttr size, GenTree* tree, regNumber reg);
1407 void inst_RV_SH(instruction ins, emitAttr size, regNumber reg, unsigned val, insFlags flags = INS_FLAGS_DONT_CARE);
1409 #if defined(TARGET_XARCH)
1411 enum class OperandKind{
1412 ClsVar, // [CLS_VAR_ADDR] - "C" in the emitter.
1413 Local, // [Local or spill temp + offset] - "S" in the emitter.
1414 Indir, // [base+index*scale+disp] - "A" in the emitter.
1415 Imm, // immediate - "I" in the emitter.
1416 Reg // reg - "R" in the emitter.
1425 CORINFO_FIELD_HANDLE m_fieldHnd;
1435 GenTreeIndir* m_indir;
1436 var_types m_indirType;
1440 ssize_t m_immediate;
1441 bool m_immediateNeedsReloc;
1450 OperandDesc(CORINFO_FIELD_HANDLE fieldHnd) : m_kind(OperandKind::ClsVar), m_fieldHnd(fieldHnd)
1454 OperandDesc(int varNum, uint16_t offset) : m_kind(OperandKind::Local), m_varNum(varNum), m_offset(offset)
1458 OperandDesc(GenTreeIndir* indir)
1459 : m_kind(OperandKind::Indir), m_addr(indir->Addr()), m_indir(indir), m_indirType(indir->TypeGet())
1463 OperandDesc(var_types indirType, GenTree* addr)
1464 : m_kind(OperandKind::Indir), m_addr(addr), m_indir(nullptr), m_indirType(indirType)
1468 OperandDesc(ssize_t immediate, bool immediateNeedsReloc)
1469 : m_kind(OperandKind::Imm), m_immediate(immediate), m_immediateNeedsReloc(immediateNeedsReloc)
1473 OperandDesc(regNumber reg) : m_kind(OperandKind::Reg), m_reg(reg)
1477 OperandKind GetKind() const
1482 CORINFO_FIELD_HANDLE GetFieldHnd() const
1484 assert(m_kind == OperandKind::ClsVar);
1488 int GetVarNum() const
1490 assert(m_kind == OperandKind::Local);
1494 int GetLclOffset() const
1496 assert(m_kind == OperandKind::Local);
1500 // TODO-Cleanup: instead of this rather unsightly workaround with
1501 // "indirForm", create a new abstraction for address modes to pass
1502 // to the emitter (or at least just use "addr"...).
1503 GenTreeIndir* GetIndirForm(GenTreeIndir* pIndirForm)
1505 if (m_indir == nullptr)
1507 GenTreeIndir indirForm = CodeGen::indirForm(m_indirType, m_addr);
1508 memcpy((void*)pIndirForm, (void*)&indirForm, sizeof(GenTreeIndir));
1512 pIndirForm = m_indir;
1518 ssize_t GetImmediate() const
1520 assert(m_kind == OperandKind::Imm);
1524 emitAttr GetEmitAttrForImmediate(emitAttr baseAttr) const
1526 assert(m_kind == OperandKind::Imm);
1527 return m_immediateNeedsReloc ? EA_SET_FLG(baseAttr, EA_CNS_RELOC_FLG) : baseAttr;
1530 regNumber GetReg() const
1535 bool IsContained() const
1537 return m_kind != OperandKind::Reg;
1541 OperandDesc genOperandDesc(GenTree* op);
1543 void inst_TT(instruction ins, emitAttr size, GenTree* op1);
1544 void inst_RV_TT(instruction ins, emitAttr size, regNumber op1Reg, GenTree* op2);
1545 void inst_RV_RV_IV(instruction ins, emitAttr size, regNumber reg1, regNumber reg2, unsigned ival);
1546 void inst_RV_TT_IV(instruction ins, emitAttr attr, regNumber reg1, GenTree* rmOp, int ival);
1547 void inst_RV_RV_TT(instruction ins, emitAttr size, regNumber targetReg, regNumber op1Reg, GenTree* op2, bool isRMW);
1548 void inst_RV_RV_TT_IV(
1549 instruction ins, emitAttr size, regNumber targetReg, regNumber op1Reg, GenTree* op2, int8_t ival, bool isRMW);
1552 void inst_set_SV_var(GenTree* tree);
1555 bool arm_Valid_Imm_For_Instr(instruction ins, target_ssize_t imm, insFlags flags);
1556 bool arm_Valid_Imm_For_Add(target_ssize_t imm, insFlags flag);
1557 bool arm_Valid_Imm_For_Add_SP(target_ssize_t imm);
1560 instruction ins_Move_Extend(var_types srcType, bool srcInReg);
1562 instruction ins_Copy(var_types dstType);
1563 instruction ins_Copy(regNumber srcReg, var_types dstType);
1564 #if defined(TARGET_XARCH)
1565 instruction ins_FloatConv(var_types to, var_types from, emitAttr attr);
1566 #elif defined(TARGET_ARM)
1567 instruction ins_FloatConv(var_types to, var_types from);
1569 instruction ins_MathOp(genTreeOps oper, var_types type);
1571 void instGen_Return(unsigned stkArgSize);
1575 BARRIER_FULL, // full barrier
1576 BARRIER_LOAD_ONLY, // load barier
1579 void instGen_MemoryBarrier(BarrierKind barrierKind = BARRIER_FULL);
1581 void instGen_Set_Reg_To_Zero(emitAttr size, regNumber reg, insFlags flags = INS_FLAGS_DONT_CARE);
1583 void instGen_Set_Reg_To_Imm(emitAttr size,
1586 insFlags flags = INS_FLAGS_DONT_CARE DEBUGARG(size_t targetHandle = 0)
1587 DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
1590 instruction genMapShiftInsToShiftByConstantIns(instruction ins, int shiftByValue);
1591 #endif // TARGET_XARCH
1593 #if defined(TARGET_ARM64)
1594 static insCond JumpKindToInsCond(emitJumpKind condition);
1595 static insOpts ShiftOpToInsOpts(genTreeOps op);
1596 #elif defined(TARGET_XARCH)
1597 static instruction JumpKindToCmov(emitJumpKind condition);
1600 #if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64)
1601 // Maps a GenCondition code to a sequence of conditional jumps or other conditional instructions
1602 // such as X86's SETcc. A sequence of instructions rather than just a single one is required for
1603 // certain floating point conditions.
1604 // For example, X86's UCOMISS sets ZF to indicate equality but it also sets it, together with PF,
1605 // to indicate an unordered result. So for GenCondition::FEQ we first need to check if PF is 0
1606 // and then jump if ZF is 1:
1607 // JP fallThroughBlock
1609 // fallThroughBlock:
1613 // This is very similar to the way shortcircuit evaluation of bool AND and OR operators works so
1614 // in order to make the GenConditionDesc mapping tables easier to read, a bool expression-like
1615 // pattern is used to encode the above:
1616 // { EJ_jnp, GT_AND, EJ_je }
1617 // { EJ_jp, GT_OR, EJ_jne }
1619 // For more details check inst_JCC and inst_SETCC functions.
1621 struct GenConditionDesc
1623 emitJumpKind jumpKind1;
1625 emitJumpKind jumpKind2;
1628 static const GenConditionDesc& Get(GenCondition condition)
1630 assert(condition.GetCode() < ArrLen(map));
1631 const GenConditionDesc& desc = map[condition.GetCode()];
1632 assert(desc.jumpKind1 != EJ_NONE);
1633 assert((desc.oper == GT_NONE) || (desc.oper == GT_AND) || (desc.oper == GT_OR));
1634 assert((desc.oper == GT_NONE) == (desc.jumpKind2 == EJ_NONE));
1639 static const GenConditionDesc map[32];
1642 void inst_JCC(GenCondition condition, BasicBlock* target);
1643 void inst_SETCC(GenCondition condition, var_types type, regNumber dstReg);
1645 void genCodeForJcc(GenTreeCC* tree);
1646 void genCodeForSetcc(GenTreeCC* setcc);
1647 void genCodeForJTrue(GenTreeOp* jtrue);
1648 #endif // !TARGET_LOONGARCH64 && !TARGET_RISCV64
1651 // A simple phase that just invokes a method on the codegen instance
1653 class CodeGenPhase final : public Phase
1656 CodeGenPhase(CodeGen* _codeGen, Phases _phase, void (CodeGen::*_action)())
1657 : Phase(_codeGen->GetCompiler(), _phase), codeGen(_codeGen), action(_action)
1662 virtual PhaseStatus DoPhase() override
1664 (codeGen->*action)();
1665 return PhaseStatus::MODIFIED_EVERYTHING;
1670 void (CodeGen::*action)();
1673 // Wrapper for using CodeGenPhase
1675 inline void DoPhase(CodeGen* _codeGen, Phases _phase, void (CodeGen::*_action)())
1677 CodeGenPhase phase(_codeGen, _phase, _action);
1681 #endif // _CODEGEN_H_