mirror of
https://github.com/ziglang/zig.git
synced 2025-12-29 17:43:17 +00:00
Merge branch 'pfgithub-stage2-testing-Copy-2'
This commit is contained in:
commit
e4eb4396c2
@ -107,6 +107,12 @@ pub fn build(b: *Builder) !void {
|
||||
const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
|
||||
const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
|
||||
|
||||
|
||||
test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
|
||||
test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
|
||||
test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
|
||||
test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
|
||||
|
||||
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
|
||||
test_stage2_step.dependOn(&test_stage2.step);
|
||||
test_step.dependOn(test_stage2_step);
|
||||
|
||||
@ -1857,10 +1857,10 @@ pub const LibExeObjStep = struct {
|
||||
zig_args.append(builder.zig_exe) catch unreachable;
|
||||
|
||||
const cmd = switch (self.kind) {
|
||||
Kind.Lib => "build-lib",
|
||||
Kind.Exe => "build-exe",
|
||||
Kind.Obj => "build-obj",
|
||||
Kind.Test => "test",
|
||||
.Lib => "build-lib",
|
||||
.Exe => "build-exe",
|
||||
.Obj => "build-obj",
|
||||
.Test => "test",
|
||||
};
|
||||
zig_args.append(cmd) catch unreachable;
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@ pub fn generateSymbol(
|
||||
//.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
//.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
//.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
//.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
//.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
//.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
|
||||
//.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 {
|
||||
entry.value = .dead;
|
||||
switch (prev_value) {
|
||||
.register => |reg| {
|
||||
const reg64 = reg.to64();
|
||||
_ = branch.registers.remove(reg64);
|
||||
branch.markRegFree(reg64);
|
||||
const canon_reg = toCanonicalReg(reg);
|
||||
_ = branch.registers.remove(canon_reg);
|
||||
branch.markRegFree(canon_reg);
|
||||
},
|
||||
else => {}, // TODO process stack allocation death
|
||||
}
|
||||
@ -1023,6 +1023,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.i386, .x86_64 => {
|
||||
try self.code.append(0xcc); // int3
|
||||
},
|
||||
.riscv64 => {
|
||||
const full = @bitCast(u32, instructions.CallBreak{
|
||||
.mode = @enumToInt(instructions.CallBreak.Mode.ebreak),
|
||||
});
|
||||
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
|
||||
},
|
||||
else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
return .none;
|
||||
@ -1085,6 +1092,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
|
||||
}
|
||||
},
|
||||
.riscv64 => {
|
||||
if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
|
||||
|
||||
if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
|
||||
if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
|
||||
const func = func_val.func;
|
||||
const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
|
||||
|
||||
try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
|
||||
const jalr = instructions.Jalr{
|
||||
.rd = Register.ra.id(),
|
||||
.rs1 = Register.ra.id(),
|
||||
.offset = 0,
|
||||
};
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
|
||||
} else {
|
||||
return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
|
||||
}
|
||||
} else {
|
||||
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
|
||||
}
|
||||
},
|
||||
else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
|
||||
@ -1133,6 +1165,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
|
||||
try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
|
||||
},
|
||||
.riscv64 => {
|
||||
const jalr = instructions.Jalr{
|
||||
.rd = Register.zero.id(),
|
||||
.rs1 = Register.ra.id(),
|
||||
.offset = 0,
|
||||
};
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
|
||||
},
|
||||
else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
return .unreach;
|
||||
@ -1325,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
|
||||
if (!inst.is_volatile and inst.base.isUnused())
|
||||
return MCValue.dead;
|
||||
if (arch != .x86_64 and arch != .i386) {
|
||||
return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
|
||||
}
|
||||
for (inst.inputs) |input, i| {
|
||||
if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
|
||||
return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
|
||||
}
|
||||
const reg_name = input[1 .. input.len - 1];
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
}
|
||||
switch (arch) {
|
||||
.riscv64 => {
|
||||
for (inst.inputs) |input, i| {
|
||||
if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
|
||||
return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
|
||||
}
|
||||
const reg_name = input[1 .. input.len - 1];
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
}
|
||||
|
||||
if (mem.eql(u8, inst.asm_source, "syscall")) {
|
||||
try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
|
||||
} else {
|
||||
return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
|
||||
}
|
||||
if (mem.eql(u8, inst.asm_source, "ecall")) {
|
||||
const full = @bitCast(u32, instructions.CallBreak{
|
||||
.mode = @enumToInt(instructions.CallBreak.Mode.ecall),
|
||||
});
|
||||
|
||||
if (inst.output) |output| {
|
||||
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
|
||||
return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
|
||||
}
|
||||
const reg_name = output[2 .. output.len - 1];
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
|
||||
return MCValue{ .register = reg };
|
||||
} else {
|
||||
return MCValue.none;
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
|
||||
} else {
|
||||
return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
|
||||
}
|
||||
|
||||
if (inst.output) |output| {
|
||||
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
|
||||
return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
|
||||
}
|
||||
const reg_name = output[2 .. output.len - 1];
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
|
||||
return MCValue{ .register = reg };
|
||||
} else {
|
||||
return MCValue.none;
|
||||
}
|
||||
},
|
||||
.x86_64, .i386 => {
|
||||
for (inst.inputs) |input, i| {
|
||||
if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
|
||||
return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
|
||||
}
|
||||
const reg_name = input[1 .. input.len - 1];
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
}
|
||||
|
||||
if (mem.eql(u8, inst.asm_source, "syscall")) {
|
||||
try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
|
||||
} else {
|
||||
return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
|
||||
}
|
||||
|
||||
if (inst.output) |output| {
|
||||
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
|
||||
return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
|
||||
}
|
||||
const reg_name = output[2 .. output.len - 1];
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
|
||||
return MCValue{ .register = reg };
|
||||
} else {
|
||||
return MCValue.none;
|
||||
}
|
||||
},
|
||||
else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1500,6 +1576,75 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
|
||||
switch (arch) {
|
||||
.riscv64 => switch (mcv) {
|
||||
.dead => unreachable,
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
.unreach, .none => return, // Nothing to do.
|
||||
.undef => {
|
||||
if (!self.wantSafety())
|
||||
return; // The already existing value will do just fine.
|
||||
// Write the debug undefined value.
|
||||
return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
|
||||
},
|
||||
.immediate => |unsigned_x| {
|
||||
const x = @bitCast(i64, unsigned_x);
|
||||
if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
|
||||
const instruction = @bitCast(u32, instructions.Addi{
|
||||
.mode = @enumToInt(instructions.Addi.Mode.addi),
|
||||
.imm = @truncate(i12, x),
|
||||
.rs1 = Register.zero.id(),
|
||||
.rd = reg.id(),
|
||||
});
|
||||
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), instruction);
|
||||
return;
|
||||
}
|
||||
if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
|
||||
const split = @bitCast(packed struct {
|
||||
low12: i12,
|
||||
up20: i20,
|
||||
}, @truncate(i32, x));
|
||||
if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{});
|
||||
|
||||
const lui = @bitCast(u32, instructions.Lui{
|
||||
.imm = split.up20,
|
||||
.rd = reg.id(),
|
||||
});
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui);
|
||||
|
||||
const addi = @bitCast(u32, instructions.Addi{
|
||||
.mode = @enumToInt(instructions.Addi.Mode.addi),
|
||||
.imm = @truncate(i12, split.low12),
|
||||
.rs1 = reg.id(),
|
||||
.rd = reg.id(),
|
||||
});
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi);
|
||||
return;
|
||||
}
|
||||
// li rd, immediate
|
||||
// "Myriad sequences"
|
||||
return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
|
||||
},
|
||||
.memory => |addr| {
|
||||
// The value is in memory at a hard-coded address.
|
||||
// If the type is a pointer, it means the pointer address is at this memory location.
|
||||
try self.genSetReg(src, reg, .{ .immediate = addr });
|
||||
|
||||
const ld = @bitCast(u32, instructions.Load{
|
||||
.mode = @enumToInt(instructions.Load.Mode.ld),
|
||||
.rs1 = reg.id(),
|
||||
.rd = reg.id(),
|
||||
.offset = 0,
|
||||
});
|
||||
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ld);
|
||||
// LOAD imm=[i12 offset = 0], rs1 =
|
||||
|
||||
// return self.fail("TODO implement genSetReg memory for riscv64");
|
||||
},
|
||||
else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),
|
||||
},
|
||||
.x86_64 => switch (mcv) {
|
||||
.dead => unreachable,
|
||||
.ptr_stack_offset => unreachable,
|
||||
@ -1873,7 +2018,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
|
||||
}
|
||||
},
|
||||
else => return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
|
||||
else => if (param_types.len != 0)
|
||||
return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
|
||||
if (ret_ty.zigTypeTag() == .NoReturn) {
|
||||
@ -1915,6 +2061,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
usingnamespace switch (arch) {
|
||||
.i386 => @import("codegen/x86.zig"),
|
||||
.x86_64 => @import("codegen/x86_64.zig"),
|
||||
.riscv64 => @import("codegen/riscv64.zig"),
|
||||
else => struct {
|
||||
pub const Register = enum {
|
||||
dummy,
|
||||
@ -1931,6 +2078,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
|
||||
|
||||
fn parseRegName(name: []const u8) ?Register {
|
||||
if (@hasDecl(Register, "parseRegName")) {
|
||||
return Register.parseRegName(name);
|
||||
}
|
||||
return std.meta.stringToEnum(Register, name);
|
||||
}
|
||||
|
||||
@ -1947,5 +2097,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
else => return reg,
|
||||
}
|
||||
}
|
||||
|
||||
/// For most architectures this does nothing. For x86_64 it resolves any aliased registers
|
||||
/// to the 64-bit wide ones.
|
||||
fn toCanonicalReg(reg: Register) Register {
|
||||
return switch (arch) {
|
||||
.x86_64 => reg.to64(),
|
||||
else => reg,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
92
src-self-hosted/codegen/riscv64.zig
Normal file
92
src-self-hosted/codegen/riscv64.zig
Normal file
@ -0,0 +1,92 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const instructions = struct {
|
||||
pub const CallBreak = packed struct {
|
||||
pub const Mode = packed enum(u12) { ecall, ebreak };
|
||||
opcode: u7 = 0b1110011,
|
||||
unused1: u5 = 0,
|
||||
unused2: u3 = 0,
|
||||
unused3: u5 = 0,
|
||||
mode: u12, //: Mode
|
||||
};
|
||||
// I-type
|
||||
pub const Addi = packed struct {
|
||||
pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
|
||||
opcode: u7 = 0b0010011,
|
||||
rd: u5,
|
||||
mode: u3, //: Mode
|
||||
rs1: u5,
|
||||
imm: i12,
|
||||
};
|
||||
pub const Lui = packed struct {
|
||||
opcode: u7 = 0b0110111,
|
||||
rd: u5,
|
||||
imm: i20,
|
||||
};
|
||||
// I_type
|
||||
pub const Load = packed struct {
|
||||
pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 };
|
||||
opcode: u7 = 0b0000011,
|
||||
rd: u5,
|
||||
mode: u3, //: Mode
|
||||
rs1: u5,
|
||||
offset: i12,
|
||||
};
|
||||
// I-type
|
||||
pub const Jalr = packed struct {
|
||||
opcode: u7 = 0b1100111,
|
||||
rd: u5,
|
||||
mode: u3 = 0,
|
||||
rs1: u5,
|
||||
offset: i12,
|
||||
};
|
||||
};
|
||||
|
||||
// zig fmt: off
|
||||
pub const RawRegister = enum(u8) {
|
||||
x0, x1, x2, x3, x4, x5, x6, x7,
|
||||
x8, x9, x10, x11, x12, x13, x14, x15,
|
||||
x16, x17, x18, x19, x20, x21, x22, x23,
|
||||
x24, x25, x26, x27, x28, x29, x30, x31,
|
||||
};
|
||||
|
||||
pub const Register = enum(u8) {
|
||||
// 64 bit registers
|
||||
zero, // zero
|
||||
ra, // return address. caller saved
|
||||
sp, // stack pointer. callee saved.
|
||||
gp, // global pointer
|
||||
tp, // thread pointer
|
||||
t0, t1, t2, // temporaries. caller saved.
|
||||
s0, // s0/fp, callee saved.
|
||||
s1, // callee saved.
|
||||
a0, a1, // fn args/return values. caller saved.
|
||||
a2, a3, a4, a5, a6, a7, // fn args. caller saved.
|
||||
s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
|
||||
t3, t4, t5, t6, // caller saved
|
||||
|
||||
pub fn parseRegName(name: []const u8) ?Register {
|
||||
if(std.meta.stringToEnum(Register, name)) |reg| return reg;
|
||||
if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns the register's id.
|
||||
pub fn id(self: @This()) u5 {
|
||||
return @truncate(u5, @enumToInt(self));
|
||||
}
|
||||
|
||||
/// Returns the index into `callee_preserved_regs`.
|
||||
pub fn allocIndex(self: Register) ?u4 {
|
||||
inline for(callee_preserved_regs) |cpreg, i| {
|
||||
if(self == cpreg) return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// zig fmt: on
|
||||
|
||||
pub const callee_preserved_regs = [_]Register{
|
||||
.s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
|
||||
};
|
||||
@ -4,6 +4,11 @@ const Module = @import("Module.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const zir = @import("zir.zig");
|
||||
const Package = @import("Package.zig");
|
||||
const build_options = @import("build_options");
|
||||
const enable_qemu: bool = build_options.enable_qemu;
|
||||
const enable_wine: bool = build_options.enable_wine;
|
||||
const enable_wasmtime: bool = build_options.enable_wasmtime;
|
||||
const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
|
||||
|
||||
const cheader = @embedFile("cbe.h");
|
||||
|
||||
@ -401,8 +406,6 @@ pub const TestContext = struct {
|
||||
const root_node = try progress.start("tests", self.cases.items.len);
|
||||
defer root_node.end();
|
||||
|
||||
const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
|
||||
|
||||
for (self.cases.items) |case| {
|
||||
std.testing.base_allocator_instance.reset();
|
||||
|
||||
@ -415,13 +418,19 @@ pub const TestContext = struct {
|
||||
progress.initial_delay_ns = 0;
|
||||
progress.refresh_rate_ns = 0;
|
||||
|
||||
const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
|
||||
try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
|
||||
try self.runOneCase(std.testing.allocator, &prg_node, case);
|
||||
try std.testing.allocator_instance.validate();
|
||||
}
|
||||
}
|
||||
|
||||
fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {
|
||||
fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
|
||||
const target_info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
|
||||
const target = target_info.target;
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
@ -429,8 +438,7 @@ pub const TestContext = struct {
|
||||
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
|
||||
defer root_pkg.destroy();
|
||||
|
||||
const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);
|
||||
defer allocator.free(bin_name);
|
||||
const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
|
||||
|
||||
var module = try Module.init(allocator, .{
|
||||
.root_name = "test_case",
|
||||
@ -485,8 +493,7 @@ pub const TestContext = struct {
|
||||
// incremental updates
|
||||
var file = try tmp.dir.openFile(bin_name, .{ .read = true });
|
||||
defer file.close();
|
||||
var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
|
||||
defer allocator.free(out);
|
||||
var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
|
||||
|
||||
if (expected_output.len != out.len) {
|
||||
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 {
|
||||
var test_node = update_node.start("assert", null);
|
||||
test_node.activate();
|
||||
defer test_node.end();
|
||||
var handled_errors = try allocator.alloc(bool, e.len);
|
||||
defer allocator.free(handled_errors);
|
||||
var handled_errors = try arena.alloc(bool, e.len);
|
||||
for (handled_errors) |*h| {
|
||||
h.* = false;
|
||||
}
|
||||
@ -569,10 +575,56 @@ pub const TestContext = struct {
|
||||
exec_node.activate();
|
||||
defer exec_node.end();
|
||||
|
||||
try module.makeBinFileExecutable();
|
||||
var argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
|
||||
defer allocator.free(exe_path);
|
||||
const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
|
||||
|
||||
switch (case.target.getExternalExecutor()) {
|
||||
.native => try argv.append(exe_path),
|
||||
.unavailable => return, // No executor available; pass test.
|
||||
|
||||
.qemu => |qemu_bin_name| {
|
||||
if (enable_qemu) qemu: {
|
||||
// TODO Ability for test cases to specify whether to link libc.
|
||||
const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;
|
||||
const glibc_dir_arg = if (need_cross_glibc)
|
||||
glibc_multi_install_dir orelse break :qemu
|
||||
else
|
||||
null;
|
||||
try argv.append(qemu_bin_name);
|
||||
if (glibc_dir_arg) |dir| {
|
||||
const linux_triple = try target.linuxTriple(arena);
|
||||
const full_dir = try std.fs.path.join(arena, &[_][]const u8{
|
||||
dir,
|
||||
linux_triple,
|
||||
});
|
||||
|
||||
try argv.append("-L");
|
||||
try argv.append(full_dir);
|
||||
}
|
||||
try argv.append(exe_path);
|
||||
}
|
||||
return; // QEMU not available; pass test.
|
||||
},
|
||||
|
||||
.wine => |wine_bin_name| if (enable_wine) {
|
||||
try argv.append(wine_bin_name);
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // Wine not available; pass test.
|
||||
},
|
||||
|
||||
.wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
|
||||
try argv.append(wasmtime_bin_name);
|
||||
try argv.append("--dir=.");
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // wasmtime not available; pass test.
|
||||
},
|
||||
}
|
||||
|
||||
try module.makeBinFileExecutable();
|
||||
|
||||
break :x try std.ChildProcess.exec(.{
|
||||
.allocator = allocator,
|
||||
|
||||
@ -510,13 +510,18 @@ pub const Type = extern union {
|
||||
.u8,
|
||||
.i8,
|
||||
.bool,
|
||||
.array_u8_sentinel_0,
|
||||
=> return 1,
|
||||
|
||||
.fn_noreturn_no_args, // represents machine code; not a pointer
|
||||
.fn_void_no_args, // represents machine code; not a pointer
|
||||
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
|
||||
.fn_ccc_void_no_args, // represents machine code; not a pointer
|
||||
.function, // represents machine code; not a pointer
|
||||
.array_u8_sentinel_0,
|
||||
=> return 1,
|
||||
=> return switch (target.cpu.arch) {
|
||||
.riscv64 => 2,
|
||||
else => 1,
|
||||
},
|
||||
|
||||
.i16, .u16 => return 2,
|
||||
.i32, .u32 => return 4,
|
||||
|
||||
@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const linux_riscv64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .riscv64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
if (std.Target.current.os.tag != .linux or
|
||||
std.Target.current.cpu.arch != .x86_64)
|
||||
@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("hello world", linux_riscv64);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (64),
|
||||
\\ [arg1] "{a0}" (1),
|
||||
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{a2}" ("Hello, World!\n".len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (94),
|
||||
\\ [arg1] "{a0}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at comptime", linux_x64);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user