| author | |
| committer | |
| log | eca2aa66feae60e1e11e482b31443692aa3002ef |
| tree | f1a9609e9db26c688ced5eae2ae87d5f0a3717c0 |
| parent | 7640bec8e0e735eaba49087840b7f6e6b2dd74ab |
| parent | 39d47b2c51d43d98f414ed5d5622c9e582ec9aa6 |
| signature |
add -fdump-analysis to dump type information to json11 files changed, 845 insertions(+), 145 deletions(-)
CMakeLists.txt+1-1| ... | @@ -443,6 +443,7 @@ set(ZIG_SOURCES | ... | @@ -443,6 +443,7 @@ set(ZIG_SOURCES |
| 443 | "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" | 443 | "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" |
| 444 | "${CMAKE_SOURCE_DIR}/src/codegen.cpp" | 444 | "${CMAKE_SOURCE_DIR}/src/codegen.cpp" |
| 445 | "${CMAKE_SOURCE_DIR}/src/compiler.cpp" | 445 | "${CMAKE_SOURCE_DIR}/src/compiler.cpp" |
| 446 | "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp" | ||
| 446 | "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" | 447 | "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" |
| 447 | "${CMAKE_SOURCE_DIR}/src/error.cpp" | 448 | "${CMAKE_SOURCE_DIR}/src/error.cpp" |
| 448 | "${CMAKE_SOURCE_DIR}/src/glibc.cpp" | 449 | "${CMAKE_SOURCE_DIR}/src/glibc.cpp" |
| ... | @@ -453,7 +454,6 @@ set(ZIG_SOURCES | ... | @@ -453,7 +454,6 @@ set(ZIG_SOURCES |
| 453 | "${CMAKE_SOURCE_DIR}/src/os.cpp" | 454 | "${CMAKE_SOURCE_DIR}/src/os.cpp" |
| 454 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" | 455 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" |
| 455 | "${CMAKE_SOURCE_DIR}/src/range_set.cpp" | 456 | "${CMAKE_SOURCE_DIR}/src/range_set.cpp" |
| 456 | "${CMAKE_SOURCE_DIR}/src/stack_report.cpp" | ||
| 457 | "${CMAKE_SOURCE_DIR}/src/target.cpp" | 457 | "${CMAKE_SOURCE_DIR}/src/target.cpp" |
| 458 | "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" | 458 | "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" |
| 459 | "${CMAKE_SOURCE_DIR}/src/translate_c.cpp" | 459 | "${CMAKE_SOURCE_DIR}/src/translate_c.cpp" |
src/all_types.hpp+7| ... | @@ -1300,6 +1300,12 @@ struct ZigTypeEnum { | ... | @@ -1300,6 +1300,12 @@ struct ZigTypeEnum { |
| 1300 | uint32_t type_ptr_hash(const ZigType *ptr); | 1300 | uint32_t type_ptr_hash(const ZigType *ptr); |
| 1301 | bool type_ptr_eql(const ZigType *a, const ZigType *b); | 1301 | bool type_ptr_eql(const ZigType *a, const ZigType *b); |
| 1302 | 1302 | ||
| 1303 | uint32_t pkg_ptr_hash(const ZigPackage *ptr); | ||
| 1304 | bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b); | ||
| 1305 | |||
| 1306 | uint32_t tld_ptr_hash(const Tld *ptr); | ||
| 1307 | bool tld_ptr_eql(const Tld *a, const Tld *b); | ||
| 1308 | |||
| 1303 | struct ZigTypeUnion { | 1309 | struct ZigTypeUnion { |
| 1304 | AstNode *decl_node; | 1310 | AstNode *decl_node; |
| 1305 | TypeUnionField *fields; | 1311 | TypeUnionField *fields; |
| ... | @@ -2056,6 +2062,7 @@ struct CodeGen { | ... | @@ -2056,6 +2062,7 @@ struct CodeGen { |
| 2056 | bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic | 2062 | bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic |
| 2057 | bool have_stack_probing; | 2063 | bool have_stack_probing; |
| 2058 | bool function_sections; | 2064 | bool function_sections; |
| 2065 | bool enable_dump_analysis; | ||
| 2059 | 2066 | ||
| 2060 | Buf *mmacosx_version_min; | 2067 | Buf *mmacosx_version_min; |
| 2061 | Buf *mios_version_min; | 2068 | Buf *mios_version_min; |
src/analyze.cpp+16| ... | @@ -7303,6 +7303,22 @@ bool type_ptr_eql(const ZigType *a, const ZigType *b) { | ... | @@ -7303,6 +7303,22 @@ bool type_ptr_eql(const ZigType *a, const ZigType *b) { |
| 7303 | return a == b; | 7303 | return a == b; |
| 7304 | } | 7304 | } |
| 7305 | 7305 | ||
| 7306 | uint32_t pkg_ptr_hash(const ZigPackage *ptr) { | ||
| 7307 | return hash_ptr((void*)ptr); | ||
| 7308 | } | ||
| 7309 | |||
| 7310 | bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) { | ||
| 7311 | return a == b; | ||
| 7312 | } | ||
| 7313 | |||
| 7314 | uint32_t tld_ptr_hash(const Tld *ptr) { | ||
| 7315 | return hash_ptr((void*)ptr); | ||
| 7316 | } | ||
| 7317 | |||
| 7318 | bool tld_ptr_eql(const Tld *a, const Tld *b) { | ||
| 7319 | return a == b; | ||
| 7320 | } | ||
| 7321 | |||
| 7306 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { | 7322 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { |
| 7307 | Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name)); | 7323 | Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name)); |
| 7308 | resolve_top_level_decl(codegen, tld, nullptr, false); | 7324 | resolve_top_level_decl(codegen, tld, nullptr, false); |
src/codegen.cpp+23-6| ... | @@ -20,6 +20,7 @@ | ... | @@ -20,6 +20,7 @@ |
| 20 | #include "util.hpp" | 20 | #include "util.hpp" |
| 21 | #include "zig_llvm.h" | 21 | #include "zig_llvm.h" |
| 22 | #include "userland.h" | 22 | #include "userland.h" |
| 23 | #include "dump_analysis.hpp" | ||
| 23 | 24 | ||
| 24 | #include <stdio.h> | 25 | #include <stdio.h> |
| 25 | #include <errno.h> | 26 | #include <errno.h> |
| ... | @@ -1724,7 +1725,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { | ... | @@ -1724,7 +1725,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { |
| 1724 | } | 1725 | } |
| 1725 | 1726 | ||
| 1726 | ATTRIBUTE_NORETURN | 1727 | ATTRIBUTE_NORETURN |
| 1727 | static void report_errors_and_exit(CodeGen *g) { | 1728 | void codegen_report_errors_and_exit(CodeGen *g) { |
| 1728 | assert(g->errors.length != 0); | 1729 | assert(g->errors.length != 0); |
| 1729 | for (size_t i = 0; i < g->errors.length; i += 1) { | 1730 | for (size_t i = 0; i < g->errors.length; i += 1) { |
| 1730 | ErrorMsg *err = g->errors.at(i); | 1731 | ErrorMsg *err = g->errors.at(i); |
| ... | @@ -1735,7 +1736,7 @@ static void report_errors_and_exit(CodeGen *g) { | ... | @@ -1735,7 +1736,7 @@ static void report_errors_and_exit(CodeGen *g) { |
| 1735 | 1736 | ||
| 1736 | static void report_errors_and_maybe_exit(CodeGen *g) { | 1737 | static void report_errors_and_maybe_exit(CodeGen *g) { |
| 1737 | if (g->errors.length != 0) { | 1738 | if (g->errors.length != 0) { |
| 1738 | report_errors_and_exit(g); | 1739 | codegen_report_errors_and_exit(g); |
| 1739 | } | 1740 | } |
| 1740 | } | 1741 | } |
| 1741 | 1742 | ||
| ... | @@ -1745,7 +1746,7 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { | ... | @@ -1745,7 +1746,7 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { |
| 1745 | buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); | 1746 | buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); |
| 1746 | add_error_note(g, msg, source_node, | 1747 | add_error_note(g, msg, source_node, |
| 1747 | buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); | 1748 | buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); |
| 1748 | report_errors_and_exit(g); | 1749 | codegen_report_errors_and_exit(g); |
| 1749 | } | 1750 | } |
| 1750 | 1751 | ||
| 1751 | static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { | 1752 | static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { |
| ... | @@ -3456,7 +3457,7 @@ static bool value_is_all_undef(CodeGen *g, ConstExprValue *const_val) { | ... | @@ -3456,7 +3457,7 @@ static bool value_is_all_undef(CodeGen *g, ConstExprValue *const_val) { |
| 3456 | Error err; | 3457 | Error err; |
| 3457 | if (const_val->special == ConstValSpecialLazy && | 3458 | if (const_val->special == ConstValSpecialLazy && |
| 3458 | (err = ir_resolve_lazy(g, nullptr, const_val))) | 3459 | (err = ir_resolve_lazy(g, nullptr, const_val))) |
| 3459 | report_errors_and_exit(g); | 3460 | codegen_report_errors_and_exit(g); |
| 3460 | 3461 | ||
| 3461 | switch (const_val->special) { | 3462 | switch (const_val->special) { |
| 3462 | case ConstValSpecialLazy: | 3463 | case ConstValSpecialLazy: |
| ... | @@ -4253,7 +4254,7 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa | ... | @@ -4253,7 +4254,7 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa |
| 4253 | ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ? | 4254 | ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ? |
| 4254 | struct_ptr_type->data.pointer.child_type : struct_ptr_type; | 4255 | struct_ptr_type->data.pointer.child_type : struct_ptr_type; |
| 4255 | if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) | 4256 | if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) |
| 4256 | report_errors_and_exit(g); | 4257 | codegen_report_errors_and_exit(g); |
| 4257 | 4258 | ||
| 4258 | assert(field->gen_index != SIZE_MAX); | 4259 | assert(field->gen_index != SIZE_MAX); |
| 4259 | return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); | 4260 | return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); |
| ... | @@ -6625,7 +6626,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c | ... | @@ -6625,7 +6626,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 6625 | check: switch (const_val->special) { | 6626 | check: switch (const_val->special) { |
| 6626 | case ConstValSpecialLazy: | 6627 | case ConstValSpecialLazy: |
| 6627 | if ((err = ir_resolve_lazy(g, nullptr, const_val))) { | 6628 | if ((err = ir_resolve_lazy(g, nullptr, const_val))) { |
| 6628 | report_errors_and_exit(g); | 6629 | codegen_report_errors_and_exit(g); |
| 6629 | } | 6630 | } |
| 6630 | goto check; | 6631 | goto check; |
| 6631 | case ConstValSpecialRuntime: | 6632 | case ConstValSpecialRuntime: |
| ... | @@ -10157,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { | ... | @@ -10157,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { |
| 10157 | cache_bool(ch, g->have_stack_probing); | 10158 | cache_bool(ch, g->have_stack_probing); |
| 10158 | cache_bool(ch, g->is_dummy_so); | 10159 | cache_bool(ch, g->is_dummy_so); |
| 10159 | cache_bool(ch, g->function_sections); | 10160 | cache_bool(ch, g->function_sections); |
| 10161 | cache_bool(ch, g->enable_dump_analysis); | ||
| 10160 | cache_buf_opt(ch, g->mmacosx_version_min); | 10162 | cache_buf_opt(ch, g->mmacosx_version_min); |
| 10161 | cache_buf_opt(ch, g->mios_version_min); | 10163 | cache_buf_opt(ch, g->mios_version_min); |
| 10162 | cache_usize(ch, g->version_major); | 10164 | cache_usize(ch, g->version_major); |
| ... | @@ -10338,6 +10340,21 @@ void codegen_build_and_link(CodeGen *g) { | ... | @@ -10338,6 +10340,21 @@ void codegen_build_and_link(CodeGen *g) { |
| 10338 | gen_h_file(g); | 10340 | gen_h_file(g); |
| 10339 | } | 10341 | } |
| 10340 | } | 10342 | } |
| 10343 | if (g->enable_dump_analysis) { | ||
| 10344 | const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", | ||
| 10345 | buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); | ||
| 10346 | FILE *f = fopen(analysis_json_filename, "wb"); | ||
| 10347 | if (f == nullptr) { | ||
| 10348 | fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); | ||
| 10349 | exit(1); | ||
| 10350 | } | ||
| 10351 | zig_print_analysis_dump(g, f); | ||
| 10352 | if (fclose(f) != 0) { | ||
| 10353 | fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); | ||
| 10354 | exit(1); | ||
| 10355 | } | ||
| 10356 | |||
| 10357 | } | ||
| 10341 | 10358 | ||
| 10342 | // If we're outputting assembly or llvm IR we skip linking. | 10359 | // If we're outputting assembly or llvm IR we skip linking. |
| 10343 | // If we're making a library or executable we must link. | 10360 | // If we're making a library or executable we must link. |
src/codegen.hpp+2| ... | @@ -64,4 +64,6 @@ void codegen_release_caches(CodeGen *codegen); | ... | @@ -64,4 +64,6 @@ void codegen_release_caches(CodeGen *codegen); |
| 64 | bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); | 64 | bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); |
| 65 | bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); | 65 | bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); |
| 66 | 66 | ||
| 67 | void codegen_report_errors_and_exit(CodeGen *g); | ||
| 68 | |||
| 67 | #endif | 69 | #endif |
src/dump_analysis.cpp created+771| ... | @@ -0,0 +1,771 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2019 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "dump_analysis.hpp" | ||
| 9 | #include "compiler.hpp" | ||
| 10 | #include "analyze.hpp" | ||
| 11 | #include "config.h" | ||
| 12 | #include "ir.hpp" | ||
| 13 | #include "codegen.hpp" | ||
| 14 | |||
| 15 | enum JsonWriterState { | ||
| 16 | JsonWriterStateInvalid, | ||
| 17 | JsonWriterStateValue, | ||
| 18 | JsonWriterStateArrayStart, | ||
| 19 | JsonWriterStateArray, | ||
| 20 | JsonWriterStateObjectStart, | ||
| 21 | JsonWriterStateObject, | ||
| 22 | }; | ||
| 23 | |||
| 24 | #define JSON_MAX_DEPTH 10 | ||
| 25 | |||
| 26 | struct JsonWriter { | ||
| 27 | size_t state_index; | ||
| 28 | FILE *f; | ||
| 29 | const char *one_indent; | ||
| 30 | const char *nl; | ||
| 31 | JsonWriterState state[JSON_MAX_DEPTH]; | ||
| 32 | }; | ||
| 33 | |||
| 34 | static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { | ||
| 35 | jw->state_index = 1; | ||
| 36 | jw->f = f; | ||
| 37 | jw->one_indent = one_indent; | ||
| 38 | jw->nl = nl; | ||
| 39 | jw->state[0] = JsonWriterStateInvalid; | ||
| 40 | jw->state[1] = JsonWriterStateValue; | ||
| 41 | } | ||
| 42 | |||
| 43 | static void jw_nl_indent(JsonWriter *jw) { | ||
| 44 | assert(jw->state_index >= 1); | ||
| 45 | fprintf(jw->f, "%s", jw->nl); | ||
| 46 | for (size_t i = 0; i < jw->state_index - 1; i += 1) { | ||
| 47 | fprintf(jw->f, jw->one_indent); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | static void jw_push_state(JsonWriter *jw, JsonWriterState state) { | ||
| 52 | jw->state_index += 1; | ||
| 53 | assert(jw->state_index < JSON_MAX_DEPTH); | ||
| 54 | jw->state[jw->state_index] = state; | ||
| 55 | } | ||
| 56 | |||
| 57 | static void jw_pop_state(JsonWriter *jw) { | ||
| 58 | assert(jw->state_index != 0); | ||
| 59 | jw->state_index -= 1; | ||
| 60 | } | ||
| 61 | |||
| 62 | static void jw_begin_array(JsonWriter *jw) { | ||
| 63 | assert(jw->state[jw->state_index] == JsonWriterStateValue); | ||
| 64 | fprintf(jw->f, "["); | ||
| 65 | jw->state[jw->state_index] = JsonWriterStateArrayStart; | ||
| 66 | } | ||
| 67 | |||
| 68 | static void jw_begin_object(JsonWriter *jw) { | ||
| 69 | assert(jw->state[jw->state_index] == JsonWriterStateValue); | ||
| 70 | fprintf(jw->f, "{"); | ||
| 71 | jw->state[jw->state_index] = JsonWriterStateObjectStart; | ||
| 72 | } | ||
| 73 | |||
| 74 | static void jw_array_elem(JsonWriter *jw) { | ||
| 75 | switch (jw->state[jw->state_index]) { | ||
| 76 | case JsonWriterStateInvalid: | ||
| 77 | case JsonWriterStateValue: | ||
| 78 | case JsonWriterStateObjectStart: | ||
| 79 | case JsonWriterStateObject: | ||
| 80 | zig_unreachable(); | ||
| 81 | case JsonWriterStateArray: | ||
| 82 | fprintf(jw->f, ","); | ||
| 83 | // fallthrough | ||
| 84 | case JsonWriterStateArrayStart: | ||
| 85 | jw->state[jw->state_index] = JsonWriterStateArray; | ||
| 86 | jw_push_state(jw, JsonWriterStateValue); | ||
| 87 | jw_nl_indent(jw); | ||
| 88 | return; | ||
| 89 | } | ||
| 90 | zig_unreachable(); | ||
| 91 | } | ||
| 92 | |||
| 93 | static void jw_write_escaped_string(JsonWriter *jw, const char *s) { | ||
| 94 | fprintf(jw->f, "\""); | ||
| 95 | for (;; s += 1) { | ||
| 96 | switch (*s) { | ||
| 97 | case 0: | ||
| 98 | fprintf(jw->f, "\""); | ||
| 99 | return; | ||
| 100 | case '"': | ||
| 101 | fprintf(jw->f, "\\\""); | ||
| 102 | continue; | ||
| 103 | case '\t': | ||
| 104 | fprintf(jw->f, "\\t"); | ||
| 105 | continue; | ||
| 106 | case '\r': | ||
| 107 | fprintf(jw->f, "\\r"); | ||
| 108 | continue; | ||
| 109 | case '\n': | ||
| 110 | fprintf(jw->f, "\\n"); | ||
| 111 | continue; | ||
| 112 | case '\b': | ||
| 113 | fprintf(jw->f, "\\b"); | ||
| 114 | continue; | ||
| 115 | case '\f': | ||
| 116 | fprintf(jw->f, "\\f"); | ||
| 117 | continue; | ||
| 118 | case '\\': | ||
| 119 | fprintf(jw->f, "\\\\"); | ||
| 120 | continue; | ||
| 121 | default: | ||
| 122 | fprintf(jw->f, "%c", *s); | ||
| 123 | continue; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | static void jw_object_field(JsonWriter *jw, const char *name) { | ||
| 129 | switch (jw->state[jw->state_index]) { | ||
| 130 | case JsonWriterStateInvalid: | ||
| 131 | case JsonWriterStateValue: | ||
| 132 | case JsonWriterStateArray: | ||
| 133 | case JsonWriterStateArrayStart: | ||
| 134 | zig_unreachable(); | ||
| 135 | case JsonWriterStateObject: | ||
| 136 | fprintf(jw->f, ","); | ||
| 137 | // fallthrough | ||
| 138 | case JsonWriterStateObjectStart: | ||
| 139 | jw->state[jw->state_index] = JsonWriterStateObject; | ||
| 140 | jw_push_state(jw, JsonWriterStateValue); | ||
| 141 | jw_nl_indent(jw); | ||
| 142 | jw_write_escaped_string(jw, name); | ||
| 143 | fprintf(jw->f, ": "); | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | zig_unreachable(); | ||
| 147 | } | ||
| 148 | |||
| 149 | static void jw_end_array(JsonWriter *jw) { | ||
| 150 | switch (jw->state[jw->state_index]) { | ||
| 151 | case JsonWriterStateInvalid: | ||
| 152 | case JsonWriterStateValue: | ||
| 153 | case JsonWriterStateObjectStart: | ||
| 154 | case JsonWriterStateObject: | ||
| 155 | zig_unreachable(); | ||
| 156 | case JsonWriterStateArrayStart: | ||
| 157 | fprintf(jw->f, "]"); | ||
| 158 | jw_pop_state(jw); | ||
| 159 | return; | ||
| 160 | case JsonWriterStateArray: | ||
| 161 | jw_nl_indent(jw); | ||
| 162 | jw_pop_state(jw); | ||
| 163 | fprintf(jw->f, "]"); | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | zig_unreachable(); | ||
| 167 | } | ||
| 168 | |||
| 169 | |||
| 170 | static void jw_end_object(JsonWriter *jw) { | ||
| 171 | switch (jw->state[jw->state_index]) { | ||
| 172 | case JsonWriterStateInvalid: | ||
| 173 | zig_unreachable(); | ||
| 174 | case JsonWriterStateValue: | ||
| 175 | zig_unreachable(); | ||
| 176 | case JsonWriterStateArray: | ||
| 177 | zig_unreachable(); | ||
| 178 | case JsonWriterStateArrayStart: | ||
| 179 | zig_unreachable(); | ||
| 180 | case JsonWriterStateObjectStart: | ||
| 181 | fprintf(jw->f, "}"); | ||
| 182 | jw_pop_state(jw); | ||
| 183 | return; | ||
| 184 | case JsonWriterStateObject: | ||
| 185 | jw_nl_indent(jw); | ||
| 186 | jw_pop_state(jw); | ||
| 187 | fprintf(jw->f, "}"); | ||
| 188 | return; | ||
| 189 | } | ||
| 190 | zig_unreachable(); | ||
| 191 | } | ||
| 192 | |||
| 193 | static void jw_null(JsonWriter *jw) { | ||
| 194 | assert(jw->state[jw->state_index] == JsonWriterStateValue); | ||
| 195 | fprintf(jw->f, "null"); | ||
| 196 | jw_pop_state(jw); | ||
| 197 | } | ||
| 198 | |||
| 199 | static void jw_bool(JsonWriter *jw, bool x) { | ||
| 200 | assert(jw->state[jw->state_index] == JsonWriterStateValue); | ||
| 201 | if (x) { | ||
| 202 | fprintf(jw->f, "true"); | ||
| 203 | } else { | ||
| 204 | fprintf(jw->f, "false"); | ||
| 205 | } | ||
| 206 | jw_pop_state(jw); | ||
| 207 | } | ||
| 208 | |||
| 209 | static void jw_int(JsonWriter *jw, int64_t x) { | ||
| 210 | assert(jw->state[jw->state_index] == JsonWriterStateValue); | ||
| 211 | if (x > 4503599627370496 || x < -4503599627370496) { | ||
| 212 | fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); | ||
| 213 | } else { | ||
| 214 | fprintf(jw->f, "%" ZIG_PRI_i64, x); | ||
| 215 | } | ||
| 216 | jw_pop_state(jw); | ||
| 217 | } | ||
| 218 | |||
| 219 | static void jw_string(JsonWriter *jw, const char *s) { | ||
| 220 | assert(jw->state[jw->state_index] == JsonWriterStateValue); | ||
| 221 | jw_write_escaped_string(jw, s); | ||
| 222 | jw_pop_state(jw); | ||
| 223 | } | ||
| 224 | |||
| 225 | |||
| 226 | static void tree_print(FILE *f, ZigType *ty, size_t indent); | ||
| 227 | |||
| 228 | static void pretty_print_bytes(FILE *f, double n) { | ||
| 229 | if (n > 1024.0 * 1024.0 * 1024.0) { | ||
| 230 | fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); | ||
| 231 | return; | ||
| 232 | } | ||
| 233 | if (n > 1024.0 * 1024.0) { | ||
| 234 | fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | if (n > 1024.0) { | ||
| 238 | fprintf(f, "%.02f KiB", n / 1024.0); | ||
| 239 | return; | ||
| 240 | } | ||
| 241 | fprintf(f, "%.02f bytes", n ); | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int compare_type_abi_sizes_desc(const void *a, const void *b) { | ||
| 246 | uint64_t size_a = (*(ZigType * const*)(a))->abi_size; | ||
| 247 | uint64_t size_b = (*(ZigType * const*)(b))->abi_size; | ||
| 248 | if (size_a > size_b) | ||
| 249 | return -1; | ||
| 250 | if (size_a < size_b) | ||
| 251 | return 1; | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | static void start_child(FILE *f, size_t indent) { | ||
| 256 | fprintf(f, "\n"); | ||
| 257 | for (size_t i = 0; i < indent; i += 1) { | ||
| 258 | fprintf(f, " "); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | static void start_peer(FILE *f, size_t indent) { | ||
| 263 | fprintf(f, ",\n"); | ||
| 264 | for (size_t i = 0; i < indent; i += 1) { | ||
| 265 | fprintf(f, " "); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { | ||
| 270 | ZigList<ZigType *> children = {}; | ||
| 271 | uint64_t sum_from_fields = 0; | ||
| 272 | for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { | ||
| 273 | TypeStructField *field = &struct_type->data.structure.fields[i]; | ||
| 274 | children.append(field->type_entry); | ||
| 275 | sum_from_fields += field->type_entry->abi_size; | ||
| 276 | } | ||
| 277 | qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); | ||
| 278 | |||
| 279 | start_peer(f, indent); | ||
| 280 | fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); | ||
| 281 | |||
| 282 | start_peer(f, indent); | ||
| 283 | fprintf(f, "\"fields\": ["); | ||
| 284 | |||
| 285 | for (size_t i = 0; i < children.length; i += 1) { | ||
| 286 | if (i == 0) { | ||
| 287 | start_child(f, indent + 1); | ||
| 288 | } else { | ||
| 289 | start_peer(f, indent + 1); | ||
| 290 | } | ||
| 291 | fprintf(f, "{"); | ||
| 292 | |||
| 293 | ZigType *child_type = children.at(i); | ||
| 294 | tree_print(f, child_type, indent + 2); | ||
| 295 | |||
| 296 | start_child(f, indent + 1); | ||
| 297 | fprintf(f, "}"); | ||
| 298 | } | ||
| 299 | |||
| 300 | start_child(f, indent); | ||
| 301 | fprintf(f, "]"); | ||
| 302 | } | ||
| 303 | |||
| 304 | static void tree_print(FILE *f, ZigType *ty, size_t indent) { | ||
| 305 | start_child(f, indent); | ||
| 306 | fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); | ||
| 307 | |||
| 308 | start_peer(f, indent); | ||
| 309 | fprintf(f, "\"sizef\": \""); | ||
| 310 | pretty_print_bytes(f, ty->abi_size); | ||
| 311 | fprintf(f, "\""); | ||
| 312 | |||
| 313 | start_peer(f, indent); | ||
| 314 | fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); | ||
| 315 | |||
| 316 | switch (ty->id) { | ||
| 317 | case ZigTypeIdFnFrame: | ||
| 318 | return tree_print_struct(f, ty->data.frame.locals_struct, indent); | ||
| 319 | case ZigTypeIdStruct: | ||
| 320 | return tree_print_struct(f, ty, indent); | ||
| 321 | default: | ||
| 322 | start_child(f, indent); | ||
| 323 | return; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | void zig_print_stack_report(CodeGen *g, FILE *f) { | ||
| 328 | if (g->largest_frame_fn == nullptr) { | ||
| 329 | fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); | ||
| 330 | return; | ||
| 331 | } | ||
| 332 | fprintf(f, "{"); | ||
| 333 | tree_print(f, g->largest_frame_fn->frame_type, 1); | ||
| 334 | |||
| 335 | start_child(f, 0); | ||
| 336 | fprintf(f, "}\n"); | ||
| 337 | } | ||
| 338 | |||
| 339 | struct AnalDumpCtx { | ||
| 340 | CodeGen *g; | ||
| 341 | JsonWriter jw; | ||
| 342 | |||
| 343 | ZigList<ZigType *> type_list; | ||
| 344 | HashMap<const ZigType *, uint32_t, type_ptr_hash, type_ptr_eql> type_map; | ||
| 345 | |||
| 346 | ZigList<ZigPackage *> pkg_list; | ||
| 347 | HashMap<const ZigPackage *, uint32_t, pkg_ptr_hash, pkg_ptr_eql> pkg_map; | ||
| 348 | |||
| 349 | ZigList<Buf *> file_list; | ||
| 350 | HashMap<Buf *, uint32_t, buf_hash, buf_eql_buf> file_map; | ||
| 351 | |||
| 352 | ZigList<Tld *> decl_list; | ||
| 353 | HashMap<const Tld *, uint32_t, tld_ptr_hash, tld_ptr_eql> decl_map; | ||
| 354 | }; | ||
| 355 | |||
| 356 | static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); | ||
| 357 | static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value); | ||
| 358 | |||
| 359 | static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value) { | ||
| 360 | Error err; | ||
| 361 | if (value->type != ty) { | ||
| 362 | return; | ||
| 363 | } | ||
| 364 | if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { | ||
| 365 | codegen_report_errors_and_exit(ctx->g); | ||
| 366 | } | ||
| 367 | if (value->special == ConstValSpecialUndef) { | ||
| 368 | return; | ||
| 369 | } | ||
| 370 | if (value->special == ConstValSpecialRuntime) { | ||
| 371 | return; | ||
| 372 | } | ||
| 373 | switch (ty->id) { | ||
| 374 | case ZigTypeIdMetaType: { | ||
| 375 | ZigType *val_ty = value->data.x_type; | ||
| 376 | (void)anal_dump_get_type_id(ctx, val_ty); | ||
| 377 | return; | ||
| 378 | } | ||
| 379 | default: | ||
| 380 | return; | ||
| 381 | } | ||
| 382 | zig_unreachable(); | ||
| 383 | } | ||
| 384 | |||
| 385 | static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { | ||
| 386 | uint32_t type_id = ctx->type_list.length; | ||
| 387 | auto existing_entry = ctx->type_map.put_unique(ty, type_id); | ||
| 388 | if (existing_entry == nullptr) { | ||
| 389 | ctx->type_list.append(ty); | ||
| 390 | } else { | ||
| 391 | type_id = existing_entry->value; | ||
| 392 | } | ||
| 393 | return type_id; | ||
| 394 | } | ||
| 395 | |||
| 396 | static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { | ||
| 397 | assert(pkg != nullptr); | ||
| 398 | uint32_t pkg_id = ctx->pkg_list.length; | ||
| 399 | auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); | ||
| 400 | if (existing_entry == nullptr) { | ||
| 401 | ctx->pkg_list.append(pkg); | ||
| 402 | } else { | ||
| 403 | pkg_id = existing_entry->value; | ||
| 404 | } | ||
| 405 | return pkg_id; | ||
| 406 | } | ||
| 407 | |||
| 408 | static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { | ||
| 409 | uint32_t file_id = ctx->file_list.length; | ||
| 410 | auto existing_entry = ctx->file_map.put_unique(file, file_id); | ||
| 411 | if (existing_entry == nullptr) { | ||
| 412 | ctx->file_list.append(file); | ||
| 413 | } else { | ||
| 414 | file_id = existing_entry->value; | ||
| 415 | } | ||
| 416 | return file_id; | ||
| 417 | } | ||
| 418 | |||
| 419 | static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { | ||
| 420 | uint32_t decl_id = ctx->decl_list.length; | ||
| 421 | auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); | ||
| 422 | if (existing_entry == nullptr) { | ||
| 423 | ctx->decl_list.append(tld); | ||
| 424 | |||
| 425 | if (tld->import != nullptr) { | ||
| 426 | (void)anal_dump_get_type_id(ctx, tld->import); | ||
| 427 | } | ||
| 428 | |||
| 429 | // poke the types | ||
| 430 | switch (tld->id) { | ||
| 431 | case TldIdVar: { | ||
| 432 | TldVar *tld_var = reinterpret_cast<TldVar *>(tld); | ||
| 433 | ZigVar *var = tld_var->var; | ||
| 434 | |||
| 435 | if (var != nullptr) { | ||
| 436 | (void)anal_dump_get_type_id(ctx, var->var_type); | ||
| 437 | |||
| 438 | if (var->const_value != nullptr) { | ||
| 439 | anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | break; | ||
| 443 | } | ||
| 444 | case TldIdFn: { | ||
| 445 | TldFn *tld_fn = reinterpret_cast<TldFn *>(tld); | ||
| 446 | ZigFn *fn = tld_fn->fn_entry; | ||
| 447 | |||
| 448 | if (fn != nullptr) { | ||
| 449 | (void)anal_dump_get_type_id(ctx, fn->type_entry); | ||
| 450 | } | ||
| 451 | break; | ||
| 452 | } | ||
| 453 | default: | ||
| 454 | break; | ||
| 455 | } | ||
| 456 | |||
| 457 | } else { | ||
| 458 | decl_id = existing_entry->value; | ||
| 459 | } | ||
| 460 | return decl_id; | ||
| 461 | } | ||
| 462 | |||
| 463 | static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { | ||
| 464 | uint32_t type_id = anal_dump_get_type_id(ctx, ty); | ||
| 465 | jw_int(&ctx->jw, type_id); | ||
| 466 | } | ||
| 467 | |||
| 468 | static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { | ||
| 469 | uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); | ||
| 470 | jw_int(&ctx->jw, pkg_id); | ||
| 471 | } | ||
| 472 | |||
| 473 | static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { | ||
| 474 | uint32_t file_id = anal_dump_get_file_id(ctx, file); | ||
| 475 | jw_int(&ctx->jw, file_id); | ||
| 476 | } | ||
| 477 | |||
| 478 | static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { | ||
| 479 | uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); | ||
| 480 | jw_int(&ctx->jw, decl_id); | ||
| 481 | } | ||
| 482 | |||
| 483 | static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { | ||
| 484 | JsonWriter *jw = &ctx->jw; | ||
| 485 | |||
| 486 | Buf full_path_buf = BUF_INIT; | ||
| 487 | os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); | ||
| 488 | Buf *resolve_paths[] = { &full_path_buf, }; | ||
| 489 | Buf *resolved_path = buf_alloc(); | ||
| 490 | *resolved_path = os_path_resolve(resolve_paths, 1); | ||
| 491 | |||
| 492 | auto import_entry = ctx->g->import_table.maybe_get(resolved_path); | ||
| 493 | if (!import_entry) { | ||
| 494 | return; | ||
| 495 | } | ||
| 496 | |||
| 497 | jw_array_elem(jw); | ||
| 498 | jw_begin_object(jw); | ||
| 499 | |||
| 500 | jw_object_field(jw, "name"); | ||
| 501 | jw_string(jw, buf_ptr(&pkg->pkg_path)); | ||
| 502 | |||
| 503 | jw_object_field(jw, "file"); | ||
| 504 | anal_dump_file_ref(ctx, resolved_path); | ||
| 505 | |||
| 506 | jw_object_field(jw, "main"); | ||
| 507 | anal_dump_type_ref(ctx, import_entry->value); | ||
| 508 | |||
| 509 | jw_object_field(jw, "table"); | ||
| 510 | jw_begin_object(jw); | ||
| 511 | auto it = pkg->package_table.entry_iterator(); | ||
| 512 | for (;;) { | ||
| 513 | auto *entry = it.next(); | ||
| 514 | if (!entry) | ||
| 515 | break; | ||
| 516 | |||
| 517 | ZigPackage *child_pkg = entry->value; | ||
| 518 | if (child_pkg != nullptr) { | ||
| 519 | jw_object_field(jw, buf_ptr(entry->key)); | ||
| 520 | anal_dump_pkg_ref(ctx, child_pkg); | ||
| 521 | } | ||
| 522 | } | ||
| 523 | jw_end_object(jw); | ||
| 524 | |||
| 525 | jw_end_object(jw); | ||
| 526 | } | ||
| 527 | |||
| 528 | static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { | ||
| 529 | JsonWriter *jw = &ctx->jw; | ||
| 530 | |||
| 531 | bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; | ||
| 532 | if (make_obj) { | ||
| 533 | jw_array_elem(jw); | ||
| 534 | jw_begin_object(jw); | ||
| 535 | |||
| 536 | jw_object_field(jw, "import"); | ||
| 537 | anal_dump_type_ref(ctx, tld->import); | ||
| 538 | |||
| 539 | jw_object_field(jw, "line"); | ||
| 540 | jw_int(jw, tld->source_node->line); | ||
| 541 | |||
| 542 | jw_object_field(jw, "col"); | ||
| 543 | jw_int(jw, tld->source_node->column); | ||
| 544 | |||
| 545 | jw_object_field(jw, "name"); | ||
| 546 | jw_string(jw, buf_ptr(tld->name)); | ||
| 547 | } | ||
| 548 | |||
| 549 | switch (tld->id) { | ||
| 550 | case TldIdVar: { | ||
| 551 | TldVar *tld_var = reinterpret_cast<TldVar *>(tld); | ||
| 552 | ZigVar *var = tld_var->var; | ||
| 553 | |||
| 554 | if (var != nullptr) { | ||
| 555 | jw_object_field(jw, "kind"); | ||
| 556 | if (var->src_is_const) { | ||
| 557 | jw_string(jw, "const"); | ||
| 558 | } else { | ||
| 559 | jw_string(jw, "var"); | ||
| 560 | } | ||
| 561 | |||
| 562 | if (var->is_thread_local) { | ||
| 563 | jw_object_field(jw, "threadlocal"); | ||
| 564 | jw_bool(jw, true); | ||
| 565 | } | ||
| 566 | |||
| 567 | jw_object_field(jw, "type"); | ||
| 568 | anal_dump_type_ref(ctx, var->var_type); | ||
| 569 | |||
| 570 | if (var->const_value != nullptr) { | ||
| 571 | jw_object_field(jw, "value"); | ||
| 572 | anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); | ||
| 573 | } | ||
| 574 | } | ||
| 575 | break; | ||
| 576 | } | ||
| 577 | case TldIdFn: { | ||
| 578 | TldFn *tld_fn = reinterpret_cast<TldFn *>(tld); | ||
| 579 | ZigFn *fn = tld_fn->fn_entry; | ||
| 580 | |||
| 581 | if (fn != nullptr) { | ||
| 582 | jw_object_field(jw, "kind"); | ||
| 583 | jw_string(jw, "const"); | ||
| 584 | |||
| 585 | jw_object_field(jw, "type"); | ||
| 586 | anal_dump_type_ref(ctx, fn->type_entry); | ||
| 587 | } | ||
| 588 | |||
| 589 | break; | ||
| 590 | } | ||
| 591 | default: | ||
| 592 | break; | ||
| 593 | } | ||
| 594 | |||
| 595 | if (make_obj) { | ||
| 596 | jw_end_object(jw); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | |||
| 600 | static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { | ||
| 601 | JsonWriter *jw = &ctx->jw; | ||
| 602 | jw_string(jw, buf_ptr(file)); | ||
| 603 | } | ||
| 604 | |||
| 605 | static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value) { | ||
| 606 | Error err; | ||
| 607 | |||
| 608 | if (value->type != ty) { | ||
| 609 | jw_null(&ctx->jw); | ||
| 610 | return; | ||
| 611 | } | ||
| 612 | if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { | ||
| 613 | codegen_report_errors_and_exit(ctx->g); | ||
| 614 | } | ||
| 615 | if (value->special == ConstValSpecialUndef) { | ||
| 616 | jw_string(&ctx->jw, "undefined"); | ||
| 617 | return; | ||
| 618 | } | ||
| 619 | if (value->special == ConstValSpecialRuntime) { | ||
| 620 | jw_null(&ctx->jw); | ||
| 621 | return; | ||
| 622 | } | ||
| 623 | switch (ty->id) { | ||
| 624 | case ZigTypeIdMetaType: { | ||
| 625 | ZigType *val_ty = value->data.x_type; | ||
| 626 | anal_dump_type_ref(ctx, val_ty); | ||
| 627 | return; | ||
| 628 | } | ||
| 629 | default: | ||
| 630 | jw_null(&ctx->jw); | ||
| 631 | return; | ||
| 632 | } | ||
| 633 | zig_unreachable(); | ||
| 634 | } | ||
| 635 | |||
| 636 | static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { | ||
| 637 | JsonWriter *jw = &ctx->jw; | ||
| 638 | jw_array_elem(jw); | ||
| 639 | jw_begin_object(jw); | ||
| 640 | |||
| 641 | jw_object_field(jw, "name"); | ||
| 642 | jw_string(jw, buf_ptr(&ty->name)); | ||
| 643 | |||
| 644 | jw_object_field(jw, "kind"); | ||
| 645 | jw_int(jw, type_id_index(ty)); | ||
| 646 | |||
| 647 | switch (ty->id) { | ||
| 648 | case ZigTypeIdStruct: { | ||
| 649 | if (ty->data.structure.is_slice) { | ||
| 650 | // TODO | ||
| 651 | break; | ||
| 652 | } | ||
| 653 | jw_object_field(jw, "decls"); | ||
| 654 | jw_begin_array(jw); | ||
| 655 | |||
| 656 | ScopeDecls *decls_scope = ty->data.structure.decls_scope; | ||
| 657 | auto it = decls_scope->decl_table.entry_iterator(); | ||
| 658 | for (;;) { | ||
| 659 | auto *entry = it.next(); | ||
| 660 | if (!entry) | ||
| 661 | break; | ||
| 662 | |||
| 663 | Tld *tld = entry->value; | ||
| 664 | |||
| 665 | jw_array_elem(jw); | ||
| 666 | anal_dump_decl_ref(ctx, tld); | ||
| 667 | } | ||
| 668 | jw_end_array(jw); | ||
| 669 | |||
| 670 | if (ty->data.structure.root_struct != nullptr) { | ||
| 671 | Buf *path_buf = ty->data.structure.root_struct->path; | ||
| 672 | |||
| 673 | jw_object_field(jw, "file"); | ||
| 674 | anal_dump_file_ref(ctx, path_buf); | ||
| 675 | |||
| 676 | } | ||
| 677 | break; | ||
| 678 | } | ||
| 679 | case ZigTypeIdFloat: { | ||
| 680 | jw_object_field(jw, "bits"); | ||
| 681 | jw_int(jw, ty->data.floating.bit_count); | ||
| 682 | break; | ||
| 683 | } | ||
| 684 | default: | ||
| 685 | // TODO | ||
| 686 | break; | ||
| 687 | } | ||
| 688 | jw_end_object(jw); | ||
| 689 | } | ||
| 690 | |||
| 691 | void zig_print_analysis_dump(CodeGen *g, FILE *f) { | ||
| 692 | Error err; | ||
| 693 | AnalDumpCtx ctx = {}; | ||
| 694 | ctx.g = g; | ||
| 695 | JsonWriter *jw = &ctx.jw; | ||
| 696 | jw_init(jw, f, " ", "\n"); | ||
| 697 | ctx.type_map.init(16); | ||
| 698 | ctx.pkg_map.init(16); | ||
| 699 | ctx.file_map.init(16); | ||
| 700 | ctx.decl_map.init(16); | ||
| 701 | |||
| 702 | jw_begin_object(jw); | ||
| 703 | |||
| 704 | jw_object_field(jw, "typeKinds"); | ||
| 705 | jw_begin_array(jw); | ||
| 706 | for (size_t i = 0; i < type_id_len(); i += 1) { | ||
| 707 | jw_array_elem(jw); | ||
| 708 | jw_string(jw, type_id_name(type_id_at_index(i))); | ||
| 709 | } | ||
| 710 | jw_end_array(jw); | ||
| 711 | |||
| 712 | jw_object_field(jw, "params"); | ||
| 713 | jw_begin_object(jw); | ||
| 714 | { | ||
| 715 | jw_object_field(jw, "zigId"); | ||
| 716 | |||
| 717 | Buf *compiler_id; | ||
| 718 | if ((err = get_compiler_id(&compiler_id))) { | ||
| 719 | fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); | ||
| 720 | exit(1); | ||
| 721 | } | ||
| 722 | jw_string(jw, buf_ptr(compiler_id)); | ||
| 723 | |||
| 724 | jw_object_field(jw, "zigVersion"); | ||
| 725 | jw_string(jw, ZIG_VERSION_STRING); | ||
| 726 | |||
| 727 | jw_object_field(jw, "target"); | ||
| 728 | Buf triple_buf = BUF_INIT; | ||
| 729 | target_triple_zig(&triple_buf, g->zig_target); | ||
| 730 | jw_string(jw, buf_ptr(&triple_buf)); | ||
| 731 | } | ||
| 732 | jw_end_object(jw); | ||
| 733 | |||
| 734 | jw_object_field(jw, "rootPkg"); | ||
| 735 | anal_dump_pkg_ref(&ctx, g->root_package); | ||
| 736 | |||
| 737 | jw_object_field(jw, "packages"); | ||
| 738 | jw_begin_array(jw); | ||
| 739 | for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { | ||
| 740 | anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); | ||
| 741 | } | ||
| 742 | jw_end_array(jw); | ||
| 743 | |||
| 744 | jw_object_field(jw, "types"); | ||
| 745 | jw_begin_array(jw); | ||
| 746 | |||
| 747 | for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { | ||
| 748 | ZigType *ty = ctx.type_list.at(i); | ||
| 749 | anal_dump_type(&ctx, ty); | ||
| 750 | } | ||
| 751 | jw_end_array(jw); | ||
| 752 | |||
| 753 | jw_object_field(jw, "decls"); | ||
| 754 | jw_begin_array(jw); | ||
| 755 | for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { | ||
| 756 | Tld *decl = ctx.decl_list.at(i); | ||
| 757 | anal_dump_decl(&ctx, decl); | ||
| 758 | } | ||
| 759 | jw_end_array(jw); | ||
| 760 | |||
| 761 | jw_object_field(jw, "files"); | ||
| 762 | jw_begin_array(jw); | ||
| 763 | for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { | ||
| 764 | Buf *file = ctx.file_list.at(i); | ||
| 765 | jw_array_elem(jw); | ||
| 766 | anal_dump_file(&ctx, file); | ||
| 767 | } | ||
| 768 | jw_end_array(jw); | ||
| 769 | |||
| 770 | jw_end_object(jw); | ||
| 771 | } | ||
src/dump_analysis.hpp created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2019 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef ZIG_DUMP_ANALYSIS_HPP | ||
| 9 | #define ZIG_DUMP_ANALYSIS_HPP | ||
| 10 | |||
| 11 | #include "all_types.hpp" | ||
| 12 | #include <stdio.h> | ||
| 13 | |||
| 14 | void zig_print_stack_report(CodeGen *g, FILE *f); | ||
| 15 | void zig_print_analysis_dump(CodeGen *g, FILE *f); | ||
| 16 | |||
| 17 | #endif | ||
src/main.cpp+6-1| ... | @@ -16,7 +16,7 @@ | ... | @@ -16,7 +16,7 @@ |
| 16 | #include "libc_installation.hpp" | 16 | #include "libc_installation.hpp" |
| 17 | #include "userland.h" | 17 | #include "userland.h" |
| 18 | #include "glibc.hpp" | 18 | #include "glibc.hpp" |
| 19 | #include "stack_report.hpp" | 19 | #include "dump_analysis.hpp" |
| 20 | 20 | ||
| 21 | #include <stdio.h> | 21 | #include <stdio.h> |
| 22 | 22 | ||
| ... | @@ -64,6 +64,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { | ... | @@ -64,6 +64,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 64 | " -fno-PIC disable Position Independent Code\n" | 64 | " -fno-PIC disable Position Independent Code\n" |
| 65 | " -ftime-report print timing diagnostics\n" | 65 | " -ftime-report print timing diagnostics\n" |
| 66 | " -fstack-report print stack size diagnostics\n" | 66 | " -fstack-report print stack size diagnostics\n" |
| 67 | " -fdump-analysis write analysis.json file for use with zig docs\n" | ||
| 67 | " --libc [file] Provide a file which specifies libc paths\n" | 68 | " --libc [file] Provide a file which specifies libc paths\n" |
| 68 | " --name [name] override output name\n" | 69 | " --name [name] override output name\n" |
| 69 | " --output-dir [dir] override output directory (defaults to cwd)\n" | 70 | " --output-dir [dir] override output directory (defaults to cwd)\n" |
| ... | @@ -479,6 +480,7 @@ int main(int argc, char **argv) { | ... | @@ -479,6 +480,7 @@ int main(int argc, char **argv) { |
| 479 | size_t ver_patch = 0; | 480 | size_t ver_patch = 0; |
| 480 | bool timing_info = false; | 481 | bool timing_info = false; |
| 481 | bool stack_report = false; | 482 | bool stack_report = false; |
| 483 | bool enable_dump_analysis = false; | ||
| 482 | const char *cache_dir = nullptr; | 484 | const char *cache_dir = nullptr; |
| 483 | CliPkg *cur_pkg = allocate<CliPkg>(1); | 485 | CliPkg *cur_pkg = allocate<CliPkg>(1); |
| 484 | BuildMode build_mode = BuildModeDebug; | 486 | BuildMode build_mode = BuildModeDebug; |
| ... | @@ -662,6 +664,8 @@ int main(int argc, char **argv) { | ... | @@ -662,6 +664,8 @@ int main(int argc, char **argv) { |
| 662 | timing_info = true; | 664 | timing_info = true; |
| 663 | } else if (strcmp(arg, "-fstack-report") == 0) { | 665 | } else if (strcmp(arg, "-fstack-report") == 0) { |
| 664 | stack_report = true; | 666 | stack_report = true; |
| 667 | } else if (strcmp(arg, "-fdump-analysis") == 0) { | ||
| 668 | enable_dump_analysis = true; | ||
| 665 | } else if (strcmp(arg, "--enable-valgrind") == 0) { | 669 | } else if (strcmp(arg, "--enable-valgrind") == 0) { |
| 666 | valgrind_support = ValgrindSupportEnabled; | 670 | valgrind_support = ValgrindSupportEnabled; |
| 667 | } else if (strcmp(arg, "--disable-valgrind") == 0) { | 671 | } else if (strcmp(arg, "--disable-valgrind") == 0) { |
| ... | @@ -1138,6 +1142,7 @@ int main(int argc, char **argv) { | ... | @@ -1138,6 +1142,7 @@ int main(int argc, char **argv) { |
| 1138 | 1142 | ||
| 1139 | g->enable_time_report = timing_info; | 1143 | g->enable_time_report = timing_info; |
| 1140 | g->enable_stack_report = stack_report; | 1144 | g->enable_stack_report = stack_report; |
| 1145 | g->enable_dump_analysis = enable_dump_analysis; | ||
| 1141 | codegen_set_out_name(g, buf_out_name); | 1146 | codegen_set_out_name(g, buf_out_name); |
| 1142 | codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); | 1147 | codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); |
| 1143 | g->want_single_threaded = want_single_threaded; | 1148 | g->want_single_threaded = want_single_threaded; |
src/os.hpp+2| ... | @@ -47,6 +47,7 @@ extern const char *possible_ld_names[]; | ... | @@ -47,6 +47,7 @@ extern const char *possible_ld_names[]; |
| 47 | 47 | ||
| 48 | #if defined(ZIG_OS_WINDOWS) | 48 | #if defined(ZIG_OS_WINDOWS) |
| 49 | #define ZIG_PRI_usize "I64u" | 49 | #define ZIG_PRI_usize "I64u" |
| 50 | #define ZIG_PRI_i64 "I64d" | ||
| 50 | #define ZIG_PRI_u64 "I64u" | 51 | #define ZIG_PRI_u64 "I64u" |
| 51 | #define ZIG_PRI_llu "I64u" | 52 | #define ZIG_PRI_llu "I64u" |
| 52 | #define ZIG_PRI_x64 "I64x" | 53 | #define ZIG_PRI_x64 "I64x" |
| ... | @@ -54,6 +55,7 @@ extern const char *possible_ld_names[]; | ... | @@ -54,6 +55,7 @@ extern const char *possible_ld_names[]; |
| 54 | #define ZIG_OS_SEP_CHAR '\\' | 55 | #define ZIG_OS_SEP_CHAR '\\' |
| 55 | #else | 56 | #else |
| 56 | #define ZIG_PRI_usize "zu" | 57 | #define ZIG_PRI_usize "zu" |
| 58 | #define ZIG_PRI_i64 PRId64 | ||
| 57 | #define ZIG_PRI_u64 PRIu64 | 59 | #define ZIG_PRI_u64 PRIu64 |
| 58 | #define ZIG_PRI_llu "llu" | 60 | #define ZIG_PRI_llu "llu" |
| 59 | #define ZIG_PRI_x64 PRIx64 | 61 | #define ZIG_PRI_x64 PRIx64 |
src/stack_report.cpp deleted-121| ... | @@ -1,121 +0,0 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2019 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "stack_report.hpp" | ||
| 9 | |||
| 10 | static void tree_print(FILE *f, ZigType *ty, size_t indent); | ||
| 11 | |||
| 12 | static void pretty_print_bytes(FILE *f, double n) { | ||
| 13 | if (n > 1024.0 * 1024.0 * 1024.0) { | ||
| 14 | fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); | ||
| 15 | return; | ||
| 16 | } | ||
| 17 | if (n > 1024.0 * 1024.0) { | ||
| 18 | fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); | ||
| 19 | return; | ||
| 20 | } | ||
| 21 | if (n > 1024.0) { | ||
| 22 | fprintf(f, "%.02f KiB", n / 1024.0); | ||
| 23 | return; | ||
| 24 | } | ||
| 25 | fprintf(f, "%.02f bytes", n ); | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | |||
| 29 | static int compare_type_abi_sizes_desc(const void *a, const void *b) { | ||
| 30 | uint64_t size_a = (*(ZigType * const*)(a))->abi_size; | ||
| 31 | uint64_t size_b = (*(ZigType * const*)(b))->abi_size; | ||
| 32 | if (size_a > size_b) | ||
| 33 | return -1; | ||
| 34 | if (size_a < size_b) | ||
| 35 | return 1; | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | static void start_child(FILE *f, size_t indent) { | ||
| 40 | fprintf(f, "\n"); | ||
| 41 | for (size_t i = 0; i < indent; i += 1) { | ||
| 42 | fprintf(f, " "); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | static void start_peer(FILE *f, size_t indent) { | ||
| 47 | fprintf(f, ",\n"); | ||
| 48 | for (size_t i = 0; i < indent; i += 1) { | ||
| 49 | fprintf(f, " "); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { | ||
| 54 | ZigList<ZigType *> children = {}; | ||
| 55 | uint64_t sum_from_fields = 0; | ||
| 56 | for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { | ||
| 57 | TypeStructField *field = &struct_type->data.structure.fields[i]; | ||
| 58 | children.append(field->type_entry); | ||
| 59 | sum_from_fields += field->type_entry->abi_size; | ||
| 60 | } | ||
| 61 | qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); | ||
| 62 | |||
| 63 | start_peer(f, indent); | ||
| 64 | fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); | ||
| 65 | |||
| 66 | start_peer(f, indent); | ||
| 67 | fprintf(f, "\"fields\": ["); | ||
| 68 | |||
| 69 | for (size_t i = 0; i < children.length; i += 1) { | ||
| 70 | if (i == 0) { | ||
| 71 | start_child(f, indent + 1); | ||
| 72 | } else { | ||
| 73 | start_peer(f, indent + 1); | ||
| 74 | } | ||
| 75 | fprintf(f, "{"); | ||
| 76 | |||
| 77 | ZigType *child_type = children.at(i); | ||
| 78 | tree_print(f, child_type, indent + 2); | ||
| 79 | |||
| 80 | start_child(f, indent + 1); | ||
| 81 | fprintf(f, "}"); | ||
| 82 | } | ||
| 83 | |||
| 84 | start_child(f, indent); | ||
| 85 | fprintf(f, "]"); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void tree_print(FILE *f, ZigType *ty, size_t indent) { | ||
| 89 | start_child(f, indent); | ||
| 90 | fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); | ||
| 91 | |||
| 92 | start_peer(f, indent); | ||
| 93 | fprintf(f, "\"sizef\": \""); | ||
| 94 | pretty_print_bytes(f, ty->abi_size); | ||
| 95 | fprintf(f, "\""); | ||
| 96 | |||
| 97 | start_peer(f, indent); | ||
| 98 | fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); | ||
| 99 | |||
| 100 | switch (ty->id) { | ||
| 101 | case ZigTypeIdFnFrame: | ||
| 102 | return tree_print_struct(f, ty->data.frame.locals_struct, indent); | ||
| 103 | case ZigTypeIdStruct: | ||
| 104 | return tree_print_struct(f, ty, indent); | ||
| 105 | default: | ||
| 106 | start_child(f, indent); | ||
| 107 | return; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | void zig_print_stack_report(CodeGen *g, FILE *f) { | ||
| 112 | if (g->largest_frame_fn == nullptr) { | ||
| 113 | fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | fprintf(f, "{"); | ||
| 117 | tree_print(f, g->largest_frame_fn->frame_type, 1); | ||
| 118 | |||
| 119 | start_child(f, 0); | ||
| 120 | fprintf(f, "}\n"); | ||
| 121 | } | ||
src/stack_report.hpp deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2019 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef ZIG_STACK_REPORT_HPP | ||
| 9 | #define ZIG_STACK_REPORT_HPP | ||
| 10 | |||
| 11 | #include "all_types.hpp" | ||
| 12 | #include <stdio.h> | ||
| 13 | |||
| 14 | void zig_print_stack_report(CodeGen *g, FILE *f); | ||
| 15 | |||
| 16 | #endif | ||