authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-13 00:28:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-13 00:28:11-07:00
log08154c0deb653e016d6eb285c85094048eeded89
treed90ce4372ed47afe3bea24245c359fad03820026
parent25b1c00c72b51ef9e011867b3fc4f37b3e216223

stage2: add retvoid support to CBE


5 files changed, 212 insertions(+), 209 deletions(-)

src-self-hosted/Module.zig+3-2
......@@ -1210,8 +1210,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12101210
12111211 try self.astGenBlock(&gen_scope.base, body_block);
12121212
1213 const last_inst = gen_scope.instructions.items[gen_scope.instructions.items.len - 1];
1214 if (!last_inst.tag.isNoReturn()) {
1213 if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
1214 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
1215 {
12151216 const src = tree.token_locs[body_block.rbrace].start;
12161217 _ = try self.addZIRInst(&gen_scope.base, src, zir.Inst.ReturnVoid, .{}, .{});
12171218 }
src-self-hosted/cgen.zig deleted-205
......@@ -1,205 +0,0 @@
1const link = @import("link.zig");
2const Module = @import("Module.zig");
3
4const std = @import("std");
5
6const Inst = @import("ir.zig").Inst;
7const Value = @import("value.zig").Value;
8const Type = @import("type.zig").Type;
9
10const C = link.File.C;
11const Decl = Module.Decl;
12const mem = std.mem;
13
14/// Maps a name from Zig source to C. This will always give the same output for
15/// any given input.
16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
17 return allocator.dupe(u8, name);
18}
19
20fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
21 if (T.tag() == .usize) {
22 file.need_stddef = true;
23 try writer.writeAll("size_t");
24 } else {
25 switch (T.zigTypeTag()) {
26 .NoReturn => {
27 file.need_noreturn = true;
28 try writer.writeAll("noreturn void");
29 },
30 .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 },
39 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
40 }
41 }
42}
43
44fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
45 const tv = decl.typed_value.most_recent.typed_value;
46 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
47 const name = try map(file.allocator, mem.spanZ(decl.name));
48 defer file.allocator.free(name);
49 try writer.print(" {}(", .{name});
50 if (tv.ty.fnParamLen() == 0)
51 try writer.writeAll("void)")
52 else
53 return file.fail(decl.src(), "TODO implement parameters", .{});
54}
55
56pub 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 {
82 const writer = file.main.writer();
83 const tv = decl.typed_value.most_recent.typed_value;
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}),
99 }
100 }
101 try writer.writeAll("\n");
102 }
103
104 try writer.writeAll("}\n\n");
105}
106
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});
194 } else {
195 // This is blocked by the earlier test
196 unreachable;
197 }
198 } else {
199 // This is blocked by the earlier test
200 unreachable;
201 }
202 }
203 }
204 try writer.writeAll(");");
205}
src-self-hosted/codegen/c.zig created+206
......@@ -0,0 +1,206 @@
1const std = @import("std");
2
3const link = @import("../link.zig");
4const Module = @import("../Module.zig");
5
6const Inst = @import("../ir.zig").Inst;
7const Value = @import("../value.zig").Value;
8const Type = @import("../type.zig").Type;
9
10const C = link.File.C;
11const Decl = Module.Decl;
12const mem = std.mem;
13
14/// Maps a name from Zig source to C. This will always give the same output for
15/// any given input.
16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
17 return allocator.dupe(u8, name);
18}
19
20fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
21 if (T.tag() == .usize) {
22 file.need_stddef = true;
23 try writer.writeAll("size_t");
24 } else {
25 switch (T.zigTypeTag()) {
26 .NoReturn => {
27 file.need_noreturn = true;
28 try writer.writeAll("noreturn void");
29 },
30 .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 },
39 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
40 }
41 }
42}
43
44fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
45 const tv = decl.typed_value.most_recent.typed_value;
46 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
47 const name = try map(file.allocator, mem.spanZ(decl.name));
48 defer file.allocator.free(name);
49 try writer.print(" {}(", .{name});
50 if (tv.ty.fnParamLen() == 0)
51 try writer.writeAll("void)")
52 else
53 return file.fail(decl.src(), "TODO implement parameters", .{});
54}
55
56pub 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 {
82 const writer = file.main.writer();
83 const tv = decl.typed_value.most_recent.typed_value;
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 .retvoid => try file.main.writer().print("return;", .{}),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }
101 }
102 try writer.writeAll("\n");
103 }
104
105 try writer.writeAll("}\n\n");
106}
107
108fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {
109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;
111 const value = ret_value.value().?;
112 if (expected_return_type.eql(ret_value.ty))
113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
114 else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int)
115 if (value.intFitsInType(expected_return_type, file.options.target))
116 if (expected_return_type.intInfo(file.options.target).bits <= 64)
117 try writer.print("return {};", .{value.toUnsignedInt()})
118 else
119 return file.fail(decl.src(), "TODO return ints > 64 bits", .{})
120 else
121 return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type })
122 else
123 return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty });
124}
125
126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127 const writer = file.main.writer();
128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {
130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131 const target = func_val.func.owner_decl;
132 const target_ty = target.typed_value.most_recent.typed_value.ty;
133 const ret_ty = target_ty.fnReturnType().tag();
134 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
135 try writer.print("(void)", .{});
136 }
137 const tname = mem.spanZ(target.name);
138 if (file.called.get(tname) == null) {
139 try file.called.put(tname, void{});
140 try renderFunctionSignature(file, header, target);
141 try header.writeAll(";\n");
142 }
143 try writer.print("{}();", .{tname});
144 } else {
145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146 }
147 if (inst.args.args.len != 0) {
148 return file.fail(decl.src(), "TODO function arguments", .{});
149 }
150 } else {
151 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
152 }
153}
154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
157 const writer = file.main.writer();
158 for (as.inputs) |i, index| {
159 if (i[0] == '{' and i[i.len - 1] == '}') {
160 const reg = i[1 .. i.len - 1];
161 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {
163 if (c.val.tag() == .int_u64) {
164 try writer.writeAll("register ");
165 try renderType(file, writer, arg.ty, decl.src());
166 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
167 } else {
168 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
169 }
170 } else {
171 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
172 }
173 } else {
174 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
175 }
176 }
177 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
178 if (as.output) |o| {
179 return file.fail(decl.src(), "TODO inline asm output", .{});
180 }
181 if (as.inputs.len > 0) {
182 if (as.output == null) {
183 try writer.writeAll(" :");
184 }
185 try writer.writeAll(": ");
186 for (as.inputs) |i, index| {
187 if (i[0] == '{' and i[i.len - 1] == '}') {
188 const reg = i[1 .. i.len - 1];
189 const arg = as.args[index];
190 if (index > 0) {
191 try writer.writeAll(", ");
192 }
193 if (arg.cast(Inst.Constant)) |c| {
194 try writer.print("\"\"({}_constant)", .{reg});
195 } else {
196 // This is blocked by the earlier test
197 unreachable;
198 }
199 } else {
200 // This is blocked by the earlier test
201 unreachable;
202 }
203 }
204 }
205 try writer.writeAll(");");
206}
src-self-hosted/link.zig+2-2
......@@ -7,7 +7,7 @@ const Module = @import("Module.zig");
77const fs = std.fs;
88const elf = std.elf;
99const codegen = @import("codegen.zig");
10const cgen = @import("cgen.zig");
10const c_codegen = @import("codegen/c.zig");
1111
1212const default_entry_addr = 0x8000000;
1313
......@@ -259,7 +259,7 @@ pub const File = struct {
259259 }
260260
261261 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
262 cgen.generate(self, decl) catch |err| {
262 c_codegen.generate(self, decl) catch |err| {
263263 if (err == error.CGenFailure) {
264264 try module.failed_decls.put(module.gpa, decl, self.error_msg);
265265 }
test/stage2/cbe.zig+1
......@@ -62,6 +62,7 @@ pub fn addCases(ctx: *TestContext) !void {
6262 \\ register size_t rax_constant __asm__("rax") = 231;
6363 \\ register size_t rdi_constant __asm__("rdi") = 0;
6464 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
65 \\ return;
6566 \\}
6667 \\
6768 );