ACSE 2.0.3
Advanced Compiler System for Education
Loading...
Searching...
No Matches
reg_alloc.c
Go to the documentation of this file.
1
3
4#include <assert.h>
5#include <stdbool.h>
6#include <string.h>
7#include <stdlib.h>
8#include "reg_alloc.h"
9#include "target_info.h"
10#include "errors.h"
11#include "codegen.h"
12#include "program.h"
13#include "list.h"
14#include "cfg.h"
15#include "target_asm_print.h"
16
18#define MAX_INSTR_ARGS (CFG_MAX_DEFS + CFG_MAX_USES)
19
21#define RA_SPILL_REQUIRED ((t_regID)(-2))
23#define RA_REGISTER_INVALID ((t_regID)(-1))
24
25
38
47
66
78
88
95
96
97static t_liveInterval *newLiveInterval(
98 t_regID tempRegID, t_listNode *mcRegs, int startPoint, int endPoint)
99{
100 t_liveInterval *result = malloc(sizeof(t_liveInterval));
101 if (result == NULL)
102 fatalError("out of memory");
103 result->tempRegID = tempRegID;
104 result->mcRegConstraints = listClone(mcRegs);
105 result->startPoint = startPoint;
106 result->endPoint = endPoint;
107 return result;
108}
109
110static void deleteLiveInterval(t_liveInterval *interval)
111{
112 if (interval == NULL)
113 return;
114 free(interval->mcRegConstraints);
115 free(interval);
116}
117
118/* Given two live intervals, compare them by the start point (find whichever
119 * starts first). */
120static int compareLiveIntStartPoints(void *varA, void *varB)
121{
122 t_liveInterval *liA = (t_liveInterval *)varA;
123 t_liveInterval *liB = (t_liveInterval *)varB;
124
125 if (varA == NULL)
126 return 0;
127 if (varB == NULL)
128 return 0;
129
130 return liA->startPoint - liB->startPoint;
131}
132
133/* Given two live intervals, compare them by the end point (find whichever
134 * ends first). */
135static int compareLiveIntEndPoints(void *varA, void *varB)
136{
137 t_liveInterval *liA = (t_liveInterval *)varA;
138 t_liveInterval *liB = (t_liveInterval *)varB;
139
140 if (varA == NULL)
141 return 0;
142 if (varB == NULL)
143 return 0;
144
145 return liA->endPoint - liB->endPoint;
146}
147
148/* Given two live intervals, check if they refer to the same interval. */
149static bool compareLiveIntWithRegID(void *a, void *b)
150{
151 t_liveInterval *interval = (t_liveInterval *)a;
152 t_regID tempRegID = *((t_regID *)b);
153 return interval->tempRegID == tempRegID;
154}
155
156/* Update the liveness interval list to account for the fact that variable 'var'
157 * is live at index 'counter' in the current program.
158 * If the variable already appears in the list, its live interval its prolonged
159 * to include the given counter location.
160 * Otherwise, a new liveness interval is generated for it. */
161static t_listNode *updateIntervalsWithLiveVarAtLocation(
162 t_listNode *intervals, t_cfgReg *var, int counter)
163{
164 // Search if there's already a liveness interval for the variable.
165 t_listNode *element_found = listFindWithCallback(
166 intervals, &(var->tempRegID), compareLiveIntWithRegID);
167
168 if (!element_found) {
169 // It's not there: add a new interval at the end of the list.
170 t_liveInterval *interval =
171 newLiveInterval(var->tempRegID, var->mcRegWhitelist, counter, counter);
172 intervals = listInsert(intervals, interval, -1);
173 } else {
174 // It's there: update the interval range.
175 t_liveInterval *interval_found = (t_liveInterval *)element_found->data;
176 // Counter should always be increasing!
177 assert(interval_found->startPoint <= counter);
178 assert(interval_found->endPoint <= counter);
179 interval_found->endPoint = counter;
180 }
181
182 return intervals;
183}
184
185/* Add/augment the live interval list with the variables live at a given
186 * instruction location in the program. */
187static t_listNode *updateIntervalsWithInstrAtLocation(
188 t_listNode *result, t_bbNode *node, int counter)
189{
190 t_listNode *elem;
191
192 elem = node->in;
193 while (elem != NULL) {
194 t_cfgReg *curCFGReg = (t_cfgReg *)elem->data;
195 result = updateIntervalsWithLiveVarAtLocation(result, curCFGReg, counter);
196 elem = elem->next;
197 }
198
199 elem = node->out;
200 while (elem != NULL) {
201 t_cfgReg *curCFGReg = (t_cfgReg *)elem->data;
202 result = updateIntervalsWithLiveVarAtLocation(result, curCFGReg, counter);
203 elem = elem->next;
204 }
205
206 for (int i = 0; i < CFG_MAX_DEFS; i++) {
207 if (node->defs[i])
208 result =
209 updateIntervalsWithLiveVarAtLocation(result, node->defs[i], counter);
210 }
211
212 return result;
213}
214
215static int getLiveIntervalsNodeCallback(
216 t_bbNode *node, int nodeIndex, void *context)
217{
218 t_listNode **list = (t_listNode **)context;
219 *list = updateIntervalsWithInstrAtLocation(*list, node, nodeIndex);
220 return 0;
221}
222
223/* Collect a list of live intervals from the in/out sets in the CFG.
224 * Since cfgIterateNodes passes incrementing counter values to the
225 * callback, the list returned from here is already ordered. */
226static t_listNode *getLiveIntervals(t_cfg *graph)
227{
228 t_listNode *result = NULL;
229 cfgIterateNodes(graph, (void *)&result, getLiveIntervalsNodeCallback);
230 return result;
231}
232
233
234/* Move the elements in list `a` which are also contained in list `b` to the
235 * front of the list. */
236static t_listNode *optimizeRegisterSet(t_listNode *a, t_listNode *b)
237{
238 for (; b; b = b->next) {
239 t_listNode *old;
240 if ((old = listFind(a, b->data))) {
241 a = listRemoveNode(a, old);
242 a = listInsert(a, b->data, 0);
243 }
244 }
245 return a;
246}
247
248static t_listNode *subtractRegisterSets(t_listNode *a, t_listNode *b)
249{
250 for (; b; b = b->next) {
251 a = listFindAndRemove(a, b->data);
252 }
253 return a;
254}
255
256/* Create register constraint sets for all temporaries that don't have one.
257 * This is the main function that makes register allocation with constraints
258 * work.
259 * The idea is that we rely on the fact that all temporaries without
260 * constraints are distinguishable from temporaries with constraints.
261 * When a temporary *without* constraints A is alive at the same time as a
262 * temporary *with* constraints B, we prohibit allocation of A to all the
263 * viable registers for B. This guarantees A won't steal a register needed by B.
264 * Of course this will stop working as nicely with multiple overlapping
265 * constraints, but in ACSE this doesn't happen. */
266static void initializeRegisterConstraints(t_regAllocator *ra)
267{
268 t_listNode *i = ra->liveIntervals;
269 for (; i; i = i->next) {
270 t_liveInterval *interval = i->data;
271 // Skip instructions that already have constraints.
272 if (interval->mcRegConstraints)
273 continue;
274 // Initial set consists of all registers.
276
277 // Scan the temporary registers that are alive together with this one and
278 // already have constraints.
279 t_listNode *j = i->next;
280 for (; j; j = j->next) {
281 t_liveInterval *overlappingIval = j->data;
282 if (overlappingIval->startPoint > interval->endPoint)
283 break;
284 if (!overlappingIval->mcRegConstraints)
285 continue;
286 if (overlappingIval->startPoint == interval->endPoint) {
287 // Some instruction is using our temporary register as a source and the
288 // other temporary register as a destination. Optimize the constraint
289 // order to allow allocating source and destination to the same register
290 // if possible.
291 interval->mcRegConstraints = optimizeRegisterSet(
292 interval->mcRegConstraints, overlappingIval->mcRegConstraints);
293 } else {
294 // Another variable (defined after this one) wants to be allocated
295 // to a restricted set of registers. Punch a hole in the current
296 // variable's set of allowed registers to ensure that this is
297 // possible.
298 interval->mcRegConstraints = subtractRegisterSets(
299 interval->mcRegConstraints, overlappingIval->mcRegConstraints);
300 }
301 }
302 }
303}
304
305static int handleCallerSaveRegistersNodeCallback(
306 t_bbNode *node, int nodeIndex, void *context)
307{
308 t_regAllocator *ra = (t_regAllocator *)context;
309
310 if (!isCallInstruction(node->instr))
311 return 0;
312
314 for (int i = 0; i < CFG_MAX_DEFS; i++) {
315 if (node->defs[i] != NULL)
316 clobberedRegs =
317 subtractRegisterSets(clobberedRegs, node->defs[i]->mcRegWhitelist);
318 }
319 for (int i = 0; i < CFG_MAX_USES; i++) {
320 if (node->uses[i] != NULL)
321 clobberedRegs =
322 subtractRegisterSets(clobberedRegs, node->uses[i]->mcRegWhitelist);
323 }
324
325 t_listNode *li_ival = ra->liveIntervals;
326 while (li_ival) {
327 t_liveInterval *ival = li_ival->data;
328
329 if (ival->startPoint <= nodeIndex && nodeIndex <= ival->endPoint) {
330 ival->mcRegConstraints =
331 subtractRegisterSets(ival->mcRegConstraints, clobberedRegs);
332 }
333
334 li_ival = li_ival->next;
335 }
336
337 return 0;
338}
339
340/* Restrict register constraints in order to avoid register corrupted by
341 * function calls. */
342static void handleCallerSaveRegisters(t_regAllocator *ra, t_cfg *cfg)
343{
344 cfgIterateNodes(cfg, (void *)ra, handleCallerSaveRegistersNodeCallback);
345}
346
347
348static t_spillLocation *newSpillLocation(t_label *label, t_regID tempRegID)
349{
350 t_spillLocation *result = malloc(sizeof(t_spillLocation));
351 if (result == NULL)
352 fatalError("out of memory");
353 result->label = label;
354 result->tempRegID = tempRegID;
355 return result;
356}
357
358static bool compareSpillLocWithRegId(void *a, void *b)
359{
360 t_spillLocation *spillLoc = (t_spillLocation *)a;
361 t_regID reg = *((t_regID *)b);
362
363 if (spillLoc == NULL)
364 return 0;
365 return spillLoc->tempRegID == reg;
366}
367
368static void deleteSpillLocationList(t_listNode *spills)
369{
370 if (spills == NULL)
371 return;
372
373 t_listNode *curNode = spills;
374 while (curNode != NULL) {
375 t_spillLocation *spillLoc = (t_spillLocation *)curNode->data;
376 free(spillLoc);
377 curNode = curNode->next;
378 }
379
380 deleteList(spills);
381}
382
383
385{
386 t_regAllocator *result = (t_regAllocator *)calloc(1, sizeof(t_regAllocator));
387 if (result == NULL)
388 fatalError("out of memory");
389
390 // Create a CFG from the given program and compute the liveness intervals.
391 result->program = program;
392 result->graph = programToCFG(program);
393 cfgComputeLiveness(result->graph);
394
395 // Compute the ordered list of live intervals.
396 result->liveIntervals = getLiveIntervals(result->graph);
397
398 // Find the maximum temporary register ID in the program, then allocate the
399 // array of register bindings with that size. If there are unused register
400 // IDs, the array will have holes, but that's not a problem.
401 t_regID maxTempRegID = 0;
402 t_listNode *curCFGRegNode = result->graph->registers;
403 while (curCFGRegNode != NULL) {
404 t_cfgReg *curCFGReg = (t_cfgReg *)curCFGRegNode->data;
405 if (maxTempRegID < curCFGReg->tempRegID)
406 maxTempRegID = curCFGReg->tempRegID;
407 curCFGRegNode = curCFGRegNode->next;
408 }
409 result->tempRegNum = maxTempRegID + 1;
410
411 // allocate space for the binding array, and initialize it.
412 result->bindings = malloc(sizeof(t_regID) * (size_t)result->tempRegNum);
413 if (result->bindings == NULL)
414 fatalError("out of memory");
415 for (int counter = 0; counter < result->tempRegNum; counter++)
416 result->bindings[counter] = RA_REGISTER_INVALID;
417
418 // If the target has a special meaning for register zero, allocate it to
419 // itself immediately.
421 result->bindings[REG_0] = REG_0;
422
423 // Initialize the list of spill locations.
424 result->spills = NULL;
425
426 // Initialize register constraints.
427 initializeRegisterConstraints(result);
428 handleCallerSaveRegisters(result, result->graph);
429
430 // return the new register allocator.
431 return result;
432}
433
435{
436 if (RA == NULL)
437 return;
438
439 t_listNode *curNode = RA->liveIntervals;
440 while (curNode) {
441 t_liveInterval *curInterval = (t_liveInterval *)curNode->data;
442 deleteLiveInterval(curInterval);
443 curNode = curNode->next;
444 }
445
447 free(RA->bindings);
448 deleteSpillLocationList(RA->spills);
449 deleteCFG(RA->graph);
450
451 free(RA);
452}
453
454
455static bool compareFreeRegListNodes(void *freeReg, void *constraintReg)
456{
457 return INT_TO_LIST_DATA(constraintReg) == INT_TO_LIST_DATA(freeReg);
458}
459
460/* Remove from activeInterv all the live intervals that end before the
461 * beginning of the current live interval. */
462static void expireOldIntervals(t_regAllocator *RA, t_listNode **activeInterv,
463 t_listNode **freeRegs, t_liveInterval *interval)
464{
465 // No active intervals, bail out!
466 if (*activeInterv == NULL)
467 return;
468
469 // Iterate over the set of active intervals.
470 t_listNode *curNode = *activeInterv;
471 while (curNode != NULL) {
472 // Get the live interval
473 t_liveInterval *curInterval = (t_liveInterval *)curNode->data;
474
475 // If the considered interval ends before the beginning of the current live
476 // interval, we don't need to keep track of it anymore; otherwise, this is
477 // the first interval we must still take into account when assigning
478 // registers.
479 if (curInterval->endPoint > interval->startPoint)
480 return;
481
482 // When curInterval->endPoint == interval->startPoint, the variable
483 // associated to curInterval is being used by the instruction that defines
484 // interval. As a result, we can allocate interval to the same reg as
485 // curInterval.
486 if (curInterval->endPoint == interval->startPoint) {
487 t_regID curIntReg = RA->bindings[curInterval->tempRegID];
488 if (curIntReg >= 0) {
489 t_listNode *allocated =
490 listInsert(NULL, INT_TO_LIST_DATA(curIntReg), 0);
491 interval->mcRegConstraints =
492 optimizeRegisterSet(interval->mcRegConstraints, allocated);
493 deleteList(allocated);
494 }
495 }
496
497 // Get the next live interval.
498 t_listNode *nextNode = curNode->next;
499
500 // Remove the current element from the list.
501 *activeInterv = listFindAndRemove(*activeInterv, curInterval);
502
503 // Free all the registers associated with the removed interval.
504 *freeRegs = listInsert(
505 *freeRegs, INT_TO_LIST_DATA(RA->bindings[curInterval->tempRegID]), 0);
506
507 // Step to the next interval.
508 curNode = nextNode;
509 }
510}
511
512/* Get a new register from the free list. */
513static t_regID assignRegister(
514 t_regAllocator *RA, t_listNode **freeRegs, t_listNode *constraints)
515{
516 if (constraints == NULL)
517 return RA_SPILL_REQUIRED;
518
519 for (t_listNode *i = constraints; i; i = i->next) {
520 t_regID tempRegID = (t_regID)LIST_DATA_TO_INT(i->data);
522 *freeRegs, INT_TO_LIST_DATA(tempRegID), compareFreeRegListNodes);
523 if (freeReg) {
524 *freeRegs = listRemoveNode(*freeRegs, freeReg);
525 return tempRegID;
526 }
527 }
528
529 return RA_SPILL_REQUIRED;
530}
531
532/* Perform a spill that allows the allocation of the given interval, given the
533 * list of active live intervals. */
534static void spillAtInterval(
535 t_regAllocator *RA, t_listNode **activeInterv, t_liveInterval *interval)
536{
537 // An interval is made active when its register is allocated. As a result,
538 // if the list of active intervals is empty and we request a spill, we are
539 // working on a machine with 0 registers and we need to spill everything.
540 if (*activeInterv == NULL) {
541 RA->bindings[interval->tempRegID] = RA_SPILL_REQUIRED;
542 return;
543 }
544
545 // If the current interval ends before the last one successfully allocated,
546 // spill the last one. This has the result of making one register available
547 // much sooner. Otherwise spill the current interval.
548 t_listNode *lastNode = listGetLastNode(*activeInterv);
549 t_liveInterval *lastInterval = (t_liveInterval *)lastNode->data;
550 if (lastInterval->endPoint > interval->endPoint) {
551 // The last interval does end later than the current one.
552 // Ensure that the current interval is allocatable to the last interval's
553 // register.
554 t_regID attempt = RA->bindings[lastInterval->tempRegID];
555 if (listFind(interval->mcRegConstraints, INT_TO_LIST_DATA(attempt))) {
556 // All conditions satisfied for the last interval.
557 // Take its register for our interval and mark it as spilled.
558 RA->bindings[interval->tempRegID] = RA->bindings[lastInterval->tempRegID];
559 RA->bindings[lastInterval->tempRegID] = RA_SPILL_REQUIRED;
560 // Update the active intervals list.
561 *activeInterv = listFindAndRemove(*activeInterv, lastInterval);
562 *activeInterv =
563 listInsertSorted(*activeInterv, interval, compareLiveIntEndPoints);
564 return;
565 }
566 }
567 RA->bindings[interval->tempRegID] = RA_SPILL_REQUIRED;
568}
569
570static void executeLinearScan(t_regAllocator *RA)
571{
573 t_listNode *activeInterv = NULL;
574
575 for (t_listNode *curNode = RA->liveIntervals; curNode != NULL;
576 curNode = curNode->next) {
577 t_liveInterval *curInterval = (t_liveInterval *)curNode->data;
578
579 // Check which intervals are ended and remove them from the active set,
580 // thus freeing registers.
581 expireOldIntervals(RA, &activeInterv, &freeRegs, curInterval);
582
583 t_regID reg = assignRegister(RA, &freeRegs, curInterval->mcRegConstraints);
584
585 // If all registers are busy, perform a spill.
586 if (reg == RA_SPILL_REQUIRED) {
587 spillAtInterval(RA, &activeInterv, curInterval);
588 } else {
589 // Otherwise, assign a new register to the current live interval
590 // and add the current interval to the list of active intervals, in
591 // order of ending points (to allow easier expire management).
592 RA->bindings[curInterval->tempRegID] = reg;
593 activeInterv =
594 listInsertSorted(activeInterv, curInterval, compareLiveIntEndPoints);
595 }
596 }
597
598 activeInterv = deleteList(activeInterv);
599 freeRegs = deleteList(freeRegs);
600}
601
602
603/* For each spilled variable, this function statically allocates memory for
604 * that variable, returns a list of t_templabel structures mapping the
605 * spilled variables and the label that points to the allocated memory block. */
606static void materializeSpillMemory(t_regAllocator *RA)
607{
608 for (t_regID counter = 0; counter < RA->tempRegNum; counter++) {
609 if (RA->bindings[counter] != RA_SPILL_REQUIRED)
610 continue;
611
612 // Statically allocate some room for the spilled variable and add it to the
613 // list of spills.
614 char name[32];
615 sprintf(name, ".t%d", counter);
616 t_symbol *sym = createSymbol(RA->program, strdup(name), TYPE_INT, 0);
617 t_spillLocation *spillLoc = newSpillLocation(sym->label, counter);
618 RA->spills = listInsert(RA->spills, spillLoc, -1);
619 }
620}
621
622static void genStoreSpillVariable(t_regAllocator *RA, t_regID rSpilled,
623 t_regID rSrc, t_basicBlock *block, t_bbNode *curCFGNode, bool before)
624{
625 // Find the spill location.
626 t_listNode *elementFound =
627 listFindWithCallback(RA->spills, &rSpilled, compareSpillLocWithRegId);
628 if (elementFound == NULL)
629 fatalError("bug: t%d missing from the spill label list", rSpilled);
630 t_spillLocation *loc = (t_spillLocation *)elementFound->data;
631
632 // Insert a store instruction in the required position.
633 t_instruction *storeInstr = genSWGlobal(NULL, rSrc, loc->label, REG_T6);
634 if (before) {
635 bbInsertInstructionBefore(block, storeInstr, curCFGNode);
636 } else {
637 bbInsertInstructionAfter(block, storeInstr, curCFGNode);
638 }
639}
640
641static void genLoadSpillVariable(t_regAllocator *RA, t_regID rSpilled,
642 t_regID rDest, t_basicBlock *block, t_bbNode *curCFGNode, bool before)
643{
644 // Find the spill location.
645 t_listNode *elementFound =
646 listFindWithCallback(RA->spills, &rSpilled, compareSpillLocWithRegId);
647 if (elementFound == NULL)
648 fatalError("bug: t%d missing from the spill label list", rSpilled);
649 t_spillLocation *loc = (t_spillLocation *)elementFound->data;
650
651 // Insert a load instruction in the required position.
652 t_instruction *loadInstr = genLWGlobal(NULL, rDest, loc->label);
653 if (before) {
654 bbInsertInstructionBefore(block, loadInstr, curCFGNode);
655 // If the `curCFGNode' instruction has a label, move it to the new
656 // load instruction.
657 if ((curCFGNode->instr)->label != NULL) {
658 loadInstr->label = (curCFGNode->instr)->label;
659 (curCFGNode->instr)->label = NULL;
660 }
661 } else {
662 bbInsertInstructionAfter(block, loadInstr, curCFGNode);
663 }
664}
665
666static void materializeRegAllocInBBForInstructionNode(t_regAllocator *RA,
667 t_spillState *state, t_basicBlock *curBlock, t_bbNode *curCFGNode)
668{
669 // The elements in this array indicate whether the corresponding spill
670 // register will be used or not by this instruction.
671 bool spillSlotInUse[NUM_SPILL_REGS] = {false};
672 // This array stores whether each argument of the instruction is allocated
673 // to a spill register or not.
674 // For example, if argState[1].spillSlot == 2, the argState[1].reg register
675 // will be materialized to the third spill register.
677
678 // Analyze the current instruction.
679 t_instruction *instr = curCFGNode->instr;
680 int numArgs = 0;
681 if (instr->rDest) {
682 argState[numArgs].reg = instr->rDest;
683 argState[numArgs].isDestination = true;
684 argState[numArgs].spillSlot = -1;
685 numArgs++;
686 }
687 if (instr->rSrc1) {
688 argState[numArgs].reg = instr->rSrc1;
689 argState[numArgs].isDestination = false;
690 argState[numArgs].spillSlot = -1;
691 numArgs++;
692 }
693 if (instr->rSrc2) {
694 argState[numArgs].reg = instr->rSrc2;
695 argState[numArgs].isDestination = false;
696 argState[numArgs].spillSlot = -1;
697 numArgs++;
698 }
699
700 // Test if a requested spilled register is already loaded into a spill slot
701 // from a previous instruction.
702 for (int argIdx = 0; argIdx < numArgs; argIdx++) {
703 // Skip non-spilled registers.
704 if (RA->bindings[argState[argIdx].reg->ID] != RA_SPILL_REQUIRED)
705 continue;
706 // Check the state of each spill slot against the argument.
707 for (int slot = 0; slot < NUM_SPILL_REGS; slot++) {
708 // Spill slot is allocated to something else.
709 if (state->regs[slot].assignedTempReg != argState[argIdx].reg->ID)
710 continue;
711
712 // Spill slot is allocated to the correct register, so just use that slot.
713 argState[argIdx].spillSlot = slot;
714 spillSlotInUse[slot] = true;
715 if (argState[argIdx].isDestination)
716 state->regs[slot].needsWB = true;
717 break;
718 }
719 }
720
721 // Find a slot for all other spilled registers, evicting the previous use of
722 // a spill slot and writing it back if necessary.
723 for (int argIdx = 0; argIdx < numArgs; argIdx++) {
724 // Skip non-spilled registers
725 if (RA->bindings[argState[argIdx].reg->ID] != RA_SPILL_REQUIRED)
726 continue;
727 if (argState[argIdx].spillSlot != -1)
728 continue;
729
730 // Check if we already have found a slot for this same spilled register
731 // because it is used in more than one operand of the same instruction.
732 // In this case nothing to do, except marking the need of a writeback
733 // (one argument may be a source and the other a destination).
734 bool alreadyFound = false;
735 for (int otherArg = 0; otherArg < argIdx && !alreadyFound; otherArg++) {
736 if (argState[argIdx].reg->ID == argState[otherArg].reg->ID) {
737 int slot = argState[otherArg].spillSlot;
738 argState[argIdx].spillSlot = slot;
739 // Note: this next assignment is technically not needed because
740 // destination registers come first in the list of arguments.
741 state->regs[slot].needsWB |= argState[argIdx].isDestination;
742 alreadyFound = true;
743 }
744 }
745 if (alreadyFound)
746 continue;
747
748 // Otherwise we need to find a new slot.
749 int slot;
750 for (slot = 0; slot < NUM_SPILL_REGS; slot++) {
751 if (spillSlotInUse[slot] == false)
752 break;
753 }
754 // If we don't find anything, we don't have enough spill registers!
755 // This should never happen, bail out!
756 if (slot == NUM_SPILL_REGS)
757 fatalError("bug: spill slots exhausted");
758
759 // If needed, write back the old variable that was assigned to this
760 // slot before reassigning it.
761 if (state->regs[slot].needsWB) {
762 genStoreSpillVariable(RA, state->regs[slot].assignedTempReg,
763 getSpillMachineRegister(slot), curBlock, curCFGNode, true);
764 }
765
766 // Update the state of this spill slot.
767 spillSlotInUse[slot] = true;
768 argState[argIdx].spillSlot = slot;
769 state->regs[slot].assignedTempReg = argState[argIdx].reg->ID;
770 state->regs[slot].needsWB = argState[argIdx].isDestination;
771
772 // Load the value of the variable in the spill register if not a
773 // destination of the instruction.
774 if (!argState[argIdx].isDestination) {
775 genLoadSpillVariable(RA, argState[argIdx].reg->ID,
776 getSpillMachineRegister(slot), curBlock, curCFGNode, true);
777 }
778 }
779
780 // Rewrite the register identifiers to use the appropriate
781 // register number instead of the variable number.
782 for (int argIdx = 0; argIdx < numArgs; argIdx++) {
783 t_instrArg *curReg = argState[argIdx].reg;
784 if (argState[argIdx].spillSlot == -1) {
785 // Normal case.
786 curReg->ID = RA->bindings[curReg->ID];
787 } else {
788 // Spilled register case.
789 curReg->ID = getSpillMachineRegister(argState[argIdx].spillSlot);
790 }
791 }
792}
793
794static void materializeRegAllocInBB(t_regAllocator *RA, t_basicBlock *curBlock)
795{
796 t_spillState state;
797 for (int counter = 0; counter < NUM_SPILL_REGS; counter++) {
798 state.regs[counter].assignedTempReg = REG_INVALID;
799 state.regs[counter].needsWB = false;
800 }
801
802 t_bbNode *curCFGNode = NULL;
803 t_listNode *curInnerNode = curBlock->nodes;
804 while (curInnerNode != NULL) {
805 curCFGNode = (t_bbNode *)curInnerNode->data;
806 // Change the register IDs of the argument of the instruction according
807 // to the given register allocation. Generate load and stores for spilled
808 // registers.
809 materializeRegAllocInBBForInstructionNode(RA, &state, curBlock, curCFGNode);
810 curInnerNode = curInnerNode->next;
811 }
812 if (curCFGNode == NULL)
813 fatalError("bug: invalid CFG where a block has no nodes");
814
815 bool bbHasTermInstr = curBlock->nodes &&
816 (isJumpInstruction(curCFGNode->instr) ||
817 isExitInstruction(curCFGNode->instr));
818
819 // Writeback everything at the end of the basic block.
820 for (int counter = 0; counter < NUM_SPILL_REGS; counter++) {
821 if (state.regs[counter].needsWB == false)
822 continue;
823 genStoreSpillVariable(RA, state.regs[counter].assignedTempReg,
824 getSpillMachineRegister(counter), curBlock, curCFGNode, bbHasTermInstr);
825 }
826}
827
828static void materializeRegAllocInCFG(t_regAllocator *RA)
829{
830 t_listNode *curBlockNode = RA->graph->blocks;
831 while (curBlockNode != NULL) {
832 t_basicBlock *curBlock = (t_basicBlock *)curBlockNode->data;
833 materializeRegAllocInBB(RA, curBlock);
834 curBlockNode = curBlockNode->next;
835 }
836}
837
838
840{
841 // Bind each temporary register to a physical register using the linear scan
842 // algorithm. Spilled registers are all tagged with the fictitious register
843 // RA_SPILL_REQUIRED.
844 executeLinearScan(regalloc);
845
846 // Generate statically allocated globals for each spilled temporary register.
847 materializeSpillMemory(regalloc);
848
849 // Replace temporary register IDs with physical register IDs. In case of
850 // spilled registers, add load/store instructions appropriately.
851 materializeRegAllocInCFG(regalloc);
852
853 // Rewrite the program object from the CFG.
854 cfgToProgram(regalloc->program, regalloc->graph);
855}
856
857
859{
860 if (fout == NULL)
861 return;
862 if (RA->bindings == NULL)
863 return;
864
865 for (t_regID tempReg = 0; tempReg < RA->tempRegNum; tempReg++) {
866 t_regID physReg = RA->bindings[tempReg];
867
868 char *regStr = registerIDToString(tempReg, false);
869 fprintf(fout, "%s: ", regStr);
870 free(regStr);
871
872 if (physReg == RA_SPILL_REQUIRED) {
873 t_listNode *spillLi = RA->spills;
874 t_spillLocation *loc;
875 while (spillLi) {
876 loc = (t_spillLocation *)spillLi->data;
877 if (loc->tempRegID == tempReg)
878 break;
879 spillLi = spillLi->next;
880 }
881 if (spillLi) {
882 char *labelName = getLabelName(loc->label);
883 fprintf(fout, "spilled to label %s\n", labelName);
884 free(labelName);
885 } else {
886 fprintf(fout, "spilled to an undefined location\n");
887 }
888 } else if (physReg == RA_REGISTER_INVALID) {
889 fprintf(fout, "unassigned\n");
890 } else {
891 char *reg = registerIDToString(physReg, true);
892 fprintf(fout, "assigned to %s\n", reg);
893 free(reg);
894 }
895 }
896
897 fflush(fout);
898}
899
900void dumpLiveIntervals(t_listNode *intervals, FILE *fout)
901{
902 if (fout == NULL)
903 return;
904
905 t_listNode *curNode = intervals;
906 while (curNode != NULL) {
907 t_liveInterval *interval = (t_liveInterval *)curNode->data;
908
909 char *regStr = registerIDToString(interval->tempRegID, false);
910 fprintf(fout, "%s:\n", regStr);
911 free(regStr);
912
913 fprintf(fout, " live interval = [%3d, %3d]\n", interval->startPoint,
914 interval->endPoint);
915 fprintf(fout, " constraints = {");
916 t_listNode *i = interval->mcRegConstraints;
917 while (i) {
918 char *reg;
919
921 fprintf(fout, "%s", reg);
922 free(reg);
923
924 if (i->next != NULL)
925 fprintf(fout, ", ");
926 i = i->next;
927 }
928 fprintf(fout, "}\n");
929
930 curNode = curNode->next;
931 }
932 fflush(fout);
933}
934
935void regallocDump(t_regAllocator *RA, FILE *fout)
936{
937 if (RA == NULL)
938 return;
939 if (fout == NULL)
940 return;
941
942 fprintf(fout, "# Register Allocation dump\n\n");
943
944 fprintf(fout, "## Statistics\n\n");
945 fprintf(fout, "Number of available physical registers: %d\n", NUM_GP_REGS);
946 fprintf(fout, "Number of virtual registers used: %d\n\n", RA->tempRegNum);
947
948 fprintf(fout, "## Live intervals and constraints\n\n");
950 fprintf(fout, "\n");
951
952 fprintf(fout, "## Register assignment\n\n");
953 dumpVariableBindings(RA, fout);
954
955 fflush(fout);
956}
Control Flow Graph generation and related analyses.
Code generation functions.
Error logging utilities.
char * registerIDToString(t_regID regID, bool machineRegIDs)
t_regID tempRegID
Register identifier.
Definition cfg.h:34
t_listNode * registers
List of all temporary registers used in the program.
Definition cfg.h:78
t_listNode * mcRegWhitelist
Physical register whitelist. Used by the register allocator.
Definition cfg.h:36
t_listNode * in
Set of registers live at the entry of the node ('in' set).
Definition cfg.h:54
t_listNode * nodes
List of instructions in the block.
Definition cfg.h:67
t_listNode * out
Set of registers live at the exit of the node ('out' set).
Definition cfg.h:56
t_instruction * instr
Pointer to the instruction associated with this node.
Definition cfg.h:48
t_listNode * blocks
List of all the basic blocks, in program order.
Definition cfg.h:73
t_cfgReg * defs[CFG_MAX_DEFS]
Set of registers defined by this node ('def' set). NULL slots are ignored.
Definition cfg.h:50
t_cfgReg * uses[CFG_MAX_USES]
Set of registers used by this node ('use' set). NULL slots are ignored.
Definition cfg.h:52
void cfgToProgram(t_program *program, t_cfg *graph)
Definition cfg.c:413
void cfgComputeLiveness(t_cfg *graph)
Definition cfg.c:639
void deleteCFG(t_cfg *graph)
Definition cfg.c:235
int cfgIterateNodes(t_cfg *graph, void *context, int(*callback)(t_bbNode *node, int nodeIndex, void *context))
Definition cfg.c:437
#define CFG_MAX_USES
Maximum number of temporary register uses for each node.
Definition cfg.h:27
#define CFG_MAX_DEFS
Maximum number of temporary register definitions for each node.
Definition cfg.h:25
t_bbNode * bbInsertInstructionBefore(t_basicBlock *block, t_instruction *instr, t_bbNode *ip)
Definition cfg.c:193
t_cfg * programToCFG(t_program *program)
Definition cfg.c:370
t_bbNode * bbInsertInstructionAfter(t_basicBlock *block, t_instruction *instr, t_bbNode *ip)
Definition cfg.c:207
Definition cfg.h:44
Definition cfg.h:71
Definition cfg.h:32
void fatalError(const char *format,...)
Definition errors.c:32
t_instruction * genSWGlobal(t_program *program, t_regID rs1, t_label *label, t_regID r_temp)
Definition codegen.c:387
t_instruction * genLWGlobal(t_program *program, t_regID rd, t_label *label)
Definition codegen.c:380
struct t_listNode * next
Definition list.h:40
void * data
Pointer to the data associated to this node.
Definition list.h:44
t_listNode * listFindAndRemove(t_listNode *list, void *data)
Definition list.c:180
t_listNode * listClone(t_listNode *list)
Definition list.c:240
t_listNode * listInsert(t_listNode *list, void *data, int pos)
Definition list.c:86
#define LIST_DATA_TO_INT(data)
Convert a data item pointer created by INT_TO_LIST_DATA() to an integer.
Definition list.h:36
#define INT_TO_LIST_DATA(data)
Convert an integer from a list data pointer.
Definition list.h:33
t_listNode * listFind(t_listNode *list, void *data)
Definition list.c:142
t_listNode * deleteList(t_listNode *list)
Definition list.c:189
t_listNode * listFindWithCallback(t_listNode *list, void *data, bool(*compareFunc)(void *a, void *b))
Definition list.c:124
t_listNode * listRemoveNode(t_listNode *list, t_listNode *element)
Definition list.c:148
t_listNode * listInsertSorted(t_listNode *list, void *data, int(*compareFunc)(void *a, void *b))
Definition list.c:100
t_listNode * listGetLastNode(t_listNode *list)
Definition list.c:51
A node belonging a list.
Definition list.h:39
t_instrArg * rSrc1
First source argument (or NULL if none).
Definition program.h:74
t_instrArg * rSrc2
Second source argument (or NULL if none).
Definition program.h:75
t_regID ID
The register identifier.
Definition program.h:63
t_label * label
Definition program.h:91
t_instrArg * rDest
Destination argument (or NULL if none).
Definition program.h:73
#define REG_INVALID
Constant used for invalid register identifiers.
Definition program.h:31
#define REG_0
Constant identifying a register whose value is always zero.
Definition program.h:33
t_symbol * createSymbol(t_program *program, char *ID, t_symbolType type, int arraySize)
Definition program.c:406
char * getLabelName(t_label *label)
Definition program.c:297
int t_regID
Type for register identifiers.
Definition program.h:28
@ TYPE_INT
‘int’ scalar type.
Definition program.h:38
t_regAllocator * newRegAllocator(t_program *program)
Definition reg_alloc.c:384
void regallocDump(t_regAllocator *RA, FILE *fout)
Definition reg_alloc.c:935
void deleteRegAllocator(t_regAllocator *RA)
Definition reg_alloc.c:434
void regallocRun(t_regAllocator *regalloc)
Definition reg_alloc.c:839
t_listNode * getListOfMachineRegisters(void)
Definition target_info.c:68
#define TARGET_REG_ZERO_IS_CONST
Definition target_info.h:34
t_regID getSpillMachineRegister(int i)
Definition target_info.c:48
t_listNode * getListOfCallerSaveMachineRegisters(void)
Definition target_info.c:77
#define NUM_GP_REGS
Number of general-purpose registers usable by the register allocator.
Definition target_info.h:37
#define NUM_SPILL_REGS
Definition target_info.h:41
bool isJumpInstruction(t_instruction *instr)
Definition target_info.c:21
bool isExitInstruction(t_instruction *instr)
Definition target_info.c:9
bool isCallInstruction(t_instruction *instr)
Definition target_info.c:42
t_listNode * getListOfGenPurposeMachineRegisters(void)
Definition target_info.c:55
@ REG_T6
Definition target_info.h:76
A double-linked list.
Program object definition and management.
int startPoint
Index of the first instruction that uses/defines this register.
Definition reg_alloc.c:34
bool isDestination
If the register is a destination register.
Definition reg_alloc.c:73
t_regID tempRegID
Identifier of the register.
Definition reg_alloc.c:29
t_listNode * mcRegConstraints
Definition reg_alloc.c:32
#define MAX_INSTR_ARGS
Maximum amount of arguments to an instruction.
Definition reg_alloc.c:18
t_regID * bindings
Definition reg_alloc.c:62
t_program * program
The program where register allocation needs to be performed.
Definition reg_alloc.c:51
t_instrArg * reg
The instruction argument structure.
Definition reg_alloc.c:71
t_cfg * graph
The temporary control flow graph produced from the program.
Definition reg_alloc.c:53
t_regID assignedTempReg
Temporary register ID associated to this spill register.
Definition reg_alloc.c:82
void dumpLiveIntervals(t_listNode *intervals, FILE *fout)
Definition reg_alloc.c:900
t_spillRegState regs[NUM_SPILL_REGS]
Definition reg_alloc.c:93
#define RA_REGISTER_INVALID
Fictitious register ID marking currently unallocated temporaries.
Definition reg_alloc.c:23
int endPoint
Index of the last instruction that uses/defines this register.
Definition reg_alloc.c:36
t_listNode * liveIntervals
List of live intervals, ordered depending on their start index.
Definition reg_alloc.c:55
int tempRegNum
Number of temporary registers in the program.
Definition reg_alloc.c:57
t_listNode * spills
List of spill locations for temporary registers in the program.
Definition reg_alloc.c:64
#define RA_SPILL_REQUIRED
Fictitious register ID associated to registers to be spilled.
Definition reg_alloc.c:21
t_label * label
The label pointing to the spill area.
Definition reg_alloc.c:45
void dumpVariableBindings(t_regAllocator *RA, FILE *fout)
Definition reg_alloc.c:858
Structure describing a live interval of a register in a program.
Definition reg_alloc.c:27
Structure encapsulating the state of the register allocator.
Definition reg_alloc.c:49
Structure representing the current state of a spill-reserved register.
Definition reg_alloc.c:80
Spill register slots state.
Definition reg_alloc.c:90
Register allocation pass.
Generation of the output assembly program.
Properties of the target machine.