authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-13 07:07:27+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-07-13 07:07:27+00:00
log1cab40d7837c69fe6a77f76be9dc27026acd935e
treeb1bc29909e1cff122c7df851288c3bae4d6c3232
parent2c882b2e651386399fd48c24302fd2d4ba430ef3
parent8d6cadee1622b2548e02c0c63f9a65625d608ada
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5864 from pixelherodev/cbe

CBE: get basic returns working

4 files changed, 263 insertions(+), 145 deletions(-)

src-self-hosted/Module.zig+28-6
......@@ -989,14 +989,22 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
989989 error.AnalysisFail => {
990990 decl.analysis = .dependency_failure;
991991 },
992 error.CGenFailure => {
993 // Error is handled by CBE, don't try adding it again
994 },
992995 else => {
993996 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
994 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
995 self.gpa,
996 decl.src(),
997 "unable to codegen: {}",
998 .{@errorName(err)},
999 ));
997 const result = self.failed_decls.getOrPutAssumeCapacity(decl);
998 if (result.found_existing) {
999 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });
1000 } else {
1001 result.entry.value = try ErrorMsg.create(
1002 self.gpa,
1003 decl.src(),
1004 "unable to codegen: {}",
1005 .{@errorName(err)},
1006 );
1007 }
10001008 decl.analysis = .codegen_failure_retryable;
10011009 },
10021010 };
......@@ -1300,6 +1308,20 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir
13001308
13011309fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {
13021310 switch (infix_node.op) {
1311 .Assign => {
1312 if (infix_node.lhs.id == .Identifier) {
1313 const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
1314 const tree = scope.tree();
1315 const ident_name = tree.tokenSlice(ident.token);
1316 if (std.mem.eql(u8, ident_name, "_")) {
1317 return self.astGenExpr(scope, infix_node.rhs);
1318 } else {
1319 return self.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
1320 }
1321 } else {
1322 return self.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
1323 }
1324 },
13031325 .Add => {
13041326 const lhs = try self.astGenExpr(scope, infix_node.lhs);
13051327 const rhs = try self.astGenExpr(scope, infix_node.rhs);
src-self-hosted/cgen.zig+154-117
......@@ -1,9 +1,11 @@
11const link = @import("link.zig");
22const Module = @import("Module.zig");
3const ir = @import("ir.zig");
3
4const std = @import("std");
5
6const Inst = @import("ir.zig").Inst;
47const Value = @import("value.zig").Value;
58const Type = @import("type.zig").Type;
6const std = @import("std");
79
810const C = link.File.C;
911const Decl = Module.Decl;
......@@ -26,6 +28,14 @@ fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !
2628 try writer.writeAll("noreturn void");
2729 },
2830 .Void => try writer.writeAll("void"),
31 .Int => {
32 if (T.tag() == .u8) {
33 file.need_stdint = true;
34 try writer.writeAll("uint8_t");
35 } else {
36 return file.fail(src, "TODO implement int types", .{});
37 }
38 },
2939 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
3040 }
3141 }
......@@ -37,132 +47,159 @@ fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *De
3747 const name = try map(file.allocator, mem.spanZ(decl.name));
3848 defer file.allocator.free(name);
3949 try writer.print(" {}(", .{name});
40 if (tv.ty.fnParamLen() == 0) {
41 try writer.writeAll("void)");
42 } else {
50 if (tv.ty.fnParamLen() == 0)
51 try writer.writeAll("void)")
52 else
4353 return file.fail(decl.src(), "TODO implement parameters", .{});
44 }
4554}
4655
4756pub fn generate(file: *C, decl: *Decl) !void {
57 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
58 .Fn => try genFn(file, decl),
59 .Array => try genArray(file, decl),
60 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
61 }
62}
63
64fn genArray(file: *C, decl: *Decl) !void {
65 const tv = decl.typed_value.most_recent.typed_value;
66 // TODO: prevent inline asm constants from being emitted
67 const name = try map(file.allocator, mem.span(decl.name));
68 defer file.allocator.free(name);
69 if (tv.val.cast(Value.Payload.Bytes)) |payload|
70 if (tv.ty.arraySentinel()) |sentinel|
71 if (sentinel.toUnsignedInt() == 0)
72 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
73 else
74 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
75 else
76 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
77 else
78 return file.fail(decl.src(), "TODO non-byte arrays", .{});
79}
80
81fn genFn(file: *C, decl: *Decl) !void {
4882 const writer = file.main.writer();
49 const header = file.header.writer();
5083 const tv = decl.typed_value.most_recent.typed_value;
51 switch (tv.ty.zigTypeTag()) {
52 .Fn => {
53 try renderFunctionSignature(file, writer, decl);
54
55 try writer.writeAll(" {");
56
57 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
58 const instructions = func.analysis.success.instructions;
59 if (instructions.len > 0) {
60 for (instructions) |inst| {
61 try writer.writeAll("\n\t");
62 switch (inst.tag) {
63 .assembly => {
64 const as = inst.cast(ir.Inst.Assembly).?.args;
65 for (as.inputs) |i, index| {
66 if (i[0] == '{' and i[i.len - 1] == '}') {
67 const reg = i[1 .. i.len - 1];
68 const arg = as.args[index];
69 if (arg.cast(ir.Inst.Constant)) |c| {
70 if (c.val.tag() == .int_u64) {
71 try writer.writeAll("register ");
72 try renderType(file, writer, arg.ty, decl.src());
73 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
74 } else {
75 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
76 }
77 } else {
78 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
79 }
80 } else {
81 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
82 }
83 }
84 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
85 if (as.output) |o| {
86 return file.fail(decl.src(), "TODO inline asm output", .{});
87 }
88 if (as.inputs.len > 0) {
89 if (as.output == null) {
90 try writer.writeAll(" :");
91 }
92 try writer.writeAll(": ");
93 for (as.inputs) |i, index| {
94 if (i[0] == '{' and i[i.len - 1] == '}') {
95 const reg = i[1 .. i.len - 1];
96 const arg = as.args[index];
97 if (index > 0) {
98 try writer.writeAll(", ");
99 }
100 if (arg.cast(ir.Inst.Constant)) |c| {
101 try writer.print("\"\"({}_constant)", .{reg});
102 } else {
103 // This is blocked by the earlier test
104 unreachable;
105 }
106 } else {
107 // This is blocked by the earlier test
108 unreachable;
109 }
110 }
111 }
112 try writer.writeAll(");");
113 },
114 .call => {
115 const call = inst.cast(ir.Inst.Call).?.args;
116 if (call.func.cast(ir.Inst.Constant)) |func_inst| {
117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
118 const target = func_val.func.owner_decl;
119 const tname = mem.spanZ(target.name);
120 if (file.called.get(tname) == null) {
121 try file.called.put(tname, void{});
122 try renderFunctionSignature(file, header, target);
123 try header.writeAll(";\n");
124 }
125 try writer.print("{}();", .{tname});
126 } else {
127 return file.fail(decl.src(), "TODO non-function call target?", .{});
128 }
129 if (call.args.len != 0) {
130 return file.fail(decl.src(), "TODO function arguments", .{});
131 }
132 } else {
133 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
134 }
135 },
136 else => |e| {
137 return file.fail(decl.src(), "TODO {}", .{e});
138 },
139 }
140 }
141 try writer.writeAll("\n");
84
85 try renderFunctionSignature(file, writer, decl);
86
87 try writer.writeAll(" {");
88
89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
90 const instructions = func.analysis.success.instructions;
91 if (instructions.len > 0) {
92 for (instructions) |inst| {
93 try writer.writeAll("\n\t");
94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),
98 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
14299 }
100 }
101 try writer.writeAll("\n");
102 }
103
104 try writer.writeAll("}\n\n");
105}
143106
144 try writer.writeAll("}\n\n");
145 },
146 .Array => {
147 // TODO: prevent inline asm constants from being emitted
148 const name = try map(file.allocator, mem.span(decl.name));
149 defer file.allocator.free(name);
150 if (tv.val.cast(Value.Payload.Bytes)) |payload| {
151 if (tv.ty.arraySentinel()) |sentinel| {
152 if (sentinel.toUnsignedInt() == 0) {
153 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data });
154 } else {
155 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{});
156 }
107fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {
108 const writer = file.main.writer();
109 const ret_value = inst.args.operand;
110 const value = ret_value.value().?;
111 if (expected_return_type.eql(ret_value.ty))
112 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
113 else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int)
114 if (value.intFitsInType(expected_return_type, file.options.target))
115 if (expected_return_type.intInfo(file.options.target).bits <= 64)
116 try writer.print("return {};", .{value.toUnsignedInt()})
117 else
118 return file.fail(decl.src(), "TODO return ints > 64 bits", .{})
119 else
120 return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type })
121 else
122 return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty });
123}
124
125fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
126 const writer = file.main.writer();
127 const header = file.header.writer();
128 if (inst.args.func.cast(Inst.Constant)) |func_inst| {
129 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
130 const target = func_val.func.owner_decl;
131 const target_ty = target.typed_value.most_recent.typed_value.ty;
132 const ret_ty = target_ty.fnReturnType().tag();
133 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
134 try writer.print("(void)", .{});
135 }
136 const tname = mem.spanZ(target.name);
137 if (file.called.get(tname) == null) {
138 try file.called.put(tname, void{});
139 try renderFunctionSignature(file, header, target);
140 try header.writeAll(";\n");
141 }
142 try writer.print("{}();", .{tname});
143 } else {
144 return file.fail(decl.src(), "TODO non-function call target?", .{});
145 }
146 if (inst.args.args.len != 0) {
147 return file.fail(decl.src(), "TODO function arguments", .{});
148 }
149 } else {
150 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
151 }
152}
153
154fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
155 const as = inst.args;
156 const writer = file.main.writer();
157 for (as.inputs) |i, index| {
158 if (i[0] == '{' and i[i.len - 1] == '}') {
159 const reg = i[1 .. i.len - 1];
160 const arg = as.args[index];
161 if (arg.cast(Inst.Constant)) |c| {
162 if (c.val.tag() == .int_u64) {
163 try writer.writeAll("register ");
164 try renderType(file, writer, arg.ty, decl.src());
165 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
166 } else {
167 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
168 }
169 } else {
170 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
171 }
172 } else {
173 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
174 }
175 }
176 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
177 if (as.output) |o| {
178 return file.fail(decl.src(), "TODO inline asm output", .{});
179 }
180 if (as.inputs.len > 0) {
181 if (as.output == null) {
182 try writer.writeAll(" :");
183 }
184 try writer.writeAll(": ");
185 for (as.inputs) |i, index| {
186 if (i[0] == '{' and i[i.len - 1] == '}') {
187 const reg = i[1 .. i.len - 1];
188 const arg = as.args[index];
189 if (index > 0) {
190 try writer.writeAll(", ");
191 }
192 if (arg.cast(Inst.Constant)) |c| {
193 try writer.print("\"\"({}_constant)", .{reg});
157194 } else {
158 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{});
195 // This is blocked by the earlier test
196 unreachable;
159197 }
160198 } else {
161 return file.fail(decl.src(), "TODO non-byte arrays", .{});
199 // This is blocked by the earlier test
200 unreachable;
162201 }
163 },
164 else => |e| {
165 return file.fail(decl.src(), "TODO {}", .{e});
166 },
202 }
167203 }
204 try writer.writeAll(");");
168205}
src-self-hosted/type.zig+59
......@@ -921,6 +921,11 @@ pub const Type = extern union {
921921 };
922922 }
923923
924 /// Returns true if and only if the type is a fixed-width integer.
925 pub fn isInt(self: Type) bool {
926 return self.isSignedInt() or self.isUnsignedInt();
927 }
928
924929 /// Returns true if and only if the type is a fixed-width, signed integer.
925930 pub fn isSignedInt(self: Type) bool {
926931 return switch (self.tag()) {
......@@ -975,6 +980,60 @@ pub const Type = extern union {
975980 };
976981 }
977982
983 /// Returns true if and only if the type is a fixed-width, unsigned integer.
984 pub fn isUnsignedInt(self: Type) bool {
985 return switch (self.tag()) {
986 .f16,
987 .f32,
988 .f64,
989 .f128,
990 .c_longdouble,
991 .c_void,
992 .bool,
993 .void,
994 .type,
995 .anyerror,
996 .comptime_int,
997 .comptime_float,
998 .noreturn,
999 .@"null",
1000 .@"undefined",
1001 .fn_noreturn_no_args,
1002 .fn_void_no_args,
1003 .fn_naked_noreturn_no_args,
1004 .fn_ccc_void_no_args,
1005 .function,
1006 .array,
1007 .single_const_pointer,
1008 .single_const_pointer_to_comptime_int,
1009 .array_u8_sentinel_0,
1010 .const_slice_u8,
1011 .int_signed,
1012 .i8,
1013 .isize,
1014 .c_short,
1015 .c_int,
1016 .c_long,
1017 .c_longlong,
1018 .i16,
1019 .i32,
1020 .i64,
1021 => false,
1022
1023 .int_unsigned,
1024 .u8,
1025 .usize,
1026 .c_ushort,
1027 .c_uint,
1028 .c_ulong,
1029 .c_ulonglong,
1030 .u16,
1031 .u32,
1032 .u64,
1033 => true,
1034 };
1035 }
1036
9781037 /// Asserts the type is an integer.
9791038 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
9801039 return switch (self.tag()) {
test/stage2/cbe.zig+22-22
......@@ -65,26 +65,26 @@ pub fn addCases(ctx: *TestContext) !void {
6565 \\}
6666 \\
6767 );
68 //ctx.c("basic return", linux_x64,
69 // \\fn main() u8 {
70 // \\ return 103;
71 // \\}
72 // \\
73 // \\export fn _start() noreturn {
74 // \\ _ = main();
75 // \\}
76 //,
77 // \\#include <stdint.h>
78 // \\
79 // \\uint8_t main(void);
80 // \\
81 // \\noreturn void _start(void) {
82 // \\ (void)main();
83 // \\}
84 // \\
85 // \\uint8_t main(void) {
86 // \\ return 103;
87 // \\}
88 // \\
89 //);
68 ctx.c("basic return", linux_x64,
69 \\fn main() u8 {
70 \\ return 103;
71 \\}
72 \\
73 \\export fn _start() noreturn {
74 \\ _ = main();
75 \\}
76 ,
77 \\#include <stdint.h>
78 \\
79 \\uint8_t main(void);
80 \\
81 \\noreturn void _start(void) {
82 \\ (void)main();
83 \\}
84 \\
85 \\uint8_t main(void) {
86 \\ return 103;
87 \\}
88 \\
89 );
9090}