| author | |
| committer | |
| log | e4eb4396c27010d8d07b6fc73e7c7557170f06ad |
| tree | d6c5691ec7c7d0d73c918672e905c43ae9f897e2 |
| parent | 952a397b0e006444e770e51d32cce93186959bdb |
| parent | c594f8dc26cebbcd371c0136da5ee7bb0eaca2b3 |
7 files changed, 408 insertions(+), 53 deletions(-)
build.zig+6| ... | ... | @@ -107,6 +107,12 @@ pub fn build(b: *Builder) !void { |
| 107 | 107 | const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; |
| 108 | 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"); |
| 109 | 109 | |
| 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 | 116 | const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); |
| 111 | 117 | test_stage2_step.dependOn(&test_stage2.step); |
| 112 | 118 | test_step.dependOn(test_stage2_step); |
lib/std/build.zig+4-4| ... | ... | @@ -1857,10 +1857,10 @@ pub const LibExeObjStep = struct { |
| 1857 | 1857 | zig_args.append(builder.zig_exe) catch unreachable; |
| 1858 | 1858 | |
| 1859 | 1859 | const cmd = switch (self.kind) { |
| 1860 | Kind.Lib => "build-lib", | |
| 1861 | Kind.Exe => "build-exe", | |
| 1862 | Kind.Obj => "build-obj", | |
| 1863 | Kind.Test => "test", | |
| 1860 | .Lib => "build-lib", | |
| 1861 | .Exe => "build-exe", | |
| 1862 | .Obj => "build-obj", | |
| 1863 | .Test => "test", | |
| 1864 | 1864 | }; |
| 1865 | 1865 | zig_args.append(cmd) catch unreachable; |
| 1866 | 1866 |
src-self-hosted/codegen.zig+192-33| ... | ... | @@ -78,7 +78,7 @@ pub fn generateSymbol( |
| 78 | 78 | //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line), |
| 79 | 79 | //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line), |
| 80 | 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 | 82 | //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line), |
| 83 | 83 | //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line), |
| 84 | 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 | 588 | entry.value = .dead; |
| 589 | 589 | switch (prev_value) { |
| 590 | 590 | .register => |reg| { |
| 591 | const reg64 = reg.to64(); | |
| 592 | _ = branch.registers.remove(reg64); | |
| 593 | branch.markRegFree(reg64); | |
| 591 | const canon_reg = toCanonicalReg(reg); | |
| 592 | _ = branch.registers.remove(canon_reg); | |
| 593 | branch.markRegFree(canon_reg); | |
| 594 | 594 | }, |
| 595 | 595 | else => {}, // TODO process stack allocation death |
| 596 | 596 | } |
| ... | ... | @@ -1023,6 +1023,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1023 | 1023 | .i386, .x86_64 => { |
| 1024 | 1024 | 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 | 1033 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), |
| 1027 | 1034 | } |
| 1028 | 1035 | return .none; |
| ... | ... | @@ -1085,6 +1092,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1085 | 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 | 1120 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), |
| 1089 | 1121 | } |
| 1090 | 1122 | |
| ... | ... | @@ -1133,6 +1165,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1133 | 1165 | self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32 |
| 1134 | 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 | 1176 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 1137 | 1177 | } |
| 1138 | 1178 | return .unreach; |
| ... | ... | @@ -1325,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1325 | 1365 | fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue { |
| 1326 | 1366 | if (!inst.is_volatile and inst.base.isUnused()) |
| 1327 | 1367 | return MCValue.dead; |
| 1328 | if (arch != .x86_64 and arch != .i386) { | |
| 1329 | return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}); | |
| 1330 | } | |
| 1331 | for (inst.inputs) |input, i| { | |
| 1332 | if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { | |
| 1333 | return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); | |
| 1334 | } | |
| 1335 | const reg_name = input[1 .. input.len - 1]; | |
| 1336 | const reg = parseRegName(reg_name) orelse | |
| 1337 | return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); | |
| 1338 | const arg = try self.resolveInst(inst.args[i]); | |
| 1339 | try self.genSetReg(inst.base.src, reg, arg); | |
| 1340 | } | |
| 1368 | switch (arch) { | |
| 1369 | .riscv64 => { | |
| 1370 | for (inst.inputs) |input, i| { | |
| 1371 | if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { | |
| 1372 | return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); | |
| 1373 | } | |
| 1374 | const reg_name = input[1 .. input.len - 1]; | |
| 1375 | const reg = parseRegName(reg_name) orelse | |
| 1376 | return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); | |
| 1377 | const arg = try self.resolveInst(inst.args[i]); | |
| 1378 | try self.genSetReg(inst.base.src, reg, arg); | |
| 1379 | } | |
| 1341 | 1380 | |
| 1342 | if (mem.eql(u8, inst.asm_source, "syscall")) { | |
| 1343 | try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); | |
| 1344 | } else { | |
| 1345 | return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); | |
| 1346 | } | |
| 1381 | if (mem.eql(u8, inst.asm_source, "ecall")) { | |
| 1382 | const full = @bitCast(u32, instructions.CallBreak{ | |
| 1383 | .mode = @enumToInt(instructions.CallBreak.Mode.ecall), | |
| 1384 | }); | |
| 1347 | 1385 | |
| 1348 | if (inst.output) |output| { | |
| 1349 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | |
| 1350 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); | |
| 1351 | } | |
| 1352 | const reg_name = output[2 .. output.len - 1]; | |
| 1353 | const reg = parseRegName(reg_name) orelse | |
| 1354 | return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); | |
| 1355 | return MCValue{ .register = reg }; | |
| 1356 | } else { | |
| 1357 | return MCValue.none; | |
| 1386 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full); | |
| 1387 | } else { | |
| 1388 | return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); | |
| 1389 | } | |
| 1390 | ||
| 1391 | if (inst.output) |output| { | |
| 1392 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | |
| 1393 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); | |
| 1394 | } | |
| 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 | } |
| 1360 | 1436 | |
| ... | ... | @@ -1500,6 +1576,75 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1500 | 1576 | |
| 1501 | 1577 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { |
| 1502 | 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 | 1648 | .x86_64 => switch (mcv) { |
| 1504 | 1649 | .dead => unreachable, |
| 1505 | 1650 | .ptr_stack_offset => unreachable, |
| ... | ... | @@ -1873,7 +2018,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1873 | 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 | } |
| 1878 | 2024 | |
| 1879 | 2025 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| ... | ... | @@ -1915,6 +2061,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1915 | 2061 | usingnamespace switch (arch) { |
| 1916 | 2062 | .i386 => @import("codegen/x86.zig"), |
| 1917 | 2063 | .x86_64 => @import("codegen/x86_64.zig"), |
| 2064 | .riscv64 => @import("codegen/riscv64.zig"), | |
| 1918 | 2065 | else => struct { |
| 1919 | 2066 | pub const Register = enum { |
| 1920 | 2067 | dummy, |
| ... | ... | @@ -1931,6 +2078,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1931 | 2078 | const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } }); |
| 1932 | 2079 | |
| 1933 | 2080 | fn parseRegName(name: []const u8) ?Register { |
| 2081 | if (@hasDecl(Register, "parseRegName")) { | |
| 2082 | return Register.parseRegName(name); | |
| 2083 | } | |
| 1934 | 2084 | return std.meta.stringToEnum(Register, name); |
| 1935 | 2085 | } |
| 1936 | 2086 | |
| ... | ... | @@ -1947,5 +2097,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1947 | 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | |
| 46 | pub 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 | ||
| 53 | pub 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 | ||
| 90 | pub 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 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | const zir = @import("zir.zig"); |
| 6 | 6 | const Package = @import("Package.zig"); |
| 7 | const build_options = @import("build_options"); | |
| 8 | const enable_qemu: bool = build_options.enable_qemu; | |
| 9 | const enable_wine: bool = build_options.enable_wine; | |
| 10 | const enable_wasmtime: bool = build_options.enable_wasmtime; | |
| 11 | const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; | |
| 7 | 12 | |
| 8 | 13 | const cheader = @embedFile("cbe.h"); |
| 9 | 14 | |
| ... | ... | @@ -401,8 +406,6 @@ pub const TestContext = struct { |
| 401 | 406 | const root_node = try progress.start("tests", self.cases.items.len); |
| 402 | 407 | defer root_node.end(); |
| 403 | 408 | |
| 404 | const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{}); | |
| 405 | ||
| 406 | 409 | for (self.cases.items) |case| { |
| 407 | 410 | std.testing.base_allocator_instance.reset(); |
| 408 | 411 | |
| ... | ... | @@ -415,13 +418,19 @@ pub const TestContext = struct { |
| 415 | 418 | progress.initial_delay_ns = 0; |
| 416 | 419 | progress.refresh_rate_ns = 0; |
| 417 | 420 | |
| 418 | const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target); | |
| 419 | try self.runOneCase(std.testing.allocator, &prg_node, case, info.target); | |
| 421 | try self.runOneCase(std.testing.allocator, &prg_node, case); | |
| 420 | 422 | try std.testing.allocator_instance.validate(); |
| 421 | 423 | } |
| 422 | 424 | } |
| 423 | 425 | |
| 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 | 434 | var tmp = std.testing.tmpDir(.{}); |
| 426 | 435 | defer tmp.cleanup(); |
| 427 | 436 | |
| ... | ... | @@ -429,8 +438,7 @@ pub const TestContext = struct { |
| 429 | 438 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); |
| 430 | 439 | defer root_pkg.destroy(); |
| 431 | 440 | |
| 432 | const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null); | |
| 433 | defer allocator.free(bin_name); | |
| 441 | const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null); | |
| 434 | 442 | |
| 435 | 443 | var module = try Module.init(allocator, .{ |
| 436 | 444 | .root_name = "test_case", |
| ... | ... | @@ -485,8 +493,7 @@ pub const TestContext = struct { |
| 485 | 493 | // incremental updates |
| 486 | 494 | var file = try tmp.dir.openFile(bin_name, .{ .read = true }); |
| 487 | 495 | defer file.close(); |
| 488 | var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!"); | |
| 489 | defer allocator.free(out); | |
| 496 | var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!"); | |
| 490 | 497 | |
| 491 | 498 | if (expected_output.len != out.len) { |
| 492 | 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 | 540 | var test_node = update_node.start("assert", null); |
| 534 | 541 | test_node.activate(); |
| 535 | 542 | defer test_node.end(); |
| 536 | var handled_errors = try allocator.alloc(bool, e.len); | |
| 537 | defer allocator.free(handled_errors); | |
| 543 | var handled_errors = try arena.alloc(bool, e.len); | |
| 538 | 544 | for (handled_errors) |*h| { |
| 539 | 545 | h.* = false; |
| 540 | 546 | } |
| ... | ... | @@ -569,10 +575,56 @@ pub const TestContext = struct { |
| 569 | 575 | exec_node.activate(); |
| 570 | 576 | defer exec_node.end(); |
| 571 | 577 | |
| 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 | } | |
| 573 | 626 | |
| 574 | const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name}); | |
| 575 | defer allocator.free(exe_path); | |
| 627 | try module.makeBinFileExecutable(); | |
| 576 | 628 | |
| 577 | 629 | break :x try std.ChildProcess.exec(.{ |
| 578 | 630 | .allocator = allocator, |
src-self-hosted/type.zig+7-2| ... | ... | @@ -510,13 +510,18 @@ pub const Type = extern union { |
| 510 | 510 | .u8, |
| 511 | 511 | .i8, |
| 512 | 512 | .bool, |
| 513 | .array_u8_sentinel_0, | |
| 514 | => return 1, | |
| 515 | ||
| 513 | 516 | .fn_noreturn_no_args, // represents machine code; not a pointer |
| 514 | 517 | .fn_void_no_args, // represents machine code; not a pointer |
| 515 | 518 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer |
| 516 | 519 | .fn_ccc_void_no_args, // represents machine code; not a pointer |
| 517 | 520 | .function, // represents machine code; not a pointer |
| 518 | .array_u8_sentinel_0, | |
| 519 | => return 1, | |
| 521 | => return switch (target.cpu.arch) { | |
| 522 | .riscv64 => 2, | |
| 523 | else => 1, | |
| 524 | }, | |
| 520 | 525 | |
| 521 | 526 | .i16, .u16 => return 2, |
| 522 | 527 | .i32, .u32 => return 4, |
test/stage2/compare_output.zig+41| ... | ... | @@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{ |
| 7 | 7 | .os_tag = .linux, |
| 8 | 8 | }; |
| 9 | 9 | |
| 10 | const linux_riscv64 = std.zig.CrossTarget{ | |
| 11 | .cpu_arch = .riscv64, | |
| 12 | .os_tag = .linux, | |
| 13 | }; | |
| 14 | ||
| 10 | 15 | pub fn addCases(ctx: *TestContext) !void { |
| 11 | 16 | if (std.Target.current.os.tag != .linux or |
| 12 | 17 | std.Target.current.cpu.arch != .x86_64) |
| ... | ... | @@ -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 | } | |
| 121 | 162 | |
| 122 | 163 | { |
| 123 | 164 | var case = ctx.exe("adding numbers at comptime", linux_x64); |