| ... | ... | @@ -11,6 +11,8 @@ const C = link.File.C; |
| 11 | 11 | const Decl = Module.Decl; |
| 12 | 12 | const mem = std.mem; |
| 13 | 13 | |
| 14 | const indentation = " "; |
| 15 | |
| 14 | 16 | /// Maps a name from Zig source to C. Currently, this will always give the same |
| 15 | 17 | /// output for any given input, sometimes resulting in broken identifiers. |
| 16 | 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 | 54 | fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { |
| 53 | 55 | const tv = decl.typed_value.most_recent.typed_value; |
| 54 | 56 | try renderType(ctx, writer, tv.ty.fnReturnType()); |
| 55 | | const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name)); |
| 56 | | defer ctx.file.base.allocator.free(name); |
| 57 | // Use the child allocator directly, as we know the name can be freed before |
| 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 | 61 | try writer.print(" {}(", .{name}); |
| 58 | 62 | var param_len = tv.ty.fnParamLen(); |
| 59 | 63 | if (param_len == 0) |
| ... | ... | @@ -87,6 +91,7 @@ fn genArray(file: *C, decl: *Decl) !void { |
| 87 | 91 | if (tv.val.cast(Value.Payload.Bytes)) |payload| |
| 88 | 92 | if (tv.ty.sentinel()) |sentinel| |
| 89 | 93 | if (sentinel.toUnsignedInt() == 0) |
| 94 | // TODO: static by default |
| 90 | 95 | try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) |
| 91 | 96 | else |
| 92 | 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 | 104 | const Context = struct { |
| 100 | 105 | file: *C, |
| 101 | 106 | decl: *Decl, |
| 102 | | inst_map: std.AutoHashMap(*Inst, []u8), |
| 107 | inst_map: *std.AutoHashMap(*Inst, []u8), |
| 108 | arena: *std.heap.ArenaAllocator, |
| 103 | 109 | argdex: usize = 0, |
| 104 | 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 | 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 | 126 | self.unnamed_index += 1; |
| 109 | 127 | return val; |
| 110 | 128 | } |
| 111 | 129 | |
| 112 | 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 | 131 | self.* = undefined; |
| 119 | 132 | } |
| 120 | 133 | }; |
| ... | ... | @@ -123,10 +136,15 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 123 | 136 | const writer = file.main.writer(); |
| 124 | 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 | 143 | var ctx = Context{ |
| 127 | 144 | .file = file, |
| 128 | 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 | 149 | defer ctx.deinit(); |
| 132 | 150 | |
| ... | ... | @@ -142,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 142 | 160 | if (switch (inst.tag) { |
| 143 | 161 | .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), |
| 144 | 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 | 165 | .ret => try genRet(&ctx, inst.castTag(.ret).?), |
| 146 | 166 | .retvoid => try genRetVoid(&ctx), |
| 147 | 167 | .arg => try genArg(&ctx), |
| ... | ... | @@ -160,13 +180,13 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 160 | 180 | } |
| 161 | 181 | |
| 162 | 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 | 184 | ctx.argdex += 1; |
| 165 | 185 | return name; |
| 166 | 186 | } |
| 167 | 187 | |
| 168 | 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 | 190 | return null; |
| 171 | 191 | } |
| 172 | 192 | |
| ... | ... | @@ -180,9 +200,8 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { |
| 180 | 200 | const op = inst.operand; |
| 181 | 201 | const writer = ctx.file.main.writer(); |
| 182 | 202 | const name = try ctx.name(); |
| 183 | | const from = ctx.inst_map.get(op) orelse |
| 184 | | return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{}); |
| 185 | | try writer.writeAll(" const "); |
| 203 | const from = try ctx.resolveInst(inst.operand); |
| 204 | try writer.writeAll(indentation ++ "const "); |
| 186 | 205 | try renderType(ctx, writer, inst.base.ty); |
| 187 | 206 | try writer.print(" {} = (", .{name}); |
| 188 | 207 | try renderType(ctx, writer, inst.base.ty); |
| ... | ... | @@ -190,10 +209,23 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { |
| 190 | 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 | 225 | fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 194 | 226 | const writer = ctx.file.main.writer(); |
| 195 | 227 | const header = ctx.file.header.writer(); |
| 196 | | try writer.writeAll(" "); |
| 228 | try writer.writeAll(indentation); |
| 197 | 229 | if (inst.func.castTag(.constant)) |func_inst| { |
| 198 | 230 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| 199 | 231 | const target = func_val.func.owner_decl; |
| ... | ... | @@ -217,7 +249,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 217 | 249 | if (arg.cast(Inst.Constant)) |con| { |
| 218 | 250 | try renderValue(ctx, writer, arg.ty, con.val); |
| 219 | 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 | 275 | } |
| 243 | 276 | |
| 244 | 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 | 279 | return null; |
| 247 | 280 | } |
| 248 | 281 | |
| 249 | 282 | fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { |
| 250 | 283 | const writer = ctx.file.main.writer(); |
| 251 | | try writer.writeAll(" "); |
| 284 | try writer.writeAll(indentation); |
| 252 | 285 | for (as.inputs) |i, index| { |
| 253 | 286 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 254 | 287 | const reg = i[1 .. i.len - 1]; |