From 5b9e9b61d1bc0935703c1b1edf1d3d49ab82b953 Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Sat, 17 Mar 2018 04:47:24 -0400 Subject: [PATCH] It seems impossible, but I think I might have sucessfully implemented struct returning correctly, the first time, with no compile errors or runtime errors, after 45 minutes starting a little past 4am. Woo! --- stdlib/bytecode_generator.krak | 65 ++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/stdlib/bytecode_generator.krak b/stdlib/bytecode_generator.krak index 9f1dcf8..ef7e991 100644 --- a/stdlib/bytecode_generator.krak +++ b/stdlib/bytecode_generator.krak @@ -357,6 +357,15 @@ obj bytecode_generator (Object) { node_function_idx[node] = functions.size functions.add(bytecode_function(get_name(node), instructions.size)) var parameter_offset = (register_size*2) cast int // have to pass saved RBP and return address + + var return_type = get_ast_type(node)->return_type + // if we're returning an object, our caller passes the address + // we should save our return value as as the first parameter + if (return_type->is_object() && return_type->indirection == 0) { + var ptr_type = return_type->clone_with_increased_indirection() + functions.last().var_to_frame_offset[ast_identifier_ptr("bytecode_struct_return_temp_address", ptr_type, null())] = parameter_offset + parameter_offset += type_size(ptr_type) + } node->function.parameters.for_each(fun(p: *ast_node) { functions.last().var_to_frame_offset[p] = parameter_offset parameter_offset += type_size(p->identifier.type) @@ -501,15 +510,23 @@ obj bytecode_generator (Object) { fun generate_return_statement(node: *ast_node): int { // STRUCT HERE if (node->return_statement.return_value) { - emit_addi(2, generate(node->return_statement.return_value), 0) - emit_addi(0, 1, register_size) - emit_ldr(1, 1, 0, operand_size::b64()) - reset_reg() - emit_ret() - } else { - reset_reg() - emit_ret() + var return_value_reg = generate(node->return_statement.return_value) + var return_type = get_ast_type(node->return_statement.return_value) + if (return_type->is_object() && return_type->indirection == 0) { + // fix this up to be nicer + // this is the hardcoded offset of the "first parameter" which + // is the address into which we should save our resulting struct + emit_struct_copy(emit_ldr(1, (register_size*2) cast int, size_to_operand_size(register_size)), 0, return_value_reg, 0, return_type) + } else { + emit_addi(2, return_value_reg, 0) + } } + + emit_addi(0, 1, register_size) + emit_ldr(1, 1, 0, operand_size::b64()) + reset_reg() + emit_ret() + return -1 } fun generate_cast(node: *ast_node): int { @@ -630,6 +647,18 @@ obj bytecode_generator (Object) { } error("unknown operator " + name) } else { + // if this function returns a struct, we have to allocate space for it on the top of the stack + // before we save registers, as it has to persist beyond the call (for whatever happens to it next) + // We stick it in the function as if this is the declaration of a temporary variable, basically + + var return_type = get_ast_type(func)->return_type + var struct_return_temp_ident = null() + if (return_type->is_object() && return_type->indirection == 0) { + struct_return_temp_ident = ast_identifier_ptr("bytecode_struct_return_temp", return_type, null()) + functions.last().frame_size += type_size(return_type) + functions.last().var_to_frame_offset[struct_return_temp_ident] = -functions.last().frame_size + } + // save regs var save_til = peek_reg() var save_size = (save_til - 3) * register_size @@ -655,10 +684,24 @@ obj bytecode_generator (Object) { emit_str(0, 0, param_reg, size_to_operand_size(param_size)) reset_reg(save_til) }) + // pass the address to save the struct into as a parameter + if (return_type->is_object() && return_type->indirection == 0) { + emit_addi(0, 0, -(register_size) cast int) + total_param_size += (register_size) cast int + emit_str(0, 0, emit_addi(1, functions.last().var_to_frame_offset[struct_return_temp_ident]), size_to_operand_size(register_size)) + reset_reg(save_til) + } var return_reg = emit_call(generate_function(node->function_call.func)) - // returning through r2 every time doesn't give unique regs for functions used together in an expression - // so get a new one for this time - return_reg = emit_addi(return_reg, 0) + + if (return_type->is_object() && return_type->indirection == 0) { + // if returned struct, then the struct was saved where we asked for it to be + // return pointer with that address + return_reg = emit_addi(1, functions.last().var_to_frame_offset[struct_return_temp_ident]) + } else { + // returning through r2 every time doesn't give unique regs for functions used together in an expression + // so get a new one for this time + return_reg = emit_addi(return_reg, 0) + } emit_addi(0, 0, total_param_size) // restore regs