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.
This commit is contained in:
Andrew Kelley 2021-01-05 13:59:33 -07:00
parent 58cfaa5982
commit cd95444e47
4 changed files with 189 additions and 296 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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 <stdint.h>
#include <stddef.h>
#define int128_t __int128
#define uint128_t unsigned __int128
#include <string.h>
void *memcpy (void *zig_restrict, const void *zig_restrict, size_t);

View File

@ -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{}
,