authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-04 15:32:41-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-04 15:32:41-07:00
loge4eb4396c27010d8d07b6fc73e7c7557170f06ad
treed6c5691ec7c7d0d73c918672e905c43ae9f897e2
parent952a397b0e006444e770e51d32cce93186959bdb
parentc594f8dc26cebbcd371c0136da5ee7bb0eaca2b3

Merge branch 'pfgithub-stage2-testing-Copy-2'


7 files changed, 408 insertions(+), 53 deletions(-)

build.zig+6
...@@ -107,6 +107,12 @@ pub fn build(b: *Builder) !void {...@@ -107,6 +107,12 @@ pub fn build(b: *Builder) !void {
107 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;107 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
108 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");108 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
109109
110
111 test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
112 test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
113 test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
114 test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
115
110 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");116 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
111 test_stage2_step.dependOn(&test_stage2.step);117 test_stage2_step.dependOn(&test_stage2.step);
112 test_step.dependOn(test_stage2_step);118 test_step.dependOn(test_stage2_step);
lib/std/build.zig+4-4
...@@ -1857,10 +1857,10 @@ pub const LibExeObjStep = struct {...@@ -1857,10 +1857,10 @@ pub const LibExeObjStep = struct {
1857 zig_args.append(builder.zig_exe) catch unreachable;1857 zig_args.append(builder.zig_exe) catch unreachable;
18581858
1859 const cmd = switch (self.kind) {1859 const cmd = switch (self.kind) {
1860 Kind.Lib => "build-lib",1860 .Lib => "build-lib",
1861 Kind.Exe => "build-exe",1861 .Exe => "build-exe",
1862 Kind.Obj => "build-obj",1862 .Obj => "build-obj",
1863 Kind.Test => "test",1863 .Test => "test",
1864 };1864 };
1865 zig_args.append(cmd) catch unreachable;1865 zig_args.append(cmd) catch unreachable;
18661866
src-self-hosted/codegen.zig+192-33
...@@ -78,7 +78,7 @@ pub fn generateSymbol(...@@ -78,7 +78,7 @@ pub fn generateSymbol(
78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),81 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
...@@ -588,9 +588,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -588,9 +588,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
588 entry.value = .dead;588 entry.value = .dead;
589 switch (prev_value) {589 switch (prev_value) {
590 .register => |reg| {590 .register => |reg| {
591 const reg64 = reg.to64();591 const canon_reg = toCanonicalReg(reg);
592 _ = branch.registers.remove(reg64);592 _ = branch.registers.remove(canon_reg);
593 branch.markRegFree(reg64);593 branch.markRegFree(canon_reg);
594 },594 },
595 else => {}, // TODO process stack allocation death595 else => {}, // TODO process stack allocation death
596 }596 }
...@@ -1023,6 +1023,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1023,6 +1023,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1023 .i386, .x86_64 => {1023 .i386, .x86_64 => {
1024 try self.code.append(0xcc); // int31024 try self.code.append(0xcc); // int3
1025 },1025 },
1026 .riscv64 => {
1027 const full = @bitCast(u32, instructions.CallBreak{
1028 .mode = @enumToInt(instructions.CallBreak.Mode.ebreak),
1029 });
1030
1031 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1032 },
1026 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),1033 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
1027 }1034 }
1028 return .none;1035 return .none;
...@@ -1085,6 +1092,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1085,6 +1092,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1085 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});1092 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1086 }1093 }
1087 },1094 },
1095 .riscv64 => {
1096 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1097
1098 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1099 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1100 const func = func_val.func;
1101 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1102 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1103 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1104 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
1105
1106 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1107 const jalr = instructions.Jalr{
1108 .rd = Register.ra.id(),
1109 .rs1 = Register.ra.id(),
1110 .offset = 0,
1111 };
1112 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1113 } else {
1114 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1115 }
1116 } else {
1117 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1118 }
1119 },
1088 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),1120 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1089 }1121 }
10901122
...@@ -1133,6 +1165,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1133,6 +1165,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1133 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel321165 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
1134 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);1166 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1135 },1167 },
1168 .riscv64 => {
1169 const jalr = instructions.Jalr{
1170 .rd = Register.zero.id(),
1171 .rs1 = Register.ra.id(),
1172 .offset = 0,
1173 };
1174 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1175 },
1136 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1176 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1137 }1177 }
1138 return .unreach;1178 return .unreach;
...@@ -1325,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1325,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1325 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {1365 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
1326 if (!inst.is_volatile and inst.base.isUnused())1366 if (!inst.is_volatile and inst.base.isUnused())
1327 return MCValue.dead;1367 return MCValue.dead;
1328 if (arch != .x86_64 and arch != .i386) {1368 switch (arch) {
1329 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});1369 .riscv64 => {
1330 }1370 for (inst.inputs) |input, i| {
1331 for (inst.inputs) |input, i| {1371 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1332 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {1372 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1333 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});1373 }
1334 }1374 const reg_name = input[1 .. input.len - 1];
1335 const reg_name = input[1 .. input.len - 1];1375 const reg = parseRegName(reg_name) orelse
1336 const reg = parseRegName(reg_name) orelse1376 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1337 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});1377 const arg = try self.resolveInst(inst.args[i]);
1338 const arg = try self.resolveInst(inst.args[i]);1378 try self.genSetReg(inst.base.src, reg, arg);
1339 try self.genSetReg(inst.base.src, reg, arg);1379 }
1340 }
13411380
1342 if (mem.eql(u8, inst.asm_source, "syscall")) {1381 if (mem.eql(u8, inst.asm_source, "ecall")) {
1343 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });1382 const full = @bitCast(u32, instructions.CallBreak{
1344 } else {1383 .mode = @enumToInt(instructions.CallBreak.Mode.ecall),
1345 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});1384 });
1346 }
13471385
1348 if (inst.output) |output| {1386 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1349 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {1387 } else {
1350 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});1388 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
1351 }1389 }
1352 const reg_name = output[2 .. output.len - 1];1390
1353 const reg = parseRegName(reg_name) orelse1391 if (inst.output) |output| {
1354 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});1392 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1355 return MCValue{ .register = reg };1393 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1356 } else {1394 }
1357 return MCValue.none;1395 const reg_name = output[2 .. output.len - 1];
1396 const reg = parseRegName(reg_name) orelse
1397 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1398 return MCValue{ .register = reg };
1399 } else {
1400 return MCValue.none;
1401 }
1402 },
1403 .x86_64, .i386 => {
1404 for (inst.inputs) |input, i| {
1405 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1406 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1407 }
1408 const reg_name = input[1 .. input.len - 1];
1409 const reg = parseRegName(reg_name) orelse
1410 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1411 const arg = try self.resolveInst(inst.args[i]);
1412 try self.genSetReg(inst.base.src, reg, arg);
1413 }
1414
1415 if (mem.eql(u8, inst.asm_source, "syscall")) {
1416 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1417 } else {
1418 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
1419 }
1420
1421 if (inst.output) |output| {
1422 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1423 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1424 }
1425 const reg_name = output[2 .. output.len - 1];
1426 const reg = parseRegName(reg_name) orelse
1427 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1428 return MCValue{ .register = reg };
1429 } else {
1430 return MCValue.none;
1431 }
1432 },
1433 else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
1358 }1434 }
1359 }1435 }
13601436
...@@ -1500,6 +1576,75 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1500,6 +1576,75 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15001576
1501 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {1577 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1502 switch (arch) {1578 switch (arch) {
1579 .riscv64 => switch (mcv) {
1580 .dead => unreachable,
1581 .ptr_stack_offset => unreachable,
1582 .ptr_embedded_in_code => unreachable,
1583 .unreach, .none => return, // Nothing to do.
1584 .undef => {
1585 if (!self.wantSafety())
1586 return; // The already existing value will do just fine.
1587 // Write the debug undefined value.
1588 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
1589 },
1590 .immediate => |unsigned_x| {
1591 const x = @bitCast(i64, unsigned_x);
1592 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1593 const instruction = @bitCast(u32, instructions.Addi{
1594 .mode = @enumToInt(instructions.Addi.Mode.addi),
1595 .imm = @truncate(i12, x),
1596 .rs1 = Register.zero.id(),
1597 .rd = reg.id(),
1598 });
1599
1600 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), instruction);
1601 return;
1602 }
1603 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1604 const split = @bitCast(packed struct {
1605 low12: i12,
1606 up20: i20,
1607 }, @truncate(i32, x));
1608 if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{});
1609
1610 const lui = @bitCast(u32, instructions.Lui{
1611 .imm = split.up20,
1612 .rd = reg.id(),
1613 });
1614 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui);
1615
1616 const addi = @bitCast(u32, instructions.Addi{
1617 .mode = @enumToInt(instructions.Addi.Mode.addi),
1618 .imm = @truncate(i12, split.low12),
1619 .rs1 = reg.id(),
1620 .rd = reg.id(),
1621 });
1622 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi);
1623 return;
1624 }
1625 // li rd, immediate
1626 // "Myriad sequences"
1627 return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
1628 },
1629 .memory => |addr| {
1630 // The value is in memory at a hard-coded address.
1631 // If the type is a pointer, it means the pointer address is at this memory location.
1632 try self.genSetReg(src, reg, .{ .immediate = addr });
1633
1634 const ld = @bitCast(u32, instructions.Load{
1635 .mode = @enumToInt(instructions.Load.Mode.ld),
1636 .rs1 = reg.id(),
1637 .rd = reg.id(),
1638 .offset = 0,
1639 });
1640
1641 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ld);
1642 // LOAD imm=[i12 offset = 0], rs1 =
1643
1644 // return self.fail("TODO implement genSetReg memory for riscv64");
1645 },
1646 else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),
1647 },
1503 .x86_64 => switch (mcv) {1648 .x86_64 => switch (mcv) {
1504 .dead => unreachable,1649 .dead => unreachable,
1505 .ptr_stack_offset => unreachable,1650 .ptr_stack_offset => unreachable,
...@@ -1873,7 +2018,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1873,7 +2018,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1873 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),2018 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1874 }2019 }
1875 },2020 },
1876 else => return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),2021 else => if (param_types.len != 0)
2022 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
1877 }2023 }
18782024
1879 if (ret_ty.zigTypeTag() == .NoReturn) {2025 if (ret_ty.zigTypeTag() == .NoReturn) {
...@@ -1915,6 +2061,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1915,6 +2061,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1915 usingnamespace switch (arch) {2061 usingnamespace switch (arch) {
1916 .i386 => @import("codegen/x86.zig"),2062 .i386 => @import("codegen/x86.zig"),
1917 .x86_64 => @import("codegen/x86_64.zig"),2063 .x86_64 => @import("codegen/x86_64.zig"),
2064 .riscv64 => @import("codegen/riscv64.zig"),
1918 else => struct {2065 else => struct {
1919 pub const Register = enum {2066 pub const Register = enum {
1920 dummy,2067 dummy,
...@@ -1931,6 +2078,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1931,6 +2078,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1931 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });2078 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
19322079
1933 fn parseRegName(name: []const u8) ?Register {2080 fn parseRegName(name: []const u8) ?Register {
2081 if (@hasDecl(Register, "parseRegName")) {
2082 return Register.parseRegName(name);
2083 }
1934 return std.meta.stringToEnum(Register, name);2084 return std.meta.stringToEnum(Register, name);
1935 }2085 }
19362086
...@@ -1947,5 +2097,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1947,5 +2097,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1947 else => return reg,2097 else => return reg,
1948 }2098 }
1949 }2099 }
2100
2101 /// For most architectures this does nothing. For x86_64 it resolves any aliased registers
2102 /// to the 64-bit wide ones.
2103 fn toCanonicalReg(reg: Register) Register {
2104 return switch (arch) {
2105 .x86_64 => reg.to64(),
2106 else => reg,
2107 };
2108 }
1950 };2109 };
1951}2110}
src-self-hosted/codegen/riscv64.zig created+92
...@@ -0,0 +1,92 @@
1const std = @import("std");
2
3pub const instructions = struct {
4 pub const CallBreak = packed struct {
5 pub const Mode = packed enum(u12) { ecall, ebreak };
6 opcode: u7 = 0b1110011,
7 unused1: u5 = 0,
8 unused2: u3 = 0,
9 unused3: u5 = 0,
10 mode: u12, //: Mode
11 };
12 // I-type
13 pub const Addi = packed struct {
14 pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
15 opcode: u7 = 0b0010011,
16 rd: u5,
17 mode: u3, //: Mode
18 rs1: u5,
19 imm: i12,
20 };
21 pub const Lui = packed struct {
22 opcode: u7 = 0b0110111,
23 rd: u5,
24 imm: i20,
25 };
26 // I_type
27 pub const Load = packed struct {
28 pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 };
29 opcode: u7 = 0b0000011,
30 rd: u5,
31 mode: u3, //: Mode
32 rs1: u5,
33 offset: i12,
34 };
35 // I-type
36 pub const Jalr = packed struct {
37 opcode: u7 = 0b1100111,
38 rd: u5,
39 mode: u3 = 0,
40 rs1: u5,
41 offset: i12,
42 };
43};
44
45// zig fmt: off
46pub const RawRegister = enum(u8) {
47 x0, x1, x2, x3, x4, x5, x6, x7,
48 x8, x9, x10, x11, x12, x13, x14, x15,
49 x16, x17, x18, x19, x20, x21, x22, x23,
50 x24, x25, x26, x27, x28, x29, x30, x31,
51};
52
53pub const Register = enum(u8) {
54 // 64 bit registers
55 zero, // zero
56 ra, // return address. caller saved
57 sp, // stack pointer. callee saved.
58 gp, // global pointer
59 tp, // thread pointer
60 t0, t1, t2, // temporaries. caller saved.
61 s0, // s0/fp, callee saved.
62 s1, // callee saved.
63 a0, a1, // fn args/return values. caller saved.
64 a2, a3, a4, a5, a6, a7, // fn args. caller saved.
65 s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
66 t3, t4, t5, t6, // caller saved
67
68 pub fn parseRegName(name: []const u8) ?Register {
69 if(std.meta.stringToEnum(Register, name)) |reg| return reg;
70 if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
71 return null;
72 }
73
74 /// Returns the register's id.
75 pub fn id(self: @This()) u5 {
76 return @truncate(u5, @enumToInt(self));
77 }
78
79 /// Returns the index into `callee_preserved_regs`.
80 pub fn allocIndex(self: Register) ?u4 {
81 inline for(callee_preserved_regs) |cpreg, i| {
82 if(self == cpreg) return i;
83 }
84 return null;
85 }
86};
87
88// zig fmt: on
89
90pub const callee_preserved_regs = [_]Register{
91 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
92};
src-self-hosted/test.zig+66-14
...@@ -4,6 +4,11 @@ const Module = @import("Module.zig");...@@ -4,6 +4,11 @@ const Module = @import("Module.zig");
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const zir = @import("zir.zig");5const zir = @import("zir.zig");
6const Package = @import("Package.zig");6const Package = @import("Package.zig");
7const build_options = @import("build_options");
8const enable_qemu: bool = build_options.enable_qemu;
9const enable_wine: bool = build_options.enable_wine;
10const enable_wasmtime: bool = build_options.enable_wasmtime;
11const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
712
8const cheader = @embedFile("cbe.h");13const cheader = @embedFile("cbe.h");
914
...@@ -401,8 +406,6 @@ pub const TestContext = struct {...@@ -401,8 +406,6 @@ pub const TestContext = struct {
401 const root_node = try progress.start("tests", self.cases.items.len);406 const root_node = try progress.start("tests", self.cases.items.len);
402 defer root_node.end();407 defer root_node.end();
403408
404 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
405
406 for (self.cases.items) |case| {409 for (self.cases.items) |case| {
407 std.testing.base_allocator_instance.reset();410 std.testing.base_allocator_instance.reset();
408411
...@@ -415,13 +418,19 @@ pub const TestContext = struct {...@@ -415,13 +418,19 @@ pub const TestContext = struct {
415 progress.initial_delay_ns = 0;418 progress.initial_delay_ns = 0;
416 progress.refresh_rate_ns = 0;419 progress.refresh_rate_ns = 0;
417420
418 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);421 try self.runOneCase(std.testing.allocator, &prg_node, case);
419 try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
420 try std.testing.allocator_instance.validate();422 try std.testing.allocator_instance.validate();
421 }423 }
422 }424 }
423425
424 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {426 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
427 const target_info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
428 const target = target_info.target;
429
430 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
431 defer arena_allocator.deinit();
432 const arena = &arena_allocator.allocator;
433
425 var tmp = std.testing.tmpDir(.{});434 var tmp = std.testing.tmpDir(.{});
426 defer tmp.cleanup();435 defer tmp.cleanup();
427436
...@@ -429,8 +438,7 @@ pub const TestContext = struct {...@@ -429,8 +438,7 @@ pub const TestContext = struct {
429 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);438 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
430 defer root_pkg.destroy();439 defer root_pkg.destroy();
431440
432 const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);441 const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
433 defer allocator.free(bin_name);
434442
435 var module = try Module.init(allocator, .{443 var module = try Module.init(allocator, .{
436 .root_name = "test_case",444 .root_name = "test_case",
...@@ -485,8 +493,7 @@ pub const TestContext = struct {...@@ -485,8 +493,7 @@ pub const TestContext = struct {
485 // incremental updates493 // incremental updates
486 var file = try tmp.dir.openFile(bin_name, .{ .read = true });494 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
487 defer file.close();495 defer file.close();
488 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");496 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
489 defer allocator.free(out);
490497
491 if (expected_output.len != out.len) {498 if (expected_output.len != out.len) {
492 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });499 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
...@@ -533,8 +540,7 @@ pub const TestContext = struct {...@@ -533,8 +540,7 @@ pub const TestContext = struct {
533 var test_node = update_node.start("assert", null);540 var test_node = update_node.start("assert", null);
534 test_node.activate();541 test_node.activate();
535 defer test_node.end();542 defer test_node.end();
536 var handled_errors = try allocator.alloc(bool, e.len);543 var handled_errors = try arena.alloc(bool, e.len);
537 defer allocator.free(handled_errors);
538 for (handled_errors) |*h| {544 for (handled_errors) |*h| {
539 h.* = false;545 h.* = false;
540 }546 }
...@@ -569,10 +575,56 @@ pub const TestContext = struct {...@@ -569,10 +575,56 @@ pub const TestContext = struct {
569 exec_node.activate();575 exec_node.activate();
570 defer exec_node.end();576 defer exec_node.end();
571577
572 try module.makeBinFileExecutable();578 var argv = std.ArrayList([]const u8).init(allocator);
579 defer argv.deinit();
580
581 const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
582
583 switch (case.target.getExternalExecutor()) {
584 .native => try argv.append(exe_path),
585 .unavailable => return, // No executor available; pass test.
586
587 .qemu => |qemu_bin_name| {
588 if (enable_qemu) qemu: {
589 // TODO Ability for test cases to specify whether to link libc.
590 const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;
591 const glibc_dir_arg = if (need_cross_glibc)
592 glibc_multi_install_dir orelse break :qemu
593 else
594 null;
595 try argv.append(qemu_bin_name);
596 if (glibc_dir_arg) |dir| {
597 const linux_triple = try target.linuxTriple(arena);
598 const full_dir = try std.fs.path.join(arena, &[_][]const u8{
599 dir,
600 linux_triple,
601 });
602
603 try argv.append("-L");
604 try argv.append(full_dir);
605 }
606 try argv.append(exe_path);
607 }
608 return; // QEMU not available; pass test.
609 },
610
611 .wine => |wine_bin_name| if (enable_wine) {
612 try argv.append(wine_bin_name);
613 try argv.append(exe_path);
614 } else {
615 return; // Wine not available; pass test.
616 },
617
618 .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
619 try argv.append(wasmtime_bin_name);
620 try argv.append("--dir=.");
621 try argv.append(exe_path);
622 } else {
623 return; // wasmtime not available; pass test.
624 },
625 }
573626
574 const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});627 try module.makeBinFileExecutable();
575 defer allocator.free(exe_path);
576628
577 break :x try std.ChildProcess.exec(.{629 break :x try std.ChildProcess.exec(.{
578 .allocator = allocator,630 .allocator = allocator,
src-self-hosted/type.zig+7-2
...@@ -510,13 +510,18 @@ pub const Type = extern union {...@@ -510,13 +510,18 @@ pub const Type = extern union {
510 .u8,510 .u8,
511 .i8,511 .i8,
512 .bool,512 .bool,
513 .array_u8_sentinel_0,
514 => return 1,
515
513 .fn_noreturn_no_args, // represents machine code; not a pointer516 .fn_noreturn_no_args, // represents machine code; not a pointer
514 .fn_void_no_args, // represents machine code; not a pointer517 .fn_void_no_args, // represents machine code; not a pointer
515 .fn_naked_noreturn_no_args, // represents machine code; not a pointer518 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
516 .fn_ccc_void_no_args, // represents machine code; not a pointer519 .fn_ccc_void_no_args, // represents machine code; not a pointer
517 .function, // represents machine code; not a pointer520 .function, // represents machine code; not a pointer
518 .array_u8_sentinel_0,521 => return switch (target.cpu.arch) {
519 => return 1,522 .riscv64 => 2,
523 else => 1,
524 },
520525
521 .i16, .u16 => return 2,526 .i16, .u16 => return 2,
522 .i32, .u32 => return 4,527 .i32, .u32 => return 4,
test/stage2/compare_output.zig+41
...@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{...@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{
7 .os_tag = .linux,7 .os_tag = .linux,
8};8};
99
10const linux_riscv64 = std.zig.CrossTarget{
11 .cpu_arch = .riscv64,
12 .os_tag = .linux,
13};
14
10pub fn addCases(ctx: *TestContext) !void {15pub fn addCases(ctx: *TestContext) !void {
11 if (std.Target.current.os.tag != .linux or16 if (std.Target.current.os.tag != .linux or
12 std.Target.current.cpu.arch != .x86_64)17 std.Target.current.cpu.arch != .x86_64)
...@@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {
118 \\123 \\
119 );124 );
120 }125 }
126
127 {
128 var case = ctx.exe("hello world", linux_riscv64);
129 // Regular old hello world
130 case.addCompareOutput(
131 \\export fn _start() noreturn {
132 \\ print();
133 \\
134 \\ exit();
135 \\}
136 \\
137 \\fn print() void {
138 \\ asm volatile ("ecall"
139 \\ :
140 \\ : [number] "{a7}" (64),
141 \\ [arg1] "{a0}" (1),
142 \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
143 \\ [arg3] "{a2}" ("Hello, World!\n".len)
144 \\ : "rcx", "r11", "memory"
145 \\ );
146 \\ return;
147 \\}
148 \\
149 \\fn exit() noreturn {
150 \\ asm volatile ("ecall"
151 \\ :
152 \\ : [number] "{a7}" (94),
153 \\ [arg1] "{a0}" (0)
154 \\ : "rcx", "r11", "memory"
155 \\ );
156 \\ unreachable;
157 \\}
158 ,
159 "Hello, World!\n",
160 );
161 }
121162
122 {163 {
123 var case = ctx.exe("adding numbers at comptime", linux_x64);164 var case = ctx.exe("adding numbers at comptime", linux_x64);