authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-03 17:58:22-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-03 17:58:22-04:00
log59ac7b91daed86af1ff03aceb7d9e9b8118e06ba
treeb6078f04125ca49bbb7a34e18c78be68e530d10f
parent7640bec8e0e735eaba49087840b7f6e6b2dd74ab
signaturelock-open Commit is signed but in an unrecognized format.

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.

11 files changed, 843 insertions(+), 145 deletions(-)

CMakeLists.txt+1-1
......@@ -443,6 +443,7 @@ set(ZIG_SOURCES
443443 "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp"
444444 "${CMAKE_SOURCE_DIR}/src/codegen.cpp"
445445 "${CMAKE_SOURCE_DIR}/src/compiler.cpp"
446 "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp"
446447 "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
447448 "${CMAKE_SOURCE_DIR}/src/error.cpp"
448449 "${CMAKE_SOURCE_DIR}/src/glibc.cpp"
......@@ -453,7 +454,6 @@ set(ZIG_SOURCES
453454 "${CMAKE_SOURCE_DIR}/src/os.cpp"
454455 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
455456 "${CMAKE_SOURCE_DIR}/src/range_set.cpp"
456 "${CMAKE_SOURCE_DIR}/src/stack_report.cpp"
457457 "${CMAKE_SOURCE_DIR}/src/target.cpp"
458458 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
459459 "${CMAKE_SOURCE_DIR}/src/translate_c.cpp"
src/all_types.hpp+7
......@@ -1300,6 +1300,12 @@ struct ZigTypeEnum {
13001300uint32_t type_ptr_hash(const ZigType *ptr);
13011301bool type_ptr_eql(const ZigType *a, const ZigType *b);
13021302
1303uint32_t pkg_ptr_hash(const ZigPackage *ptr);
1304bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b);
1305
1306uint32_t tld_ptr_hash(const Tld *ptr);
1307bool tld_ptr_eql(const Tld *a, const Tld *b);
1308
13031309struct ZigTypeUnion {
13041310 AstNode *decl_node;
13051311 TypeUnionField *fields;
......@@ -2056,6 +2062,7 @@ struct CodeGen {
20562062 bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic
20572063 bool have_stack_probing;
20582064 bool function_sections;
2065 bool enable_dump_analysis;
20592066
20602067 Buf *mmacosx_version_min;
20612068 Buf *mios_version_min;
src/analyze.cpp+16
......@@ -7303,6 +7303,22 @@ bool type_ptr_eql(const ZigType *a, const ZigType *b) {
73037303 return a == b;
73047304}
73057305
7306uint32_t pkg_ptr_hash(const ZigPackage *ptr) {
7307 return hash_ptr((void*)ptr);
7308}
7309
7310bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) {
7311 return a == b;
7312}
7313
7314uint32_t tld_ptr_hash(const Tld *ptr) {
7315 return hash_ptr((void*)ptr);
7316}
7317
7318bool tld_ptr_eql(const Tld *a, const Tld *b) {
7319 return a == b;
7320}
7321
73067322ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
73077323 Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name));
73087324 resolve_top_level_decl(codegen, tld, nullptr, false);
src/codegen.cpp+23-6
......@@ -20,6 +20,7 @@
2020#include "util.hpp"
2121#include "zig_llvm.h"
2222#include "userland.h"
23#include "dump_analysis.hpp"
2324
2425#include <stdio.h>
2526#include <errno.h>
......@@ -1724,7 +1725,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
17241725}
17251726
17261727ATTRIBUTE_NORETURN
1727static void report_errors_and_exit(CodeGen *g) {
1728void codegen_report_errors_and_exit(CodeGen *g) {
17281729 assert(g->errors.length != 0);
17291730 for (size_t i = 0; i < g->errors.length; i += 1) {
17301731 ErrorMsg *err = g->errors.at(i);
......@@ -1735,7 +1736,7 @@ static void report_errors_and_exit(CodeGen *g) {
17351736
17361737static void report_errors_and_maybe_exit(CodeGen *g) {
17371738 if (g->errors.length != 0) {
1738 report_errors_and_exit(g);
1739 codegen_report_errors_and_exit(g);
17391740 }
17401741}
17411742
......@@ -1745,7 +1746,7 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
17451746 buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481"));
17461747 add_error_note(g, msg, source_node,
17471748 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);
17491750}
17501751
17511752static 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) {
34563457 Error err;
34573458 if (const_val->special == ConstValSpecialLazy &&
34583459 (err = ir_resolve_lazy(g, nullptr, const_val)))
3459 report_errors_and_exit(g);
3460 codegen_report_errors_and_exit(g);
34603461
34613462 switch (const_val->special) {
34623463 case ConstValSpecialLazy:
......@@ -4253,7 +4254,7 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa
42534254 ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ?
42544255 struct_ptr_type->data.pointer.child_type : struct_ptr_type;
42554256 if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull)))
4256 report_errors_and_exit(g);
4257 codegen_report_errors_and_exit(g);
42574258
42584259 assert(field->gen_index != SIZE_MAX);
42594260 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
66256626check: switch (const_val->special) {
66266627 case ConstValSpecialLazy:
66276628 if ((err = ir_resolve_lazy(g, nullptr, const_val))) {
6628 report_errors_and_exit(g);
6629 codegen_report_errors_and_exit(g);
66296630 }
66306631 goto check;
66316632 case ConstValSpecialRuntime:
......@@ -10157,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
1015710158 cache_bool(ch, g->have_stack_probing);
1015810159 cache_bool(ch, g->is_dummy_so);
1015910160 cache_bool(ch, g->function_sections);
10161 cache_bool(ch, g->enable_dump_analysis);
1016010162 cache_buf_opt(ch, g->mmacosx_version_min);
1016110163 cache_buf_opt(ch, g->mios_version_min);
1016210164 cache_usize(ch, g->version_major);
......@@ -10338,6 +10340,21 @@ void codegen_build_and_link(CodeGen *g) {
1033810340 gen_h_file(g);
1033910341 }
1034010342 }
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 }
1034110358
1034210359 // If we're outputting assembly or llvm IR we skip linking.
1034310360 // 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);
6464bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type);
6565bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async);
6666
67void codegen_report_errors_and_exit(CodeGen *g);
68
6769#endif
src/dump_analysis.cpp created+769
......@@ -0,0 +1,769 @@
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
15enum JsonWriterState {
16 JsonWriterStateInvalid,
17 JsonWriterStateValue,
18 JsonWriterStateArrayStart,
19 JsonWriterStateArray,
20 JsonWriterStateObjectStart,
21 JsonWriterStateObject,
22};
23
24#define JSON_MAX_DEPTH 10
25
26struct 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
34static 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
43static 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
51static 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
57static void jw_pop_state(JsonWriter *jw) {
58 assert(jw->state_index != 0);
59 jw->state_index -= 1;
60}
61
62static 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
68static 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
74static 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
93static 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
128static 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
149static 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
170static 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
193static 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
199static 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
209static 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
219static 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
226static void tree_print(FILE *f, ZigType *ty, size_t indent);
227
228static 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
245static 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
255static 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
262static 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
269static 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
304static 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
327void 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
339struct 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
356static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty);
357static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value);
358
359static 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
385static 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
396static 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
408static 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
419static 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
463static 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
468static 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
473static 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
478static 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
483static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) {
484 JsonWriter *jw = &ctx->jw;
485 jw_array_elem(jw);
486 jw_begin_object(jw);
487
488 jw_object_field(jw, "name");
489 jw_string(jw, buf_ptr(&pkg->pkg_path));
490
491 jw_object_field(jw, "file");
492 Buf full_path_buf = BUF_INIT;
493 os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf);
494 Buf *resolve_paths[] = { &full_path_buf, };
495 Buf *resolved_path = buf_alloc();
496 *resolved_path = os_path_resolve(resolve_paths, 1);
497 anal_dump_file_ref(ctx, resolved_path);
498
499 auto import_entry = ctx->g->import_table.maybe_get(resolved_path);
500 if (!import_entry) {
501 fprintf(stderr, "due to a race condition or bug, files moved around during analysis\n");
502 exit(1);
503 }
504 jw_object_field(jw, "main");
505 anal_dump_type_ref(ctx, import_entry->value);
506
507 jw_object_field(jw, "table");
508 jw_begin_object(jw);
509 auto it = pkg->package_table.entry_iterator();
510 for (;;) {
511 auto *entry = it.next();
512 if (!entry)
513 break;
514
515 ZigPackage *child_pkg = entry->value;
516 if (child_pkg != nullptr) {
517 jw_object_field(jw, buf_ptr(entry->key));
518 anal_dump_pkg_ref(ctx, child_pkg);
519 }
520 }
521 jw_end_object(jw);
522
523 jw_end_object(jw);
524}
525
526static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) {
527 JsonWriter *jw = &ctx->jw;
528
529 bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn;
530 if (make_obj) {
531 jw_array_elem(jw);
532 jw_begin_object(jw);
533
534 jw_object_field(jw, "import");
535 anal_dump_type_ref(ctx, tld->import);
536
537 jw_object_field(jw, "line");
538 jw_int(jw, tld->source_node->line);
539
540 jw_object_field(jw, "col");
541 jw_int(jw, tld->source_node->column);
542
543 jw_object_field(jw, "name");
544 jw_string(jw, buf_ptr(tld->name));
545 }
546
547 switch (tld->id) {
548 case TldIdVar: {
549 TldVar *tld_var = reinterpret_cast<TldVar *>(tld);
550 ZigVar *var = tld_var->var;
551
552 if (var != nullptr) {
553 jw_object_field(jw, "kind");
554 if (var->src_is_const) {
555 jw_string(jw, "const");
556 } else {
557 jw_string(jw, "var");
558 }
559
560 if (var->is_thread_local) {
561 jw_object_field(jw, "threadlocal");
562 jw_bool(jw, true);
563 }
564
565 jw_object_field(jw, "type");
566 anal_dump_type_ref(ctx, var->var_type);
567
568 if (var->const_value != nullptr) {
569 jw_object_field(jw, "value");
570 anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value);
571 }
572 }
573 break;
574 }
575 case TldIdFn: {
576 TldFn *tld_fn = reinterpret_cast<TldFn *>(tld);
577 ZigFn *fn = tld_fn->fn_entry;
578
579 if (fn != nullptr) {
580 jw_object_field(jw, "kind");
581 jw_string(jw, "const");
582
583 jw_object_field(jw, "type");
584 anal_dump_type_ref(ctx, fn->type_entry);
585 }
586
587 break;
588 }
589 default:
590 break;
591 }
592
593 if (make_obj) {
594 jw_end_object(jw);
595 }
596}
597
598static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) {
599 JsonWriter *jw = &ctx->jw;
600 jw_string(jw, buf_ptr(file));
601}
602
603static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ConstExprValue *value) {
604 Error err;
605
606 if (value->type != ty) {
607 jw_null(&ctx->jw);
608 return;
609 }
610 if ((err = ir_resolve_lazy(ctx->g, source_node, value))) {
611 codegen_report_errors_and_exit(ctx->g);
612 }
613 if (value->special == ConstValSpecialUndef) {
614 jw_string(&ctx->jw, "undefined");
615 return;
616 }
617 if (value->special == ConstValSpecialRuntime) {
618 jw_null(&ctx->jw);
619 return;
620 }
621 switch (ty->id) {
622 case ZigTypeIdMetaType: {
623 ZigType *val_ty = value->data.x_type;
624 anal_dump_type_ref(ctx, val_ty);
625 return;
626 }
627 default:
628 jw_null(&ctx->jw);
629 return;
630 }
631 zig_unreachable();
632}
633
634static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
635 JsonWriter *jw = &ctx->jw;
636 jw_array_elem(jw);
637 jw_begin_object(jw);
638
639 jw_object_field(jw, "name");
640 jw_string(jw, buf_ptr(&ty->name));
641
642 jw_object_field(jw, "kind");
643 jw_int(jw, type_id_index(ty));
644
645 switch (ty->id) {
646 case ZigTypeIdStruct: {
647 if (ty->data.structure.is_slice) {
648 // TODO
649 break;
650 }
651 jw_object_field(jw, "decls");
652 jw_begin_array(jw);
653
654 ScopeDecls *decls_scope = ty->data.structure.decls_scope;
655 auto it = decls_scope->decl_table.entry_iterator();
656 for (;;) {
657 auto *entry = it.next();
658 if (!entry)
659 break;
660
661 Tld *tld = entry->value;
662
663 jw_array_elem(jw);
664 anal_dump_decl_ref(ctx, tld);
665 }
666 jw_end_array(jw);
667
668 if (ty->data.structure.root_struct != nullptr) {
669 Buf *path_buf = ty->data.structure.root_struct->path;
670
671 jw_object_field(jw, "file");
672 anal_dump_file_ref(ctx, path_buf);
673
674 }
675 break;
676 }
677 case ZigTypeIdFloat: {
678 jw_object_field(jw, "bits");
679 jw_int(jw, ty->data.floating.bit_count);
680 break;
681 }
682 default:
683 // TODO
684 break;
685 }
686 jw_end_object(jw);
687}
688
689void zig_print_analysis_dump(CodeGen *g, FILE *f) {
690 Error err;
691 AnalDumpCtx ctx = {};
692 ctx.g = g;
693 JsonWriter *jw = &ctx.jw;
694 jw_init(jw, f, " ", "\n");
695 ctx.type_map.init(16);
696 ctx.pkg_map.init(16);
697 ctx.file_map.init(16);
698 ctx.decl_map.init(16);
699
700 jw_begin_object(jw);
701
702 jw_object_field(jw, "typeKinds");
703 jw_begin_array(jw);
704 for (size_t i = 0; i < type_id_len(); i += 1) {
705 jw_array_elem(jw);
706 jw_string(jw, type_id_name(type_id_at_index(i)));
707 }
708 jw_end_array(jw);
709
710 jw_object_field(jw, "params");
711 jw_begin_object(jw);
712 {
713 jw_object_field(jw, "zigId");
714
715 Buf *compiler_id;
716 if ((err = get_compiler_id(&compiler_id))) {
717 fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err));
718 exit(1);
719 }
720 jw_string(jw, buf_ptr(compiler_id));
721
722 jw_object_field(jw, "zigVersion");
723 jw_string(jw, ZIG_VERSION_STRING);
724
725 jw_object_field(jw, "target");
726 Buf triple_buf = BUF_INIT;
727 target_triple_zig(&triple_buf, g->zig_target);
728 jw_string(jw, buf_ptr(&triple_buf));
729 }
730 jw_end_object(jw);
731
732 jw_object_field(jw, "rootPkg");
733 anal_dump_pkg_ref(&ctx, g->root_package);
734
735 jw_object_field(jw, "packages");
736 jw_begin_array(jw);
737 for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) {
738 anal_dump_pkg(&ctx, ctx.pkg_list.at(i));
739 }
740 jw_end_array(jw);
741
742 jw_object_field(jw, "types");
743 jw_begin_array(jw);
744
745 for (uint32_t i = 0; i < ctx.type_list.length; i += 1) {
746 ZigType *ty = ctx.type_list.at(i);
747 anal_dump_type(&ctx, ty);
748 }
749 jw_end_array(jw);
750
751 jw_object_field(jw, "decls");
752 jw_begin_array(jw);
753 for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) {
754 Tld *decl = ctx.decl_list.at(i);
755 anal_dump_decl(&ctx, decl);
756 }
757 jw_end_array(jw);
758
759 jw_object_field(jw, "files");
760 jw_begin_array(jw);
761 for (uint32_t i = 0; i < ctx.file_list.length; i += 1) {
762 Buf *file = ctx.file_list.at(i);
763 jw_array_elem(jw);
764 anal_dump_file(&ctx, file);
765 }
766 jw_end_array(jw);
767
768 jw_end_object(jw);
769}
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
14void zig_print_stack_report(CodeGen *g, FILE *f);
15void zig_print_analysis_dump(CodeGen *g, FILE *f);
16
17#endif
src/main.cpp+6-1
......@@ -16,7 +16,7 @@
1616#include "libc_installation.hpp"
1717#include "userland.h"
1818#include "glibc.hpp"
19#include "stack_report.hpp"
19#include "dump_analysis.hpp"
2020
2121#include <stdio.h>
2222
......@@ -64,6 +64,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
6464 " -fno-PIC disable Position Independent Code\n"
6565 " -ftime-report print timing diagnostics\n"
6666 " -fstack-report print stack size diagnostics\n"
67 " -fdump-analysis write analysis.json file for use with zig docs\n"
6768 " --libc [file] Provide a file which specifies libc paths\n"
6869 " --name [name] override output name\n"
6970 " --output-dir [dir] override output directory (defaults to cwd)\n"
......@@ -479,6 +480,7 @@ int main(int argc, char **argv) {
479480 size_t ver_patch = 0;
480481 bool timing_info = false;
481482 bool stack_report = false;
483 bool enable_dump_analysis = false;
482484 const char *cache_dir = nullptr;
483485 CliPkg *cur_pkg = allocate<CliPkg>(1);
484486 BuildMode build_mode = BuildModeDebug;
......@@ -662,6 +664,8 @@ int main(int argc, char **argv) {
662664 timing_info = true;
663665 } else if (strcmp(arg, "-fstack-report") == 0) {
664666 stack_report = true;
667 } else if (strcmp(arg, "-fdump-analysis") == 0) {
668 enable_dump_analysis = true;
665669 } else if (strcmp(arg, "--enable-valgrind") == 0) {
666670 valgrind_support = ValgrindSupportEnabled;
667671 } else if (strcmp(arg, "--disable-valgrind") == 0) {
......@@ -1138,6 +1142,7 @@ int main(int argc, char **argv) {
11381142
11391143 g->enable_time_report = timing_info;
11401144 g->enable_stack_report = stack_report;
1145 g->enable_dump_analysis = enable_dump_analysis;
11411146 codegen_set_out_name(g, buf_out_name);
11421147 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
11431148 g->want_single_threaded = want_single_threaded;
src/os.hpp+2
......@@ -47,6 +47,7 @@ extern const char *possible_ld_names[];
4747
4848#if defined(ZIG_OS_WINDOWS)
4949#define ZIG_PRI_usize "I64u"
50#define ZIG_PRI_i64 "I64d"
5051#define ZIG_PRI_u64 "I64u"
5152#define ZIG_PRI_llu "I64u"
5253#define ZIG_PRI_x64 "I64x"
......@@ -54,6 +55,7 @@ extern const char *possible_ld_names[];
5455#define ZIG_OS_SEP_CHAR '\\'
5556#else
5657#define ZIG_PRI_usize "zu"
58#define ZIG_PRI_i64 PRId64
5759#define ZIG_PRI_u64 PRIu64
5860#define ZIG_PRI_llu "llu"
5961#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
10static void tree_print(FILE *f, ZigType *ty, size_t indent);
11
12static 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
29static 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
39static 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
46static 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
53static 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
88static 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
111void 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
14void zig_print_stack_report(CodeGen *g, FILE *f);
15
16#endif