| author | |
| committer | |
| log | 11a06443659154ec53e4deb00956b2f025cd71ca |
| tree | fc02b195cf8850f0f1743027055461aca8b4e57f |
| parent | afdb47c32d00db3777352fb745b277b5d0805c69 |
9 files changed, 249 insertions(+), 58 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -122,6 +122,7 @@ set(C_HEADERS |
| 122 | 122 | set(ZIG_STD_SRC |
| 123 | 123 | "${CMAKE_SOURCE_DIR}/std/bootstrap.zig" |
| 124 | 124 | "${CMAKE_SOURCE_DIR}/std/builtin.zig" |
| 125 | "${CMAKE_SOURCE_DIR}/std/test_runner.zig" | |
| 125 | 126 | "${CMAKE_SOURCE_DIR}/std/std.zig" |
| 126 | 127 | "${CMAKE_SOURCE_DIR}/std/syscall.zig" |
| 127 | 128 | "${CMAKE_SOURCE_DIR}/std/errno.zig" |
src/all_types.hpp+8-7| ... | ... | @@ -36,11 +36,6 @@ enum OutType { |
| 36 | 36 | OutTypeObj, |
| 37 | 37 | }; |
| 38 | 38 | |
| 39 | enum CodeGenBuildType { | |
| 40 | CodeGenBuildTypeDebug, | |
| 41 | CodeGenBuildTypeRelease, | |
| 42 | }; | |
| 43 | ||
| 44 | 39 | struct ConstEnumValue { |
| 45 | 40 | uint64_t tag; |
| 46 | 41 | ConstExprValue *payload; |
| ... | ... | @@ -983,6 +978,7 @@ struct FnTableEntry { |
| 983 | 978 | bool is_inline; |
| 984 | 979 | bool internal_linkage; |
| 985 | 980 | bool is_extern; |
| 981 | bool is_test; | |
| 986 | 982 | uint32_t ref_count; // if this is 0 we don't have to codegen it |
| 987 | 983 | |
| 988 | 984 | // reminder: hash tables must be initialized before use |
| ... | ... | @@ -1073,7 +1069,8 @@ struct CodeGen { |
| 1073 | 1069 | bool link_libc; |
| 1074 | 1070 | Buf *libc_lib_dir; |
| 1075 | 1071 | Buf *libc_include_dir; |
| 1076 | CodeGenBuildType build_type; | |
| 1072 | bool is_release_build; | |
| 1073 | bool is_test_build; | |
| 1077 | 1074 | LLVMTargetMachineRef target_machine; |
| 1078 | 1075 | LLVMZigDIFile *dummy_di_file; |
| 1079 | 1076 | bool is_native_target; |
| ... | ... | @@ -1087,10 +1084,11 @@ struct CodeGen { |
| 1087 | 1084 | // there will not be a corresponding fn_defs entry. |
| 1088 | 1085 | ZigList<FnTableEntry *> fn_protos; |
| 1089 | 1086 | ZigList<VariableTableEntry *> global_vars; |
| 1090 | ZigList<Expr *> global_const_list; | |
| 1087 | ZigList<AstNode *> global_const_list; | |
| 1091 | 1088 | |
| 1092 | 1089 | OutType out_type; |
| 1093 | 1090 | FnTableEntry *cur_fn; |
| 1091 | FnTableEntry *main_fn; | |
| 1094 | 1092 | LLVMValueRef cur_ret_ptr; |
| 1095 | 1093 | ZigList<LLVMBasicBlockRef> break_block_stack; |
| 1096 | 1094 | ZigList<LLVMBasicBlockRef> continue_block_stack; |
| ... | ... | @@ -1103,6 +1101,7 @@ struct CodeGen { |
| 1103 | 1101 | ErrColor err_color; |
| 1104 | 1102 | ImportTableEntry *root_import; |
| 1105 | 1103 | ImportTableEntry *bootstrap_import; |
| 1104 | ImportTableEntry *test_runner_import; | |
| 1106 | 1105 | LLVMValueRef memcpy_fn_val; |
| 1107 | 1106 | LLVMValueRef memset_fn_val; |
| 1108 | 1107 | LLVMValueRef trap_fn_val; |
| ... | ... | @@ -1116,6 +1115,8 @@ struct CodeGen { |
| 1116 | 1115 | const char **clang_argv; |
| 1117 | 1116 | int clang_argv_len; |
| 1118 | 1117 | ZigList<const char *> lib_dirs; |
| 1118 | ||
| 1119 | uint32_t test_fn_count; | |
| 1119 | 1120 | }; |
| 1120 | 1121 | |
| 1121 | 1122 | struct VariableTableEntry { |
src/analyze.cpp+26-10| ... | ... | @@ -781,6 +781,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 781 | 781 | |
| 782 | 782 | bool is_cold = false; |
| 783 | 783 | bool is_naked = false; |
| 784 | bool is_test = false; | |
| 784 | 785 | |
| 785 | 786 | if (fn_proto->directives) { |
| 786 | 787 | for (int i = 0; i < fn_proto->directives->length; i += 1) { |
| ... | ... | @@ -794,6 +795,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 794 | 795 | is_naked = true; |
| 795 | 796 | } else if (buf_eql_str(attr_name, "cold")) { |
| 796 | 797 | is_cold = true; |
| 798 | } else if (buf_eql_str(attr_name, "test")) { | |
| 799 | is_test = true; | |
| 800 | g->test_fn_count += 1; | |
| 797 | 801 | } else { |
| 798 | 802 | add_node_error(g, directive_node, |
| 799 | 803 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); |
| ... | ... | @@ -813,6 +817,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 813 | 817 | is_naked, is_cold); |
| 814 | 818 | |
| 815 | 819 | fn_table_entry->type_entry = fn_type; |
| 820 | fn_table_entry->is_test = is_test; | |
| 816 | 821 | |
| 817 | 822 | if (fn_type->id == TypeTableEntryIdInvalid) { |
| 818 | 823 | fn_proto->skip = true; |
| ... | ... | @@ -846,7 +851,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 846 | 851 | unsigned scope_line = line_number; |
| 847 | 852 | bool is_definition = fn_table_entry->fn_def_node != nullptr; |
| 848 | 853 | unsigned flags = 0; |
| 849 | bool is_optimized = g->build_type == CodeGenBuildTypeRelease; | |
| 854 | bool is_optimized = g->is_release_build; | |
| 850 | 855 | LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder, |
| 851 | 856 | import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "", |
| 852 | 857 | import->di_file, line_number, |
| ... | ... | @@ -1247,10 +1252,18 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1247 | 1252 | |
| 1248 | 1253 | fn_table->put(proto_name, fn_table_entry); |
| 1249 | 1254 | |
| 1250 | if (!struct_type && | |
| 1251 | g->bootstrap_import && | |
| 1252 | import == g->root_import && buf_eql_str(proto_name, "main")) | |
| 1253 | { | |
| 1255 | bool is_main_fn = !struct_type && (import == g->root_import) && buf_eql_str(proto_name, "main"); | |
| 1256 | if (is_main_fn) { | |
| 1257 | g->main_fn = fn_table_entry; | |
| 1258 | ||
| 1259 | if (g->bootstrap_import && !g->is_test_build) { | |
| 1260 | g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); | |
| 1261 | } | |
| 1262 | } | |
| 1263 | bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main"); | |
| 1264 | if (is_test_main_fn) { | |
| 1265 | assert(g->bootstrap_import); | |
| 1266 | assert(g->is_test_build); | |
| 1254 | 1267 | g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); |
| 1255 | 1268 | } |
| 1256 | 1269 | |
| ... | ... | @@ -1551,13 +1564,14 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) { |
| 1551 | 1564 | } |
| 1552 | 1565 | } |
| 1553 | 1566 | |
| 1554 | static void add_global_const_expr(CodeGen *g, Expr *expr) { | |
| 1567 | static void add_global_const_expr(CodeGen *g, AstNode *expr_node) { | |
| 1568 | Expr *expr = get_resolved_expr(expr_node); | |
| 1555 | 1569 | if (expr->const_val.ok && |
| 1556 | 1570 | type_has_codegen_value(expr->type_entry) && |
| 1557 | 1571 | !expr->has_global_const && |
| 1558 | 1572 | type_has_bits(expr->type_entry)) |
| 1559 | 1573 | { |
| 1560 | g->global_const_list.append(expr); | |
| 1574 | g->global_const_list.append(expr_node); | |
| 1561 | 1575 | expr->has_global_const = true; |
| 1562 | 1576 | } |
| 1563 | 1577 | } |
| ... | ... | @@ -1924,7 +1938,7 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn |
| 1924 | 1938 | *child_node, expected_type, child_types[i]); |
| 1925 | 1939 | Expr *expr = get_resolved_expr(*child_node); |
| 1926 | 1940 | expr->type_entry = resolved_type; |
| 1927 | add_global_const_expr(g, expr); | |
| 1941 | add_global_const_expr(g, *child_node); | |
| 1928 | 1942 | } |
| 1929 | 1943 | |
| 1930 | 1944 | return expected_type; |
| ... | ... | @@ -4022,7 +4036,9 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4022 | 4036 | if (buf_eql_str(&var_name, "is_big_endian")) { |
| 4023 | 4037 | return resolve_expr_const_val_as_bool(g, node, g->is_big_endian); |
| 4024 | 4038 | } else if (buf_eql_str(&var_name, "is_release")) { |
| 4025 | return resolve_expr_const_val_as_bool(g, node, g->build_type == CodeGenBuildTypeRelease); | |
| 4039 | return resolve_expr_const_val_as_bool(g, node, g->is_release_build); | |
| 4040 | } else if (buf_eql_str(&var_name, "is_test")) { | |
| 4041 | return resolve_expr_const_val_as_bool(g, node, g->is_test_build); | |
| 4026 | 4042 | } else { |
| 4027 | 4043 | add_node_error(g, *str_node, |
| 4028 | 4044 | buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); |
| ... | ... | @@ -4752,7 +4768,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 4752 | 4768 | expr->type_entry = return_type; |
| 4753 | 4769 | node->block_context = context; |
| 4754 | 4770 | |
| 4755 | add_global_const_expr(g, expr); | |
| 4771 | add_global_const_expr(g, node); | |
| 4756 | 4772 | |
| 4757 | 4773 | return resolved_type; |
| 4758 | 4774 | } |
src/codegen.cpp+121-19| ... | ... | @@ -28,7 +28,8 @@ CodeGen *codegen_create(Buf *root_source_dir) { |
| 28 | 28 | g->primitive_type_table.init(32); |
| 29 | 29 | g->unresolved_top_level_decls.init(32); |
| 30 | 30 | g->fn_type_table.init(32); |
| 31 | g->build_type = CodeGenBuildTypeDebug; | |
| 31 | g->is_release_build = false; | |
| 32 | g->is_test_build = false; | |
| 32 | 33 | g->root_source_dir = root_source_dir; |
| 33 | 34 | g->next_error_index = 1; |
| 34 | 35 | g->error_value_count = 1; |
| ... | ... | @@ -44,8 +45,12 @@ void codegen_set_clang_argv(CodeGen *g, const char **args, int len) { |
| 44 | 45 | g->clang_argv_len = len; |
| 45 | 46 | } |
| 46 | 47 | |
| 47 | void codegen_set_build_type(CodeGen *g, CodeGenBuildType build_type) { | |
| 48 | g->build_type = build_type; | |
| 48 | void codegen_set_is_release(CodeGen *g, bool is_release_build) { | |
| 49 | g->is_release_build = is_release_build; | |
| 50 | } | |
| 51 | ||
| 52 | void codegen_set_is_test(CodeGen *g, bool is_test_build) { | |
| 53 | g->is_test_build = is_test_build; | |
| 49 | 54 | } |
| 50 | 55 | |
| 51 | 56 | void codegen_set_is_static(CodeGen *g, bool is_static) { |
| ... | ... | @@ -1003,7 +1008,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1003 | 1008 | assert(expr_type->id == TypeTableEntryIdErrorUnion); |
| 1004 | 1009 | TypeTableEntry *child_type = expr_type->data.error.child_type; |
| 1005 | 1010 | |
| 1006 | if (g->build_type != CodeGenBuildTypeRelease) { | |
| 1011 | if (!g->is_release_build) { | |
| 1007 | 1012 | LLVMValueRef err_val; |
| 1008 | 1013 | if (type_has_bits(child_type)) { |
| 1009 | 1014 | add_debug_source_node(g, node); |
| ... | ... | @@ -1044,7 +1049,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1044 | 1049 | assert(expr_type->id == TypeTableEntryIdMaybe); |
| 1045 | 1050 | TypeTableEntry *child_type = expr_type->data.maybe.child_type; |
| 1046 | 1051 | |
| 1047 | if (g->build_type != CodeGenBuildTypeRelease) { | |
| 1052 | if (!g->is_release_build) { | |
| 1048 | 1053 | add_debug_source_node(g, node); |
| 1049 | 1054 | LLVMValueRef cond_val; |
| 1050 | 1055 | if (child_type->id == TypeTableEntryIdPointer || |
| ... | ... | @@ -1977,7 +1982,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { |
| 1977 | 1982 | } else if (type_entry->id == TypeTableEntryIdUnreachable) { |
| 1978 | 1983 | assert(node->data.container_init_expr.entries.length == 0); |
| 1979 | 1984 | add_debug_source_node(g, node); |
| 1980 | if (g->build_type != CodeGenBuildTypeRelease) { | |
| 1985 | if (!g->is_release_build) { | |
| 1981 | 1986 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 1982 | 1987 | } |
| 1983 | 1988 | return LLVMBuildUnreachable(g->builder); |
| ... | ... | @@ -2233,7 +2238,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 2233 | 2238 | } |
| 2234 | 2239 | } |
| 2235 | 2240 | } |
| 2236 | if (!ignore_uninit && g->build_type != CodeGenBuildTypeRelease) { | |
| 2241 | if (!ignore_uninit && !g->is_release_build) { | |
| 2237 | 2242 | TypeTableEntry *isize = g->builtin_types.entry_isize; |
| 2238 | 2243 | uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); |
| 2239 | 2244 | uint64_t align_bytes = get_memcpy_align(g, variable->type); |
| ... | ... | @@ -2645,7 +2650,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE |
| 2645 | 2650 | |
| 2646 | 2651 | static void gen_const_globals(CodeGen *g) { |
| 2647 | 2652 | for (int i = 0; i < g->global_const_list.length; i += 1) { |
| 2648 | Expr *expr = g->global_const_list.at(i); | |
| 2653 | AstNode *expr_node = g->global_const_list.at(i); | |
| 2654 | Expr *expr = get_resolved_expr(expr_node); | |
| 2649 | 2655 | ConstExprValue *const_val = &expr->const_val; |
| 2650 | 2656 | assert(const_val->ok); |
| 2651 | 2657 | TypeTableEntry *type_entry = expr->type_entry; |
| ... | ... | @@ -2680,6 +2686,24 @@ static void delete_unused_builtin_fns(CodeGen *g) { |
| 2680 | 2686 | } |
| 2681 | 2687 | } |
| 2682 | 2688 | |
| 2689 | static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) { | |
| 2690 | if (g->is_test_build) { | |
| 2691 | if (fn_entry->is_test) { | |
| 2692 | return false; | |
| 2693 | } | |
| 2694 | if (fn_entry == g->main_fn) { | |
| 2695 | return true; | |
| 2696 | } | |
| 2697 | return fn_entry->ref_count == 0; | |
| 2698 | } | |
| 2699 | ||
| 2700 | if (fn_entry->is_test) { | |
| 2701 | return true; | |
| 2702 | } | |
| 2703 | ||
| 2704 | return fn_entry->ref_count == 0; | |
| 2705 | } | |
| 2706 | ||
| 2683 | 2707 | static void do_code_gen(CodeGen *g) { |
| 2684 | 2708 | assert(!g->errors.length); |
| 2685 | 2709 | |
| ... | ... | @@ -2731,12 +2755,19 @@ static void do_code_gen(CodeGen *g) { |
| 2731 | 2755 | var->value_ref = global_value; |
| 2732 | 2756 | } |
| 2733 | 2757 | |
| 2758 | LLVMValueRef *test_fn_vals = nullptr; | |
| 2759 | uint32_t next_test_index = 0; | |
| 2760 | if (g->is_test_build) { | |
| 2761 | test_fn_vals = allocate<LLVMValueRef>(g->test_fn_count); | |
| 2762 | } | |
| 2763 | ||
| 2734 | 2764 | // Generate function prototypes |
| 2735 | 2765 | for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) { |
| 2736 | 2766 | FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i); |
| 2737 | if (fn_table_entry->ref_count == 0) { | |
| 2767 | if (skip_fn_codegen(g, fn_table_entry)) { | |
| 2738 | 2768 | // huge time saver |
| 2739 | 2769 | LLVMDeleteFunction(fn_table_entry->fn_value); |
| 2770 | fn_table_entry->fn_value = nullptr; | |
| 2740 | 2771 | continue; |
| 2741 | 2772 | } |
| 2742 | 2773 | |
| ... | ... | @@ -2785,12 +2816,44 @@ static void do_code_gen(CodeGen *g) { |
| 2785 | 2816 | } |
| 2786 | 2817 | } |
| 2787 | 2818 | |
| 2819 | if (fn_table_entry->is_test) { | |
| 2820 | test_fn_vals[next_test_index] = fn_table_entry->fn_value; | |
| 2821 | next_test_index += 1; | |
| 2822 | } | |
| 2823 | } | |
| 2824 | ||
| 2825 | // Generate the list of test function pointers. | |
| 2826 | if (g->is_test_build) { | |
| 2827 | assert(g->test_fn_count > 0); | |
| 2828 | assert(next_test_index == g->test_fn_count); | |
| 2829 | ||
| 2830 | { | |
| 2831 | LLVMValueRef test_fn_array_val = LLVMConstArray(LLVMTypeOf(test_fn_vals[0]), | |
| 2832 | test_fn_vals, g->test_fn_count); | |
| 2833 | LLVMValueRef global_value = LLVMAddGlobal(g->module, | |
| 2834 | LLVMTypeOf(test_fn_array_val), "zig_test_fn_list"); | |
| 2835 | LLVMSetInitializer(global_value, test_fn_array_val); | |
| 2836 | LLVMSetLinkage(global_value, LLVMExternalLinkage); | |
| 2837 | LLVMSetGlobalConstant(global_value, true); | |
| 2838 | LLVMSetUnnamedAddr(global_value, true); | |
| 2839 | } | |
| 2840 | ||
| 2841 | { | |
| 2842 | LLVMValueRef test_fn_count_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, | |
| 2843 | g->test_fn_count, false); | |
| 2844 | LLVMValueRef global_value = LLVMAddGlobal(g->module, | |
| 2845 | LLVMTypeOf(test_fn_count_val), "zig_test_fn_count"); | |
| 2846 | LLVMSetInitializer(global_value, test_fn_count_val); | |
| 2847 | LLVMSetLinkage(global_value, LLVMExternalLinkage); | |
| 2848 | LLVMSetGlobalConstant(global_value, true); | |
| 2849 | LLVMSetUnnamedAddr(global_value, true); | |
| 2850 | } | |
| 2788 | 2851 | } |
| 2789 | 2852 | |
| 2790 | 2853 | // Generate function definitions. |
| 2791 | 2854 | for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { |
| 2792 | 2855 | FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i); |
| 2793 | if (fn_table_entry->ref_count == 0) { | |
| 2856 | if (skip_fn_codegen(g, fn_table_entry)) { | |
| 2794 | 2857 | // huge time saver |
| 2795 | 2858 | continue; |
| 2796 | 2859 | } |
| ... | ... | @@ -3297,8 +3360,7 @@ static void init(CodeGen *g, Buf *source_path) { |
| 3297 | 3360 | char *native_cpu = LLVMZigGetHostCPUName(); |
| 3298 | 3361 | char *native_features = LLVMZigGetNativeFeatures(); |
| 3299 | 3362 | |
| 3300 | LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ? | |
| 3301 | LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive; | |
| 3363 | LLVMCodeGenOptLevel opt_level = g->is_release_build ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone; | |
| 3302 | 3364 | |
| 3303 | 3365 | LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; |
| 3304 | 3366 | |
| ... | ... | @@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) { |
| 3321 | 3383 | |
| 3322 | 3384 | |
| 3323 | 3385 | Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING); |
| 3324 | bool is_optimized = g->build_type == CodeGenBuildTypeRelease; | |
| 3386 | bool is_optimized = g->is_release_build; | |
| 3325 | 3387 | const char *flags = ""; |
| 3326 | 3388 | unsigned runtime_version = 0; |
| 3327 | 3389 | g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(), |
| ... | ... | @@ -3611,14 +3673,10 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou |
| 3611 | 3673 | buf_sprintf("missing export declaration and export type not provided")); |
| 3612 | 3674 | } |
| 3613 | 3675 | |
| 3614 | if (!g->link_libc) { | |
| 3676 | if (!g->link_libc && !g->is_test_build) { | |
| 3615 | 3677 | if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) { |
| 3616 | 3678 | g->bootstrap_import = add_special_code(g, "bootstrap.zig"); |
| 3617 | 3679 | } |
| 3618 | ||
| 3619 | if (g->out_type == OutTypeExe) { | |
| 3620 | add_special_code(g, "builtin.zig"); | |
| 3621 | } | |
| 3622 | 3680 | } |
| 3623 | 3681 | |
| 3624 | 3682 | if (g->verbose) { |
| ... | ... | @@ -3677,6 +3735,8 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) { |
| 3677 | 3735 | } |
| 3678 | 3736 | |
| 3679 | 3737 | static void generate_h_file(CodeGen *g) { |
| 3738 | assert(!g->is_test_build); | |
| 3739 | ||
| 3680 | 3740 | Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name)); |
| 3681 | 3741 | FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb"); |
| 3682 | 3742 | if (!out_h) |
| ... | ... | @@ -3768,8 +3828,38 @@ static const char *get_libc_file(CodeGen *g, const char *file) { |
| 3768 | 3828 | return buf_ptr(out_buf); |
| 3769 | 3829 | } |
| 3770 | 3830 | |
| 3831 | static Buf *build_o(CodeGen *parent_gen, const char *oname) { | |
| 3832 | Buf *source_basename = buf_sprintf("%s.zig", oname); | |
| 3833 | Buf *std_dir_path = buf_create_from_str(ZIG_STD_DIR); | |
| 3834 | ||
| 3835 | CodeGen *child_gen = codegen_create(std_dir_path); | |
| 3836 | codegen_set_is_release(child_gen, parent_gen->is_release_build); | |
| 3837 | ||
| 3838 | codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); | |
| 3839 | codegen_set_is_static(child_gen, parent_gen->is_static); | |
| 3840 | ||
| 3841 | codegen_set_out_type(child_gen, OutTypeObj); | |
| 3842 | codegen_set_out_name(child_gen, buf_create_from_str(oname)); | |
| 3843 | ||
| 3844 | codegen_set_verbose(child_gen, parent_gen->verbose); | |
| 3845 | codegen_set_errmsg_color(child_gen, parent_gen->err_color); | |
| 3846 | ||
| 3847 | Buf *full_path = buf_alloc(); | |
| 3848 | os_path_join(std_dir_path, source_basename, full_path); | |
| 3849 | Buf source_code = BUF_INIT; | |
| 3850 | if (os_fetch_file_path(full_path, &source_code)) { | |
| 3851 | zig_panic("unable to fetch file: %s\n", buf_ptr(full_path)); | |
| 3852 | } | |
| 3853 | ||
| 3854 | codegen_add_root_code(child_gen, std_dir_path, source_basename, &source_code); | |
| 3855 | Buf *o_out = buf_sprintf("%s.o", oname); | |
| 3856 | codegen_link(child_gen, buf_ptr(o_out)); | |
| 3857 | ||
| 3858 | return o_out; | |
| 3859 | } | |
| 3860 | ||
| 3771 | 3861 | void codegen_link(CodeGen *g, const char *out_file) { |
| 3772 | bool is_optimized = (g->build_type == CodeGenBuildTypeRelease); | |
| 3862 | bool is_optimized = g->is_release_build; | |
| 3773 | 3863 | if (is_optimized) { |
| 3774 | 3864 | if (g->verbose) { |
| 3775 | 3865 | fprintf(stderr, "\nOptimization:\n"); |
| ... | ... | @@ -3788,6 +3878,7 @@ void codegen_link(CodeGen *g, const char *out_file) { |
| 3788 | 3878 | } |
| 3789 | 3879 | |
| 3790 | 3880 | if (!out_file) { |
| 3881 | assert(g->root_out_name); | |
| 3791 | 3882 | out_file = buf_ptr(g->root_out_name); |
| 3792 | 3883 | } |
| 3793 | 3884 | |
| ... | ... | @@ -3821,6 +3912,7 @@ void codegen_link(CodeGen *g, const char *out_file) { |
| 3821 | 3912 | return; |
| 3822 | 3913 | } |
| 3823 | 3914 | |
| 3915 | ||
| 3824 | 3916 | // invoke `ld` |
| 3825 | 3917 | ZigList<const char *> args = {0}; |
| 3826 | 3918 | const char *crt1o; |
| ... | ... | @@ -3871,6 +3963,16 @@ void codegen_link(CodeGen *g, const char *out_file) { |
| 3871 | 3963 | args.append(get_libc_file(g, "crtn.o")); |
| 3872 | 3964 | } |
| 3873 | 3965 | |
| 3966 | if (g->is_test_build) { | |
| 3967 | Buf *test_runner_o_path = build_o(g, "test_runner"); | |
| 3968 | args.append(buf_ptr(test_runner_o_path)); | |
| 3969 | } | |
| 3970 | ||
| 3971 | if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { | |
| 3972 | Buf *builtin_o_path = build_o(g, "builtin"); | |
| 3973 | args.append(buf_ptr(builtin_o_path)); | |
| 3974 | } | |
| 3975 | ||
| 3874 | 3976 | for (int i = 0; i < g->lib_dirs.length; i += 1) { |
| 3875 | 3977 | const char *lib_dir = g->lib_dirs.at(i); |
| 3876 | 3978 | args.append("-L"); |
src/codegen.hpp+3-1| ... | ... | @@ -16,7 +16,9 @@ |
| 16 | 16 | CodeGen *codegen_create(Buf *root_source_dir); |
| 17 | 17 | |
| 18 | 18 | void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len); |
| 19 | void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type); | |
| 19 | void codegen_set_is_release(CodeGen *codegen, bool is_release); | |
| 20 | void codegen_set_is_test(CodeGen *codegen, bool is_test); | |
| 21 | ||
| 20 | 22 | void codegen_set_is_static(CodeGen *codegen, bool is_static); |
| 21 | 23 | void codegen_set_strip(CodeGen *codegen, bool strip); |
| 22 | 24 | void codegen_set_verbose(CodeGen *codegen, bool verbose); |
src/main.cpp+30-5| ... | ... | @@ -17,6 +17,7 @@ static int usage(const char *arg0) { |
| 17 | 17 | fprintf(stderr, "Usage: %s [command] [options]\n" |
| 18 | 18 | "Commands:\n" |
| 19 | 19 | " build create executable, object, or library from target\n" |
| 20 | " test create and run a test build\n" | |
| 20 | 21 | " version print version number and exit\n" |
| 21 | 22 | " parseh convert a c header file to zig extern declarations\n" |
| 22 | 23 | "Options:\n" |
| ... | ... | @@ -40,6 +41,7 @@ static int usage(const char *arg0) { |
| 40 | 41 | enum Cmd { |
| 41 | 42 | CmdInvalid, |
| 42 | 43 | CmdBuild, |
| 44 | CmdTest, | |
| 43 | 45 | CmdVersion, |
| 44 | 46 | CmdParseH, |
| 45 | 47 | }; |
| ... | ... | @@ -49,7 +51,7 @@ int main(int argc, char **argv) { |
| 49 | 51 | Cmd cmd = CmdInvalid; |
| 50 | 52 | const char *in_file = nullptr; |
| 51 | 53 | const char *out_file = nullptr; |
| 52 | bool release = false; | |
| 54 | bool is_release_build = false; | |
| 53 | 55 | bool strip = false; |
| 54 | 56 | bool is_static = false; |
| 55 | 57 | OutType out_type = OutTypeUnknown; |
| ... | ... | @@ -67,7 +69,7 @@ int main(int argc, char **argv) { |
| 67 | 69 | |
| 68 | 70 | if (arg[0] == '-') { |
| 69 | 71 | if (strcmp(arg, "--release") == 0) { |
| 70 | release = true; | |
| 72 | is_release_build = true; | |
| 71 | 73 | } else if (strcmp(arg, "--strip") == 0) { |
| 72 | 74 | strip = true; |
| 73 | 75 | } else if (strcmp(arg, "--static") == 0) { |
| ... | ... | @@ -127,6 +129,8 @@ int main(int argc, char **argv) { |
| 127 | 129 | cmd = CmdVersion; |
| 128 | 130 | } else if (strcmp(arg, "parseh") == 0) { |
| 129 | 131 | cmd = CmdParseH; |
| 132 | } else if (strcmp(arg, "test") == 0) { | |
| 133 | cmd = CmdTest; | |
| 130 | 134 | } else { |
| 131 | 135 | fprintf(stderr, "Unrecognized command: %s\n", arg); |
| 132 | 136 | return usage(arg0); |
| ... | ... | @@ -135,6 +139,7 @@ int main(int argc, char **argv) { |
| 135 | 139 | switch (cmd) { |
| 136 | 140 | case CmdBuild: |
| 137 | 141 | case CmdParseH: |
| 142 | case CmdTest: | |
| 138 | 143 | if (!in_file) { |
| 139 | 144 | in_file = arg; |
| 140 | 145 | } else { |
| ... | ... | @@ -152,6 +157,7 @@ int main(int argc, char **argv) { |
| 152 | 157 | switch (cmd) { |
| 153 | 158 | case CmdBuild: |
| 154 | 159 | case CmdParseH: |
| 160 | case CmdTest: | |
| 155 | 161 | { |
| 156 | 162 | if (!in_file) |
| 157 | 163 | return usage(arg0); |
| ... | ... | @@ -178,14 +184,22 @@ int main(int argc, char **argv) { |
| 178 | 184 | } |
| 179 | 185 | |
| 180 | 186 | CodeGen *g = codegen_create(&root_source_dir); |
| 181 | codegen_set_build_type(g, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug); | |
| 187 | codegen_set_is_release(g, is_release_build); | |
| 188 | codegen_set_is_test(g, cmd == CmdTest); | |
| 189 | ||
| 182 | 190 | codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); |
| 183 | 191 | codegen_set_strip(g, strip); |
| 184 | 192 | codegen_set_is_static(g, is_static); |
| 185 | if (out_type != OutTypeUnknown) | |
| 193 | if (out_type != OutTypeUnknown) { | |
| 186 | 194 | codegen_set_out_type(g, out_type); |
| 187 | if (out_name) | |
| 195 | } else if (cmd == CmdTest) { | |
| 196 | codegen_set_out_type(g, OutTypeExe); | |
| 197 | } | |
| 198 | if (out_name) { | |
| 188 | 199 | codegen_set_out_name(g, buf_create_from_str(out_name)); |
| 200 | } else if (cmd == CmdTest) { | |
| 201 | codegen_set_out_name(g, buf_create_from_str("test")); | |
| 202 | } | |
| 189 | 203 | if (libc_lib_dir) |
| 190 | 204 | codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir)); |
| 191 | 205 | if (libc_include_dir) |
| ... | ... | @@ -205,6 +219,17 @@ int main(int argc, char **argv) { |
| 205 | 219 | codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code); |
| 206 | 220 | codegen_render_ast(g, stdout, 4); |
| 207 | 221 | return EXIT_SUCCESS; |
| 222 | } else if (cmd == CmdTest) { | |
| 223 | codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); | |
| 224 | codegen_link(g, "./test"); | |
| 225 | ZigList<const char *> args = {0}; | |
| 226 | int return_code; | |
| 227 | os_spawn_process("./test", args, &return_code); | |
| 228 | if (return_code != 0) { | |
| 229 | fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); | |
| 230 | fprintf(stderr, "./test\n"); | |
| 231 | } | |
| 232 | return return_code; | |
| 208 | 233 | } else { |
| 209 | 234 | zig_unreachable(); |
| 210 | 235 | } |
src/os.cpp+14-15| ... | ... | @@ -17,25 +17,24 @@ |
| 17 | 17 | #include <fcntl.h> |
| 18 | 18 | #include <limits.h> |
| 19 | 19 | |
| 20 | void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) { | |
| 20 | void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code) { | |
| 21 | 21 | pid_t pid = fork(); |
| 22 | 22 | if (pid == -1) |
| 23 | 23 | zig_panic("fork failed"); |
| 24 | if (pid != 0) | |
| 25 | return; | |
| 26 | if (detached) { | |
| 27 | if (setsid() == -1) | |
| 28 | zig_panic("process detach failed"); | |
| 29 | } | |
| 30 | ||
| 31 | const char **argv = allocate<const char *>(args.length + 2); | |
| 32 | argv[0] = exe; | |
| 33 | argv[args.length + 1] = nullptr; | |
| 34 | for (int i = 0; i < args.length; i += 1) { | |
| 35 | argv[i + 1] = args.at(i); | |
| 24 | if (pid == 0) { | |
| 25 | // child | |
| 26 | const char **argv = allocate<const char *>(args.length + 2); | |
| 27 | argv[0] = exe; | |
| 28 | argv[args.length + 1] = nullptr; | |
| 29 | for (int i = 0; i < args.length; i += 1) { | |
| 30 | argv[i + 1] = args.at(i); | |
| 31 | } | |
| 32 | execvp(exe, const_cast<char * const *>(argv)); | |
| 33 | zig_panic("execvp failed: %s", strerror(errno)); | |
| 34 | } else { | |
| 35 | // parent | |
| 36 | waitpid(pid, return_code, 0); | |
| 36 | 37 | } |
| 37 | execvp(exe, const_cast<char * const *>(argv)); | |
| 38 | zig_panic("execvp failed: %s", strerror(errno)); | |
| 39 | 38 | } |
| 40 | 39 | |
| 41 | 40 | static int read_all_fd_stream(int fd, Buf *out_buf) { |
src/os.hpp+1-1| ... | ... | @@ -13,7 +13,7 @@ |
| 13 | 13 | |
| 14 | 14 | #include <stdio.h> |
| 15 | 15 | |
| 16 | void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached); | |
| 16 | void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code); | |
| 17 | 17 | void os_exec_process(const char *exe, ZigList<const char *> &args, |
| 18 | 18 | int *return_code, Buf *out_stderr, Buf *out_stdout); |
| 19 | 19 |
std/test_runner.zig created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | import "std.zig"; | |
| 2 | ||
| 3 | /* | |
| 4 | struct TestFn { | |
| 5 | name: []u8, | |
| 6 | func: extern fn(), | |
| 7 | } | |
| 8 | ||
| 9 | extern var test_fn_list: []TestFn; | |
| 10 | */ | |
| 11 | ||
| 12 | extern var zig_test_fn_count: isize; | |
| 13 | ||
| 14 | // TODO make this a slice of structs | |
| 15 | extern var zig_test_fn_list: [99999999]extern fn(); | |
| 16 | ||
| 17 | pub fn main(args: [][]u8) -> %void { | |
| 18 | var i : isize = 0; | |
| 19 | while (i < zig_test_fn_count) { | |
| 20 | %%stderr.print_str("Test "); | |
| 21 | // TODO get rid of the isize | |
| 22 | %%stderr.print_i64(i + isize(1)); | |
| 23 | %%stderr.print_str("/"); | |
| 24 | %%stderr.print_i64(zig_test_fn_count); | |
| 25 | %%stderr.print_str(" "); | |
| 26 | /* | |
| 27 | %%stderr.print_str(test_fn.name); | |
| 28 | */ | |
| 29 | %%stderr.print_str("..."); | |
| 30 | ||
| 31 | /* | |
| 32 | // TODO support calling function pointers as fields directly | |
| 33 | const fn_ptr = test_fn.func; | |
| 34 | fn_ptr(); | |
| 35 | */ | |
| 36 | ||
| 37 | const test_fn = zig_test_fn_list[i]; | |
| 38 | test_fn(); | |
| 39 | ||
| 40 | %%stderr.print_str("OK\n"); | |
| 41 | %%stderr.flush(); | |
| 42 | ||
| 43 | i += 1; | |
| 44 | } | |
| 45 | } |