| ... | @@ -11,6 +11,8 @@ const C = link.File.C; | ... | @@ -11,6 +11,8 @@ const C = link.File.C; |
| 11 | const Decl = Module.Decl; | 11 | const Decl = Module.Decl; |
| 12 | const mem = std.mem; | 12 | const mem = std.mem; |
| 13 | | 13 | |
| | 14 | const indentation = " "; |
| | 15 | |
| 14 | /// Maps a name from Zig source to C. Currently, this will always give the same | 16 | /// Maps a name from Zig source to C. Currently, this will always give the same |
| 15 | /// output for any given input, sometimes resulting in broken identifiers. | 17 | /// output for any given input, sometimes resulting in broken identifiers. |
| 16 | fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { | 18 | fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { |
| ... | @@ -52,8 +54,10 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va | ... | @@ -52,8 +54,10 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va |
| 52 | fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { | 54 | fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { |
| 53 | const tv = decl.typed_value.most_recent.typed_value; | 55 | const tv = decl.typed_value.most_recent.typed_value; |
| 54 | try renderType(ctx, writer, tv.ty.fnReturnType()); | 56 | try renderType(ctx, writer, tv.ty.fnReturnType()); |
| 55 | const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name)); | 57 | // Use the child allocator directly, as we know the name can be freed before |
| 56 | defer ctx.file.base.allocator.free(name); | 58 | // the rest of the arena. |
| | 59 | const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); |
| | 60 | defer ctx.arena.child_allocator.free(name); |
| 57 | try writer.print(" {}(", .{name}); | 61 | try writer.print(" {}(", .{name}); |
| 58 | var param_len = tv.ty.fnParamLen(); | 62 | var param_len = tv.ty.fnParamLen(); |
| 59 | if (param_len == 0) | 63 | if (param_len == 0) |
| ... | @@ -87,6 +91,7 @@ fn genArray(file: *C, decl: *Decl) !void { | ... | @@ -87,6 +91,7 @@ fn genArray(file: *C, decl: *Decl) !void { |
| 87 | if (tv.val.cast(Value.Payload.Bytes)) |payload| | 91 | if (tv.val.cast(Value.Payload.Bytes)) |payload| |
| 88 | if (tv.ty.sentinel()) |sentinel| | 92 | if (tv.ty.sentinel()) |sentinel| |
| 89 | if (sentinel.toUnsignedInt() == 0) | 93 | if (sentinel.toUnsignedInt() == 0) |
| | 94 | // TODO: static by default |
| 90 | try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) | 95 | try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) |
| 91 | else | 96 | else |
| 92 | return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{}) | 97 | return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{}) |
| ... | @@ -99,22 +104,30 @@ fn genArray(file: *C, decl: *Decl) !void { | ... | @@ -99,22 +104,30 @@ fn genArray(file: *C, decl: *Decl) !void { |
| 99 | const Context = struct { | 104 | const Context = struct { |
| 100 | file: *C, | 105 | file: *C, |
| 101 | decl: *Decl, | 106 | decl: *Decl, |
| 102 | inst_map: std.AutoHashMap(*Inst, []u8), | 107 | inst_map: *std.AutoHashMap(*Inst, []u8), |
| | 108 | arena: *std.heap.ArenaAllocator, |
| 103 | argdex: usize = 0, | 109 | argdex: usize = 0, |
| 104 | unnamed_index: usize = 0, | 110 | unnamed_index: usize = 0, |
| 105 | | 111 | |
| | 112 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { |
| | 113 | if (inst.cast(Inst.Constant)) |const_inst| { |
| | 114 | var out = std.ArrayList(u8).init(&self.arena.allocator); |
| | 115 | try renderValue(self, out.writer(), inst.ty, const_inst.val); |
| | 116 | return out.toOwnedSlice(); |
| | 117 | } |
| | 118 | if (self.inst_map.get(inst)) |val| { |
| | 119 | return val; |
| | 120 | } |
| | 121 | unreachable; |
| | 122 | } |
| | 123 | |
| 106 | fn name(self: *Context) ![]u8 { | 124 | fn name(self: *Context) ![]u8 { |
| 107 | const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index}); | 125 | const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{}", .{self.unnamed_index}); |
| 108 | self.unnamed_index += 1; | 126 | self.unnamed_index += 1; |
| 109 | return val; | 127 | return val; |
| 110 | } | 128 | } |
| 111 | | 129 | |
| 112 | fn deinit(self: *Context) void { | 130 | fn deinit(self: *Context) void { |
| 113 | var it = self.inst_map.iterator(); | | |
| 114 | while (it.next()) |kv| { | | |
| 115 | self.file.base.allocator.free(kv.value); | | |
| 116 | } | | |
| 117 | self.inst_map.deinit(); | | |
| 118 | self.* = undefined; | 131 | self.* = undefined; |
| 119 | } | 132 | } |
| 120 | }; | 133 | }; |
| ... | @@ -123,10 +136,15 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -123,10 +136,15 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 123 | const writer = file.main.writer(); | 136 | const writer = file.main.writer(); |
| 124 | const tv = decl.typed_value.most_recent.typed_value; | 137 | const tv = decl.typed_value.most_recent.typed_value; |
| 125 | | 138 | |
| | 139 | var arena = std.heap.ArenaAllocator.init(file.base.allocator); |
| | 140 | defer arena.deinit(); |
| | 141 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); |
| | 142 | defer inst_map.deinit(); |
| 126 | var ctx = Context{ | 143 | var ctx = Context{ |
| 127 | .file = file, | 144 | .file = file, |
| 128 | .decl = decl, | 145 | .decl = decl, |
| 129 | .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator), | 146 | .arena = &arena, |
| | 147 | .inst_map = &inst_map, |
| 130 | }; | 148 | }; |
| 131 | defer ctx.deinit(); | 149 | defer ctx.deinit(); |
| 132 | | 150 | |
| ... | @@ -142,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -142,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 142 | if (switch (inst.tag) { | 160 | if (switch (inst.tag) { |
| 143 | .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), | 161 | .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), |
| 144 | .call => try genCall(&ctx, inst.castTag(.call).?), | 162 | .call => try genCall(&ctx, inst.castTag(.call).?), |
| | 163 | .add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"), |
| | 164 | .sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"), |
| 145 | .ret => try genRet(&ctx, inst.castTag(.ret).?), | 165 | .ret => try genRet(&ctx, inst.castTag(.ret).?), |
| 146 | .retvoid => try genRetVoid(&ctx), | 166 | .retvoid => try genRetVoid(&ctx), |
| 147 | .arg => try genArg(&ctx), | 167 | .arg => try genArg(&ctx), |
| ... | @@ -160,13 +180,13 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -160,13 +180,13 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 160 | } | 180 | } |
| 161 | | 181 | |
| 162 | fn genArg(ctx: *Context) !?[]u8 { | 182 | fn genArg(ctx: *Context) !?[]u8 { |
| 163 | const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex}); | 183 | const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex}); |
| 164 | ctx.argdex += 1; | 184 | ctx.argdex += 1; |
| 165 | return name; | 185 | return name; |
| 166 | } | 186 | } |
| 167 | | 187 | |
| 168 | fn genRetVoid(ctx: *Context) !?[]u8 { | 188 | fn genRetVoid(ctx: *Context) !?[]u8 { |
| 169 | try ctx.file.main.writer().print(" return;\n", .{}); | 189 | try ctx.file.main.writer().print(indentation ++ "return;\n", .{}); |
| 170 | return null; | 190 | return null; |
| 171 | } | 191 | } |
| 172 | | 192 | |
| ... | @@ -180,9 +200,8 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | ... | @@ -180,9 +200,8 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { |
| 180 | const op = inst.operand; | 200 | const op = inst.operand; |
| 181 | const writer = ctx.file.main.writer(); | 201 | const writer = ctx.file.main.writer(); |
| 182 | const name = try ctx.name(); | 202 | const name = try ctx.name(); |
| 183 | const from = ctx.inst_map.get(op) orelse | 203 | const from = try ctx.resolveInst(inst.operand); |
| 184 | return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{}); | 204 | try writer.writeAll(indentation ++ "const "); |
| 185 | try writer.writeAll(" const "); | | |
| 186 | try renderType(ctx, writer, inst.base.ty); | 205 | try renderType(ctx, writer, inst.base.ty); |
| 187 | try writer.print(" {} = (", .{name}); | 206 | try writer.print(" {} = (", .{name}); |
| 188 | try renderType(ctx, writer, inst.base.ty); | 207 | try renderType(ctx, writer, inst.base.ty); |
| ... | @@ -190,10 +209,23 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | ... | @@ -190,10 +209,23 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { |
| 190 | return name; | 209 | return name; |
| 191 | } | 210 | } |
| 192 | | 211 | |
| | 212 | fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { |
| | 213 | if (inst.base.isUnused()) |
| | 214 | return null; |
| | 215 | const lhs = ctx.resolveInst(inst.lhs); |
| | 216 | const rhs = ctx.resolveInst(inst.rhs); |
| | 217 | const writer = ctx.file.main.writer(); |
| | 218 | const name = try ctx.name(); |
| | 219 | try writer.writeAll(indentation ++ "const "); |
| | 220 | try renderType(ctx, writer, inst.base.ty); |
| | 221 | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); |
| | 222 | return name; |
| | 223 | } |
| | 224 | |
| 193 | fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | 225 | fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 194 | const writer = ctx.file.main.writer(); | 226 | const writer = ctx.file.main.writer(); |
| 195 | const header = ctx.file.header.writer(); | 227 | const header = ctx.file.header.writer(); |
| 196 | try writer.writeAll(" "); | 228 | try writer.writeAll(indentation); |
| 197 | if (inst.func.castTag(.constant)) |func_inst| { | 229 | if (inst.func.castTag(.constant)) |func_inst| { |
| 198 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | 230 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| 199 | const target = func_val.func.owner_decl; | 231 | const target = func_val.func.owner_decl; |
| ... | @@ -217,7 +249,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | ... | @@ -217,7 +249,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 217 | if (arg.cast(Inst.Constant)) |con| { | 249 | if (arg.cast(Inst.Constant)) |con| { |
| 218 | try renderValue(ctx, writer, arg.ty, con.val); | 250 | try renderValue(ctx, writer, arg.ty, con.val); |
| 219 | } else { | 251 | } else { |
| 220 | return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg}); | 252 | const val = try ctx.resolveInst(arg); |
| | 253 | try writer.print("{}", .{val}); |
| 221 | } | 254 | } |
| 222 | } | 255 | } |
| 223 | } | 256 | } |
| ... | @@ -242,13 +275,13 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | ... | @@ -242,13 +275,13 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 242 | } | 275 | } |
| 243 | | 276 | |
| 244 | fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | 277 | fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 245 | try ctx.file.main.writer().writeAll(" zig_unreachable();\n"); | 278 | try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); |
| 246 | return null; | 279 | return null; |
| 247 | } | 280 | } |
| 248 | | 281 | |
| 249 | fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { | 282 | fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { |
| 250 | const writer = ctx.file.main.writer(); | 283 | const writer = ctx.file.main.writer(); |
| 251 | try writer.writeAll(" "); | 284 | try writer.writeAll(indentation); |
| 252 | for (as.inputs) |i, index| { | 285 | for (as.inputs) |i, index| { |
| 253 | if (i[0] == '{' and i[i.len - 1] == '}') { | 286 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 254 | const reg = i[1 .. i.len - 1]; | 287 | const reg = i[1 .. i.len - 1]; |