def compile(self, assembler): """Compile this statement Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ value = bytearray() relocation_objects = [] for statement in self.statement_sequence: compiled_object = statement.compile(assembler) if compiled_object is None: continue reloc_objects = compiled_object.relocation_objects for relocation_object in reloc_objects: additional_offset = len(value) relocation_object.offset += additional_offset relocation_objects.append(relocation_object) value += compiled_object.value size = len(value) compiled_object = CompiledObject('compoundStatement', size, value, CompiledObjectType.code, relocation_objects) return compiled_object
def compile_stack_variable(self, assembler, stack_variable): """Compile an assignment of an stack variable. Args: assembler (Assembler): the assembler to use stack_variable (StackVariable): the retrieved stack variable Returns: CompiledObject: the compiled version of this assignment """ value = bytearray() stack_offset = stack_variable.stack_offset size = stack_variable.size if stack_variable.type_name == 'double': register = ProcessorRegister.double_scalar_0 elif stack_variable.type_name == 'float': register = ProcessorRegister.single_scalar_0 else: register = ProcessorRegister.accumulator compiled_code, relocation_objects = \ self.initializer_exp.load_result_to_reg(register, assembler) for relocation_object in relocation_objects: additional_offset = len(value) relocation_object.offset += additional_offset value += compiled_code value += assembler.copy_reg_to_stack(stack_offset, register) compiled_object = CompiledObject(self.id, size, value, CompiledObjectType.code, relocation_objects) return compiled_object
def compile(self, assembler): """Compile this statement Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ value = bytearray() if_part = self.if_statement.compile(assembler) # compare the value from the condition, to 0. If not equal, # go to the if part, else to the else part if present. condition_reg = ProcessorRegister.accumulator condition_code, relocation_objects = \ self.condition.load_result_to_reg(condition_reg, assembler) value += condition_code compare_register = ProcessorRegister.counter value_to_load = 0 value += assembler.copy_value_to_reg(value_to_load, compare_register) value += assembler.cmp(condition_reg, compare_register) jump_distance = len(if_part.value) if self.else_statement: else_part = self.else_statement.compile(assembler) jump_distance_else = len(else_part.value) # if there if an else part, the last instruction of the if part # is the jump over the else part. jump_distance += len(assembler.jmp(jump_distance_else)) value += assembler.je(jump_distance) for relocation_object in if_part.relocation_objects: additional_offset = len(value) relocation_object.offset += additional_offset relocation_objects.append(relocation_object) value += if_part.value if self.else_statement: # jump over the else part (for the if part) value += assembler.jmp(jump_distance_else) for relocation_object in else_part.relocation_objects: additional_offset = len(value) relocation_object.offset += additional_offset relocation_objects.append(relocation_object) # the actual else part value += else_part.value size = len(value) compiled_object = CompiledObject('if', size, value, CompiledObjectType.code, relocation_objects) return compiled_object
def compile_internal_variable(self, assembler, size): """Compile a non-external variable Args: assembler (Assembler): the assembler to use size (int): the size of the type of this variable Returns: CompiledObject: the compiled version of this variable """ value = bytearray() if self.parent_node.parent_node: if self.initializer: stack_variable = self.stack_var stack_offset = stack_variable.stack_offset if stack_variable.type_name == 'double': register = ProcessorRegister.double_scalar_0 elif stack_variable.type_name == 'float': register = ProcessorRegister.single_scalar_0 else: register = ProcessorRegister.accumulator compiled_code, relocation_objects = \ self.initializer.load_result_to_reg(register, assembler) value += compiled_code value += assembler.copy_reg_to_stack(stack_offset, register) compiled_object = CompiledObject(self.name, size, value, CompiledObjectType.data, relocation_objects) else: compiled_object = CompiledObject(self.name, size, value, CompiledObjectType.data) else: if self.initializer: value = self.initializer_to_bytearray(size) compiled_object = CompiledObject(self.name, size, value, CompiledObjectType.data) return compiled_object
def compile(self, _): """Compile this statement. Args: _ (Assembler): the assembler to use, unused but required because inheritance prototype. Returns: CompiledObject: the compiled version of this statement """ value = bytearray() size = 0 compiled_object = CompiledObject(self.name, size, value, CompiledObjectType.code) return compiled_object
def compile_global_variable(self, assembler, identifier): """Compile an assignment of an stack variable. Args: assembler (Assembler): the assembler to use identifier (str): the identifier of the resulting variable Returns: CompiledObject: the compiled version of this assignment """ value = bytearray() node = self.get_global_symbol(identifier) if node is None: file_name = 'unknown' line_number = -1 message = f'could not fine the identifier {identifier}' pcc.utils.warning.error(file_name, line_number, message) return None variable_type = node.variable_type if variable_type.name == 'double': register = ProcessorRegister.double_scalar_0 elif variable_type.name == 'float': register = ProcessorRegister.single_scalar_0 else: register = ProcessorRegister.accumulator compiled_code, relocation_objects = \ self.initializer_exp.load_result_to_reg(register, assembler) value += compiled_code # use a 0 displacement as the linker will fill it in compiled_code, displacement_offset = \ assembler.mov_to_displacement(register, displacement=0) offset = len(value) + displacement_offset value += compiled_code # the offset in the symbol is 4 addend = -4 size = len(value) relocation_object = RelocationObject(node.name, offset, CompiledObjectType.data, addend) relocation_objects.append(relocation_object) compiled_object = CompiledObject(self.id, size, value, CompiledObjectType.code, relocation_objects) return compiled_object
def compile(self, assembler): """Compile this statement Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ value = bytearray() if self.expression: return_type = self.get_return_type() if return_type == 'double': reg = ProcessorRegister.double_scalar_0 elif return_type == 'float': reg = ProcessorRegister.single_scalar_0 else: reg = ProcessorRegister.accumulator compiled_code, rela_objects = \ self.expression.load_result_to_reg(reg, assembler) value += compiled_code else: rela_objects = [] # set the base pointer as the new stack pointer src = ProcessorRegister.base_pointer dest = ProcessorRegister.frame_pointer ret = assembler.copy_from_reg_to_reg(destination=dest, source=src) value.extend(ret) # restore the frame pointer from stack ret = assembler.pop_from_stack(ProcessorRegister.base_pointer) value.extend(ret) # return to the called function ret = assembler.return_to_caller() value.extend(ret) size = len(value) compiled_object = CompiledObject(self.expression, size, value, CompiledObjectType.code, rela_objects) return compiled_object
def compile(self, assembler): """Compile this statement Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ value = bytearray() body_part = self.body_statement.compile(assembler) # compare the value from the condition, to 0. If not equal, # go to the if part, else to the else part if present. condition_reg = ProcessorRegister.accumulator condition_code, relocation_objects = \ self.condition.load_result_to_reg(condition_reg, assembler) value += condition_code body_len = len(body_part.value) # the distance over the body is the length of the body as well as the # jump back at the end. Just encode a random length to calc this length jump_over_body = body_len + len(assembler.jmp(body_len)) value += assembler.cmp_against_const(condition_reg, const=0) value += assembler.je(jump_over_body) len_condition = len(value) value += body_part.value # the distance to jump back, is the relative distance after the jump # instruction. So the complete distance is the length of the body, # the length of the condition and this jump (use the length of a # random jump to know the length) jump_back_dist = -len_condition - body_len - \ len(assembler.jmp(0)) value += assembler.jmp(jump_back_dist) size = len(value) compiled_object = CompiledObject('if', size, value, CompiledObjectType.code, relocation_objects) return compiled_object
def compile(self, assembler): """Compile this statement Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ size = self.variable_type.size value = bytearray() if self.is_extern: size = 0 compiled_object = CompiledObject(self.name, size, value, CompiledObjectType.data) else: compiled_object = self.compile_internal_variable(assembler, size) return compiled_object
def compile(self, assembler): """Compile this statement Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ pass value = bytearray() relocation_objects = [] # load all variables in the registers compiled_code, rela_objs = \ self._copy_argmuments_to_registers(assembler) relocation_objects += rela_objs value += compiled_code node = self.get_global_symbol(self.id) compiled_code, displacement_offset = \ assembler.call(displacement=0) offset = len(value) + displacement_offset value += compiled_code # the offset in the symbol is 4 addend = -4 relocation_object = RelocationObject(node.name, offset, CompiledObjectType.code, addend) relocation_objects.append(relocation_object) size = len(value) compiled_object = CompiledObject(self.id, size, value, CompiledObjectType.code, relocation_objects) return compiled_object
def compile(self, assembler): """Compile this statement. Args: assembler (Assembler): the assembler to use Returns: CompiledObject: the compiled version of this statement """ value = bytearray() # save the frame pointer on stack ret = assembler.push_to_stack(ProcessorRegister.base_pointer) value.extend(ret) # set the stack pointer as the new base pointer dest = ProcessorRegister.base_pointer src = ProcessorRegister.frame_pointer ret = assembler.copy_from_reg_to_reg(destination=dest, source=src) value.extend(ret) current_list = [] self.add_stack_variable(current_list) # first the frame pointer has been saved to stack stack_offset = 0 for stack_var in current_list: stack_var.stack_start = stack_offset value_array = stack_var.initializer_byte_array value, stack_offset = push_variable_on_stack(assembler, stack_offset, value, value_array) stack_var.stack_offset = stack_offset self.stack_variable_list = current_list reg = ProcessorRegister.counter # allign the stack to a multiple of 16 # stack_offset is a negative number that is rounded to a multiple # of 16, stack_offset=12 -> allinged_stack_size=16 allinged_stack_size = 16*((-stack_offset)//16 + 1) value += assembler.copy_value_to_reg(imm_value=allinged_stack_size, destination=reg) value += assembler.sub(source=reg, destination=ProcessorRegister.frame_pointer) # add a nop ret = assembler.nop() value.extend(ret) value += self._copy_argmuments_to_stack(assembler) relocation_objects = [] for statement in self.statement_sequence: compiled_object = statement.compile(assembler) if compiled_object is None: continue reloc_objects = compiled_object.relocation_objects for relocation_object in reloc_objects: additional_offset = len(value) relocation_object.offset += additional_offset relocation_objects.append(relocation_object) value += compiled_object.value size = len(value) compiled_object = CompiledObject(self.statement_sequence[0].name, size, value, CompiledObjectType.code, relocation_objects) return compiled_object