diff --git a/lib/std/hash.zig b/lib/std/hash.zig index ab3a0ea8f3..c51353b328 100644 --- a/lib/std/hash.zig +++ b/lib/std/hash.zig @@ -3,6 +3,8 @@ pub const Adler32 = adler.Adler32; const auto_hash = @import("hash/auto_hash.zig"); pub const autoHash = auto_hash.autoHash; +pub const autoHashStrat = auto_hash.hash; +pub const Strategy = auto_hash.HashStrategy; // pub for polynomials + generic crc32 construction pub const crc = @import("hash/crc.zig"); diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 4ffe88067b..677dfe4435 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -550,3 +550,13 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { } }.eql; } + +pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) { + return struct { + fn hash(key: K) u32 { + var hasher = Wyhash.init(0); + std.hash.autoHashStrat(&hasher, key, strategy); + return @truncate(u32, hasher.final()); + } + }.hash; +} diff --git a/lib/std/json/write_stream.zig b/lib/std/json/write_stream.zig index 2bb79a017c..c30f8ba8d8 100644 --- a/lib/std/json/write_stream.zig +++ b/lib/std/json/write_stream.zig @@ -152,10 +152,17 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type { ) !void { assert(self.state[self.state_index] == State.Value); switch (@typeInfo(@typeOf(value))) { - .Int => |info| if (info.bits < 53 or (value < 4503599627370496 and value > -4503599627370496)) { - try self.stream.print("{}", value); - self.popState(); - return; + .Int => |info| { + if (info.bits < 53) { + try self.stream.print("{}", value); + self.popState(); + return; + } + if (value < 4503599627370496 and (!info.is_signed or value > -4503599627370496)) { + try self.stream.print("{}", value); + self.popState(); + return; + } }, .Float => if (@floatCast(f64, value) == value) { try self.stream.print("{}", value); diff --git a/src/all_types.hpp b/src/all_types.hpp index f13a5577d2..0ce55e598b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2091,6 +2091,7 @@ struct CodeGen { bool function_sections; bool enable_dump_analysis; bool enable_doc_generation; + bool disable_bin_generation; Buf *mmacosx_version_min; Buf *mios_version_min; diff --git a/src/codegen.cpp b/src/codegen.cpp index e2e117d27a..4cdf22b1eb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7586,6 +7586,8 @@ static void zig_llvm_emit_output(CodeGen *g) { char *err_msg = nullptr; switch (g->emit_file_type) { case EmitFileTypeBinary: + if (g->disable_bin_generation) + return; if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small, g->enable_time_report)) @@ -10158,6 +10160,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->function_sections); cache_bool(ch, g->enable_dump_analysis); cache_bool(ch, g->enable_doc_generation); + cache_bool(ch, g->disable_bin_generation); cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); @@ -10396,7 +10399,8 @@ void codegen_build_and_link(CodeGen *g) { // If there is more than one object, we have to link them (with -r). // Finally, if we didn't make an object from zig source, and we don't have caching enabled, // then we have an object from C source that we must copy to the output dir which we do with a -r link. - if (g->emit_file_type == EmitFileTypeBinary && (g->out_type != OutTypeObj || g->link_objects.length > 1 || + if (!g->disable_bin_generation && g->emit_file_type == EmitFileTypeBinary && + (g->out_type != OutTypeObj || g->link_objects.length > 1 || (!need_llvm_module(g) && !g->enable_cache))) { codegen_link(g); diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp index 6e84095dfd..954f6d2ab2 100644 --- a/src/dump_analysis.cpp +++ b/src/dump_analysis.cpp @@ -1309,15 +1309,6 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const } 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_object_field(jw, "errors"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { @@ -1336,5 +1327,14 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const } 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/main.cpp b/src/main.cpp index fb2881f3a3..d5901f73bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,7 +65,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -ftime-report print timing diagnostics\n" " -fstack-report print stack size diagnostics\n" " -fdump-analysis write analysis.json file with type information\n" - " -fgenerate-docs create a docs/ dir with html documentation\n" + " -femit-docs create a docs/ dir with html documentation\n" + " -fno-emit-bin skip emitting machine code\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" @@ -483,6 +484,7 @@ int main(int argc, char **argv) { bool stack_report = false; bool enable_dump_analysis = false; bool enable_doc_generation = false; + bool disable_bin_generation = false; const char *cache_dir = nullptr; CliPkg *cur_pkg = allocate(1); BuildMode build_mode = BuildModeDebug; @@ -668,8 +670,10 @@ int main(int argc, char **argv) { stack_report = true; } else if (strcmp(arg, "-fdump-analysis") == 0) { enable_dump_analysis = true; - } else if (strcmp(arg, "-fgenerate-docs") == 0) { + } else if (strcmp(arg, "-femit-docs") == 0) { enable_doc_generation = true; + } else if (strcmp(arg, "-fno-emit-bin") == 0) { + disable_bin_generation = true; } else if (strcmp(arg, "--enable-valgrind") == 0) { valgrind_support = ValgrindSupportEnabled; } else if (strcmp(arg, "--disable-valgrind") == 0) { @@ -1148,6 +1152,7 @@ int main(int argc, char **argv) { g->enable_stack_report = stack_report; g->enable_dump_analysis = enable_dump_analysis; g->enable_doc_generation = enable_doc_generation; + g->disable_bin_generation = disable_bin_generation; 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; @@ -1290,6 +1295,11 @@ int main(int argc, char **argv) { zig_print_stack_report(g, stdout); } + if (g->disable_bin_generation) { + fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n"); + return 0; + } + Buf *test_exe_path_unresolved = &g->output_file_path; Buf *test_exe_path = buf_alloc(); *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1); diff --git a/tools/merge_anal_dumps.zig b/tools/merge_anal_dumps.zig index 65b21766f4..ccd13999df 100644 --- a/tools/merge_anal_dumps.zig +++ b/tools/merge_anal_dumps.zig @@ -24,19 +24,49 @@ pub fn main() anyerror!void { try dump.render(&stdout.outStream().stream); } +/// AST source node +const Node = struct { + file: usize, + line: usize, + col: usize, + fields: []usize, + + fn hash(n: Node) u32 { + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, n.file); + std.hash.autoHash(&hasher, n.line); + std.hash.autoHash(&hasher, n.col); + return @truncate(u32, hasher.final()); + } + + fn eql(a: Node, b: Node) bool { + return a.file == b.file and + a.line == b.line and + a.col == b.col; + } +}; + const Dump = struct { zig_id: ?[]const u8 = null, zig_version: ?[]const u8 = null, root_name: ?[]const u8 = null, targets: std.ArrayList([]const u8), - files_list: std.ArrayList([]const u8), - files_map: std.StringHashMap(usize), + + const FileMap = std.StringHashMap(usize); + file_list: std.ArrayList([]const u8), + file_map: FileMap, + + const NodeMap = std.HashMap(Node, usize, Node.hash, Node.eql); + node_list: std.ArrayList(Node), + node_map: NodeMap, fn init(allocator: *mem.Allocator) Dump { return Dump{ .targets = std.ArrayList([]const u8).init(allocator), - .files_list = std.ArrayList([]const u8).init(allocator), - .files_map = std.StringHashMap(usize).init(allocator), + .file_list = std.ArrayList([]const u8).init(allocator), + .file_map = FileMap.init(allocator), + .node_list = std.ArrayList(Node).init(allocator), + .node_map = NodeMap.init(allocator), }; } @@ -56,17 +86,45 @@ const Dump = struct { const other_files = root.Object.get("files").?.value.Array.toSliceConst(); var other_file_to_mine = std.AutoHashMap(usize, usize).init(self.a()); for (other_files) |other_file, i| { - const gop = try self.files_map.getOrPut(other_file.String); - if (gop.found_existing) { - try other_file_to_mine.putNoClobber(i, gop.kv.value); - } else { - gop.kv.value = self.files_list.len; - try self.files_list.append(other_file.String); + const gop = try self.file_map.getOrPut(other_file.String); + if (!gop.found_existing) { + gop.kv.value = self.file_list.len; + try self.file_list.append(other_file.String); } + try other_file_to_mine.putNoClobber(i, gop.kv.value); } + // Merge ast nodes const other_ast_nodes = root.Object.get("astNodes").?.value.Array.toSliceConst(); var other_ast_node_to_mine = std.AutoHashMap(usize, usize).init(self.a()); + for (other_ast_nodes) |other_ast_node_json, i| { + const other_file_id = jsonObjInt(other_ast_node_json, "file"); + const other_node = Node{ + .line = jsonObjInt(other_ast_node_json, "line"), + .col = jsonObjInt(other_ast_node_json, "col"), + .file = other_file_to_mine.getValue(other_file_id).?, + .fields = ([*]usize)(undefined)[0..0], + }; + const gop = try self.node_map.getOrPut(other_node); + if (!gop.found_existing) { + gop.kv.value = self.node_list.len; + try self.node_list.append(other_node); + } + try other_ast_node_to_mine.putNoClobber(i, gop.kv.value); + } + // convert fields lists + for (other_ast_nodes) |other_ast_node_json, i| { + const my_node_index = other_ast_node_to_mine.get(i).?.value; + const my_node = &self.node_list.toSlice()[my_node_index]; + if (other_ast_node_json.Object.get("fields")) |fields_json_kv| { + const other_fields = fields_json_kv.value.Array.toSliceConst(); + my_node.fields = try self.a().alloc(usize, other_fields.len); + for (other_fields) |other_field_index, field_i| { + const other_index = @intCast(usize, other_field_index.Integer); + my_node.fields[field_i] = other_ast_node_to_mine.get(other_index).?.value; + } + } + } } fn render(self: *Dump, stream: var) !void { @@ -81,9 +139,39 @@ const Dump = struct { } try jw.endArray(); + try jw.objectField("astNodes"); + try jw.beginArray(); + for (self.node_list.toSliceConst()) |node| { + try jw.arrayElem(); + try jw.beginObject(); + + try jw.objectField("file"); + try jw.emitNumber(node.file); + + try jw.objectField("line"); + try jw.emitNumber(node.line); + + try jw.objectField("col"); + try jw.emitNumber(node.col); + + if (node.fields.len != 0) { + try jw.objectField("fields"); + try jw.beginArray(); + + for (node.fields) |field_node_index| { + try jw.arrayElem(); + try jw.emitNumber(field_node_index); + } + try jw.endArray(); + } + + try jw.endObject(); + } + try jw.endArray(); + try jw.objectField("files"); try jw.beginArray(); - for (self.files_list.toSliceConst()) |file| { + for (self.file_list.toSliceConst()) |file| { try jw.arrayElem(); try jw.emitString(file); } @@ -105,3 +193,8 @@ const Dump = struct { } } }; + +fn jsonObjInt(json_val: json.Value, field: []const u8) usize { + const uncasted = json_val.Object.get(field).?.value.Integer; + return @intCast(usize, uncasted); +}