From 7e58c56ca72099f6e71752289be7165947bfaa04 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Jun 2020 04:29:54 -0400 Subject: self-hosted: implement Decl lookup * Take advantage of coercing anonymous struct literals to struct types. * Reworks Module to favor Zig source as the primary use case. Breaks ZIR compilation, which will have to be restored in a future commit. * Decl uses src_index rather then src, pointing to an AST Decl node index, or ZIR Module Decl index, rather than a byte offset. * ZIR instructions have an `analyzed_inst` field instead of Module having a hash table. * Module.Fn loses the `fn_type` field since it is redundant with its `owner_decl` `TypedValue` type. * Implement Type and Value copying. A ZIR Const instruction's TypedValue is copied to the Decl arena during analysis, which allows freeing the ZIR text instructions post-analysis. * Don't flush the ELF file if there are compilation errors. * Function return types allow arbitrarily complex expressions. * AST->ZIR for function calls and return statements. --- src-self-hosted/codegen.zig | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src-self-hosted/codegen.zig') diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index a3a15b463e..f412b2dad2 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -178,6 +178,7 @@ const Function = struct { .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), + .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?), .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), @@ -213,7 +214,7 @@ const Function = struct { try self.code.resize(self.code.items.len + 7); self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 }; mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr); - const return_type = func.fn_type.fnReturnType(); + const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType(); switch (return_type.zigTypeTag()) { .Void => return MCValue{ .none = {} }, .NoReturn => return MCValue{ .unreach = {} }, @@ -230,16 +231,28 @@ const Function = struct { } } - fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { + fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue { + if (mcv != .none) { + return self.fail(src, "TODO implement return with non-void operand", .{}); + } switch (self.target.cpu.arch) { .i386, .x86_64 => { try self.code.append(0xc3); // ret }, - else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}), + else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), } return .unreach; } + fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { + const operand = try self.resolveInst(inst.args.operand); + return self.ret(inst.base.src, operand); + } + + fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue { + return self.ret(inst.base.src, .none); + } + fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { switch (self.target.cpu.arch) { else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), -- cgit v1.2.3 From c9a0ec25e0baae7f128a8f7efe4d308af7fad753 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Jun 2020 19:03:41 -0400 Subject: self-hosted: add Tracy integration This tool helps give an intuitive picture of performance. This will help us understand where to improve the code. --- build.zig | 13 +++++++++++++ lib/std/build.zig | 5 +++-- src-self-hosted/Module.zig | 16 ++++++++++++++++ src-self-hosted/codegen.zig | 4 ++++ src-self-hosted/tracy.zig | 45 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src-self-hosted/tracy.zig (limited to 'src-self-hosted/codegen.zig') diff --git a/build.zig b/build.zig index 4d71b0fb36..e2af4ba8ce 100644 --- a/build.zig +++ b/build.zig @@ -72,9 +72,22 @@ pub fn build(b: *Builder) !void { if (!only_install_lib_files) { exe.install(); } + const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; if (link_libc) exe.linkLibC(); + exe.addBuildOption(bool, "enable_tracy", tracy != null); + if (tracy) |tracy_path| { + const client_cpp = fs.path.join( + b.allocator, + &[_][]const u8{ tracy_path, "TracyClient.cpp" }, + ) catch unreachable; + exe.addIncludeDir(tracy_path); + exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); + exe.linkSystemLibraryName("c++"); + exe.linkLibC(); + } + b.installDirectory(InstallDirectoryOptions{ .source_dir = "lib", .install_dir = .Lib, diff --git a/lib/std/build.zig b/lib/std/build.zig index d98ef71a59..3a6585c90f 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct { builder.allocator, &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) }, ); - try fs.cwd().writeFile(build_options_file, self.build_options_contents.span()); + const path_from_root = builder.pathFromRoot(build_options_file); + try fs.cwd().writeFile(path_from_root, self.build_options_contents.span()); try zig_args.append("--pkg-begin"); try zig_args.append("build_options"); - try zig_args.append(builder.pathFromRoot(build_options_file)); + try zig_args.append(path_from_root); try zig_args.append("--pkg-end"); } diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 947495f0ae..508a9b2799 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -16,6 +16,7 @@ const zir = @import("zir.zig"); const Module = @This(); const Inst = ir.Inst; const ast = std.zig.ast; +const trace = @import("tracy.zig").trace; /// General-purpose allocator. allocator: *Allocator, @@ -796,6 +797,9 @@ pub fn target(self: Module) std.Target { /// Detect changes to source files, perform semantic analysis, and update the output files. pub fn update(self: *Module) !void { + const tracy = trace(@src()); + defer tracy.end(); + self.generation += 1; // TODO Use the cache hash file system to detect which source files changed. @@ -1050,6 +1054,9 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { } fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void { + const tracy = trace(@src()); + defer tracy.end(); + const file_scope = decl.scope.cast(Scope.File).?; const tree = try self.getAstTree(file_scope); const ast_node = tree.root_node.decls()[decl.src_index]; @@ -1330,6 +1337,9 @@ fn astGenIntegerLiteral(self: *Module, scope: *Scope, int_lit: *ast.Node.Integer } fn astGenBlock(self: *Module, scope: *Scope, block_node: *ast.Node.Block) !void { + const tracy = trace(@src()); + defer tracy.end(); + if (block_node.label) |label| { return self.failTok(scope, label, "TODO implement labeled blocks", .{}); } @@ -1526,6 +1536,9 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { } fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { + const tracy = trace(@src()); + defer tracy.end(); + switch (root_scope.status) { .never_loaded, .unloaded_success => { try self.failed_files.ensureCapacity(self.failed_files.size + 1); @@ -1739,6 +1752,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { } fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { + const tracy = trace(@src()); + defer tracy.end(); + // Use the Decl's arena for function memory. var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator); defer decl.typed_value.most_recent.arena.?.* = arena.state; diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index f412b2dad2..7473ccc431 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -10,6 +10,7 @@ const Module = @import("Module.zig"); const ErrorMsg = Module.ErrorMsg; const Target = std.Target; const Allocator = mem.Allocator; +const trace = @import("tracy.zig").trace; pub const Result = union(enum) { /// The `code` parameter passed to `generateSymbol` has the value appended. @@ -29,6 +30,9 @@ pub fn generateSymbol( /// A Decl that this symbol depends on had a semantic analysis failure. AnalysisFail, }!Result { + const tracy = trace(@src()); + defer tracy.end(); + switch (typed_value.ty.zigTypeTag()) { .Fn => { const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; diff --git a/src-self-hosted/tracy.zig b/src-self-hosted/tracy.zig new file mode 100644 index 0000000000..1e480d75b0 --- /dev/null +++ b/src-self-hosted/tracy.zig @@ -0,0 +1,45 @@ +pub const std = @import("std"); + +pub const enable = @import("build_options").enable_tracy; + +extern fn ___tracy_emit_zone_begin_callstack( + srcloc: *const ___tracy_source_location_data, + depth: c_int, + active: c_int, +) ___tracy_c_zone_context; + +extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void; + +pub const ___tracy_source_location_data = extern struct { + name: ?[*:0]const u8, + function: [*:0]const u8, + file: [*:0]const u8, + line: u32, + color: u32, +}; + +pub const ___tracy_c_zone_context = extern struct { + id: u32, + active: c_int, + + pub fn end(self: ___tracy_c_zone_context) void { + ___tracy_emit_zone_end(self); + } +}; + +pub const Ctx = if (enable) ___tracy_c_zone_context else struct { + pub fn end(self: Ctx) void {} +}; + +pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx { + if (!enable) return .{}; + + const loc: ___tracy_source_location_data = .{ + .name = null, + .function = src.fn_name.ptr, + .file = src.file.ptr, + .line = src.line, + .color = 0, + }; + return ___tracy_emit_zone_begin_callstack(&loc, 1, 1); +} -- cgit v1.2.3