From ce29fc09470f0db9e7bdc075ebb84fe42146a222 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 8 Sep 2020 14:08:40 -0400 Subject: [PATCH 1/9] Make indentation adjustable (hardcode 4 spaces for now) --- src/codegen/c.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 34ddcfbb3b..5b28eb2255 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -11,6 +11,8 @@ const C = link.File.C; const Decl = Module.Decl; const mem = std.mem; +const indentation = " "; + /// Maps a name from Zig source to C. Currently, this will always give the same /// output for any given input, sometimes resulting in broken identifiers. fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { @@ -87,6 +89,7 @@ fn genArray(file: *C, decl: *Decl) !void { if (tv.val.cast(Value.Payload.Bytes)) |payload| if (tv.ty.sentinel()) |sentinel| if (sentinel.toUnsignedInt() == 0) + // TODO: static by default try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) else return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{}) @@ -166,7 +169,7 @@ fn genArg(ctx: *Context) !?[]u8 { } fn genRetVoid(ctx: *Context) !?[]u8 { - try ctx.file.main.writer().print(" return;\n", .{}); + try ctx.file.main.writer().print(indentation ++ "return;\n", .{}); return null; } @@ -182,7 +185,7 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { const name = try ctx.name(); const from = ctx.inst_map.get(op) orelse return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{}); - try writer.writeAll(" const "); + try writer.writeAll(indentation ++ "const "); try renderType(ctx, writer, inst.base.ty); try writer.print(" {} = (", .{name}); try renderType(ctx, writer, inst.base.ty); @@ -193,7 +196,7 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { const writer = ctx.file.main.writer(); const header = ctx.file.header.writer(); - try writer.writeAll(" "); + try writer.writeAll(indentation); if (inst.func.castTag(.constant)) |func_inst| { if (func_inst.val.cast(Value.Payload.Function)) |func_val| { const target = func_val.func.owner_decl; @@ -242,13 +245,13 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { } fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { - try ctx.file.main.writer().writeAll(" zig_unreachable();\n"); + try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); return null; } fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { const writer = ctx.file.main.writer(); - try writer.writeAll(" "); + try writer.writeAll(indentation); for (as.inputs) |i, index| { if (i[0] == '{' and i[i.len - 1] == '}') { const reg = i[1 .. i.len - 1]; From 9979719a9d0902ffde692ef06e7facf4c1921b99 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 8 Sep 2020 14:10:17 -0400 Subject: [PATCH 2/9] Stage2 peer type resolution: comptime_int + other_int_type --- src/Module.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Module.zig b/src/Module.zig index 75b6afffcd..151f507ef4 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2661,6 +2661,11 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty continue; } + if (prev_inst.ty.zigTypeTag() == .ComptimeInt and next_inst.ty.isInt()) { + prev_inst = next_inst; + continue; + } + // TODO error notes pointing out each type return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); } From 9ef6c0a035820b586cc2d3a051eef4ae21ec244b Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 8 Sep 2020 21:26:24 -0400 Subject: [PATCH 3/9] CBE: utilize per-function arena allocator --- src/codegen/c.zig | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5b28eb2255..21bd0093d8 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -54,8 +54,10 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { const tv = decl.typed_value.most_recent.typed_value; try renderType(ctx, writer, tv.ty.fnReturnType()); - const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name)); - defer ctx.file.base.allocator.free(name); + // Use the child allocator directly, as we know the name can be freed before + // the rest of the arena. + const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); + defer ctx.arena.child_allocator.free(name); try writer.print(" {}(", .{name}); var param_len = tv.ty.fnParamLen(); if (param_len == 0) @@ -102,22 +104,18 @@ fn genArray(file: *C, decl: *Decl) !void { const Context = struct { file: *C, decl: *Decl, - inst_map: std.AutoHashMap(*Inst, []u8), + inst_map: *std.AutoHashMap(*Inst, []u8), + arena: *std.heap.ArenaAllocator, argdex: usize = 0, unnamed_index: usize = 0, fn name(self: *Context) ![]u8 { - const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index}); + const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{}", .{self.unnamed_index}); self.unnamed_index += 1; return val; } fn deinit(self: *Context) void { - var it = self.inst_map.iterator(); - while (it.next()) |kv| { - self.file.base.allocator.free(kv.value); - } - self.inst_map.deinit(); self.* = undefined; } }; @@ -126,10 +124,15 @@ fn genFn(file: *C, decl: *Decl) !void { const writer = file.main.writer(); const tv = decl.typed_value.most_recent.typed_value; + var arena = std.heap.ArenaAllocator.init(file.base.allocator); + defer arena.deinit(); + var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); + defer inst_map.deinit(); var ctx = Context{ .file = file, .decl = decl, - .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator), + .arena = &arena, + .inst_map = &inst_map, }; defer ctx.deinit(); @@ -163,7 +166,7 @@ fn genFn(file: *C, decl: *Decl) !void { } fn genArg(ctx: *Context) !?[]u8 { - const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex}); + const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex}); ctx.argdex += 1; return name; } From e06ba9e86e53797d90d74e87724b897858fe2d77 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 8 Sep 2020 14:41:51 -0400 Subject: [PATCH 4/9] CBE: properly resolve Insts --- src/codegen/c.zig | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 21bd0093d8..5d350f7bd5 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -109,6 +109,18 @@ const Context = struct { argdex: usize = 0, unnamed_index: usize = 0, + fn resolveInst(self: *Context, inst: *Inst) ![]u8 { + if (inst.cast(Inst.Constant)) |const_inst| { + var out = std.ArrayList(u8).init(&self.arena.allocator); + try renderValue(self, out.writer(), inst.ty, const_inst.val); + return out.toOwnedSlice(); + } + if (self.inst_map.get(inst)) |val| { + return val; + } + return self.file.fail(inst.src, "Internal error: failed to resolve inst!", .{}); + } + fn name(self: *Context) ![]u8 { const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{}", .{self.unnamed_index}); self.unnamed_index += 1; @@ -186,8 +198,7 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { const op = inst.operand; const writer = ctx.file.main.writer(); const name = try ctx.name(); - const from = ctx.inst_map.get(op) orelse - return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{}); + const from = try ctx.resolveInst(inst.operand); try writer.writeAll(indentation ++ "const "); try renderType(ctx, writer, inst.base.ty); try writer.print(" {} = (", .{name}); @@ -223,7 +234,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { if (arg.cast(Inst.Constant)) |con| { try renderValue(ctx, writer, arg.ty, con.val); } else { - return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg}); + const val = try ctx.resolveInst(arg); + try writer.print("{}", .{val}); } } } From ea7b2750c823e7160db790582a1dcb96a9feebc7 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 8 Sep 2020 14:39:47 -0400 Subject: [PATCH 5/9] CBE: addition and subtraction --- src/codegen/c.zig | 15 ++++++++++++++ test/stage2/cbe.zig | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5d350f7bd5..99ac0cfe00 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -160,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void { if (switch (inst.tag) { .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), .call => try genCall(&ctx, inst.castTag(.call).?), + .add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"), + .sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"), .ret => try genRet(&ctx, inst.castTag(.ret).?), .retvoid => try genRetVoid(&ctx), .arg => try genArg(&ctx), @@ -207,6 +209,19 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { return name; } +fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { + if (inst.base.isUnused()) + return null; + const lhs = ctx.resolveInst(inst.lhs); + const rhs = ctx.resolveInst(inst.rhs); + const writer = ctx.file.main.writer(); + const name = try ctx.name(); + try writer.writeAll(indentation ++ "const "); + try renderType(ctx, writer, inst.base.ty); + try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); + return name; +} + fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { const writer = ctx.file.main.writer(); const header = ctx.file.header.writer(); diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 2a176e0368..0be5bd8704 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -147,4 +147,52 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ ); + ctx.c("exit with u8 arithmetic", linux_x64, + \\export fn _start() noreturn { + \\ exitMath(1); + \\} + \\ + \\fn exitMath(a: u8) noreturn { + \\ exit(0 + a - a); + \\} + \\ + \\fn exit(code: u8) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + \\ + , + \\#include + \\#include + \\ + \\zig_noreturn void exitMath(uint8_t arg0); + \\zig_noreturn void exit(uint8_t arg0); + \\ + \\const char *const exit__anon_0 = "{rax}"; + \\const char *const exit__anon_1 = "{rdi}"; + \\const char *const exit__anon_2 = "syscall"; + \\ + \\zig_noreturn void _start(void) { + \\ exitMath(1); + \\} + \\ + \\zig_noreturn void exitMath(uint8_t arg0) { + \\ const uint8_t __temp_0 = 0 + arg0; + \\ const uint8_t __temp_1 = __temp_0 - arg0; + \\ exit(__temp_1); + \\} + \\ + \\zig_noreturn void exit(uint8_t arg0) { + \\ const size_t __temp_0 = (size_t)arg0; + \\ register size_t rax_constant __asm__("rax") = 231; + \\ register size_t rdi_constant __asm__("rdi") = __temp_0; + \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); + \\ zig_unreachable(); + \\} + \\ + ); } From a2e1639d6d10143c91b6c25968cc58f865dbe267 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Mon, 14 Sep 2020 19:44:32 -0400 Subject: [PATCH 6/9] Peer type resolution: comptime_int decay to other int --- src/Module.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module.zig b/src/Module.zig index 151f507ef4..ba79407a80 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2661,7 +2661,7 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty continue; } - if (prev_inst.ty.zigTypeTag() == .ComptimeInt and next_inst.ty.isInt()) { + if ((prev_inst.ty.zigTypeTag() == .ComptimeInt and next_inst.ty.isInt()) or (next_inst.ty.zigTypeTag() == .ComptimeInt and prev_inst.ty.isInt())) { prev_inst = next_inst; continue; } From 7b88215a49b2e394508bb8014649747527a4edf6 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Mon, 14 Sep 2020 19:45:23 -0400 Subject: [PATCH 7/9] Replace error message with unreachable --- src/codegen/c.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 99ac0cfe00..2d98729721 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -118,7 +118,7 @@ const Context = struct { if (self.inst_map.get(inst)) |val| { return val; } - return self.file.fail(inst.src, "Internal error: failed to resolve inst!", .{}); + unreachable; } fn name(self: *Context) ![]u8 { From de093879ccc46263c3c6a87173bcf5089d48b4b7 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Wed, 7 Oct 2020 02:31:47 -0400 Subject: [PATCH 8/9] Fix peer type resolution --- src/Module.zig | 6 +++++- test/stage2/cbe.zig | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Module.zig b/src/Module.zig index ba79407a80..b3bdefceec 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2661,11 +2661,15 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty continue; } - if ((prev_inst.ty.zigTypeTag() == .ComptimeInt and next_inst.ty.isInt()) or (next_inst.ty.zigTypeTag() == .ComptimeInt and prev_inst.ty.isInt())) { + if (prev_inst.ty.zigTypeTag() == .ComptimeInt and next_inst.ty.isInt()) { prev_inst = next_inst; continue; } + if (prev_inst.ty.isInt() and next_inst.ty.zigTypeTag() == .ComptimeInt) { + continue; + } + // TODO error notes pointing out each type return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 0be5bd8704..fbf0f9e708 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -195,4 +195,52 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ ); + ctx.c("exit with u8 arithmetic inverted", linux_x64, + \\export fn _start() noreturn { + \\ exitMath(1); + \\} + \\ + \\fn exitMath(a: u8) noreturn { + \\ exit(a + 0 - a); + \\} + \\ + \\fn exit(code: u8) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + \\ + , + \\#include + \\#include + \\ + \\zig_noreturn void exitMath(uint8_t arg0); + \\zig_noreturn void exit(uint8_t arg0); + \\ + \\const char *const exit__anon_0 = "{rax}"; + \\const char *const exit__anon_1 = "{rdi}"; + \\const char *const exit__anon_2 = "syscall"; + \\ + \\zig_noreturn void _start(void) { + \\ exitMath(1); + \\} + \\ + \\zig_noreturn void exitMath(uint8_t arg0) { + \\ const uint8_t __temp_0 = arg0 + 0; + \\ const uint8_t __temp_1 = __temp_0 - arg0; + \\ exit(__temp_1); + \\} + \\ + \\zig_noreturn void exit(uint8_t arg0) { + \\ const size_t __temp_0 = (size_t)arg0; + \\ register size_t rax_constant __asm__("rax") = 231; + \\ register size_t rdi_constant __asm__("rdi") = __temp_0; + \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); + \\ zig_unreachable(); + \\} + \\ + ); } From 7d69e1d84e272251e6aead39c551154797c2b1a4 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Wed, 7 Oct 2020 02:36:04 -0400 Subject: [PATCH 9/9] Rename variables in resolvePeerTypes for clarity --- src/Module.zig | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index b3bdefceec..1abaf2a63a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2629,52 +2629,52 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty if (instructions.len == 1) return instructions[0].ty; - var prev_inst = instructions[0]; - for (instructions[1..]) |next_inst| { - if (next_inst.ty.eql(prev_inst.ty)) + var chosen = instructions[0]; + for (instructions[1..]) |candidate| { + if (candidate.ty.eql(chosen.ty)) continue; - if (next_inst.ty.zigTypeTag() == .NoReturn) + if (candidate.ty.zigTypeTag() == .NoReturn) continue; - if (prev_inst.ty.zigTypeTag() == .NoReturn) { - prev_inst = next_inst; + if (chosen.ty.zigTypeTag() == .NoReturn) { + chosen = candidate; continue; } - if (next_inst.ty.zigTypeTag() == .Undefined) + if (candidate.ty.zigTypeTag() == .Undefined) continue; - if (prev_inst.ty.zigTypeTag() == .Undefined) { - prev_inst = next_inst; + if (chosen.ty.zigTypeTag() == .Undefined) { + chosen = candidate; continue; } - if (prev_inst.ty.isInt() and - next_inst.ty.isInt() and - prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt()) + if (chosen.ty.isInt() and + candidate.ty.isInt() and + chosen.ty.isSignedInt() == candidate.ty.isSignedInt()) { - if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) { - prev_inst = next_inst; + if (chosen.ty.intInfo(self.getTarget()).bits < candidate.ty.intInfo(self.getTarget()).bits) { + chosen = candidate; } continue; } - if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { - if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) { - prev_inst = next_inst; + if (chosen.ty.isFloat() and candidate.ty.isFloat()) { + if (chosen.ty.floatBits(self.getTarget()) < candidate.ty.floatBits(self.getTarget())) { + chosen = candidate; } continue; } - if (prev_inst.ty.zigTypeTag() == .ComptimeInt and next_inst.ty.isInt()) { - prev_inst = next_inst; + if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) { + chosen = candidate; continue; } - if (prev_inst.ty.isInt() and next_inst.ty.zigTypeTag() == .ComptimeInt) { + if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) { continue; } // TODO error notes pointing out each type - return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty }); + return self.fail(scope, candidate.src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty }); } - return prev_inst.ty; + return chosen.ty; } pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {