Exemplo n.º 1
0
 def to_operand(self):
     return operand.IndirectDisplaced(Reg.RBP, (self.offset * 8), operand.OperandSize.Qword)
Exemplo n.º 2
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)