def alloc(compiler): ''' given a function compiler, allocate registers for its registers ''' # only use temporary registers for now k = 18 # s0 - s7 and t0 - t9 insts = compiler.insts cfg = flow.make_cfg(insts) int_graph, liveouts = make_int_graph(cfg) coloring, uncolored = color(int_graph, k) while len(uncolored) > 0: # have to spill spilled = set(uncolored) new_insts = [] addrs = {} for reg in uncolored: addrs[reg] = compiler.alloc(size=4) for inst in insts: defed, used = flow.get_defuse(inst) for reg in used: if reg in spilled: # load spilled from memory new_insts.append(IR('lw', rt=reg, rs=REG_SP, rd=addrs[reg])) new_insts.append(inst) if defed in spilled: # store def new_insts.append(IR('sw', rt=defed, rs=REG_SP, rd=addrs[defed])) insts = new_insts cfg = flow.make_cfg(insts) int_graph, liveouts = make_int_graph(cfg) coloring, uncolored = color(int_graph, k) # keep register living across function calls # in saved register and the rest in temporarys calls = cfg.get_calls() # colors that should be mapped in saved registers saved = {coloring[reg] for call in calls for reg in liveouts[call] if reg in coloring} # there are only 8 saved registers # we need to use some t registers to hold values that # live across function calls saved_tregs = [] while len(saved) > 8: saved_tregs.append(saved.pop()) # $s0 - $s7 sregs = [Register('s', i) for i in range(7+1, -1, -1)] # $t0 - $t9 tregs = [Register('t', i) for i in range(9+1, -1, -1)] # mapping: colors -> registers translations = { color: sregs.pop() if color in saved else tregs.pop() for color in set(coloring.values())} # replace virtual registers with physical registers compiler.insts = [] for inst in insts: if type(inst) == IR: opcode, rs, rt, rd = inst if rs in coloring: rs = translations[coloring[rs]] if rt in coloring: rt = translations[coloring[rt]] if rd in coloring: rd = translations[coloring[rd]] if opcode == 'move' and rs == rd: continue inst = IR(opcode, rs, rt, rd) if any(type(u) == Register and u.typ == 'virtual' for u in (rs, rt)): # if there's any virtual registers left # it means it's a dead value hence the instruction is dead code continue compiler.insts.append(inst) compiler.sregs = [translations[c] for c in saved]
def remove_placeholders(self): ''' replace Prolog/Epilog/SaveRegisters/RestoreRegisters with real instructions now that we know what registers are used ''' cfg = flow.make_cfg(self.insts) outs = flow.get_lives(cfg) # t registers that need to be saved tregs = [] for node in sorted(cfg.get_calls()): tregs.append(sorted({reg for reg in outs[node] if reg.typ == 't'})) space = (max(len(regs) for regs in tregs) * 4 if len(tregs) > 0 else 0) t_offset = self.alloc(size=space) s_offset = self.alloc(size=len(self.sregs)*4+4) # replace prologs/epilogs with stores and loads insts = [] i = 0 if i < len(tregs): regs = tregs[i] i += 1 for inst in self.insts: inst_type = type(inst) if inst_type == Call: store_regs(regs, t_offset, insts) insts.append( new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(-mulof4(inst.extra)))) insts.append(new_ir('jal', rs=funcname_to_branch(inst.name), rt=None, rd=None)) insts.append( new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(mulof4(inst.extra)))) load_regs(regs, t_offset, insts) if i < len(tregs): regs = tregs[i] i += 1 elif inst_type == Prolog: # grow stack and store needed s registers grow = new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(-self.stack_size())) insts.extend([ new_ir('move', rd=REG_FP, rs=REG_SP, rt=None), grow ]) store_regs(self.sregs+[REG_RA], s_offset, insts) elif inst_type == Epilog: # restore used registers load_regs(self.sregs+[REG_RA], s_offset, insts) shrink = new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(self.stack_size())) insts.extend([ shrink, new_ir('jr', rs=Register('ra', None), rt=None, rd=None) ]) else: insts.append(inst) self.insts = insts
def remove_placeholders(self): ''' replace Prolog/Epilog/SaveRegisters/RestoreRegisters with real instructions now that we know what registers are used ''' cfg = flow.make_cfg(self.insts) outs = flow.get_lives(cfg) # t registers that need to be saved tregs = [] for node in sorted(cfg.get_calls()): tregs.append(sorted({reg for reg in outs[node] if reg.typ == 't'})) space = (max(len(regs) for regs in tregs) * 4 if len(tregs) > 0 else 0) t_offset = self.alloc(size=space) s_offset = self.alloc(size=len(self.sregs) * 4 + 4) # replace prologs/epilogs with stores and loads insts = [] i = 0 if i < len(tregs): regs = tregs[i] i += 1 for inst in self.insts: inst_type = type(inst) if inst_type == Call: store_regs(regs, t_offset, insts) insts.append( new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(-mulof4(inst.extra)))) insts.append( new_ir('jal', rs=funcname_to_branch(inst.name), rt=None, rd=None)) insts.append( new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(mulof4(inst.extra)))) load_regs(regs, t_offset, insts) if i < len(tregs): regs = tregs[i] i += 1 elif inst_type == Prolog: # grow stack and store needed s registers grow = new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(-self.stack_size())) insts.extend( [new_ir('move', rd=REG_FP, rs=REG_SP, rt=None), grow]) store_regs(self.sregs + [REG_RA], s_offset, insts) elif inst_type == Epilog: # restore used registers load_regs(self.sregs + [REG_RA], s_offset, insts) shrink = new_ir('add', rd=REG_SP, rs=REG_SP, rt=grammar.Int(self.stack_size())) insts.extend([ shrink, new_ir('jr', rs=Register('ra', None), rt=None, rd=None) ]) else: insts.append(inst) self.insts = insts