From f102fe93319b8fcc9b88bdef0fa69bda967d15a7 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Mon, 20 May 2024 13:45:39 -0700 Subject: [PATCH] perf: use more buffered writers --- CHANGELOG.md | 2 +- build.zig | 12 ++++++++++ demo.txt | 26 ++++++++++++++++++++- src/compiler/JsLink.zig | 37 ++++++++++++++++++++++++++++++ src/compiler/JsNodeGen.zig | 46 ++++++++++++++++++++++++++++++++++++++ src/compiler/Report.zig | 23 ++++++++++++------- src/compiler/Sema.zig | 8 ++++--- src/compiler/Transform.zig | 6 +++-- src/compiler/main.zig | 42 +++++++++++++++++++++++----------- 9 files changed, 174 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c74c5fc58489f3128134c2b03b80ff86f6f2b54..e208b2cba7d5a2b92d7d58de6d1a288c6e969736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.1.1-alpha -- introduce `Report` and show errors during Transform. +- introduce `Report` and show errors during Tokenize, Parse, and Transform. - add flag --dump-ir for enabling ir dumps ## 0.1.0-alpha diff --git a/build.zig b/build.zig index 4901620fb6c1ec93c4316653b2689d44456b7adc..40a4cad33305d73b4236a37d9dd0f3270b737d05 100644 --- a/build.zig +++ b/build.zig @@ -6,12 +6,23 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const tracing = b.option(enum { + none, + spall, + chrome, + log, + }, "trace", "Enable tracing") orelse .none; + const dep_opts = .{ .target = target, .optimize = optimize }; const zg = b.dependency("zg", dep_opts); const tracer = b.dependency("tracer", dep_opts); // const ada = b.dependency("ada", dep_opts); + const opts = b.addOptions(); + opts.addOption([]const u8, "tracing", @tagName(tracing)); + const opts_mod = opts.createModule(); + const exe = b.addExecutable(.{ .name = "compiler", .root_source_file = b.path("src/compiler/main.zig"), @@ -20,6 +31,7 @@ pub fn build(b: *std.Build) void { }); b.installArtifact(exe); + exe.root_module.addImport("build_options", opts_mod); exe.root_module.addImport("zg:PropsData", zg.module("PropsData")); exe.root_module.addImport("tracer", tracer.module("tracer")); // if (!target.result.isWasm()) { diff --git a/demo.txt b/demo.txt index 39750d3339d522e2ab214079e016390dd62e5d8f..ec22ae0f6084addf11709de75976f2a0fbce62ec 100644 --- a/demo.txt +++ b/demo.txt @@ -1 +1,25 @@ -no "waay +let print = @js("console").log; + +fn doThing(x: float, y: float) -> float { + let math = if x < y { + x - y + } else b: { + let sum = x + y; + if sum > 100 { + break :b sum; + } + if x == y { + return 5; + } + sum * -sum + }; + if x == y { + return 5; + } + let divisor = if x > 10 { 2 } else { 3 }; + math / divisor +} + +print(doThing(1, 6)); +print(doThing(6, 4)); +print(doThing(100, 52)); diff --git a/src/compiler/JsLink.zig b/src/compiler/JsLink.zig index 31fddb953304cf48ddf5e5c94fdcf825add9a6dc..d7c0d57877747498e89f09fcfe5752b6c2e9acde 100644 --- a/src/compiler/JsLink.zig +++ b/src/compiler/JsLink.zig @@ -19,6 +19,8 @@ pub fn linkJsCode( decls: []const Decl.Index, writer: anytype, ) !void { + var t = tracer.trace(@src()); + defer t.end(); // Arena allocator is used for the main flow var link: JsLink = .{ .comp = comp }; @@ -79,6 +81,9 @@ pub fn linkJsCode( } fn deinit(link: *JsLink) void { + var t = tracer.trace(@src()); + defer t.end(); + defer link.* = undefined; link.decls.deinit(link.comp.gpa); link.link_strings.deinit(link.comp.gpa); @@ -86,6 +91,9 @@ fn deinit(link: *JsLink) void { } fn reserveExternSymbol(link: *JsLink, extern_symbol: String.WithLen) !void { + var t = tracer.trace(@src()); + defer t.end(); + const name = extern_symbol.toSlice(&link.comp.intern_pool); const gop = try link.link_strings.getOrPutContextAdapted( link.comp.gpa, @@ -104,12 +112,18 @@ fn reserveExternSymbol(link: *JsLink, extern_symbol: String.WithLen) !void { } fn appendLinkString(link: *JsLink, string: []const u8) !LinkString { + var t = tracer.trace(@src()); + defer t.end(); + const start = link.string_bytes.items.len; try link.string_bytes.appendSlice(link.comp.gpa, string); return .{ .start = @intCast(start), .len = @intCast(string.len) }; } fn declIdentifier(link: *const JsLink, decl: Decl.Index) []const u8 { + var t = tracer.trace(@src()); + defer t.end(); + return (link.decls.get(decl) orelse if (std.debug.runtime_safety) { std.debug.panic("Decl {d} ({}) is not present in link decl map", .{ @intFromEnum(decl), @@ -119,6 +133,9 @@ fn declIdentifier(link: *const JsLink, decl: Decl.Index) []const u8 { } fn printDecl(link: *JsLink, decl_index: Decl.Index, writer: anytype) !void { + var t = tracer.trace(@src()); + defer t.end(); + try writer.writeAll("let "); try writer.writeAll(link.declIdentifier(decl_index)); try writer.writeAll(" = "); @@ -137,6 +154,9 @@ fn printDecl(link: *JsLink, decl_index: Decl.Index, writer: anytype) !void { } fn printDeclContents(link: *JsLink, tree: *const JsTree, writer: anytype) !void { + var t = tracer.trace(@src()); + defer t.end(); + for (tree.mainStatements()) |stmt| { try link.printExpr(tree, stmt, writer); try writer.writeAll(";\n"); @@ -144,6 +164,9 @@ fn printDeclContents(link: *JsLink, tree: *const JsTree, writer: anytype) !void } fn printBlockContents(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer: anytype) !void { + var t = tracer.trace(@src()); + defer t.end(); + assert(list.slice(tree).len > 0); for (list.slice(tree)) |stmt| { try link.printExpr(tree, stmt, writer); @@ -158,6 +181,9 @@ fn printBlockContents(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList } fn printBlock(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer: anytype) !void { + var t = tracer.trace(@src()); + defer t.end(); + if (list.len != 1) { try writer.writeAll("{"); try link.printBlockContents(tree, list, writer); @@ -168,6 +194,9 @@ fn printBlock(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer } fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anytype) @TypeOf(writer).Error!void { + var t = tracer.trace(@src()); + defer t.end(); + switch (tree.nodeTag(node)) { else => |tag| std.debug.panic("TODO: printExpr .{s}", .{@tagName(tag)}), .root => unreachable, @@ -328,11 +357,17 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty } fn printLocal(link: *JsLink, local: JsTree.LocalIndex, writer: anytype) !void { + var t = tracer.trace(@src()); + defer t.end(); + _ = link; // todo: be smarter about this try writer.print("v{d}", .{local}); } fn printLabelIdentifier(link: *JsLink, local: JsTree.LocalIndex, writer: anytype) !void { + var t = tracer.trace(@src()); + defer t.end(); + _ = link; // todo: be smarter about this try writer.print("label{d}", .{local}); } @@ -452,3 +487,5 @@ const Node = JsTree.Node; const InternPool = @import("InternPool.zig"); const String = InternPool.String; + +const tracer = @import("tracer"); diff --git a/src/compiler/JsNodeGen.zig b/src/compiler/JsNodeGen.zig index 4bfd52d61676ed7b2759e07bd2b91196428b869c..6844decf311e81bba302324c23b1e9222a5b7833 100644 --- a/src/compiler/JsNodeGen.zig +++ b/src/compiler/JsNodeGen.zig @@ -29,6 +29,9 @@ const MapEntry = union { }; pub fn init(gpa: Allocator, arena: Allocator, ip: *const InternPool, air: *const Air) !JsNodeGen { + const t = tracer.trace(@src()); + defer t.end(); + const air_node_map = try arena.alloc(MapEntry, air.insts.len); if (std.debug.runtime_safety) { @memset(air_node_map, .{ .none = {} }); @@ -52,6 +55,9 @@ pub fn init(gpa: Allocator, arena: Allocator, ip: *const InternPool, air: *const } pub fn deinit(c: *JsNodeGen) void { + const t = tracer.trace(@src()); + defer t.end(); + defer c.* = undefined; c.nodes.deinit(c.gpa); c.extra.deinit(c.gpa); @@ -62,6 +68,9 @@ pub fn deinit(c: *JsNodeGen) void { } pub fn finalize(c: *JsNodeGen) !JsTree { + const t = tracer.trace(@src()); + defer t.end(); + // todo: errdefer will probably leave memory in a fully invalid state try c.nodes.setCapacity(c.gpa, c.nodes.len); @@ -87,6 +96,9 @@ pub fn finalize(c: *JsNodeGen) !JsTree { } pub fn build(c: *JsNodeGen) !void { + const t = tracer.trace(@src()); + defer t.end(); + assert(c.air.instTag(.main_block) == .block); const statements = c.air.instData(.block, .main_block).slice(c.air); @@ -117,6 +129,9 @@ pub fn build(c: *JsNodeGen) !void { } fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { + const t = tracer.traceFmt(@src(), "#{d} {s}", .{ @intFromEnum(inst), @tagName(c.air.instTag(inst)) }); + defer t.end(); + switch (c.air.instTag(inst)) { .arg => unreachable, // codegen must skip over these instructions. @@ -290,6 +305,9 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { } fn markExpression(c: *JsNodeGen, inst: Air.Inst.Index, node: Node.Index) !void { + const t = tracer.trace(@src()); + defer t.end(); + const flags: Air.Flags = c.air.instFlags(inst); const i = @intFromEnum(inst); switch (flags.uses) { @@ -314,6 +332,9 @@ fn markExpression(c: *JsNodeGen, inst: Air.Inst.Index, node: Node.Index) !void { } fn fetchExpression(c: *JsNodeGen, inst: Air.Inst.Ref) !Node.Index { + const t = tracer.trace(@src()); + defer t.end(); + return switch (inst.unwrap()) { .value => |v| c.valueToExpression(v), .inst => |index| { @@ -332,6 +353,9 @@ fn fetchExpression(c: *JsNodeGen, inst: Air.Inst.Ref) !Node.Index { } fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index { + const t = tracer.trace(@src()); + defer t.end(); + const intern_tag = c.ip.items.items(.tag)[@intFromEnum(v.toIntern())]; switch (intern_tag) { .removed => unreachable, // UAF @@ -352,6 +376,9 @@ fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index { } fn addTemporary(c: *JsNodeGen) !Node.Index { + const t = tracer.trace(@src()); + defer t.end(); + try c.addStmt(try c.add(.stmt_declare_local, .{ .identifier_index = c.next_id, .init = .none, @@ -367,6 +394,9 @@ const Branch = struct { }; fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Branch { + const t = tracer.trace(@src()); + defer t.end(); + const scratch_top = c.scratch.items.len; defer c.scratch.shrinkRetainingCapacity(scratch_top); @@ -430,6 +460,9 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br } fn codeGenBlock(c: *JsNodeGen, inst: Air.Inst.Index) !void { + const t = tracer.trace(@src()); + defer t.end(); + const scratch_top = c.scratch.items.len; errdefer c.scratch.shrinkRetainingCapacity(scratch_top); @@ -467,6 +500,9 @@ fn codeGenBlock(c: *JsNodeGen, inst: Air.Inst.Index) !void { } fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool { + const t = tracer.trace(@src()); + defer t.end(); + return switch (inst.unwrap()) { .value => true, .inst => |index| switch (c.air.instTag(index)) { @@ -493,6 +529,9 @@ fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool { } fn lowerToExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index { + const t = tracer.trace(@src()); + defer t.end(); + assert(c.canLowerToExpression(ref)); return switch (ref.unwrap()) { .value => |v| try c.valueToExpression(v), @@ -504,6 +543,9 @@ fn lowerToExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index { } fn add(c: *JsNodeGen, comptime tag: JsTree.Tag, data: tag.Data()) !Node.Index { + const t = tracer.trace(@src()); + defer t.end(); + try c.nodes.append(c.gpa, .{ .tag = tag, .data = EncodedData.encode(tag, data), @@ -512,6 +554,9 @@ fn add(c: *JsNodeGen, comptime tag: JsTree.Tag, data: tag.Data()) !Node.Index { } fn addStmt(c: *JsNodeGen, stmt: Node.Index) !void { + const t = tracer.trace(@src()); + defer t.end(); + try c.scratch.append(c.arena, @intFromEnum(stmt)); } @@ -537,3 +582,4 @@ const Air = @import("Air.zig"); const Value = @import("Value.zig"); const dod = @import("dod.zig"); +const tracer = @import("tracer"); diff --git a/src/compiler/Report.zig b/src/compiler/Report.zig index 93a6d9e7cef45287821ae974af8a5d32db3781df..b6a01561c3b55313a84f3a8a603b553210f7eba5 100644 --- a/src/compiler/Report.zig +++ b/src/compiler/Report.zig @@ -183,8 +183,7 @@ pub fn deinit(report: Report, gpa: Allocator) void { gpa.free(report.string_bytes); } -pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype) !void { - const tty = util.detectTTYFromWriter(writer); +pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype, tty: std.io.tty.Config) !void { try writer.print("{d} errors, {d} warnings\n", .{ report.errors.len, report.warnings.len }); for (report.errors) |item| { try report.formatItem(item, .@"error", comp, writer, tty); @@ -348,13 +347,21 @@ pub fn formatItem( } pub fn dumpToStdErr(report: Report, comp: *Comp) !void { - const stderr = std.io.getStdErr().writer(); + const builtin = @import("builtin"); - // Disabled because of coloring issues. - // var buf = std.io.bufferedWriter(stderr); - // const writer = buf.writer(); - try dumpToWriter(report, comp, stderr); - // try buf.flush(); + const stderr = std.io.getStdErr(); + const tty = std.io.tty.detectConfig(stderr); + + if (builtin.os.tag == .windows and tty == .windows_api) { + // Windows API uses it's own API calls, so a buffered writer + // cannot be used :/ + try dumpToWriter(report, comp, stderr.writer(), tty); + } else { + var buf = std.io.bufferedWriter(stderr.writer()); + const writer = buf.writer(); + try dumpToWriter(report, comp, writer, tty); + try buf.flush(); + } } pub const Builder = struct { diff --git a/src/compiler/Sema.zig b/src/compiler/Sema.zig index d68a7409ace4beed313cecf98aed549c4017519a..badc1be0edaa14ec436c91a4ca2ed1206b55ec5d 100644 --- a/src/compiler/Sema.zig +++ b/src/compiler/Sema.zig @@ -92,9 +92,11 @@ pub fn deinit(s: *Sema) void { pub fn finalize(s: *Sema) !void { const air: Air = s.tempAir(); - std.debug.print("=" ** 20, .{}); - std.debug.print("\nAir for @ {d}\n", .{@intFromEnum(s.decl.teg_decl_index)}); - air.dumpToWriter(s.ip, std.io.getStdErr().writer()) catch {}; + if (s.comp.options.dump_ir) { + std.debug.print("=" ** 20, .{}); + std.debug.print("\nAir for @ {d}\n", .{@intFromEnum(s.decl.teg_decl_index)}); + air.dumpToWriter(s.ip, std.io.getStdErr().writer()) catch {}; + } try s.comp.codeGenDecl(s.decl, &air); } diff --git a/src/compiler/Transform.zig b/src/compiler/Transform.zig index 3ec6fc99384a4dc37a9e2bd629bd6c05ffa0ef1a..55d9d703b48cbef8db5db8cbdab9df6705248769 100644 --- a/src/compiler/Transform.zig +++ b/src/compiler/Transform.zig @@ -603,6 +603,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc return .void_value; }; + lookup.mutated = true; + return switch (lookup.data) { .declaration => try t.addInstRef(.{ .tag = .load_decl, @@ -1235,11 +1237,11 @@ fn scratchToIndexList(t: *Transform, scratch_top: usize) !IndexList { }; } -fn lookupId(t: *Transform, id: []const u8) ?Scope.Symbol { +fn lookupId(t: *Transform, id: []const u8) ?*Scope.Symbol { var i = t.scopes.items.len; while (i > 0) { i -= 1; - if (t.scopes.items[i].symbols.get(id)) |sym| { + if (t.scopes.items[i].symbols.getPtr(id)) |sym| { return sym; } } diff --git a/src/compiler/main.zig b/src/compiler/main.zig index 1b0925e7c6e567df4bc86738d56756a3e09f4c0b..e8991d71f64904475a004d8da1ee92926bb3a182 100644 --- a/src/compiler/main.zig +++ b/src/compiler/main.zig @@ -1,30 +1,40 @@ const std = @import("std"); +const builtin = @import("builtin"); + const Tokenizer = @import("Tokenizer.zig"); const Parse = @import("Parse.zig"); const Transform = @import("Transform.zig"); const Comp = @import("Comp.zig"); const unicode_props = @import("unicode_props.zig"); -const T = @import("tracer"); -pub const tracer_impl = @field(T, "none"); + +const tracer = @import("tracer"); + +pub const tracer_impl = @field(tracer, @import("build_options").tracing); pub const Report = @import("Report.zig"); pub fn main() !void { + try tracer.init(); + defer tracer.deinit(); + try tracer.init_thread(null); + defer tracer.deinit_thread(); + try unicode_props.init(); std.debug.print("\r\x1b[K", .{}); - try T.init(); - defer T.deinit(); + const use_gpa = builtin.mode == .Debug; - try T.init_thread(null); - defer T.deinit_thread(); - - var gpa: std.heap.GeneralPurposeAllocator(.{ + var gpa: if (use_gpa) std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = 100, - }) = .{}; - defer _ = gpa.deinit(); - const alloc = gpa.allocator(); + }) else struct {} = .{}; + defer if (use_gpa) { + _ = gpa.deinit(); + }; + const alloc = if (use_gpa) + gpa.allocator() + else + std.heap.c_allocator; var maybe_file_path: ?[]const u8 = null; defer if (maybe_file_path) |path| alloc.free(path); @@ -60,7 +70,7 @@ pub fn main() !void { std.process.exit(1); }; - var comp: Comp = .{ .gpa = T.tracingAllocator(&alloc) }; + var comp: Comp = .{ .gpa = tracer.tracingAllocator(&alloc) }; try comp.init(); defer comp.deinit(); const main_file = try comp.getFile(file_path); @@ -72,10 +82,14 @@ pub fn main() !void { try fail_report.dumpToStdErr(&comp); std.process.exit(1); }, + error.ParseFail => { + // Already printed. + std.process.exit(1); + }, else => return err, }; - { + if (comp.options.dump_ir) { const stderr = std.io.getStdErr().writer(); try teg.dumpToWriter(stderr); } @@ -88,6 +102,8 @@ pub fn main() !void { try comp.link(writer); try bufw.flush(); + std.process.cleanExit(); + // var error_pos: u32 = 0; // var tokens = Tokenizer.tokenize(source, alloc.allocator(), &error_pos) catch |err| { // var line: usize = 1; -- 2.54.0