Ejemplo n.º 1
0
def return_function(function: MCFunction) -> [List[MCInstruction]]:
    output = []
    syscode = 10
    if function.name == "main":
        output.append(MCInstruction("li", regs=["$v0"], imm=syscode))
        output.append(MCInstruction("syscall"))
    else:
        output.append(MCInstruction("jr", regs=["$ra"]))
    return output
Ejemplo n.º 2
0
def convert_assignment(instr: IRInstruction) -> str:
    dest, src = instr.argument_list
    assert (instr.instruction_type == "val_assign")
    assert (not is_constant(dest))

    if not is_constant(src):
        output = [MCInstruction("move", regs=[dest, src])]
    else:
        output = [MCInstruction("li", regs=[dest], imm=src)]

    return output
Ejemplo n.º 3
0
def convert_return(instr):
    assert (instr.instruction_type == "return")
    assert (len(instr.argument_list) == 1)
    ret_val = instr.argument_list[0]
    output = []
    v0 = "$v0"
    if is_constant(ret_val):
        output.append(MCInstruction("li", regs=[v0], imm=ret_val))
    else:
        output.append(MCInstruction("move", regs=[v0, ret_val]))

    output.append(MCInstruction("ret"))

    return output
Ejemplo n.º 4
0
def alloc_array(name: str, size: int, offset: int) -> List[MCInstruction]:
    syscode = 9
    output = []
    fp = "$fp"

    # syscall
    output.append(MCInstruction("li", regs=["$v0"], imm=syscode))
    output.append(MCInstruction("li", regs=["$a0"], imm=size * 4))
    output.append(MCInstruction("syscall"))

    # store pointer on the stack
    output.append(MCInstruction("sw", regs=["$v0", fp], offset=offset))

    return output
Ejemplo n.º 5
0
def convert_instr(reg_map,
                  instr: MCInstruction,
                  offsets: Dict[str, int],
                  epilogue,
                  rtn,
                  optimize=False) -> List[MCInstruction]:
    """
    Convert a single instruction from using virtual register to physical register. Also, if this instruction is `call` or `callr`, then it's also changed to an actual machine instruction.
    Args:
        - reg_map: the register map in this basic block
        - instr: the instruction to be changed
        - offsets: offsets dictionary
    Return:
        - a list of instruction that's equivalent
    """

    if instr.op == "ret":
        return epilogue + rtn

    fp = "$fp"
    if instr.op == "save_arg":
        reg = instr.regs[0]
        return [MCInstruction("sw", regs=[reg, fp], offset=offsets[reg])]
    if instr.op == "restore_arg":
        reg = instr.regs[0]
        return [MCInstruction("lw", regs=[reg, fp], offset=offsets[reg])]

    # TODO: assume the caller's stack is already even, add padding if the number of args that need to be passed through the stack is odd
    output = []
    # physical_regs = [reg_map[virtual_reg] for virtual_reg in instr.regs]
    curr_temp = 0
    if instr.regs is None:
        return [instr]
    prologue = []
    epilogue = []
    save, restore, new_regs = spill(
        reg_map, instr, offsets, optimize=optimize
    )  # it's fine to call this on non-spilling since the code arrays will be empty

    output += prologue
    output += save
    instr.regs = new_regs
    output.append(instr)
    output += restore
    output += epilogue

    return output
Ejemplo n.º 6
0
def save_and_restore(
        reg_name: str) -> Tuple[List[MCInstruction], List[MCInstruction]]:
    """
    Computes the save and restore code for a single register.
    Args:
        - reg_name: the register to be saved and restored, make sure this is a physical register
    Returns:
        - a tuple of list of instructions. The first of which is the save code, the second is the restore code
    """
    save_code = []
    restore_code = []
    sp = "$sp"

    save_code.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
    save_code.append(MCInstruction("sw", regs=[reg_name, sp], offset=0))

    restore_code.append(MCInstruction("lw", regs=[reg_name, sp], offset=0))
    restore_code.append(MCInstruction("addiu", regs=[sp, sp], imm=4))

    return save_code, restore_code
Ejemplo n.º 7
0
def load_and_save_locals(
    reg_map: Dict[str, int],
    offsets: Dict[str,
                  int]) -> Tuple[List[MCInstruction], List[MCInstruction]]:
    load = []
    save = []
    fp = "$fp"

    # pprint.pprint(reg_map)

    # print(offsets)

    arg_pattern = re.compile(r"\$a[0123]")

    for virt, phys in reg_map.items():
        if phys != "spill" and arg_pattern.match(phys) is None:
            offset = offsets[virt]
            load.append(MCInstruction("lw", regs=[phys, fp], offset=offset))
            save.append(MCInstruction("sw", regs=[phys, fp], offset=offset))

    return load, save
Ejemplo n.º 8
0
def convert_branch(instr: IRInstruction, func) -> str:
    if instr.instruction_type == "goto":
        target = instr.argument_list[0]
        return [MCInstruction("j", target="%s_%s" % (func, target))]
    else:
        branch_map = {
            "breq": "beq",
            "brneq": "bne",
            "brlt": "blt",
            "brgt": "bgt",
            "brgeq": "bge",
            "brleq": "ble"
        }
        label, src0, src1 = instr.argument_list
        op = branch_map[instr.instruction_type]

        output = []
        temp = s_map["CONVERT_BRANCH_TEMP_REG"]
        if not is_constant(src0) and not is_constant(src1):
            output.append(
                MCInstruction(op,
                              regs=[src0, src1],
                              target="%s_%s" % (func, label)))
        elif not is_constant(src0) and is_constant(src1):
            output.append(MCInstruction("li", regs=[temp], imm=src1))
            output.append(
                MCInstruction(op,
                              regs=[src0, temp],
                              target="%s_%s" % (func, label)))
        elif is_constant(src0) and not is_constant(src1):
            output.append(MCInstruction("li", regs=[temp], imm=src0))
            output.append(
                MCInstruction(op,
                              regs=[temp, src1],
                              target="%s_%s" % (func, label)))
        else:
            raise ValueError(
                "Both operands of branch instruction are constants")
        del s_map["CONVERT_BRANCH_TEMP_REG"]

        return output
Ejemplo n.º 9
0
def convert_calls(instr: IRInstruction):
    # NOTE: this is temporary and should not ever end up in the final output
    assert (instr.instruction_type == "call"
            or instr.instruction_type == "callr")
    intrinsics = ["geti", "getf", "getc", "puti", "putf", "putc"]
    sp = "$sp"
    if instr.instruction_type == "call":
        function_name = instr.argument_list[0]
        arguments = instr.argument_list[1:]
        return_dest = None
    else:
        return_dest = instr.argument_list[0]
        function_name = instr.argument_list[1]
        arguments = instr.argument_list[2:]

    if function_name in intrinsics:
        return convert_intrinsic(instr, function_name, arguments, return_dest)

    output = []
    save_arg = []
    restore_arg = []
    # normal function call
    first_section = arguments[:4]
    for i, arg in enumerate(first_section):
        arg_reg = "$a%d" % i
        save_arg.append(MCInstruction("save_arg", regs=[arg_reg]))
        restore_arg.append(MCInstruction("restore_arg", regs=[arg_reg]))
        if is_constant(arg):
            output.append(
                MCInstruction("li", regs=[arg_reg], imm=arg,
                              is_call_move=True))
        else:
            output.append(
                MCInstruction("move", regs=[arg_reg, arg], is_call_move=True))

    second_section = arguments[4:][:-1]  # pushing in reverse order
    temp = s_map["CONVERT_CALLS_TEMP_REG"]
    stack_length = 0
    for arg in second_section:
        output.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        stack_length += 1
        if is_constant(arg):
            output.append(MCInstruction("li", regs=[temp], imm=arg))
            output.append(MCInstruction("sw", regs=[temp, sp], imm=0))
        else:
            output.append(MCInstruction("sw", regs=[arg, sp], imm=0))
    del s_map["CONVERT_CALLS_TEMP_REG"]
    # paddding
    if len(second_section) % 2 == 1:
        output.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        stack_length += 1

    # the actual call itself
    output.append(MCInstruction("jal", target=function_name))

    if instr.instruction_type == "callr":
        # reading the return value
        output.append(MCInstruction("move", regs=[
            return_dest, "$v0"
        ]))  # shouldn't have anything that doesn't fit in one word

    # popping the stack
    if stack_length != 0:
        output.append(
            MCInstruction("addiu", regs=[sp, sp], imm=4 * stack_length))

    return save_arg + output + restore_arg
Ejemplo n.º 10
0
def convert_intrinsic(instr: IRInstruction,
                      name: str,
                      args: List[str],
                      dest: str = None) -> List[MCInstruction]:
    """
    Converts an intrinsic function call to use the relevant syscall. Note that v0 and a0 is always saved and restored, regardless of whether these registers are actually used in the function.
    
    Args:
        - instr: the instruction to be translated
    Returns:
        - a list of instructions equivalent to the intrinsic function
    """
    sys_codes = {
        "print_int": 1,
        "print_float": 2,
        "print_double": 3,
        "print_string": 4,
        "read_int": 5,
        "raed_float": 6,
        "read_double": 7,
        "read_string": 8,
        "sbrk": 9,
        "exit": 10,
        "print_char": 11
    }
    output = []
    op = name
    if op == "geti" or op == "getc":  # CHECK: does getc and geti actually work in the same way?
        # saving v0, CHECK: is this really needed?
        # save, restore = save_and_restore("$v0")
        # output += save

        # moving syscode into v0
        output.append(
            MCInstruction("li", regs=["$v0"], imm=sys_codes["read_int"]))

        # syscall
        output.append(MCInstruction("syscall"))

        # moving output into destination
        output.append(MCInstruction("move", regs=[dest, "$v0"]))

        # restoring v0
        # output += restore
        return output
    elif op == "getf":
        raise NotImplementedError(
            "getf() isn't implemented as floats are not supported")
    elif op == "puti":
        # arg = instr.arguments[0]
        arg = args[0]

        # saving a0 and v0
        # save_a0, restore_a0 = save_and_restore("$a0")
        # save_v0, restore_v0 = save_and_restore("$v0")
        # output += save_a0
        # output += save_v0

        # moving argument into a0
        output.append(MCInstruction("save_arg", regs=["$a0"]))
        if is_constant(arg):
            output.append(MCInstruction("li", regs=["$a0"], imm=arg))
        else:
            output.append(MCInstruction("move", regs=["$a0", arg]))

        # moving syscode into v0
        output.append(
            MCInstruction("li", regs=["$v0"], imm=sys_codes["print_int"]))

        # syscall
        output.append(MCInstruction("syscall"))
        output.append(MCInstruction("restore_arg", regs=["$a0"]))

        # restoring a0 and v0
        # output += restore_v0
        # output += restore_a0
        return output
    elif op == "putf":
        raise NotImplementedError(
            "putf() isn't implemented as floats are not supported")
    elif op == "putc":
        # arg = instr.arguments[0]
        arg = args[0]

        # saving a0 and v0
        # save_a0, restore_a0 = save_and_restore("$a0")
        # save_v0, restore_v0 = save_and_restore("$v0")
        # output += save_a0
        # output += save_v0

        # moving argument into a0
        output.append(MCInstruction("save_arg", regs=["$a0"]))
        if is_constant(arg):
            output.append(MCInstruction("li", regs=["$a0"], imm=arg))
        else:
            output.append(MCInstruction("move", regs=["$a0", arg]))

        # moving syscode into v0
        output.append(
            MCInstruction("li", regs=["$v0"], imm=sys_codes["print_char"]))

        # syscall
        output.append(MCInstruction("syscall"))
        output.append(MCInstruction("restore_arg", regs=["$a0"]))

        # restoring a0 and v0
        # output += restore_v0
        # output += restore_a0
        return output
    else:
        raise ValueError("Unexpected intrinsic function: %s" % op)
Ejemplo n.º 11
0
def convert_arithmetic(instr: IRInstruction) -> str:
    """
    Converts an arithmetic instruction to assembly
    """
    assert (instr.is_arithmetic())
    assert (not is_constant(instr.argument_list[0]))

    global s_map

    dest, src0, src1 = instr.argument_list
    output = []
    if instr.instruction_type == "add" or instr.instruction_type == "sub":
        if not is_constant(src0) and not is_constant(src1):
            # neither is constant
            output.append(
                MCInstruction(instr.instruction_type, regs=[dest, src0, src1]))
        elif not is_constant(src0) and is_constant(src1):
            # src1 is constant
            src1 = int(src1)
            if instr.instruction_type == "add":
                output.append(
                    MCInstruction("addi", regs=[dest, src0], imm=src1))
            else:
                output.append(
                    MCInstruction("addi", regs=[dest, src0], imm=-src1))
        elif is_constant(src0) and not is_constant(src1):
            # src0 is constant
            src = int(src0)
            if instr.instruction_type == "add":
                output.append(
                    MCInstruction("addi", regs=[dest, src1], imm=src0))
            else:
                output.append(MCInstruction("li", regs=[dest], imm=src0))
                output.append(MCInstruction("sub", regs=[dest], imm=src1))
        else:
            # both are constant
            src1 = int(src1)
            src0 = int(src0)
            output.append(MCInstruction("li", regs=[dest], imm=src0))
            if instr.instruction_type == "add":
                output.append(MCInstruction("addi", regs=[dest], imm=src1))
            else:
                output.append(MCInstruction("addi", regs=[dest], imm=-src1))
    if instr.instruction_type == "mult":
        if is_constant(src0):
            output.append(
                MCInstruction("li",
                              regs=[s_map["multiply_temp_reg0"]],
                              imm=src0))
            first_op = s_map["multiply_temp_reg0"]
        else:
            first_op = src0

        if is_constant(src1):
            output.append(
                MCInstruction("li",
                              regs=[s_map["multiply_temp_reg1"]],
                              imm=src1))
            second_op = s_map["multiply_temp_reg1"]
        else:
            second_op = src1

        output.append(MCInstruction("mul", regs=[dest, first_op, second_op]))
    if instr.instruction_type == "div":
        if is_constant(src0):
            output.append(
                MCInstruction("li", regs=[s_map["divide_temp_reg0"]],
                              imm=src0))
            first_op = s_map["divide_temp_reg0"]
        else:
            first_op = src0

        if is_constant(src1):
            output.append(
                MCInstruction("li", regs=[s_map["divide_temp_reg1"]],
                              imm=src1))
            second_op = s_map["divide_temp_reg1"]
        else:
            second_op = src1

        output.append(MCInstruction("div", regs=[dest, first_op, second_op]))
    if instr.instruction_type in ["and", "or"]:
        i_type = instr.instruction_type
        if not is_constant(src0) and not is_constant(src1):
            output.append(MCInstruction(i_type, regs=[dest, src0, src1]))
        elif not is_constant(src0) and is_constant(src1):
            output.append(
                MCInstruction(i_type + "i", regs=[dest, src0], imm=src1))
        elif is_constant(src0) and not is_constant(src1):
            output.append(
                MCInstruction(i_type + "i", regs=[dest, src1], imm=src0))
        else:
            output.append(MCInstruction("addi", regs=[dest, "$0"], imm=src0))
            output.append(
                MCInstruction(i_type + "i", regs=[dest, dest], imm=src1))

    return output
Ejemplo n.º 12
0
def convert_label(instr: IRInstruction, func):
    assert (instr.instruction_type == "label")
    return [
        MCInstruction("label", target="%s_%s" % (func, instr.argument_list[0]))
    ]
Ejemplo n.º 13
0
def convert_array_assign(instr, func):
    # TODO: implement
    assert (instr.instruction_type == "array_assign")
    assert (len(instr.argument_list) == 3)
    array, size, value = instr.argument_list

    num = func.curr_array_assign
    func.curr_array_assign += 1

    # temp0 = s_map["array_assign_temp0"]
    # temp1 = s_map["array_assign_temp1"]
    # temp2 = s_map["array_assign_temp2"]

    output = []

    index = s_map["array_assign_index"]
    if is_constant(size):
        output.append(MCInstruction("li", regs=[index], imm=size))
    else:
        output.append(MCInstruction("move", regs=[index, size]))

    address = s_map["array_assign_address"]
    output.append(MCInstruction("move", regs=[address, array]))

    if is_constant(value):
        val_reg = s_map["array_assign_value"]
        output.append(MCInstruction("li", regs=[val_reg], imm=value))
        value = val_reg

    # continue from here
    output.append(
        MCInstruction("label",
                      target=func.name + "_array_assign_loop" + str(num)))
    output.append(
        MCInstruction("blez",
                      regs=[index],
                      target=func.name + "_array_assign_end" + str(num)))

    output.append(MCInstruction("sw", regs=[value, address], imm=0))
    output.append(MCInstruction("addi", regs=[index, index], imm=-1))
    output.append(MCInstruction("addiu", regs=[address, address], imm=4))
    output.append(
        MCInstruction("j", target=func.name + "_array_assign_loop" + str(num)))

    output.append(
        MCInstruction("label",
                      target=func.name + "_array_assign_end" + str(num)))

    # if is_constant(size) and is_constant(value):
    # output.append(MCInstruction("move", regs=[temp0, array]))
    # output.append(MCInstruction("addi", regs=[temp1, temp0], imm=size))
    # output.append(MCInstruction("li", regs=[temp2], imm=value))

    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("bge",regs=[temp0, temp1], target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # output.append(MCInstruction("sw", regs=[temp2, temp0]))
    # output.append(MCInstruction("addi", regs=[temp0, temp0], imm=4))
    # output.append(MCInstruction("j", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # elif is_constant(size) and not is_constant(value):
    # output.append(MCInstruction("move", regs=[temp0, array]))
    # output.append(MCInstruction("add", regs=[temp1, temp0, size]))

    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("bge",regs=[temp0, temp1], target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # output.append(MCInstruction("sw", regs=[value, temp0]))
    # output.append(MCInstruction("addi", regs=[temp0, temp0], imm=4))
    # output.append(MCInstruction("j", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # elif not is_constant(size) and is_constant(value):
    # output.append(MCInstruction("move", regs=[temp0, array]))
    # output.append(MCInstruction("add", regs=[temp1, temp0, size]))
    # output.append(MCInstruction("li", regs=[temp2], imm=value))

    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("bge",regs=[temp0, temp1], target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # output.append(MCInstruction("sw", regs=[temp2, temp0]))
    # output.append(MCInstruction("addi", regs=[temp0, temp0], imm=4))
    # output.append(MCInstruction("j", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # else:
    # output.append(MCInstruction("move", regs=[temp0, array]))
    # output.append(MCInstruction("add", regs=[temp1, temp0, size]))

    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("bge",regs=[temp0, temp1], target="%s_CONVERT_ARRAY_ASSIGN_END" % func))
    # output.append(MCInstruction("sw", regs=[value, temp0]))
    # output.append(MCInstruction("addi", regs=[temp0, temp0], imm=4))
    # output.append(MCInstruction("j", target="%s_CONVERT_ARRAY_ASSIGN_LOOP" % func))
    # output.append(MCInstruction("label", target="%s_CONVERT_ARRAY_ASSIGN_END" % func))

    # del s_map["array_assign_temp0"]
    # del s_map["array_assign_temp1"]
    # del s_map["array_assign_temp2"]

    return output
Ejemplo n.º 14
0
def convert_array_load_store(instr):
    # TODO: implement
    assert (instr.instruction_type in ["array_store", "array_load"])
    assert (len(instr.argument_list) == 3)
    val, array, index = instr.argument_list
    array = array
    output = []

    if instr.instruction_type == "array_load":
        op = "lw"
    else:
        op = "sw"

    if is_constant(index) and not is_constant(val):
        index = int(index)
        output.append(MCInstruction(op, regs=[val, array], offset=index * 4))
    elif is_constant(index) and is_constant(val):
        index = int(index)
        output.append(
            MCInstruction("li", regs=[s_map["array_temp_reg"]], imm=val))
        output.append(
            MCInstruction(op,
                          regs=[s_map["array_temp_reg"], array],
                          offset=index * 4))
        del s_map["array_temp_reg"]
    elif not is_constant(index) and is_constant(val):
        # getting the immediate value into a register
        output.append(
            MCInstruction("li", regs=[s_map["array_temp_reg0"]], imm=val))

        # address calculation
        output.append(
            MCInstruction("move", regs=[s_map["array_temp_reg1"], index]))
        output.append(
            MCInstruction(
                "sll",
                regs=[s_map["array_temp_reg1"], s_map["array_temp_reg1"]],
                imm=2))
        output.append(
            MCInstruction("addu",
                          regs=[
                              s_map["array_temp_reg1"], array,
                              s_map["array_temp_reg1"]
                          ]))
        output.append(
            MCInstruction(
                op,
                regs=[s_map["array_temp_reg0"], s_map["array_temp_reg1"]],
                offset=0))
        del s_map["array_temp_reg0"]
        del s_map["array_temp_reg1"]
    else:
        output.append(
            MCInstruction("move", regs=[s_map["array_temp_reg"], index]))
        output.append(
            MCInstruction(
                "sll",
                regs=[s_map["array_temp_reg"], s_map["array_temp_reg"]],
                imm=2))
        output.append(
            MCInstruction(
                "addu",
                regs=[s_map["array_temp_reg"], array,
                      s_map["array_temp_reg"]]))

        output.append(
            MCInstruction(op, regs=[val, s_map["array_temp_reg"]], offset=0))
        del s_map["array_temp_reg"]
    return output
Ejemplo n.º 15
0
def calling_convention(
    function: MCFunction
) -> (List[MCInstruction], List[MCInstruction], Dict[str, int]):
    """
    Handles the callee portion of the calling convention for a particular function. Note that this does not handle any of the caller responsibilities. Those should be handled by the code that deals with `call` and `callr` instructions.

    Currently arrays are always spilled

    Args:
        function: The function whose callee saving and restoring we are doing
        save: Whether we should produce the saving or the restoring code

    Returns:
        A list of MCInstruction
        A dictionary map of spilled virtual register names to its location on the stack
    """
    # if not function.has_data:
    # return None

    # prologue of the calling convention
    prologue = []
    offsets = {}
    sp = "$sp"
    fp = "$fp"

    # prologue

    # make space for fp and save
    prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
    prologue.append(MCInstruction("sw", regs=[fp, sp], offset=0))
    prologue.append(MCInstruction("move", regs=[fp, sp]))

    curr_offset = -4
    # make space for arg registers
    for i in range(4):
        arg_reg = "$a%d" % i
        # prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        offsets[arg_reg] = curr_offset
        curr_offset -= 4

    # make space for arrays
    arr_names = []
    for arr in function.int_arrs:
        arr_names.append(arr[0])
        prologue += alloc_array(arr[0], arr[1], curr_offset)
        offsets[arr[0]] = curr_offset
        curr_offset -= 4

    # make space for local variables
    # for greedy local alloc, need to save everything (even non-spilled)
    for val in function.int_vals:
        if val not in arr_names:
            # prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
            offsets[val] = curr_offset
            curr_offset -= 4

    # save all the t registers (as specified in the pdf)
    for i in range(10):
        t_reg = "$t%d" % i
        # if function.name != "main":
        # prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        # prologue.append(MCInstruction("sw", regs=[t_reg, sp], imm=0))
        prologue.append(
            MCInstruction("sw", regs=[t_reg, fp], offset=curr_offset))
        offsets[t_reg] = curr_offset
        curr_offset -= 4
        # else:
        # prologue.append(MCInstruction("li", regs=[t_reg], imm=0))

    # padding
    if needs_pad(function):
        # prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        curr_offset -= 4

    if function.name != "main":
        # return address
        # prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        prologue.append(
            MCInstruction("sw", regs=["$ra", fp], offset=curr_offset))
        offsets["$ra"] = curr_offset
        curr_offset -= 4

    # s registers
    for s in function.saved_regs:
        # prologue.append(MCInstruction("addiu", regs=[sp, sp], imm=-4))
        prologue.append(MCInstruction("sw", regs=[s, fp], offset=curr_offset))
        offsets[s] = curr_offset
        curr_offset -= 4

    # now, finally move the sp
    prologue.append(MCInstruction("addiu", regs=[sp, fp], imm=curr_offset + 4))

    # epilogue
    epilogue = []

    # restore the s registers
    # for s in function.saved_regs[::-1]:
    for s in function.saved_regs:
        epilogue.append(MCInstruction("lw", regs=[s, fp], offset=offsets[s]))

    if function.name != "main":
        # restore the return address
        epilogue.append(
            MCInstruction("lw", regs=["$ra", fp], offset=offsets["$ra"]))
        # epilogue.append(MCInstruction("addiu", regs=[sp, sp], imm=4))

    # restore t registers
    for i in range(10):
        t_reg = "$t%d" % i
        epilogue.append(
            MCInstruction("lw", regs=[t_reg, fp], offset=offsets[t_reg]))

    # restore the fp
    epilogue.append(MCInstruction("move", regs=[sp,
                                                fp]))  # moving sp back to fp
    epilogue.append(MCInstruction("lw", regs=[fp, sp], offset=0))
    epilogue.append(MCInstruction("addiu", regs=[sp, sp], imm=4))

    return prologue, epilogue, offsets
Ejemplo n.º 16
0
def spill(
    reg_map: Dict[str, str],
    instr: MCInstruction,
    offsets: Dict[str, int],
    optimize=False
) -> (List[MCInstruction], List[MCInstruction], Dict[str, str]):
    """
    Performs spilling given a register map and the set of original virtual registers, with some additional information. At the end, outputs the spilling code as well as the complete register mapping (with every virtual register mapped to a physical register). Uses t0-t2 for spilling (since a single instruction should not have more than three registers).

    Args:
        reg_map: the register mapping given by the allocator
        instr: the MCInstruction to perform spilling on
        offsets: the map from virtual register to the frame pointer offset on the stack
        load: whether this is for loading or storing

    Returns:
        save: a list of MCInstructions which is the spill save code
        restore: a list of MCInstructions which is the spill restore code
        new_args: the physical registers of the instruction
    """
    # NOTE: it's assumed that the position at $fp will be used for saving the register used here
    save_temp = []
    load_virt = []
    save_virt = []
    load_temp = []
    new_args = []

    curr_temp = 9
    # for phys, ir in zip(phys_reg, ir_regs):
    new_map = {}

    # compute used temp regs in the instruction
    used_temps = set()
    for virt in instr.regs:
        if virt[0] == "$":
            used_temps.add(virt)
        else:
            phys = reg_map[virt]
            if phys != "spill":
                used_temps.add(phys)

    is_spilled = False
    for virtual in instr.regs:
        if virtual not in new_map:
            if virtual[0] == "$":
                new_args.append(virtual)
                new_map[virtual] = virtual
            else:
                physical = reg_map[virtual]
                if physical == "spill":
                    is_spilled = True
                    temp_reg = "$t%d" % curr_temp

                    while temp_reg in used_temps:
                        curr_temp -= 1
                        assert (curr_temp < 10)
                        temp_reg = "$t%d" % curr_temp
                    # used_temps.add(temp_reg)

                    if optimize:
                        temp_needs_save = temp_reg in reg_map.values()
                        virt_needs_load = virtual in instr.get_uses()
                    else:
                        temp_needs_save = True
                        virt_needs_load = True

                    if temp_needs_save:
                        save_temp.append(
                            MCInstruction("sw",
                                          regs=[temp_reg, "$fp"],
                                          offset=offsets[temp_reg]))

                    if virt_needs_load:
                        load_virt.append(
                            MCInstruction("lw",
                                          regs=[temp_reg, "$fp"],
                                          offset=offsets[virtual]))

                    save_virt.append(
                        MCInstruction("sw",
                                      regs=[temp_reg, "$fp"],
                                      offset=offsets[virtual]))

                    if temp_needs_save:
                        load_temp.append(
                            MCInstruction("lw",
                                          regs=[temp_reg, "$fp"],
                                          offset=offsets[temp_reg]))

                    new_args.append(temp_reg)
                    new_map[virtual] = temp_reg
                    curr_temp -= 1
                else:
                    new_args.append(physical)
                    new_map[virtual] = physical
        else:
            new_args.append(new_map[virtual])

    prologue = save_temp + load_virt
    epilogue = save_virt + load_temp

    # if is_spilled:
    # print("used temps: ", used_temps)
    # print(reg_map)
    # print(offsets)
    # print("prologue:")
    # for i in prologue:
    # print(i)

    # print("instr: ")

    # new_instr = instr
    # new_instr.regs = new_args
    # print(new_instr)

    # print("epilogue:")
    # for i in epilogue:
    # print(i)
    # print()
    # print()
    # print()

    return prologue, epilogue, new_args