| 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,6 +122,7 @@ set(C_HEADERS |
| 122 | set(ZIG_STD_SRC | 122 | set(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 | }; |
| 38 | 38 | ||
| 39 | enum CodeGenBuildType { | ||
| 40 | CodeGenBuildTypeDebug, | ||
| 41 | CodeGenBuildTypeRelease, | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct ConstEnumValue { | 39 | struct 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 it | 982 | uint32_t ref_count; // if this is 0 we don't have to codegen it |
| 987 | 983 | ||
| 988 | // reminder: hash tables must be initialized before use | 984 | // 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; |
| 1091 | 1088 | ||
| 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 | }; |
| 1120 | 1121 | ||
| 1121 | struct VariableTableEntry { | 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,6 +781,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 781 | 781 | ||
| 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; | ||
| 784 | 785 | ||
| 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); |
| 814 | 818 | ||
| 815 | fn_table_entry->type_entry = fn_type; | 819 | fn_table_entry->type_entry = fn_type; |
| 820 | fn_table_entry->is_test = is_test; | ||
| 816 | 821 | ||
| 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, |
| 1247 | 1252 | ||
| 1248 | fn_table->put(proto_name, fn_table_entry); | 1253 | fn_table->put(proto_name, fn_table_entry); |
| 1249 | 1254 | ||
| 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 | } |
| 1256 | 1269 | ||
| ... | @@ -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 | } |
| 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 | 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 | } |
| 1929 | 1943 | ||
| 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; |
| 4754 | 4770 | ||
| 4755 | add_global_const_expr(g, expr); | 4771 | add_global_const_expr(g, node); |
| 4756 | 4772 | ||
| 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 | } |
| 46 | 47 | ||
| 47 | void codegen_set_build_type(CodeGen *g, CodeGenBuildType build_type) { | 48 | void 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 | |||
| 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 | void codegen_set_is_static(CodeGen *g, bool is_static) { | 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,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; |
| 1005 | 1010 | ||
| 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; |
| 1046 | 1051 | ||
| 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 |
| 2645 | 2650 | ||
| 2646 | static void gen_const_globals(CodeGen *g) { | 2651 | static 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 | } |
| 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 | static void do_code_gen(CodeGen *g) { | 2707 | static void do_code_gen(CodeGen *g) { |
| 2684 | assert(!g->errors.length); | 2708 | assert(!g->errors.length); |
| 2685 | 2709 | ||
| ... | @@ -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 | } |
| 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 | // Generate function prototypes | 2764 | // 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 saver | 2768 | // 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 | } |
| 2742 | 2773 | ||
| ... | @@ -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 | } |
| 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 | // 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 saver | 2857 | // 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(); |
| 3299 | 3362 | ||
| 3300 | LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ? | 3363 | LLVMCodeGenOptLevel opt_level = g->is_release_build ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone; |
| 3301 | LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive; | ||
| 3302 | 3364 | ||
| 3303 | LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; | 3365 | LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; |
| 3304 | 3366 | ||
| ... | @@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) { | ... | @@ -3321,7 +3383,7 @@ static void init(CodeGen *g, Buf *source_path) { |
| 3321 | 3383 | ||
| 3322 | 3384 | ||
| 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 | } |
| 3613 | 3675 | ||
| 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 | } |
| 3623 | 3681 | ||
| 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 | } |
| 3678 | 3736 | ||
| 3679 | static void generate_h_file(CodeGen *g) { | 3737 | static 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 | } |
| 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 | void codegen_link(CodeGen *g, const char *out_file) { | 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 | 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 | } |
| 3789 | 3879 | ||
| 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 | } |
| 3793 | 3884 | ||
| ... | @@ -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 | } |
| 3823 | 3914 | ||
| 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 | } |
| 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 | 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 @@ |
| 16 | CodeGen *codegen_create(Buf *root_source_dir); | 16 | CodeGen *codegen_create(Buf *root_source_dir); |
| 17 | 17 | ||
| 18 | void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len); | 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 | void codegen_set_is_static(CodeGen *codegen, bool is_static); | 22 | void codegen_set_is_static(CodeGen *codegen, bool is_static); |
| 21 | void codegen_set_strip(CodeGen *codegen, bool strip); | 23 | void codegen_set_strip(CodeGen *codegen, bool strip); |
| 22 | void codegen_set_verbose(CodeGen *codegen, bool verbose); | 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,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) { |
| 40 | enum Cmd { | 41 | enum 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) { |
| 67 | 69 | ||
| 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 | } |
| 179 | 185 | ||
| 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> |
| 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 | 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) { |
| 30 | 30 | 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 | } |
| 40 | 39 | ||
| 41 | static int read_all_fd_stream(int fd, Buf *out_buf) { | 40 | static int read_all_fd_stream(int fd, Buf *out_buf) { |
src/os.hpp+1-1| ... | @@ -13,7 +13,7 @@ | ... | @@ -13,7 +13,7 @@ |
| 13 | 13 | ||
| 14 | #include <stdio.h> | 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 | void os_exec_process(const char *exe, ZigList<const char *> &args, | 17 | void 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); |
| 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 | } | ||