authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-18 08:50:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-18 08:50:10-07:00
log3a326d50051ad8b65639e5e2bbe45e41a8b35591
tree797dc5661ae401c4d75971fb8c722a8834cfb2c9
parent4c50606b9d41c2c3d9f25dc8bf0848e30e338f6e

pave the road for function pointers

See #14

7 files changed, 110 insertions(+), 137 deletions(-)

src/all_types.hpp+2-6
......@@ -793,11 +793,6 @@ struct LabelTableEntry {
793793 bool entered_from_fallthrough;
794794};
795795
796enum FnAttrId {
797 FnAttrIdNaked,
798 FnAttrIdAlwaysInline,
799};
800
801796struct FnTableEntry {
802797 LLVMValueRef fn_value;
803798 AstNode *proto_node;
......@@ -806,7 +801,8 @@ struct FnTableEntry {
806801 bool internal_linkage;
807802 unsigned calling_convention;
808803 ImportTableEntry *import_entry;
809 ZigList<FnAttrId> fn_attr_list;
804 bool is_naked;
805 bool is_inline;
810806 // Required to be a pre-order traversal of the AST. (parents must come before children)
811807 ZigList<BlockContext *> all_block_contexts;
812808 TypeTableEntry *member_of_struct;
src/analyze.cpp+90-30
......@@ -346,18 +346,19 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
346346 ImportTableEntry *import)
347347{
348348 assert(node->type == NodeTypeFnProto);
349 AstNodeFnProto *fn_proto = &node->data.fn_proto;
349350
350 for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) {
351 AstNode *directive_node = node->data.fn_proto.directives->at(i);
351 for (int i = 0; i < fn_proto->directives->length; i += 1) {
352 AstNode *directive_node = fn_proto->directives->at(i);
352353 Buf *name = &directive_node->data.directive.name;
353354
354355 if (buf_eql_str(name, "attribute")) {
355356 Buf *attr_name = &directive_node->data.directive.param;
356357 if (fn_table_entry->fn_def_node) {
357358 if (buf_eql_str(attr_name, "naked")) {
358 fn_table_entry->fn_attr_list.append(FnAttrIdNaked);
359 fn_table_entry->is_naked = true;
359360 } else if (buf_eql_str(attr_name, "inline")) {
360 fn_table_entry->fn_attr_list.append(FnAttrIdAlwaysInline);
361 fn_table_entry->is_inline = true;
361362 } else {
362363 add_node_error(g, directive_node,
363364 buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));
......@@ -382,38 +383,100 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
382383
383384 buf_resize(&fn_type->name, 0);
384385 buf_appendf(&fn_type->name, "fn(");
386 int gen_param_count = 0;
387 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(param_count);
388 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(1 + param_count);
385389 for (int i = 0; i < param_count; i += 1) {
386390 AstNode *child = node->data.fn_proto.params.at(i);
387391 assert(child->type == NodeTypeParamDecl);
388392 TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context,
389393 child->data.param_decl.type);
390 fn_table_entry->type_entry->data.fn.param_types[i] = type_entry;
391
392 buf_appendf(&fn_type->name, "%s", buf_ptr(&type_entry->name));
393
394 if (i + 1 < param_count) {
395 buf_appendf(&fn_type->name, ", ");
396 }
394 fn_type->data.fn.param_types[i] = type_entry;
397395
398396 if (type_entry->id == TypeTableEntryIdUnreachable) {
399397 add_node_error(g, child->data.param_decl.type,
400398 buf_sprintf("parameter of type 'unreachable' not allowed"));
401 } else if (type_entry->id == TypeTableEntryIdVoid) {
402 if (node->data.fn_proto.visib_mod == VisibModExport) {
403 add_node_error(g, child->data.param_decl.type,
404 buf_sprintf("parameter of type 'void' not allowed on exported functions"));
405 }
399 fn_proto->skip = true;
400 } else if (type_entry->id == TypeTableEntryIdInvalid) {
401 fn_proto->skip = true;
402 }
403
404 if (!fn_proto->skip && type_entry->size_in_bits > 0) {
405 const char *comma = (gen_param_count == 0) ? "" : ", ";
406 buf_appendf(&fn_type->name, "%s%s", comma, buf_ptr(&type_entry->name));
407
408 TypeTableEntry *gen_type = handle_is_ptr(type_entry) ?
409 get_pointer_to_type(g, type_entry, true) : type_entry;
410 gen_param_types[gen_param_count] = gen_type->type_ref;
411
412 gen_param_count += 1;
413
414 // after the gen_param_count += 1 because 0 is the return type
415 param_di_types[gen_param_count] = gen_type->di_type;
406416 }
407417 }
408418
409419 TypeTableEntry *return_type = analyze_type_expr(g, import, import->block_context,
410420 node->data.fn_proto.return_type);
411 fn_table_entry->type_entry->data.fn.return_type = return_type;
421 fn_type->data.fn.return_type = return_type;
422 if (return_type->id == TypeTableEntryIdInvalid) {
423 fn_proto->skip = true;
424 }
412425
413426 buf_appendf(&fn_type->name, ")");
414427 if (return_type->id != TypeTableEntryIdVoid) {
415428 buf_appendf(&fn_type->name, " %s", buf_ptr(&return_type->name));
416429 }
430
431 if (fn_proto->skip) {
432 return;
433 }
434
435 fn_type->type_ref = LLVMFunctionType(return_type->type_ref, gen_param_types, gen_param_count,
436 fn_proto->is_var_args);
437
438 fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(&fn_table_entry->symbol_name),
439 fn_table_entry->type_entry->type_ref);
440
441 if (fn_table_entry->is_inline) {
442 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
443 }
444 if (fn_table_entry->is_naked) {
445 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute);
446 }
447
448 LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?
449 LLVMInternalLinkage : LLVMExternalLinkage);
450
451 if (return_type->id == TypeTableEntryIdUnreachable) {
452 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);
453 }
454 LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_table_entry->calling_convention);
455 if (!fn_table_entry->is_extern) {
456 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute);
457 }
458
459 param_di_types[0] = return_type->di_type;
460 LLVMZigDISubroutineType *di_sub_type = LLVMZigCreateSubroutineType(g->dbuilder, import->di_file,
461 param_di_types, gen_param_count + 1, 0);
462
463 // Add debug info.
464 unsigned line_number = node->line + 1;
465 unsigned scope_line = line_number;
466 bool is_definition = fn_table_entry->fn_def_node != nullptr;
467 unsigned flags = 0;
468 bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
469 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
470 import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
471 import->di_file, line_number,
472 di_sub_type, fn_table_entry->internal_linkage,
473 is_definition, scope_line, flags, is_optimized, fn_table_entry->fn_value);
474 fn_type->di_type = LLVMZigSubroutineToType(di_sub_type);
475 if (fn_table_entry->fn_def_node) {
476 BlockContext *context = new_block_context(fn_table_entry->fn_def_node, import->block_context);
477 fn_table_entry->fn_def_node->data.fn_def.block_context = context;
478 context->di_scope = LLVMZigSubprogramToScope(subprogram);
479 }
417480}
418481
419482static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) {
......@@ -724,14 +787,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
724787 buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
725788 proto_node->data.fn_proto.skip = true;
726789 skip = true;
727 } else if (is_pub) {
728 // TODO is this else if branch a mistake?
729 auto entry = fn_table->maybe_get(proto_name);
730 if (entry) {
731 add_node_error(g, proto_node, buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
732 proto_node->data.fn_proto.skip = true;
733 skip = true;
734 }
735790 }
736791 if (!extern_node && proto_node->data.fn_proto.is_var_args) {
737792 add_node_error(g, proto_node,
......@@ -775,10 +830,8 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
775830 g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
776831 }
777832
778 resolve_function_proto(g, proto_node, fn_table_entry, import);
779
780
781833 proto_node->data.fn_proto.fn_table_entry = fn_table_entry;
834 resolve_function_proto(g, proto_node, fn_table_entry, import);
782835
783836 if (fn_def_node) {
784837 preview_function_labels(g, fn_def_node->data.fn_def.body, fn_table_entry);
......@@ -3146,8 +3199,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
31463199 return;
31473200 }
31483201
3149 BlockContext *context = new_block_context(node, import->block_context);
3150 node->data.fn_def.block_context = context;
3202 BlockContext *context = node->data.fn_def.block_context;
31513203
31523204 AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
31533205 bool is_exported = (fn_proto->visib_mod == VisibModExport);
......@@ -3923,3 +3975,11 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits)
39233975TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) {
39243976 return *get_int_type_ptr(g, is_signed, size_in_bits);
39253977}
3978
3979bool handle_is_ptr(TypeTableEntry *type_entry) {
3980 return type_entry->id == TypeTableEntryIdStruct ||
3981 (type_entry->id == TypeTableEntryIdEnum && type_entry->data.enumeration.gen_field_count != 0) ||
3982 type_entry->id == TypeTableEntryIdMaybe ||
3983 type_entry->id == TypeTableEntryIdArray;
3984}
3985
src/analyze.hpp+1-1
......@@ -23,5 +23,5 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
2323bool is_node_void_expr(AstNode *node);
2424TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits);
2525TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits);
26
26bool handle_is_ptr(TypeTableEntry *type_entry);
2727#endif
src/codegen.cpp+11-96
......@@ -82,29 +82,13 @@ static TypeTableEntry *get_type_for_type_node(AstNode *node) {
8282 return const_val->data.x_type;
8383}
8484
85static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
86 TypeTableEntry *type_entry = get_type_for_type_node(type_node);
87
88 if (type_entry->id == TypeTableEntryIdStruct || type_entry->id == TypeTableEntryIdArray) {
89 return get_pointer_to_type(g, type_entry, true);
90 } else {
91 return type_entry;
92 }
93}
94
95static LLVMZigDIType *to_llvm_debug_type(CodeGen *g, AstNode *type_node) {
96 TypeTableEntry *type_entry = get_type_for_type_node(type_node);
97 return type_entry->di_type;
98}
99
100
10185static bool type_is_unreachable(CodeGen *g, AstNode *type_node) {
10286 return get_type_for_type_node(type_node)->id == TypeTableEntryIdUnreachable;
10387}
10488
10589static bool is_param_decl_type_void(CodeGen *g, AstNode *param_decl_node) {
10690 assert(param_decl_node->type == NodeTypeParamDecl);
107 return get_type_for_type_node(param_decl_node->data.param_decl.type)->id == TypeTableEntryIdVoid;
91 return get_type_for_type_node(param_decl_node->data.param_decl.type)->size_in_bits == 0;
10892}
10993
11094static int count_non_void_params(CodeGen *g, ZigList<AstNode *> *params) {
......@@ -150,11 +134,14 @@ static TypeTableEntry *get_expr_type(AstNode *node) {
150134 return expr->type_entry;
151135}
152136
153static bool handle_is_ptr(TypeTableEntry *type_entry) {
154 return type_entry->id == TypeTableEntryIdStruct ||
155 (type_entry->id == TypeTableEntryIdEnum && type_entry->data.enumeration.gen_field_count != 0) ||
156 type_entry->id == TypeTableEntryIdMaybe ||
157 type_entry->id == TypeTableEntryIdArray;
137static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
138 TypeTableEntry *type_entry = get_type_for_type_node(type_node);
139
140 if (handle_is_ptr(type_entry)) {
141 return get_pointer_to_type(g, type_entry, true);
142 } else {
143 return type_entry;
144 }
158145}
159146
160147static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node,
......@@ -2121,31 +2108,6 @@ static void build_label_blocks(CodeGen *g, AstNode *block_node) {
21212108
21222109}
21232110
2124static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
2125 LLVMZigDIFile *di_file)
2126{
2127 LLVMZigDIType **types = allocate<LLVMZigDIType*>(1 + fn_proto->params.length);
2128 types[0] = to_llvm_debug_type(g, fn_proto->return_type);
2129 int types_len = fn_proto->params.length + 1;
2130 for (int i = 0; i < fn_proto->params.length; i += 1) {
2131 AstNode *param_node = fn_proto->params.at(i);
2132 assert(param_node->type == NodeTypeParamDecl);
2133 LLVMZigDIType *param_type = to_llvm_debug_type(g, param_node->data.param_decl.type);
2134 types[i + 1] = param_type;
2135 }
2136 return LLVMZigCreateSubroutineType(g->dbuilder, di_file, types, types_len, 0);
2137}
2138
2139static LLVMAttribute to_llvm_fn_attr(FnAttrId attr_id) {
2140 switch (attr_id) {
2141 case FnAttrIdNaked:
2142 return LLVMNakedAttribute;
2143 case FnAttrIdAlwaysInline:
2144 return LLVMAlwaysInlineAttribute;
2145 }
2146 zig_unreachable();
2147}
2148
21492111static void do_code_gen(CodeGen *g) {
21502112 assert(!g->errors.length);
21512113
......@@ -2172,45 +2134,12 @@ static void do_code_gen(CodeGen *g) {
21722134 // Generate function prototypes
21732135 for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
21742136 FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);
2175
21762137 AstNode *proto_node = fn_table_entry->proto_node;
21772138 assert(proto_node->type == NodeTypeFnProto);
21782139 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
21792140
2180 LLVMTypeRef ret_type = get_type_for_type_node(fn_proto->return_type)->type_ref;
2181 int param_count = count_non_void_params(g, &fn_proto->params);
2182 LLVMTypeRef *param_types = allocate<LLVMTypeRef>(param_count);
2183 int gen_param_index = 0;
2184 for (int param_decl_i = 0; param_decl_i < fn_proto->params.length; param_decl_i += 1) {
2185 AstNode *param_node = fn_proto->params.at(param_decl_i);
2186 assert(param_node->type == NodeTypeParamDecl);
2187 if (is_param_decl_type_void(g, param_node))
2188 continue;
2189 AstNode *type_node = param_node->data.param_decl.type;
2190 param_types[gen_param_index] = fn_proto_type_from_type_node(g, type_node)->type_ref;
2191 gen_param_index += 1;
2192 }
2193 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args);
2194
2195 LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_table_entry->symbol_name), function_type);
2196
2197 for (int attr_i = 0; attr_i < fn_table_entry->fn_attr_list.length; attr_i += 1) {
2198 FnAttrId attr_id = fn_table_entry->fn_attr_list.at(attr_i);
2199 LLVMAddFunctionAttr(fn, to_llvm_fn_attr(attr_id));
2200 }
2201
2202 LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage);
2203
2204 if (type_is_unreachable(g, fn_proto->return_type)) {
2205 LLVMAddFunctionAttr(fn, LLVMNoReturnAttribute);
2206 }
2207 LLVMSetFunctionCallConv(fn, fn_table_entry->calling_convention);
2208 if (!fn_table_entry->is_extern) {
2209 LLVMAddFunctionAttr(fn, LLVMNoUnwindAttribute);
2210 }
2211
22122141 // set parameter attributes
2213 gen_param_index = 0;
2142 int gen_param_index = 0;
22142143 for (int param_decl_i = 0; param_decl_i < fn_proto->params.length; param_decl_i += 1) {
22152144 AstNode *param_node = fn_proto->params.at(param_decl_i);
22162145 assert(param_node->type == NodeTypeParamDecl);
......@@ -2218,7 +2147,7 @@ static void do_code_gen(CodeGen *g) {
22182147 continue;
22192148 AstNode *type_node = param_node->data.param_decl.type;
22202149 TypeTableEntry *param_type = fn_proto_type_from_type_node(g, type_node);
2221 LLVMValueRef argument_val = LLVMGetParam(fn, gen_param_index);
2150 LLVMValueRef argument_val = LLVMGetParam(fn_table_entry->fn_value, gen_param_index);
22222151 bool param_is_noalias = param_node->data.param_decl.is_noalias;
22232152 if (param_type->id == TypeTableEntryIdPointer && param_is_noalias) {
22242153 LLVMAddAttribute(argument_val, LLVMNoAliasAttribute);
......@@ -2230,7 +2159,6 @@ static void do_code_gen(CodeGen *g) {
22302159 gen_param_index += 1;
22312160 }
22322161
2233 fn_table_entry->fn_value = fn;
22342162 }
22352163
22362164 // Generate function definitions.
......@@ -2245,22 +2173,9 @@ static void do_code_gen(CodeGen *g) {
22452173 assert(proto_node->type == NodeTypeFnProto);
22462174 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
22472175
2248 // Add debug info.
2249 unsigned line_number = fn_def_node->line + 1;
2250 unsigned scope_line = line_number;
2251 bool is_definition = true;
2252 unsigned flags = 0;
2253 bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
2254 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
2255 import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
2256 import->di_file, line_number,
2257 create_di_function_type(g, fn_proto, import->di_file), fn_table_entry->internal_linkage,
2258 is_definition, scope_line, flags, is_optimized, fn);
2259
22602176 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn, "entry");
22612177 LLVMPositionBuilderAtEnd(g->builder, entry_block);
22622178
2263 fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
22642179
22652180 AstNode *body_node = fn_def_node->data.fn_def.body;
22662181 build_label_blocks(g, body_node);
src/zig_llvm.cpp+5
......@@ -388,6 +388,11 @@ LLVMZigDIScope *LLVMZigSubprogramToScope(LLVMZigDISubprogram *subprogram) {
388388 return reinterpret_cast<LLVMZigDIScope*>(scope);
389389}
390390
391LLVMZigDIType *LLVMZigSubroutineToType(LLVMZigDISubroutineType *subrtype) {
392 DIType *di_type = reinterpret_cast<DISubroutineType*>(subrtype);
393 return reinterpret_cast<LLVMZigDIType*>(di_type);
394}
395
391396LLVMZigDIScope *LLVMZigTypeToScope(LLVMZigDIType *type) {
392397 DIScope *scope = reinterpret_cast<DIType*>(type);
393398 return reinterpret_cast<LLVMZigDIScope*>(scope);
src/zig_llvm.hpp+1
......@@ -101,6 +101,7 @@ LLVMZigDIScope *LLVMZigCompileUnitToScope(LLVMZigDICompileUnit *compile_unit);
101101LLVMZigDIScope *LLVMZigFileToScope(LLVMZigDIFile *difile);
102102LLVMZigDIScope *LLVMZigSubprogramToScope(LLVMZigDISubprogram *subprogram);
103103LLVMZigDIScope *LLVMZigTypeToScope(LLVMZigDIType *type);
104LLVMZigDIType *LLVMZigSubroutineToType(LLVMZigDISubroutineType *subrtype);
104105
105106LLVMZigDILocalVariable *LLVMZigCreateLocalVariable(LLVMZigDIBuilder *dbuilder, unsigned tag,
106107 LLVMZigDIScope *scope, const char *name, LLVMZigDIFile *file, unsigned line_no,
test/run_tests.cpp-4
......@@ -1296,10 +1296,6 @@ fn f() => {
12961296fn f(a : unreachable) => {}
12971297 )SOURCE", 1, ".tmp_source.zig:2:10: error: parameter of type 'unreachable' not allowed");
12981298
1299 add_compile_fail_case("exporting a void parameter", R"SOURCE(
1300export fn f(a : void) => {}
1301 )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions");
1302
13031299 add_compile_fail_case("unused label", R"SOURCE(
13041300fn f() => {
13051301a_label: