Commit cd95444e47
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;
src/link/C/zig.h
@@ -22,24 +22,31 @@
#define zig_unreachable()
#endif
-#if defined(_MSC_VER)
-#define zig_breakpoint __debugbreak()
+#if __STDC_VERSION__ >= 199901L
+#define zig_restrict restrict
+#elif defined(__GNUC__)
+#define zig_restrict __restrict
#else
-#if defined(__MINGW32__) || defined(__MINGW64__)
-#define zig_breakpoint __debugbreak()
+#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()
+#define zig_breakpoint() __builtin_debugtrap()
#elif defined(__GNUC__)
-#define zig_breakpoint __builtin_trap()
+#define zig_breakpoint() __builtin_trap()
#elif defined(__i386__) || defined(__x86_64__)
-#define zig_breakpoint __asm__ volatile("int $0x03");
+#define zig_breakpoint() __asm__ volatile("int $0x03");
#else
-#define zig_breakpoint raise(SIGTRAP)
-#endif
+#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);
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();
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{}
,