| author | |
| committer | |
| log | 08154c0deb653e016d6eb285c85094048eeded89 |
| tree | d90ce4372ed47afe3bea24245c359fad03820026 |
| parent | 25b1c00c72b51ef9e011867b3fc4f37b3e216223 |
5 files changed, 212 insertions(+), 209 deletions(-)
src-self-hosted/Module.zig+3-2| ... | ... | @@ -1210,8 +1210,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1210 | 1210 | |
| 1211 | 1211 | try self.astGenBlock(&gen_scope.base, body_block); |
| 1212 | 1212 | |
| 1213 | const last_inst = gen_scope.instructions.items[gen_scope.instructions.items.len - 1]; | |
| 1214 | if (!last_inst.tag.isNoReturn()) { | |
| 1213 | if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or | |
| 1214 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())) | |
| 1215 | { | |
| 1215 | 1216 | const src = tree.token_locs[body_block.rbrace].start; |
| 1216 | 1217 | _ = try self.addZIRInst(&gen_scope.base, src, zir.Inst.ReturnVoid, .{}, .{}); |
| 1217 | 1218 | } |
src-self-hosted/cgen.zig deleted-205| ... | ... | @@ -1,205 +0,0 @@ |
| 1 | const link = @import("link.zig"); | |
| 2 | const Module = @import("Module.zig"); | |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | ||
| 6 | const Inst = @import("ir.zig").Inst; | |
| 7 | const Value = @import("value.zig").Value; | |
| 8 | const Type = @import("type.zig").Type; | |
| 9 | ||
| 10 | const C = link.File.C; | |
| 11 | const Decl = Module.Decl; | |
| 12 | const mem = std.mem; | |
| 13 | ||
| 14 | /// Maps a name from Zig source to C. This will always give the same output for | |
| 15 | /// any given input. | |
| 16 | fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { | |
| 17 | return allocator.dupe(u8, name); | |
| 18 | } | |
| 19 | ||
| 20 | fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void { | |
| 21 | if (T.tag() == .usize) { | |
| 22 | file.need_stddef = true; | |
| 23 | try writer.writeAll("size_t"); | |
| 24 | } else { | |
| 25 | switch (T.zigTypeTag()) { | |
| 26 | .NoReturn => { | |
| 27 | file.need_noreturn = true; | |
| 28 | try writer.writeAll("noreturn void"); | |
| 29 | }, | |
| 30 | .Void => try writer.writeAll("void"), | |
| 31 | .Int => { | |
| 32 | if (T.tag() == .u8) { | |
| 33 | file.need_stdint = true; | |
| 34 | try writer.writeAll("uint8_t"); | |
| 35 | } else { | |
| 36 | return file.fail(src, "TODO implement int types", .{}); | |
| 37 | } | |
| 38 | }, | |
| 39 | else => |e| return file.fail(src, "TODO implement type {}", .{e}), | |
| 40 | } | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { | |
| 45 | const tv = decl.typed_value.most_recent.typed_value; | |
| 46 | try renderType(file, writer, tv.ty.fnReturnType(), decl.src()); | |
| 47 | const name = try map(file.allocator, mem.spanZ(decl.name)); | |
| 48 | defer file.allocator.free(name); | |
| 49 | try writer.print(" {}(", .{name}); | |
| 50 | if (tv.ty.fnParamLen() == 0) | |
| 51 | try writer.writeAll("void)") | |
| 52 | else | |
| 53 | return file.fail(decl.src(), "TODO implement parameters", .{}); | |
| 54 | } | |
| 55 | ||
| 56 | pub fn generate(file: *C, decl: *Decl) !void { | |
| 57 | switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { | |
| 58 | .Fn => try genFn(file, decl), | |
| 59 | .Array => try genArray(file, decl), | |
| 60 | else => |e| return file.fail(decl.src(), "TODO {}", .{e}), | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | fn genArray(file: *C, decl: *Decl) !void { | |
| 65 | const tv = decl.typed_value.most_recent.typed_value; | |
| 66 | // TODO: prevent inline asm constants from being emitted | |
| 67 | const name = try map(file.allocator, mem.span(decl.name)); | |
| 68 | defer file.allocator.free(name); | |
| 69 | if (tv.val.cast(Value.Payload.Bytes)) |payload| | |
| 70 | if (tv.ty.arraySentinel()) |sentinel| | |
| 71 | if (sentinel.toUnsignedInt() == 0) | |
| 72 | try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) | |
| 73 | else | |
| 74 | return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{}) | |
| 75 | else | |
| 76 | return file.fail(decl.src(), "TODO byte arrays without sentinels", .{}) | |
| 77 | else | |
| 78 | return file.fail(decl.src(), "TODO non-byte arrays", .{}); | |
| 79 | } | |
| 80 | ||
| 81 | fn genFn(file: *C, decl: *Decl) !void { | |
| 82 | const writer = file.main.writer(); | |
| 83 | const tv = decl.typed_value.most_recent.typed_value; | |
| 84 | ||
| 85 | try renderFunctionSignature(file, writer, decl); | |
| 86 | ||
| 87 | try writer.writeAll(" {"); | |
| 88 | ||
| 89 | const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func; | |
| 90 | const instructions = func.analysis.success.instructions; | |
| 91 | if (instructions.len > 0) { | |
| 92 | for (instructions) |inst| { | |
| 93 | try writer.writeAll("\n\t"); | |
| 94 | switch (inst.tag) { | |
| 95 | .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl), | |
| 96 | .call => try genCall(file, inst.cast(Inst.Call).?, decl), | |
| 97 | .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()), | |
| 98 | else => |e| return file.fail(decl.src(), "TODO {}", .{e}), | |
| 99 | } | |
| 100 | } | |
| 101 | try writer.writeAll("\n"); | |
| 102 | } | |
| 103 | ||
| 104 | try writer.writeAll("}\n\n"); | |
| 105 | } | |
| 106 | ||
| 107 | fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void { | |
| 108 | const writer = file.main.writer(); | |
| 109 | const ret_value = inst.args.operand; | |
| 110 | const value = ret_value.value().?; | |
| 111 | if (expected_return_type.eql(ret_value.ty)) | |
| 112 | return file.fail(decl.src(), "TODO return {}", .{expected_return_type}) | |
| 113 | else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int) | |
| 114 | if (value.intFitsInType(expected_return_type, file.options.target)) | |
| 115 | if (expected_return_type.intInfo(file.options.target).bits <= 64) | |
| 116 | try writer.print("return {};", .{value.toUnsignedInt()}) | |
| 117 | else | |
| 118 | return file.fail(decl.src(), "TODO return ints > 64 bits", .{}) | |
| 119 | else | |
| 120 | return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type }) | |
| 121 | else | |
| 122 | return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty }); | |
| 123 | } | |
| 124 | ||
| 125 | fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void { | |
| 126 | const writer = file.main.writer(); | |
| 127 | const header = file.header.writer(); | |
| 128 | if (inst.args.func.cast(Inst.Constant)) |func_inst| { | |
| 129 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | |
| 130 | const target = func_val.func.owner_decl; | |
| 131 | const target_ty = target.typed_value.most_recent.typed_value.ty; | |
| 132 | const ret_ty = target_ty.fnReturnType().tag(); | |
| 133 | if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) { | |
| 134 | try writer.print("(void)", .{}); | |
| 135 | } | |
| 136 | const tname = mem.spanZ(target.name); | |
| 137 | if (file.called.get(tname) == null) { | |
| 138 | try file.called.put(tname, void{}); | |
| 139 | try renderFunctionSignature(file, header, target); | |
| 140 | try header.writeAll(";\n"); | |
| 141 | } | |
| 142 | try writer.print("{}();", .{tname}); | |
| 143 | } else { | |
| 144 | return file.fail(decl.src(), "TODO non-function call target?", .{}); | |
| 145 | } | |
| 146 | if (inst.args.args.len != 0) { | |
| 147 | return file.fail(decl.src(), "TODO function arguments", .{}); | |
| 148 | } | |
| 149 | } else { | |
| 150 | return file.fail(decl.src(), "TODO non-constant call inst?", .{}); | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void { | |
| 155 | const as = inst.args; | |
| 156 | const writer = file.main.writer(); | |
| 157 | for (as.inputs) |i, index| { | |
| 158 | if (i[0] == '{' and i[i.len - 1] == '}') { | |
| 159 | const reg = i[1 .. i.len - 1]; | |
| 160 | const arg = as.args[index]; | |
| 161 | if (arg.cast(Inst.Constant)) |c| { | |
| 162 | if (c.val.tag() == .int_u64) { | |
| 163 | try writer.writeAll("register "); | |
| 164 | try renderType(file, writer, arg.ty, decl.src()); | |
| 165 | try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() }); | |
| 166 | } else { | |
| 167 | return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()}); | |
| 168 | } | |
| 169 | } else { | |
| 170 | return file.fail(decl.src(), "TODO non-constant inline asm args", .{}); | |
| 171 | } | |
| 172 | } else { | |
| 173 | return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{}); | |
| 174 | } | |
| 175 | } | |
| 176 | try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); | |
| 177 | if (as.output) |o| { | |
| 178 | return file.fail(decl.src(), "TODO inline asm output", .{}); | |
| 179 | } | |
| 180 | if (as.inputs.len > 0) { | |
| 181 | if (as.output == null) { | |
| 182 | try writer.writeAll(" :"); | |
| 183 | } | |
| 184 | try writer.writeAll(": "); | |
| 185 | for (as.inputs) |i, index| { | |
| 186 | if (i[0] == '{' and i[i.len - 1] == '}') { | |
| 187 | const reg = i[1 .. i.len - 1]; | |
| 188 | const arg = as.args[index]; | |
| 189 | if (index > 0) { | |
| 190 | try writer.writeAll(", "); | |
| 191 | } | |
| 192 | if (arg.cast(Inst.Constant)) |c| { | |
| 193 | try writer.print("\"\"({}_constant)", .{reg}); | |
| 194 | } else { | |
| 195 | // This is blocked by the earlier test | |
| 196 | unreachable; | |
| 197 | } | |
| 198 | } else { | |
| 199 | // This is blocked by the earlier test | |
| 200 | unreachable; | |
| 201 | } | |
| 202 | } | |
| 203 | } | |
| 204 | try writer.writeAll(");"); | |
| 205 | } |
src-self-hosted/codegen/c.zig created+206| ... | ... | @@ -0,0 +1,206 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | const link = @import("../link.zig"); | |
| 4 | const Module = @import("../Module.zig"); | |
| 5 | ||
| 6 | const Inst = @import("../ir.zig").Inst; | |
| 7 | const Value = @import("../value.zig").Value; | |
| 8 | const Type = @import("../type.zig").Type; | |
| 9 | ||
| 10 | const C = link.File.C; | |
| 11 | const Decl = Module.Decl; | |
| 12 | const mem = std.mem; | |
| 13 | ||
| 14 | /// Maps a name from Zig source to C. This will always give the same output for | |
| 15 | /// any given input. | |
| 16 | fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { | |
| 17 | return allocator.dupe(u8, name); | |
| 18 | } | |
| 19 | ||
| 20 | fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void { | |
| 21 | if (T.tag() == .usize) { | |
| 22 | file.need_stddef = true; | |
| 23 | try writer.writeAll("size_t"); | |
| 24 | } else { | |
| 25 | switch (T.zigTypeTag()) { | |
| 26 | .NoReturn => { | |
| 27 | file.need_noreturn = true; | |
| 28 | try writer.writeAll("noreturn void"); | |
| 29 | }, | |
| 30 | .Void => try writer.writeAll("void"), | |
| 31 | .Int => { | |
| 32 | if (T.tag() == .u8) { | |
| 33 | file.need_stdint = true; | |
| 34 | try writer.writeAll("uint8_t"); | |
| 35 | } else { | |
| 36 | return file.fail(src, "TODO implement int types", .{}); | |
| 37 | } | |
| 38 | }, | |
| 39 | else => |e| return file.fail(src, "TODO implement type {}", .{e}), | |
| 40 | } | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { | |
| 45 | const tv = decl.typed_value.most_recent.typed_value; | |
| 46 | try renderType(file, writer, tv.ty.fnReturnType(), decl.src()); | |
| 47 | const name = try map(file.allocator, mem.spanZ(decl.name)); | |
| 48 | defer file.allocator.free(name); | |
| 49 | try writer.print(" {}(", .{name}); | |
| 50 | if (tv.ty.fnParamLen() == 0) | |
| 51 | try writer.writeAll("void)") | |
| 52 | else | |
| 53 | return file.fail(decl.src(), "TODO implement parameters", .{}); | |
| 54 | } | |
| 55 | ||
| 56 | pub fn generate(file: *C, decl: *Decl) !void { | |
| 57 | switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { | |
| 58 | .Fn => try genFn(file, decl), | |
| 59 | .Array => try genArray(file, decl), | |
| 60 | else => |e| return file.fail(decl.src(), "TODO {}", .{e}), | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | fn genArray(file: *C, decl: *Decl) !void { | |
| 65 | const tv = decl.typed_value.most_recent.typed_value; | |
| 66 | // TODO: prevent inline asm constants from being emitted | |
| 67 | const name = try map(file.allocator, mem.span(decl.name)); | |
| 68 | defer file.allocator.free(name); | |
| 69 | if (tv.val.cast(Value.Payload.Bytes)) |payload| | |
| 70 | if (tv.ty.arraySentinel()) |sentinel| | |
| 71 | if (sentinel.toUnsignedInt() == 0) | |
| 72 | try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) | |
| 73 | else | |
| 74 | return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{}) | |
| 75 | else | |
| 76 | return file.fail(decl.src(), "TODO byte arrays without sentinels", .{}) | |
| 77 | else | |
| 78 | return file.fail(decl.src(), "TODO non-byte arrays", .{}); | |
| 79 | } | |
| 80 | ||
| 81 | fn genFn(file: *C, decl: *Decl) !void { | |
| 82 | const writer = file.main.writer(); | |
| 83 | const tv = decl.typed_value.most_recent.typed_value; | |
| 84 | ||
| 85 | try renderFunctionSignature(file, writer, decl); | |
| 86 | ||
| 87 | try writer.writeAll(" {"); | |
| 88 | ||
| 89 | const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func; | |
| 90 | const instructions = func.analysis.success.instructions; | |
| 91 | if (instructions.len > 0) { | |
| 92 | for (instructions) |inst| { | |
| 93 | try writer.writeAll("\n\t"); | |
| 94 | switch (inst.tag) { | |
| 95 | .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl), | |
| 96 | .call => try genCall(file, inst.cast(Inst.Call).?, decl), | |
| 97 | .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()), | |
| 98 | .retvoid => try file.main.writer().print("return;", .{}), | |
| 99 | else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}), | |
| 100 | } | |
| 101 | } | |
| 102 | try writer.writeAll("\n"); | |
| 103 | } | |
| 104 | ||
| 105 | try writer.writeAll("}\n\n"); | |
| 106 | } | |
| 107 | ||
| 108 | fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void { | |
| 109 | const writer = file.main.writer(); | |
| 110 | const ret_value = inst.args.operand; | |
| 111 | const value = ret_value.value().?; | |
| 112 | if (expected_return_type.eql(ret_value.ty)) | |
| 113 | return file.fail(decl.src(), "TODO return {}", .{expected_return_type}) | |
| 114 | else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int) | |
| 115 | if (value.intFitsInType(expected_return_type, file.options.target)) | |
| 116 | if (expected_return_type.intInfo(file.options.target).bits <= 64) | |
| 117 | try writer.print("return {};", .{value.toUnsignedInt()}) | |
| 118 | else | |
| 119 | return file.fail(decl.src(), "TODO return ints > 64 bits", .{}) | |
| 120 | else | |
| 121 | return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type }) | |
| 122 | else | |
| 123 | return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty }); | |
| 124 | } | |
| 125 | ||
| 126 | fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void { | |
| 127 | const writer = file.main.writer(); | |
| 128 | const header = file.header.writer(); | |
| 129 | if (inst.args.func.cast(Inst.Constant)) |func_inst| { | |
| 130 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | |
| 131 | const target = func_val.func.owner_decl; | |
| 132 | const target_ty = target.typed_value.most_recent.typed_value.ty; | |
| 133 | const ret_ty = target_ty.fnReturnType().tag(); | |
| 134 | if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) { | |
| 135 | try writer.print("(void)", .{}); | |
| 136 | } | |
| 137 | const tname = mem.spanZ(target.name); | |
| 138 | if (file.called.get(tname) == null) { | |
| 139 | try file.called.put(tname, void{}); | |
| 140 | try renderFunctionSignature(file, header, target); | |
| 141 | try header.writeAll(";\n"); | |
| 142 | } | |
| 143 | try writer.print("{}();", .{tname}); | |
| 144 | } else { | |
| 145 | return file.fail(decl.src(), "TODO non-function call target?", .{}); | |
| 146 | } | |
| 147 | if (inst.args.args.len != 0) { | |
| 148 | return file.fail(decl.src(), "TODO function arguments", .{}); | |
| 149 | } | |
| 150 | } else { | |
| 151 | return file.fail(decl.src(), "TODO non-constant call inst?", .{}); | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void { | |
| 156 | const as = inst.args; | |
| 157 | const writer = file.main.writer(); | |
| 158 | for (as.inputs) |i, index| { | |
| 159 | if (i[0] == '{' and i[i.len - 1] == '}') { | |
| 160 | const reg = i[1 .. i.len - 1]; | |
| 161 | const arg = as.args[index]; | |
| 162 | if (arg.cast(Inst.Constant)) |c| { | |
| 163 | if (c.val.tag() == .int_u64) { | |
| 164 | try writer.writeAll("register "); | |
| 165 | try renderType(file, writer, arg.ty, decl.src()); | |
| 166 | try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() }); | |
| 167 | } else { | |
| 168 | return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()}); | |
| 169 | } | |
| 170 | } else { | |
| 171 | return file.fail(decl.src(), "TODO non-constant inline asm args", .{}); | |
| 172 | } | |
| 173 | } else { | |
| 174 | return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{}); | |
| 175 | } | |
| 176 | } | |
| 177 | try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); | |
| 178 | if (as.output) |o| { | |
| 179 | return file.fail(decl.src(), "TODO inline asm output", .{}); | |
| 180 | } | |
| 181 | if (as.inputs.len > 0) { | |
| 182 | if (as.output == null) { | |
| 183 | try writer.writeAll(" :"); | |
| 184 | } | |
| 185 | try writer.writeAll(": "); | |
| 186 | for (as.inputs) |i, index| { | |
| 187 | if (i[0] == '{' and i[i.len - 1] == '}') { | |
| 188 | const reg = i[1 .. i.len - 1]; | |
| 189 | const arg = as.args[index]; | |
| 190 | if (index > 0) { | |
| 191 | try writer.writeAll(", "); | |
| 192 | } | |
| 193 | if (arg.cast(Inst.Constant)) |c| { | |
| 194 | try writer.print("\"\"({}_constant)", .{reg}); | |
| 195 | } else { | |
| 196 | // This is blocked by the earlier test | |
| 197 | unreachable; | |
| 198 | } | |
| 199 | } else { | |
| 200 | // This is blocked by the earlier test | |
| 201 | unreachable; | |
| 202 | } | |
| 203 | } | |
| 204 | } | |
| 205 | try writer.writeAll(");"); | |
| 206 | } |
src-self-hosted/link.zig+2-2| ... | ... | @@ -7,7 +7,7 @@ const Module = @import("Module.zig"); |
| 7 | 7 | const fs = std.fs; |
| 8 | 8 | const elf = std.elf; |
| 9 | 9 | const codegen = @import("codegen.zig"); |
| 10 | const cgen = @import("cgen.zig"); | |
| 10 | const c_codegen = @import("codegen/c.zig"); | |
| 11 | 11 | |
| 12 | 12 | const default_entry_addr = 0x8000000; |
| 13 | 13 | |
| ... | ... | @@ -259,7 +259,7 @@ pub const File = struct { |
| 259 | 259 | } |
| 260 | 260 | |
| 261 | 261 | pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void { |
| 262 | cgen.generate(self, decl) catch |err| { | |
| 262 | c_codegen.generate(self, decl) catch |err| { | |
| 263 | 263 | if (err == error.CGenFailure) { |
| 264 | 264 | try module.failed_decls.put(module.gpa, decl, self.error_msg); |
| 265 | 265 | } |
test/stage2/cbe.zig+1| ... | ... | @@ -62,6 +62,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 62 | 62 | \\	register size_t rax_constant __asm__("rax") = 231; |
| 63 | 63 | \\	register size_t rdi_constant __asm__("rdi") = 0; |
| 64 | 64 | \\	__asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); |
| 65 | \\	return; | |
| 65 | 66 | \\} |
| 66 | 67 | \\ |
| 67 | 68 | ); |