| ... | ... | @@ -1,495 +1,526 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; |
| 3 | const log = std.log.scoped(.c); |
| 4 | const Writer = std.ArrayList(u8).Writer; |
| 2 | 5 | |
| 3 | 6 | const link = @import("../link.zig"); |
| 4 | 7 | const Module = @import("../Module.zig"); |
| 5 | 8 | const Compilation = @import("../Compilation.zig"); |
| 6 | | |
| 7 | 9 | const Inst = @import("../ir.zig").Inst; |
| 8 | 10 | const Value = @import("../value.zig").Value; |
| 9 | 11 | const Type = @import("../type.zig").Type; |
| 10 | | |
| 11 | 12 | const C = link.File.C; |
| 12 | 13 | const Decl = Module.Decl; |
| 13 | | const mem = std.mem; |
| 14 | | const log = std.log.scoped(.c); |
| 14 | const trace = @import("../tracy.zig").trace; |
| 15 | 15 | |
| 16 | | const Writer = std.ArrayList(u8).Writer; |
| 16 | const Mutability = enum { Const, Mut }; |
| 17 | 17 | |
| 18 | | /// Maps a name from Zig source to C. Currently, this will always give the same |
| 19 | | /// output for any given input, sometimes resulting in broken identifiers. |
| 20 | | fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { |
| 21 | | return allocator.dupe(u8, name); |
| 22 | | } |
| 18 | pub const CValue = union(enum) { |
| 19 | none: void, |
| 20 | /// Index into local_names |
| 21 | local: usize, |
| 22 | /// Index into local_names, but take the address. |
| 23 | local_ref: usize, |
| 24 | /// A constant instruction, to be rendered inline. |
| 25 | constant: *Inst, |
| 26 | /// Index into the parameters |
| 27 | arg: usize, |
| 28 | /// By-value |
| 29 | decl: *Decl, |
| 23 | 30 | |
| 24 | | const Mutability = enum { Const, Mut }; |
| 31 | pub fn printed(value: CValue, object: *Object) Printed { |
| 32 | return .{ |
| 33 | .value = value, |
| 34 | .object = object, |
| 35 | }; |
| 36 | } |
| 37 | |
| 38 | pub const Printed = struct { |
| 39 | value: CValue, |
| 40 | object: *Object, |
| 41 | |
| 42 | /// TODO this got unwieldly, I want to remove the ability to print this way |
| 43 | pub fn format( |
| 44 | self: Printed, |
| 45 | comptime fmt: []const u8, |
| 46 | options: std.fmt.FormatOptions, |
| 47 | writer: anytype, |
| 48 | ) error{OutOfMemory}!void { |
| 49 | if (fmt.len != 0) @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 50 | switch (self.value) { |
| 51 | .none => unreachable, |
| 52 | .local => |i| return std.fmt.format(writer, "t{d}", .{i}), |
| 53 | .local_ref => |i| return std.fmt.format(writer, "&t{d}", .{i}), |
| 54 | .constant => |inst| { |
| 55 | const o = self.object; |
| 56 | o.dg.renderValue(writer, inst.ty, inst.value().?) catch |err| switch (err) { |
| 57 | error.OutOfMemory => return error.OutOfMemory, |
| 58 | error.AnalysisFail => return, |
| 59 | }; |
| 60 | }, |
| 61 | .arg => |i| return std.fmt.format(writer, "a{d}", .{i}), |
| 62 | .decl => |decl| return writer.writeAll(mem.span(decl.name)), |
| 63 | } |
| 64 | } |
| 65 | }; |
| 66 | }; |
| 25 | 67 | |
| 26 | | fn renderTypeAndName( |
| 27 | | ctx: *Context, |
| 28 | | writer: Writer, |
| 29 | | ty: Type, |
| 30 | | name: []const u8, |
| 31 | | mutability: Mutability, |
| 32 | | ) error{ OutOfMemory, AnalysisFail }!void { |
| 33 | | var suffix = std.ArrayList(u8).init(&ctx.arena.allocator); |
| 34 | | |
| 35 | | var render_ty = ty; |
| 36 | | while (render_ty.zigTypeTag() == .Array) { |
| 37 | | const sentinel_bit = @boolToInt(render_ty.sentinel() != null); |
| 38 | | const c_len = render_ty.arrayLen() + sentinel_bit; |
| 39 | | try suffix.writer().print("[{d}]", .{c_len}); |
| 40 | | render_ty = render_ty.elemType(); |
| 68 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); |
| 69 | |
| 70 | /// This data is available when outputting .c code for a Module. |
| 71 | /// It is not available when generating .h file. |
| 72 | pub const Object = struct { |
| 73 | dg: DeclGen, |
| 74 | gpa: *mem.Allocator, |
| 75 | code: std.ArrayList(u8), |
| 76 | value_map: CValueMap, |
| 77 | next_arg_index: usize = 0, |
| 78 | next_local_index: usize = 0, |
| 79 | |
| 80 | fn resolveInst(o: *Object, inst: *Inst) !CValue { |
| 81 | if (inst.value()) |_| { |
| 82 | return CValue{ .constant = inst }; |
| 83 | } |
| 84 | return o.value_map.get(inst).?; // Instruction does not dominate all uses! |
| 41 | 85 | } |
| 42 | 86 | |
| 43 | | try renderType(ctx, writer, render_ty); |
| 87 | fn allocLocalValue(o: *Object) CValue { |
| 88 | const result = o.next_local_index; |
| 89 | o.next_local_index += 1; |
| 90 | return .{ .local = result }; |
| 91 | } |
| 44 | 92 | |
| 45 | | const const_prefix = switch (mutability) { |
| 46 | | .Const => "const ", |
| 47 | | .Mut => "", |
| 48 | | }; |
| 49 | | try writer.print(" {s}{s}{s}", .{ const_prefix, name, suffix.items }); |
| 50 | | } |
| 93 | fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue { |
| 94 | const local_value = o.allocLocalValue(); |
| 95 | try o.renderTypeAndName(o.code.writer(), ty, local_value, mutability); |
| 96 | return local_value; |
| 97 | } |
| 51 | 98 | |
| 52 | | fn renderType( |
| 53 | | ctx: *Context, |
| 54 | | writer: Writer, |
| 55 | | t: Type, |
| 56 | | ) error{ OutOfMemory, AnalysisFail }!void { |
| 57 | | switch (t.zigTypeTag()) { |
| 58 | | .NoReturn => { |
| 59 | | try writer.writeAll("zig_noreturn void"); |
| 60 | | }, |
| 61 | | .Void => try writer.writeAll("void"), |
| 62 | | .Bool => try writer.writeAll("bool"), |
| 63 | | .Int => { |
| 64 | | switch (t.tag()) { |
| 65 | | .u8 => try writer.writeAll("uint8_t"), |
| 66 | | .i8 => try writer.writeAll("int8_t"), |
| 67 | | .u16 => try writer.writeAll("uint16_t"), |
| 68 | | .i16 => try writer.writeAll("int16_t"), |
| 69 | | .u32 => try writer.writeAll("uint32_t"), |
| 70 | | .i32 => try writer.writeAll("int32_t"), |
| 71 | | .u64 => try writer.writeAll("uint64_t"), |
| 72 | | .i64 => try writer.writeAll("int64_t"), |
| 73 | | .usize => try writer.writeAll("uintptr_t"), |
| 74 | | .isize => try writer.writeAll("intptr_t"), |
| 75 | | .c_short => try writer.writeAll("short"), |
| 76 | | .c_ushort => try writer.writeAll("unsigned short"), |
| 77 | | .c_int => try writer.writeAll("int"), |
| 78 | | .c_uint => try writer.writeAll("unsigned int"), |
| 79 | | .c_long => try writer.writeAll("long"), |
| 80 | | .c_ulong => try writer.writeAll("unsigned long"), |
| 81 | | .c_longlong => try writer.writeAll("long long"), |
| 82 | | .c_ulonglong => try writer.writeAll("unsigned long long"), |
| 83 | | .int_signed, .int_unsigned => { |
| 84 | | const info = t.intInfo(ctx.target); |
| 85 | | const sign_prefix = switch (info.signedness) { |
| 86 | | .signed => "i", |
| 87 | | .unsigned => "", |
| 88 | | }; |
| 89 | | inline for (.{ 8, 16, 32, 64, 128 }) |nbits| { |
| 90 | | if (info.bits <= nbits) { |
| 91 | | try writer.print("{s}int{d}_t", .{ sign_prefix, nbits }); |
| 92 | | break; |
| 93 | | } |
| 99 | fn indent(o: *Object) !void { |
| 100 | const indent_size = 4; |
| 101 | const indent_level = 1; |
| 102 | const indent_amt = indent_size * indent_level; |
| 103 | try o.code.writer().writeByteNTimes(' ', indent_amt); |
| 104 | } |
| 105 | |
| 106 | fn renderTypeAndName( |
| 107 | o: *Object, |
| 108 | writer: Writer, |
| 109 | ty: Type, |
| 110 | name: CValue, |
| 111 | mutability: Mutability, |
| 112 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 113 | var suffix = std.ArrayList(u8).init(o.gpa); |
| 114 | defer suffix.deinit(); |
| 115 | |
| 116 | var render_ty = ty; |
| 117 | while (render_ty.zigTypeTag() == .Array) { |
| 118 | const sentinel_bit = @boolToInt(render_ty.sentinel() != null); |
| 119 | const c_len = render_ty.arrayLen() + sentinel_bit; |
| 120 | try suffix.writer().print("[{d}]", .{c_len}); |
| 121 | render_ty = render_ty.elemType(); |
| 122 | } |
| 123 | |
| 124 | try o.dg.renderType(writer, render_ty); |
| 125 | |
| 126 | const const_prefix = switch (mutability) { |
| 127 | .Const => "const ", |
| 128 | .Mut => "", |
| 129 | }; |
| 130 | try writer.print(" {s}{}{s}", .{ const_prefix, name.printed(o), suffix.items }); |
| 131 | } |
| 132 | }; |
| 133 | |
| 134 | /// This data is available both when outputting .c code and when outputting an .h file. |
| 135 | const DeclGen = struct { |
| 136 | module: *Module, |
| 137 | decl: *Decl, |
| 138 | fwd_decl: std.ArrayList(u8), |
| 139 | error_msg: ?*Compilation.ErrorMsg, |
| 140 | |
| 141 | fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 142 | dg.error_msg = try Compilation.ErrorMsg.create(dg.module.gpa, src, format, args); |
| 143 | return error.AnalysisFail; |
| 144 | } |
| 145 | |
| 146 | fn renderValue( |
| 147 | dg: *DeclGen, |
| 148 | writer: Writer, |
| 149 | t: Type, |
| 150 | val: Value, |
| 151 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 152 | switch (t.zigTypeTag()) { |
| 153 | .Int => { |
| 154 | if (t.isSignedInt()) |
| 155 | return writer.print("{d}", .{val.toSignedInt()}); |
| 156 | return writer.print("{d}", .{val.toUnsignedInt()}); |
| 157 | }, |
| 158 | .Pointer => switch (val.tag()) { |
| 159 | .undef, .zero => try writer.writeAll("0"), |
| 160 | .one => try writer.writeAll("1"), |
| 161 | .decl_ref => { |
| 162 | const decl = val.castTag(.decl_ref).?.data; |
| 163 | |
| 164 | // Determine if we must pointer cast. |
| 165 | const decl_tv = decl.typed_value.most_recent.typed_value; |
| 166 | if (t.eql(decl_tv.ty)) { |
| 167 | try writer.print("&{s}", .{decl.name}); |
| 94 | 168 | } else { |
| 95 | | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 169 | try writer.writeAll("("); |
| 170 | try dg.renderType(writer, t); |
| 171 | try writer.print(")&{s}", .{decl.name}); |
| 96 | 172 | } |
| 97 | 173 | }, |
| 174 | .function => { |
| 175 | const func = val.castTag(.function).?.data; |
| 176 | try writer.print("{s}", .{func.owner_decl.name}); |
| 177 | }, |
| 178 | .extern_fn => { |
| 179 | const decl = val.castTag(.extern_fn).?.data; |
| 180 | try writer.print("{s}", .{decl.name}); |
| 181 | }, |
| 182 | else => |e| return dg.fail( |
| 183 | dg.decl.src(), |
| 184 | "TODO: C backend: implement Pointer value {s}", |
| 185 | .{@tagName(e)}, |
| 186 | ), |
| 187 | }, |
| 188 | .Array => { |
| 189 | // First try specific tag representations for more efficiency. |
| 190 | switch (val.tag()) { |
| 191 | .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"), |
| 192 | .bytes => { |
| 193 | const bytes = val.castTag(.bytes).?.data; |
| 194 | // TODO: make our own C string escape instead of using {Z} |
| 195 | try writer.print("\"{Z}\"", .{bytes}); |
| 196 | }, |
| 197 | else => { |
| 198 | // Fall back to generic implementation. |
| 199 | var arena = std.heap.ArenaAllocator.init(dg.module.gpa); |
| 200 | defer arena.deinit(); |
| 201 | |
| 202 | try writer.writeAll("{"); |
| 203 | var index: usize = 0; |
| 204 | const len = t.arrayLen(); |
| 205 | const elem_ty = t.elemType(); |
| 206 | while (index < len) : (index += 1) { |
| 207 | if (index != 0) try writer.writeAll(","); |
| 208 | const elem_val = try val.elemValue(&arena.allocator, index); |
| 209 | try dg.renderValue(writer, elem_ty, elem_val); |
| 210 | } |
| 211 | if (t.sentinel()) |sentinel_val| { |
| 212 | if (index != 0) try writer.writeAll(","); |
| 213 | try dg.renderValue(writer, elem_ty, sentinel_val); |
| 214 | } |
| 215 | try writer.writeAll("}"); |
| 216 | }, |
| 217 | } |
| 218 | }, |
| 219 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ |
| 220 | @tagName(e), |
| 221 | }), |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | fn renderFunctionSignature(dg: *DeclGen, w: Writer) !void { |
| 226 | const tv = dg.decl.typed_value.most_recent.typed_value; |
| 227 | // Determine whether the function is globally visible. |
| 228 | const is_global = blk: { |
| 229 | switch (tv.val.tag()) { |
| 230 | .extern_fn => break :blk true, |
| 231 | .function => { |
| 232 | const func = tv.val.castTag(.function).?.data; |
| 233 | break :blk dg.module.decl_exports.contains(func.owner_decl); |
| 234 | }, |
| 98 | 235 | else => unreachable, |
| 99 | 236 | } |
| 100 | | }, |
| 101 | | .Pointer => { |
| 102 | | if (t.isSlice()) { |
| 103 | | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{}); |
| 104 | | } else { |
| 105 | | try renderType(ctx, writer, t.elemType()); |
| 106 | | try writer.writeAll(" *"); |
| 107 | | if (t.isConstPtr()) { |
| 108 | | try writer.writeAll("const "); |
| 109 | | } |
| 110 | | if (t.isVolatilePtr()) { |
| 111 | | try writer.writeAll("volatile "); |
| 237 | }; |
| 238 | if (!is_global) { |
| 239 | try w.writeAll("static "); |
| 240 | } |
| 241 | try dg.renderType(w, tv.ty.fnReturnType()); |
| 242 | const decl_name = mem.span(dg.decl.name); |
| 243 | try w.print(" {s}(", .{decl_name}); |
| 244 | var param_len = tv.ty.fnParamLen(); |
| 245 | if (param_len == 0) |
| 246 | try w.writeAll("void") |
| 247 | else { |
| 248 | var index: usize = 0; |
| 249 | while (index < param_len) : (index += 1) { |
| 250 | if (index > 0) { |
| 251 | try w.writeAll(", "); |
| 112 | 252 | } |
| 253 | try dg.renderType(w, tv.ty.fnParamType(index)); |
| 254 | try w.print(" a{d}", .{index}); |
| 113 | 255 | } |
| 114 | | }, |
| 115 | | .Array => { |
| 116 | | try renderType(ctx, writer, t.elemType()); |
| 117 | | try writer.writeAll(" *"); |
| 118 | | }, |
| 119 | | else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement type {s}", .{ |
| 120 | | @tagName(e), |
| 121 | | }), |
| 256 | } |
| 257 | try w.writeByte(')'); |
| 122 | 258 | } |
| 123 | | } |
| 124 | 259 | |
| 125 | | fn renderValue( |
| 126 | | ctx: *Context, |
| 127 | | writer: Writer, |
| 128 | | t: Type, |
| 129 | | val: Value, |
| 130 | | ) error{ OutOfMemory, AnalysisFail }!void { |
| 131 | | switch (t.zigTypeTag()) { |
| 132 | | .Int => { |
| 133 | | if (t.isSignedInt()) |
| 134 | | return writer.print("{d}", .{val.toSignedInt()}); |
| 135 | | return writer.print("{d}", .{val.toUnsignedInt()}); |
| 136 | | }, |
| 137 | | .Pointer => switch (val.tag()) { |
| 138 | | .undef, .zero => try writer.writeAll("0"), |
| 139 | | .one => try writer.writeAll("1"), |
| 140 | | .decl_ref => { |
| 141 | | const decl = val.castTag(.decl_ref).?.data; |
| 142 | | |
| 143 | | // Determine if we must pointer cast. |
| 144 | | const decl_tv = decl.typed_value.most_recent.typed_value; |
| 145 | | if (t.eql(decl_tv.ty)) { |
| 146 | | try writer.print("&{s}", .{decl.name}); |
| 147 | | } else { |
| 148 | | try writer.writeAll("("); |
| 149 | | try renderType(ctx, writer, t); |
| 150 | | try writer.print(")&{s}", .{decl.name}); |
| 151 | | } |
| 260 | fn renderType(dg: *DeclGen, w: Writer, t: Type) error{ OutOfMemory, AnalysisFail }!void { |
| 261 | switch (t.zigTypeTag()) { |
| 262 | .NoReturn => { |
| 263 | try w.writeAll("zig_noreturn void"); |
| 152 | 264 | }, |
| 153 | | .function => { |
| 154 | | const func = val.castTag(.function).?.data; |
| 155 | | try writer.print("{s}", .{func.owner_decl.name}); |
| 156 | | }, |
| 157 | | .extern_fn => { |
| 158 | | const decl = val.castTag(.extern_fn).?.data; |
| 159 | | try writer.print("{s}", .{decl.name}); |
| 265 | .Void => try w.writeAll("void"), |
| 266 | .Bool => try w.writeAll("bool"), |
| 267 | .Int => { |
| 268 | switch (t.tag()) { |
| 269 | .u8 => try w.writeAll("uint8_t"), |
| 270 | .i8 => try w.writeAll("int8_t"), |
| 271 | .u16 => try w.writeAll("uint16_t"), |
| 272 | .i16 => try w.writeAll("int16_t"), |
| 273 | .u32 => try w.writeAll("uint32_t"), |
| 274 | .i32 => try w.writeAll("int32_t"), |
| 275 | .u64 => try w.writeAll("uint64_t"), |
| 276 | .i64 => try w.writeAll("int64_t"), |
| 277 | .usize => try w.writeAll("uintptr_t"), |
| 278 | .isize => try w.writeAll("intptr_t"), |
| 279 | .c_short => try w.writeAll("short"), |
| 280 | .c_ushort => try w.writeAll("unsigned short"), |
| 281 | .c_int => try w.writeAll("int"), |
| 282 | .c_uint => try w.writeAll("unsigned int"), |
| 283 | .c_long => try w.writeAll("long"), |
| 284 | .c_ulong => try w.writeAll("unsigned long"), |
| 285 | .c_longlong => try w.writeAll("long long"), |
| 286 | .c_ulonglong => try w.writeAll("unsigned long long"), |
| 287 | .int_signed, .int_unsigned => { |
| 288 | const info = t.intInfo(dg.module.getTarget()); |
| 289 | const sign_prefix = switch (info.signedness) { |
| 290 | .signed => "i", |
| 291 | .unsigned => "", |
| 292 | }; |
| 293 | inline for (.{ 8, 16, 32, 64, 128 }) |nbits| { |
| 294 | if (info.bits <= nbits) { |
| 295 | try w.print("{s}int{d}_t", .{ sign_prefix, nbits }); |
| 296 | break; |
| 297 | } |
| 298 | } else { |
| 299 | return dg.fail(dg.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 300 | } |
| 301 | }, |
| 302 | else => unreachable, |
| 303 | } |
| 160 | 304 | }, |
| 161 | | else => |e| return ctx.fail( |
| 162 | | ctx.decl.src(), |
| 163 | | "TODO: C backend: implement Pointer value {s}", |
| 164 | | .{@tagName(e)}, |
| 165 | | ), |
| 166 | | }, |
| 167 | | .Array => { |
| 168 | | // First try specific tag representations for more efficiency. |
| 169 | | switch (val.tag()) { |
| 170 | | .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"), |
| 171 | | .bytes => { |
| 172 | | const bytes = val.castTag(.bytes).?.data; |
| 173 | | // TODO: make our own C string escape instead of using {Z} |
| 174 | | try writer.print("\"{Z}\"", .{bytes}); |
| 175 | | }, |
| 176 | | else => { |
| 177 | | // Fall back to generic implementation. |
| 178 | | try writer.writeAll("{"); |
| 179 | | var index: usize = 0; |
| 180 | | const len = t.arrayLen(); |
| 181 | | const elem_ty = t.elemType(); |
| 182 | | while (index < len) : (index += 1) { |
| 183 | | if (index != 0) try writer.writeAll(","); |
| 184 | | const elem_val = try val.elemValue(&ctx.arena.allocator, index); |
| 185 | | try renderValue(ctx, writer, elem_ty, elem_val); |
| 305 | .Pointer => { |
| 306 | if (t.isSlice()) { |
| 307 | return dg.fail(dg.decl.src(), "TODO: C backend: implement slices", .{}); |
| 308 | } else { |
| 309 | try dg.renderType(w, t.elemType()); |
| 310 | try w.writeAll(" *"); |
| 311 | if (t.isConstPtr()) { |
| 312 | try w.writeAll("const "); |
| 186 | 313 | } |
| 187 | | if (t.sentinel()) |sentinel_val| { |
| 188 | | if (index != 0) try writer.writeAll(","); |
| 189 | | try renderValue(ctx, writer, elem_ty, sentinel_val); |
| 314 | if (t.isVolatilePtr()) { |
| 315 | try w.writeAll("volatile "); |
| 190 | 316 | } |
| 191 | | try writer.writeAll("}"); |
| 192 | | }, |
| 193 | | } |
| 194 | | }, |
| 195 | | else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{ |
| 196 | | @tagName(e), |
| 197 | | }), |
| 198 | | } |
| 199 | | } |
| 200 | | |
| 201 | | fn renderFunctionSignature( |
| 202 | | ctx: *Context, |
| 203 | | writer: Writer, |
| 204 | | decl: *Decl, |
| 205 | | ) !void { |
| 206 | | const tv = decl.typed_value.most_recent.typed_value; |
| 207 | | // Determine whether the function is globally visible. |
| 208 | | const is_global = blk: { |
| 209 | | switch (tv.val.tag()) { |
| 210 | | .extern_fn => break :blk true, |
| 211 | | .function => { |
| 212 | | const func = tv.val.castTag(.function).?.data; |
| 213 | | break :blk ctx.module.decl_exports.contains(func.owner_decl); |
| 317 | } |
| 214 | 318 | }, |
| 215 | | else => unreachable, |
| 216 | | } |
| 217 | | }; |
| 218 | | if (!is_global) { |
| 219 | | try writer.writeAll("static "); |
| 220 | | } |
| 221 | | try renderType(ctx, writer, tv.ty.fnReturnType()); |
| 222 | | // Use the child allocator directly, as we know the name can be freed before |
| 223 | | // the rest of the arena. |
| 224 | | const decl_name = mem.span(decl.name); |
| 225 | | const name = try map(ctx.arena.child_allocator, decl_name); |
| 226 | | defer ctx.arena.child_allocator.free(name); |
| 227 | | try writer.print(" {s}(", .{name}); |
| 228 | | var param_len = tv.ty.fnParamLen(); |
| 229 | | if (param_len == 0) |
| 230 | | try writer.writeAll("void") |
| 231 | | else { |
| 232 | | var index: usize = 0; |
| 233 | | while (index < param_len) : (index += 1) { |
| 234 | | if (index > 0) { |
| 235 | | try writer.writeAll(", "); |
| 236 | | } |
| 237 | | try renderType(ctx, writer, tv.ty.fnParamType(index)); |
| 238 | | try writer.print(" arg{d}", .{index}); |
| 319 | .Array => { |
| 320 | try dg.renderType(w, t.elemType()); |
| 321 | try w.writeAll(" *"); |
| 322 | }, |
| 323 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ |
| 324 | @tagName(e), |
| 325 | }), |
| 239 | 326 | } |
| 240 | 327 | } |
| 241 | | try writer.writeByte(')'); |
| 242 | | } |
| 328 | }; |
| 243 | 329 | |
| 244 | | fn indent(file: *C) !void { |
| 245 | | const indent_size = 4; |
| 246 | | const indent_level = 1; |
| 247 | | const indent_amt = indent_size * indent_level; |
| 248 | | try file.main.writer().writeByteNTimes(' ', indent_amt); |
| 249 | | } |
| 330 | pub fn genDecl(o: *Object) !void { |
| 331 | const tracy = trace(@src()); |
| 332 | defer tracy.end(); |
| 250 | 333 | |
| 251 | | pub fn generate(file: *C, module: *Module, decl: *Decl) !void { |
| 252 | | const tv = decl.typed_value.most_recent.typed_value; |
| 253 | | |
| 254 | | var arena = std.heap.ArenaAllocator.init(file.base.allocator); |
| 255 | | defer arena.deinit(); |
| 256 | | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); |
| 257 | | defer inst_map.deinit(); |
| 258 | | var ctx = Context{ |
| 259 | | .decl = decl, |
| 260 | | .arena = &arena, |
| 261 | | .inst_map = &inst_map, |
| 262 | | .target = file.base.options.target, |
| 263 | | .header = &file.header, |
| 264 | | .module = module, |
| 265 | | }; |
| 266 | | defer { |
| 267 | | file.error_msg = ctx.error_msg; |
| 268 | | ctx.deinit(); |
| 269 | | } |
| 334 | const tv = o.dg.decl.typed_value.most_recent.typed_value; |
| 270 | 335 | |
| 271 | 336 | if (tv.val.castTag(.function)) |func_payload| { |
| 272 | | const writer = file.main.writer(); |
| 273 | | try renderFunctionSignature(&ctx, writer, decl); |
| 274 | | |
| 275 | | try writer.writeAll(" {"); |
| 337 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 338 | try o.dg.renderFunctionSignature(fwd_decl_writer); |
| 339 | try fwd_decl_writer.writeAll(";\n"); |
| 276 | 340 | |
| 277 | 341 | const func: *Module.Fn = func_payload.data; |
| 278 | 342 | const instructions = func.body.instructions; |
| 279 | | if (instructions.len > 0) { |
| 280 | | try writer.writeAll("\n"); |
| 281 | | for (instructions) |inst| { |
| 282 | | if (switch (inst.tag) { |
| 283 | | .add => try genBinOp(&ctx, file, inst.castTag(.add).?, "+"), |
| 284 | | .alloc => try genAlloc(&ctx, file, inst.castTag(.alloc).?), |
| 285 | | .arg => try genArg(&ctx), |
| 286 | | .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?), |
| 287 | | .block => try genBlock(&ctx, file, inst.castTag(.block).?), |
| 288 | | .bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?), |
| 289 | | .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?), |
| 290 | | .call => try genCall(&ctx, file, inst.castTag(.call).?), |
| 291 | | .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="), |
| 292 | | .cmp_gt => try genBinOp(&ctx, file, inst.castTag(.cmp_gt).?, ">"), |
| 293 | | .cmp_gte => try genBinOp(&ctx, file, inst.castTag(.cmp_gte).?, ">="), |
| 294 | | .cmp_lt => try genBinOp(&ctx, file, inst.castTag(.cmp_lt).?, "<"), |
| 295 | | .cmp_lte => try genBinOp(&ctx, file, inst.castTag(.cmp_lte).?, "<="), |
| 296 | | .cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="), |
| 297 | | .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), |
| 298 | | .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?), |
| 299 | | .load => try genLoad(&ctx, file, inst.castTag(.load).?), |
| 300 | | .ret => try genRet(&ctx, file, inst.castTag(.ret).?), |
| 301 | | .retvoid => try genRetVoid(file), |
| 302 | | .store => try genStore(&ctx, file, inst.castTag(.store).?), |
| 303 | | .sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"), |
| 304 | | .unreach => try genUnreach(file, inst.castTag(.unreach).?), |
| 305 | | else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}), |
| 306 | | }) |name| { |
| 307 | | try ctx.inst_map.putNoClobber(inst, name); |
| 308 | | } |
| 343 | const writer = o.code.writer(); |
| 344 | try o.dg.renderFunctionSignature(writer); |
| 345 | if (instructions.len == 0) { |
| 346 | try writer.writeAll(" {}\n\n"); |
| 347 | return; |
| 348 | } |
| 349 | |
| 350 | try writer.writeAll(" {"); |
| 351 | |
| 352 | try writer.writeAll("\n"); |
| 353 | for (instructions) |inst| { |
| 354 | const result_value = switch (inst.tag) { |
| 355 | .add => try genBinOp(o, inst.castTag(.add).?, "+"), |
| 356 | .alloc => try genAlloc(o, inst.castTag(.alloc).?), |
| 357 | .arg => genArg(o), |
| 358 | .assembly => try genAsm(o, inst.castTag(.assembly).?), |
| 359 | .block => try genBlock(o, inst.castTag(.block).?), |
| 360 | .bitcast => try genBitcast(o, inst.castTag(.bitcast).?), |
| 361 | .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?), |
| 362 | .call => try genCall(o, inst.castTag(.call).?), |
| 363 | .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, "=="), |
| 364 | .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, ">"), |
| 365 | .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, ">="), |
| 366 | .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, "<"), |
| 367 | .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, "<="), |
| 368 | .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, "!="), |
| 369 | .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?), |
| 370 | .intcast => try genIntCast(o, inst.castTag(.intcast).?), |
| 371 | .load => try genLoad(o, inst.castTag(.load).?), |
| 372 | .ret => try genRet(o, inst.castTag(.ret).?), |
| 373 | .retvoid => try genRetVoid(o), |
| 374 | .store => try genStore(o, inst.castTag(.store).?), |
| 375 | .sub => try genBinOp(o, inst.castTag(.sub).?, "-"), |
| 376 | .unreach => try genUnreach(o, inst.castTag(.unreach).?), |
| 377 | else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), |
| 378 | }; |
| 379 | switch (result_value) { |
| 380 | .none => {}, |
| 381 | else => try o.value_map.putNoClobber(inst, result_value), |
| 309 | 382 | } |
| 310 | 383 | } |
| 311 | 384 | |
| 312 | 385 | try writer.writeAll("}\n\n"); |
| 313 | 386 | } else if (tv.val.tag() == .extern_fn) { |
| 314 | | return; // handled when referenced |
| 387 | const writer = o.code.writer(); |
| 388 | try o.dg.renderFunctionSignature(writer); |
| 389 | try writer.writeAll(";\n"); |
| 315 | 390 | } else { |
| 316 | | const writer = file.constants.writer(); |
| 391 | const writer = o.code.writer(); |
| 317 | 392 | try writer.writeAll("static "); |
| 318 | 393 | |
| 319 | 394 | // TODO ask the Decl if it is const |
| 320 | 395 | // https://github.com/ziglang/zig/issues/7582 |
| 321 | 396 | |
| 322 | | try renderTypeAndName(&ctx, writer, tv.ty, mem.span(decl.name), .Mut); |
| 397 | const decl_c_value: CValue = .{ .decl = o.dg.decl }; |
| 398 | try o.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut); |
| 323 | 399 | |
| 324 | 400 | try writer.writeAll(" = "); |
| 325 | | try renderValue(&ctx, writer, tv.ty, tv.val); |
| 401 | try o.dg.renderValue(writer, tv.ty, tv.val); |
| 326 | 402 | try writer.writeAll(";\n"); |
| 327 | 403 | } |
| 328 | 404 | } |
| 329 | 405 | |
| 330 | | pub fn generateHeader( |
| 331 | | comp: *Compilation, |
| 332 | | module: *Module, |
| 333 | | header: *C.Header, |
| 334 | | decl: *Decl, |
| 335 | | ) error{ AnalysisFail, OutOfMemory }!void { |
| 406 | pub fn genHeader(comp: *Compilation, dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 407 | const tracy = trace(@src()); |
| 408 | defer tracy.end(); |
| 409 | |
| 336 | 410 | switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { |
| 337 | 411 | .Fn => { |
| 338 | | var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa); |
| 339 | | defer inst_map.deinit(); |
| 340 | | |
| 341 | | var arena = std.heap.ArenaAllocator.init(comp.gpa); |
| 342 | | defer arena.deinit(); |
| 343 | | |
| 344 | | var ctx = Context{ |
| 345 | | .decl = decl, |
| 346 | | .arena = &arena, |
| 347 | | .inst_map = &inst_map, |
| 348 | | .target = comp.getTarget(), |
| 349 | | .header = header, |
| 350 | | .module = module, |
| 351 | | }; |
| 352 | | const writer = header.buf.writer(); |
| 353 | | renderFunctionSignature(&ctx, writer, decl) catch |err| { |
| 354 | | if (err == error.AnalysisFail) { |
| 355 | | try module.failed_decls.put(module.gpa, decl, ctx.error_msg); |
| 356 | | } |
| 357 | | return err; |
| 412 | dg.renderFunctionSignature() catch |err| switch (err) { |
| 413 | error.AnalysisFail => { |
| 414 | try dg.module.failed_decls.put(dg.module.gpa, decl, dg.error_msg.?); |
| 415 | dg.error_msg = null; |
| 416 | return error.AnalysisFail; |
| 417 | }, |
| 418 | else => |e| return e, |
| 358 | 419 | }; |
| 359 | | try writer.writeAll(";\n"); |
| 420 | try dg.fwd_decl.appendSlice(";\n"); |
| 360 | 421 | }, |
| 361 | 422 | else => {}, |
| 362 | 423 | } |
| 363 | 424 | } |
| 364 | 425 | |
| 365 | | const Context = struct { |
| 366 | | decl: *Decl, |
| 367 | | inst_map: *std.AutoHashMap(*Inst, []u8), |
| 368 | | arena: *std.heap.ArenaAllocator, |
| 369 | | argdex: usize = 0, |
| 370 | | unnamed_index: usize = 0, |
| 371 | | error_msg: *Compilation.ErrorMsg = undefined, |
| 372 | | target: std.Target, |
| 373 | | header: *C.Header, |
| 374 | | module: *Module, |
| 375 | | |
| 376 | | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { |
| 377 | | if (inst.value()) |val| { |
| 378 | | var out = std.ArrayList(u8).init(&self.arena.allocator); |
| 379 | | try renderValue(self, out.writer(), inst.ty, val); |
| 380 | | return out.toOwnedSlice(); |
| 381 | | } |
| 382 | | return self.inst_map.get(inst).?; // Instruction does not dominate all uses! |
| 383 | | } |
| 384 | | |
| 385 | | fn name(self: *Context) ![]u8 { |
| 386 | | const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{d}", .{self.unnamed_index}); |
| 387 | | self.unnamed_index += 1; |
| 388 | | return val; |
| 389 | | } |
| 390 | | |
| 391 | | fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 392 | | self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args); |
| 393 | | return error.AnalysisFail; |
| 394 | | } |
| 395 | | |
| 396 | | fn deinit(self: *Context) void { |
| 397 | | self.* = undefined; |
| 398 | | } |
| 399 | | }; |
| 400 | | |
| 401 | | fn genAlloc(ctx: *Context, file: *C, alloc: *Inst.NoOp) !?[]u8 { |
| 402 | | const writer = file.main.writer(); |
| 426 | fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue { |
| 427 | const writer = o.code.writer(); |
| 403 | 428 | |
| 404 | 429 | // First line: the variable used as data storage. |
| 405 | | try indent(file); |
| 406 | | const local_name = try ctx.name(); |
| 430 | try o.indent(); |
| 407 | 431 | const elem_type = alloc.base.ty.elemType(); |
| 408 | 432 | const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut; |
| 409 | | try renderTypeAndName(ctx, writer, elem_type, local_name, mutability); |
| 433 | const local = try o.allocLocal(elem_type, mutability); |
| 410 | 434 | try writer.writeAll(";\n"); |
| 411 | 435 | |
| 412 | | // Second line: a pointer to it so that we can refer to it as the allocation. |
| 413 | | // One line for the variable, one line for the pointer to the variable, which we return. |
| 414 | | try indent(file); |
| 415 | | const ptr_local_name = try ctx.name(); |
| 416 | | try renderTypeAndName(ctx, writer, alloc.base.ty, ptr_local_name, .Const); |
| 417 | | try writer.print(" = &{s};\n", .{local_name}); |
| 418 | | |
| 419 | | return ptr_local_name; |
| 436 | return CValue{ .local_ref = local.local }; |
| 420 | 437 | } |
| 421 | 438 | |
| 422 | | fn genArg(ctx: *Context) !?[]u8 { |
| 423 | | const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex}); |
| 424 | | ctx.argdex += 1; |
| 425 | | return name; |
| 439 | fn genArg(o: *Object) CValue { |
| 440 | const i = o.next_arg_index; |
| 441 | o.next_arg_index += 1; |
| 442 | return .{ .arg = i }; |
| 426 | 443 | } |
| 427 | 444 | |
| 428 | | fn genRetVoid(file: *C) !?[]u8 { |
| 429 | | try indent(file); |
| 430 | | try file.main.writer().print("return;\n", .{}); |
| 431 | | return null; |
| 445 | fn genRetVoid(o: *Object) !CValue { |
| 446 | try o.indent(); |
| 447 | try o.code.writer().print("return;\n", .{}); |
| 448 | return CValue.none; |
| 432 | 449 | } |
| 433 | 450 | |
| 434 | | fn genLoad(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 435 | | const operand = try ctx.resolveInst(inst.operand); |
| 436 | | const writer = file.main.writer(); |
| 437 | | try indent(file); |
| 438 | | const local_name = try ctx.name(); |
| 439 | | try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const); |
| 440 | | try writer.print(" = *{s};\n", .{operand}); |
| 441 | | return local_name; |
| 451 | fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { |
| 452 | const operand = try o.resolveInst(inst.operand); |
| 453 | const writer = o.code.writer(); |
| 454 | try o.indent(); |
| 455 | const local = try o.allocLocal(inst.base.ty, .Const); |
| 456 | switch (operand) { |
| 457 | .local_ref => |i| { |
| 458 | const wrapped: CValue = .{ .local = i }; |
| 459 | try writer.print(" = {};\n", .{wrapped.printed(o)}); |
| 460 | }, |
| 461 | else => { |
| 462 | try writer.print(" = *{};\n", .{operand.printed(o)}); |
| 463 | }, |
| 464 | } |
| 465 | return local; |
| 442 | 466 | } |
| 443 | 467 | |
| 444 | | fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 445 | | try indent(file); |
| 446 | | const writer = file.main.writer(); |
| 447 | | try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)}); |
| 448 | | return null; |
| 468 | fn genRet(o: *Object, inst: *Inst.UnOp) !CValue { |
| 469 | const operand = try o.resolveInst(inst.operand); |
| 470 | try o.indent(); |
| 471 | try o.code.writer().print("return {};\n", .{operand.printed(o)}); |
| 472 | return CValue.none; |
| 449 | 473 | } |
| 450 | 474 | |
| 451 | | fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 475 | fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue { |
| 452 | 476 | if (inst.base.isUnused()) |
| 453 | | return null; |
| 454 | | try indent(file); |
| 455 | | const writer = file.main.writer(); |
| 456 | | const name = try ctx.name(); |
| 457 | | const from = try ctx.resolveInst(inst.operand); |
| 477 | return CValue.none; |
| 458 | 478 | |
| 459 | | try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const); |
| 479 | const from = try o.resolveInst(inst.operand); |
| 480 | |
| 481 | try o.indent(); |
| 482 | const writer = o.code.writer(); |
| 483 | const local = try o.allocLocal(inst.base.ty, .Const); |
| 460 | 484 | try writer.writeAll(" = ("); |
| 461 | | try renderType(ctx, writer, inst.base.ty); |
| 462 | | try writer.print("){s};\n", .{from}); |
| 463 | | return name; |
| 485 | try o.dg.renderType(writer, inst.base.ty); |
| 486 | try writer.print("){};\n", .{from.printed(o)}); |
| 487 | return local; |
| 464 | 488 | } |
| 465 | 489 | |
| 466 | | fn genStore(ctx: *Context, file: *C, inst: *Inst.BinOp) !?[]u8 { |
| 490 | fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { |
| 467 | 491 | // *a = b; |
| 468 | | try indent(file); |
| 469 | | const writer = file.main.writer(); |
| 470 | | const dest_ptr_name = try ctx.resolveInst(inst.lhs); |
| 471 | | const src_val_name = try ctx.resolveInst(inst.rhs); |
| 472 | | try writer.print("*{s} = {s};\n", .{ dest_ptr_name, src_val_name }); |
| 473 | | return null; |
| 492 | const dest_ptr = try o.resolveInst(inst.lhs); |
| 493 | const src_val = try o.resolveInst(inst.rhs); |
| 494 | |
| 495 | try o.indent(); |
| 496 | const writer = o.code.writer(); |
| 497 | switch (dest_ptr) { |
| 498 | .local_ref => |i| { |
| 499 | const dest: CValue = .{ .local = i }; |
| 500 | try writer.print("{} = {};\n", .{ dest.printed(o), src_val.printed(o) }); |
| 501 | }, |
| 502 | else => { |
| 503 | try writer.print("*{} = {};\n", .{ dest_ptr.printed(o), src_val.printed(o) }); |
| 504 | }, |
| 505 | } |
| 506 | return CValue.none; |
| 474 | 507 | } |
| 475 | 508 | |
| 476 | | fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, operator: []const u8) !?[]u8 { |
| 509 | fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { |
| 477 | 510 | if (inst.base.isUnused()) |
| 478 | | return null; |
| 479 | | try indent(file); |
| 480 | | const lhs = try ctx.resolveInst(inst.lhs); |
| 481 | | const rhs = try ctx.resolveInst(inst.rhs); |
| 482 | | const writer = file.main.writer(); |
| 483 | | const name = try ctx.name(); |
| 484 | | try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const); |
| 485 | | try writer.print(" = {s} {s} {s};\n", .{ lhs, operator, rhs }); |
| 486 | | return name; |
| 511 | return CValue.none; |
| 512 | |
| 513 | const lhs = try o.resolveInst(inst.lhs); |
| 514 | const rhs = try o.resolveInst(inst.rhs); |
| 515 | |
| 516 | try o.indent(); |
| 517 | const writer = o.code.writer(); |
| 518 | const local = try o.allocLocal(inst.base.ty, .Const); |
| 519 | try writer.print(" = {} {s} {};\n", .{ lhs.printed(o), operator, rhs.printed(o) }); |
| 520 | return local; |
| 487 | 521 | } |
| 488 | 522 | |
| 489 | | fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 490 | | try indent(file); |
| 491 | | const writer = file.main.writer(); |
| 492 | | const header = file.header.buf.writer(); |
| 523 | fn genCall(o: *Object, inst: *Inst.Call) !CValue { |
| 493 | 524 | if (inst.func.castTag(.constant)) |func_inst| { |
| 494 | 525 | const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn| |
| 495 | 526 | extern_fn.data |
| ... | ... | @@ -501,23 +532,19 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 501 | 532 | const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty; |
| 502 | 533 | const ret_ty = fn_ty.fnReturnType(); |
| 503 | 534 | const unused_result = inst.base.isUnused(); |
| 504 | | var result_name: ?[]u8 = null; |
| 535 | var result_local: CValue = .none; |
| 536 | |
| 537 | try o.indent(); |
| 538 | const writer = o.code.writer(); |
| 505 | 539 | if (unused_result) { |
| 506 | 540 | if (ret_ty.hasCodeGenBits()) { |
| 507 | 541 | try writer.print("(void)", .{}); |
| 508 | 542 | } |
| 509 | 543 | } else { |
| 510 | | const local_name = try ctx.name(); |
| 511 | | try renderTypeAndName(ctx, writer, ret_ty, local_name, .Const); |
| 544 | result_local = try o.allocLocal(ret_ty, .Const); |
| 512 | 545 | try writer.writeAll(" = "); |
| 513 | | result_name = local_name; |
| 514 | 546 | } |
| 515 | 547 | const fn_name = mem.spanZ(fn_decl.name); |
| 516 | | if (file.called.get(fn_name) == null) { |
| 517 | | try file.called.put(fn_name, {}); |
| 518 | | try renderFunctionSignature(ctx, header, fn_decl); |
| 519 | | try header.writeAll(";\n"); |
| 520 | | } |
| 521 | 548 | try writer.print("{s}(", .{fn_name}); |
| 522 | 549 | if (inst.args.len != 0) { |
| 523 | 550 | for (inst.args) |arg, i| { |
| ... | ... | @@ -525,87 +552,88 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 525 | 552 | try writer.writeAll(", "); |
| 526 | 553 | } |
| 527 | 554 | if (arg.value()) |val| { |
| 528 | | try renderValue(ctx, writer, arg.ty, val); |
| 555 | try o.dg.renderValue(writer, arg.ty, val); |
| 529 | 556 | } else { |
| 530 | | const val = try ctx.resolveInst(arg); |
| 531 | | try writer.print("{s}", .{val}); |
| 557 | const val = try o.resolveInst(arg); |
| 558 | try writer.print("{}", .{val.printed(o)}); |
| 532 | 559 | } |
| 533 | 560 | } |
| 534 | 561 | } |
| 535 | 562 | try writer.writeAll(");\n"); |
| 536 | | return result_name; |
| 563 | return result_local; |
| 537 | 564 | } else { |
| 538 | | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{}); |
| 565 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement function pointers", .{}); |
| 539 | 566 | } |
| 540 | 567 | } |
| 541 | 568 | |
| 542 | | fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 569 | fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue { |
| 543 | 570 | // TODO emit #line directive here with line number and filename |
| 544 | | return null; |
| 571 | return CValue.none; |
| 545 | 572 | } |
| 546 | 573 | |
| 547 | | fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 { |
| 548 | | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{}); |
| 574 | fn genBlock(o: *Object, inst: *Inst.Block) !CValue { |
| 575 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement blocks", .{}); |
| 549 | 576 | } |
| 550 | 577 | |
| 551 | | fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 552 | | const writer = file.main.writer(); |
| 553 | | try indent(file); |
| 554 | | const local_name = try ctx.name(); |
| 555 | | const operand = try ctx.resolveInst(inst.operand); |
| 556 | | try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const); |
| 578 | fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { |
| 579 | const operand = try o.resolveInst(inst.operand); |
| 580 | |
| 581 | const writer = o.code.writer(); |
| 582 | try o.indent(); |
| 557 | 583 | if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) { |
| 584 | const local = try o.allocLocal(inst.base.ty, .Const); |
| 558 | 585 | try writer.writeAll(" = ("); |
| 559 | | try renderType(ctx, writer, inst.base.ty); |
| 560 | | try writer.print("){s};\n", .{operand}); |
| 561 | | } else { |
| 562 | | try writer.writeAll(";\n"); |
| 563 | | try indent(file); |
| 564 | | try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name }); |
| 586 | try o.dg.renderType(writer, inst.base.ty); |
| 587 | try writer.print("){};\n", .{operand.printed(o)}); |
| 588 | return local; |
| 565 | 589 | } |
| 566 | | return local_name; |
| 590 | |
| 591 | const local = try o.allocLocal(inst.base.ty, .Mut); |
| 592 | try writer.writeAll(";\n"); |
| 593 | try o.indent(); |
| 594 | try writer.print("memcpy(&{}, &{}, sizeof {});\n", .{ |
| 595 | local.printed(o), operand.printed(o), local.printed(o), |
| 596 | }); |
| 597 | return local; |
| 567 | 598 | } |
| 568 | 599 | |
| 569 | | fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 { |
| 570 | | try indent(file); |
| 571 | | try file.main.writer().writeAll("zig_breakpoint();\n"); |
| 572 | | return null; |
| 600 | fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue { |
| 601 | try o.indent(); |
| 602 | try o.code.writer().writeAll("zig_breakpoint();\n"); |
| 603 | return CValue.none; |
| 573 | 604 | } |
| 574 | 605 | |
| 575 | | fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 { |
| 576 | | try indent(file); |
| 577 | | try file.main.writer().writeAll("zig_unreachable();\n"); |
| 578 | | return null; |
| 606 | fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue { |
| 607 | try o.indent(); |
| 608 | try o.code.writer().writeAll("zig_unreachable();\n"); |
| 609 | return CValue.none; |
| 579 | 610 | } |
| 580 | 611 | |
| 581 | | fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { |
| 582 | | try indent(file); |
| 583 | | const writer = file.main.writer(); |
| 612 | fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 613 | if (as.base.isUnused() and !as.is_volatile) |
| 614 | return CValue.none; |
| 615 | |
| 616 | const writer = o.code.writer(); |
| 584 | 617 | for (as.inputs) |i, index| { |
| 585 | 618 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 586 | 619 | const reg = i[1 .. i.len - 1]; |
| 587 | 620 | const arg = as.args[index]; |
| 621 | const arg_c_value = try o.resolveInst(arg); |
| 622 | try o.indent(); |
| 588 | 623 | try writer.writeAll("register "); |
| 589 | | try renderType(ctx, writer, arg.ty); |
| 590 | | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); |
| 591 | | // TODO merge constant handling into inst_map as well |
| 592 | | if (arg.castTag(.constant)) |c| { |
| 593 | | try renderValue(ctx, writer, arg.ty, c.val); |
| 594 | | try writer.writeAll(";\n "); |
| 595 | | } else { |
| 596 | | const gop = try ctx.inst_map.getOrPut(arg); |
| 597 | | if (!gop.found_existing) { |
| 598 | | return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); |
| 599 | | } |
| 600 | | try writer.print("{s};\n ", .{gop.entry.value}); |
| 601 | | } |
| 624 | try o.dg.renderType(writer, arg.ty); |
| 625 | try writer.print(" {s}_constant __asm__(\"{s}\") = {};\n", .{ |
| 626 | reg, reg, arg_c_value.printed(o), |
| 627 | }); |
| 602 | 628 | } else { |
| 603 | | return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); |
| 629 | return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{}); |
| 604 | 630 | } |
| 605 | 631 | } |
| 606 | | try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); |
| 607 | | if (as.output) |o| { |
| 608 | | return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{}); |
| 632 | try o.indent(); |
| 633 | const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; |
| 634 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); |
| 635 | if (as.output) |_| { |
| 636 | return o.dg.fail(o.dg.decl.src(), "TODO inline asm output", .{}); |
| 609 | 637 | } |
| 610 | 638 | if (as.inputs.len > 0) { |
| 611 | 639 | if (as.output == null) { |
| ... | ... | @@ -627,5 +655,9 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { |
| 627 | 655 | } |
| 628 | 656 | } |
| 629 | 657 | try writer.writeAll(");\n"); |
| 630 | | return null; |
| 658 | |
| 659 | if (as.base.isUnused()) |
| 660 | return CValue.none; |
| 661 | |
| 662 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{}); |
| 631 | 663 | } |