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/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX RegSet XX
9XX XX
10XX Represents the register set, and their states during code generation XX
11XX Can select an unused register, keeps track of the contents of the XX
12XX registers, and can spill registers XX
13XX XX
14XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16*/
17
18#include "jitpch.h"
19#ifdef _MSC_VER
20#pragma hdrstop
21#endif
22
23#include "emit.h"
24
25/*****************************************************************************/
26
27#ifdef _TARGET_ARM64_
28const regMaskSmall regMasks[] = {
29#define REGDEF(name, rnum, mask, xname, wname) mask,
30#include "register.h"
31};
32#else // !_TARGET_ARM64_
33const regMaskSmall regMasks[] = {
34#define REGDEF(name, rnum, mask, sname) mask,
35#include "register.h"
36};
37#endif
38
39/*
40XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
41XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
42XX RegSet XX
43XX XX
44XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
45XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
46*/
47
48//------------------------------------------------------------------------
49// verifyRegUsed: verify that the register is marked as used.
50//
51// Arguments:
52// reg - The register to verify.
53//
54// Return Value:
55// None.
56//
57// Assumptions:
58// The caller must have ensured that the register is already marked
59// as used.
60//
61// Notes:
62// This method is intended to be called during code generation, and
63// should simply validate that the register (or registers) have
64// already been added to the modified set.
65
66void RegSet::verifyRegUsed(regNumber reg)
67{
68 // TODO-Cleanup: we need to identify the places where the register
69 // is not marked as used when this is called.
70 rsSetRegsModified(genRegMask(reg));
71}
72
73//------------------------------------------------------------------------
74// verifyRegistersUsed: verify that the registers are marked as used.
75//
76// Arguments:
77// regs - The registers to verify.
78//
79// Return Value:
80// None.
81//
82// Assumptions:
83// The caller must have ensured that the registers are already marked
84// as used.
85//
86// Notes:
87// This method is intended to be called during code generation, and
88// should simply validate that the register (or registers) have
89// already been added to the modified set.
90
91void RegSet::verifyRegistersUsed(regMaskTP regMask)
92{
93 if (m_rsCompiler->opts.OptimizationDisabled())
94 {
95 return;
96 }
97
98 if (regMask == RBM_NONE)
99 {
100 return;
101 }
102
103 // TODO-Cleanup: we need to identify the places where the registers
104 // are not marked as used when this is called.
105 rsSetRegsModified(regMask);
106}
107
108void RegSet::rsClearRegsModified()
109{
110 assert(m_rsCompiler->lvaDoneFrameLayout < Compiler::FINAL_FRAME_LAYOUT);
111
112#ifdef DEBUG
113 if (m_rsCompiler->verbose)
114 {
115 printf("Clearing modified regs.\n");
116 }
117 rsModifiedRegsMaskInitialized = true;
118#endif // DEBUG
119
120 rsModifiedRegsMask = RBM_NONE;
121}
122
123void RegSet::rsSetRegsModified(regMaskTP mask DEBUGARG(bool suppressDump))
124{
125 assert(mask != RBM_NONE);
126 assert(rsModifiedRegsMaskInitialized);
127
128 // We can't update the modified registers set after final frame layout (that is, during code
129 // generation and after). Ignore prolog and epilog generation: they call register tracking to
130 // modify rbp, for example, even in functions that use rbp as a frame pointer. Make sure normal
131 // code generation isn't actually adding to set of modified registers.
132 // Frame layout is only affected by callee-saved registers, so only ensure that callee-saved
133 // registers aren't modified after final frame layout.
134 assert((m_rsCompiler->lvaDoneFrameLayout < Compiler::FINAL_FRAME_LAYOUT) || m_rsCompiler->compGeneratingProlog ||
135 m_rsCompiler->compGeneratingEpilog ||
136 (((rsModifiedRegsMask | mask) & RBM_CALLEE_SAVED) == (rsModifiedRegsMask & RBM_CALLEE_SAVED)));
137
138#ifdef DEBUG
139 if (m_rsCompiler->verbose && !suppressDump)
140 {
141 if (rsModifiedRegsMask != (rsModifiedRegsMask | mask))
142 {
143 printf("Marking regs modified: ");
144 dspRegMask(mask);
145 printf(" (");
146 dspRegMask(rsModifiedRegsMask);
147 printf(" => ");
148 dspRegMask(rsModifiedRegsMask | mask);
149 printf(")\n");
150 }
151 }
152#endif // DEBUG
153
154 rsModifiedRegsMask |= mask;
155}
156
157void RegSet::rsRemoveRegsModified(regMaskTP mask)
158{
159 assert(mask != RBM_NONE);
160 assert(rsModifiedRegsMaskInitialized);
161
162 // See comment in rsSetRegsModified().
163 assert((m_rsCompiler->lvaDoneFrameLayout < Compiler::FINAL_FRAME_LAYOUT) || m_rsCompiler->compGeneratingProlog ||
164 m_rsCompiler->compGeneratingEpilog ||
165 (((rsModifiedRegsMask & ~mask) & RBM_CALLEE_SAVED) == (rsModifiedRegsMask & RBM_CALLEE_SAVED)));
166
167#ifdef DEBUG
168 if (m_rsCompiler->verbose)
169 {
170 printf("Removing modified regs: ");
171 dspRegMask(mask);
172 if (rsModifiedRegsMask == (rsModifiedRegsMask & ~mask))
173 {
174 printf(" (unchanged)");
175 }
176 else
177 {
178 printf(" (");
179 dspRegMask(rsModifiedRegsMask);
180 printf(" => ");
181 dspRegMask(rsModifiedRegsMask & ~mask);
182 printf(")");
183 }
184 printf("\n");
185 }
186#endif // DEBUG
187
188 rsModifiedRegsMask &= ~mask;
189}
190
191void RegSet::SetMaskVars(regMaskTP newMaskVars)
192{
193#ifdef DEBUG
194 if (m_rsCompiler->verbose)
195 {
196 printf("\t\t\t\t\t\t\tLive regs: ");
197 if (_rsMaskVars == newMaskVars)
198 {
199 printf("(unchanged) ");
200 }
201 else
202 {
203 printRegMaskInt(_rsMaskVars);
204 m_rsCompiler->getEmitter()->emitDispRegSet(_rsMaskVars);
205 printf(" => ");
206 }
207 printRegMaskInt(newMaskVars);
208 m_rsCompiler->getEmitter()->emitDispRegSet(newMaskVars);
209 printf("\n");
210 }
211#endif // DEBUG
212
213 _rsMaskVars = newMaskVars;
214}
215
216/*****************************************************************************/
217
218RegSet::RegSet(Compiler* compiler, GCInfo& gcInfo) : m_rsCompiler(compiler), m_rsGCInfo(gcInfo)
219{
220 /* Initialize the spill logic */
221
222 rsSpillInit();
223
224 /* Initialize the argument register count */
225 // TODO-Cleanup: Consider moving intRegState and floatRegState to RegSet. They used
226 // to be initialized here, but are now initialized in the CodeGen constructor.
227 // intRegState.rsCurRegArgNum = 0;
228 // loatRegState.rsCurRegArgNum = 0;
229
230 rsMaskResvd = RBM_NONE;
231
232#ifdef _TARGET_ARMARCH_
233 rsMaskCalleeSaved = RBM_NONE;
234#endif // _TARGET_ARMARCH_
235
236#ifdef _TARGET_ARM_
237 rsMaskPreSpillRegArg = RBM_NONE;
238 rsMaskPreSpillAlign = RBM_NONE;
239#endif
240
241#ifdef DEBUG
242 rsModifiedRegsMaskInitialized = false;
243#endif // DEBUG
244}
245
246/*****************************************************************************
247 *
248 * Finds the SpillDsc corresponding to 'tree' assuming it was spilled from 'reg'.
249 */
250
251RegSet::SpillDsc* RegSet::rsGetSpillInfo(GenTree* tree, regNumber reg, SpillDsc** pPrevDsc)
252{
253 /* Normally, trees are unspilled in the order of being spilled due to
254 the post-order walking of trees during code-gen. However, this will
255 not be true for something like a GT_ARR_ELEM node */
256
257 SpillDsc* prev;
258 SpillDsc* dsc;
259 for (prev = nullptr, dsc = rsSpillDesc[reg]; dsc != nullptr; prev = dsc, dsc = dsc->spillNext)
260 {
261 if (dsc->spillTree == tree)
262 {
263 break;
264 }
265 }
266
267 if (pPrevDsc)
268 {
269 *pPrevDsc = prev;
270 }
271
272 return dsc;
273}
274
275//------------------------------------------------------------
276// rsSpillTree: Spill the tree held in 'reg'.
277//
278// Arguments:
279// reg - Register of tree node that is to be spilled
280// tree - GenTree node that is being spilled
281// regIdx - Register index identifying the specific result
282// register of a multi-reg call node. For single-reg
283// producing tree nodes its value is zero.
284//
285// Return Value:
286// None.
287//
288// Assumption:
289// RyuJIT backend specific: in case of multi-reg call nodes, GTF_SPILL
290// flag associated with the reg that is being spilled is cleared. The
291// caller of this method is expected to clear GTF_SPILL flag on call
292// node after all of its registers marked for spilling are spilled.
293//
294void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
295{
296 assert(tree != nullptr);
297
298 GenTreeCall* call = nullptr;
299 var_types treeType;
300#if defined(_TARGET_ARM_)
301 GenTreePutArgSplit* splitArg = nullptr;
302 GenTreeMultiRegOp* multiReg = nullptr;
303#endif
304
305 if (tree->IsMultiRegCall())
306 {
307 call = tree->AsCall();
308 ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
309 treeType = retTypeDesc->GetReturnRegType(regIdx);
310 }
311#ifdef _TARGET_ARM_
312 else if (tree->OperIsPutArgSplit())
313 {
314 splitArg = tree->AsPutArgSplit();
315 treeType = splitArg->GetRegType(regIdx);
316 }
317 else if (tree->OperIsMultiRegOp())
318 {
319 multiReg = tree->AsMultiRegOp();
320 treeType = multiReg->GetRegType(regIdx);
321 }
322#endif // _TARGET_ARM_
323 else
324 {
325 treeType = tree->TypeGet();
326 }
327
328 var_types tempType = RegSet::tmpNormalizeType(treeType);
329 regMaskTP mask;
330 bool floatSpill = false;
331
332 if (isFloatRegType(treeType))
333 {
334 floatSpill = true;
335 mask = genRegMaskFloat(reg, treeType);
336 }
337 else
338 {
339 mask = genRegMask(reg);
340 }
341
342 rsNeededSpillReg = true;
343
344 // We should only be spilling nodes marked for spill,
345 // vars should be handled elsewhere, and to prevent
346 // spilling twice clear GTF_SPILL flag on tree node.
347 //
348 // In case of multi-reg call nodes only the spill flag
349 // associated with the reg is cleared. Spill flag on
350 // call node should be cleared by the caller of this method.
351 assert((tree->gtFlags & GTF_SPILL) != 0);
352
353 unsigned regFlags = 0;
354 if (call != nullptr)
355 {
356 regFlags = call->GetRegSpillFlagByIdx(regIdx);
357 assert((regFlags & GTF_SPILL) != 0);
358 regFlags &= ~GTF_SPILL;
359 }
360#ifdef _TARGET_ARM_
361 else if (splitArg != nullptr)
362 {
363 regFlags = splitArg->GetRegSpillFlagByIdx(regIdx);
364 assert((regFlags & GTF_SPILL) != 0);
365 regFlags &= ~GTF_SPILL;
366 }
367 else if (multiReg != nullptr)
368 {
369 regFlags = multiReg->GetRegSpillFlagByIdx(regIdx);
370 assert((regFlags & GTF_SPILL) != 0);
371 regFlags &= ~GTF_SPILL;
372 }
373#endif // _TARGET_ARM_
374 else
375 {
376 assert(!varTypeIsMultiReg(tree));
377 tree->gtFlags &= ~GTF_SPILL;
378 }
379
380#if defined(_TARGET_ARM_)
381 assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg) ||
382 (splitArg != nullptr && splitArg->GetRegNumByIdx(regIdx) == reg) ||
383 (multiReg != nullptr && multiReg->GetRegNumByIdx(regIdx) == reg));
384#else
385 assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg));
386#endif // !_TARGET_ARM_
387
388 // Are any registers free for spillage?
389 SpillDsc* spill = SpillDsc::alloc(m_rsCompiler, this, tempType);
390
391 // Grab a temp to store the spilled value
392 TempDsc* temp = tmpGetTemp(tempType);
393 spill->spillTemp = temp;
394 tempType = temp->tdTempType();
395
396 // Remember what it is we have spilled
397 spill->spillTree = tree;
398
399#ifdef DEBUG
400 if (m_rsCompiler->verbose)
401 {
402 printf("\t\t\t\t\t\t\tThe register %s spilled with ", m_rsCompiler->compRegVarName(reg));
403 Compiler::printTreeID(spill->spillTree);
404 }
405#endif
406
407 // 'lastDsc' is 'spill' for simple cases, and will point to the last
408 // multi-use descriptor if 'reg' is being multi-used
409 SpillDsc* lastDsc = spill;
410
411 // Insert the spill descriptor(s) in the list
412 lastDsc->spillNext = rsSpillDesc[reg];
413 rsSpillDesc[reg] = spill;
414
415#ifdef DEBUG
416 if (m_rsCompiler->verbose)
417 {
418 printf("\n");
419 }
420#endif
421
422 // Generate the code to spill the register
423 var_types storeType = floatSpill ? treeType : tempType;
424
425 m_rsCompiler->codeGen->spillReg(storeType, temp, reg);
426
427 // Mark the tree node as having been spilled
428 rsMarkSpill(tree, reg);
429
430 // In case of multi-reg call node also mark the specific
431 // result reg as spilled.
432 if (call != nullptr)
433 {
434 regFlags |= GTF_SPILLED;
435 call->SetRegSpillFlagByIdx(regFlags, regIdx);
436 }
437#ifdef _TARGET_ARM_
438 else if (splitArg != nullptr)
439 {
440 regFlags |= GTF_SPILLED;
441 splitArg->SetRegSpillFlagByIdx(regFlags, regIdx);
442 }
443 else if (multiReg != nullptr)
444 {
445 regFlags |= GTF_SPILLED;
446 multiReg->SetRegSpillFlagByIdx(regFlags, regIdx);
447 }
448#endif // _TARGET_ARM_
449}
450
451#if defined(_TARGET_X86_)
452/*****************************************************************************
453*
454* Spill the top of the FP x87 stack.
455*/
456void RegSet::rsSpillFPStack(GenTreeCall* call)
457{
458 SpillDsc* spill;
459 TempDsc* temp;
460 var_types treeType = call->TypeGet();
461
462 spill = SpillDsc::alloc(m_rsCompiler, this, treeType);
463
464 /* Grab a temp to store the spilled value */
465
466 spill->spillTemp = temp = tmpGetTemp(treeType);
467
468 /* Remember what it is we have spilled */
469
470 spill->spillTree = call;
471 SpillDsc* lastDsc = spill;
472
473 regNumber reg = call->gtRegNum;
474 lastDsc->spillNext = rsSpillDesc[reg];
475 rsSpillDesc[reg] = spill;
476
477#ifdef DEBUG
478 if (m_rsCompiler->verbose)
479 printf("\n");
480#endif
481
482 m_rsCompiler->codeGen->getEmitter()->emitIns_S(INS_fstp, emitActualTypeSize(treeType), temp->tdTempNum(), 0);
483
484 /* Mark the tree node as having been spilled */
485
486 rsMarkSpill(call, reg);
487}
488#endif // defined(_TARGET_X86_)
489
490/*****************************************************************************
491 *
492 * Get the temp that was spilled from the given register (and free its
493 * spill descriptor while we're at it). Returns the temp (i.e. local var)
494 */
495
496TempDsc* RegSet::rsGetSpillTempWord(regNumber reg, SpillDsc* dsc, SpillDsc* prevDsc)
497{
498 assert((prevDsc == nullptr) || (prevDsc->spillNext == dsc));
499
500 /* Remove this spill entry from the register's list */
501
502 (prevDsc ? prevDsc->spillNext : rsSpillDesc[reg]) = dsc->spillNext;
503
504 /* Remember which temp the value is in */
505
506 TempDsc* temp = dsc->spillTemp;
507
508 SpillDsc::freeDsc(this, dsc);
509
510 /* return the temp variable */
511
512 return temp;
513}
514
515//---------------------------------------------------------------------
516// rsUnspillInPlace: The given tree operand has been spilled; just mark
517// it as unspilled so that we can use it as "normal" local.
518//
519// Arguments:
520// tree - GenTree that needs to be marked as unspilled.
521// oldReg - reg of tree that was spilled.
522//
523// Return Value:
524// None.
525//
526// Assumptions:
527// 1. It is the responsibility of the caller to free the spill temp.
528// 2. RyuJIT backend specific: In case of multi-reg call node
529// GTF_SPILLED flag associated with reg is cleared. It is the
530// responsibility of caller to clear GTF_SPILLED flag on call node
531// itself after ensuring there are no outstanding regs in GTF_SPILLED
532// state.
533//
534TempDsc* RegSet::rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regIdx /* =0 */)
535{
536 // Get the tree's SpillDsc
537 SpillDsc* prevDsc;
538 SpillDsc* spillDsc = rsGetSpillInfo(tree, oldReg, &prevDsc);
539 PREFIX_ASSUME(spillDsc != nullptr);
540
541 // Get the temp
542 TempDsc* temp = rsGetSpillTempWord(oldReg, spillDsc, prevDsc);
543
544 // The value is now unspilled
545 if (tree->IsMultiRegCall())
546 {
547 GenTreeCall* call = tree->AsCall();
548 unsigned flags = call->GetRegSpillFlagByIdx(regIdx);
549 flags &= ~GTF_SPILLED;
550 call->SetRegSpillFlagByIdx(flags, regIdx);
551 }
552#if defined(_TARGET_ARM_)
553 else if (tree->OperIsPutArgSplit())
554 {
555 GenTreePutArgSplit* splitArg = tree->AsPutArgSplit();
556 unsigned flags = splitArg->GetRegSpillFlagByIdx(regIdx);
557 flags &= ~GTF_SPILLED;
558 splitArg->SetRegSpillFlagByIdx(flags, regIdx);
559 }
560 else if (tree->OperIsMultiRegOp())
561 {
562 GenTreeMultiRegOp* multiReg = tree->AsMultiRegOp();
563 unsigned flags = multiReg->GetRegSpillFlagByIdx(regIdx);
564 flags &= ~GTF_SPILLED;
565 multiReg->SetRegSpillFlagByIdx(flags, regIdx);
566 }
567#endif // _TARGET_ARM_
568 else
569 {
570 tree->gtFlags &= ~GTF_SPILLED;
571 }
572
573#ifdef DEBUG
574 if (m_rsCompiler->verbose)
575 {
576 printf("\t\t\t\t\t\t\tTree-Node marked unspilled from ");
577 Compiler::printTreeID(tree);
578 printf("\n");
579 }
580#endif
581
582 return temp;
583}
584
585void RegSet::rsMarkSpill(GenTree* tree, regNumber reg)
586{
587 tree->gtFlags |= GTF_SPILLED;
588}
589
590/*****************************************************************************/
591
592/*
593XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
594XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
595XX XX
596XX TempsInfo XX
597XX XX
598XX The temporary lclVars allocated by the compiler for code generation XX
599XX XX
600XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
601XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
602*/
603
604void RegSet::tmpInit()
605{
606 tmpCount = 0;
607 tmpSize = 0;
608#ifdef DEBUG
609 tmpGetCount = 0;
610#endif
611
612 memset(tmpFree, 0, sizeof(tmpFree));
613 memset(tmpUsed, 0, sizeof(tmpUsed));
614}
615
616/* static */
617var_types RegSet::tmpNormalizeType(var_types type)
618{
619 type = genActualType(type);
620
621#if defined(FEATURE_SIMD)
622 // We always spill SIMD12 to a 16-byte SIMD16 temp.
623 // This is because we don't have a single instruction to store 12 bytes, so we want
624 // to ensure that we always have the full 16 bytes for loading & storing the value.
625 // We also allocate non-argument locals as 16 bytes; see lvSize().
626 if (type == TYP_SIMD12)
627 {
628 type = TYP_SIMD16;
629 }
630#endif // defined(FEATURE_SIMD) && !defined(_TARGET_64BIT_)
631
632 return type;
633}
634
635/*****************************************************************************
636 *
637 * Allocate a temp of the given size (and type, if tracking pointers for
638 * the garbage collector).
639 */
640
641TempDsc* RegSet::tmpGetTemp(var_types type)
642{
643 type = tmpNormalizeType(type);
644 unsigned size = genTypeSize(type);
645
646 // If TYP_STRUCT ever gets in here we do bad things (tmpSlot returns -1)
647 noway_assert(size >= sizeof(int));
648
649 /* Find the slot to search for a free temp of the right size */
650
651 unsigned slot = tmpSlot(size);
652
653 /* Look for a temp with a matching type */
654
655 TempDsc** last = &tmpFree[slot];
656 TempDsc* temp;
657
658 for (temp = *last; temp; last = &temp->tdNext, temp = *last)
659 {
660 /* Does the type match? */
661
662 if (temp->tdTempType() == type)
663 {
664 /* We have a match -- remove it from the free list */
665
666 *last = temp->tdNext;
667 break;
668 }
669 }
670
671#ifdef DEBUG
672 /* Do we need to allocate a new temp */
673 bool isNewTemp = false;
674#endif // DEBUG
675
676 noway_assert(temp != nullptr);
677
678#ifdef DEBUG
679 if (m_rsCompiler->verbose)
680 {
681 printf("%s temp #%u, slot %u, size = %u\n", isNewTemp ? "created" : "reused", -temp->tdTempNum(), slot,
682 temp->tdTempSize());
683 }
684 tmpGetCount++;
685#endif // DEBUG
686
687 temp->tdNext = tmpUsed[slot];
688 tmpUsed[slot] = temp;
689
690 return temp;
691}
692
693/*****************************************************************************
694 * Preallocate 'count' temps of type 'type'. This type must be a normalized
695 * type (by the definition of tmpNormalizeType()).
696 *
697 * This is used at the end of LSRA, which knows precisely the maximum concurrent
698 * number of each type of spill temp needed, before code generation. Code generation
699 * then uses these preallocated temp. If code generation ever asks for more than
700 * has been preallocated, it is a fatal error.
701 */
702
703void RegSet::tmpPreAllocateTemps(var_types type, unsigned count)
704{
705 assert(type == tmpNormalizeType(type));
706 unsigned size = genTypeSize(type);
707
708 // If TYP_STRUCT ever gets in here we do bad things (tmpSlot returns -1)
709 noway_assert(size >= sizeof(int));
710
711 // Find the slot to search for a free temp of the right size.
712 // Note that slots are shared by types of the identical size (e.g., TYP_REF and TYP_LONG on AMD64),
713 // so we can't assert that the slot is empty when we get here.
714
715 unsigned slot = tmpSlot(size);
716
717 for (unsigned i = 0; i < count; i++)
718 {
719 tmpCount++;
720 tmpSize += size;
721
722#ifdef _TARGET_ARM_
723 if (type == TYP_DOUBLE)
724 {
725 // Adjust tmpSize to accommodate possible alignment padding.
726 // Note that at this point the offsets aren't yet finalized, so we don't yet know if it will be required.
727 tmpSize += TARGET_POINTER_SIZE;
728 }
729#endif // _TARGET_ARM_
730
731 TempDsc* temp = new (m_rsCompiler, CMK_Unknown) TempDsc(-((int)tmpCount), size, type);
732
733#ifdef DEBUG
734 if (m_rsCompiler->verbose)
735 {
736 printf("pre-allocated temp #%u, slot %u, size = %u\n", -temp->tdTempNum(), slot, temp->tdTempSize());
737 }
738#endif // DEBUG
739
740 // Add it to the front of the appropriate slot list.
741 temp->tdNext = tmpFree[slot];
742 tmpFree[slot] = temp;
743 }
744}
745
746/*****************************************************************************
747 *
748 * Release the given temp.
749 */
750
751void RegSet::tmpRlsTemp(TempDsc* temp)
752{
753 assert(temp != nullptr);
754
755 unsigned slot;
756
757 /* Add the temp to the 'free' list */
758
759 slot = tmpSlot(temp->tdTempSize());
760
761#ifdef DEBUG
762 if (m_rsCompiler->verbose)
763 {
764 printf("release temp #%u, slot %u, size = %u\n", -temp->tdTempNum(), slot, temp->tdTempSize());
765 }
766 assert(tmpGetCount);
767 tmpGetCount--;
768#endif
769
770 // Remove it from the 'used' list.
771
772 TempDsc** last = &tmpUsed[slot];
773 TempDsc* t;
774 for (t = *last; t != nullptr; last = &t->tdNext, t = *last)
775 {
776 if (t == temp)
777 {
778 /* Found it! -- remove it from the 'used' list */
779
780 *last = t->tdNext;
781 break;
782 }
783 }
784 assert(t != nullptr); // We better have found it!
785
786 // Add it to the free list.
787
788 temp->tdNext = tmpFree[slot];
789 tmpFree[slot] = temp;
790}
791
792/*****************************************************************************
793 * Given a temp number, find the corresponding temp.
794 *
795 * When looking for temps on the "free" list, this can only be used after code generation. (This is
796 * simply because we have an assert to that effect in tmpListBeg(); we could relax that, or hoist
797 * the assert to the appropriate callers.)
798 *
799 * When looking for temps on the "used" list, this can be used any time.
800 */
801TempDsc* RegSet::tmpFindNum(int tnum, TEMP_USAGE_TYPE usageType /* = TEMP_USAGE_FREE */) const
802{
803 assert(tnum < 0); // temp numbers are negative
804
805 for (TempDsc* temp = tmpListBeg(usageType); temp != nullptr; temp = tmpListNxt(temp, usageType))
806 {
807 if (temp->tdTempNum() == tnum)
808 {
809 return temp;
810 }
811 }
812
813 return nullptr;
814}
815
816/*****************************************************************************
817 *
818 * A helper function is used to iterate over all the temps.
819 */
820
821TempDsc* RegSet::tmpListBeg(TEMP_USAGE_TYPE usageType /* = TEMP_USAGE_FREE */) const
822{
823 TempDsc* const* tmpLists;
824 if (usageType == TEMP_USAGE_FREE)
825 {
826 tmpLists = tmpFree;
827 }
828 else
829 {
830 tmpLists = tmpUsed;
831 }
832
833 // Return the first temp in the slot for the smallest size
834 unsigned slot = 0;
835 while (slot < (TEMP_SLOT_COUNT - 1) && tmpLists[slot] == nullptr)
836 {
837 slot++;
838 }
839 TempDsc* temp = tmpLists[slot];
840
841 return temp;
842}
843
844/*****************************************************************************
845 * Used with tmpListBeg() to iterate over the list of temps.
846 */
847
848TempDsc* RegSet::tmpListNxt(TempDsc* curTemp, TEMP_USAGE_TYPE usageType /* = TEMP_USAGE_FREE */) const
849{
850 assert(curTemp != nullptr);
851
852 TempDsc* temp = curTemp->tdNext;
853 if (temp == nullptr)
854 {
855 unsigned size = curTemp->tdTempSize();
856
857 // If there are no more temps in the list, check if there are more
858 // slots (for bigger sized temps) to walk.
859
860 TempDsc* const* tmpLists;
861 if (usageType == TEMP_USAGE_FREE)
862 {
863 tmpLists = tmpFree;
864 }
865 else
866 {
867 tmpLists = tmpUsed;
868 }
869
870 while (size < TEMP_MAX_SIZE && temp == nullptr)
871 {
872 size += sizeof(int);
873 unsigned slot = tmpSlot(size);
874 temp = tmpLists[slot];
875 }
876
877 assert((temp == nullptr) || (temp->tdTempSize() == size));
878 }
879
880 return temp;
881}
882
883#ifdef DEBUG
884/*****************************************************************************
885 * Return 'true' if all allocated temps are free (not in use).
886 */
887bool RegSet::tmpAllFree() const
888{
889 // The 'tmpGetCount' should equal the number of things in the 'tmpUsed' lists. This is a convenient place
890 // to assert that.
891 unsigned usedCount = 0;
892 for (TempDsc* temp = tmpListBeg(TEMP_USAGE_USED); temp != nullptr; temp = tmpListNxt(temp, TEMP_USAGE_USED))
893 {
894 ++usedCount;
895 }
896 assert(usedCount == tmpGetCount);
897
898 if (tmpGetCount != 0)
899 {
900 return false;
901 }
902
903 for (unsigned i = 0; i < _countof(tmpUsed); i++)
904 {
905 if (tmpUsed[i] != nullptr)
906 {
907 return false;
908 }
909 }
910
911 return true;
912}
913
914#endif // DEBUG
915
916/*
917XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
918XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
919XX XX
920XX Register-related utility functions XX
921XX XX
922XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
923XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
924*/
925
926/*****************************************************************************
927 *
928 * Given a register that is an argument register
929 * returns the next argument register
930 *
931 * Note: that this method will return a non arg register
932 * when given REG_ARG_LAST
933 *
934 */
935
936regNumber genRegArgNext(regNumber argReg)
937{
938 assert(isValidIntArgReg(argReg) || isValidFloatArgReg(argReg));
939
940 switch (argReg)
941 {
942
943#ifdef _TARGET_AMD64_
944#ifdef UNIX_AMD64_ABI
945
946 // Linux x64 ABI: REG_RDI, REG_RSI, REG_RDX, REG_RCX, REG_R8, REG_R9
947 case REG_ARG_0: // REG_RDI
948 return REG_ARG_1; // REG_RSI
949 case REG_ARG_1: // REG_RSI
950 return REG_ARG_2; // REG_RDX
951 case REG_ARG_2: // REG_RDX
952 return REG_ARG_3; // REG_RCX
953 case REG_ARG_3: // REG_RCX
954 return REG_ARG_4; // REG_R8
955
956#else // !UNIX_AMD64_ABI
957
958 // Windows x64 ABI: REG_RCX, REG_RDX, REG_R8, REG_R9
959 case REG_ARG_1: // REG_RDX
960 return REG_ARG_2; // REG_R8
961
962#endif // !UNIX_AMD64_ABI
963#endif // _TARGET_AMD64_
964
965 default:
966 return REG_NEXT(argReg);
967 }
968}
969
970/*****************************************************************************
971 *
972 * The following table determines the order in which callee-saved registers
973 * are encoded in GC information at call sites (perhaps among other things).
974 * In any case, they establish a mapping from ordinal callee-save reg "indices" to
975 * register numbers and corresponding bitmaps.
976 */
977
978const regNumber raRegCalleeSaveOrder[] = {REG_CALLEE_SAVED_ORDER};
979const regMaskTP raRbmCalleeSaveOrder[] = {RBM_CALLEE_SAVED_ORDER};
980
981regMaskSmall genRegMaskFromCalleeSavedMask(unsigned short calleeSaveMask)
982{
983 regMaskSmall res = 0;
984 for (int i = 0; i < CNT_CALLEE_SAVED; i++)
985 {
986 if ((calleeSaveMask & ((regMaskTP)1 << i)) != 0)
987 {
988 res |= raRbmCalleeSaveOrder[i];
989 }
990 }
991 return res;
992}
993
994/*****************************************************************************
995 *
996 * Initializes the spill code. Should be called once per function compiled.
997 */
998
999// inline
1000void RegSet::rsSpillInit()
1001{
1002 /* Clear out the spill and multi-use tables */
1003
1004 memset(rsSpillDesc, 0, sizeof(rsSpillDesc));
1005
1006 rsNeededSpillReg = false;
1007
1008 /* We don't have any descriptors allocated */
1009
1010 rsSpillFree = nullptr;
1011}
1012
1013/*****************************************************************************
1014 *
1015 * Shuts down the spill code. Should be called once per function compiled.
1016 */
1017
1018// inline
1019void RegSet::rsSpillDone()
1020{
1021 rsSpillChk();
1022}
1023
1024/*****************************************************************************
1025 *
1026 * Begin tracking spills - should be called each time before a pass is made
1027 * over a function body.
1028 */
1029
1030// inline
1031void RegSet::rsSpillBeg()
1032{
1033 rsSpillChk();
1034}
1035
1036/*****************************************************************************
1037 *
1038 * Finish tracking spills - should be called each time after a pass is made
1039 * over a function body.
1040 */
1041
1042// inline
1043void RegSet::rsSpillEnd()
1044{
1045 rsSpillChk();
1046}
1047
1048//****************************************************************************
1049// Create a new SpillDsc or get one off the free list
1050//
1051
1052// inline
1053RegSet::SpillDsc* RegSet::SpillDsc::alloc(Compiler* pComp, RegSet* regSet, var_types type)
1054{
1055 RegSet::SpillDsc* spill;
1056 RegSet::SpillDsc** pSpill;
1057
1058 pSpill = &(regSet->rsSpillFree);
1059
1060 // Allocate spill structure
1061 if (*pSpill)
1062 {
1063 spill = *pSpill;
1064 *pSpill = spill->spillNext;
1065 }
1066 else
1067 {
1068 spill = pComp->getAllocator().allocate<SpillDsc>(1);
1069 }
1070 return spill;
1071}
1072
1073//****************************************************************************
1074// Free a SpillDsc and return it to the rsSpillFree list
1075//
1076
1077// inline
1078void RegSet::SpillDsc::freeDsc(RegSet* regSet, RegSet::SpillDsc* spillDsc)
1079{
1080 spillDsc->spillNext = regSet->rsSpillFree;
1081 regSet->rsSpillFree = spillDsc;
1082}
1083
1084/*****************************************************************************
1085 *
1086 * Make sure no spills are currently active - used for debugging of the code
1087 * generator.
1088 */
1089
1090#ifdef DEBUG
1091
1092// inline
1093void RegSet::rsSpillChk()
1094{
1095 // All grabbed temps should have been released
1096 assert(tmpGetCount == 0);
1097
1098 for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg))
1099 {
1100 assert(rsSpillDesc[reg] == nullptr);
1101 }
1102}
1103
1104#else
1105
1106// inline
1107void RegSet::rsSpillChk()
1108{
1109}
1110
1111#endif
1112