def handle_move(self, label, result, *operands): """Handles the move instruction of IR. """ source = operands[0] # If both the operands are memory locations, use %r15 as an intermediate # register if isinstance(source, Memory) and isinstance(operands[1], Memory): r15 = Register() r15.color = 13 mov = MOV(r15, operands[0]) self.add_instruction(label, mov) source = r15 mov = MOV(operands[1], source) self.add_instruction(label, mov)
def handle_ret(self, label, result, *operands): """Handles the return instruction of IR. """ # Return is nothing but the jump to the end of the function. # If it has an operand it should be returned in %rax, according to Linux # Application Binary Interface (ABI). if operands: # 0 is the Register color for %rax. if operands[0]: # Create a dummy register for %rax. rax = Register() rax.color = 0 mov = MOV(rax, operands[0]) self.add_instruction(label, mov) jmp = JMP() self.add_instruction(label, jmp) self.returns_to_process.append(jmp)
def create_stack(self): """Creates the stack with the current memory offset value. """ # Create a dummy register and give it a color to keep the API consistent. rbp = Register() rbp.color = 'rbp' # The color of %rbp push = PUSH(rbp) self.add_instruction(0, push) # Another dummy register for %rsp rsp = Register() rsp.color = 'rsp' mov = MOV(rbp, rsp) self.add_instruction(0, mov) if self.memory_offset: # Allocate memory for locals sub = SUB(rsp, Immediate(self.memory_offset)) self.add_instruction(0, sub)
def entry(): """Returns the precompiled code for starting the code. """ call = CALL() # Move exit status to %rdi rdi = Register() rdi.color = 5 mov_status = MOV(rdi, Immediate(0)) # Move exit SYSCALL descriptor number to %rax rax = Register() rax.color = 0 mov_sycall = MOV(rax, Immediate(60)) # Make syscall syscall = SYSCALL() return (len(call) + len(mov_status) + len(mov_sycall) + len(syscall), [call, mov_status, mov_sycall, syscall])
def handle_cmp(self, label, result, *operands): """Handles the cmp instruction of IR. """ dest = operands[0] if isinstance(operands[0], Memory) and isinstance(operands[1], Memory): mov = MOV(result, operands[0]) self.add_instruction(label, mov) dest = result elif isinstance(operands[0], Immediate): # FIXME: If the destination is an immediate operand, this is moved to # a temporary scratch register and then compared, however this calls for # a better mechanism. Please see issue #6. r15 = Register() r15.color = 13 mov = MOV(r15, operands[0]) self.add_instruction(label, mov) dest = r15 cmp_instruction = CMP(dest, operands[1]) self.add_instruction(label, cmp_instruction)
def handle_store(self, label, result, *operands): """Handles the store instruction of IR. """ if isinstance(operands[1], Immediate) and isinstance(operands[2], Memory): memory = Memory( base=operands[2].base, offset=operands[2].offset + (operands[1].value * MEMORY_WIDTH)) elif isinstance(operands[1], Register): memory = Memory() memory.base = operands[2] memory.offset = operands[1] elif isinstance(operands[1], Memory): memory = operands[1] memory.offset = self.memory_offset self.memory_offset += MEMORY_WIDTH if isinstance(operands[0], Memory) or (isinstance(operands[0], Register) and isinstance(operands[0].color, Memory)): # Take the set difference free_registers = (REGISTERS_COLOR_SET - set(self.instruction_live_registers[label].keys())) if free_registers: # Create a dummy register for this color register = Register() # pop and add back since sets don't support indexing register.color = free_registers.pop() free_registers.add(register.color) mov1 = MOV(register, operands[0]) self.add_instruction(label, mov1) mov2 = MOV(memory, register) self.add_instruction(label, mov2) else: # We don't have any free registers :( # We can't do much, randomly pick a register, push it to the stack, # do the memory move from the source to this register and then use # this register to move the source to the destination and then # pop back our register. # NOTE: We pick %rax for this temporary operation # Create a dummy register for %rax register = Register() register.color = 0 push = PUSH(register) self.add_instruction(label, push) mov1 = MOV(register, operands[0]) self.add_instruction(label, mov1) mov2 = MOV(memory, register) self.add_instruction(label, mov2) pop = POP(register) self.add_instruction(label, pop) else: register = operands[0] mov = MOV(memory, register) self.add_instruction(label, mov)
def handle_div(self, label, result, *operands): """Handles the div instruction of IR. """ restore_rax = True restore_rdx = True # This will be true only if the second operand is immediate. restore_r15 = False # Create dummy register objects for RAX and RDX and force color them to # ensure they take RAX and RDX values. So we can spill these two registers. # Also create memory object to where they get spilled. if self.is_register(result): if result.color == 0: restore_rax = False elif result.color == 3: restore_rdx = False # We may want %rax register to move the first dividend to %rax if it is # not already in %rax, so create a dummy %rax register in any case. rax = Register() rax.color = 0 # Color of RAX # We want to clear the contents of %rdx no matter what, so create a dummy # register for it. rdx = Register() rdx.color = 3 # Color of RDX # Store %rax and %rdx in memory if they have to be restored if restore_rax: rax.memory = Memory() rax_operands = (rax, rax.memory) self.handle_store(label, None, *rax_operands) if restore_rdx: rdx.memory = Memory() rdx_operands = (rdx, rdx.memory) self.handle_store(label, None, *rdx_operands) if not (self.is_register(operands[0]) and operands[0].color == 0): mov = MOV(rax, operands[0]) self.add_instruction(label, mov) # Clear %rdx, because x86_64 DIV instruction uses both %rax and %rdx as # the divisor xor = XOR(rdx, rdx) self.add_instruction(label, xor) if isinstance(operands[1], Immediate): # Create a dummy register object for %r15 for moving the operand to it. r15 = Register() r15.color = 13 if not(self.is_register(result) and result.color == 13): restore_r15 = True r15.memory = Memory() r15_operands = (r15, r15.memory) self.handle_store(label, None, *r15_operands) mov = MOV(r15, operands[1]) self.add_instruction(label, mov) idiv = IDIV(r15) else: idiv = IDIV(operands[1]) self.add_instruction(label, idiv) mov = MOV(result, rax) self.add_instruction(label, mov) if restore_rax: self.handle_load(label, rax, rax.memory) if restore_rdx: self.handle_load(label, rdx, rdx.memory) if restore_r15: self.handle_load(label, r15, r15.memory)
def handle_call(self, function_name, label, result, *operands): """Handles the call instruction of IR. """ pushed_registers = [] if 0 in self.instruction_live_registers[label]: self.instruction_live_registers[label].pop(0) if not self.is_register(result) or result.color != 0: # Create a dummy register register = Register() register.color = 0 push = PUSH(register) self.add_instruction(label, push) pushed_registers.append(register) for register_color in self.instruction_live_registers[label]: # Create a dummy register register = Register() register.color = register_color push = PUSH(register) self.add_instruction(label, push) pushed_registers.append(register) # Arguments are passed through %rdi, %rsi, %rdx, %rcx, %r8, %r9 for argument, register_color in zip(operands, FUNCTION_ARGUMENTS_COLORS): if not (isinstance(argument, Register) and argument.color == register_color): # Create a dummy register for %rax. new_register = Register() new_register.color = register_color mov = MOV(new_register, argument) self.add_instruction(label, mov) # If there are more than arguments to the function, push them on to the # stack in the right-to-left order if len(operands) > 6: # Note we run this upto 5 but not including 5 which is the 6th argument. for argument in operands[-1:5:-1]: push = PUSH(argument) self.add_instruction(label, push) call = CALL() self.add_instruction(label, call) self.calls_to_link.append((call, function_name)) # Popping should be in the reverse order pop_rax_later = False for register in pushed_registers[-1::-1]: # Don't pop %rax yet, if result exists, we need to mov %rax to # actual result register and then pop if register.color == 0: pop_rax_later = True continue pop = POP(register) self.add_instruction(label, pop) # NOTE: The following move is done after popping all the values because # popping overwrites the values otherwise. # If the result of the call instruction is to be passed on to some other # register than %rax, we need to insert a move for it. if self.is_register(result) and result.color != 0: rax = Register() rax.color = 0 mov = MOV(result, rax) self.add_instruction(label, mov) # We should pop_rax if we rax has been pushed to the stack, but we can only # pop it after moving the result to the right location. if pop_rax_later: register = Register() register.color = 0 pop = POP(register) self.add_instruction(label, pop)