| ... | @@ -2,6 +2,7 @@ const std = @import("std"); | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | | 2 | |
| 3 | const link = @import("../link.zig"); | 3 | const link = @import("../link.zig"); |
| 4 | const Module = @import("../Module.zig"); | 4 | const Module = @import("../Module.zig"); |
| | 5 | const Compilation = @import("../Compilation.zig"); |
| 5 | | 6 | |
| 6 | const Inst = @import("../ir.zig").Inst; | 7 | const Inst = @import("../ir.zig").Inst; |
| 7 | const Value = @import("../value.zig").Value; | 8 | const Value = @import("../value.zig").Value; |
| ... | @@ -19,7 +20,7 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { | ... | @@ -19,7 +20,7 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { |
| 19 | return allocator.dupe(u8, name); | 20 | return allocator.dupe(u8, name); |
| 20 | } | 21 | } |
| 21 | | 22 | |
| 22 | fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { | 23 | fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void { |
| 23 | switch (T.zigTypeTag()) { | 24 | switch (T.zigTypeTag()) { |
| 24 | .NoReturn => { | 25 | .NoReturn => { |
| 25 | try writer.writeAll("zig_noreturn void"); | 26 | try writer.writeAll("zig_noreturn void"); |
| ... | @@ -27,16 +28,16 @@ fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { | ... | @@ -27,16 +28,16 @@ fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { |
| 27 | .Void => try writer.writeAll("void"), | 28 | .Void => try writer.writeAll("void"), |
| 28 | .Int => { | 29 | .Int => { |
| 29 | if (T.tag() == .u8) { | 30 | if (T.tag() == .u8) { |
| 30 | ctx.file.need_stdint = true; | 31 | header.need_stdint = true; |
| 31 | try writer.writeAll("uint8_t"); | 32 | try writer.writeAll("uint8_t"); |
| 32 | } else if (T.tag() == .usize) { | 33 | } else if (T.tag() == .usize) { |
| 33 | ctx.file.need_stddef = true; | 34 | header.need_stddef = true; |
| 34 | try writer.writeAll("size_t"); | 35 | try writer.writeAll("size_t"); |
| 35 | } else { | 36 | } else { |
| 36 | return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{}); | 37 | return ctx.fail(ctx.decl.src(), "TODO implement int types", .{}); |
| 37 | } | 38 | } |
| 38 | }, | 39 | }, |
| 39 | else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}), | 40 | else => |e| return ctx.fail(ctx.decl.src(), "TODO implement type {}", .{e}), |
| 40 | } | 41 | } |
| 41 | } | 42 | } |
| 42 | | 43 | |
| ... | @@ -47,13 +48,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va | ... | @@ -47,13 +48,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va |
| 47 | return writer.print("{}", .{val.toSignedInt()}); | 48 | return writer.print("{}", .{val.toSignedInt()}); |
| 48 | return writer.print("{}", .{val.toUnsignedInt()}); | 49 | return writer.print("{}", .{val.toUnsignedInt()}); |
| 49 | }, | 50 | }, |
| 50 | else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}), | 51 | else => |e| return ctx.fail(ctx.decl.src(), "TODO implement value {}", .{e}), |
| 51 | } | 52 | } |
| 52 | } | 53 | } |
| 53 | | 54 | |
| 54 | fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { | 55 | fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { |
| 55 | const tv = decl.typed_value.most_recent.typed_value; | 56 | const tv = decl.typed_value.most_recent.typed_value; |
| 56 | try renderType(ctx, writer, tv.ty.fnReturnType()); | 57 | try renderType(ctx, header, writer, tv.ty.fnReturnType()); |
| 57 | // Use the child allocator directly, as we know the name can be freed before | 58 | // Use the child allocator directly, as we know the name can be freed before |
| 58 | // the rest of the arena. | 59 | // the rest of the arena. |
| 59 | const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); | 60 | const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); |
| ... | @@ -68,7 +69,7 @@ fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl | ... | @@ -68,7 +69,7 @@ fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl |
| 68 | if (index > 0) { | 69 | if (index > 0) { |
| 69 | try writer.writeAll(", "); | 70 | try writer.writeAll(", "); |
| 70 | } | 71 | } |
| 71 | try renderType(ctx, writer, tv.ty.fnParamType(index)); | 72 | try renderType(ctx, header, writer, tv.ty.fnParamType(index)); |
| 72 | try writer.print(" arg{}", .{index}); | 73 | try writer.print(" arg{}", .{index}); |
| 73 | } | 74 | } |
| 74 | } | 75 | } |
| ... | @@ -83,6 +84,34 @@ pub fn generate(file: *C, decl: *Decl) !void { | ... | @@ -83,6 +84,34 @@ pub fn generate(file: *C, decl: *Decl) !void { |
| 83 | } | 84 | } |
| 84 | } | 85 | } |
| 85 | | 86 | |
| | 87 | pub fn generateHeader( |
| | 88 | arena: *std.heap.ArenaAllocator, |
| | 89 | module: *Module, |
| | 90 | header: *C.Header, |
| | 91 | decl: *Decl, |
| | 92 | ) error{ AnalysisFail, OutOfMemory }!void { |
| | 93 | switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { |
| | 94 | .Fn => { |
| | 95 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); |
| | 96 | defer inst_map.deinit(); |
| | 97 | var ctx = Context{ |
| | 98 | .decl = decl, |
| | 99 | .arena = arena, |
| | 100 | .inst_map = &inst_map, |
| | 101 | }; |
| | 102 | const writer = header.buf.writer(); |
| | 103 | renderFunctionSignature(&ctx, header, writer, decl) catch |err| { |
| | 104 | if (err == error.AnalysisFail) { |
| | 105 | try module.failed_decls.put(module.gpa, decl, ctx.error_msg); |
| | 106 | } |
| | 107 | return err; |
| | 108 | }; |
| | 109 | try writer.writeAll(";\n"); |
| | 110 | }, |
| | 111 | else => {}, |
| | 112 | } |
| | 113 | } |
| | 114 | |
| 86 | fn genArray(file: *C, decl: *Decl) !void { | 115 | fn genArray(file: *C, decl: *Decl) !void { |
| 87 | const tv = decl.typed_value.most_recent.typed_value; | 116 | const tv = decl.typed_value.most_recent.typed_value; |
| 88 | // TODO: prevent inline asm constants from being emitted | 117 | // TODO: prevent inline asm constants from being emitted |
| ... | @@ -102,12 +131,12 @@ fn genArray(file: *C, decl: *Decl) !void { | ... | @@ -102,12 +131,12 @@ fn genArray(file: *C, decl: *Decl) !void { |
| 102 | } | 131 | } |
| 103 | | 132 | |
| 104 | const Context = struct { | 133 | const Context = struct { |
| 105 | file: *C, | | |
| 106 | decl: *Decl, | 134 | decl: *Decl, |
| 107 | inst_map: *std.AutoHashMap(*Inst, []u8), | 135 | inst_map: *std.AutoHashMap(*Inst, []u8), |
| 108 | arena: *std.heap.ArenaAllocator, | 136 | arena: *std.heap.ArenaAllocator, |
| 109 | argdex: usize = 0, | 137 | argdex: usize = 0, |
| 110 | unnamed_index: usize = 0, | 138 | unnamed_index: usize = 0, |
| | 139 | error_msg: *Compilation.ErrorMsg = undefined, |
| 111 | | 140 | |
| 112 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { | 141 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { |
| 113 | if (inst.cast(Inst.Constant)) |const_inst| { | 142 | if (inst.cast(Inst.Constant)) |const_inst| { |
| ... | @@ -127,6 +156,11 @@ const Context = struct { | ... | @@ -127,6 +156,11 @@ const Context = struct { |
| 127 | return val; | 156 | return val; |
| 128 | } | 157 | } |
| 129 | | 158 | |
| | 159 | fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| | 160 | self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args); |
| | 161 | return error.AnalysisFail; |
| | 162 | } |
| | 163 | |
| 130 | fn deinit(self: *Context) void { | 164 | fn deinit(self: *Context) void { |
| 131 | self.* = undefined; | 165 | self.* = undefined; |
| 132 | } | 166 | } |
| ... | @@ -141,14 +175,16 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -141,14 +175,16 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 141 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); | 175 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); |
| 142 | defer inst_map.deinit(); | 176 | defer inst_map.deinit(); |
| 143 | var ctx = Context{ | 177 | var ctx = Context{ |
| 144 | .file = file, | | |
| 145 | .decl = decl, | 178 | .decl = decl, |
| 146 | .arena = &arena, | 179 | .arena = &arena, |
| 147 | .inst_map = &inst_map, | 180 | .inst_map = &inst_map, |
| 148 | }; | 181 | }; |
| 149 | defer ctx.deinit(); | 182 | defer { |
| | 183 | file.error_msg = ctx.error_msg; |
| | 184 | ctx.deinit(); |
| | 185 | } |
| 150 | | 186 | |
| 151 | try renderFunctionSignature(&ctx, writer, decl); | 187 | try renderFunctionSignature(&ctx, &file.header, writer, decl); |
| 152 | | 188 | |
| 153 | try writer.writeAll(" {"); | 189 | try writer.writeAll(" {"); |
| 154 | | 190 | |
| ... | @@ -158,18 +194,18 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -158,18 +194,18 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 158 | try writer.writeAll("\n"); | 194 | try writer.writeAll("\n"); |
| 159 | for (instructions) |inst| { | 195 | for (instructions) |inst| { |
| 160 | if (switch (inst.tag) { | 196 | if (switch (inst.tag) { |
| 161 | .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), | 197 | .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?), |
| 162 | .call => try genCall(&ctx, inst.castTag(.call).?), | 198 | .call => try genCall(&ctx, file, inst.castTag(.call).?), |
| 163 | .add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"), | 199 | .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"), |
| 164 | .sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"), | 200 | .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"), |
| 165 | .ret => try genRet(&ctx, inst.castTag(.ret).?), | 201 | .ret => try genRet(&ctx, inst.castTag(.ret).?), |
| 166 | .retvoid => try genRetVoid(&ctx), | 202 | .retvoid => try genRetVoid(file), |
| 167 | .arg => try genArg(&ctx), | 203 | .arg => try genArg(&ctx), |
| 168 | .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), | 204 | .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), |
| 169 | .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?), | 205 | .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?), |
| 170 | .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?), | 206 | .unreach => try genUnreach(file, inst.castTag(.unreach).?), |
| 171 | .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?), | 207 | .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?), |
| 172 | else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}), | 208 | else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}), |
| 173 | }) |name| { | 209 | }) |name| { |
| 174 | try ctx.inst_map.putNoClobber(inst, name); | 210 | try ctx.inst_map.putNoClobber(inst, name); |
| 175 | } | 211 | } |
| ... | @@ -185,46 +221,46 @@ fn genArg(ctx: *Context) !?[]u8 { | ... | @@ -185,46 +221,46 @@ fn genArg(ctx: *Context) !?[]u8 { |
| 185 | return name; | 221 | return name; |
| 186 | } | 222 | } |
| 187 | | 223 | |
| 188 | fn genRetVoid(ctx: *Context) !?[]u8 { | 224 | fn genRetVoid(file: *C) !?[]u8 { |
| 189 | try ctx.file.main.writer().print(indentation ++ "return;\n", .{}); | 225 | try file.main.writer().print(indentation ++ "return;\n", .{}); |
| 190 | return null; | 226 | return null; |
| 191 | } | 227 | } |
| 192 | | 228 | |
| 193 | fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | 229 | fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { |
| 194 | return ctx.file.fail(ctx.decl.src(), "TODO return", .{}); | 230 | return ctx.fail(ctx.decl.src(), "TODO return", .{}); |
| 195 | } | 231 | } |
| 196 | | 232 | |
| 197 | fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | 233 | fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 198 | if (inst.base.isUnused()) | 234 | if (inst.base.isUnused()) |
| 199 | return null; | 235 | return null; |
| 200 | const op = inst.operand; | 236 | const op = inst.operand; |
| 201 | const writer = ctx.file.main.writer(); | 237 | const writer = file.main.writer(); |
| 202 | const name = try ctx.name(); | 238 | const name = try ctx.name(); |
| 203 | const from = try ctx.resolveInst(inst.operand); | 239 | const from = try ctx.resolveInst(inst.operand); |
| 204 | try writer.writeAll(indentation ++ "const "); | 240 | try writer.writeAll(indentation ++ "const "); |
| 205 | try renderType(ctx, writer, inst.base.ty); | 241 | try renderType(ctx, &file.header, writer, inst.base.ty); |
| 206 | try writer.print(" {} = (", .{name}); | 242 | try writer.print(" {} = (", .{name}); |
| 207 | try renderType(ctx, writer, inst.base.ty); | 243 | try renderType(ctx, &file.header, writer, inst.base.ty); |
| 208 | try writer.print("){};\n", .{from}); | 244 | try writer.print("){};\n", .{from}); |
| 209 | return name; | 245 | return name; |
| 210 | } | 246 | } |
| 211 | | 247 | |
| 212 | fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { | 248 | fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { |
| 213 | if (inst.base.isUnused()) | 249 | if (inst.base.isUnused()) |
| 214 | return null; | 250 | return null; |
| 215 | const lhs = ctx.resolveInst(inst.lhs); | 251 | const lhs = ctx.resolveInst(inst.lhs); |
| 216 | const rhs = ctx.resolveInst(inst.rhs); | 252 | const rhs = ctx.resolveInst(inst.rhs); |
| 217 | const writer = ctx.file.main.writer(); | 253 | const writer = file.main.writer(); |
| 218 | const name = try ctx.name(); | 254 | const name = try ctx.name(); |
| 219 | try writer.writeAll(indentation ++ "const "); | 255 | try writer.writeAll(indentation ++ "const "); |
| 220 | try renderType(ctx, writer, inst.base.ty); | 256 | try renderType(ctx, &file.header, writer, inst.base.ty); |
| 221 | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); | 257 | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); |
| 222 | return name; | 258 | return name; |
| 223 | } | 259 | } |
| 224 | | 260 | |
| 225 | fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | 261 | fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 226 | const writer = ctx.file.main.writer(); | 262 | const writer = file.main.writer(); |
| 227 | const header = ctx.file.header.writer(); | 263 | const header = file.header.buf.writer(); |
| 228 | try writer.writeAll(indentation); | 264 | try writer.writeAll(indentation); |
| 229 | if (inst.func.castTag(.constant)) |func_inst| { | 265 | if (inst.func.castTag(.constant)) |func_inst| { |
| 230 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | 266 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| ... | @@ -235,9 +271,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | ... | @@ -235,9 +271,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 235 | try writer.print("(void)", .{}); | 271 | try writer.print("(void)", .{}); |
| 236 | } | 272 | } |
| 237 | const tname = mem.spanZ(target.name); | 273 | const tname = mem.spanZ(target.name); |
| 238 | if (ctx.file.called.get(tname) == null) { | 274 | if (file.called.get(tname) == null) { |
| 239 | try ctx.file.called.put(tname, void{}); | 275 | try file.called.put(tname, void{}); |
| 240 | try renderFunctionSignature(ctx, header, target); | 276 | try renderFunctionSignature(ctx, &file.header, header, target); |
| 241 | try header.writeAll(";\n"); | 277 | try header.writeAll(";\n"); |
| 242 | } | 278 | } |
| 243 | try writer.print("{}(", .{tname}); | 279 | try writer.print("{}(", .{tname}); |
| ... | @@ -256,10 +292,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | ... | @@ -256,10 +292,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 256 | } | 292 | } |
| 257 | try writer.writeAll(");\n"); | 293 | try writer.writeAll(");\n"); |
| 258 | } else { | 294 | } else { |
| 259 | return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{}); | 295 | return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{}); |
| 260 | } | 296 | } |
| 261 | } else { | 297 | } else { |
| 262 | return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); | 298 | return ctx.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); |
| 263 | } | 299 | } |
| 264 | return null; | 300 | return null; |
| 265 | } | 301 | } |
| ... | @@ -274,20 +310,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | ... | @@ -274,20 +310,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 274 | return null; | 310 | return null; |
| 275 | } | 311 | } |
| 276 | | 312 | |
| 277 | fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | 313 | fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 { |
| 278 | try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); | 314 | try file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); |
| 279 | return null; | 315 | return null; |
| 280 | } | 316 | } |
| 281 | | 317 | |
| 282 | fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { | 318 | fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { |
| 283 | const writer = ctx.file.main.writer(); | 319 | const writer = file.main.writer(); |
| 284 | try writer.writeAll(indentation); | 320 | try writer.writeAll(indentation); |
| 285 | for (as.inputs) |i, index| { | 321 | for (as.inputs) |i, index| { |
| 286 | if (i[0] == '{' and i[i.len - 1] == '}') { | 322 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 287 | const reg = i[1 .. i.len - 1]; | 323 | const reg = i[1 .. i.len - 1]; |
| 288 | const arg = as.args[index]; | 324 | const arg = as.args[index]; |
| 289 | try writer.writeAll("register "); | 325 | try writer.writeAll("register "); |
| 290 | try renderType(ctx, writer, arg.ty); | 326 | try renderType(ctx, &file.header, writer, arg.ty); |
| 291 | try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); | 327 | try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); |
| 292 | // TODO merge constant handling into inst_map as well | 328 | // TODO merge constant handling into inst_map as well |
| 293 | if (arg.castTag(.constant)) |c| { | 329 | if (arg.castTag(.constant)) |c| { |
| ... | @@ -296,17 +332,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { | ... | @@ -296,17 +332,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { |
| 296 | } else { | 332 | } else { |
| 297 | const gop = try ctx.inst_map.getOrPut(arg); | 333 | const gop = try ctx.inst_map.getOrPut(arg); |
| 298 | if (!gop.found_existing) { | 334 | if (!gop.found_existing) { |
| 299 | return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); | 335 | return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); |
| 300 | } | 336 | } |
| 301 | try writer.print("{};\n ", .{gop.entry.value}); | 337 | try writer.print("{};\n ", .{gop.entry.value}); |
| 302 | } | 338 | } |
| 303 | } else { | 339 | } else { |
| 304 | return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); | 340 | return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); |
| 305 | } | 341 | } |
| 306 | } | 342 | } |
| 307 | try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); | 343 | try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); |
| 308 | if (as.output) |o| { | 344 | if (as.output) |o| { |
| 309 | return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{}); | 345 | return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{}); |
| 310 | } | 346 | } |
| 311 | if (as.inputs.len > 0) { | 347 | if (as.inputs.len > 0) { |
| 312 | if (as.output == null) { | 348 | if (as.output == null) { |