def _set_bool(self, spotmap, get_reg, asm_code): """Emit code for SET command if arg is boolean type.""" # When any scalar value is converted to _Bool, the result is 0 if the # value compares equal to 0; otherwise, the result is 1 # If arg_asm is a LITERAL or conflicts with output, move to register. if (isinstance(spotmap[self.arg], LiteralSpot) or spotmap[self.arg] == spotmap[self.output]): r = get_reg([], [spotmap[self.output]]) asm_code.add( asm_cmds.Mov(r, spotmap[self.arg], self.arg.ctype.size)) arg_spot = r else: arg_spot = spotmap[self.arg] label = asm_code.get_label() output_spot = spotmap[self.output] zero = LiteralSpot("0") one = LiteralSpot("1") asm_code.add(asm_cmds.Mov(output_spot, zero, self.output.ctype.size)) asm_code.add(asm_cmds.Cmp(arg_spot, zero, self.arg.ctype.size)) asm_code.add(asm_cmds.Je(label)) asm_code.add(asm_cmds.Mov(output_spot, one, self.output.ctype.size)) asm_code.add(asm_cmds.Label(label))
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102 regs = [] result = get_reg([spotmap[self.output]], [spotmap[self.arg1], spotmap[self.arg2]]) regs.append(result) out_size = self.output.ctype.size eq_val_spot = LiteralSpot(1) asm_code.add(asm_cmds.Mov(result, eq_val_spot, out_size)) arg1_spot, arg2_spot = self._fix_both_literal_or_mem( spotmap[self.arg1], spotmap[self.arg2], regs, get_reg, asm_code) arg1_spot, arg2_spot = self._fix_either_literal64( arg1_spot, arg2_spot, regs, get_reg, asm_code) arg1_spot, arg2_spot = self._fix_literal_wrong_order( arg1_spot, arg2_spot) arg_size = self.arg1.ctype.size neq_val_spot = LiteralSpot(0) label = asm_code.get_label() asm_code.add(asm_cmds.Cmp(arg1_spot, arg2_spot, arg_size)) asm_code.add(self.cmp_command()(label)) asm_code.add(asm_cmds.Mov(result, neq_val_spot, out_size)) asm_code.add(asm_cmds.Label(label)) if result != spotmap[self.output]: asm_code.add(asm_cmds.Mov(spotmap[self.output], result, out_size))
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102 size = self.cond.ctype.size if isinstance(spotmap[self.cond], LiteralSpot): r = get_reg() asm_code.add(asm_cmds.Mov(r, spotmap[self.cond], size)) cond_spot = r else: cond_spot = spotmap[self.cond] zero_spot = LiteralSpot("0") asm_code.add(asm_cmds.Cmp(cond_spot, zero_spot, size)) asm_code.add(self.command(self.label))
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102 arg1_spot = spotmap[self.arg1] arg2_spot = spotmap[self.arg2] output_spot = spotmap[self.output] r12_spot = spots.RegSpot("r12") r13_spot = spots.RegSpot("r13") label = asm_code.get_label() # this stays here until I removed size from all functions size = None # start with setting the output reg to 1, in case of success asm_code.add(asm_cmds.Load(LiteralSpot(1), output_spot, size)) self.move(arg1_spot, r12_spot, asm_code) self.move(arg2_spot, r13_spot, asm_code) # swap args when using less than instructions (to convert to gte's) if self.reverse_less_cmd: asm_code.add(self.cmp_command()(r12_spot, r13_spot, LiteralSpot(2))) else: asm_code.add(self.cmp_command()(r13_spot, r12_spot, LiteralSpot(2))) #not bgt r12 r0 2 -> bge r0 r12 2 #not bge r12 r0 2 -> bgt r0 r12 2 #not beq r12 r0 2 -> bne r0 r12 2 #not bne r12 r0 2 -> beq r0 r12 2 asm_code.add(asm_cmds.Jump(label)) # in case the comparison was unsuccessful, set output reg to 0 asm_code.add(asm_cmds.Load(LiteralSpot(0), output_spot, size)) asm_code.add(asm_cmds.Label(label))
def _generate_asm(self, commands, live_vars, spotmap): """Generate assembly code.""" # This is kinda hacky... max_offset = max(spot.rbp_offset() for spot in spotmap.values()) #if max_offset % 16 != 0: # max_offset += 16 - max_offset % 16 # Back up rbp and move rsp #self.asm_code.add(asm_cmds.Push(spots.RBP, None, 8)) self.asm_code.add(asm_cmds.Sub(spots.RSP, spots.LiteralSpot(1))) self.asm_code.add(asm_cmds.Write(spots.RSP, spots.RBP)) self.asm_code.add(asm_cmds.Mov(spots.RBP, spots.RSP)) offset_spot = LiteralSpot(str(max_offset)) self.asm_code.add(asm_cmds.Sub(spots.RSP, offset_spot)) # Generate code for each command for i, command in enumerate(commands): self.asm_code.add(asm_cmds.Comment(type(command).__name__.upper())) def get_reg(pref=None, conf=None): if not pref: pref = [] if not conf: conf = [] # Spot is bad if it is containing a variable that is live both # entering and exiting this command. bad_vars = set(live_vars[i][0]) & set(live_vars[i][1]) bad_spots = set(spotmap[var] for var in bad_vars) # Spot is free if it is where an output is stored. for v in command.outputs(): bad_spots.discard(spotmap[v]) # Spot is bad if it is listed as a conflicting spot. bad_spots |= set(conf) for s in (pref + self.all_registers): if isinstance(s, RegSpot) and s not in bad_spots: return s raise NotImplementedError("spill required for get_reg") command.make_asm(spotmap, spotmap, get_reg, self.asm_code)
def _get_global_spotmap(self): """Generate global spotmap and free values. Returns a tuple. First element is a dictionary mapping ILValue to spot for spots which do not need register allocation, like static variables or literals. The second element is a list of the free values; the variables which were not mapped in the global spotmap. """ global_spotmap = {} free_values = [] all_values = self._all_il_values() string_literal_number = 0 for value in all_values: if value in self.il_code.literals: # If literal, assign it a preassigned literal spot s = LiteralSpot(self.il_code.literals[value]) global_spotmap[value] = s elif value in self.il_code.externs: # If extern, assign assign spot and add the extern to asm code s = MemSpot(self.il_code.externs[value]) global_spotmap[value] = s self.asm_code.add_extern(self.il_code.externs[value]) elif value in self.il_code.string_literals: # Add the string literal representation to the output ASM. name = f"__strlit{string_literal_number}" string_literal_number += 1 self.asm_code.add_string_literal( name, self.il_code.string_literals[value]) global_spotmap[value] = MemSpot(name) elif (self.arguments.variables_on_stack and value in self.il_code.variables): # pragma: no cover # If all variables are allocated on the stack self.offset += value.ctype.size s = MemSpot(spots.RBP, -self.offset) global_spotmap[value] = s else: # Value is free and needs an assignment free_values.append(value) return global_spotmap, free_values
def _get_nondynamic_spot(self, v, num): """Get a spot for non-dynamic values. In particular, assigns a spot to all literals, string literals, variables with no storage, and variables with static storage. v - value to get a spot for, or None if the value goes in a dynamic spot like a register nnum - positive integer guaranteed never to be the same for two distinct calls to this function """ EXTERNAL = self.symbol_table.EXTERNAL INTERNAL = self.symbol_table.INTERNAL TENTATIVE = self.symbol_table.TENTATIVE if v in self.il_code.literals: return LiteralSpot(self.il_code.literals[v]) elif v in self.il_code.string_literals: name = f"__strlit{num}" self.asm_code.add_string_literal(name, self.il_code.string_literals[v]) return MemSpot(name) # Values with no storage can be referenced directly by name elif not self.symbol_table.storage.get(v, True): return MemSpot(self.symbol_table.names[v]) elif self.symbol_table.storage.get(v) == self.symbol_table.STATIC: name = self.symbol_table.names[v] if self.symbol_table.linkage_type.get(v) != EXTERNAL: name = f"{name}.{num}" if self.symbol_table.def_state.get(v) == TENTATIVE: local = (self.symbol_table.linkage_type[v] == INTERNAL) self.asm_code.add_comm(name, v.ctype.size, local) else: init_val = self.il_code.static_inits.get(v, 0) self.asm_code.add_data(name, v.ctype.size, init_val) return MemSpot(name)