From cd95444e4729761033f35d689a3b6ad6f4630552 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jan 2021 13:59:33 -0700 Subject: [PATCH] stage2: C backend: remove format() hackery All C backend tests passing now, except for emit-h tests. Next task in the branch is to restore emit-h. --- src/codegen/c.zig | 131 +++++++++--------- src/link/C.zig | 10 +- src/link/C/zig.h | 31 +++-- test/stage2/cbe.zig | 313 ++++++++++++++------------------------------ 4 files changed, 189 insertions(+), 296 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index ab66869d31..5e274e0351 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -27,42 +27,6 @@ pub const CValue = union(enum) { arg: usize, /// By-value decl: *Decl, - - pub fn printed(value: CValue, object: *Object) Printed { - return .{ - .value = value, - .object = object, - }; - } - - pub const Printed = struct { - value: CValue, - object: *Object, - - /// TODO this got unwieldly, I want to remove the ability to print this way - pub fn format( - self: Printed, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) error{OutOfMemory}!void { - if (fmt.len != 0) @compileError("Unknown format string: '" ++ fmt ++ "'"); - switch (self.value) { - .none => unreachable, - .local => |i| return std.fmt.format(writer, "t{d}", .{i}), - .local_ref => |i| return std.fmt.format(writer, "&t{d}", .{i}), - .constant => |inst| { - const o = self.object; - o.dg.renderValue(writer, inst.ty, inst.value().?) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => return, - }; - }, - .arg => |i| return std.fmt.format(writer, "a{d}", .{i}), - .decl => |decl| return writer.writeAll(mem.span(decl.name)), - } - } - }; }; pub const CValueMap = std.AutoHashMap(*Inst, CValue); @@ -103,6 +67,17 @@ pub const Object = struct { try o.code.writer().writeByteNTimes(' ', indent_amt); } + fn writeCValue(o: *Object, writer: Writer, c_value: CValue) !void { + switch (c_value) { + .none => unreachable, + .local => |i| return writer.print("t{d}", .{i}), + .local_ref => |i| return writer.print("&t{d}", .{i}), + .constant => |inst| return o.dg.renderValue(writer, inst.ty, inst.value().?), + .arg => |i| return writer.print("a{d}", .{i}), + .decl => |decl| return writer.writeAll(mem.span(decl.name)), + } + } + fn renderTypeAndName( o: *Object, writer: Writer, @@ -127,7 +102,9 @@ pub const Object = struct { .Const => "const ", .Mut => "", }; - try writer.print(" {s}{}{s}", .{ const_prefix, name.printed(o), suffix.items }); + try writer.print(" {s}", .{const_prefix}); + try o.writeCValue(writer, name); + try writer.writeAll(suffix.items); } }; @@ -353,7 +330,7 @@ pub fn genDecl(o: *Object) !void { try writer.writeAll("\n"); for (instructions) |inst| { const result_value = switch (inst.tag) { - .add => try genBinOp(o, inst.castTag(.add).?, "+"), + .add => try genBinOp(o, inst.castTag(.add).?, " + "), .alloc => try genAlloc(o, inst.castTag(.alloc).?), .arg => genArg(o), .assembly => try genAsm(o, inst.castTag(.assembly).?), @@ -361,19 +338,19 @@ pub fn genDecl(o: *Object) !void { .bitcast => try genBitcast(o, inst.castTag(.bitcast).?), .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?), .call => try genCall(o, inst.castTag(.call).?), - .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, "=="), - .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, ">"), - .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, ">="), - .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, "<"), - .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, "<="), - .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, "!="), + .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "), + .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "), + .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "), + .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "), + .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "), + .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "), .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?), .intcast => try genIntCast(o, inst.castTag(.intcast).?), .load => try genLoad(o, inst.castTag(.load).?), .ret => try genRet(o, inst.castTag(.ret).?), .retvoid => try genRetVoid(o), .store => try genStore(o, inst.castTag(.store).?), - .sub => try genBinOp(o, inst.castTag(.sub).?, "-"), + .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), .unreach => try genUnreach(o, inst.castTag(.unreach).?), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; @@ -457,10 +434,14 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { switch (operand) { .local_ref => |i| { const wrapped: CValue = .{ .local = i }; - try writer.print(" = {};\n", .{wrapped.printed(o)}); + try writer.writeAll(" = "); + try o.writeCValue(writer, wrapped); + try writer.writeAll(";\n"); }, else => { - try writer.print(" = *{};\n", .{operand.printed(o)}); + try writer.writeAll(" = *"); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); }, } return local; @@ -469,7 +450,10 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { fn genRet(o: *Object, inst: *Inst.UnOp) !CValue { const operand = try o.resolveInst(inst.operand); try o.indent(); - try o.code.writer().print("return {};\n", .{operand.printed(o)}); + const writer = o.code.writer(); + try writer.writeAll("return "); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); return CValue.none; } @@ -484,7 +468,9 @@ fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue { const local = try o.allocLocal(inst.base.ty, .Const); try writer.writeAll(" = ("); try o.dg.renderType(writer, inst.base.ty); - try writer.print("){};\n", .{from.printed(o)}); + try writer.writeAll(")"); + try o.writeCValue(writer, from); + try writer.writeAll(";\n"); return local; } @@ -498,10 +484,17 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { switch (dest_ptr) { .local_ref => |i| { const dest: CValue = .{ .local = i }; - try writer.print("{} = {};\n", .{ dest.printed(o), src_val.printed(o) }); + try o.writeCValue(writer, dest); + try writer.writeAll(" = "); + try o.writeCValue(writer, src_val); + try writer.writeAll(";\n"); }, else => { - try writer.print("*{} = {};\n", .{ dest_ptr.printed(o), src_val.printed(o) }); + try writer.writeAll("*"); + try o.writeCValue(writer, dest_ptr); + try writer.writeAll(" = "); + try o.writeCValue(writer, src_val); + try writer.writeAll(";\n"); }, } return CValue.none; @@ -517,7 +510,13 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { try o.indent(); const writer = o.code.writer(); const local = try o.allocLocal(inst.base.ty, .Const); - try writer.print(" = {} {s} {};\n", .{ lhs.printed(o), operator, rhs.printed(o) }); + + try writer.writeAll(" = "); + try o.writeCValue(writer, lhs); + try writer.writeAll(operator); + try o.writeCValue(writer, rhs); + try writer.writeAll(";\n"); + return local; } @@ -556,7 +555,7 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue { try o.dg.renderValue(writer, arg.ty, val); } else { const val = try o.resolveInst(arg); - try writer.print("{}", .{val.printed(o)}); + try o.writeCValue(writer, val); } } } @@ -585,16 +584,25 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { const local = try o.allocLocal(inst.base.ty, .Const); try writer.writeAll(" = ("); try o.dg.renderType(writer, inst.base.ty); - try writer.print("){};\n", .{operand.printed(o)}); + + try writer.writeAll(")"); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); return local; } const local = try o.allocLocal(inst.base.ty, .Mut); try writer.writeAll(";\n"); try o.indent(); - try writer.print("memcpy(&{}, &{}, sizeof {});\n", .{ - local.printed(o), operand.printed(o), local.printed(o), - }); + + try writer.writeAll("memcpy(&"); + try o.writeCValue(writer, local); + try writer.writeAll(", &"); + try o.writeCValue(writer, operand); + try writer.writeAll(", sizeof "); + try o.writeCValue(writer, local); + try writer.writeAll(");\n"); + return local; } @@ -623,9 +631,10 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { try o.indent(); try writer.writeAll("register "); try o.dg.renderType(writer, arg.ty); - try writer.print(" {s}_constant __asm__(\"{s}\") = {};\n", .{ - reg, reg, arg_c_value.printed(o), - }); + + try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); + try o.writeCValue(writer, arg_c_value); + try writer.writeAll(";\n"); } else { return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{}); } @@ -648,7 +657,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (index > 0) { try writer.writeAll(", "); } - try writer.print("\"\"({s}_constant)", .{reg}); + try writer.print("\"r\"({s}_constant)", .{reg}); } else { // This is blocked by the earlier test unreachable; diff --git a/src/link/C.zig b/src/link/C.zig index 68eb56c5b9..0bca77f25f 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -101,14 +101,12 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { defer object.dg.fwd_decl.deinit(); codegen.genDecl(&object) catch |err| switch (err) { - error.AnalysisFail => {}, + error.AnalysisFail => { + try module.failed_decls.put(module.gpa, decl, object.dg.error_msg.?); + return; + }, else => |e| return e, }; - // The code may populate this error without returning error.AnalysisFail. - if (object.dg.error_msg) |msg| { - try module.failed_decls.put(module.gpa, decl, msg); - return; - } fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); code.* = object.code.moveToUnmanaged(); diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 49f97210eb..fed799d348 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -22,24 +22,31 @@ #define zig_unreachable() #endif -#if defined(_MSC_VER) -#define zig_breakpoint __debugbreak() -#else -#if defined(__MINGW32__) || defined(__MINGW64__) -#define zig_breakpoint __debugbreak() -#elif defined(__clang__) -#define zig_breakpoint __builtin_debugtrap() +#if __STDC_VERSION__ >= 199901L +#define zig_restrict restrict #elif defined(__GNUC__) -#define zig_breakpoint __builtin_trap() -#elif defined(__i386__) || defined(__x86_64__) -#define zig_breakpoint __asm__ volatile("int $0x03"); +#define zig_restrict __restrict #else -#define zig_breakpoint raise(SIGTRAP) +#define zig_restrict #endif + +#if defined(_MSC_VER) +#define zig_breakpoint() __debugbreak() +#elif defined(__MINGW32__) || defined(__MINGW64__) +#define zig_breakpoint() __debugbreak() +#elif defined(__clang__) +#define zig_breakpoint() __builtin_debugtrap() +#elif defined(__GNUC__) +#define zig_breakpoint() __builtin_trap() +#elif defined(__i386__) || defined(__x86_64__) +#define zig_breakpoint() __asm__ volatile("int $0x03"); +#else +#define zig_breakpoint() raise(SIGTRAP) #endif #include +#include #define int128_t __int128 #define uint128_t unsigned __int128 -#include +void *memcpy (void *zig_restrict, const void *zig_restrict, size_t); diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 9947c90f13..e66dabc147 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -31,6 +31,100 @@ pub fn addCases(ctx: *TestContext) !void { , "yo" ++ std.cstr.line_sep); } + { + var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); + + // Exit with 0 + case.addCompareOutput( + \\fn exitGood() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ ); + \\ unreachable; + \\} + \\ + \\export fn main() c_int { + \\ exitGood(); + \\} + , ""); + + // Pass a usize parameter to exit + case.addCompareOutput( + \\export fn main() c_int { + \\ exit(0); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + , ""); + + // Change the parameter to u8 + case.addCompareOutput( + \\export fn main() c_int { + \\ exit(0); + \\} + \\ + \\fn exit(code: u8) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + , ""); + + // Do some arithmetic at the exit callsite + case.addCompareOutput( + \\export fn main() c_int { + \\ 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; + \\} + \\ + , ""); + + // Invert the arithmetic + case.addCompareOutput( + \\export fn main() c_int { + \\ 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; + \\} + \\ + , ""); + } + { var case = ctx.exeFromCompiledC("alloc and retptr", .{}); @@ -86,6 +180,8 @@ pub fn addCases(ctx: *TestContext) !void { \\ unreachable; \\} , + \\zig_noreturn void _start(void); + \\ \\zig_noreturn void _start(void) { \\ zig_breakpoint(); \\ zig_unreachable(); @@ -98,223 +194,6 @@ pub fn addCases(ctx: *TestContext) !void { \\void start(void); \\ ); - ctx.c("less empty start function", linux_x64, - \\fn main() noreturn { - \\ unreachable; - \\} - \\ - \\export fn _start() noreturn { - \\ main(); - \\} - , - \\static zig_noreturn void main(void); - \\ - \\static zig_noreturn void main(void) { - \\ zig_breakpoint(); - \\ zig_unreachable(); - \\} - \\ - \\zig_noreturn void _start(void) { - \\ main(); - \\} - \\ - ); - // TODO: implement return values - // TODO: figure out a way to prevent asm constants from being generated - ctx.c("inline asm", linux_x64, - \\fn exitGood() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ ); - \\ unreachable; - \\} - \\ - \\export fn _start() noreturn { - \\ exitGood(); - \\} - , - \\static zig_noreturn void exitGood(void); - \\ - \\static uint8_t exitGood__anon_0[6] = "{rax}"; - \\static uint8_t exitGood__anon_1[6] = "{rdi}"; - \\static uint8_t exitGood__anon_2[8] = "syscall"; - \\ - \\static zig_noreturn void exitGood(void) { - \\ register uintptr_t rax_constant __asm__("rax") = 231; - \\ register uintptr_t rdi_constant __asm__("rdi") = 0; - \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); - \\ zig_breakpoint(); - \\ zig_unreachable(); - \\} - \\ - \\zig_noreturn void _start(void) { - \\ exitGood(); - \\} - \\ - ); - ctx.c("exit with parameter", linux_x64, - \\export fn _start() noreturn { - \\ exit(0); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - \\ - , - \\static zig_noreturn void exit(uintptr_t arg0); - \\ - \\static uint8_t exit__anon_0[6] = "{rax}"; - \\static uint8_t exit__anon_1[6] = "{rdi}"; - \\static uint8_t exit__anon_2[8] = "syscall"; - \\ - \\zig_noreturn void _start(void) { - \\ exit(0); - \\} - \\ - \\static zig_noreturn void exit(uintptr_t arg0) { - \\ register uintptr_t rax_constant __asm__("rax") = 231; - \\ register uintptr_t rdi_constant __asm__("rdi") = arg0; - \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); - \\ zig_breakpoint(); - \\ zig_unreachable(); - \\} - \\ - ); - ctx.c("exit with u8 parameter", linux_x64, - \\export fn _start() noreturn { - \\ exit(0); - \\} - \\ - \\fn exit(code: u8) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - \\ - , - \\static zig_noreturn void exit(uint8_t arg0); - \\ - \\static uint8_t exit__anon_0[6] = "{rax}"; - \\static uint8_t exit__anon_1[6] = "{rdi}"; - \\static uint8_t exit__anon_2[8] = "syscall"; - \\ - \\zig_noreturn void _start(void) { - \\ exit(0); - \\} - \\ - \\static zig_noreturn void exit(uint8_t arg0) { - \\ uintptr_t const __temp_0 = (uintptr_t)arg0; - \\ register uintptr_t rax_constant __asm__("rax") = 231; - \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0; - \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); - \\ zig_breakpoint(); - \\ zig_unreachable(); - \\} - \\ - ); - 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; - \\} - \\ - , - \\static zig_noreturn void exitMath(uint8_t arg0); - \\static zig_noreturn void exit(uint8_t arg0); - \\ - \\static uint8_t exit__anon_0[6] = "{rax}"; - \\static uint8_t exit__anon_1[6] = "{rdi}"; - \\static uint8_t exit__anon_2[8] = "syscall"; - \\ - \\zig_noreturn void _start(void) { - \\ exitMath(1); - \\} - \\ - \\static zig_noreturn void exitMath(uint8_t arg0) { - \\ uint8_t const __temp_0 = 0 + arg0; - \\ uint8_t const __temp_1 = __temp_0 - arg0; - \\ exit(__temp_1); - \\} - \\ - \\static zig_noreturn void exit(uint8_t arg0) { - \\ uintptr_t const __temp_0 = (uintptr_t)arg0; - \\ register uintptr_t rax_constant __asm__("rax") = 231; - \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0; - \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); - \\ zig_breakpoint(); - \\ zig_unreachable(); - \\} - \\ - ); - 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; - \\} - \\ - , - \\static zig_noreturn void exitMath(uint8_t arg0); - \\static zig_noreturn void exit(uint8_t arg0); - \\ - \\static uint8_t exit__anon_0[6] = "{rax}"; - \\static uint8_t exit__anon_1[6] = "{rdi}"; - \\static uint8_t exit__anon_2[8] = "syscall"; - \\ - \\zig_noreturn void _start(void) { - \\ exitMath(1); - \\} - \\ - \\static zig_noreturn void exitMath(uint8_t arg0) { - \\ uint8_t const __temp_0 = arg0 + 0; - \\ uint8_t const __temp_1 = __temp_0 - arg0; - \\ exit(__temp_1); - \\} - \\ - \\static zig_noreturn void exit(uint8_t arg0) { - \\ uintptr_t const __temp_0 = (uintptr_t)arg0; - \\ register uintptr_t rax_constant __asm__("rax") = 231; - \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0; - \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); - \\ zig_breakpoint(); - \\ zig_unreachable(); - \\} - \\ - ); ctx.h("header with single param function", linux_x64, \\export fn start(a: u8) void{} ,