diff --git a/src/tilde.hpp b/src/tilde.hpp index 0e5b3b551..b8434ce4b 100644 --- a/src/tilde.hpp +++ b/src/tilde.hpp @@ -196,6 +196,8 @@ struct cgProcedure { Scope *curr_scope; i32 scope_index; bool in_multi_assignment; + isize split_returns_index; + bool return_by_ptr; PtrMap variable_map; diff --git a/src/tilde/tb.lib b/src/tilde/tb.lib index a3c6ba044..f50666cff 100644 Binary files a/src/tilde/tb.lib and b/src/tilde/tb.lib differ diff --git a/src/tilde_builtin.cpp b/src/tilde_builtin.cpp index 08c3f7bdd..d6e161ec3 100644 --- a/src/tilde_builtin.cpp +++ b/src/tilde_builtin.cpp @@ -91,6 +91,18 @@ gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &value) { return res; } +gb_internal cgValue cg_builtin_min(cgProcedure *p, Type *t, cgValue x, cgValue y) { + x = cg_emit_conv(p, x, t); + y = cg_emit_conv(p, y, t); + return cg_emit_select(p, cg_emit_comp(p, Token_Lt, x, y), x, y); +} +gb_internal cgValue cg_builtin_max(cgProcedure *p, Type *t, cgValue x, cgValue y) { + x = cg_emit_conv(p, x, t); + y = cg_emit_conv(p, y, t); + return cg_emit_select(p, cg_emit_comp(p, Token_Gt, x, y), x, y); +} + + gb_internal cgValue cg_build_builtin(cgProcedure *p, BuiltinProcId id, Ast *expr) { ast_node(ce, CallExpr, expr); @@ -137,6 +149,32 @@ gb_internal cgValue cg_build_builtin(cgProcedure *p, BuiltinProcId id, Ast *expr return cg_builtin_len(p, v); } + case BuiltinProc_min: + if (ce->args.count == 2) { + Type *t = type_of_expr(expr); + return cg_builtin_min(p, t, cg_build_expr(p, ce->args[0]), cg_build_expr(p, ce->args[1])); + } else { + Type *t = type_of_expr(expr); + cgValue x = cg_build_expr(p, ce->args[0]); + for (isize i = 1; i < ce->args.count; i++) { + x = cg_builtin_min(p, t, x, cg_build_expr(p, ce->args[i])); + } + return x; + } + break; + case BuiltinProc_max: + if (ce->args.count == 2) { + Type *t = type_of_expr(expr); + return cg_builtin_max(p, t, cg_build_expr(p, ce->args[0]), cg_build_expr(p, ce->args[1])); + } else { + Type *t = type_of_expr(expr); + cgValue x = cg_build_expr(p, ce->args[0]); + for (isize i = 1; i < ce->args.count; i++) { + x = cg_builtin_max(p, t, x, cg_build_expr(p, ce->args[i])); + } + return x; + } + break; } diff --git a/src/tilde_debug.cpp b/src/tilde_debug.cpp index e3b45ff1b..21594ef07 100644 --- a/src/tilde_debug.cpp +++ b/src/tilde_debug.cpp @@ -1,5 +1,7 @@ gb_internal TB_DebugType *cg_debug_type_internal(cgModule *m, Type *type); gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type) { + type = reduce_tuple_to_single_type(type); + mutex_lock(&m->debug_type_mutex); defer (mutex_unlock(&m->debug_type_mutex)); TB_DebugType **found = map_get(&m->debug_type_map, type); @@ -235,7 +237,8 @@ gb_internal TB_DebugType *cg_debug_type_internal(cgModule *m, Type *type) { case Basic_uint: return tb_debug_get_integer(m->mod, is_signed, bits); case Basic_uintptr: return tb_debug_get_integer(m->mod, is_signed, bits); - case Basic_rawptr: return tb_debug_create_ptr(m->mod, tb_debug_get_void(m->mod)); + case Basic_rawptr: + return tb_debug_create_ptr(m->mod, tb_debug_get_void(m->mod)); case Basic_string: { String name = basic_types[type->Basic.kind].Basic.name; diff --git a/src/tilde_expr.cpp b/src/tilde_expr.cpp index 3d9054803..b6dbce181 100644 --- a/src/tilde_expr.cpp +++ b/src/tilde_expr.cpp @@ -15,6 +15,15 @@ gb_internal cgValue cg_flatten_value(cgProcedure *p, cgValue value) { return value; } +gb_internal cgValue cg_emit_select(cgProcedure *p, cgValue const &cond, cgValue const &x, cgValue const &y) { + GB_ASSERT(x.kind == y.kind); + GB_ASSERT(cond.kind == cgValue_Value); + cgValue res = x; + res.node = tb_inst_select(p->func, cond.node, x.node, y.node); + return res; +} + + gb_internal bool cg_is_expr_untyped_const(Ast *expr) { auto const &tv = type_and_value_of_expr(expr); if (is_type_untyped(tv.type)) { @@ -1724,36 +1733,35 @@ gb_internal cgValue cg_build_binary_expr(cgProcedure *p, Ast *expr) { case Token_CmpEq: case Token_NotEq: - GB_PANIC("TODO(bill): comparisons"); - // if (is_type_untyped_nil(be->right->tav.type)) { - // // `x == nil` or `x != nil` - // cgValue left = cg_build_expr(p, be->left); - // cgValue cmp = cg_emit_comp_against_nil(p, be->op.kind, left); - // Type *type = default_type(tv.type); - // return cg_emit_conv(p, cmp, type); - // } else if (is_type_untyped_nil(be->left->tav.type)) { - // // `nil == x` or `nil != x` - // cgValue right = cg_build_expr(p, be->right); - // cgValue cmp = cg_emit_comp_against_nil(p, be->op.kind, right); - // Type *type = default_type(tv.type); - // return cg_emit_conv(p, cmp, type); - // } else if (cg_is_empty_string_constant(be->right)) { - // // `x == ""` or `x != ""` - // cgValue s = cg_build_expr(p, be->left); - // s = cg_emit_conv(p, s, t_string); - // cgValue len = cg_string_len(p, s); - // cgValue cmp = cg_emit_comp(p, be->op.kind, len, cg_const_int(p->module, t_int, 0)); - // Type *type = default_type(tv.type); - // return cg_emit_conv(p, cmp, type); - // } else if (cg_is_empty_string_constant(be->left)) { - // // `"" == x` or `"" != x` - // cgValue s = cg_build_expr(p, be->right); - // s = cg_emit_conv(p, s, t_string); - // cgValue len = cg_string_len(p, s); - // cgValue cmp = cg_emit_comp(p, be->op.kind, len, cg_const_int(p->module, t_int, 0)); - // Type *type = default_type(tv.type); - // return cg_emit_conv(p, cmp, type); - // } + if (is_type_untyped_nil(be->right->tav.type)) { + // `x == nil` or `x != nil` + cgValue left = cg_build_expr(p, be->left); + cgValue cmp = cg_emit_comp_against_nil(p, be->op.kind, left); + Type *type = default_type(tv.type); + return cg_emit_conv(p, cmp, type); + } else if (is_type_untyped_nil(be->left->tav.type)) { + // `nil == x` or `nil != x` + cgValue right = cg_build_expr(p, be->right); + cgValue cmp = cg_emit_comp_against_nil(p, be->op.kind, right); + Type *type = default_type(tv.type); + return cg_emit_conv(p, cmp, type); + }/* else if (cg_is_empty_string_constant(be->right)) { + // `x == ""` or `x != ""` + cgValue s = cg_build_expr(p, be->left); + s = cg_emit_conv(p, s, t_string); + cgValue len = cg_string_len(p, s); + cgValue cmp = cg_emit_comp(p, be->op.kind, len, cg_const_int(p->module, t_int, 0)); + Type *type = default_type(tv.type); + return cg_emit_conv(p, cmp, type); + } else if (cg_is_empty_string_constant(be->left)) { + // `"" == x` or `"" != x` + cgValue s = cg_build_expr(p, be->right); + s = cg_emit_conv(p, s, t_string); + cgValue len = cg_string_len(p, s); + cgValue cmp = cg_emit_comp(p, be->op.kind, len, cg_const_int(p->module, t_int, 0)); + Type *type = default_type(tv.type); + return cg_emit_conv(p, cmp, type); + }*/ /*fallthrough*/ case Token_Lt: case Token_LtEq: @@ -2255,7 +2263,6 @@ gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr) { token_pos_to_string(token.pos)); return {}; } else if (e->kind == Entity_Nil) { - GB_PANIC("TODO: cg_find_ident nil"); // TODO(bill): is this correct? return cg_value(cast(TB_Node *)nullptr, e->type); } diff --git a/src/tilde_proc.cpp b/src/tilde_proc.cpp index 7c7bda197..41c185dc6 100644 --- a/src/tilde_proc.cpp +++ b/src/tilde_proc.cpp @@ -63,6 +63,7 @@ gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool i p->is_foreign = entity->Procedure.is_foreign; p->is_export = entity->Procedure.is_export; p->is_entry_point = false; + p->split_returns_index = -1; gbAllocator a = heap_allocator(); p->children.allocator = a; @@ -122,6 +123,7 @@ gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &li p->is_foreign = false; p->is_export = false; p->is_entry_point = false; + p->split_returns_index = -1; gbAllocator a = heap_allocator(); p->children.allocator = a; @@ -188,6 +190,7 @@ gb_internal void cg_procedure_begin(cgProcedure *p) { TB_DebugType *debug_type = cg_debug_type(p->module, result_type); TB_PassingRule rule = tb_get_passing_rule_from_dbg(p->module->mod, debug_type, true); if (rule == TB_PASSING_INDIRECT) { + p->return_by_ptr = true; param_index++; } } @@ -272,7 +275,7 @@ gb_internal void cg_procedure_begin(cgProcedure *p) { // } } - // isize split_offset = param_index; + p->split_returns_index = param_index; if (pt->calling_convention == ProcCC_Odin) { // NOTE(bill): Push context on to stack from implicit parameter @@ -323,24 +326,6 @@ gb_internal void cg_procedure_end(cgProcedure *p) { tb_inst_ret(p->func, 0, nullptr); } bool emit_asm = false; - // if (p->name == "main") { - if ( - p->name == "runtime" ABI_PKG_NAME_SEPARATOR "print_string" || - // p->name == "bug" ABI_PKG_NAME_SEPARATOR "main" || - // p->name == "main" || - false - ) { - TB_Arena *arena = tb_default_arena(); - defer (arena->free(arena)); - TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena); - defer (tb_funcopt_exit(opt)); - tb_funcopt_print(opt); - - // emit_asm = true; - - // GraphViz printing - // tb_function_print(p->func, tb_default_print_callback, stdout); - } TB_FunctionOutput *output = tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST, emit_asm); if (emit_asm) { TB_Assembly *assembly = tb_output_get_asm(output); @@ -361,17 +346,33 @@ gb_internal void cg_procedure_generate(cgProcedure *p) { return; } + bool build_body = false; + if ( - p->name == "runtime" ABI_PKG_NAME_SEPARATOR "print_string" || + string_starts_with(p->name, str_lit("runtime" ABI_PKG_NAME_SEPARATOR "_os_write")) || // p->name == "bug" ABI_PKG_NAME_SEPARATOR "main" || // p->name == "main" || false ) { + build_body = true; + } + + if (build_body) { cg_procedure_begin(p); cg_build_stmt(p, p->body); } - cg_procedure_end(p); + + if (build_body) { + TB_Arena *arena = tb_default_arena(); + defer (arena->free(arena)); + TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena); + defer (tb_funcopt_exit(opt)); + tb_funcopt_print(opt); + + // GraphViz printing + // tb_function_print(p->func, tb_default_print_callback, stdout); + } } @@ -543,9 +544,11 @@ gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice case 1: if (return_is_indirect) { return cg_lvalue_addr(params[0], pt->results->Tuple.variables[0]->type); + } else { + GB_ASSERT(multi_output.count == 1); + TB_Node *node = multi_output.single; + return cg_value(node, pt->results->Tuple.variables[0]->type); } - GB_ASSERT(multi_output.count == 1); - return cg_value(multi_output.single, pt->results->Tuple.variables[0]->type); } cgValueMulti *multi = gb_alloc_item(permanent_allocator(), cgValueMulti); diff --git a/src/tilde_stmt.cpp b/src/tilde_stmt.cpp index 4fc00dd28..074d2674c 100644 --- a/src/tilde_stmt.cpp +++ b/src/tilde_stmt.cpp @@ -1011,31 +1011,91 @@ gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) { gb_internal void cg_build_return_stmt(cgProcedure *p, Slice const &return_results) { TypeTuple *tuple = &p->type->Proc.results->Tuple; isize return_count = p->type->Proc.result_count; - gb_unused(tuple); - isize res_count = return_results.count; - gb_unused(res_count); if (return_count == 0) { tb_inst_ret(p->func, 0, nullptr); return; - } else if (return_count == 1) { - Entity *e = tuple->variables[0]; - if (res_count == 0) { - cgValue zero = cg_const_nil(p, tuple->variables[0]->type); - if (zero.kind == cgValue_Value) { - tb_inst_ret(p->func, 1, &zero.node); + } + TEMPORARY_ALLOCATOR_GUARD(); + + auto results = array_make(temporary_allocator(), 0, return_count); + + if (return_results.count != 0) { + for (isize i = 0; i < return_results.count; i++) { + cgValue res = cg_build_expr(p, return_results[i]); + cg_append_tuple_values(p, &results, res); + } + } else { + for_array(i, tuple->variables) { + Entity *e = tuple->variables[i]; + cgAddr addr = map_must_get(&p->variable_map, e); + cgValue res = cg_addr_load(p, addr); + array_add(&results, res); + } + } + GB_ASSERT(results.count == return_count); + + if (return_results.count != 0 && p->type->Proc.has_named_results) { + // NOTE(bill): store the named values before returning + for_array(i, tuple->variables) { + Entity *e = tuple->variables[i]; + cgAddr addr = map_must_get(&p->variable_map, e); + cg_addr_store(p, addr, results[i]); + } + } + for_array(i, tuple->variables) { + Entity *e = tuple->variables[i]; + results[i] = cg_emit_conv(p, results[i], e->type); + } + + + if (p->split_returns_index >= 0) { + GB_ASSERT(is_calling_convention_odin(p->type->Proc.calling_convention)); + + for (isize i = 0; i < return_count-1; i++) { + Entity *e = tuple->variables[i]; + TB_Node *ret_ptr = tb_inst_param(p->func, cast(int)(p->split_returns_index+i)); + cgValue ptr = cg_value(ret_ptr, alloc_type_pointer(e->type)); + cg_emit_store(p, ptr, results[i]); + } + + if (p->return_by_ptr) { + Entity *e = tuple->variables[return_count-1]; + TB_Node *ret_ptr = tb_inst_param(p->func, 0); + cgValue ptr = cg_value(ret_ptr, alloc_type_pointer(e->type)); + cg_emit_store(p, ptr, results[return_count-1]); + + tb_inst_ret(p->func, 0, nullptr); + return; + } else { + GB_ASSERT(p->proto->return_count == 1); + TB_DataType dt = TB_PROTOTYPE_RETURNS(p->proto)->dt; + + cgValue result = cg_flatten_value(p, results[0]); + TB_Node *final_res = nullptr; + if (result.kind == cgValue_Addr) { + TB_CharUnits align = cast(TB_CharUnits)type_align_of(result.type); + final_res = tb_inst_load(p->func, dt, result.node, align, false); + } else { + GB_ASSERT(result.kind == cgValue_Value); + if (result.node->dt.raw == dt.raw) { + final_res = result.node; + } else { + final_res = tb_inst_bitcast(p->func, result.node, dt); + } } + GB_ASSERT(final_res != nullptr); + + tb_inst_ret(p->func, 1, &final_res); return; } - cgValue res = cg_build_expr(p, return_results[0]); - res = cg_emit_conv(p, res, e->type); - if (res.kind == cgValue_Value) { - tb_inst_ret(p->func, 1, &res.node); - } - return; + } else { - GB_PANIC("TODO(bill): MUTLIPLE RETURN VALUES"); + GB_ASSERT(!is_calling_convention_odin(p->type->Proc.calling_convention)); } + + + GB_PANIC("TODO(bill): %.*s MUTLIPLE RETURN VALUES %td %td", LIT(p->name), results.count, return_results.count); } gb_internal void cg_build_if_stmt(cgProcedure *p, Ast *node) {