コード例 #1
0
 def clear_register(self, insts: List[Instruction], reg: Reg):
     free_reg = None  # Defer finding free reg until actually needed
     for index, value in enumerate(self.stack):
         if isinstance(value, Register) and value.reg == reg:
             if free_reg is None:
                 free_reg = self.find_free_register(insts, X64DataManager.ALL_REGISTERS)
             self.stack[index] = Register(free_reg)
     for index, value in enumerate(self.args):  # Also for arguments
         if isinstance(value, Register) and value.reg == reg:
             if free_reg is None:
                 free_reg = self.find_free_register(insts, X64DataManager.ALL_REGISTERS)
             self.args[index] = Register(free_reg)
     if free_reg is not None:
         insts.append(Instruction(Mnemonic.MOV, operand.Direct(free_reg), operand.Direct(reg)))
コード例 #2
0
    def move_register_to_stack_val(self, insts: List[Instruction], reg: Reg, value: X64Data):
        insts.append(Instruction(Mnemonic.MOV, value.to_operand(), operand.Direct(reg)))

        for index, v in enumerate(self.stack):
            if isinstance(v, Register) and v.reg == reg:
                self.stack[index] = value  # Update references on virtual stack
        for index, v in enumerate(self.args):
            if isinstance(v, Register) and v.reg == reg:
                self.args[index] = value  # Update references in args
コード例 #3
0
    def move_stack_val_to_register(self, insts: List[Instruction], value: X64Data, allowed_regs: List[Reg]):
        free_reg = self.find_free_register(insts, allowed_regs)
        insts.append(Instruction(Mnemonic.MOV, operand.Direct(free_reg), value.to_operand()))

        for index, v in enumerate(self.stack):
            if v.equals(value):  # Update references on virtual stack
                self.stack[index] = Register(free_reg)
        for index, v in enumerate(self.args):
            if v.equals(value):  # Update references in args
                self.args[index] = Register(free_reg)
        return free_reg
コード例 #4
0
 def add_function_call(self, insts: List[Instruction], is_aligned: bool, fun_name: str):
     if is_aligned:
         insts.append(Instruction(Mnemonic.CALL, operand.Label(fun_name)))
     else:  # Align stack to 16 bytes
         insts.append(Instruction(Mnemonic.SUB, operand.Direct(Reg.RSP), operand.Literal(8)))
         insts.append(Instruction(Mnemonic.CALL, operand.Label(fun_name)))
         insts.append(Instruction(Mnemonic.ADD, operand.Direct(Reg.RSP), operand.Literal(8)))
コード例 #5
0
 def copy_to_register(self, insts: List[Instruction], offset: int, allowed_regs: List[Reg]):
     stack_offset = len(self.stack) - 1 - offset
     stack_item = self.stack[stack_offset]
     if isinstance(stack_item, Register):
         occs = len([x for x in itertools.chain(self.stack, self.args) if
                     isinstance(x, Register) and x.reg == stack_item.reg])
         if stack_item.reg in allowed_regs and occs == 1:
             # Only 1 occurence of this register on stack, so no need to move
             return stack_item.reg
     free_reg = self.find_free_register(insts, allowed_regs)
     insts.append(Instruction(Mnemonic.MOV, operand.Direct(free_reg), stack_item.to_operand()))
     self.stack[stack_offset] = Register(free_reg)
     return free_reg
コード例 #6
0
    def restore_arguments(self, insts: List[Instruction], label: gen_utils.Label):
        assert len(self.stack) == 0, 'Stack must be empty when restoring arguments'
        if (args := self.restore_points.get(label.id)) is not None:
            assert len(self.args) == len(args), 'Error while restoring arguments: lengths are not the same!'
            diff = [i for i, (x, y) in enumerate(zip(args, copy.copy(self.args))) if not x.equals(y)]
            if len(diff) == 0:
                return  # Arguments are the same already, no need to restore anything

            num_reserved_push = 0
            for i in diff:
                from_arg = self.args[i]
                to_arg = args[i]
                if isinstance(to_arg, Register):
                    num_reserved_push += 1
                    self.move_stack_val_to_register(insts, from_arg, [to_arg.reg])
                    self.reserved_registers.append(to_arg.reg)
                elif isinstance(to_arg, StackLocal):
                    tmp_reg = self.move_stack_val_to_register(insts, from_arg, self.ALL_REGISTERS)
                    insts.append(Instruction(Mnemonic.MOV, to_arg.to_operand(), operand.Direct(tmp_reg)))
                else:
                    raise Exception('Argument cannot be a constant')
            for _ in range(num_reserved_push):
                self.reserved_registers.pop()
コード例 #7
0
    def generate_function_code(self, fun_instance: FunctionInstance, fun_impl: FunctionImpl):
        gen_type = 'entry point' if fun_instance.entry_point else 'function'
        Logger.debug(f'Generating code for {gen_type} \'{fun_instance.name}\' '
                     f'(instance: \'{fun_instance.create_identifier()}\')')
        if fun_instance.entry_point:
            self.declare_globals(fun_impl)
        insts = []
        local_vars_size = 0
        for op in fun_impl.ops:
            if isinstance(op, gen_codes.LdLoc):
                off = op.local.offset() + 1
                if off > local_vars_size:
                    local_vars_size = off
            elif isinstance(op, gen_codes.StLoc):
                off = op.local.offset() + 1
                if off > local_vars_size:
                    local_vars_size = off

        dm = X64DataManager(len(fun_instance.arg_types), local_vars_size)

        dm.add_push_instr(insts, operand.Direct(Reg.RBP))  # Save rbp
        insts.append(Instruction(Mnemonic.MOV, operand.Direct(Reg.RBP),
                                 operand.Direct(Reg.RSP)))  # Use rbp for access to local vars

        # Store object ref to update stack size later
        stack_size = local_vars_size * 8 if (local_vars_size * 8) % 16 == 0 else (local_vars_size * 8) + 8
        stack_size_op = operand.Literal(stack_size)
        insts.append(Instruction(Mnemonic.SUB, operand.Direct(Reg.RSP), stack_size_op))  # Make room for local vars

        # Push callee preserved regs to stack to be able to restore at function end
        for reg in self.callee_preserved_regs:
            dm.add_push_instr(insts, operand.Direct(reg))

        for op in fun_impl.ops:
            self.map_generic_to_ssm(fun_instance, dm, op, insts)
        self.x64_code['f_' + fun_instance.create_identifier() if not fun_instance.entry_point else '_main'] = insts

        # Update stack size after knowing how much was used
        stack_usage = dm.max_stack_used * 8
        if stack_usage > stack_size_op.lit:
            stack_size_op.lit = stack_usage if stack_usage % 16 == 0 else stack_usage + 8
コード例 #8
0
 def to_operand(self):
     return operand.Literal(self.i)
コード例 #9
0
 def to_operand(self):
     return operand.Direct(self.reg)
コード例 #10
0
 def to_operand(self):
     return operand.IndirectDisplaced(Reg.RBP, (self.offset * 8), operand.OperandSize.Qword)
コード例 #11
0
    def map_generic_to_ssm(self, fun_inst: FunctionInstance, dm: X64DataManager,
                           gen_op: gen_codes.GenericOpCode, insts: List[Instruction]):
        if isinstance(gen_op, gen_codes.Add) \
                or isinstance(gen_op, gen_codes.Sub) \
                or isinstance(gen_op, gen_codes.And) \
                or isinstance(gen_op, gen_codes.Or)\
                or isinstance(gen_op, gen_codes.Mul):  # add, sub, and, or, mul op1 with op2, put result in op1 register
            op2 = dm.pop()
            dm.reserve_if_register(op2)
            dm.copy_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            op1 = dm.pop()
            dm.reserve_if_register(op1)
            if isinstance(gen_op, gen_codes.Add):
                instr = Mnemonic.ADD
            elif isinstance(gen_op, gen_codes.Sub):
                instr = Mnemonic.SUB
            elif isinstance(gen_op, gen_codes.And):
                instr = Mnemonic.AND
            elif isinstance(gen_op, gen_codes.Mul):
                instr = Mnemonic.IMUL
            else:
                instr = Mnemonic.OR
            insts.append(Instruction(instr, op1.to_operand(), op2.to_operand()))
            dm.push(op1)
            dm.release_if_register(op1)
            dm.release_if_register(op2)
        # elif isinstance(gen_op, gen_codes.Mul):  # multiply op1 and op2, put result in dest register
        #     op2 = dm.pop()
        #     dm.reserve_if_register(op2)
        #     dm.copy_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
        #     op1 = dm.pop()
        #     dm.reserve_if_register(op1)
        #     dest = dm.find_free_register(insts, X64DataManager.ALL_REGISTERS)
        #     insts.append(Instruction(Mnemonic.IMUL, operand.Direct(dest), op1.to_operand(), op2.to_operand()))
        #     dm.push(Register(dest))
        #     dm.release_if_register(op1)
        #     dm.release_if_register(op2)
        elif isinstance(gen_op, gen_codes.Div) or isinstance(gen_op, gen_codes.Mod):  # div and mod
            # IDIV divides RDX:RAX by first operand, puts result in RAX and remainder in RDX
            dm.reserve_register(Reg.RDX)  # divide value in RDX:RAX, so reserve RDX
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            divisor = dm.pop()
            dm.reserve_if_register(divisor)
            dm.copy_to_register(insts, 0, [Reg.RAX])
            val = dm.pop()
            dm.reserve_if_register(val)  # divide value in RDX:RAX, so reserve RAX
            insts.append(Instruction(Mnemonic.XOR, operand.Direct(Reg.RDX), operand.Direct(Reg.RDX)))  # Set RDX to 0
            insts.append(Instruction(Mnemonic.IDIV, divisor.to_operand()))
            reg_to_push = Reg.RAX if isinstance(gen_op, gen_codes.Div) else Reg.RDX  # If div, use RAX, if mod use RDX
            dm.push(Register(reg_to_push))
            dm.release_if_register(divisor)
            dm.release_if_register(val)
            dm.release_register(Reg.RDX)
        elif isinstance(gen_op, gen_codes.Eq) or isinstance(gen_op, gen_codes.Ge) \
                or isinstance(gen_op, gen_codes.Gt) or isinstance(gen_op, gen_codes.Le) \
                or isinstance(gen_op, gen_codes.Lt) or isinstance(gen_op, gen_codes.Ne):  # comparisons

            target = dm.find_free_register(insts, X64DataManager.ALL_REGISTERS)
            dm.reserve_register(target)
            op2 = dm.pop()
            dm.reserve_if_register(op2)
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            op1 = dm.pop()
            dm.reserve_if_register(op1)
            insts.append(Instruction(Mnemonic.XOR, operand.Direct(target), operand.Direct(target)))  # Set to 0 (False)
            insts.append(Instruction(Mnemonic.CMP, op1.to_operand(), op2.to_operand()))
            mov = Instruction(Mnemonic.MOV, operand.Direct(target), operand.Literal(-1))  # Set to -1 (True)

            if isinstance(gen_op, gen_codes.Eq):  # Decide which jump instruction to use
                m = Mnemonic.JNE  # Jump if not equal
            elif isinstance(gen_op, gen_codes.Ge):
                m = Mnemonic.JL  # Jump if less
            elif isinstance(gen_op, gen_codes.Gt):
                m = Mnemonic.JLE  # Jump if less or equal
            elif isinstance(gen_op, gen_codes.Le):
                m = Mnemonic.JG  # Jump if greater
            elif isinstance(gen_op, gen_codes.Lt):
                m = Mnemonic.JGE  # Jump if greater or equal
            elif isinstance(gen_op, gen_codes.Ne):
                m = Mnemonic.JE  # Jump if equal
            else:
                raise Exception('Should not happen')
            label = operand.Label(dm.fresh_label(fun_inst))
            insts.append(Instruction(m, label))  # add jump instr to jump to label
            insts.append(mov)  #
            insts.append(Instruction(Mnemonic.MARK_LABEL, label))  # Mark label to skip setting reg to -1
            dm.push(Register(target))
            dm.release_register(target)
            dm.release_if_register(op2)
            dm.release_if_register(op1)

        elif isinstance(gen_op, gen_codes.Not):
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.reserve_if_register(val)
            insts.append(Instruction(Mnemonic.NOT, val.to_operand()))
            dm.push(val)
            dm.release_if_register(val)
        elif isinstance(gen_op, gen_codes.Neg):  # replace val of op with 2's complement,
            # equivalent to subtracting op from 0
            dm.copy_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.reserve_if_register(val)
            if isinstance(val, Constant):
                dm.push(Constant(-val.i))
            else:
                insts.append(Instruction(Mnemonic.NEG, val.to_operand()))
                dm.push(val)
            dm.release_if_register(val)
        elif isinstance(gen_op, gen_codes.Swp):  # Swap two top values on virtual stack
            val1 = dm.pop()
            val2 = dm.pop()
            dm.push(val1)
            dm.push(val2)
        elif isinstance(gen_op, gen_codes.Pop):  # Pop value from virtual stack
            dm.pop()
        elif isinstance(gen_op, gen_codes.PushConst):  # Add constant to virtual stack
            dm.push(Constant(gen_op.const))
        elif isinstance(gen_op, gen_codes.Br):  # Jump to label
            dm.restore_arguments(insts, gen_op.label)
            insts.append(Instruction(Mnemonic.JMP, operand.Label(gen_op.label.get_distinct_name(fun_inst))))
        elif isinstance(gen_op, gen_codes.BrEq) or isinstance(gen_op, gen_codes.BrNe) \
                or isinstance(gen_op, gen_codes.BrGe) or isinstance(gen_op, gen_codes.BrGt)\
                or isinstance(gen_op, gen_codes.BrLe or isinstance(gen_op, gen_codes.BrLt)):
            op2 = dm.pop()
            dm.reserve_if_register(op2)
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            op1 = dm.pop()
            dm.reserve_if_register(op1)
            insts.append(Instruction(Mnemonic.CMP, op1.to_operand(), op2.to_operand()))
            dm.restore_arguments(insts, gen_op.label)

            if isinstance(gen_op, gen_codes.BrEq):
                mnemonic = Mnemonic.JE
            elif isinstance(gen_op, gen_codes.BrNe):
                mnemonic = Mnemonic.JNE
            elif isinstance(gen_op, gen_codes.BrGe):
                mnemonic = Mnemonic.JGE
            elif isinstance(gen_op, gen_codes.BrGt):
                mnemonic = Mnemonic.JG
            elif isinstance(gen_op, gen_codes.BrLe):
                mnemonic = Mnemonic.JLE
            elif isinstance(gen_op, gen_codes.BrLt):
                mnemonic = Mnemonic.JL
            else:
                raise Exception('Should not happen')
            insts.append(Instruction(mnemonic, gen_op.label.get_distinct_name(fun_inst)))
            dm.release_if_register(op1)
            dm.release_if_register(op2)
        elif isinstance(gen_op, gen_codes.BrTrue):
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.restore_arguments(insts, gen_op.label)
            insts.append(Instruction(Mnemonic.CMP, val.to_operand(), operand.Literal(0)))  # CMP with 0 (false)
            insts.append(Instruction(Mnemonic.JNE, gen_op.label.get_distinct_name(fun_inst)))  # Jump if not equal
        elif isinstance(gen_op, gen_codes.BrFalse):
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.restore_arguments(insts, gen_op.label)
            insts.append(Instruction(Mnemonic.CMP, val.to_operand(), operand.Literal(0)))  # CMP with 0 (false)
            insts.append(Instruction(Mnemonic.JE, gen_op.label.get_distinct_name(fun_inst)))  # Jump if equal
        elif isinstance(gen_op, gen_codes.MarkLabel):
            dm.restore_arguments(insts, gen_op.label)
            insts.append(Instruction(Mnemonic.MARK_LABEL, operand.Label(gen_op.label.get_distinct_name(fun_inst))))
        elif isinstance(gen_op, gen_codes.PrintInt):
            # Move int from virtual stack to SECOND arg register (RSI), first is for printf format
            dm.move_to_register(insts, 0, [X64DataManager.ARGUMENT_REGISTERS[1]])
            val = dm.pop()
            dm.reserve_if_register(val)
            for r in self.caller_preserved_regs:  # Save values in caller preserved registers
                dm.clear_register(insts, r)
                dm.reserve_register(r)
            # Load printf format into RDI (which is already cleared above)
            insts.append(Instruction(Mnemonic.LEA, operand.Direct(Reg.RDI), operand.IndirectVar('int_format')))
            self.add_function_call(insts, dm.is_aligned(), '_printf')
            self.externals['_printf'] = True
            for r in self.caller_preserved_regs:
                dm.release_register(r)
            dm.release_if_register(val)
        elif isinstance(gen_op, gen_codes.PrintChar):
            # Move char from virtual stack to first arg register (RDI)
            dm.move_to_register(insts, 0, [X64DataManager.ARGUMENT_REGISTERS[0]])
            val = dm.pop()
            dm.reserve_if_register(val)
            for r in self.caller_preserved_regs:  # Save values in caller preserved registers
                dm.clear_register(insts, r)
                dm.reserve_register(r)
            self.add_function_call(insts, dm.is_aligned(), '_putchar')  # Call C function 'putchar'
            self.externals['_putchar'] = True
            for r in self.caller_preserved_regs:
                dm.release_register(r)
            dm.release_if_register(val)
        elif isinstance(gen_op, gen_codes.Call):
            args_on_stack_bytes = 0
            for i in reversed(range(gen_op.f.num_args)):  # Place fun args in registers (and on stack)
                if i < len(X64DataManager.ARGUMENT_REGISTERS):
                    dm.move_to_register(insts, 0, [X64DataManager.ARGUMENT_REGISTERS[i]])
                    dm.pop()
                    dm.reserve_register(X64DataManager.ARGUMENT_REGISTERS[i])
                else:
                    dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
                    reg = dm.pop()
                    dm.reserve_if_register(reg)
                    dm.add_push_instr(insts, reg.to_operand())
                    args_on_stack_bytes += 8

            dm.clear_register(insts, Reg.RAX)
            dm.reserve_register(Reg.RAX)
            for r in self.caller_preserved_regs_without_rax:
                dm.clear_register(insts, r)
                dm.reserve_register(r)
            self.add_function_call(insts, dm.is_aligned(), str(gen_op.f))
            dm.push(Register(Reg.RAX))
            for r in self.caller_preserved_regs_without_rax:
                dm.release_register(r)

            if args_on_stack_bytes > 0:  # 'pop' arguments on stack
                insts.append(Instruction(Mnemonic.ADD, operand.Direct(Reg.RSP), operand.Literal(args_on_stack_bytes)))
                dm.current_stack_alignment -= args_on_stack_bytes

            dm.release_register(Reg.RAX)
            for i in reversed(range(gen_op.f.num_args)):
                if i < len(X64DataManager.ARGUMENT_REGISTERS):
                    dm.release_register(X64DataManager.ARGUMENT_REGISTERS[i])

        elif isinstance(gen_op, gen_codes.LdLoc):
            if gen_op.local.offset() < 0:
                dm.push_arg(len(fun_inst.arg_types) + gen_op.local.offset())  # Function argument
            else:
                dm.push(StackLocal(-gen_op.local.offset() - 1))  # Local variable
        elif isinstance(gen_op, gen_codes.StLoc):
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.reserve_if_register(val)
            if gen_op.local.offset() < 0:  # If offset < 0, then it's a function argument
                arg = dm.args[len(fun_inst.arg_types) + gen_op.local.offset()]
                insts.append(Instruction(Mnemonic.MOV, arg.to_operand(), val.to_operand()))
            else:  # Local variable
                insts.append(Instruction(Mnemonic.MOV,
                                         operand.IndirectDisplaced(Reg.RBP, (-gen_op.local.offset() - 1) * 8,
                                                                   operand.OperandSize.Qword), val.to_operand()))
            dm.release_if_register(val)

        elif isinstance(gen_op, gen_codes.LdGlob):
            reg = dm.find_free_register(insts, X64DataManager.ALL_REGISTERS)
            assert gen_op.glob.id in self.globals, f'Global with id {gen_op.glob.id} should be known, but isn\'t!'
            insts.append(Instruction(Mnemonic.MOV, operand.Direct(reg),
                                     operand.IndirectVar(self.globals[gen_op.glob.id].name, operand.OperandSize.Qword)))
            dm.push(Register(reg))
        elif isinstance(gen_op, gen_codes.StGlob):
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.reserve_if_register(val)
            assert gen_op.glob.id in self.globals, f'Global with id {gen_op.glob.id} should be known, but isn\'t!'
            insts.append(Instruction(Mnemonic.MOV, operand.IndirectVar(self.globals[gen_op.glob.id].name,
                                                                       operand.OperandSize.Qword), val.to_operand()))
            dm.release_if_register(val)

        elif isinstance(gen_op, gen_codes.CreateListCons) or isinstance(gen_op, gen_codes.CreateTuple):
            dm.clear_register(insts, Reg.RAX)  # Clear RAX because its used for result of malloc
            dm.reserve_register(Reg.RAX)
            dm.clear_register(insts, Reg.RDI)  # Clear RDI because its used for 1st arg of call to malloc
            dm.reserve_register(Reg.RDI)

            for r in self.caller_preserved_regs_without_rax:  # Clear registers that might be overwritten
                dm.clear_register(insts, r)
                dm.reserve_register(r)
            # We want to allocate space for 2 ints, so 16 bytes
            insts.append(Instruction(Mnemonic.MOV, operand.Direct(Reg.RDI), operand.Literal(16)))
            self.add_function_call(insts, dm.is_aligned(), '_malloc')  # Call C function malloc
            self.externals['_malloc'] = True  # Let compiler know to include extern _malloc
            for r in self.caller_preserved_regs_without_rax:
                dm.release_register(r)

            # First write second list/tuple element to offset 8 of 16, then first element to offset 0 of 16
            for offset in [8, 0]:
                dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
                val = dm.pop()
                dm.reserve_if_register(val)
                insts.append(Instruction(Mnemonic.MOV, operand.IndirectDisplaced(Reg.RAX, offset, operand.OperandSize.Qword),
                                         val.to_operand()))
                dm.release_if_register(val)
            dm.push(Register(Reg.RAX))  # Push address to virtual stack

            dm.release_register(Reg.RDI)
            dm.release_register(Reg.RAX)
        elif isinstance(gen_op, gen_codes.CreateListNil):
            dm.push(Constant(0))
        elif isinstance(gen_op, gen_codes.LdFld):
            reg = dm.find_free_register(insts, X64DataManager.ALL_REGISTERS)
            dm.clear_register(insts, reg)
            dm.reserve_register(reg)
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.reserve_if_register(val)

            op2_dis = {
                FieldType.Fst: 0,
                FieldType.Hd: 0,
                FieldType.Snd: 8,
                FieldType.Tl: 8,
            }.get(gen_op.field_type)
            assert isinstance(val, Register)
            insts.append(Instruction(Mnemonic.MOV, operand.Direct(reg),
                                     operand.IndirectDisplaced(val.reg, op2_dis, operand.OperandSize.Qword)))
            dm.push(Register(reg))
            dm.release_if_register(val)
            dm.release_register(reg)
        elif isinstance(gen_op, gen_codes.StFld):
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            val = dm.pop()
            dm.reserve_if_register(val)
            dm.move_to_register(insts, 0, X64DataManager.ALL_REGISTERS)
            addr = dm.pop()
            dm.reserve_if_register(addr)

            op_dis = {
                FieldType.Fst: 0,
                FieldType.Hd: 0,
                FieldType.Snd: 8,
                FieldType.Tl: 8,
            }.get(gen_op.field_type)
            assert isinstance(addr, Register)
            insts.append(Instruction(Mnemonic.MOV,
                                     operand.IndirectDisplaced(addr.reg, op_dis, operand.OperandSize.Qword),
                                     val.to_operand()))
            dm.release_if_register(val)
            dm.release_if_register(addr)
        elif isinstance(gen_op, gen_codes.Ret) or isinstance(gen_op, gen_codes.RetNoValue) \
                or isinstance(gen_op, gen_codes.Halt):

            if isinstance(gen_op, gen_codes.Ret):  # Move result to RAX before returning
                dm.move_to_register(insts, 0, [Reg.RAX])
                val = dm.pop()
                dm.reserve_if_register(val)

            # Pop callee preserved regs again to restore value
            for reg in reversed(self.callee_preserved_regs):
                dm.add_pop_instr(insts, operand.Direct(reg))

            # Restore stack pointer
            insts.append(Instruction(Mnemonic.MOV, operand.Direct(Reg.RSP), operand.Direct(Reg.RBP)))
            # Restore stack base pointer
            dm.add_pop_instr(insts, operand.Direct(Reg.RBP))
            insts.append(Instruction(Mnemonic.RET))

            if isinstance(gen_op, gen_codes.Ret):  # Release RAX
                dm.release_if_register(val)