From 59ac7b91daed86af1ff03aceb7d9e9b8118e06ba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Oct 2019 17:58:22 -0400 Subject: [PATCH 1/2] add -fdump-analysis to dump type information to json This commit adds -fdump-analysis which creates a `$NAME-analysis.json` file with all of the finished semantic analysis that the stage1 compiler produced. It contains types, packages, declarations, and files. This is an initial implementation; some data will be missing. However it's easy to improve the implementation, which is in `src/dump_analysis.cpp`. The next step for #21 will be to create Zig code which parses this json file and creates user-facing HTML documentation. This feature has other uses, however; for example, it could be used for IDE integration features until the self-hosted compiler is available. --- CMakeLists.txt | 2 +- src/all_types.hpp | 7 + src/analyze.cpp | 16 + src/codegen.cpp | 29 +- src/codegen.hpp | 2 + src/dump_analysis.cpp | 769 ++++++++++++++++++++ src/{stack_report.hpp => dump_analysis.hpp} | 5 +- src/main.cpp | 7 +- src/os.hpp | 2 + src/stack_report.cpp | 121 --- 10 files changed, 829 insertions(+), 131 deletions(-) create mode 100644 src/dump_analysis.cpp rename src/{stack_report.hpp => dump_analysis.hpp} (68%) delete mode 100644 src/stack_report.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a189a082d37e..5d918f2b143a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -443,6 +443,7 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" "${CMAKE_SOURCE_DIR}/src/codegen.cpp" "${CMAKE_SOURCE_DIR}/src/compiler.cpp" + "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp" "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" "${CMAKE_SOURCE_DIR}/src/error.cpp" "${CMAKE_SOURCE_DIR}/src/glibc.cpp" @@ -453,7 +454,6 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/os.cpp" "${CMAKE_SOURCE_DIR}/src/parser.cpp" "${CMAKE_SOURCE_DIR}/src/range_set.cpp" - "${CMAKE_SOURCE_DIR}/src/stack_report.cpp" "${CMAKE_SOURCE_DIR}/src/target.cpp" "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" "${CMAKE_SOURCE_DIR}/src/translate_c.cpp" diff --git a/src/all_types.hpp b/src/all_types.hpp index 695f22ac90a0..6ebfe934b48d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1300,6 +1300,12 @@ struct ZigTypeEnum { uint32_t type_ptr_hash(const ZigType *ptr); bool type_ptr_eql(const ZigType *a, const ZigType *b); +uint32_t pkg_ptr_hash(const ZigPackage *ptr); +bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b); + +uint32_t tld_ptr_hash(const Tld *ptr); +bool tld_ptr_eql(const Tld *a, const Tld *b); + struct ZigTypeUnion { AstNode *decl_node; TypeUnionField *fields; @@ -2056,6 +2062,7 @@ struct CodeGen { bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic bool have_stack_probing; bool function_sections; + bool enable_dump_analysis; Buf *mmacosx_version_min; Buf *mios_version_min; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1282126fbc41..cca239c27521 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -7303,6 +7303,22 @@ bool type_ptr_eql(const ZigType *a, const ZigType *b) { return a == b; } +uint32_t pkg_ptr_hash(const ZigPackage *ptr) { + return hash_ptr((void*)ptr); +} + +bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) { + return a == b; +} + +uint32_t tld_ptr_hash(const Tld *ptr) { + return hash_ptr((void*)ptr); +} + +bool tld_ptr_eql(const Tld *a, const Tld *b) { + return a == b; +} + ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name)); resolve_top_level_decl(codegen, tld, nullptr, false); diff --git a/src/codegen.cpp b/src/codegen.cpp index 5d1f2b8bf9e1..c6acc0524d4c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -20,6 +20,7 @@ #include "util.hpp" #include "zig_llvm.h" #include "userland.h" +#include "dump_analysis.hpp" #include #include @@ -1724,7 +1725,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { } ATTRIBUTE_NORETURN -static void report_errors_and_exit(CodeGen *g) { +void codegen_report_errors_and_exit(CodeGen *g) { assert(g->errors.length != 0); for (size_t i = 0; i < g->errors.length; i += 1) { ErrorMsg *err = g->errors.at(i); @@ -1735,7 +1736,7 @@ static void report_errors_and_exit(CodeGen *g) { static void report_errors_and_maybe_exit(CodeGen *g) { if (g->errors.length != 0) { - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); } } @@ -1745,7 +1746,7 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); add_error_note(g, msg, source_node, buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); } 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) { Error err; if (const_val->special == ConstValSpecialLazy && (err = ir_resolve_lazy(g, nullptr, const_val))) - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); switch (const_val->special) { case ConstValSpecialLazy: @@ -4253,7 +4254,7 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ? struct_ptr_type->data.pointer.child_type : struct_ptr_type; if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); assert(field->gen_index != SIZE_MAX); 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 check: switch (const_val->special) { case ConstValSpecialLazy: if ((err = ir_resolve_lazy(g, nullptr, const_val))) { - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); } goto check; case ConstValSpecialRuntime: @@ -10157,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->have_stack_probing); cache_bool(ch, g->is_dummy_so); cache_bool(ch, g->function_sections); + cache_bool(ch, g->enable_dump_analysis); cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); @@ -10338,6 +10340,21 @@ void codegen_build_and_link(CodeGen *g) { gen_h_file(g); } } + if (g->enable_dump_analysis) { + const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", + buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); + FILE *f = fopen(analysis_json_filename, "wb"); + if (f == nullptr) { + fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); + exit(1); + } + zig_print_analysis_dump(g, f); + if (fclose(f) != 0) { + fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); + exit(1); + } + + } // If we're outputting assembly or llvm IR we skip linking. // If we're making a library or executable we must link. diff --git a/src/codegen.hpp b/src/codegen.hpp index 5be15b051398..7f46c6bfb527 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -64,4 +64,6 @@ void codegen_release_caches(CodeGen *codegen); bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); +void codegen_report_errors_and_exit(CodeGen *g); + #endif diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp new file mode 100644 index 000000000000..9e928ea7f051 --- /dev/null +++ b/src/dump_analysis.cpp @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "dump_analysis.hpp" +#include "compiler.hpp" +#include "analyze.hpp" +#include "config.h" +#include "ir.hpp" +#include "codegen.hpp" + +enum JsonWriterState { + JsonWriterStateInvalid, + JsonWriterStateValue, + JsonWriterStateArrayStart, + JsonWriterStateArray, + JsonWriterStateObjectStart, + JsonWriterStateObject, +}; + +#define JSON_MAX_DEPTH 10 + +struct JsonWriter { + size_t state_index; + FILE *f; + const char *one_indent; + const char *nl; + JsonWriterState state[JSON_MAX_DEPTH]; +}; + +static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { + jw->state_index = 1; + jw->f = f; + jw->one_indent = one_indent; + jw->nl = nl; + jw->state[0] = JsonWriterStateInvalid; + jw->state[1] = JsonWriterStateValue; +} + +static void jw_nl_indent(JsonWriter *jw) { + assert(jw->state_index >= 1); + fprintf(jw->f, "%s", jw->nl); + for (size_t i = 0; i < jw->state_index - 1; i += 1) { + fprintf(jw->f, jw->one_indent); + } +} + +static void jw_push_state(JsonWriter *jw, JsonWriterState state) { + jw->state_index += 1; + assert(jw->state_index < JSON_MAX_DEPTH); + jw->state[jw->state_index] = state; +} + +static void jw_pop_state(JsonWriter *jw) { + assert(jw->state_index != 0); + jw->state_index -= 1; +} + +static void jw_begin_array(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "["); + jw->state[jw->state_index] = JsonWriterStateArrayStart; +} + +static void jw_begin_object(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "{"); + jw->state[jw->state_index] = JsonWriterStateObjectStart; +} + +static void jw_array_elem(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateObjectStart: + case JsonWriterStateObject: + zig_unreachable(); + case JsonWriterStateArray: + fprintf(jw->f, ","); + // fallthrough + case JsonWriterStateArrayStart: + jw->state[jw->state_index] = JsonWriterStateArray; + jw_push_state(jw, JsonWriterStateValue); + jw_nl_indent(jw); + return; + } + zig_unreachable(); +} + +static void jw_write_escaped_string(JsonWriter *jw, const char *s) { + fprintf(jw->f, "\""); + for (;; s += 1) { + switch (*s) { + case 0: + fprintf(jw->f, "\""); + return; + case '"': + fprintf(jw->f, "\\\""); + continue; + case '\t': + fprintf(jw->f, "\\t"); + continue; + case '\r': + fprintf(jw->f, "\\r"); + continue; + case '\n': + fprintf(jw->f, "\\n"); + continue; + case '\b': + fprintf(jw->f, "\\b"); + continue; + case '\f': + fprintf(jw->f, "\\f"); + continue; + case '\\': + fprintf(jw->f, "\\\\"); + continue; + default: + fprintf(jw->f, "%c", *s); + continue; + } + } +} + +static void jw_object_field(JsonWriter *jw, const char *name) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateArray: + case JsonWriterStateArrayStart: + zig_unreachable(); + case JsonWriterStateObject: + fprintf(jw->f, ","); + // fallthrough + case JsonWriterStateObjectStart: + jw->state[jw->state_index] = JsonWriterStateObject; + jw_push_state(jw, JsonWriterStateValue); + jw_nl_indent(jw); + jw_write_escaped_string(jw, name); + fprintf(jw->f, ": "); + return; + } + zig_unreachable(); +} + +static void jw_end_array(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateObjectStart: + case JsonWriterStateObject: + zig_unreachable(); + case JsonWriterStateArrayStart: + fprintf(jw->f, "]"); + jw_pop_state(jw); + return; + case JsonWriterStateArray: + jw_nl_indent(jw); + jw_pop_state(jw); + fprintf(jw->f, "]"); + return; + } + zig_unreachable(); +} + + +static void jw_end_object(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + zig_unreachable(); + case JsonWriterStateValue: + zig_unreachable(); + case JsonWriterStateArray: + zig_unreachable(); + case JsonWriterStateArrayStart: + zig_unreachable(); + case JsonWriterStateObjectStart: + fprintf(jw->f, "}"); + jw_pop_state(jw); + return; + case JsonWriterStateObject: + jw_nl_indent(jw); + jw_pop_state(jw); + fprintf(jw->f, "}"); + return; + } + zig_unreachable(); +} + +static void jw_null(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "null"); + jw_pop_state(jw); +} + +static void jw_bool(JsonWriter *jw, bool x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + if (x) { + fprintf(jw->f, "true"); + } else { + fprintf(jw->f, "false"); + } + jw_pop_state(jw); +} + +static void jw_int(JsonWriter *jw, int64_t x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + if (x > 4503599627370496 || x < -4503599627370496) { + fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); + } else { + fprintf(jw->f, "%" ZIG_PRI_i64, x); + } + jw_pop_state(jw); +} + +static void jw_string(JsonWriter *jw, const char *s) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + jw_write_escaped_string(jw, s); + jw_pop_state(jw); +} + + +static void tree_print(FILE *f, ZigType *ty, size_t indent); + +static void pretty_print_bytes(FILE *f, double n) { + if (n > 1024.0 * 1024.0 * 1024.0) { + fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); + return; + } + if (n > 1024.0 * 1024.0) { + fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); + return; + } + if (n > 1024.0) { + fprintf(f, "%.02f KiB", n / 1024.0); + return; + } + fprintf(f, "%.02f bytes", n ); + return; +} + +static int compare_type_abi_sizes_desc(const void *a, const void *b) { + uint64_t size_a = (*(ZigType * const*)(a))->abi_size; + uint64_t size_b = (*(ZigType * const*)(b))->abi_size; + if (size_a > size_b) + return -1; + if (size_a < size_b) + return 1; + return 0; +} + +static void start_child(FILE *f, size_t indent) { + fprintf(f, "\n"); + for (size_t i = 0; i < indent; i += 1) { + fprintf(f, " "); + } +} + +static void start_peer(FILE *f, size_t indent) { + fprintf(f, ",\n"); + for (size_t i = 0; i < indent; i += 1) { + fprintf(f, " "); + } +} + +static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { + ZigList children = {}; + uint64_t sum_from_fields = 0; + for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + children.append(field->type_entry); + sum_from_fields += field->type_entry->abi_size; + } + qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); + + start_peer(f, indent); + fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); + + start_peer(f, indent); + fprintf(f, "\"fields\": ["); + + for (size_t i = 0; i < children.length; i += 1) { + if (i == 0) { + start_child(f, indent + 1); + } else { + start_peer(f, indent + 1); + } + fprintf(f, "{"); + + ZigType *child_type = children.at(i); + tree_print(f, child_type, indent + 2); + + start_child(f, indent + 1); + fprintf(f, "}"); + } + + start_child(f, indent); + fprintf(f, "]"); +} + +static void tree_print(FILE *f, ZigType *ty, size_t indent) { + start_child(f, indent); + fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); + + start_peer(f, indent); + fprintf(f, "\"sizef\": \""); + pretty_print_bytes(f, ty->abi_size); + fprintf(f, "\""); + + start_peer(f, indent); + fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); + + switch (ty->id) { + case ZigTypeIdFnFrame: + return tree_print_struct(f, ty->data.frame.locals_struct, indent); + case ZigTypeIdStruct: + return tree_print_struct(f, ty, indent); + default: + start_child(f, indent); + return; + } +} + +void zig_print_stack_report(CodeGen *g, FILE *f) { + if (g->largest_frame_fn == nullptr) { + fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); + return; + } + fprintf(f, "{"); + tree_print(f, g->largest_frame_fn->frame_type, 1); + + start_child(f, 0); + fprintf(f, "}\n"); +} + +struct AnalDumpCtx { + CodeGen *g; + JsonWriter jw; + + ZigList type_list; + HashMap type_map; + + ZigList pkg_list; + HashMap pkg_map; + + ZigList file_list; + HashMap file_map; + + ZigList decl_list; + HashMap decl_map; +}; + +static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); +static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value); + +static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value) { + Error err; + if (value->type != ty) { + return; + } + if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { + codegen_report_errors_and_exit(ctx->g); + } + if (value->special == ConstValSpecialUndef) { + return; + } + if (value->special == ConstValSpecialRuntime) { + return; + } + switch (ty->id) { + case ZigTypeIdMetaType: { + ZigType *val_ty = value->data.x_type; + (void)anal_dump_get_type_id(ctx, val_ty); + return; + } + default: + return; + } + zig_unreachable(); +} + +static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { + uint32_t type_id = ctx->type_list.length; + auto existing_entry = ctx->type_map.put_unique(ty, type_id); + if (existing_entry == nullptr) { + ctx->type_list.append(ty); + } else { + type_id = existing_entry->value; + } + return type_id; +} + +static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { + assert(pkg != nullptr); + uint32_t pkg_id = ctx->pkg_list.length; + auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); + if (existing_entry == nullptr) { + ctx->pkg_list.append(pkg); + } else { + pkg_id = existing_entry->value; + } + return pkg_id; +} + +static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { + uint32_t file_id = ctx->file_list.length; + auto existing_entry = ctx->file_map.put_unique(file, file_id); + if (existing_entry == nullptr) { + ctx->file_list.append(file); + } else { + file_id = existing_entry->value; + } + return file_id; +} + +static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { + uint32_t decl_id = ctx->decl_list.length; + auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); + if (existing_entry == nullptr) { + ctx->decl_list.append(tld); + + if (tld->import != nullptr) { + (void)anal_dump_get_type_id(ctx, tld->import); + } + + // poke the types + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = reinterpret_cast(tld); + ZigVar *var = tld_var->var; + + if (var != nullptr) { + (void)anal_dump_get_type_id(ctx, var->var_type); + + if (var->const_value != nullptr) { + anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); + } + } + break; + } + case TldIdFn: { + TldFn *tld_fn = reinterpret_cast(tld); + ZigFn *fn = tld_fn->fn_entry; + + if (fn != nullptr) { + (void)anal_dump_get_type_id(ctx, fn->type_entry); + } + break; + } + default: + break; + } + + } else { + decl_id = existing_entry->value; + } + return decl_id; +} + +static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { + uint32_t type_id = anal_dump_get_type_id(ctx, ty); + jw_int(&ctx->jw, type_id); +} + +static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { + uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); + jw_int(&ctx->jw, pkg_id); +} + +static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { + uint32_t file_id = anal_dump_get_file_id(ctx, file); + jw_int(&ctx->jw, file_id); +} + +static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { + uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); + jw_int(&ctx->jw, decl_id); +} + +static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { + JsonWriter *jw = &ctx->jw; + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&pkg->pkg_path)); + + jw_object_field(jw, "file"); + Buf full_path_buf = BUF_INIT; + os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); + Buf *resolve_paths[] = { &full_path_buf, }; + Buf *resolved_path = buf_alloc(); + *resolved_path = os_path_resolve(resolve_paths, 1); + anal_dump_file_ref(ctx, resolved_path); + + auto import_entry = ctx->g->import_table.maybe_get(resolved_path); + if (!import_entry) { + fprintf(stderr, "due to a race condition or bug, files moved around during analysis\n"); + exit(1); + } + jw_object_field(jw, "main"); + anal_dump_type_ref(ctx, import_entry->value); + + jw_object_field(jw, "table"); + jw_begin_object(jw); + auto it = pkg->package_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + ZigPackage *child_pkg = entry->value; + if (child_pkg != nullptr) { + jw_object_field(jw, buf_ptr(entry->key)); + anal_dump_pkg_ref(ctx, child_pkg); + } + } + jw_end_object(jw); + + jw_end_object(jw); +} + +static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { + JsonWriter *jw = &ctx->jw; + + bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; + if (make_obj) { + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "import"); + anal_dump_type_ref(ctx, tld->import); + + jw_object_field(jw, "line"); + jw_int(jw, tld->source_node->line); + + jw_object_field(jw, "col"); + jw_int(jw, tld->source_node->column); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(tld->name)); + } + + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = reinterpret_cast(tld); + ZigVar *var = tld_var->var; + + if (var != nullptr) { + jw_object_field(jw, "kind"); + if (var->src_is_const) { + jw_string(jw, "const"); + } else { + jw_string(jw, "var"); + } + + if (var->is_thread_local) { + jw_object_field(jw, "threadlocal"); + jw_bool(jw, true); + } + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, var->var_type); + + if (var->const_value != nullptr) { + jw_object_field(jw, "value"); + anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); + } + } + break; + } + case TldIdFn: { + TldFn *tld_fn = reinterpret_cast(tld); + ZigFn *fn = tld_fn->fn_entry; + + if (fn != nullptr) { + jw_object_field(jw, "kind"); + jw_string(jw, "const"); + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, fn->type_entry); + } + + break; + } + default: + break; + } + + if (make_obj) { + jw_end_object(jw); + } +} + +static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { + JsonWriter *jw = &ctx->jw; + jw_string(jw, buf_ptr(file)); +} + +static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value) { + Error err; + + if (value->type != ty) { + jw_null(&ctx->jw); + return; + } + if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { + codegen_report_errors_and_exit(ctx->g); + } + if (value->special == ConstValSpecialUndef) { + jw_string(&ctx->jw, "undefined"); + return; + } + if (value->special == ConstValSpecialRuntime) { + jw_null(&ctx->jw); + return; + } + switch (ty->id) { + case ZigTypeIdMetaType: { + ZigType *val_ty = value->data.x_type; + anal_dump_type_ref(ctx, val_ty); + return; + } + default: + jw_null(&ctx->jw); + return; + } + zig_unreachable(); +} + +static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { + JsonWriter *jw = &ctx->jw; + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "kind"); + jw_int(jw, type_id_index(ty)); + + switch (ty->id) { + case ZigTypeIdStruct: { + if (ty->data.structure.is_slice) { + // TODO + break; + } + jw_object_field(jw, "decls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.structure.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + jw_end_array(jw); + + if (ty->data.structure.root_struct != nullptr) { + Buf *path_buf = ty->data.structure.root_struct->path; + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, path_buf); + + } + break; + } + case ZigTypeIdFloat: { + jw_object_field(jw, "bits"); + jw_int(jw, ty->data.floating.bit_count); + break; + } + default: + // TODO + break; + } + jw_end_object(jw); +} + +void zig_print_analysis_dump(CodeGen *g, FILE *f) { + Error err; + AnalDumpCtx ctx = {}; + ctx.g = g; + JsonWriter *jw = &ctx.jw; + jw_init(jw, f, " ", "\n"); + ctx.type_map.init(16); + ctx.pkg_map.init(16); + ctx.file_map.init(16); + ctx.decl_map.init(16); + + jw_begin_object(jw); + + jw_object_field(jw, "typeKinds"); + jw_begin_array(jw); + for (size_t i = 0; i < type_id_len(); i += 1) { + jw_array_elem(jw); + jw_string(jw, type_id_name(type_id_at_index(i))); + } + jw_end_array(jw); + + jw_object_field(jw, "params"); + jw_begin_object(jw); + { + jw_object_field(jw, "zigId"); + + Buf *compiler_id; + if ((err = get_compiler_id(&compiler_id))) { + fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); + exit(1); + } + jw_string(jw, buf_ptr(compiler_id)); + + jw_object_field(jw, "zigVersion"); + jw_string(jw, ZIG_VERSION_STRING); + + jw_object_field(jw, "target"); + Buf triple_buf = BUF_INIT; + target_triple_zig(&triple_buf, g->zig_target); + jw_string(jw, buf_ptr(&triple_buf)); + } + jw_end_object(jw); + + jw_object_field(jw, "rootPkg"); + anal_dump_pkg_ref(&ctx, g->root_package); + + jw_object_field(jw, "packages"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { + anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); + } + jw_end_array(jw); + + jw_object_field(jw, "types"); + jw_begin_array(jw); + + for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { + ZigType *ty = ctx.type_list.at(i); + anal_dump_type(&ctx, ty); + } + jw_end_array(jw); + + jw_object_field(jw, "decls"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { + Tld *decl = ctx.decl_list.at(i); + anal_dump_decl(&ctx, decl); + } + jw_end_array(jw); + + jw_object_field(jw, "files"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { + Buf *file = ctx.file_list.at(i); + jw_array_elem(jw); + anal_dump_file(&ctx, file); + } + jw_end_array(jw); + + jw_end_object(jw); +} diff --git a/src/stack_report.hpp b/src/dump_analysis.hpp similarity index 68% rename from src/stack_report.hpp rename to src/dump_analysis.hpp index 8a644466f94e..44a87290e5f0 100644 --- a/src/stack_report.hpp +++ b/src/dump_analysis.hpp @@ -5,12 +5,13 @@ * See http://opensource.org/licenses/MIT */ -#ifndef ZIG_STACK_REPORT_HPP -#define ZIG_STACK_REPORT_HPP +#ifndef ZIG_DUMP_ANALYSIS_HPP +#define ZIG_DUMP_ANALYSIS_HPP #include "all_types.hpp" #include void zig_print_stack_report(CodeGen *g, FILE *f); +void zig_print_analysis_dump(CodeGen *g, FILE *f); #endif diff --git a/src/main.cpp b/src/main.cpp index ffaa4e6ca0cb..5300938f850e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,7 +16,7 @@ #include "libc_installation.hpp" #include "userland.h" #include "glibc.hpp" -#include "stack_report.hpp" +#include "dump_analysis.hpp" #include @@ -64,6 +64,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -fno-PIC disable Position Independent Code\n" " -ftime-report print timing diagnostics\n" " -fstack-report print stack size diagnostics\n" + " -fdump-analysis write analysis.json file for use with zig docs\n" " --libc [file] Provide a file which specifies libc paths\n" " --name [name] override output name\n" " --output-dir [dir] override output directory (defaults to cwd)\n" @@ -479,6 +480,7 @@ int main(int argc, char **argv) { size_t ver_patch = 0; bool timing_info = false; bool stack_report = false; + bool enable_dump_analysis = false; const char *cache_dir = nullptr; CliPkg *cur_pkg = allocate(1); BuildMode build_mode = BuildModeDebug; @@ -662,6 +664,8 @@ int main(int argc, char **argv) { timing_info = true; } else if (strcmp(arg, "-fstack-report") == 0) { stack_report = true; + } else if (strcmp(arg, "-fdump-analysis") == 0) { + enable_dump_analysis = true; } else if (strcmp(arg, "--enable-valgrind") == 0) { valgrind_support = ValgrindSupportEnabled; } else if (strcmp(arg, "--disable-valgrind") == 0) { @@ -1138,6 +1142,7 @@ int main(int argc, char **argv) { g->enable_time_report = timing_info; g->enable_stack_report = stack_report; + g->enable_dump_analysis = enable_dump_analysis; codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); g->want_single_threaded = want_single_threaded; diff --git a/src/os.hpp b/src/os.hpp index c8135e9844db..afeef10e0c7a 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -47,6 +47,7 @@ extern const char *possible_ld_names[]; #if defined(ZIG_OS_WINDOWS) #define ZIG_PRI_usize "I64u" +#define ZIG_PRI_i64 "I64d" #define ZIG_PRI_u64 "I64u" #define ZIG_PRI_llu "I64u" #define ZIG_PRI_x64 "I64x" @@ -54,6 +55,7 @@ extern const char *possible_ld_names[]; #define ZIG_OS_SEP_CHAR '\\' #else #define ZIG_PRI_usize "zu" +#define ZIG_PRI_i64 PRId64 #define ZIG_PRI_u64 PRIu64 #define ZIG_PRI_llu "llu" #define ZIG_PRI_x64 PRIx64 diff --git a/src/stack_report.cpp b/src/stack_report.cpp deleted file mode 100644 index bd5a436316c9..000000000000 --- a/src/stack_report.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "stack_report.hpp" - -static void tree_print(FILE *f, ZigType *ty, size_t indent); - -static void pretty_print_bytes(FILE *f, double n) { - if (n > 1024.0 * 1024.0 * 1024.0) { - fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); - return; - } - if (n > 1024.0 * 1024.0) { - fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); - return; - } - if (n > 1024.0) { - fprintf(f, "%.02f KiB", n / 1024.0); - return; - } - fprintf(f, "%.02f bytes", n ); - return; -} - -static int compare_type_abi_sizes_desc(const void *a, const void *b) { - uint64_t size_a = (*(ZigType * const*)(a))->abi_size; - uint64_t size_b = (*(ZigType * const*)(b))->abi_size; - if (size_a > size_b) - return -1; - if (size_a < size_b) - return 1; - return 0; -} - -static void start_child(FILE *f, size_t indent) { - fprintf(f, "\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void start_peer(FILE *f, size_t indent) { - fprintf(f, ",\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { - ZigList children = {}; - uint64_t sum_from_fields = 0; - for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; - children.append(field->type_entry); - sum_from_fields += field->type_entry->abi_size; - } - qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); - - start_peer(f, indent); - fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); - - start_peer(f, indent); - fprintf(f, "\"fields\": ["); - - for (size_t i = 0; i < children.length; i += 1) { - if (i == 0) { - start_child(f, indent + 1); - } else { - start_peer(f, indent + 1); - } - fprintf(f, "{"); - - ZigType *child_type = children.at(i); - tree_print(f, child_type, indent + 2); - - start_child(f, indent + 1); - fprintf(f, "}"); - } - - start_child(f, indent); - fprintf(f, "]"); -} - -static void tree_print(FILE *f, ZigType *ty, size_t indent) { - start_child(f, indent); - fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); - - start_peer(f, indent); - fprintf(f, "\"sizef\": \""); - pretty_print_bytes(f, ty->abi_size); - fprintf(f, "\""); - - start_peer(f, indent); - fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); - - switch (ty->id) { - case ZigTypeIdFnFrame: - return tree_print_struct(f, ty->data.frame.locals_struct, indent); - case ZigTypeIdStruct: - return tree_print_struct(f, ty, indent); - default: - start_child(f, indent); - return; - } -} - -void zig_print_stack_report(CodeGen *g, FILE *f) { - if (g->largest_frame_fn == nullptr) { - fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); - return; - } - fprintf(f, "{"); - tree_print(f, g->largest_frame_fn->frame_type, 1); - - start_child(f, 0); - fprintf(f, "}\n"); -} From 39d47b2c51d43d98f414ed5d5622c9e582ec9aa6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Oct 2019 18:05:44 -0400 Subject: [PATCH 2/2] handle when std lib is unused --- src/dump_analysis.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp index 9e928ea7f051..9cb0f46c9ff3 100644 --- a/src/dump_analysis.cpp +++ b/src/dump_analysis.cpp @@ -482,25 +482,27 @@ static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { JsonWriter *jw = &ctx->jw; - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&pkg->pkg_path)); - jw_object_field(jw, "file"); Buf full_path_buf = BUF_INIT; os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); Buf *resolve_paths[] = { &full_path_buf, }; Buf *resolved_path = buf_alloc(); *resolved_path = os_path_resolve(resolve_paths, 1); - anal_dump_file_ref(ctx, resolved_path); auto import_entry = ctx->g->import_table.maybe_get(resolved_path); if (!import_entry) { - fprintf(stderr, "due to a race condition or bug, files moved around during analysis\n"); - exit(1); + return; } + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&pkg->pkg_path)); + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, resolved_path); + jw_object_field(jw, "main"); anal_dump_type_ref(ctx, import_entry->value);