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 |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX RegSet XX |
9 | XX XX |
10 | XX Represents the register set, and their states during code generation XX |
11 | XX Can select an unused register, keeps track of the contents of the XX |
12 | XX registers, and can spill registers XX |
13 | XX XX |
14 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
15 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
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_ |
28 | const regMaskSmall regMasks[] = { |
29 | #define REGDEF(name, rnum, mask, xname, wname) mask, |
30 | #include "register.h" |
31 | }; |
32 | #else // !_TARGET_ARM64_ |
33 | const regMaskSmall regMasks[] = { |
34 | #define REGDEF(name, rnum, mask, sname) mask, |
35 | #include "register.h" |
36 | }; |
37 | #endif |
38 | |
39 | /* |
40 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
41 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
42 | XX RegSet XX |
43 | XX XX |
44 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
45 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
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 | |
66 | void 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 | |
91 | void 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 | |
108 | void 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 | |
123 | void RegSet::(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 | |
157 | void 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 | |
191 | void 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 | |
218 | RegSet::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 | |
251 | RegSet::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 | // |
294 | void RegSet::(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 | */ |
456 | void 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 | |
496 | TempDsc* 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 | // |
534 | TempDsc* 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 | |
585 | void RegSet::rsMarkSpill(GenTree* tree, regNumber reg) |
586 | { |
587 | tree->gtFlags |= GTF_SPILLED; |
588 | } |
589 | |
590 | /*****************************************************************************/ |
591 | |
592 | /* |
593 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
594 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
595 | XX XX |
596 | XX TempsInfo XX |
597 | XX XX |
598 | XX The temporary lclVars allocated by the compiler for code generation XX |
599 | XX XX |
600 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
601 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
602 | */ |
603 | |
604 | void 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 */ |
617 | var_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 | |
641 | TempDsc* 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 | |
703 | void 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 | |
751 | void 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 | */ |
801 | TempDsc* 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 | |
821 | TempDsc* 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 | |
848 | TempDsc* 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 | */ |
887 | bool 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 | /* |
917 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
918 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
919 | XX XX |
920 | XX Register-related utility functions XX |
921 | XX XX |
922 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
923 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
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 | |
936 | regNumber 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 | |
978 | const regNumber raRegCalleeSaveOrder[] = {REG_CALLEE_SAVED_ORDER}; |
979 | const regMaskTP raRbmCalleeSaveOrder[] = {RBM_CALLEE_SAVED_ORDER}; |
980 | |
981 | regMaskSmall 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 |
1000 | void RegSet::() |
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 |
1019 | void RegSet::() |
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 |
1031 | void RegSet::() |
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 |
1043 | void RegSet::() |
1044 | { |
1045 | rsSpillChk(); |
1046 | } |
1047 | |
1048 | //**************************************************************************** |
1049 | // Create a new SpillDsc or get one off the free list |
1050 | // |
1051 | |
1052 | // inline |
1053 | RegSet::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 |
1078 | void 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 |
1093 | void RegSet::() |
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 |
1107 | void RegSet::rsSpillChk() |
1108 | { |
1109 | } |
1110 | |
1111 | #endif |
1112 | |