| author | |
| committer | |
| log | 0541532ed6cf5a32b57a4a3f74e6d3a1699223a7 |
| tree | 3359c1bc68ea224c55e9d2ffb5261eff6f7c2b9f |
| parent | 363606d87b3f45c7f62969f85062e2a7b1b4b5dc |
7 files changed, 301 insertions(+), 75 deletions(-)
src/all_types.hpp+15-2| ... | ... | @@ -726,6 +726,19 @@ struct FnTypeParamInfo { |
| 726 | 726 | TypeTableEntry *type; |
| 727 | 727 | }; |
| 728 | 728 | |
| 729 | struct GenericParamValue { | |
| 730 | TypeTableEntry *type; | |
| 731 | ConstExprValue *value; | |
| 732 | }; | |
| 733 | ||
| 734 | struct GenericFnTypeId { | |
| 735 | FnTableEntry *fn_entry; | |
| 736 | GenericParamValue *params; | |
| 737 | size_t param_count; | |
| 738 | }; | |
| 739 | ||
| 740 | uint32_t generic_fn_type_id_hash(GenericFnTypeId *id); | |
| 741 | bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b); | |
| 729 | 742 | |
| 730 | 743 | struct FnTypeId { |
| 731 | 744 | TypeTableEntry *return_type; |
| ... | ... | @@ -957,7 +970,6 @@ struct FnTableEntry { |
| 957 | 970 | ScopeFnDef *fndef_scope; // parent should be the top level decls or container decls |
| 958 | 971 | Scope *child_scope; // parent is scope for last parameter |
| 959 | 972 | ScopeBlock *def_scope; // parent is child_scope |
| 960 | ImportTableEntry *import_entry; | |
| 961 | 973 | Buf symbol_name; |
| 962 | 974 | TypeTableEntry *type_entry; // function type |
| 963 | 975 | TypeTableEntry *implicit_return_type; |
| ... | ... | @@ -969,6 +981,7 @@ struct FnTableEntry { |
| 969 | 981 | IrExecutable ir_executable; |
| 970 | 982 | IrExecutable analyzed_executable; |
| 971 | 983 | size_t prealloc_bbc; |
| 984 | AstNode **param_source_nodes; | |
| 972 | 985 | |
| 973 | 986 | AstNode *fn_no_inline_set_node; |
| 974 | 987 | AstNode *fn_export_set_node; |
| ... | ... | @@ -1050,6 +1063,7 @@ struct CodeGen { |
| 1050 | 1063 | HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> primitive_type_table; |
| 1051 | 1064 | HashMap<FnTypeId *, TypeTableEntry *, fn_type_id_hash, fn_type_id_eql> fn_type_table; |
| 1052 | 1065 | HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table; |
| 1066 | HashMap<GenericFnTypeId *, FnTableEntry *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table; | |
| 1053 | 1067 | |
| 1054 | 1068 | ZigList<ImportTableEntry *> import_queue; |
| 1055 | 1069 | size_t import_queue_index; |
| ... | ... | @@ -1201,7 +1215,6 @@ struct VariableTableEntry { |
| 1201 | 1215 | Scope *parent_scope; |
| 1202 | 1216 | Scope *child_scope; |
| 1203 | 1217 | LLVMValueRef param_value_ref; |
| 1204 | bool force_depends_on_compile_var; | |
| 1205 | 1218 | bool shadowable; |
| 1206 | 1219 | size_t mem_slot_index; |
| 1207 | 1220 | size_t ref_count; |
src/analyze.cpp+92-36| ... | ... | @@ -907,22 +907,25 @@ static TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 907 | 907 | return fn_type; |
| 908 | 908 | } |
| 909 | 909 | |
| 910 | static TypeTableEntry *analyze_fn_type(CodeGen *g, TldFn *tld_fn) { | |
| 911 | AstNode *proto_node = tld_fn->base.source_node; | |
| 910 | void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node) { | |
| 912 | 911 | assert(proto_node->type == NodeTypeFnProto); |
| 913 | 912 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; |
| 914 | 913 | |
| 915 | FnTypeId fn_type_id = {0}; | |
| 916 | fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); | |
| 917 | fn_type_id.is_naked = fn_proto->is_nakedcc; | |
| 918 | fn_type_id.is_cold = fn_proto->is_coldcc; | |
| 919 | fn_type_id.param_count = fn_proto->params.length; | |
| 920 | fn_type_id.param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id.param_count); | |
| 921 | fn_type_id.next_param_index = 0; | |
| 922 | fn_type_id.is_var_args = fn_proto->is_var_args; | |
| 914 | fn_type_id->is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); | |
| 915 | fn_type_id->is_naked = fn_proto->is_nakedcc; | |
| 916 | fn_type_id->is_cold = fn_proto->is_coldcc; | |
| 917 | fn_type_id->param_count = fn_proto->params.length; | |
| 918 | fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id->param_count); | |
| 919 | fn_type_id->next_param_index = 0; | |
| 920 | fn_type_id->is_var_args = fn_proto->is_var_args; | |
| 921 | } | |
| 923 | 922 | |
| 924 | FnTableEntry *fn_entry = tld_fn->fn_entry; | |
| 925 | Scope *child_scope = fn_entry->fndef_scope ? &fn_entry->fndef_scope->base : tld_fn->base.parent_scope; | |
| 923 | static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) { | |
| 924 | assert(proto_node->type == NodeTypeFnProto); | |
| 925 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; | |
| 926 | ||
| 927 | FnTypeId fn_type_id = {0}; | |
| 928 | init_fn_type_id(&fn_type_id, proto_node); | |
| 926 | 929 | |
| 927 | 930 | for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { |
| 928 | 931 | AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); |
| ... | ... | @@ -939,10 +942,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, TldFn *tld_fn) { |
| 939 | 942 | return get_generic_fn_type(g, &fn_type_id); |
| 940 | 943 | } |
| 941 | 944 | |
| 942 | if (fn_entry && buf_len(param_node->data.param_decl.name) == 0) { | |
| 943 | add_node_error(g, param_node, buf_sprintf("missing parameter name")); | |
| 944 | } | |
| 945 | ||
| 946 | 945 | TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); |
| 947 | 946 | |
| 948 | 947 | switch (type_entry->id) { |
| ... | ... | @@ -1366,6 +1365,23 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) { |
| 1366 | 1365 | } |
| 1367 | 1366 | } |
| 1368 | 1367 | |
| 1368 | FnTableEntry *create_fn(CodeGen *g, AstNode *proto_node) { | |
| 1369 | assert(proto_node->type == NodeTypeFnProto); | |
| 1370 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; | |
| 1371 | ||
| 1372 | FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); | |
| 1373 | fn_table_entry->analyzed_executable.backward_branch_count = &fn_table_entry->prealloc_bbc; | |
| 1374 | fn_table_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota; | |
| 1375 | fn_table_entry->analyzed_executable.fn_entry = fn_table_entry; | |
| 1376 | fn_table_entry->ir_executable.fn_entry = fn_table_entry; | |
| 1377 | fn_table_entry->proto_node = proto_node; | |
| 1378 | fn_table_entry->fn_def_node = proto_node->data.fn_proto.fn_def_node; | |
| 1379 | fn_table_entry->fn_inline = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto; | |
| 1380 | fn_table_entry->internal_linkage = (fn_proto->visib_mod != VisibModExport); | |
| 1381 | ||
| 1382 | return fn_table_entry; | |
| 1383 | } | |
| 1384 | ||
| 1369 | 1385 | static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 1370 | 1386 | ImportTableEntry *import = tld_fn->base.import; |
| 1371 | 1387 | AstNode *proto_node = tld_fn->base.source_node; |
| ... | ... | @@ -1381,17 +1397,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 1381 | 1397 | return; |
| 1382 | 1398 | } |
| 1383 | 1399 | |
| 1384 | FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); | |
| 1385 | fn_table_entry->analyzed_executable.backward_branch_count = &fn_table_entry->prealloc_bbc; | |
| 1386 | fn_table_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota; | |
| 1387 | fn_table_entry->analyzed_executable.fn_entry = fn_table_entry; | |
| 1388 | fn_table_entry->ir_executable.fn_entry = fn_table_entry; | |
| 1389 | fn_table_entry->import_entry = import; | |
| 1390 | fn_table_entry->proto_node = proto_node; | |
| 1391 | fn_table_entry->fn_def_node = fn_def_node; | |
| 1392 | fn_table_entry->fn_inline = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto; | |
| 1393 | fn_table_entry->internal_linkage = (fn_proto->visib_mod != VisibModExport); | |
| 1394 | ||
| 1400 | FnTableEntry *fn_table_entry = create_fn(g, tld_fn->base.source_node); | |
| 1395 | 1401 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_'); |
| 1396 | 1402 | |
| 1397 | 1403 | tld_fn->fn_entry = fn_table_entry; |
| ... | ... | @@ -1399,9 +1405,18 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 1399 | 1405 | if (fn_table_entry->fn_def_node) { |
| 1400 | 1406 | fn_table_entry->fndef_scope = create_fndef_scope( |
| 1401 | 1407 | fn_table_entry->fn_def_node, tld_fn->base.parent_scope, fn_table_entry); |
| 1408 | ||
| 1409 | for (size_t i = 0; i < fn_proto->params.length; i += 1) { | |
| 1410 | AstNode *param_node = fn_proto->params.at(i); | |
| 1411 | assert(param_node->type == NodeTypeParamDecl); | |
| 1412 | if (buf_len(param_node->data.param_decl.name) == 0) { | |
| 1413 | add_node_error(g, param_node, buf_sprintf("missing parameter name")); | |
| 1414 | } | |
| 1415 | } | |
| 1402 | 1416 | } |
| 1403 | 1417 | |
| 1404 | fn_table_entry->type_entry = analyze_fn_type(g, tld_fn); | |
| 1418 | Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; | |
| 1419 | fn_table_entry->type_entry = analyze_fn_type(g, proto_node, child_scope); | |
| 1405 | 1420 | |
| 1406 | 1421 | if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) { |
| 1407 | 1422 | tld_fn->base.resolution = TldResolutionInvalid; |
| ... | ... | @@ -2142,6 +2157,13 @@ bool type_is_codegen_pointer(TypeTableEntry *type) { |
| 2142 | 2157 | return false; |
| 2143 | 2158 | } |
| 2144 | 2159 | |
| 2160 | AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) { | |
| 2161 | if (fn_entry->param_source_nodes) | |
| 2162 | return fn_entry->param_source_nodes[index]; | |
| 2163 | else | |
| 2164 | return fn_entry->proto_node->data.fn_proto.params.at(index); | |
| 2165 | } | |
| 2166 | ||
| 2145 | 2167 | static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 2146 | 2168 | assert(fn_table_entry->anal_state != FnAnalStateProbing); |
| 2147 | 2169 | if (fn_table_entry->anal_state != FnAnalStateReady) |
| ... | ... | @@ -2151,17 +2173,19 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 2151 | 2173 | |
| 2152 | 2174 | AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto; |
| 2153 | 2175 | |
| 2154 | Scope *child_scope = &fn_table_entry->fndef_scope->base; | |
| 2155 | assert(child_scope); | |
| 2176 | assert(fn_table_entry->fndef_scope); | |
| 2177 | if (!fn_table_entry->child_scope) | |
| 2178 | fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; | |
| 2156 | 2179 | |
| 2157 | 2180 | // define local variables for parameters |
| 2158 | 2181 | TypeTableEntry *fn_type = fn_table_entry->type_entry; |
| 2159 | 2182 | assert(!fn_type->data.fn.is_generic); |
| 2160 | 2183 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; |
| 2161 | 2184 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { |
| 2162 | AstNode *param_decl_node = fn_proto->params.at(i); | |
| 2163 | AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl; | |
| 2164 | 2185 | FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; |
| 2186 | AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); | |
| 2187 | AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl; | |
| 2188 | ||
| 2165 | 2189 | |
| 2166 | 2190 | TypeTableEntry *param_type = param_info->type; |
| 2167 | 2191 | bool is_noalias = param_info->is_noalias; |
| ... | ... | @@ -2175,9 +2199,9 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 2175 | 2199 | buf_sprintf("byvalue types not yet supported on extern function parameters")); |
| 2176 | 2200 | } |
| 2177 | 2201 | |
| 2178 | VariableTableEntry *var = add_variable(g, param_decl_node, child_scope, param_decl->name, param_type, true, nullptr); | |
| 2202 | VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, param_decl->name, param_type, true, nullptr); | |
| 2179 | 2203 | var->src_arg_index = i; |
| 2180 | child_scope = var->child_scope; | |
| 2204 | fn_table_entry->child_scope = var->child_scope; | |
| 2181 | 2205 | fn_table_entry->variable_list.append(var); |
| 2182 | 2206 | |
| 2183 | 2207 | if (fn_type->data.fn.gen_param_info) { |
| ... | ... | @@ -2185,8 +2209,6 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 2185 | 2209 | } |
| 2186 | 2210 | } |
| 2187 | 2211 | |
| 2188 | fn_table_entry->child_scope = child_scope; | |
| 2189 | ||
| 2190 | 2212 | TypeTableEntry *expected_type = fn_type_id->return_type; |
| 2191 | 2213 | |
| 2192 | 2214 | if (fn_type_id->is_extern && handle_is_ptr(expected_type)) { |
| ... | ... | @@ -2622,6 +2644,40 @@ static uint32_t hash_const_val(TypeTableEntry *type, ConstExprValue *const_val) |
| 2622 | 2644 | zig_unreachable(); |
| 2623 | 2645 | } |
| 2624 | 2646 | |
| 2647 | uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) { | |
| 2648 | uint32_t result = 0; | |
| 2649 | result += hash_ptr(id->fn_entry); | |
| 2650 | for (size_t i = 0; i < id->param_count; i += 1) { | |
| 2651 | GenericParamValue *generic_param = &id->params[i]; | |
| 2652 | if (generic_param->value) { | |
| 2653 | result += hash_const_val(generic_param->type, generic_param->value); | |
| 2654 | result += hash_ptr(generic_param->type); | |
| 2655 | } | |
| 2656 | } | |
| 2657 | return result; | |
| 2658 | } | |
| 2659 | ||
| 2660 | bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { | |
| 2661 | assert(a->fn_entry); | |
| 2662 | if (a->fn_entry != b->fn_entry) return false; | |
| 2663 | assert(a->param_count == b->param_count); | |
| 2664 | for (size_t i = 0; i < a->param_count; i += 1) { | |
| 2665 | GenericParamValue *a_val = &a->params[i]; | |
| 2666 | GenericParamValue *b_val = &b->params[i]; | |
| 2667 | if (a_val->type != b_val->type) return false; | |
| 2668 | if (a_val->value && b_val->value) { | |
| 2669 | assert(a_val->value->special == ConstValSpecialStatic); | |
| 2670 | assert(b_val->value->special == ConstValSpecialStatic); | |
| 2671 | if (!const_values_equal(a_val->value, b_val->value, a_val->type)) { | |
| 2672 | return false; | |
| 2673 | } | |
| 2674 | } else { | |
| 2675 | assert(!a_val->value && !b_val->value); | |
| 2676 | } | |
| 2677 | } | |
| 2678 | return true; | |
| 2679 | } | |
| 2680 | ||
| 2625 | 2681 | bool type_has_bits(TypeTableEntry *type_entry) { |
| 2626 | 2682 | assert(type_entry); |
| 2627 | 2683 | assert(type_entry->id != TypeTableEntryIdInvalid); |
src/analyze.hpp+3| ... | ... | @@ -70,6 +70,9 @@ void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source |
| 70 | 70 | VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, |
| 71 | 71 | TypeTableEntry *type_entry, bool is_const, ConstExprValue *init_value); |
| 72 | 72 | TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node); |
| 73 | FnTableEntry *create_fn(CodeGen *g, AstNode *proto_node); | |
| 74 | void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node); | |
| 75 | AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index); | |
| 73 | 76 | |
| 74 | 77 | Scope *create_block_scope(AstNode *node, Scope *parent); |
| 75 | 78 | Scope *create_defer_scope(AstNode *node, Scope *parent); |
src/ast_render.cpp+1-1| ... | ... | @@ -397,7 +397,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 397 | 397 | assert(param_decl->type == NodeTypeParamDecl); |
| 398 | 398 | if (buf_len(param_decl->data.param_decl.name) > 0) { |
| 399 | 399 | const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : ""; |
| 400 | const char *inline_str = param_decl->data.param_decl.is_inline ? "inline " : ""; | |
| 400 | const char *inline_str = param_decl->data.param_decl.is_inline ? "inline " : ""; | |
| 401 | 401 | fprintf(ar->f, "%s%s", noalias_str, inline_str); |
| 402 | 402 | print_symbol(ar, param_decl->data.param_decl.name); |
| 403 | 403 | fprintf(ar->f, ": "); |
src/codegen.cpp+18-25| ... | ... | @@ -60,6 +60,7 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { |
| 60 | 60 | g->primitive_type_table.init(32); |
| 61 | 61 | g->fn_type_table.init(32); |
| 62 | 62 | g->error_table.init(16); |
| 63 | g->generic_table.init(16); | |
| 63 | 64 | g->is_release_build = false; |
| 64 | 65 | g->is_test_build = false; |
| 65 | 66 | g->want_h_file = true; |
| ... | ... | @@ -2305,11 +2306,8 @@ static void do_code_gen(CodeGen *g) { |
| 2305 | 2306 | if (should_skip_fn_codegen(g, fn_table_entry)) |
| 2306 | 2307 | continue; |
| 2307 | 2308 | |
| 2308 | AstNode *proto_node = fn_table_entry->proto_node; | |
| 2309 | assert(proto_node->type == NodeTypeFnProto); | |
| 2310 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; | |
| 2311 | ||
| 2312 | 2309 | TypeTableEntry *fn_type = fn_table_entry->type_entry; |
| 2310 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; | |
| 2313 | 2311 | |
| 2314 | 2312 | LLVMValueRef fn_val = fn_llvm_value(g, fn_table_entry); |
| 2315 | 2313 | |
| ... | ... | @@ -2327,22 +2325,20 @@ static void do_code_gen(CodeGen *g) { |
| 2327 | 2325 | |
| 2328 | 2326 | |
| 2329 | 2327 | // set parameter attributes |
| 2330 | for (size_t param_decl_i = 0; param_decl_i < fn_proto->params.length; param_decl_i += 1) { | |
| 2331 | AstNode *param_node = fn_proto->params.at(param_decl_i); | |
| 2332 | assert(param_node->type == NodeTypeParamDecl); | |
| 2333 | ||
| 2334 | FnGenParamInfo *info = &fn_type->data.fn.gen_param_info[param_decl_i]; | |
| 2335 | size_t gen_index = info->gen_index; | |
| 2336 | bool is_byval = info->is_byval; | |
| 2328 | for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { | |
| 2329 | FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; | |
| 2330 | size_t gen_index = gen_info->gen_index; | |
| 2331 | bool is_byval = gen_info->is_byval; | |
| 2337 | 2332 | |
| 2338 | 2333 | if (gen_index == SIZE_MAX) { |
| 2339 | 2334 | continue; |
| 2340 | 2335 | } |
| 2341 | 2336 | |
| 2342 | TypeTableEntry *param_type = info->type; | |
| 2337 | FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; | |
| 2338 | ||
| 2339 | TypeTableEntry *param_type = gen_info->type; | |
| 2343 | 2340 | LLVMValueRef argument_val = LLVMGetParam(fn_val, gen_index); |
| 2344 | bool param_is_noalias = param_node->data.param_decl.is_noalias; | |
| 2345 | if (param_is_noalias) { | |
| 2341 | if (param_info->is_noalias) { | |
| 2346 | 2342 | LLVMAddAttribute(argument_val, LLVMNoAliasAttribute); |
| 2347 | 2343 | } |
| 2348 | 2344 | if ((param_type->id == TypeTableEntryIdPointer && param_type->data.pointer.is_const) || is_byval) { |
| ... | ... | @@ -2402,7 +2398,6 @@ static void do_code_gen(CodeGen *g) { |
| 2402 | 2398 | if (should_skip_fn_codegen(g, fn_table_entry)) |
| 2403 | 2399 | continue; |
| 2404 | 2400 | |
| 2405 | ImportTableEntry *import = fn_table_entry->import_entry; | |
| 2406 | 2401 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); |
| 2407 | 2402 | g->cur_fn = fn_table_entry; |
| 2408 | 2403 | g->cur_fn_val = fn; |
| ... | ... | @@ -2412,10 +2407,6 @@ static void do_code_gen(CodeGen *g) { |
| 2412 | 2407 | g->cur_ret_ptr = nullptr; |
| 2413 | 2408 | } |
| 2414 | 2409 | |
| 2415 | AstNode *proto_node = fn_table_entry->proto_node; | |
| 2416 | assert(proto_node->type == NodeTypeFnProto); | |
| 2417 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; | |
| 2418 | ||
| 2419 | 2410 | build_all_basic_blocks(g, fn_table_entry); |
| 2420 | 2411 | clear_debug_source_node(g); |
| 2421 | 2412 | |
| ... | ... | @@ -2444,6 +2435,8 @@ static void do_code_gen(CodeGen *g) { |
| 2444 | 2435 | *slot = LLVMBuildAlloca(g->builder, instruction->type_entry->type_ref, ""); |
| 2445 | 2436 | } |
| 2446 | 2437 | |
| 2438 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); | |
| 2439 | ||
| 2447 | 2440 | // create debug variable declarations for variables and allocate all local variables |
| 2448 | 2441 | for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { |
| 2449 | 2442 | VariableTableEntry *var = fn_table_entry->variable_list.at(var_i); |
| ... | ... | @@ -2484,10 +2477,12 @@ static void do_code_gen(CodeGen *g) { |
| 2484 | 2477 | } |
| 2485 | 2478 | } |
| 2486 | 2479 | |
| 2480 | FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; | |
| 2481 | ||
| 2487 | 2482 | // create debug variable declarations for parameters |
| 2488 | 2483 | // rely on the first variables in the variable_list being parameters. |
| 2489 | 2484 | size_t next_var_i = 0; |
| 2490 | for (size_t param_i = 0; param_i < fn_proto->params.length; param_i += 1) { | |
| 2485 | for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { | |
| 2491 | 2486 | FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; |
| 2492 | 2487 | if (info->gen_index == SIZE_MAX) |
| 2493 | 2488 | continue; |
| ... | ... | @@ -3392,14 +3387,12 @@ void codegen_generate_h_file(CodeGen *g) { |
| 3392 | 3387 | buf_resize(&h_buf, 0); |
| 3393 | 3388 | for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { |
| 3394 | 3389 | FnTableEntry *fn_table_entry = g->fn_defs.at(fn_def_i); |
| 3395 | AstNode *proto_node = fn_table_entry->proto_node; | |
| 3396 | assert(proto_node->type == NodeTypeFnProto); | |
| 3397 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; | |
| 3398 | 3390 | |
| 3399 | if (fn_proto->visib_mod != VisibModExport) | |
| 3391 | if (fn_table_entry->internal_linkage) | |
| 3400 | 3392 | continue; |
| 3401 | 3393 | |
| 3402 | 3394 | FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; |
| 3395 | ||
| 3403 | 3396 | Buf return_type_c = BUF_INIT; |
| 3404 | 3397 | get_c_type(g, fn_type_id->return_type, &return_type_c); |
| 3405 | 3398 | |
| ... | ... | @@ -3412,7 +3405,7 @@ void codegen_generate_h_file(CodeGen *g) { |
| 3412 | 3405 | if (fn_type_id->param_count > 0) { |
| 3413 | 3406 | for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { |
| 3414 | 3407 | FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; |
| 3415 | AstNode *param_decl_node = fn_proto->params.at(param_i); | |
| 3408 | AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); | |
| 3416 | 3409 | Buf *param_name = param_decl_node->data.param_decl.name; |
| 3417 | 3410 | |
| 3418 | 3411 | const char *comma_str = (param_i == 0) ? "" : ", "; |
src/ir.cpp+143-11| ... | ... | @@ -2825,9 +2825,8 @@ IrInstruction *ir_gen_fn(CodeGen *codegen, FnTableEntry *fn_entry) { |
| 2825 | 2825 | AstNode *body_node = fn_def_node->data.fn_def.body; |
| 2826 | 2826 | |
| 2827 | 2827 | assert(fn_entry->child_scope); |
| 2828 | Scope *child_scope = fn_entry->child_scope; | |
| 2829 | 2828 | |
| 2830 | return ir_gen(codegen, body_node, child_scope, ir_executable); | |
| 2829 | return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable); | |
| 2831 | 2830 | } |
| 2832 | 2831 | |
| 2833 | 2832 | static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) { |
| ... | ... | @@ -4220,9 +4219,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 4220 | 4219 | } |
| 4221 | 4220 | |
| 4222 | 4221 | static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, |
| 4223 | IrInstruction *arg, Scope **exec_scope, size_t *next_arg_index) | |
| 4222 | IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) | |
| 4224 | 4223 | { |
| 4225 | AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_arg_index); | |
| 4224 | AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); | |
| 4226 | 4225 | assert(param_decl_node->type == NodeTypeParamDecl); |
| 4227 | 4226 | AstNode *param_type_node = param_decl_node->data.param_decl.type; |
| 4228 | 4227 | TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node); |
| ... | ... | @@ -4241,14 +4240,66 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node |
| 4241 | 4240 | VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, |
| 4242 | 4241 | *exec_scope, param_name, param_type, true, first_arg_val); |
| 4243 | 4242 | *exec_scope = var->child_scope; |
| 4244 | *next_arg_index += 1; | |
| 4243 | *next_proto_i += 1; | |
| 4245 | 4244 | |
| 4246 | 4245 | return true; |
| 4247 | 4246 | } |
| 4248 | 4247 | |
| 4248 | static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node, | |
| 4249 | IrInstruction *arg, Scope **child_scope, size_t *next_proto_i, | |
| 4250 | GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstruction **casted_args, | |
| 4251 | FnTableEntry *impl_fn) | |
| 4252 | { | |
| 4253 | AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); | |
| 4254 | assert(param_decl_node->type == NodeTypeParamDecl); | |
| 4255 | AstNode *param_type_node = param_decl_node->data.param_decl.type; | |
| 4256 | TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *child_scope, param_type_node); | |
| 4257 | if (param_type->id == TypeTableEntryIdInvalid) | |
| 4258 | return false; | |
| 4259 | ||
| 4260 | bool is_var_type = (param_type->id == TypeTableEntryIdVar); | |
| 4261 | IrInstruction *casted_arg; | |
| 4262 | if (is_var_type) { | |
| 4263 | casted_arg = arg; | |
| 4264 | } else { | |
| 4265 | casted_arg = ir_get_casted_value(ira, arg, param_type); | |
| 4266 | if (casted_arg->type_entry->id == TypeTableEntryIdInvalid) | |
| 4267 | return false; | |
| 4268 | } | |
| 4269 | ||
| 4270 | bool inline_arg = param_decl_node->data.param_decl.is_inline; | |
| 4271 | if (inline_arg || is_var_type) { | |
| 4272 | ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg); | |
| 4273 | if (!arg_val) | |
| 4274 | return false; | |
| 4275 | ||
| 4276 | Buf *param_name = param_decl_node->data.param_decl.name; | |
| 4277 | VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, | |
| 4278 | *child_scope, param_name, param_type, true, arg_val); | |
| 4279 | *child_scope = var->child_scope; | |
| 4280 | // This generic function instance could be called with anything, so when this variable is read it | |
| 4281 | // needs to know that it depends on compile time variable data. | |
| 4282 | var->value->depends_on_compile_var = true; | |
| 4283 | ||
| 4284 | GenericParamValue *generic_param = &generic_id->params[generic_id->param_count]; | |
| 4285 | generic_param->type = casted_arg->type_entry; | |
| 4286 | generic_param->value = arg_val; | |
| 4287 | generic_id->param_count += 1; | |
| 4288 | } else { | |
| 4289 | casted_args[fn_type_id->param_count] = casted_arg; | |
| 4290 | FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; | |
| 4291 | param_info->type = param_type; | |
| 4292 | param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; | |
| 4293 | impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; | |
| 4294 | fn_type_id->param_count += 1; | |
| 4295 | } | |
| 4296 | *next_proto_i += 1; | |
| 4297 | return true; | |
| 4298 | } | |
| 4299 | ||
| 4249 | 4300 | static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, |
| 4250 | 4301 | FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref, |
| 4251 | IrInstruction *first_arg_ptr, bool is_inline) | |
| 4302 | IrInstruction *first_arg_ptr, bool inline_fn_call) | |
| 4252 | 4303 | { |
| 4253 | 4304 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; |
| 4254 | 4305 | size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; |
| ... | ... | @@ -4278,7 +4329,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 4278 | 4329 | return ira->codegen->builtin_types.entry_invalid; |
| 4279 | 4330 | } |
| 4280 | 4331 | |
| 4281 | if (is_inline) { | |
| 4332 | if (inline_fn_call) { | |
| 4333 | // No special handling is needed for compile time evaluation of generic functions. | |
| 4282 | 4334 | if (!fn_entry) { |
| 4283 | 4335 | ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression")); |
| 4284 | 4336 | return ira->codegen->builtin_types.entry_invalid; |
| ... | ... | @@ -4290,13 +4342,13 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 4290 | 4342 | // Fork a scope of the function with known values for the parameters. |
| 4291 | 4343 | Scope *exec_scope = &fn_entry->fndef_scope->base; |
| 4292 | 4344 | |
| 4293 | size_t next_arg_index = 0; | |
| 4345 | size_t next_proto_i = 0; | |
| 4294 | 4346 | if (first_arg_ptr) { |
| 4295 | 4347 | IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); |
| 4296 | 4348 | if (first_arg->type_entry->id == TypeTableEntryIdInvalid) |
| 4297 | 4349 | return ira->codegen->builtin_types.entry_invalid; |
| 4298 | 4350 | |
| 4299 | if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_arg_index)) | |
| 4351 | if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i)) | |
| 4300 | 4352 | return ira->codegen->builtin_types.entry_invalid; |
| 4301 | 4353 | } |
| 4302 | 4354 | |
| ... | ... | @@ -4305,7 +4357,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 4305 | 4357 | if (old_arg->type_entry->id == TypeTableEntryIdInvalid) |
| 4306 | 4358 | return ira->codegen->builtin_types.entry_invalid; |
| 4307 | 4359 | |
| 4308 | if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_arg_index)) | |
| 4360 | if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) | |
| 4309 | 4361 | return ira->codegen->builtin_types.entry_invalid; |
| 4310 | 4362 | } |
| 4311 | 4363 | |
| ... | ... | @@ -4327,9 +4379,89 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 4327 | 4379 | return ir_finish_anal(ira, return_type); |
| 4328 | 4380 | } |
| 4329 | 4381 | |
| 4382 | if (fn_type->data.fn.is_generic) { | |
| 4383 | assert(fn_entry); | |
| 4384 | ||
| 4385 | IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count); | |
| 4386 | ||
| 4387 | // Fork a scope of the function with known values for the parameters. | |
| 4388 | Scope *parent_scope = fn_entry->fndef_scope->base.parent; | |
| 4389 | FnTableEntry *impl_fn = create_fn(ira->codegen, fn_proto_node); | |
| 4390 | impl_fn->param_source_nodes = allocate<AstNode *>(call_param_count); | |
| 4391 | buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name); | |
| 4392 | impl_fn->fndef_scope = create_fndef_scope(impl_fn->fn_def_node, parent_scope, impl_fn); | |
| 4393 | impl_fn->child_scope = &impl_fn->fndef_scope->base; | |
| 4394 | FnTypeId fn_type_id = {0}; | |
| 4395 | init_fn_type_id(&fn_type_id, fn_proto_node); | |
| 4396 | fn_type_id.param_count = 0; | |
| 4397 | ||
| 4398 | // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly | |
| 4399 | // as the key in generic_table | |
| 4400 | GenericFnTypeId *generic_id = allocate<GenericFnTypeId>(1); | |
| 4401 | generic_id->fn_entry = fn_entry; | |
| 4402 | generic_id->param_count = 0; | |
| 4403 | generic_id->params = allocate<GenericParamValue>(src_param_count); | |
| 4404 | size_t next_proto_i = 0; | |
| 4405 | ||
| 4406 | if (first_arg_ptr) { | |
| 4407 | IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); | |
| 4408 | if (first_arg->type_entry->id == TypeTableEntryIdInvalid) | |
| 4409 | return ira->codegen->builtin_types.entry_invalid; | |
| 4410 | ||
| 4411 | if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, &impl_fn->child_scope, | |
| 4412 | &next_proto_i, generic_id, &fn_type_id, casted_args, impl_fn)) | |
| 4413 | { | |
| 4414 | return ira->codegen->builtin_types.entry_invalid; | |
| 4415 | } | |
| 4416 | } | |
| 4417 | for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { | |
| 4418 | IrInstruction *arg = call_instruction->args[call_i]->other; | |
| 4419 | if (arg->type_entry->id == TypeTableEntryIdInvalid) | |
| 4420 | return ira->codegen->builtin_types.entry_invalid; | |
| 4421 | ||
| 4422 | if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, | |
| 4423 | &next_proto_i, generic_id, &fn_type_id, casted_args, impl_fn)) | |
| 4424 | { | |
| 4425 | return ira->codegen->builtin_types.entry_invalid; | |
| 4426 | } | |
| 4427 | } | |
| 4428 | ||
| 4429 | auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn); | |
| 4430 | if (existing_entry) { | |
| 4431 | // throw away all our work and use the existing function | |
| 4432 | impl_fn = existing_entry->value; | |
| 4433 | } else { | |
| 4434 | // finish instantiating the function | |
| 4435 | AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; | |
| 4436 | TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); | |
| 4437 | if (return_type->id == TypeTableEntryIdInvalid) | |
| 4438 | return ira->codegen->builtin_types.entry_invalid; | |
| 4439 | fn_type_id.return_type = return_type; | |
| 4440 | ||
| 4441 | impl_fn->type_entry = get_fn_type(ira->codegen, &fn_type_id); | |
| 4442 | if (impl_fn->type_entry->id == TypeTableEntryIdInvalid) | |
| 4443 | return ira->codegen->builtin_types.entry_invalid; | |
| 4444 | ||
| 4445 | ira->codegen->fn_protos.append(impl_fn); | |
| 4446 | ira->codegen->fn_defs.append(impl_fn); | |
| 4447 | } | |
| 4448 | ||
| 4449 | size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; | |
| 4450 | IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, | |
| 4451 | impl_fn, nullptr, impl_param_count, casted_args); | |
| 4452 | ||
| 4453 | TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; | |
| 4454 | if (type_has_bits(return_type) && handle_is_ptr(return_type)) { | |
| 4455 | FnTableEntry *callsite_fn = exec_fn_entry(ira->new_irb.exec); | |
| 4456 | assert(callsite_fn); | |
| 4457 | callsite_fn->alloca_list.append(new_call_instruction); | |
| 4458 | } | |
| 4459 | ||
| 4460 | return ir_finish_anal(ira, return_type); | |
| 4461 | } | |
| 4462 | ||
| 4330 | 4463 | IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count); |
| 4331 | 4464 | size_t next_arg_index = 0; |
| 4332 | ||
| 4333 | 4465 | if (first_arg_ptr) { |
| 4334 | 4466 | IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); |
| 4335 | 4467 | if (first_arg->type_entry->id == TypeTableEntryIdInvalid) |
test/self_hosted2.zig+29| ... | ... | @@ -111,6 +111,33 @@ fn testCompileTimeFib() { |
| 111 | 111 | assert(fib_7 == 13); |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | fn max(inline T: type, a: T, b: T) -> T { | |
| 115 | if (a > b) a else b | |
| 116 | } | |
| 117 | const the_max = max(u32, 1234, 5678); | |
| 118 | ||
| 119 | fn testCompileTimeGenericEval() { | |
| 120 | assert(the_max == 5678); | |
| 121 | } | |
| 122 | ||
| 123 | fn gimmeTheBigOne(a: u32, b: u32) -> u32 { | |
| 124 | max(u32, a, b) | |
| 125 | } | |
| 126 | ||
| 127 | fn shouldCallSameInstance(a: u32, b: u32) -> u32 { | |
| 128 | max(u32, a, b) | |
| 129 | } | |
| 130 | ||
| 131 | fn sameButWithFloats(a: f64, b: f64) -> f64 { | |
| 132 | max(f64, a, b) | |
| 133 | } | |
| 134 | ||
| 135 | fn testFnWithInlineArgs() { | |
| 136 | assert(gimmeTheBigOne(1234, 5678) == 5678); | |
| 137 | assert(shouldCallSameInstance(34, 12) == 34); | |
| 138 | assert(sameButWithFloats(0.43, 0.49) == 0.49); | |
| 139 | } | |
| 140 | ||
| 114 | 141 | |
| 115 | 142 | fn assert(ok: bool) { |
| 116 | 143 | if (!ok) |
| ... | ... | @@ -129,6 +156,8 @@ fn runAllTests() { |
| 129 | 156 | testStructStatic(); |
| 130 | 157 | testStaticFnEval(); |
| 131 | 158 | testCompileTimeFib(); |
| 159 | testCompileTimeGenericEval(); | |
| 160 | testFnWithInlineArgs(); | |
| 132 | 161 | } |
| 133 | 162 | |
| 134 | 163 | export nakedcc fn _start() -> unreachable { |