| author | |
| committer | |
| log | 77ffb5075bd550893b9f6ac99f151b1d55a8040e |
| tree | d7cde8b3527f2458f216562f42b5957fefc477e9 |
| parent | 91101f08c24a931e8e0ecbe46d80df759135332c |
* Directives can have arbitrary expressions as parameters
* Fix switch statement not generating code sometimes
* Rename "main" fn in bootstrap.zig to "zig_user_main" to
avoid name collisions
* codegen: fix badref when unreachable is last thing in an
expression
* support #condition directive on exported functions8 files changed, 208 insertions(+), 141 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -31,7 +31,7 @@ ExternDecl = "extern" (FnProto | VariableDeclaration) ";" |
| 31 | 31 | |
| 32 | 32 | FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) |
| 33 | 33 | |
| 34 | Directive = "#" "Symbol" "(" "String" ")" | |
| 34 | Directive = "#" "Symbol" "(" Expression ")" | |
| 35 | 35 | |
| 36 | 36 | VisibleMod = "pub" | "export" |
| 37 | 37 |
src/all_types.hpp+2-1| ... | ... | @@ -427,7 +427,7 @@ struct AstNodeFieldAccessExpr { |
| 427 | 427 | |
| 428 | 428 | struct AstNodeDirective { |
| 429 | 429 | Buf name; |
| 430 | Buf param; | |
| 430 | AstNode *expr; | |
| 431 | 431 | }; |
| 432 | 432 | |
| 433 | 433 | struct AstNodeRootExportDecl { |
| ... | ... | @@ -526,6 +526,7 @@ struct AstNodeSwitchExpr { |
| 526 | 526 | |
| 527 | 527 | // populated by semantic analyzer |
| 528 | 528 | Expr resolved_expr; |
| 529 | int const_chosen_prong_index; | |
| 529 | 530 | }; |
| 530 | 531 | |
| 531 | 532 | struct AstNodeSwitchProng { |
src/analyze.cpp+110-56| ... | ... | @@ -816,6 +816,53 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 816 | 816 | return get_fn_type(g, &fn_type_id); |
| 817 | 817 | } |
| 818 | 818 | |
| 819 | static Buf *resolve_const_expr_str(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node) { | |
| 820 | TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); | |
| 821 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *node); | |
| 822 | ||
| 823 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 824 | return nullptr; | |
| 825 | } | |
| 826 | ||
| 827 | ConstExprValue *const_str_val = &get_resolved_expr(*node)->const_val; | |
| 828 | ||
| 829 | if (!const_str_val->ok) { | |
| 830 | add_node_error(g, *node, buf_sprintf("unable to resolve constant expression")); | |
| 831 | return nullptr; | |
| 832 | } | |
| 833 | ||
| 834 | ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; | |
| 835 | uint64_t len = ptr_field->data.x_ptr.len; | |
| 836 | Buf *result = buf_alloc(); | |
| 837 | for (uint64_t i = 0; i < len; i += 1) { | |
| 838 | ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i]; | |
| 839 | uint64_t big_c = char_val->data.x_bignum.data.x_uint; | |
| 840 | assert(big_c <= UINT8_MAX); | |
| 841 | uint8_t c = big_c; | |
| 842 | buf_append_char(result, c); | |
| 843 | } | |
| 844 | return result; | |
| 845 | } | |
| 846 | ||
| 847 | static bool resolve_const_expr_bool(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 848 | AstNode **node, bool *value) | |
| 849 | { | |
| 850 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *node); | |
| 851 | ||
| 852 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 853 | return false; | |
| 854 | } | |
| 855 | ||
| 856 | ConstExprValue *const_bool_val = &get_resolved_expr(*node)->const_val; | |
| 857 | ||
| 858 | if (!const_bool_val->ok) { | |
| 859 | add_node_error(g, *node, buf_sprintf("unable to resolve constant expression")); | |
| 860 | return false; | |
| 861 | } | |
| 862 | ||
| 863 | *value = const_bool_val->data.x_bool; | |
| 864 | return true; | |
| 865 | } | |
| 819 | 866 | |
| 820 | 867 | static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, |
| 821 | 868 | ImportTableEntry *import) |
| ... | ... | @@ -839,23 +886,38 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 839 | 886 | Buf *name = &directive_node->data.directive.name; |
| 840 | 887 | |
| 841 | 888 | if (buf_eql_str(name, "attribute")) { |
| 842 | Buf *attr_name = &directive_node->data.directive.param; | |
| 843 | 889 | if (fn_table_entry->fn_def_node) { |
| 844 | if (buf_eql_str(attr_name, "naked")) { | |
| 845 | is_naked = true; | |
| 846 | } else if (buf_eql_str(attr_name, "cold")) { | |
| 847 | is_cold = true; | |
| 848 | } else if (buf_eql_str(attr_name, "test")) { | |
| 849 | is_test = true; | |
| 850 | g->test_fn_count += 1; | |
| 851 | } else { | |
| 852 | add_node_error(g, directive_node, | |
| 853 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 890 | Buf *attr_name = resolve_const_expr_str(g, import, import->block_context, | |
| 891 | &directive_node->data.directive.expr); | |
| 892 | if (attr_name) { | |
| 893 | if (buf_eql_str(attr_name, "naked")) { | |
| 894 | is_naked = true; | |
| 895 | } else if (buf_eql_str(attr_name, "cold")) { | |
| 896 | is_cold = true; | |
| 897 | } else if (buf_eql_str(attr_name, "test")) { | |
| 898 | is_test = true; | |
| 899 | g->test_fn_count += 1; | |
| 900 | } else { | |
| 901 | add_node_error(g, directive_node, | |
| 902 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 903 | } | |
| 854 | 904 | } |
| 855 | 905 | } else { |
| 856 | 906 | add_node_error(g, directive_node, |
| 857 | 907 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); |
| 858 | 908 | } |
| 909 | } else if (buf_eql_str(name, "condition")) { | |
| 910 | if (fn_proto->visib_mod == VisibModExport) { | |
| 911 | bool include; | |
| 912 | bool ok = resolve_const_expr_bool(g, import, import->block_context, | |
| 913 | &directive_node->data.directive.expr, &include); | |
| 914 | if (ok && !include) { | |
| 915 | fn_proto->visib_mod = VisibModPub; | |
| 916 | } | |
| 917 | } else { | |
| 918 | add_node_error(g, directive_node, | |
| 919 | buf_sprintf("#condition valid only on exported symbols")); | |
| 920 | } | |
| 859 | 921 | } else { |
| 860 | 922 | add_node_error(g, directive_node, |
| 861 | 923 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); |
| ... | ... | @@ -863,6 +925,15 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 863 | 925 | } |
| 864 | 926 | } |
| 865 | 927 | |
| 928 | bool is_internal = (fn_proto->visib_mod != VisibModExport); | |
| 929 | bool is_c_compat = !is_internal || fn_proto->is_extern; | |
| 930 | fn_table_entry->internal_linkage = !is_c_compat; | |
| 931 | if (!is_internal) { | |
| 932 | fn_table_entry->ref_count += 1; | |
| 933 | } | |
| 934 | ||
| 935 | ||
| 936 | ||
| 866 | 937 | TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, import->block_context, nullptr, node, |
| 867 | 938 | is_naked, is_cold); |
| 868 | 939 | |
| ... | ... | @@ -1242,8 +1313,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1242 | 1313 | |
| 1243 | 1314 | auto entry = fn_table->maybe_get(proto_name); |
| 1244 | 1315 | bool skip = false; |
| 1245 | bool is_internal = (proto_node->data.fn_proto.visib_mod != VisibModExport); | |
| 1246 | bool is_c_compat = !is_internal || is_extern; | |
| 1247 | 1316 | bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate); |
| 1248 | 1317 | if (entry) { |
| 1249 | 1318 | add_node_error(g, proto_node, |
| ... | ... | @@ -1263,10 +1332,8 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1263 | 1332 | fn_table_entry->import_entry = import; |
| 1264 | 1333 | fn_table_entry->proto_node = proto_node; |
| 1265 | 1334 | fn_table_entry->fn_def_node = fn_def_node; |
| 1266 | fn_table_entry->internal_linkage = !is_c_compat; | |
| 1267 | 1335 | fn_table_entry->is_extern = is_extern; |
| 1268 | 1336 | fn_table_entry->member_of_struct = struct_type; |
| 1269 | fn_table_entry->ref_count = (proto_node->data.fn_proto.visib_mod == VisibModExport) ? 1 : 0; | |
| 1270 | 1337 | |
| 1271 | 1338 | if (struct_type) { |
| 1272 | 1339 | buf_resize(&fn_table_entry->symbol_name, 0); |
| ... | ... | @@ -1290,7 +1357,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1290 | 1357 | g->main_fn = fn_table_entry; |
| 1291 | 1358 | |
| 1292 | 1359 | if (g->bootstrap_import && !g->is_test_build) { |
| 1293 | g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); | |
| 1360 | g->bootstrap_import->fn_table.put(buf_create_from_str("zig_user_main"), fn_table_entry); | |
| 1294 | 1361 | } |
| 1295 | 1362 | } |
| 1296 | 1363 | bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main"); |
| ... | ... | @@ -4246,54 +4313,33 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4246 | 4313 | { |
| 4247 | 4314 | AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field; |
| 4248 | 4315 | |
| 4249 | TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); | |
| 4250 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *str_node); | |
| 4251 | ||
| 4252 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 4253 | return resolved_type; | |
| 4254 | } | |
| 4255 | ||
| 4256 | ConstExprValue *const_str_val = &get_resolved_expr(*str_node)->const_val; | |
| 4257 | ||
| 4258 | if (!const_str_val->ok) { | |
| 4259 | add_node_error(g, *str_node, buf_sprintf("@compile_var requires constant expression")); | |
| 4260 | return g->builtin_types.entry_void; | |
| 4261 | } | |
| 4262 | ||
| 4263 | ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; | |
| 4264 | uint64_t len = ptr_field->data.x_ptr.len; | |
| 4265 | Buf var_name = BUF_INIT; | |
| 4266 | buf_resize(&var_name, 0); | |
| 4267 | for (uint64_t i = 0; i < len; i += 1) { | |
| 4268 | ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i]; | |
| 4269 | uint64_t big_c = char_val->data.x_bignum.data.x_uint; | |
| 4270 | assert(big_c <= UINT8_MAX); | |
| 4271 | uint8_t c = big_c; | |
| 4272 | buf_append_char(&var_name, c); | |
| 4316 | Buf *var_name = resolve_const_expr_str(g, import, context, str_node); | |
| 4317 | if (!var_name) { | |
| 4318 | return g->builtin_types.entry_invalid; | |
| 4273 | 4319 | } |
| 4274 | 4320 | |
| 4275 | 4321 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; |
| 4276 | 4322 | const_val->ok = true; |
| 4277 | 4323 | const_val->depends_on_compile_var = true; |
| 4278 | 4324 | |
| 4279 | if (buf_eql_str(&var_name, "is_big_endian")) { | |
| 4325 | if (buf_eql_str(var_name, "is_big_endian")) { | |
| 4280 | 4326 | return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true); |
| 4281 | } else if (buf_eql_str(&var_name, "is_release")) { | |
| 4327 | } else if (buf_eql_str(var_name, "is_release")) { | |
| 4282 | 4328 | return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true); |
| 4283 | } else if (buf_eql_str(&var_name, "is_test")) { | |
| 4329 | } else if (buf_eql_str(var_name, "is_test")) { | |
| 4284 | 4330 | return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); |
| 4285 | } else if (buf_eql_str(&var_name, "os")) { | |
| 4331 | } else if (buf_eql_str(var_name, "os")) { | |
| 4286 | 4332 | const_val->data.x_enum.tag = g->target_os_index; |
| 4287 | 4333 | return g->builtin_types.entry_os_enum; |
| 4288 | } else if (buf_eql_str(&var_name, "arch")) { | |
| 4334 | } else if (buf_eql_str(var_name, "arch")) { | |
| 4289 | 4335 | const_val->data.x_enum.tag = g->target_arch_index; |
| 4290 | 4336 | return g->builtin_types.entry_arch_enum; |
| 4291 | } else if (buf_eql_str(&var_name, "environ")) { | |
| 4337 | } else if (buf_eql_str(var_name, "environ")) { | |
| 4292 | 4338 | const_val->data.x_enum.tag = g->target_environ_index; |
| 4293 | 4339 | return g->builtin_types.entry_environ_enum; |
| 4294 | 4340 | } else { |
| 4295 | 4341 | add_node_error(g, *str_node, |
| 4296 | buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); | |
| 4342 | buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name))); | |
| 4297 | 4343 | return g->builtin_types.entry_invalid; |
| 4298 | 4344 | } |
| 4299 | 4345 | } |
| ... | ... | @@ -4740,7 +4786,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4740 | 4786 | field_use_counts = allocate<int>(expr_type->data.enumeration.field_count); |
| 4741 | 4787 | } |
| 4742 | 4788 | |
| 4743 | int const_chosen_prong_index = -1; | |
| 4789 | int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; | |
| 4790 | *const_chosen_prong_index = -1; | |
| 4744 | 4791 | AstNode *else_prong = nullptr; |
| 4745 | 4792 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| 4746 | 4793 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); |
| ... | ... | @@ -4756,8 +4803,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4756 | 4803 | } |
| 4757 | 4804 | var_type = expr_type; |
| 4758 | 4805 | var_is_target_expr = true; |
| 4759 | if (const_chosen_prong_index == -1) { | |
| 4760 | const_chosen_prong_index = prong_i; | |
| 4806 | if (*const_chosen_prong_index == -1 && expr_val->ok) { | |
| 4807 | *const_chosen_prong_index = prong_i; | |
| 4761 | 4808 | } |
| 4762 | 4809 | } else { |
| 4763 | 4810 | bool all_agree_on_var_type = true; |
| ... | ... | @@ -4792,7 +4839,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4792 | 4839 | } |
| 4793 | 4840 | if (!any_errors && expr_val->ok) { |
| 4794 | 4841 | if (expr_val->data.x_enum.tag == type_enum_field->value) { |
| 4795 | const_chosen_prong_index = prong_i; | |
| 4842 | *const_chosen_prong_index = prong_i; | |
| 4796 | 4843 | } |
| 4797 | 4844 | } |
| 4798 | 4845 | } else { |
| ... | ... | @@ -4844,7 +4891,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4844 | 4891 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| 4845 | 4892 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); |
| 4846 | 4893 | BlockContext *child_context = prong_node->data.switch_prong.block_context; |
| 4847 | child_context->codegen_excluded = expr_val->ok && (const_chosen_prong_index != prong_i); | |
| 4894 | child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i); | |
| 4848 | 4895 | |
| 4849 | 4896 | peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type, |
| 4850 | 4897 | prong_node->data.switch_prong.expr); |
| ... | ... | @@ -4872,10 +4919,9 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4872 | 4919 | } |
| 4873 | 4920 | |
| 4874 | 4921 | if (expr_val->ok) { |
| 4875 | assert(const_chosen_prong_index != -1); | |
| 4922 | assert(*const_chosen_prong_index != -1); | |
| 4876 | 4923 | |
| 4877 | *const_val = get_resolved_expr(peer_nodes[const_chosen_prong_index])->const_val; | |
| 4878 | const_val->ok = true; | |
| 4924 | *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val; | |
| 4879 | 4925 | // the target expr depends on a compile var, |
| 4880 | 4926 | // so the entire if statement does too |
| 4881 | 4927 | const_val->depends_on_compile_var = true; |
| ... | ... | @@ -5490,6 +5536,12 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 5490 | 5536 | AstNode *param = node->data.fn_proto.params.at(i); |
| 5491 | 5537 | collect_expr_decl_deps(g, import, param, decl_node); |
| 5492 | 5538 | } |
| 5539 | if (node->data.fn_proto.directives) { | |
| 5540 | for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) { | |
| 5541 | AstNode *directive = node->data.fn_proto.directives->at(i); | |
| 5542 | collect_expr_decl_deps(g, import, directive, decl_node); | |
| 5543 | } | |
| 5544 | } | |
| 5493 | 5545 | collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); |
| 5494 | 5546 | break; |
| 5495 | 5547 | case NodeTypeParamDecl: |
| ... | ... | @@ -5498,12 +5550,14 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 5498 | 5550 | case NodeTypeTypeDecl: |
| 5499 | 5551 | collect_expr_decl_deps(g, import, node->data.type_decl.child_type, decl_node); |
| 5500 | 5552 | break; |
| 5553 | case NodeTypeDirective: | |
| 5554 | collect_expr_decl_deps(g, import, node->data.directive.expr, decl_node); | |
| 5555 | break; | |
| 5501 | 5556 | case NodeTypeVariableDeclaration: |
| 5502 | 5557 | case NodeTypeRootExportDecl: |
| 5503 | 5558 | case NodeTypeFnDef: |
| 5504 | 5559 | case NodeTypeRoot: |
| 5505 | 5560 | case NodeTypeFnDecl: |
| 5506 | case NodeTypeDirective: | |
| 5507 | 5561 | case NodeTypeImport: |
| 5508 | 5562 | case NodeTypeCImport: |
| 5509 | 5563 | case NodeTypeLabel: |
src/ast_render.cpp+4-2| ... | ... | @@ -339,6 +339,7 @@ void ast_print(FILE *f, AstNode *node, int indent) { |
| 339 | 339 | break; |
| 340 | 340 | case NodeTypeDirective: |
| 341 | 341 | fprintf(f, "%s\n", node_type_str(node->type)); |
| 342 | ast_print(f, node->data.directive.expr, indent + 2); | |
| 342 | 343 | break; |
| 343 | 344 | case NodeTypePrefixOpExpr: |
| 344 | 345 | fprintf(f, "%s %s\n", node_type_str(node->type), |
| ... | ... | @@ -631,8 +632,9 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 631 | 632 | fprintf(ar->f, "}"); |
| 632 | 633 | break; |
| 633 | 634 | case NodeTypeDirective: |
| 634 | fprintf(ar->f, "#%s(\"%s\")\n", buf_ptr(&node->data.directive.name), | |
| 635 | buf_ptr(&node->data.directive.param)); | |
| 635 | fprintf(ar->f, "#%s(", buf_ptr(&node->data.directive.name)); | |
| 636 | render_node(ar, node->data.directive.expr); | |
| 637 | fprintf(ar->f, ")\n"); | |
| 636 | 638 | break; |
| 637 | 639 | case NodeTypeReturnExpr: |
| 638 | 640 | zig_panic("TODO"); |
src/codegen.cpp+24-11| ... | ... | @@ -2150,7 +2150,8 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { |
| 2150 | 2150 | if (!g->is_release_build) { |
| 2151 | 2151 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 2152 | 2152 | } |
| 2153 | return LLVMBuildUnreachable(g->builder); | |
| 2153 | LLVMBuildUnreachable(g->builder); | |
| 2154 | return nullptr; | |
| 2154 | 2155 | } else if (type_entry->id == TypeTableEntryIdVoid) { |
| 2155 | 2156 | assert(node->data.container_init_expr.entries.length == 0); |
| 2156 | 2157 | return nullptr; |
| ... | ... | @@ -2487,6 +2488,13 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { |
| 2487 | 2488 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2488 | 2489 | assert(node->type == NodeTypeSwitchExpr); |
| 2489 | 2490 | |
| 2491 | if (node->data.switch_expr.const_chosen_prong_index >= 0) { | |
| 2492 | AstNode *prong_node = node->data.switch_expr.prongs.at(node->data.switch_expr.const_chosen_prong_index); | |
| 2493 | assert(prong_node->type == NodeTypeSwitchProng); | |
| 2494 | AstNode *prong_expr = prong_node->data.switch_prong.expr; | |
| 2495 | return gen_expr(g, prong_expr); | |
| 2496 | } | |
| 2497 | ||
| 2490 | 2498 | TypeTableEntry *target_type = get_expr_type(node->data.switch_expr.expr); |
| 2491 | 2499 | LLVMValueRef target_value_handle = gen_expr(g, node->data.switch_expr.expr); |
| 2492 | 2500 | LLVMValueRef target_value; |
| ... | ... | @@ -3877,18 +3885,23 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, |
| 3877 | 3885 | for (int i = 0; i < directives->length; i += 1) { |
| 3878 | 3886 | AstNode *directive_node = directives->at(i); |
| 3879 | 3887 | Buf *name = &directive_node->data.directive.name; |
| 3880 | Buf *param = &directive_node->data.directive.param; | |
| 3881 | if (buf_eql_str(name, "version")) { | |
| 3882 | set_root_export_version(g, param, directive_node); | |
| 3883 | } else if (buf_eql_str(name, "link")) { | |
| 3884 | if (buf_eql_str(param, "c")) { | |
| 3885 | g->link_libc = true; | |
| 3888 | AstNode *param_node = directive_node->data.directive.expr; | |
| 3889 | assert(param_node->type == NodeTypeStringLiteral); | |
| 3890 | Buf *param = &param_node->data.string_literal.buf; | |
| 3891 | ||
| 3892 | if (param) { | |
| 3893 | if (buf_eql_str(name, "version")) { | |
| 3894 | set_root_export_version(g, param, directive_node); | |
| 3895 | } else if (buf_eql_str(name, "link")) { | |
| 3896 | if (buf_eql_str(param, "c")) { | |
| 3897 | g->link_libc = true; | |
| 3898 | } else { | |
| 3899 | g->link_libs.append(param); | |
| 3900 | } | |
| 3886 | 3901 | } else { |
| 3887 | g->link_libs.append(param); | |
| 3902 | add_node_error(g, directive_node, | |
| 3903 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 3888 | 3904 | } |
| 3889 | } else { | |
| 3890 | add_node_error(g, directive_node, | |
| 3891 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 3892 | 3905 | } |
| 3893 | 3906 | } |
| 3894 | 3907 | } |
src/link.cpp+39-44| ... | ... | @@ -580,35 +580,33 @@ static void construct_linker_job_darwin(LinkJob *lj) { |
| 580 | 580 | lj->args.append("-o"); |
| 581 | 581 | lj->args.append(buf_ptr(&lj->out_file)); |
| 582 | 582 | |
| 583 | if (lj->link_in_crt) { | |
| 584 | if (shared) { | |
| 585 | zig_panic("TODO"); | |
| 586 | } else if (g->is_static) { | |
| 587 | lj->args.append("-lcrt0.o"); | |
| 588 | } else { | |
| 589 | switch (platform.kind) { | |
| 590 | case MacOS: | |
| 591 | if (darwin_version_lt(&platform, 10, 5)) { | |
| 592 | lj->args.append("-lcrt1.o"); | |
| 593 | } else if (darwin_version_lt(&platform, 10, 6)) { | |
| 594 | lj->args.append("-lcrt1.10.5.o"); | |
| 595 | } else if (darwin_version_lt(&platform, 10, 8)) { | |
| 596 | lj->args.append("-lcrt1.10.6.o"); | |
| 597 | } | |
| 598 | break; | |
| 599 | case IPhoneOS: | |
| 600 | if (g->zig_target.arch.arch == ZigLLVM_aarch64) { | |
| 601 | // iOS does not need any crt1 files for arm64 | |
| 602 | } else if (darwin_version_lt(&platform, 3, 1)) { | |
| 603 | lj->args.append("-lcrt1.o"); | |
| 604 | } else if (darwin_version_lt(&platform, 6, 0)) { | |
| 605 | lj->args.append("-lcrt1.3.1.o"); | |
| 606 | } | |
| 607 | break; | |
| 608 | case IPhoneOSSimulator: | |
| 609 | // no crt1.o needed | |
| 610 | break; | |
| 611 | } | |
| 583 | if (shared) { | |
| 584 | zig_panic("TODO"); | |
| 585 | } else if (g->is_static) { | |
| 586 | lj->args.append("-lcrt0.o"); | |
| 587 | } else { | |
| 588 | switch (platform.kind) { | |
| 589 | case MacOS: | |
| 590 | if (darwin_version_lt(&platform, 10, 5)) { | |
| 591 | lj->args.append("-lcrt1.o"); | |
| 592 | } else if (darwin_version_lt(&platform, 10, 6)) { | |
| 593 | lj->args.append("-lcrt1.10.5.o"); | |
| 594 | } else if (darwin_version_lt(&platform, 10, 8)) { | |
| 595 | lj->args.append("-lcrt1.10.6.o"); | |
| 596 | } | |
| 597 | break; | |
| 598 | case IPhoneOS: | |
| 599 | if (g->zig_target.arch.arch == ZigLLVM_aarch64) { | |
| 600 | // iOS does not need any crt1 files for arm64 | |
| 601 | } else if (darwin_version_lt(&platform, 3, 1)) { | |
| 602 | lj->args.append("-lcrt1.o"); | |
| 603 | } else if (darwin_version_lt(&platform, 6, 0)) { | |
| 604 | lj->args.append("-lcrt1.3.1.o"); | |
| 605 | } | |
| 606 | break; | |
| 607 | case IPhoneOSSimulator: | |
| 608 | // no crt1.o needed | |
| 609 | break; | |
| 612 | 610 | } |
| 613 | 611 | } |
| 614 | 612 | |
| ... | ... | @@ -620,29 +618,26 @@ static void construct_linker_job_darwin(LinkJob *lj) { |
| 620 | 618 | |
| 621 | 619 | lj->args.append((const char *)buf_ptr(&lj->out_file_o)); |
| 622 | 620 | |
| 623 | if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { | |
| 624 | Buf *builtin_o_path = build_o(g, "builtin"); | |
| 625 | lj->args.append(buf_ptr(builtin_o_path)); | |
| 626 | } | |
| 627 | ||
| 628 | 621 | for (int i = 0; i < g->link_libs.length; i += 1) { |
| 629 | 622 | Buf *link_lib = g->link_libs.at(i); |
| 630 | 623 | Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib)); |
| 631 | 624 | lj->args.append(buf_ptr(arg)); |
| 632 | 625 | } |
| 633 | 626 | |
| 634 | if (g->link_libc) { | |
| 635 | lj->args.append("-lSystem"); | |
| 627 | // on Darwin, libSystem has libc in it, but also you have to use it | |
| 628 | // to make syscalls because the syscall numbers are not documented | |
| 629 | // and change between versions. | |
| 630 | // so we always link against libSystem | |
| 631 | lj->args.append("-lSystem"); | |
| 636 | 632 | |
| 637 | if (platform.kind == MacOS) { | |
| 638 | if (darwin_version_lt(&platform, 10, 5)) { | |
| 639 | lj->args.append("-lgcc_s.10.4"); | |
| 640 | } else if (darwin_version_lt(&platform, 10, 6)) { | |
| 641 | lj->args.append("-lgcc_s.10.5"); | |
| 642 | } | |
| 643 | } else { | |
| 644 | zig_panic("TODO"); | |
| 633 | if (platform.kind == MacOS) { | |
| 634 | if (darwin_version_lt(&platform, 10, 5)) { | |
| 635 | lj->args.append("-lgcc_s.10.4"); | |
| 636 | } else if (darwin_version_lt(&platform, 10, 6)) { | |
| 637 | lj->args.append("-lgcc_s.10.5"); | |
| 645 | 638 | } |
| 639 | } else { | |
| 640 | zig_panic("TODO"); | |
| 646 | 641 | } |
| 647 | 642 | |
| 648 | 643 | } |
src/parser.cpp+8-21| ... | ... | @@ -499,6 +499,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo |
| 499 | 499 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, |
| 500 | 500 | ZigList<AstNode*> *directives, VisibMod visib_mod); |
| 501 | 501 | static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index); |
| 502 | static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool mandatory); | |
| 502 | 503 | |
| 503 | 504 | static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { |
| 504 | 505 | if (token->id == token_id) { |
| ... | ... | @@ -517,33 +518,19 @@ static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id |
| 517 | 518 | return token; |
| 518 | 519 | } |
| 519 | 520 | |
| 520 | ||
| 521 | /* | |
| 522 | Directive = "#" "Symbol" "(" Expression ")" | |
| 523 | */ | |
| 521 | 524 | static AstNode *ast_parse_directive(ParseContext *pc, int *token_index) { |
| 522 | Token *number_sign = &pc->tokens->at(*token_index); | |
| 523 | *token_index += 1; | |
| 524 | ast_expect_token(pc, number_sign, TokenIdNumberSign); | |
| 525 | Token *number_sign = ast_eat_token(pc, token_index, TokenIdNumberSign); | |
| 525 | 526 | |
| 526 | 527 | AstNode *node = ast_create_node(pc, NodeTypeDirective, number_sign); |
| 527 | 528 | |
| 528 | Token *name_symbol = &pc->tokens->at(*token_index); | |
| 529 | *token_index += 1; | |
| 530 | ast_expect_token(pc, name_symbol, TokenIdSymbol); | |
| 529 | Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); | |
| 531 | 530 | |
| 532 | 531 | ast_buf_from_token(pc, name_symbol, &node->data.directive.name); |
| 533 | 532 | |
| 534 | Token *l_paren = &pc->tokens->at(*token_index); | |
| 535 | *token_index += 1; | |
| 536 | ast_expect_token(pc, l_paren, TokenIdLParen); | |
| 537 | ||
| 538 | Token *param_str = &pc->tokens->at(*token_index); | |
| 539 | *token_index += 1; | |
| 540 | ast_expect_token(pc, param_str, TokenIdStringLiteral); | |
| 541 | ||
| 542 | parse_string_literal(pc, param_str, &node->data.directive.param, nullptr, nullptr); | |
| 543 | ||
| 544 | Token *r_paren = &pc->tokens->at(*token_index); | |
| 545 | *token_index += 1; | |
| 546 | ast_expect_token(pc, r_paren, TokenIdRParen); | |
| 533 | node->data.directive.expr = ast_parse_grouped_expr(pc, token_index, true); | |
| 547 | 534 | |
| 548 | 535 | normalize_parent_ptrs(node); |
| 549 | 536 | return node; |
| ... | ... | @@ -2741,7 +2728,7 @@ void normalize_parent_ptrs(AstNode *node) { |
| 2741 | 2728 | set_list_fields(&node->data.block.statements); |
| 2742 | 2729 | break; |
| 2743 | 2730 | case NodeTypeDirective: |
| 2744 | // none | |
| 2731 | set_field(&node->data.directive.expr); | |
| 2745 | 2732 | break; |
| 2746 | 2733 | case NodeTypeReturnExpr: |
| 2747 | 2734 | set_field(&node->data.return_expr.expr); |
std/bootstrap.zig+20-5| ... | ... | @@ -1,24 +1,28 @@ |
| 1 | 1 | import "syscall.zig"; |
| 2 | 2 | |
| 3 | 3 | // The compiler treats this file special by implicitly importing the function `main` |
| 4 | // from the root source file. | |
| 4 | // from the root source file as the symbol `zig_user_main`. | |
| 5 | ||
| 6 | const want_start_symbol = switch(@compile_var("os")) { | |
| 7 | linux => true, | |
| 8 | else => false, | |
| 9 | }; | |
| 10 | const want_main_symbol = !want_start_symbol; | |
| 5 | 11 | |
| 6 | 12 | var argc: isize = undefined; |
| 7 | 13 | var argv: &&u8 = undefined; |
| 8 | var env: &&u8 = undefined; | |
| 9 | 14 | |
| 10 | 15 | #attribute("naked") |
| 16 | #condition(want_start_symbol) | |
| 11 | 17 | export fn _start() -> unreachable { |
| 12 | 18 | switch (@compile_var("arch")) { |
| 13 | 19 | x86_64 => { |
| 14 | 20 | argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> isize)); |
| 15 | 21 | argv = asm("lea 0x8(%%rsp), %[argv]": [argv] "=r" (-> &&u8)); |
| 16 | env = asm("lea 0x10(%%rsp,[argc],8), %[env]": [env] "=r" (-> &&u8): [argc] "r" (argc)); | |
| 17 | 22 | }, |
| 18 | 23 | i386 => { |
| 19 | 24 | argc = asm("mov (%%esp), %[argc]": [argc] "=r" (-> isize)); |
| 20 | 25 | argv = asm("lea 0x4(%%esp), %[argv]": [argv] "=r" (-> &&u8)); |
| 21 | env = asm("lea 0x8(%%esp,%[argc],4), %[env]": [env] "=r" (-> &&u8): [argc] "r" (argc)); | |
| 22 | 26 | }, |
| 23 | 27 | else => unreachable{}, |
| 24 | 28 | } |
| ... | ... | @@ -39,6 +43,17 @@ fn call_main() -> unreachable { |
| 39 | 43 | const ptr = argv[i]; |
| 40 | 44 | args[i] = ptr[0...strlen(ptr)]; |
| 41 | 45 | } |
| 42 | main(args) %% exit(1); | |
| 46 | zig_user_main(args) %% exit(1); | |
| 43 | 47 | exit(0); |
| 44 | 48 | } |
| 49 | ||
| 50 | #condition(want_main_symbol) | |
| 51 | export fn main(argc: i32, argv: &&u8) -> i32 { | |
| 52 | var args: [argc][]u8 = undefined; | |
| 53 | for (args) |arg, i| { | |
| 54 | const ptr = argv[i]; | |
| 55 | args[i] = ptr[0...strlen(ptr)]; | |
| 56 | } | |
| 57 | zig_user_main(args) %% return 1; | |
| 58 | return 0; | |
| 59 | } |