authorgravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-08-11 10:24:32-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-12 21:58:21-07:00
log5a166cead80d4d77bffba0b116fdeffb5820f5a4
treef2a60d219fb889fdd23358a2538da9602c727bb6
parentdbd1e42ef227bf55e9356b2d1a1cbde1fbec0e82

CBE: fix handling of IR dependencies


3 files changed, 102 insertions(+), 54 deletions(-)

src-self-hosted/codegen/c.zig+99-52
...@@ -94,6 +94,28 @@ fn genArray(file: *C, decl: *Decl) !void {...@@ -94,6 +94,28 @@ fn genArray(file: *C, decl: *Decl) !void {
94 return file.fail(decl.src(), "TODO non-byte arrays", .{});94 return file.fail(decl.src(), "TODO non-byte arrays", .{});
95}95}
9696
97const Context = struct {
98 file: *C,
99 decl: *Decl,
100 inst_map: std.AutoHashMap(*Inst, []u8),
101 argdex: usize = 0,
102 unnamed_index: usize = 0,
103
104 fn name(self: *Context) ![]u8 {
105 const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index});
106 self.unnamed_index += 1;
107 return val;
108 }
109
110 fn deinit(self: *Context) void {
111 for (self.inst_map.items()) |kv| {
112 self.file.base.allocator.free(kv.value);
113 }
114 self.inst_map.deinit();
115 self.* = undefined;
116 }
117};
118
97fn genFn(file: *C, decl: *Decl) !void {119fn genFn(file: *C, decl: *Decl) !void {
98 const writer = file.main.writer();120 const writer = file.main.writer();
99 const tv = decl.typed_value.most_recent.typed_value;121 const tv = decl.typed_value.most_recent.typed_value;
...@@ -102,24 +124,31 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -102,24 +124,31 @@ fn genFn(file: *C, decl: *Decl) !void {
102124
103 try writer.writeAll(" {");125 try writer.writeAll(" {");
104126
127 var ctx = Context{
128 .file = file,
129 .decl = decl,
130 .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator),
131 };
132 defer ctx.deinit();
133
105 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;134 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
106 const instructions = func.analysis.success.instructions;135 const instructions = func.analysis.success.instructions;
107 var argdex: usize = 0;
108 if (instructions.len > 0) {136 if (instructions.len > 0) {
109 try writer.writeAll("\n");137 try writer.writeAll("\n");
110 for (instructions) |inst| {138 for (instructions) |inst| {
111 switch (inst.tag) {139 if (switch (inst.tag) {
112 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl, &argdex),140 .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
113 .call => try genCall(file, inst.castTag(.call).?, decl),141 .call => try genCall(&ctx, inst.castTag(.call).?),
114 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),142 .ret => try genRet(&ctx, inst.castTag(.ret).?),
115 .retvoid => try file.main.writer().print(" return;\n", .{}),143 .retvoid => try genRetVoid(&ctx),
116 .arg => {},144 .arg => try genArg(&ctx),
117 .dbg_stmt => try genDbgStmt(file, inst.castTag(.dbg_stmt).?, decl),145 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
118 .breakpoint => try genBreak(file, inst.castTag(.breakpoint).?, decl),146 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
119 .unreach => try genUnreach(file, inst.castTag(.unreach).?, decl),147 .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?),
120 // This will be handled correctly later?148 .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?),
121 .intcast => {},
122 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),149 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
150 }) |name| {
151 try ctx.inst_map.putNoClobber(inst, name);
123 }152 }
124 }153 }
125 }154 }
...@@ -127,27 +156,40 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -127,27 +156,40 @@ fn genFn(file: *C, decl: *Decl) !void {
127 try writer.writeAll("}\n\n");156 try writer.writeAll("}\n\n");
128}157}
129158
130fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {159fn genArg(ctx: *Context) !?[]u8 {
131 return file.fail(decl.src(), "TODO return {}", .{expected_return_type});160 const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex});
161 ctx.argdex += 1;
162 return name;
163}
164
165fn genRetVoid(ctx: *Context) !?[]u8 {
166 try ctx.file.main.writer().print(" return;\n", .{});
167 return null;
132}168}
133169
134fn genIntCast(file: *C, inst: *Inst.UnOp, decl: *Decl, argdex: *usize) !void {170fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
171 return ctx.file.fail(ctx.decl.src(), "TODO return", .{});
172}
173
174fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
175 if (inst.base.isUnused())
176 return null;
135 const op = inst.operand;177 const op = inst.operand;
136 const writer = file.main.writer();178 const writer = ctx.file.main.writer();
137 try writer.writeByte('(');179 const name = try ctx.name();
138 try renderType(file, writer, inst.base.ty, decl.src());180 const from = ctx.inst_map.get(op) orelse
139 try writer.writeByte(')');181 return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{});
140 if (op.castTag(.arg)) |_| {182 try writer.writeAll(" const ");
141 try writer.print("arg{}", .{argdex.*});183 try renderType(ctx.file, writer, inst.base.ty, ctx.decl.src());
142 argdex.* += 1;184 try writer.print(" {} = (", .{name});
143 } else {185 try renderType(ctx.file, writer, inst.base.ty, ctx.decl.src());
144 return file.fail(decl.src(), "TODO intcast {} to {}", .{ op, inst.base.ty });186 try writer.print("){};\n", .{from});
145 }187 return name;
146}188}
147189
148fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {190fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
149 const writer = file.main.writer();191 const writer = ctx.file.main.writer();
150 const header = file.header.writer();192 const header = ctx.file.header.writer();
151 try writer.writeAll(" ");193 try writer.writeAll(" ");
152 if (inst.func.castTag(.constant)) |func_inst| {194 if (inst.func.castTag(.constant)) |func_inst| {
153 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {195 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
...@@ -158,9 +200,9 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -158,9 +200,9 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
158 try writer.print("(void)", .{});200 try writer.print("(void)", .{});
159 }201 }
160 const tname = mem.spanZ(target.name);202 const tname = mem.spanZ(target.name);
161 if (file.called.get(tname) == null) {203 if (ctx.file.called.get(tname) == null) {
162 try file.called.put(tname, void{});204 try ctx.file.called.put(tname, void{});
163 try renderFunctionSignature(file, header, target);205 try renderFunctionSignature(ctx.file, header, target);
164 try header.writeAll(";\n");206 try header.writeAll(";\n");
165 }207 }
166 try writer.print("{}(", .{tname});208 try writer.print("{}(", .{tname});
...@@ -170,61 +212,65 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -170,61 +212,65 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
170 try writer.writeAll(", ");212 try writer.writeAll(", ");
171 }213 }
172 if (arg.cast(Inst.Constant)) |con| {214 if (arg.cast(Inst.Constant)) |con| {
173 try renderValue(file, writer, con.val, decl.src());215 try renderValue(ctx.file, writer, con.val, ctx.decl.src());
174 } else {216 } else {
175 return file.fail(decl.src(), "TODO call pass arg {}", .{arg});217 return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg});
176 }218 }
177 }219 }
178 }220 }
179 try writer.writeAll(");\n");221 try writer.writeAll(");\n");
180 } else {222 } else {
181 return file.fail(decl.src(), "TODO non-function call target?", .{});223 return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{});
182 }224 }
183 } else {225 } else {
184 return file.fail(decl.src(), "TODO non-constant call inst?", .{});226 return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});
185 }227 }
228 return null;
186}229}
187230
188fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {231fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
189 // TODO emit #line directive here with line number and filename232 // TODO emit #line directive here with line number and filename
233 return null;
190}234}
191235
192fn genBreak(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {236fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
193 // TODO ??237 // TODO ??
238 return null;
194}239}
195240
196fn genUnreach(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {241fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
197 try file.main.writer().writeAll(" zig_unreachable();\n");242 try ctx.file.main.writer().writeAll(" zig_unreachable();\n");
243 return null;
198}244}
199245
200fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl, argdex: *usize) !void {246fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
201 const writer = file.main.writer();247 const writer = ctx.file.main.writer();
202 try writer.writeAll(" ");248 try writer.writeAll(" ");
203 for (as.inputs) |i, index| {249 for (as.inputs) |i, index| {
204 if (i[0] == '{' and i[i.len - 1] == '}') {250 if (i[0] == '{' and i[i.len - 1] == '}') {
205 const reg = i[1 .. i.len - 1];251 const reg = i[1 .. i.len - 1];
206 const arg = as.args[index];252 const arg = as.args[index];
207 try writer.writeAll("register ");253 try writer.writeAll("register ");
208 try renderType(file, writer, arg.ty, decl.src());254 try renderType(ctx.file, writer, arg.ty, ctx.decl.src());
209 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });255 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
256 // TODO merge constant handling into inst_map as well
210 if (arg.castTag(.constant)) |c| {257 if (arg.castTag(.constant)) |c| {
211 try renderValue(file, writer, c.val, decl.src());258 try renderValue(ctx.file, writer, c.val, ctx.decl.src());
212 } else if (arg.castTag(.arg)) |inst| {259 try writer.writeAll(";\n ");
213 try writer.print("arg{}", .{argdex.*});
214 argdex.* += 1;
215 } else if (arg.castTag(.intcast)) |inst| {
216 try genIntCast(file, inst, decl, argdex);
217 } else {260 } else {
218 return file.fail(decl.src(), "TODO non-constant inline asm args ({})", .{arg.tag});261 const gop = try ctx.inst_map.getOrPut(arg);
262 if (!gop.found_existing) {
263 return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
264 }
265 try writer.print("{};\n ", .{gop.entry.value});
219 }266 }
220 try writer.writeAll(";\n ");
221 } else {267 } else {
222 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});268 return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});
223 }269 }
224 }270 }
225 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });271 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
226 if (as.output) |o| {272 if (as.output) |o| {
227 return file.fail(decl.src(), "TODO inline asm output", .{});273 return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{});
228 }274 }
229 if (as.inputs.len > 0) {275 if (as.inputs.len > 0) {
230 if (as.output == null) {276 if (as.output == null) {
...@@ -246,4 +292,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl, argdex: *usize) !void {...@@ -246,4 +292,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl, argdex: *usize) !void {
246 }292 }
247 }293 }
248 try writer.writeAll(");\n");294 try writer.writeAll(");\n");
295 return null;
249}296}
src-self-hosted/link.zig+1-1
...@@ -229,7 +229,7 @@ pub const File = struct {...@@ -229,7 +229,7 @@ pub const File = struct {
229 return &c_file.base;229 return &c_file.base;
230 }230 }
231231
232 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {232 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) anyerror {
233 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);233 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
234 return error.AnalysisFail;234 return error.AnalysisFail;
235 }235 }
test/stage2/cbe.zig+2-1
...@@ -129,8 +129,9 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -129,8 +129,9 @@ pub fn addCases(ctx: *TestContext) !void {
129 \\}129 \\}
130 \\130 \\
131 \\zig_noreturn void exit(uint8_t arg0) {131 \\zig_noreturn void exit(uint8_t arg0) {
132 \\ const size_t __temp_0 = (size_t)arg0;
132 \\ register size_t rax_constant __asm__("rax") = 231;133 \\ register size_t rax_constant __asm__("rax") = 231;
133 \\ register size_t rdi_constant __asm__("rdi") = (size_t)arg0;134 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;
134 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));135 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
135 \\ zig_unreachable();136 \\ zig_unreachable();
136 \\}137 \\}