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 | |