authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-03 18:02:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-03 18:02:01-07:00
log11a06443659154ec53e4deb00956b2f025cd71ca
treefc02b195cf8850f0f1743027055461aca8b4e57f
parentafdb47c32d00db3777352fb745b277b5d0805c69

basic support for building a test target


9 files changed, 249 insertions(+), 58 deletions(-)

CMakeLists.txt+1
...@@ -122,6 +122,7 @@ set(C_HEADERS...@@ -122,6 +122,7 @@ set(C_HEADERS
122set(ZIG_STD_SRC122set(ZIG_STD_SRC
123 "${CMAKE_SOURCE_DIR}/std/bootstrap.zig"123 "${CMAKE_SOURCE_DIR}/std/bootstrap.zig"
124 "${CMAKE_SOURCE_DIR}/std/builtin.zig"124 "${CMAKE_SOURCE_DIR}/std/builtin.zig"
125 "${CMAKE_SOURCE_DIR}/std/test_runner.zig"
125 "${CMAKE_SOURCE_DIR}/std/std.zig"126 "${CMAKE_SOURCE_DIR}/std/std.zig"
126 "${CMAKE_SOURCE_DIR}/std/syscall.zig"127 "${CMAKE_SOURCE_DIR}/std/syscall.zig"
127 "${CMAKE_SOURCE_DIR}/std/errno.zig"128 "${CMAKE_SOURCE_DIR}/std/errno.zig"
src/all_types.hpp+8-7
...@@ -36,11 +36,6 @@ enum OutType {...@@ -36,11 +36,6 @@ enum OutType {
36 OutTypeObj,36 OutTypeObj,
37};37};
3838
39enum CodeGenBuildType {
40 CodeGenBuildTypeDebug,
41 CodeGenBuildTypeRelease,
42};
43
44struct ConstEnumValue {39struct ConstEnumValue {
45 uint64_t tag;40 uint64_t tag;
46 ConstExprValue *payload;41 ConstExprValue *payload;
...@@ -983,6 +978,7 @@ struct FnTableEntry {...@@ -983,6 +978,7 @@ struct FnTableEntry {
983 bool is_inline;978 bool is_inline;
984 bool internal_linkage;979 bool internal_linkage;
985 bool is_extern;980 bool is_extern;
981 bool is_test;
986 uint32_t ref_count; // if this is 0 we don't have to codegen it982 uint32_t ref_count; // if this is 0 we don't have to codegen it
987983
988 // reminder: hash tables must be initialized before use984 // reminder: hash tables must be initialized before use
...@@ -1073,7 +1069,8 @@ struct CodeGen {...@@ -1073,7 +1069,8 @@ struct CodeGen {
1073 bool link_libc;1069 bool link_libc;
1074 Buf *libc_lib_dir;1070 Buf *libc_lib_dir;
1075 Buf *libc_include_dir;1071 Buf *libc_include_dir;
1076 CodeGenBuildType build_type;1072 bool is_release_build;
1073 bool is_test_build;
1077 LLVMTargetMachineRef target_machine;1074 LLVMTargetMachineRef target_machine;
1078 LLVMZigDIFile *dummy_di_file;1075 LLVMZigDIFile *dummy_di_file;
1079 bool is_native_target;1076 bool is_native_target;
...@@ -1087,10 +1084,11 @@ struct CodeGen {...@@ -1087,10 +1084,11 @@ struct CodeGen {
1087 // there will not be a corresponding fn_defs entry.1084 // there will not be a corresponding fn_defs entry.
1088 ZigList<FnTableEntry *> fn_protos;1085 ZigList<FnTableEntry *> fn_protos;
1089 ZigList<VariableTableEntry *> global_vars;1086 ZigList<VariableTableEntry *> global_vars;
1090 ZigList<Expr *> global_const_list;1087 ZigList<AstNode *> global_const_list;
10911088
1092 OutType out_type;1089 OutType out_type;
1093 FnTableEntry *cur_fn;1090 FnTableEntry *cur_fn;
1091 FnTableEntry *main_fn;
1094 LLVMValueRef cur_ret_ptr;1092 LLVMValueRef cur_ret_ptr;
1095 ZigList<LLVMBasicBlockRef> break_block_stack;1093 ZigList<LLVMBasicBlockRef> break_block_stack;
1096 ZigList<LLVMBasicBlockRef> continue_block_stack;1094 ZigList<LLVMBasicBlockRef> continue_block_stack;
...@@ -1103,6 +1101,7 @@ struct CodeGen {...@@ -1103,6 +1101,7 @@ struct CodeGen {
1103 ErrColor err_color;1101 ErrColor err_color;
1104 ImportTableEntry *root_import;1102 ImportTableEntry *root_import;
1105 ImportTableEntry *bootstrap_import;1103 ImportTableEntry *bootstrap_import;
1104 ImportTableEntry *test_runner_import;
1106 LLVMValueRef memcpy_fn_val;1105 LLVMValueRef memcpy_fn_val;
1107 LLVMValueRef memset_fn_val;1106 LLVMValueRef memset_fn_val;
1108 LLVMValueRef trap_fn_val;1107 LLVMValueRef trap_fn_val;
...@@ -1116,6 +1115,8 @@ struct CodeGen {...@@ -1116,6 +1115,8 @@ struct CodeGen {
1116 const char **clang_argv;1115 const char **clang_argv;
1117 int clang_argv_len;1116 int clang_argv_len;
1118 ZigList<const char *> lib_dirs;1117 ZigList<const char *> lib_dirs;
1118
1119 uint32_t test_fn_count;
1119};1120};
11201121
1121struct VariableTableEntry {1122struct VariableTableEntry {
src/analyze.cpp+26-10
...@@ -781,6 +781,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t...@@ -781,6 +781,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
781781
782 bool is_cold = false;782 bool is_cold = false;
783 bool is_naked = false;783 bool is_naked = false;
784 bool is_test = false;
784785
785 if (fn_proto->directives) {786 if (fn_proto->directives) {
786 for (int i = 0; i < fn_proto->directives->length; i += 1) {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,6 +795,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
794 is_naked = true;795 is_naked = true;
795 } else if (buf_eql_str(attr_name, "cold")) {796 } else if (buf_eql_str(attr_name, "cold")) {
796 is_cold = true;797 is_cold = true;
798 } else if (buf_eql_str(attr_name, "test")) {
799 is_test = true;
800 g->test_fn_count += 1;
797 } else {801 } else {
798 add_node_error(g, directive_node,802 add_node_error(g, directive_node,
799 buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));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,6 +817,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
813 is_naked, is_cold);817 is_naked, is_cold);
814818
815 fn_table_entry->type_entry = fn_type;819 fn_table_entry->type_entry = fn_type;
820 fn_table_entry->is_test = is_test;
816821
817 if (fn_type->id == TypeTableEntryIdInvalid) {822 if (fn_type->id == TypeTableEntryIdInvalid) {
818 fn_proto->skip = true;823 fn_proto->skip = true;
...@@ -846,7 +851,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t...@@ -846,7 +851,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
846 unsigned scope_line = line_number;851 unsigned scope_line = line_number;
847 bool is_definition = fn_table_entry->fn_def_node != nullptr;852 bool is_definition = fn_table_entry->fn_def_node != nullptr;
848 unsigned flags = 0;853 unsigned flags = 0;
849 bool is_optimized = g->build_type == CodeGenBuildTypeRelease;854 bool is_optimized = g->is_release_build;
850 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,855 LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
851 import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",856 import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
852 import->di_file, line_number,857 import->di_file, line_number,
...@@ -1247,10 +1252,18 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,...@@ -1247,10 +1252,18 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
12471252
1248 fn_table->put(proto_name, fn_table_entry);1253 fn_table->put(proto_name, fn_table_entry);
12491254
1250 if (!struct_type &&1255 bool is_main_fn = !struct_type && (import == g->root_import) && buf_eql_str(proto_name, "main");
1251 g->bootstrap_import &&1256 if (is_main_fn) {
1252 import == g->root_import && buf_eql_str(proto_name, "main"))1257 g->main_fn = fn_table_entry;
1253 {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 g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);1267 g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
1255 }1268 }
12561269
...@@ -1551,13 +1564,14 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {...@@ -1551,13 +1564,14 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
1551 }1564 }
1552}1565}
15531566
1554static void add_global_const_expr(CodeGen *g, Expr *expr) {1567static void add_global_const_expr(CodeGen *g, AstNode *expr_node) {
1568 Expr *expr = get_resolved_expr(expr_node);
1555 if (expr->const_val.ok &&1569 if (expr->const_val.ok &&
1556 type_has_codegen_value(expr->type_entry) &&1570 type_has_codegen_value(expr->type_entry) &&
1557 !expr->has_global_const &&1571 !expr->has_global_const &&
1558 type_has_bits(expr->type_entry))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 expr->has_global_const = true;1575 expr->has_global_const = true;
1562 }1576 }
1563}1577}
...@@ -1924,7 +1938,7 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn...@@ -1924,7 +1938,7 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn
1924 *child_node, expected_type, child_types[i]);1938 *child_node, expected_type, child_types[i]);
1925 Expr *expr = get_resolved_expr(*child_node);1939 Expr *expr = get_resolved_expr(*child_node);
1926 expr->type_entry = resolved_type;1940 expr->type_entry = resolved_type;
1927 add_global_const_expr(g, expr);1941 add_global_const_expr(g, *child_node);
1928 }1942 }
19291943
1930 return expected_type;1944 return expected_type;
...@@ -4022,7 +4036,9 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry...@@ -4022,7 +4036,9 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
4022 if (buf_eql_str(&var_name, "is_big_endian")) {4036 if (buf_eql_str(&var_name, "is_big_endian")) {
4023 return resolve_expr_const_val_as_bool(g, node, g->is_big_endian);4037 return resolve_expr_const_val_as_bool(g, node, g->is_big_endian);
4024 } else if (buf_eql_str(&var_name, "is_release")) {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 } else {4042 } else {
4027 add_node_error(g, *str_node,4043 add_node_error(g, *str_node,
4028 buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name)));4044 buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name)));
...@@ -4752,7 +4768,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -4752,7 +4768,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
4752 expr->type_entry = return_type;4768 expr->type_entry = return_type;
4753 node->block_context = context;4769 node->block_context = context;
47544770
4755 add_global_const_expr(g, expr);4771 add_global_const_expr(g, node);
47564772
4757 return resolved_type;4773 return resolved_type;
4758}4774}
src/codegen.cpp+121-19
...@@ -28,7 +28,8 @@ CodeGen *codegen_create(Buf *root_source_dir) {...@@ -28,7 +28,8 @@ CodeGen *codegen_create(Buf *root_source_dir) {
28 g->primitive_type_table.init(32);28 g->primitive_type_table.init(32);
29 g->unresolved_top_level_decls.init(32);29 g->unresolved_top_level_decls.init(32);
30 g->fn_type_table.init(32);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 g->root_source_dir = root_source_dir;33 g->root_source_dir = root_source_dir;
33 g->next_error_index = 1;34 g->next_error_index = 1;
34 g->error_value_count = 1;35 g->error_value_count = 1;
...@@ -44,8 +45,12 @@ void codegen_set_clang_argv(CodeGen *g, const char **args, int len) {...@@ -44,8 +45,12 @@ void codegen_set_clang_argv(CodeGen *g, const char **args, int len) {
44 g->clang_argv_len = len;45 g->clang_argv_len = len;
45}46}
4647
47void codegen_set_build_type(CodeGen *g, CodeGenBuildType build_type) {48void codegen_set_is_release(CodeGen *g, bool is_release_build) {
48 g->build_type = build_type;49 g->is_release_build = is_release_build;
50}
51
52void codegen_set_is_test(CodeGen *g, bool is_test_build) {
53 g->is_test_build = is_test_build;
49}54}
5055
51void codegen_set_is_static(CodeGen *g, bool is_static) {56void codegen_set_is_static(CodeGen *g, bool is_static) {
...@@ -1003,7 +1008,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1003,7 +1008,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1003 assert(expr_type->id == TypeTableEntryIdErrorUnion);1008 assert(expr_type->id == TypeTableEntryIdErrorUnion);
1004 TypeTableEntry *child_type = expr_type->data.error.child_type;1009 TypeTableEntry *child_type = expr_type->data.error.child_type;
10051010
1006 if (g->build_type != CodeGenBuildTypeRelease) {1011 if (!g->is_release_build) {
1007 LLVMValueRef err_val;1012 LLVMValueRef err_val;
1008 if (type_has_bits(child_type)) {1013 if (type_has_bits(child_type)) {
1009 add_debug_source_node(g, node);1014 add_debug_source_node(g, node);
...@@ -1044,7 +1049,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1044,7 +1049,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1044 assert(expr_type->id == TypeTableEntryIdMaybe);1049 assert(expr_type->id == TypeTableEntryIdMaybe);
1045 TypeTableEntry *child_type = expr_type->data.maybe.child_type;1050 TypeTableEntry *child_type = expr_type->data.maybe.child_type;
10461051
1047 if (g->build_type != CodeGenBuildTypeRelease) {1052 if (!g->is_release_build) {
1048 add_debug_source_node(g, node);1053 add_debug_source_node(g, node);
1049 LLVMValueRef cond_val;1054 LLVMValueRef cond_val;
1050 if (child_type->id == TypeTableEntryIdPointer ||1055 if (child_type->id == TypeTableEntryIdPointer ||
...@@ -1977,7 +1982,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {...@@ -1977,7 +1982,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
1977 } else if (type_entry->id == TypeTableEntryIdUnreachable) {1982 } else if (type_entry->id == TypeTableEntryIdUnreachable) {
1978 assert(node->data.container_init_expr.entries.length == 0);1983 assert(node->data.container_init_expr.entries.length == 0);
1979 add_debug_source_node(g, node);1984 add_debug_source_node(g, node);
1980 if (g->build_type != CodeGenBuildTypeRelease) {1985 if (!g->is_release_build) {
1981 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");1986 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
1982 }1987 }
1983 return LLVMBuildUnreachable(g->builder);1988 return LLVMBuildUnreachable(g->builder);
...@@ -2233,7 +2238,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa...@@ -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 TypeTableEntry *isize = g->builtin_types.entry_isize;2242 TypeTableEntry *isize = g->builtin_types.entry_isize;
2238 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);2243 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
2239 uint64_t align_bytes = get_memcpy_align(g, variable->type);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,7 +2650,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
26452650
2646static void gen_const_globals(CodeGen *g) {2651static void gen_const_globals(CodeGen *g) {
2647 for (int i = 0; i < g->global_const_list.length; i += 1) {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 ConstExprValue *const_val = &expr->const_val;2655 ConstExprValue *const_val = &expr->const_val;
2650 assert(const_val->ok);2656 assert(const_val->ok);
2651 TypeTableEntry *type_entry = expr->type_entry;2657 TypeTableEntry *type_entry = expr->type_entry;
...@@ -2680,6 +2686,24 @@ static void delete_unused_builtin_fns(CodeGen *g) {...@@ -2680,6 +2686,24 @@ static void delete_unused_builtin_fns(CodeGen *g) {
2680 }2686 }
2681}2687}
26822688
2689static 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
2683static void do_code_gen(CodeGen *g) {2707static void do_code_gen(CodeGen *g) {
2684 assert(!g->errors.length);2708 assert(!g->errors.length);
26852709
...@@ -2731,12 +2755,19 @@ static void do_code_gen(CodeGen *g) {...@@ -2731,12 +2755,19 @@ static void do_code_gen(CodeGen *g) {
2731 var->value_ref = global_value;2755 var->value_ref = global_value;
2732 }2756 }
27332757
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 // Generate function prototypes2764 // Generate function prototypes
2735 for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {2765 for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) {
2736 FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i);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 // huge time saver2768 // huge time saver
2739 LLVMDeleteFunction(fn_table_entry->fn_value);2769 LLVMDeleteFunction(fn_table_entry->fn_value);
2770 fn_table_entry->fn_value = nullptr;
2740 continue;2771 continue;
2741 }2772 }
27422773
...@@ -2785,12 +2816,44 @@ static void do_code_gen(CodeGen *g) {...@@ -2785,12 +2816,44 @@ static void do_code_gen(CodeGen *g) {
2785 }2816 }
2786 }2817 }
27872818
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 }
27892852
2790 // Generate function definitions.2853 // Generate function definitions.
2791 for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {2854 for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
2792 FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i);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 // huge time saver2857 // huge time saver
2795 continue;2858 continue;
2796 }2859 }
...@@ -3297,8 +3360,7 @@ static void init(CodeGen *g, Buf *source_path) {...@@ -3297,8 +3360,7 @@ static void init(CodeGen *g, Buf *source_path) {
3297 char *native_cpu = LLVMZigGetHostCPUName();3360 char *native_cpu = LLVMZigGetHostCPUName();
3298 char *native_features = LLVMZigGetNativeFeatures();3361 char *native_features = LLVMZigGetNativeFeatures();
32993362
3300 LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ?3363 LLVMCodeGenOptLevel opt_level = g->is_release_build ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone;
3301 LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive;
33023364
3303 LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC;3365 LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC;
33043366
...@@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) {...@@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) {
33213383
33223384
3323 Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);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 const char *flags = "";3387 const char *flags = "";
3326 unsigned runtime_version = 0;3388 unsigned runtime_version = 0;
3327 g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(),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,14 +3673,10 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou
3611 buf_sprintf("missing export declaration and export type not provided"));3673 buf_sprintf("missing export declaration and export type not provided"));
3612 }3674 }
36133675
3614 if (!g->link_libc) {3676 if (!g->link_libc && !g->is_test_build) {
3615 if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) {3677 if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) {
3616 g->bootstrap_import = add_special_code(g, "bootstrap.zig");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 }
36233681
3624 if (g->verbose) {3682 if (g->verbose) {
...@@ -3677,6 +3735,8 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) {...@@ -3677,6 +3735,8 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) {
3677}3735}
36783736
3679static void generate_h_file(CodeGen *g) {3737static void generate_h_file(CodeGen *g) {
3738 assert(!g->is_test_build);
3739
3680 Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name));3740 Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name));
3681 FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb");3741 FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb");
3682 if (!out_h)3742 if (!out_h)
...@@ -3768,8 +3828,38 @@ static const char *get_libc_file(CodeGen *g, const char *file) {...@@ -3768,8 +3828,38 @@ static const char *get_libc_file(CodeGen *g, const char *file) {
3768 return buf_ptr(out_buf);3828 return buf_ptr(out_buf);
3769}3829}
37703830
3831static 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
3771void codegen_link(CodeGen *g, const char *out_file) {3861void 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 if (is_optimized) {3863 if (is_optimized) {
3774 if (g->verbose) {3864 if (g->verbose) {
3775 fprintf(stderr, "\nOptimization:\n");3865 fprintf(stderr, "\nOptimization:\n");
...@@ -3788,6 +3878,7 @@ void codegen_link(CodeGen *g, const char *out_file) {...@@ -3788,6 +3878,7 @@ void codegen_link(CodeGen *g, const char *out_file) {
3788 }3878 }
37893879
3790 if (!out_file) {3880 if (!out_file) {
3881 assert(g->root_out_name);
3791 out_file = buf_ptr(g->root_out_name);3882 out_file = buf_ptr(g->root_out_name);
3792 }3883 }
37933884
...@@ -3821,6 +3912,7 @@ void codegen_link(CodeGen *g, const char *out_file) {...@@ -3821,6 +3912,7 @@ void codegen_link(CodeGen *g, const char *out_file) {
3821 return;3912 return;
3822 }3913 }
38233914
3915
3824 // invoke `ld`3916 // invoke `ld`
3825 ZigList<const char *> args = {0};3917 ZigList<const char *> args = {0};
3826 const char *crt1o;3918 const char *crt1o;
...@@ -3871,6 +3963,16 @@ void codegen_link(CodeGen *g, const char *out_file) {...@@ -3871,6 +3963,16 @@ void codegen_link(CodeGen *g, const char *out_file) {
3871 args.append(get_libc_file(g, "crtn.o"));3963 args.append(get_libc_file(g, "crtn.o"));
3872 }3964 }
38733965
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 for (int i = 0; i < g->lib_dirs.length; i += 1) {3976 for (int i = 0; i < g->lib_dirs.length; i += 1) {
3875 const char *lib_dir = g->lib_dirs.at(i);3977 const char *lib_dir = g->lib_dirs.at(i);
3876 args.append("-L");3978 args.append("-L");
src/codegen.hpp+3-1
...@@ -16,7 +16,9 @@...@@ -16,7 +16,9 @@
16CodeGen *codegen_create(Buf *root_source_dir);16CodeGen *codegen_create(Buf *root_source_dir);
1717
18void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len);18void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len);
19void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type);19void codegen_set_is_release(CodeGen *codegen, bool is_release);
20void codegen_set_is_test(CodeGen *codegen, bool is_test);
21
20void codegen_set_is_static(CodeGen *codegen, bool is_static);22void codegen_set_is_static(CodeGen *codegen, bool is_static);
21void codegen_set_strip(CodeGen *codegen, bool strip);23void codegen_set_strip(CodeGen *codegen, bool strip);
22void codegen_set_verbose(CodeGen *codegen, bool verbose);24void codegen_set_verbose(CodeGen *codegen, bool verbose);
src/main.cpp+30-5
...@@ -17,6 +17,7 @@ static int usage(const char *arg0) {...@@ -17,6 +17,7 @@ static int usage(const char *arg0) {
17 fprintf(stderr, "Usage: %s [command] [options]\n"17 fprintf(stderr, "Usage: %s [command] [options]\n"
18 "Commands:\n"18 "Commands:\n"
19 " build create executable, object, or library from target\n"19 " build create executable, object, or library from target\n"
20 " test create and run a test build\n"
20 " version print version number and exit\n"21 " version print version number and exit\n"
21 " parseh convert a c header file to zig extern declarations\n"22 " parseh convert a c header file to zig extern declarations\n"
22 "Options:\n"23 "Options:\n"
...@@ -40,6 +41,7 @@ static int usage(const char *arg0) {...@@ -40,6 +41,7 @@ static int usage(const char *arg0) {
40enum Cmd {41enum Cmd {
41 CmdInvalid,42 CmdInvalid,
42 CmdBuild,43 CmdBuild,
44 CmdTest,
43 CmdVersion,45 CmdVersion,
44 CmdParseH,46 CmdParseH,
45};47};
...@@ -49,7 +51,7 @@ int main(int argc, char **argv) {...@@ -49,7 +51,7 @@ int main(int argc, char **argv) {
49 Cmd cmd = CmdInvalid;51 Cmd cmd = CmdInvalid;
50 const char *in_file = nullptr;52 const char *in_file = nullptr;
51 const char *out_file = nullptr;53 const char *out_file = nullptr;
52 bool release = false;54 bool is_release_build = false;
53 bool strip = false;55 bool strip = false;
54 bool is_static = false;56 bool is_static = false;
55 OutType out_type = OutTypeUnknown;57 OutType out_type = OutTypeUnknown;
...@@ -67,7 +69,7 @@ int main(int argc, char **argv) {...@@ -67,7 +69,7 @@ int main(int argc, char **argv) {
6769
68 if (arg[0] == '-') {70 if (arg[0] == '-') {
69 if (strcmp(arg, "--release") == 0) {71 if (strcmp(arg, "--release") == 0) {
70 release = true;72 is_release_build = true;
71 } else if (strcmp(arg, "--strip") == 0) {73 } else if (strcmp(arg, "--strip") == 0) {
72 strip = true;74 strip = true;
73 } else if (strcmp(arg, "--static") == 0) {75 } else if (strcmp(arg, "--static") == 0) {
...@@ -127,6 +129,8 @@ int main(int argc, char **argv) {...@@ -127,6 +129,8 @@ int main(int argc, char **argv) {
127 cmd = CmdVersion;129 cmd = CmdVersion;
128 } else if (strcmp(arg, "parseh") == 0) {130 } else if (strcmp(arg, "parseh") == 0) {
129 cmd = CmdParseH;131 cmd = CmdParseH;
132 } else if (strcmp(arg, "test") == 0) {
133 cmd = CmdTest;
130 } else {134 } else {
131 fprintf(stderr, "Unrecognized command: %s\n", arg);135 fprintf(stderr, "Unrecognized command: %s\n", arg);
132 return usage(arg0);136 return usage(arg0);
...@@ -135,6 +139,7 @@ int main(int argc, char **argv) {...@@ -135,6 +139,7 @@ int main(int argc, char **argv) {
135 switch (cmd) {139 switch (cmd) {
136 case CmdBuild:140 case CmdBuild:
137 case CmdParseH:141 case CmdParseH:
142 case CmdTest:
138 if (!in_file) {143 if (!in_file) {
139 in_file = arg;144 in_file = arg;
140 } else {145 } else {
...@@ -152,6 +157,7 @@ int main(int argc, char **argv) {...@@ -152,6 +157,7 @@ int main(int argc, char **argv) {
152 switch (cmd) {157 switch (cmd) {
153 case CmdBuild:158 case CmdBuild:
154 case CmdParseH:159 case CmdParseH:
160 case CmdTest:
155 {161 {
156 if (!in_file)162 if (!in_file)
157 return usage(arg0);163 return usage(arg0);
...@@ -178,14 +184,22 @@ int main(int argc, char **argv) {...@@ -178,14 +184,22 @@ int main(int argc, char **argv) {
178 }184 }
179185
180 CodeGen *g = codegen_create(&root_source_dir);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 codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);190 codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
183 codegen_set_strip(g, strip);191 codegen_set_strip(g, strip);
184 codegen_set_is_static(g, is_static);192 codegen_set_is_static(g, is_static);
185 if (out_type != OutTypeUnknown)193 if (out_type != OutTypeUnknown) {
186 codegen_set_out_type(g, out_type);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 codegen_set_out_name(g, buf_create_from_str(out_name));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 if (libc_lib_dir)203 if (libc_lib_dir)
190 codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir));204 codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir));
191 if (libc_include_dir)205 if (libc_include_dir)
...@@ -205,6 +219,17 @@ int main(int argc, char **argv) {...@@ -205,6 +219,17 @@ int main(int argc, char **argv) {
205 codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code);219 codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code);
206 codegen_render_ast(g, stdout, 4);220 codegen_render_ast(g, stdout, 4);
207 return EXIT_SUCCESS;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 } else {233 } else {
209 zig_unreachable();234 zig_unreachable();
210 }235 }
src/os.cpp+14-15
...@@ -17,25 +17,24 @@...@@ -17,25 +17,24 @@
17#include <fcntl.h>17#include <fcntl.h>
18#include <limits.h>18#include <limits.h>
1919
20void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) {20void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code) {
21 pid_t pid = fork();21 pid_t pid = fork();
22 if (pid == -1)22 if (pid == -1)
23 zig_panic("fork failed");23 zig_panic("fork failed");
24 if (pid != 0)24 if (pid == 0) {
25 return;25 // child
26 if (detached) {26 const char **argv = allocate<const char *>(args.length + 2);
27 if (setsid() == -1)27 argv[0] = exe;
28 zig_panic("process detach failed");28 argv[args.length + 1] = nullptr;
29 }29 for (int i = 0; i < args.length; i += 1) {
3030 argv[i + 1] = args.at(i);
31 const char **argv = allocate<const char *>(args.length + 2);31 }
32 argv[0] = exe;32 execvp(exe, const_cast<char * const *>(argv));
33 argv[args.length + 1] = nullptr;33 zig_panic("execvp failed: %s", strerror(errno));
34 for (int i = 0; i < args.length; i += 1) {34 } else {
35 argv[i + 1] = args.at(i);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}
4039
41static int read_all_fd_stream(int fd, Buf *out_buf) {40static int read_all_fd_stream(int fd, Buf *out_buf) {
src/os.hpp+1-1
...@@ -13,7 +13,7 @@...@@ -13,7 +13,7 @@
1313
14#include <stdio.h>14#include <stdio.h>
1515
16void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached);16void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code);
17void os_exec_process(const char *exe, ZigList<const char *> &args,17void os_exec_process(const char *exe, ZigList<const char *> &args,
18 int *return_code, Buf *out_stderr, Buf *out_stdout);18 int *return_code, Buf *out_stderr, Buf *out_stdout);
1919
std/test_runner.zig created+45
...@@ -0,0 +1,45 @@
1import "std.zig";
2
3/*
4struct TestFn {
5 name: []u8,
6 func: extern fn(),
7}
8
9extern var test_fn_list: []TestFn;
10*/
11
12extern var zig_test_fn_count: isize;
13
14// TODO make this a slice of structs
15extern var zig_test_fn_list: [99999999]extern fn();
16
17pub 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}