Merge pull request #20807 from Rexicon226/riscv

riscv: more backend progress
This commit is contained in:
Jakub Konka
2024-07-28 11:34:23 +02:00
committed by GitHub
64 changed files with 3483 additions and 3246 deletions
+6 -3
View File
@@ -535,11 +535,14 @@ set(ZIG_STAGE2_SOURCES
src/arch/arm/Mir.zig
src/arch/arm/abi.zig
src/arch/arm/bits.zig
src/arch/riscv64/CodeGen.zig
src/arch/riscv64/Emit.zig
src/arch/riscv64/Mir.zig
src/arch/riscv64/abi.zig
src/arch/riscv64/bits.zig
src/arch/riscv64/CodeGen.zig
src/arch/riscv64/Emit.zig
src/arch/riscv64/encoding.zig
src/arch/riscv64/Lower.zig
src/arch/riscv64/Mir.zig
src/arch/riscv64/mnem.zig
src/arch/sparc64/CodeGen.zig
src/arch/sparc64/Emit.zig
src/arch/sparc64/Mir.zig
+8 -7
View File
@@ -282,11 +282,13 @@ pub fn mainSimple() anyerror!void {
const stderr = if (comptime enable_print) std.io.getStdErr() else {};
for (builtin.test_functions) |test_fn| {
if (enable_print) {
stderr.writeAll(test_fn.name) catch {};
stderr.writeAll("... ") catch {};
}
test_fn.func() catch |err| {
if (test_fn.func()) |_| {
if (enable_print) {
stderr.writeAll(test_fn.name) catch {};
stderr.writeAll("... ") catch {};
stderr.writeAll("PASS\n") catch {};
}
} else |err| if (enable_print) {
if (enable_print) {
stderr.writeAll(test_fn.name) catch {};
stderr.writeAll("... ") catch {};
@@ -300,8 +302,7 @@ pub fn mainSimple() anyerror!void {
if (enable_print) stderr.writeAll("SKIP\n") catch {};
skipped += 1;
continue;
};
if (enable_print) stderr.writeAll("PASS\n") catch {};
}
passed += 1;
}
if (enable_print and print_summary) {
+1 -1
View File
@@ -385,7 +385,7 @@ pub fn yield() YieldError!void {
}
/// State to synchronize detachment of spawner thread to spawned thread
const Completion = std.atomic.Value(enum(u8) {
const Completion = std.atomic.Value(enum(if (builtin.zig_backend == .stage2_riscv64) u32 else u8) {
running,
detached,
completed,
+2 -8
View File
@@ -775,14 +775,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
}
if (builtin.zig_backend == .stage2_riscv64) {
asm volatile ("ecall"
:
: [number] "{a7}" (64),
[arg1] "{a0}" (1),
[arg2] "{a1}" (@intFromPtr(msg.ptr)),
[arg3] "{a2}" (msg.len),
: "memory"
);
std.debug.print("panic: {s}\n", .{msg});
@breakpoint();
std.posix.exit(127);
}
+5 -42
View File
@@ -20,8 +20,7 @@ pub const simplified_logic =
builtin.zig_backend == .stage2_arm or
builtin.zig_backend == .stage2_sparc64 or
builtin.cpu.arch == .spirv32 or
builtin.cpu.arch == .spirv64 or
builtin.zig_backend == .stage2_riscv64;
builtin.cpu.arch == .spirv64;
comptime {
// No matter what, we import the root file, so that any export, test, comptime
@@ -41,10 +40,6 @@ comptime {
} else if (builtin.os.tag == .opencl) {
if (@hasDecl(root, "main"))
@export(spirvMain2, .{ .name = "main" });
} else if (native_arch.isRISCV()) {
if (!@hasDecl(root, "_start")) {
@export(riscv_start, .{ .name = "_start" });
}
} else {
if (!@hasDecl(root, "_start")) {
@export(_start2, .{ .name = "_start" });
@@ -206,42 +201,6 @@ fn wasi_start() callconv(.C) void {
}
}
fn riscv_start() callconv(.C) noreturn {
std.process.exit(switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
.NoReturn => root.main(),
.Void => ret: {
root.main();
break :ret 0;
},
.Int => |info| ret: {
if (info.bits != 8 or info.signedness == .signed) {
@compileError(bad_main_ret);
}
break :ret root.main();
},
.ErrorUnion => ret: {
const result = root.main() catch {
const stderr = std.io.getStdErr().writer();
stderr.writeAll("failed with error\n") catch {
@panic("failed to print when main returned error");
};
break :ret 1;
};
switch (@typeInfo(@TypeOf(result))) {
.Void => break :ret 0,
.Int => |info| {
if (info.bits != 8 or info.signedness == .signed) {
@compileError(bad_main_ret);
}
return result;
},
else => @compileError(bad_main_ret),
}
},
else => @compileError(bad_main_ret),
});
}
fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize {
uefi.handle = handle;
uefi.system_table = system_table;
@@ -563,6 +522,10 @@ pub inline fn callMain() u8 {
if (@typeInfo(ReturnType) != .ErrorUnion) @compileError(bad_main_ret);
const result = root.main() catch |err| {
if (builtin.zig_backend == .stage2_riscv64) {
std.debug.print("error: failed with error\n", .{});
return 1;
}
std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
+1
View File
@@ -6334,6 +6334,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
}
return switch (target_util.zigBackend(target, use_llvm)) {
.stage2_llvm => true,
.stage2_riscv64 => true,
.stage2_x86_64 => if (target.ofmt == .elf or target.ofmt == .macho) true else build_options.have_llvm,
else => build_options.have_llvm,
};
+1905 -885
View File
@@ -37,6 +37,10 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
const bits = @import("bits.zig");
const abi = @import("abi.zig");
const Lower = @import("Lower.zig");
const mnem_import = @import("mnem.zig");
const Mnemonic = mnem_import.Mnemonic;
const Pseudo = mnem_import.Pseudo;
const encoding = @import("encoding.zig");
const Register = bits.Register;
const CSR = bits.CSR;
@@ -45,19 +49,18 @@ const Memory = bits.Memory;
const FrameIndex = bits.FrameIndex;
const RegisterManager = abi.RegisterManager;
const RegisterLock = RegisterManager.RegisterLock;
const Instruction = encoding.Instruction;
const InnerError = CodeGenError || error{OutOfRegisters};
pt: Zcu.PerThread,
air: Air,
liveness: Liveness,
zcu: *Zcu,
bin_file: *link.File,
gpa: Allocator,
mod: *Package.Module,
target: *const std.Target,
func_index: InternPool.Index,
debug_output: DebugInfoOutput,
err_msg: ?*ErrorMsg,
args: []MCValue,
@@ -67,7 +70,8 @@ arg_index: usize,
src_loc: Zcu.LazySrcLoc,
mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
mir_extra: std.ArrayListUnmanaged(u32) = .{},
owner: Owner,
/// Byte offset within the source file of the ending curly.
end_di_line: u32,
@@ -113,6 +117,34 @@ const SymbolOffset = struct { sym: u32, off: i32 = 0 };
const RegisterOffset = struct { reg: Register, off: i32 = 0 };
pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
const Owner = union(enum) {
func_index: InternPool.Index,
lazy_sym: link.File.LazySymbol,
fn getDecl(owner: Owner, zcu: *Zcu) InternPool.DeclIndex {
return switch (owner) {
.func_index => |func_index| zcu.funcOwnerDeclIndex(func_index),
.lazy_sym => |lazy_sym| lazy_sym.ty.getOwnerDecl(zcu),
};
}
fn getSymbolIndex(owner: Owner, func: *Func) !u32 {
const pt = func.pt;
switch (owner) {
.func_index => |func_index| {
const decl_index = func.pt.zcu.funcOwnerDeclIndex(func_index);
const elf_file = func.bin_file.cast(link.File.Elf).?;
return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
},
.lazy_sym => |lazy_sym| {
const elf_file = func.bin_file.cast(link.File.Elf).?;
return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
func.fail("{s} creating lazy symbol", .{@errorName(err)});
},
}
}
};
const MCValue = union(enum) {
/// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
/// TODO Look into deleting this tag and using `dead` instead, since every use
@@ -131,8 +163,12 @@ const MCValue = union(enum) {
immediate: u64,
/// The value doesn't exist in memory yet.
load_symbol: SymbolOffset,
/// A TLV value.
load_tlv: u32,
/// The address of the memory location not-yet-allocated by the linker.
lea_symbol: SymbolOffset,
/// The address of a TLV value.
lea_tlv: u32,
/// The value is in a target-specific register.
register: Register,
/// The value is split across two registers
@@ -189,6 +225,7 @@ const MCValue = union(enum) {
.lea_frame,
.undef,
.lea_symbol,
.lea_tlv,
.air_ref,
.reserved_frame,
=> false,
@@ -198,6 +235,7 @@ const MCValue = union(enum) {
.register_offset,
.load_frame,
.load_symbol,
.load_tlv,
.indirect,
=> true,
};
@@ -216,10 +254,12 @@ const MCValue = union(enum) {
.undef,
.air_ref,
.lea_symbol,
.lea_tlv,
.reserved_frame,
=> unreachable, // not in memory
.load_symbol => |sym_off| .{ .lea_symbol = sym_off },
.load_tlv => |sym| .{ .lea_tlv = sym },
.memory => |addr| .{ .immediate = addr },
.load_frame => |off| .{ .lea_frame = off },
.indirect => |reg_off| switch (reg_off.off) {
@@ -238,17 +278,19 @@ const MCValue = union(enum) {
.indirect,
.undef,
.air_ref,
.load_frame,
.register_pair,
.load_frame,
.load_symbol,
.load_tlv,
.reserved_frame,
=> unreachable, // not a pointer
.immediate => |addr| .{ .memory = addr },
.lea_frame => |off| .{ .load_frame = off },
.register => |reg| .{ .indirect = .{ .reg = reg } },
.register_offset => |reg_off| .{ .indirect = reg_off },
.lea_frame => |off| .{ .load_frame = off },
.lea_symbol => |sym_off| .{ .load_symbol = sym_off },
.lea_tlv => |sym| .{ .load_tlv = sym },
};
}
@@ -264,13 +306,15 @@ const MCValue = union(enum) {
.register_pair,
.memory,
.indirect,
.load_frame,
.load_symbol,
.lea_symbol,
.lea_tlv,
.load_tlv,
=> switch (off) {
0 => mcv,
else => unreachable, // not offsettable
else => unreachable,
},
.load_frame => |frame| .{ .load_frame = .{ .index = frame.index, .off = frame.off + off } },
.immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) },
.register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } },
.register_offset => |reg_off| .{ .register_offset = .{ .reg = reg_off.reg, .off = reg_off.off + off } },
@@ -323,6 +367,8 @@ const InstTracking = struct {
.memory,
.load_frame,
.lea_frame,
.load_tlv,
.lea_tlv,
.load_symbol,
.lea_symbol,
=> result,
@@ -378,6 +424,8 @@ const InstTracking = struct {
.lea_frame,
.load_symbol,
.lea_symbol,
.load_tlv,
.lea_tlv,
=> inst_tracking.long,
.dead,
.register,
@@ -406,6 +454,8 @@ const InstTracking = struct {
.lea_frame,
.load_symbol,
.lea_symbol,
.load_tlv,
.lea_tlv,
=> assert(std.meta.eql(inst_tracking.long, target.long)),
.load_frame,
.reserved_frame,
@@ -737,12 +787,11 @@ pub fn generate(
.air = air,
.pt = pt,
.mod = mod,
.zcu = zcu,
.bin_file = bin_file,
.liveness = liveness,
.target = target,
.func_index = func_index,
.debug_output = debug_output,
.owner = .{ .func_index = func_index },
.err_msg = null,
.args = undefined, // populated after `resolveCallingConventionValues`
.ret_mcv = undefined, // populated after `resolveCallingConventionValues`
@@ -767,7 +816,6 @@ pub fn generate(
function.const_tracking.deinit(gpa);
function.exitlude_jump_relocs.deinit(gpa);
function.mir_instructions.deinit(gpa);
function.mir_extra.deinit(gpa);
}
wip_mir_log.debug("{}:", .{function.fmtDecl(func.owner_decl)});
@@ -799,11 +847,11 @@ pub fn generate(
function.args = call_info.args;
function.ret_mcv = call_info.return_value;
function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), FrameAlloc.init(.{
.size = Type.usize.abiSize(pt),
.alignment = Type.usize.abiAlignment(pt).min(call_info.stack_align),
.size = Type.u64.abiSize(pt),
.alignment = Type.u64.abiAlignment(pt).min(call_info.stack_align),
}));
function.frame_allocs.set(@intFromEnum(FrameIndex.base_ptr), FrameAlloc.init(.{
.size = Type.usize.abiSize(pt),
.size = Type.u64.abiSize(pt),
.alignment = Alignment.min(
call_info.stack_align,
Alignment.fromNonzeroByteUnits(function.target.stackAlignment()),
@@ -815,7 +863,7 @@ pub fn generate(
}));
function.frame_allocs.set(@intFromEnum(FrameIndex.spill_frame), FrameAlloc.init(.{
.size = 0,
.alignment = Type.usize.abiAlignment(pt),
.alignment = Type.u64.abiAlignment(pt),
}));
function.gen() catch |err| switch (err) {
@@ -828,7 +876,6 @@ pub fn generate(
var mir: Mir = .{
.instructions = function.mir_instructions.toOwnedSlice(),
.extra = try function.mir_extra.toOwnedSlice(gpa),
.frame_locs = function.frame_locs.toOwnedSlice(),
};
defer mir.deinit(gpa);
@@ -878,6 +925,102 @@ pub fn generate(
}
}
pub fn generateLazy(
bin_file: *link.File,
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
lazy_sym: link.File.LazySymbol,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
) CodeGenError!Result {
const comp = bin_file.comp;
const gpa = comp.gpa;
const mod = comp.root_mod;
var function: Func = .{
.gpa = gpa,
.air = undefined,
.pt = pt,
.mod = mod,
.bin_file = bin_file,
.liveness = undefined,
.target = &mod.resolved_target.result,
.debug_output = debug_output,
.owner = .{ .lazy_sym = lazy_sym },
.err_msg = null,
.args = undefined, // populated after `resolveCallingConventionValues`
.ret_mcv = undefined, // populated after `resolveCallingConventionValues`
.fn_type = undefined,
.arg_index = 0,
.branch_stack = undefined,
.src_loc = src_loc,
.end_di_line = undefined,
.end_di_column = undefined,
.scope_generation = 0,
.avl = null,
.vtype = null,
};
defer function.mir_instructions.deinit(gpa);
function.genLazy(lazy_sym) catch |err| switch (err) {
error.CodegenFail => return Result{ .fail = function.err_msg.? },
error.OutOfRegisters => return Result{
.fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
},
else => |e| return e,
};
var mir: Mir = .{
.instructions = function.mir_instructions.toOwnedSlice(),
.frame_locs = function.frame_locs.toOwnedSlice(),
};
defer mir.deinit(gpa);
var emit: Emit = .{
.lower = .{
.pt = pt,
.allocator = gpa,
.mir = mir,
.cc = .Unspecified,
.src_loc = src_loc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
.pic = mod.pic,
},
.bin_file = bin_file,
.debug_output = debug_output,
.code = code,
.prev_di_pc = undefined, // no debug info yet
.prev_di_line = undefined, // no debug info yet
.prev_di_column = undefined, // no debug info yet
};
defer emit.deinit();
emit.emitMir() catch |err| switch (err) {
error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },
error.InvalidInstruction => |e| {
const msg = switch (e) {
error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
};
return Result{
.fail = try ErrorMsg.create(
gpa,
src_loc,
"{s} This is a bug in the Zig compiler.",
.{msg},
),
};
},
else => |e| return e,
};
if (function.err_msg) |em| {
return Result{ .fail = em };
} else {
return Result.ok;
}
}
const FormatWipMirData = struct {
func: *Func,
inst: Mir.Inst.Index,
@@ -895,7 +1038,6 @@ fn formatWipMir(
.allocator = data.func.gpa,
.mir = .{
.instructions = data.func.mir_instructions.slice(),
.extra = data.func.mir_extra.items,
.frame_locs = data.func.frame_locs.slice(),
},
.cc = .Unspecified,
@@ -946,7 +1088,7 @@ fn formatDecl(
}
fn fmtDecl(func: *Func, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) {
return .{ .data = .{
.zcu = func.zcu,
.zcu = func.pt.zcu,
.decl_index = decl_index,
} };
}
@@ -993,7 +1135,7 @@ fn addInst(func: *Func, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
try func.mir_instructions.ensureUnusedCapacity(gpa, 1);
const result_index: Mir.Inst.Index = @intCast(func.mir_instructions.len);
func.mir_instructions.appendAssumeCapacity(inst);
if (inst.tag != .pseudo or switch (inst.ops) {
if (switch (inst.tag) {
else => true,
.pseudo_dbg_prologue_end,
.pseudo_dbg_line_column,
@@ -1004,65 +1146,26 @@ fn addInst(func: *Func, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
return result_index;
}
fn addNop(func: *Func) error{OutOfMemory}!Mir.Inst.Index {
fn addPseudo(func: *Func, mnem: Mnemonic) error{OutOfMemory}!Mir.Inst.Index {
return func.addInst(.{
.tag = .nop,
.ops = .none,
.data = undefined,
.tag = mnem,
.data = .none,
});
}
fn addPseudoNone(func: *Func, ops: Mir.Inst.Ops) !void {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = ops,
.data = undefined,
});
}
fn addPseudo(func: *Func, ops: Mir.Inst.Ops) !Mir.Inst.Index {
return func.addInst(.{
.tag = .pseudo,
.ops = ops,
.data = undefined,
});
}
pub fn addExtra(func: *Func, extra: anytype) Allocator.Error!u32 {
const fields = std.meta.fields(@TypeOf(extra));
try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len);
return func.addExtraAssumeCapacity(extra);
}
pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 {
const fields = std.meta.fields(@TypeOf(extra));
const result: u32 = @intCast(func.mir_extra.items.len);
inline for (fields) |field| {
func.mir_extra.appendAssumeCapacity(switch (field.type) {
u32 => @field(extra, field.name),
i32 => @bitCast(@field(extra, field.name)),
else => @compileError("bad field type"),
});
}
return result;
}
/// Returns a temporary register that contains the value of the `reg` csr.
///
/// Caller's duty to lock the return register is needed.
fn getCsr(func: *Func, csr: CSR) !Register {
assert(func.hasFeature(.zicsr));
const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.usize));
const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.u64));
_ = try func.addInst(.{
.tag = .csrrs,
.ops = .csr,
.data = .{
.csr = .{
.csr = csr,
.rd = dst_reg,
.rs1 = .x0,
},
},
.data = .{ .csr = .{
.csr = csr,
.rd = dst_reg,
.rs1 = .x0,
} },
});
return dst_reg;
}
@@ -1081,7 +1184,6 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void {
const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options));
_ = try func.addInst(.{
.tag = .vsetvli,
.ops = .rri,
.data = .{ .i_type = .{
.rd = dst_reg,
.rs1 = .zero,
@@ -1094,7 +1196,6 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void {
const options_int: u12 = (~@as(u12, 0) << 10) | @as(u8, @bitCast(options));
_ = try func.addInst(.{
.tag = .vsetivli,
.ops = .rri,
.data = .{
.i_type = .{
.rd = dst_reg,
@@ -1105,10 +1206,9 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void {
});
} else {
const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options));
const temp_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = avl });
const temp_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = avl });
_ = try func.addInst(.{
.tag = .vsetvli,
.ops = .rri,
.data = .{ .i_type = .{
.rd = dst_reg,
.rs1 = temp_reg,
@@ -1125,6 +1225,7 @@ const required_features = [_]Target.riscv.Feature{
.a,
.zicsr,
.v,
.zbb,
};
fn gen(func: *Func) !void {
@@ -1142,7 +1243,7 @@ fn gen(func: *Func) !void {
}
if (fn_info.cc != .Naked) {
try func.addPseudoNone(.pseudo_dbg_prologue_end);
_ = try func.addPseudo(.pseudo_dbg_prologue_end);
const backpatch_stack_alloc = try func.addPseudo(.pseudo_dead);
const backpatch_ra_spill = try func.addPseudo(.pseudo_dead);
@@ -1156,11 +1257,11 @@ fn gen(func: *Func) !void {
// The address where to store the return value for the caller is in a
// register which the callee is free to clobber. Therefore, we purposely
// spill it to stack immediately.
const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(Type.usize, pt));
const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(Type.u64, pt));
try func.genSetMem(
.{ .frame = frame_index },
0,
Type.usize,
Type.u64,
func.ret_mcv.long.address().offset(-func.ret_mcv.short.indirect.off),
);
func.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } };
@@ -1172,11 +1273,11 @@ fn gen(func: *Func) !void {
try func.genBody(func.air.getMainBody());
for (func.exitlude_jump_relocs.items) |jmp_reloc| {
func.mir_instructions.items(.data)[jmp_reloc].inst =
func.mir_instructions.items(.data)[jmp_reloc].j_type.inst =
@intCast(func.mir_instructions.len);
}
try func.addPseudoNone(.pseudo_dbg_epilogue_begin);
_ = try func.addPseudo(.pseudo_dbg_epilogue_begin);
const backpatch_restore_callee_preserved_regs = try func.addPseudo(.pseudo_dead);
const backpatch_ra_restore = try func.addPseudo(.pseudo_dead);
@@ -1186,14 +1287,11 @@ fn gen(func: *Func) !void {
// ret
_ = try func.addInst(.{
.tag = .jalr,
.ops = .rri,
.data = .{
.i_type = .{
.rd = .zero,
.rs1 = .ra,
.imm12 = Immediate.s(0),
},
},
.data = .{ .i_type = .{
.rd = .zero,
.rs1 = .ra,
.imm12 = Immediate.s(0),
} },
});
const frame_layout = try func.computeFrameLayout();
@@ -1201,7 +1299,6 @@ fn gen(func: *Func) !void {
func.mir_instructions.set(backpatch_stack_alloc, .{
.tag = .addi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = .sp,
.rs1 = .sp,
@@ -1209,8 +1306,7 @@ fn gen(func: *Func) !void {
} },
});
func.mir_instructions.set(backpatch_ra_spill, .{
.tag = .pseudo,
.ops = .pseudo_store_rm,
.tag = .pseudo_store_rm,
.data = .{ .rm = .{
.r = .ra,
.m = .{
@@ -1220,8 +1316,7 @@ fn gen(func: *Func) !void {
} },
});
func.mir_instructions.set(backpatch_ra_restore, .{
.tag = .pseudo,
.ops = .pseudo_load_rm,
.tag = .pseudo_load_rm,
.data = .{ .rm = .{
.r = .ra,
.m = .{
@@ -1231,8 +1326,7 @@ fn gen(func: *Func) !void {
} },
});
func.mir_instructions.set(backpatch_fp_spill, .{
.tag = .pseudo,
.ops = .pseudo_store_rm,
.tag = .pseudo_store_rm,
.data = .{ .rm = .{
.r = .s0,
.m = .{
@@ -1242,8 +1336,7 @@ fn gen(func: *Func) !void {
} },
});
func.mir_instructions.set(backpatch_fp_restore, .{
.tag = .pseudo,
.ops = .pseudo_load_rm,
.tag = .pseudo_load_rm,
.data = .{ .rm = .{
.r = .s0,
.m = .{
@@ -1254,7 +1347,6 @@ fn gen(func: *Func) !void {
});
func.mir_instructions.set(backpatch_fp_add, .{
.tag = .addi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = .s0,
.rs1 = .sp,
@@ -1263,7 +1355,6 @@ fn gen(func: *Func) !void {
});
func.mir_instructions.set(backpatch_stack_alloc_restore, .{
.tag = .addi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = .sp,
.rs1 = .sp,
@@ -1273,27 +1364,24 @@ fn gen(func: *Func) !void {
if (need_save_reg) {
func.mir_instructions.set(backpatch_spill_callee_preserved_regs, .{
.tag = .pseudo,
.ops = .pseudo_spill_regs,
.tag = .pseudo_spill_regs,
.data = .{ .reg_list = frame_layout.save_reg_list },
});
func.mir_instructions.set(backpatch_restore_callee_preserved_regs, .{
.tag = .pseudo,
.ops = .pseudo_restore_regs,
.tag = .pseudo_restore_regs,
.data = .{ .reg_list = frame_layout.save_reg_list },
});
}
} else {
try func.addPseudoNone(.pseudo_dbg_prologue_end);
_ = try func.addPseudo(.pseudo_dbg_prologue_end);
try func.genBody(func.air.getMainBody());
try func.addPseudoNone(.pseudo_dbg_epilogue_begin);
_ = try func.addPseudo(.pseudo_dbg_epilogue_begin);
}
// Drop them off at the rbrace.
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_dbg_line_column,
.tag = .pseudo_dbg_line_column,
.data = .{ .pseudo_dbg_line_column = .{
.line = func.end_di_line,
.column = func.end_di_column,
@@ -1301,6 +1389,101 @@ fn gen(func: *Func) !void {
});
}
fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void {
const pt = func.pt;
const mod = pt.zcu;
const ip = &mod.intern_pool;
switch (lazy_sym.ty.zigTypeTag(mod)) {
.Enum => {
const enum_ty = lazy_sym.ty;
wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(pt)});
const param_regs = abi.Registers.Integer.function_arg_regs;
const ret_reg = param_regs[0];
const enum_mcv: MCValue = .{ .register = param_regs[1] };
const exitlude_jump_relocs = try func.gpa.alloc(Mir.Inst.Index, enum_ty.enumFieldCount(mod));
defer func.gpa.free(exitlude_jump_relocs);
const data_reg, const data_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(data_lock);
const elf_file = func.bin_file.cast(link.File.Elf).?;
const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, .{
.kind = .const_data,
.ty = enum_ty,
}) catch |err|
return func.fail("{s} creating lazy symbol", .{@errorName(err)});
const sym = elf_file.symbol(sym_index);
try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = sym.esym_index } });
const cmp_reg, const cmp_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(cmp_lock);
var data_off: i32 = 0;
const tag_names = enum_ty.enumFields(mod);
for (exitlude_jump_relocs, 0..) |*exitlude_jump_reloc, tag_index| {
const tag_name_len = tag_names.get(ip)[tag_index].length(ip);
const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index));
const tag_mcv = try func.genTypedValue(tag_val);
_ = try func.genBinOp(
.cmp_neq,
enum_mcv,
enum_ty,
tag_mcv,
enum_ty,
cmp_reg,
);
const skip_reloc = try func.condBr(Type.bool, .{ .register = cmp_reg });
try func.genSetMem(
.{ .reg = ret_reg },
0,
Type.u64,
.{ .register_offset = .{ .reg = data_reg, .off = data_off } },
);
try func.genSetMem(
.{ .reg = ret_reg },
8,
Type.u64,
.{ .immediate = tag_name_len },
);
exitlude_jump_reloc.* = try func.addInst(.{
.tag = .pseudo_j,
.data = .{ .j_type = .{
.rd = .zero,
.inst = undefined,
} },
});
func.performReloc(skip_reloc);
data_off += @intCast(tag_name_len + 1);
}
try func.airTrap();
for (exitlude_jump_relocs) |reloc| func.performReloc(reloc);
_ = try func.addInst(.{
.tag = .jalr,
.data = .{ .i_type = .{
.rd = .zero,
.rs1 = .ra,
.imm12 = Immediate.s(0),
} },
});
},
else => return func.fail(
"TODO implement {s} for {}",
.{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(pt) },
),
}
}
fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
const pt = func.pt;
const zcu = pt.zcu;
@@ -1322,9 +1505,12 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.sub,
.sub_wrap,
.add_sat,
.mul,
.mul_wrap,
.div_trunc,
.rem,
.shl, .shl_exact,
.shr, .shr_exact,
@@ -1344,7 +1530,6 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.ptr_add,
.ptr_sub => try func.airPtrArithmetic(inst, tag),
.rem,
.mod,
.div_float,
.div_floor,
@@ -1373,7 +1558,6 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.shl_with_overflow => try func.airShlWithOverflow(inst),
.add_sat => try func.airAddSat(inst),
.sub_sat => try func.airSubSat(inst),
.mul_sat => try func.airMulSat(inst),
.shl_sat => try func.airShlSat(inst),
@@ -1440,8 +1624,8 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.struct_field_val=> try func.airStructFieldVal(inst),
.float_from_int => try func.airFloatFromInt(inst),
.int_from_float => try func.airIntFromFloat(inst),
.cmpxchg_strong => try func.airCmpxchg(inst),
.cmpxchg_weak => try func.airCmpxchg(inst),
.cmpxchg_strong => try func.airCmpxchg(inst, .strong),
.cmpxchg_weak => try func.airCmpxchg(inst, .weak),
.atomic_rmw => try func.airAtomicRmw(inst),
.atomic_load => try func.airAtomicLoad(inst),
.memcpy => try func.airMemcpy(inst),
@@ -1579,7 +1763,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
for (tracking.getRegs()) |reg| {
if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break;
} else return std.debug.panic(
\\%{} takes up these regs: {any}, however these regs {any}, don't use it
\\%{} takes up these regs: {any}, however this regs {any}, don't use it
, .{ tracked_inst, tracking.getRegs(), RegisterManager.regAtTrackedIndex(@intCast(index)) });
}
}
@@ -1657,7 +1841,7 @@ fn finishAir(
}
const FrameLayout = struct {
stack_adjust: u32,
stack_adjust: i12,
save_reg_list: Mir.RegisterList,
};
@@ -1671,10 +1855,7 @@ fn setFrameLoc(
const frame_i = @intFromEnum(frame_index);
if (aligned) {
const alignment: InternPool.Alignment = func.frame_allocs.items(.abi_align)[frame_i];
offset.* = if (math.sign(offset.*) < 0)
-1 * @as(i32, @intCast(alignment.backward(@intCast(@abs(offset.*)))))
else
@intCast(alignment.forward(@intCast(@abs(offset.*))));
offset.* = math.sign(offset.*) * @as(i32, @intCast(alignment.backward(@intCast(@abs(offset.*)))));
}
func.frame_locs.set(frame_i, .{ .base = base, .disp = offset.* });
offset.* += func.frame_allocs.items(.abi_size)[frame_i];
@@ -1717,8 +1898,8 @@ fn computeFrameLayout(func: *Func) !FrameLayout {
}
break :blk i;
};
const saved_reg_size = save_reg_list.size();
const saved_reg_size = save_reg_list.size();
frame_size[@intFromEnum(FrameIndex.spill_frame)] = @intCast(saved_reg_size);
// The total frame size is calculated by the amount of s registers you need to save * 8, as each
@@ -1811,12 +1992,14 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
.signedness = .unsigned,
.bits = @intCast(ty.bitSize(pt)),
};
assert(reg.class() == .int);
const shift = math.cast(u6, 64 - int_info.bits % 64) orelse return;
switch (int_info.signedness) {
.signed => {
_ = try func.addInst(.{
.tag = .slli,
.ops = .rri,
.data = .{
.i_type = .{
.rd = reg,
@@ -1827,7 +2010,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
});
_ = try func.addInst(.{
.tag = .srai,
.ops = .rri,
.data = .{
.i_type = .{
.rd = reg,
@@ -1842,7 +2025,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
if (mask < 256) {
_ = try func.addInst(.{
.tag = .andi,
.ops = .rri,
.data = .{
.i_type = .{
.rd = reg,
@@ -1854,7 +2037,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
} else {
_ = try func.addInst(.{
.tag = .slli,
.ops = .rri,
.data = .{
.i_type = .{
.rd = reg,
@@ -1865,7 +2048,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
});
_ = try func.addInst(.{
.tag = .srli,
.ops = .rri,
.data = .{
.i_type = .{
.rd = reg,
@@ -1879,15 +2062,6 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
}
}
fn symbolIndex(func: *Func) !u32 {
const pt = func.pt;
const zcu = pt.zcu;
const decl_index = zcu.funcOwnerDeclIndex(func.func_index);
const elf_file = func.bin_file.cast(link.File.Elf).?;
const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
return atom_index;
}
fn allocFrameIndex(func: *Func, alloc: FrameAlloc) !FrameIndex {
const frame_allocs_slice = func.frame_allocs.slice();
const frame_size = frame_allocs_slice.items(.abi_size);
@@ -2051,6 +2225,10 @@ pub fn spillInstruction(func: *Func, reg: Register, inst: Air.Inst.Index) !void
try tracking.trackSpill(func, inst);
}
pub fn spillRegisters(func: *Func, comptime registers: []const Register) !void {
inline for (registers) |reg| try func.register_manager.getKnownReg(reg, null);
}
/// Copies a value to a register without tracking the register. The register is not considered
/// allocated. A second call to `copyToTmpRegister` may return the same register.
/// This can have a side effect of spilling instructions to the stack to free up a register.
@@ -2151,11 +2329,16 @@ fn airTrunc(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
if (func.liveness.isUnused(inst))
return func.finishAir(inst, .unreach, .{ ty_op.operand, .none, .none });
// we assume no zeroext in the "Zig ABI", so it's fine to just not truncate it.
const operand = try func.resolveInst(ty_op.operand);
_ = operand;
return func.fail("TODO implement trunc for {}", .{func.target.cpu.arch});
// return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
// we can do it just to be safe, but this shouldn't be needed for no-runtime safety modes
switch (operand) {
.register => |reg| try func.truncateRegister(func.typeOf(ty_op.operand), reg),
else => {},
}
return func.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
}
fn airIntFromBool(func: *Func, inst: Air.Inst.Index) !void {
@@ -2186,8 +2369,7 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void {
switch (ty.zigTypeTag(zcu)) {
.Bool => {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_not,
.tag = .pseudo_not,
.data = .{
.rr = .{
.rs = operand_reg,
@@ -2205,7 +2387,6 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void {
32, 64 => {
_ = try func.addInst(.{
.tag = .xori,
.ops = .rri,
.data = .{
.i_type = .{
.rd = dst_reg,
@@ -2305,10 +2486,7 @@ fn binOp(
80, 128 => true,
else => unreachable,
};
switch (air_tag) {
.rem, .mod => {},
else => if (!type_needs_libcall) break :libcall,
}
if (!type_needs_libcall) break :libcall;
return func.fail("binOp libcall runtime-float ops", .{});
}
@@ -2367,7 +2545,6 @@ fn genBinOp(
const pt = func.pt;
const zcu = pt.zcu;
const bit_size = lhs_ty.bitSize(pt);
assert(bit_size <= 64);
const is_unsigned = lhs_ty.isUnsignedInt(zcu);
@@ -2384,16 +2561,30 @@ fn genBinOp(
.sub_wrap,
.mul,
.mul_wrap,
.rem,
.div_trunc,
=> {
if (!math.isPowerOfTwo(bit_size))
return func.fail(
"TODO: genBinOp {s} non-pow 2, found {}",
.{ @tagName(tag), bit_size },
);
switch (tag) {
.rem,
.div_trunc,
=> {
if (!math.isPowerOfTwo(bit_size)) {
try func.truncateRegister(lhs_ty, lhs_reg);
try func.truncateRegister(rhs_ty, rhs_reg);
}
},
else => {
if (!math.isPowerOfTwo(bit_size))
return func.fail(
"TODO: genBinOp verify {s} non-pow 2, found {}",
.{ @tagName(tag), bit_size },
);
},
}
switch (lhs_ty.zigTypeTag(zcu)) {
.Int => {
const mir_tag: Mir.Inst.Tag = switch (tag) {
const mnem: Mnemonic = switch (tag) {
.add, .add_wrap => switch (bit_size) {
8, 16, 64 => .add,
32 => .addw,
@@ -2409,12 +2600,19 @@ fn genBinOp(
32 => .mulw,
else => unreachable,
},
.rem => switch (bit_size) {
8, 16, 32 => if (is_unsigned) .remuw else .remw,
else => if (is_unsigned) .remu else .rem,
},
.div_trunc => switch (bit_size) {
8, 16, 32 => if (is_unsigned) .divuw else .divw,
else => if (is_unsigned) .divu else .div,
},
else => unreachable,
};
_ = try func.addInst(.{
.tag = mir_tag,
.ops = .rrr,
.tag = mnem,
.data = .{
.r_type = .{
.rd = dst_reg,
@@ -2423,17 +2621,9 @@ fn genBinOp(
},
},
});
// truncate when the instruction is larger than the bit size.
switch (bit_size) {
8, 16 => try func.truncateRegister(lhs_ty, dst_reg),
32 => {}, // addw/subw affects the first 32-bits
64 => {}, // add/sub affects the entire register
else => unreachable,
}
},
.Float => {
const mir_tag: Mir.Inst.Tag = switch (tag) {
const mir_tag: Mnemonic = switch (tag) {
.add => switch (bit_size) {
32 => .fadds,
64 => .faddd,
@@ -2449,12 +2639,11 @@ fn genBinOp(
64 => .fmuld,
else => unreachable,
},
else => unreachable,
else => return func.fail("TODO: genBinOp {s} Float", .{@tagName(tag)}),
};
_ = try func.addInst(.{
.tag = mir_tag,
.ops = .rrr,
.data = .{
.r_type = .{
.rd = dst_reg,
@@ -2470,7 +2659,7 @@ fn genBinOp(
const child_ty = lhs_ty.childType(zcu);
const mir_tag: Mir.Inst.Tag = switch (tag) {
const mir_tag: Mnemonic = switch (tag) {
.add => switch (child_ty.zigTypeTag(zcu)) {
.Int => .vaddvv,
.Float => .vfaddvv,
@@ -2481,6 +2670,11 @@ fn genBinOp(
.Float => .vfsubvv,
else => unreachable,
},
.mul => switch (child_ty.zigTypeTag(zcu)) {
.Int => .vmulvv,
.Float => .vfmulvv,
else => unreachable,
},
else => return func.fail("TODO: genBinOp {s} Vector", .{@tagName(tag)}),
};
@@ -2490,7 +2684,7 @@ fn genBinOp(
16 => .@"16",
32 => .@"32",
64 => .@"64",
else => unreachable,
else => return func.fail("TODO: genBinOp > 64 bit elements, found {d}", .{elem_size}),
},
.vlmul = .m1,
.vma = true,
@@ -2499,7 +2693,6 @@ fn genBinOp(
_ = try func.addInst(.{
.tag = mir_tag,
.ops = .rrr,
.data = .{
.r_type = .{
.rd = dst_reg,
@@ -2513,6 +2706,53 @@ fn genBinOp(
}
},
.add_sat,
=> {
if (bit_size != 64 or !is_unsigned)
return func.fail("TODO: genBinOp ty: {}", .{lhs_ty.fmt(pt)});
const tmp_reg = try func.copyToTmpRegister(rhs_ty, .{ .register = rhs_reg });
const tmp_lock = func.register_manager.lockRegAssumeUnused(tmp_reg);
defer func.register_manager.unlockReg(tmp_lock);
_ = try func.addInst(.{
.tag = .add,
.data = .{ .r_type = .{
.rd = tmp_reg,
.rs1 = rhs_reg,
.rs2 = lhs_reg,
} },
});
_ = try func.addInst(.{
.tag = .sltu,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = tmp_reg,
.rs2 = lhs_reg,
} },
});
// neg dst_reg, dst_reg
_ = try func.addInst(.{
.tag = .sub,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = .zero,
.rs2 = dst_reg,
} },
});
_ = try func.addInst(.{
.tag = .@"or",
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = dst_reg,
.rs2 = tmp_reg,
} },
});
},
.ptr_add,
.ptr_sub,
=> {
@@ -2523,14 +2763,14 @@ fn genBinOp(
// RISC-V has no immediate mul, so we copy the size to a temporary register
const elem_size = lhs_ty.elemType2(zcu).abiSize(pt);
const elem_size_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = elem_size });
const elem_size_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = elem_size });
try func.genBinOp(
.mul,
tmp_mcv,
rhs_ty,
.{ .register = elem_size_reg },
Type.usize,
Type.u64,
tmp_reg,
);
@@ -2541,9 +2781,9 @@ fn genBinOp(
else => unreachable,
},
lhs_mcv,
Type.usize, // we know it's a pointer, so it'll be usize.
Type.u64, // we know it's a pointer, so it'll be usize.
tmp_mcv,
Type.usize,
Type.u64,
dst_reg,
);
},
@@ -2559,7 +2799,6 @@ fn genBinOp(
.bit_or, .bool_or => .@"or",
else => unreachable,
},
.ops = .rrr,
.data = .{
.r_type = .{
.rd = dst_reg,
@@ -2577,68 +2816,22 @@ fn genBinOp(
}
},
.div_trunc,
=> {
if (!math.isPowerOfTwo(bit_size))
return func.fail(
"TODO: genBinOp {s} non-pow 2, found {}",
.{ @tagName(tag), bit_size },
);
const mir_tag: Mir.Inst.Tag = switch (tag) {
.div_trunc => switch (bit_size) {
8, 16, 32 => if (is_unsigned) .divuw else .divw,
64 => if (is_unsigned) .divu else .div,
else => unreachable,
},
else => unreachable,
};
_ = try func.addInst(.{
.tag = mir_tag,
.ops = .rrr,
.data = .{
.r_type = .{
.rd = dst_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
},
},
});
if (!is_unsigned) {
// truncate when the instruction is larger than the bit size.
switch (bit_size) {
8, 16 => try func.truncateRegister(lhs_ty, dst_reg),
32 => {}, // divw affects the first 32-bits
64 => {}, // div affects the entire register
else => unreachable,
}
}
},
.shr,
.shr_exact,
.shl,
.shl_exact,
=> {
if (!math.isPowerOfTwo(bit_size))
return func.fail(
"TODO: genBinOp {s} non-pow 2, found {}",
.{ @tagName(tag), bit_size },
);
// it's important that the shift amount is exact
if (bit_size > 64) return func.fail("TODO: genBinOp shift > 64 bits, {}", .{bit_size});
try func.truncateRegister(rhs_ty, rhs_reg);
const mir_tag: Mir.Inst.Tag = switch (tag) {
const mir_tag: Mnemonic = switch (tag) {
.shl, .shl_exact => switch (bit_size) {
8, 16, 64 => .sll,
1...31, 33...64 => .sll,
32 => .sllw,
else => unreachable,
},
.shr, .shr_exact => switch (bit_size) {
8, 16, 64 => .srl,
1...31, 33...64 => .srl,
32 => .srlw,
else => unreachable,
},
@@ -2647,20 +2840,12 @@ fn genBinOp(
_ = try func.addInst(.{
.tag = mir_tag,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});
switch (bit_size) {
8, 16 => try func.truncateRegister(lhs_ty, dst_reg),
32 => {},
64 => {},
else => unreachable,
}
},
// TODO: move the isel logic out of lower and into here.
@@ -2671,9 +2856,14 @@ fn genBinOp(
.cmp_gt,
.cmp_gte,
=> {
assert(lhs_reg.class() == rhs_reg.class());
if (lhs_reg.class() == .int) {
try func.truncateRegister(lhs_ty, lhs_reg);
try func.truncateRegister(rhs_ty, rhs_reg);
}
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_compare,
.tag = .pseudo_compare,
.data = .{
.compare = .{
.op = switch (tag) {
@@ -2719,60 +2909,60 @@ fn genBinOp(
// a1, s0 was -1, flipping all the bits in a2 and effectively restoring a0. If a0 was greater than or equal to a1,
// s0 was 0, leaving a2 unchanged as a0.
.min, .max => {
const int_info = lhs_ty.intInfo(zcu);
switch (lhs_ty.zigTypeTag(zcu)) {
.Int => {
const int_info = lhs_ty.intInfo(zcu);
const mask_reg, const mask_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(mask_lock);
const mask_reg, const mask_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(mask_lock);
_ = try func.addInst(.{
.tag = if (int_info.signedness == .unsigned) .sltu else .slt,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = mask_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});
_ = try func.addInst(.{
.tag = if (int_info.signedness == .unsigned) .sltu else .slt,
.data = .{ .r_type = .{
.rd = mask_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});
_ = try func.addInst(.{
.tag = .sub,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = mask_reg,
.rs1 = .zero,
.rs2 = mask_reg,
} },
});
_ = try func.addInst(.{
.tag = .sub,
.data = .{ .r_type = .{
.rd = mask_reg,
.rs1 = .zero,
.rs2 = mask_reg,
} },
});
_ = try func.addInst(.{
.tag = .xor,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});
_ = try func.addInst(.{
.tag = .xor,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});
_ = try func.addInst(.{
.tag = .@"and",
.ops = .rrr,
.data = .{ .r_type = .{
.rd = mask_reg,
.rs1 = dst_reg,
.rs2 = mask_reg,
} },
});
_ = try func.addInst(.{
.tag = .@"and",
.data = .{ .r_type = .{
.rd = mask_reg,
.rs1 = dst_reg,
.rs2 = mask_reg,
} },
});
_ = try func.addInst(.{
.tag = .xor,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = if (tag == .min) rhs_reg else lhs_reg,
.rs2 = mask_reg,
} },
});
_ = try func.addInst(.{
.tag = .xor,
.data = .{ .r_type = .{
.rd = dst_reg,
.rs1 = if (tag == .min) rhs_reg else lhs_reg,
.rs2 = mask_reg,
} },
});
},
else => |t| return func.fail("TODO: genBinOp min/max for {s}", .{@tagName(t)}),
}
},
else => return func.fail("TODO: genBinOp {}", .{tag}),
}
@@ -2791,12 +2981,14 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
const rhs_ty = func.typeOf(extra.rhs);
const lhs_ty = func.typeOf(extra.lhs);
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const ty = func.typeOf(extra.lhs);
switch (ty.zigTypeTag(zcu)) {
switch (lhs_ty.zigTypeTag(zcu)) {
.Vector => return func.fail("TODO implement add with overflow for Vector type", .{}),
.Int => {
const int_info = ty.intInfo(zcu);
const int_info = lhs_ty.intInfo(zcu);
const tuple_ty = func.typeOfIndex(inst);
const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false);
@@ -2805,26 +2997,29 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
const add_result = try func.binOp(null, .add, extra.lhs, extra.rhs);
const add_result_reg = try func.copyToTmpRegister(ty, add_result);
const add_result_reg_lock = func.register_manager.lockRegAssumeUnused(add_result_reg);
defer func.register_manager.unlockReg(add_result_reg_lock);
try func.genSetMem(
.{ .frame = offset.index },
offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))),
ty,
lhs_ty,
add_result,
);
const trunc_reg = try func.copyToTmpRegister(lhs_ty, add_result);
const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg);
defer func.register_manager.unlockReg(trunc_reg_lock);
const overflow_reg, const overflow_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(overflow_lock);
// if the result isn't equal after truncating it to the given type,
// an overflow must have happened.
try func.truncateRegister(lhs_ty, trunc_reg);
try func.genBinOp(
.cmp_neq,
.{ .register = add_result_reg },
ty,
.{ .register = add_result_reg },
ty,
add_result,
lhs_ty,
.{ .register = trunc_reg },
rhs_ty,
overflow_reg,
);
@@ -2837,7 +3032,68 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
break :result result_mcv;
} else {
return func.fail("TODO: less than 8 bit or non-pow 2 addition", .{});
const rhs_mcv = try func.resolveInst(extra.rhs);
const lhs_mcv = try func.resolveInst(extra.lhs);
const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs_mcv);
const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs_mcv);
defer {
if (rhs_lock) |lock| func.register_manager.unlockReg(lock);
if (lhs_lock) |lock| func.register_manager.unlockReg(lock);
}
try func.truncateRegister(rhs_ty, rhs_reg);
try func.truncateRegister(lhs_ty, lhs_reg);
const dest_reg, const dest_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(dest_lock);
_ = try func.addInst(.{
.tag = .add,
.data = .{ .r_type = .{
.rs1 = rhs_reg,
.rs2 = lhs_reg,
.rd = dest_reg,
} },
});
try func.truncateRegister(func.typeOfIndex(inst), dest_reg);
const add_result: MCValue = .{ .register = dest_reg };
try func.genSetMem(
.{ .frame = offset.index },
offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))),
lhs_ty,
add_result,
);
const trunc_reg = try func.copyToTmpRegister(lhs_ty, add_result);
const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg);
defer func.register_manager.unlockReg(trunc_reg_lock);
const overflow_reg, const overflow_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(overflow_lock);
// if the result isn't equal after truncating it to the given type,
// an overflow must have happened.
try func.truncateRegister(lhs_ty, trunc_reg);
try func.genBinOp(
.cmp_neq,
add_result,
lhs_ty,
.{ .register = trunc_reg },
rhs_ty,
overflow_reg,
);
try func.genSetMem(
.{ .frame = offset.index },
offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, pt))),
Type.u1,
.{ .register = overflow_reg },
);
break :result result_mcv;
}
},
else => unreachable,
@@ -2890,7 +3146,7 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs);
defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock);
const overflow_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = 0 });
const overflow_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = 0 });
const overflow_lock = func.register_manager.lockRegAssumeUnused(overflow_reg);
defer func.register_manager.unlockReg(overflow_lock);
@@ -2899,7 +3155,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
.unsigned => {
_ = try func.addInst(.{
.tag = .sltu,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = overflow_reg,
.rs1 = lhs_reg,
@@ -2921,7 +3176,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
64 => {
_ = try func.addInst(.{
.tag = .slt,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = overflow_reg,
.rs1 = overflow_reg,
@@ -2931,7 +3185,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
_ = try func.addInst(.{
.tag = .slt,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = rhs_reg,
.rs1 = rhs_reg,
@@ -2941,7 +3194,6 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
_ = try func.addInst(.{
.tag = .xor,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = lhs_reg,
.rs1 = overflow_reg,
@@ -2952,9 +3204,9 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
try func.genBinOp(
.cmp_neq,
.{ .register = overflow_reg },
Type.usize,
Type.u64,
.{ .register = rhs_reg },
Type.usize,
Type.u64,
overflow_reg,
);
@@ -3017,61 +3269,34 @@ fn airMulWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
switch (lhs_ty.zigTypeTag(zcu)) {
else => |x| return func.fail("TODO: airMulWithOverflow {s}", .{@tagName(x)}),
.Int => {
assert(lhs_ty.eql(rhs_ty, zcu));
const int_info = lhs_ty.intInfo(zcu);
switch (int_info.bits) {
1...32 => {
if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
if (int_info.signedness == .unsigned) {
switch (int_info.bits) {
1...8 => {
const max_val = std.math.pow(u16, 2, int_info.bits) - 1;
if (std.debug.runtime_safety) assert(lhs_ty.eql(rhs_ty, zcu));
const add_reg, const add_lock = try func.promoteReg(lhs_ty, lhs);
defer if (add_lock) |lock| func.register_manager.unlockReg(lock);
const trunc_reg = try func.copyToTmpRegister(lhs_ty, .{ .register = dest_reg });
const trunc_reg_lock = func.register_manager.lockRegAssumeUnused(trunc_reg);
defer func.register_manager.unlockReg(trunc_reg_lock);
const overflow_reg, const overflow_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(overflow_lock);
const overflow_reg, const overflow_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(overflow_lock);
_ = try func.addInst(.{
.tag = .andi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = overflow_reg,
.rs1 = add_reg,
.imm12 = Immediate.s(max_val),
} },
});
// if the result isn't equal after truncating it to the given type,
// an overflow must have happened.
try func.truncateRegister(func.typeOf(extra.lhs), trunc_reg);
try func.genBinOp(
.cmp_neq,
.{ .register = dest_reg },
lhs_ty,
.{ .register = trunc_reg },
rhs_ty,
overflow_reg,
);
try func.genBinOp(
.cmp_neq,
.{ .register = overflow_reg },
lhs_ty,
.{ .register = add_reg },
lhs_ty,
overflow_reg,
);
try func.genCopy(
lhs_ty,
result_mcv.offset(overflow_off),
.{ .register = overflow_reg },
);
try func.genCopy(
lhs_ty,
result_mcv.offset(overflow_off),
.{ .register = overflow_reg },
);
break :result result_mcv;
},
else => return func.fail("TODO: airMulWithOverflow check for size {d}", .{int_info.bits}),
}
} else {
return func.fail("TODO: airMulWithOverflow calculate carry for signed addition", .{});
}
} else {
return func.fail("TODO: airMulWithOverflow with < 8 bits or non-pow of 2", .{});
}
},
else => return func.fail("TODO: airMulWithOverflow larger than 32-bit mul", .{}),
}
break :result result_mcv;
},
}
};
@@ -3085,12 +3310,6 @@ fn airShlWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airAddSat(func: *Func, inst: Air.Inst.Index) !void {
const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airAddSat", .{});
return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airSubSat(func: *Func, inst: Air.Inst.Index) !void {
const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airSubSat", .{});
@@ -3300,19 +3519,21 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void {
};
defer if (pl_lock) |lock| func.register_manager.unlockReg(lock);
const opt_mcv = try func.allocRegOrMem(opt_ty, inst, true);
const opt_mcv = try func.allocRegOrMem(opt_ty, inst, false);
try func.genCopy(pl_ty, opt_mcv, pl_mcv);
if (!same_repr) {
const pl_abi_size: i32 = @intCast(pl_ty.abiSize(pt));
switch (opt_mcv) {
.load_frame => |frame_addr| try func.genSetMem(
.{ .frame = frame_addr.index },
frame_addr.off + pl_abi_size,
Type.u8,
.{ .immediate = 1 },
),
.register => return func.fail("TODO: airWrapOption opt_mcv register", .{}),
.load_frame => |frame_addr| {
try func.genCopy(pl_ty, opt_mcv, pl_mcv);
try func.genSetMem(
.{ .frame = frame_addr.index },
frame_addr.off + pl_abi_size,
Type.u8,
.{ .immediate = 1 },
);
},
else => unreachable,
}
}
@@ -3454,7 +3675,7 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void {
if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv;
const dst_mcv = try func.allocRegOrMem(ty, inst, true);
try func.genCopy(Type.usize, dst_mcv, len_mcv);
try func.genCopy(Type.u64, dst_mcv, len_mcv);
break :result dst_mcv;
},
.register_pair => |pair| {
@@ -3463,7 +3684,7 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void {
if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv;
const dst_mcv = try func.allocRegOrMem(ty, inst, true);
try func.genCopy(Type.usize, dst_mcv, len_mcv);
try func.genCopy(Type.u64, dst_mcv, len_mcv);
break :result dst_mcv;
},
else => return func.fail("TODO airSliceLen for {}", .{src_mcv}),
@@ -3474,14 +3695,28 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void {
fn airPtrSliceLenPtr(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement ptr_slice_len_ptr for {}", .{func.target.cpu.arch});
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const src_mcv = try func.resolveInst(ty_op.operand);
const dst_reg, const dst_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(dst_lock);
const dst_mcv: MCValue = .{ .register = dst_reg };
try func.genCopy(Type.u64, dst_mcv, src_mcv.offset(8));
break :result dst_mcv;
};
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airPtrSlicePtrPtr(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement ptr_slice_ptr_ptr for {}", .{func.target.cpu.arch});
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
const opt_mcv = try func.resolveInst(ty_op.operand);
const dst_mcv = if (func.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
opt_mcv
else
try func.copyToNewRegister(inst, opt_mcv);
return func.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
}
fn airSliceElemVal(func: *Func, inst: Air.Inst.Index) !void {
@@ -3538,11 +3773,10 @@ fn genSliceElemPtr(func: *Func, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
const addr_reg, const addr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(addr_lock);
try func.genSetReg(Type.usize, addr_reg, slice_mcv);
try func.genSetReg(Type.u64, addr_reg, slice_mcv);
_ = try func.addInst(.{
.tag = .add,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = addr_reg,
.rs1 = addr_reg,
@@ -3576,12 +3810,12 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void {
.register => {
const frame_index = try func.allocFrameIndex(FrameAlloc.initType(array_ty, pt));
try func.genSetMem(.{ .frame = frame_index }, 0, array_ty, array_mcv);
try func.genSetReg(Type.usize, addr_reg, .{ .lea_frame = .{ .index = frame_index } });
try func.genSetReg(Type.u64, addr_reg, .{ .lea_frame = .{ .index = frame_index } });
},
.load_frame => |frame_addr| {
try func.genSetReg(Type.usize, addr_reg, .{ .lea_frame = frame_addr });
try func.genSetReg(Type.u64, addr_reg, .{ .lea_frame = frame_addr });
},
else => try func.genSetReg(Type.usize, addr_reg, array_mcv.address()),
else => try func.genSetReg(Type.u64, addr_reg, array_mcv.address()),
}
const dst_mcv = try func.allocRegOrMem(result_ty, inst, false);
@@ -3602,11 +3836,10 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void {
// we can do a shortcut here where we don't need a vslicedown
// and can just copy to the frame index.
if (!(index_mcv == .immediate and index_mcv.immediate == 0)) {
const index_reg = try func.copyToTmpRegister(Type.usize, index_mcv);
const index_reg = try func.copyToTmpRegister(Type.u64, index_mcv);
_ = try func.addInst(.{
.tag = .vslidedownvx,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = src_reg,
.rs1 = index_reg,
@@ -3624,7 +3857,6 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void {
defer func.register_manager.unlockReg(offset_lock);
_ = try func.addInst(.{
.tag = .add,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = addr_reg,
.rs1 = addr_reg,
@@ -3640,8 +3872,55 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void {
fn airPtrElemVal(func: *Func, inst: Air.Inst.Index) !void {
const is_volatile = false; // TODO
const pt = func.pt;
const zcu = pt.zcu;
const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const result: MCValue = if (!is_volatile and func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement ptr_elem_val for {}", .{func.target.cpu.arch});
const base_ptr_ty = func.typeOf(bin_op.lhs);
const result: MCValue = if (!is_volatile and func.liveness.isUnused(inst)) .unreach else result: {
const elem_ty = base_ptr_ty.elemType2(zcu);
if (!elem_ty.hasRuntimeBitsIgnoreComptime(pt)) break :result .none;
const base_ptr_mcv = try func.resolveInst(bin_op.lhs);
const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
else => null,
};
defer if (base_ptr_lock) |lock| func.register_manager.unlockReg(lock);
const index_mcv = try func.resolveInst(bin_op.rhs);
const index_lock: ?RegisterLock = switch (index_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
else => null,
};
defer if (index_lock) |lock| func.register_manager.unlockReg(lock);
const elem_ptr_reg = if (base_ptr_mcv.isRegister() and func.liveness.operandDies(inst, 0))
base_ptr_mcv.register
else
try func.copyToTmpRegister(base_ptr_ty, base_ptr_mcv);
const elem_ptr_lock = func.register_manager.lockRegAssumeUnused(elem_ptr_reg);
defer func.register_manager.unlockReg(elem_ptr_lock);
try func.genBinOp(
.ptr_add,
base_ptr_mcv,
base_ptr_ty,
index_mcv,
Type.u64,
elem_ptr_reg,
);
const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true);
const dst_lock = switch (dst_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
else => null,
};
defer if (dst_lock) |lock| func.register_manager.unlockReg(lock);
try func.load(dst_mcv, .{ .register = elem_ptr_reg }, base_ptr_ty);
break :result dst_mcv;
};
return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
@@ -3651,10 +3930,14 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void {
const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
const result = result: {
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const elem_ptr_ty = func.typeOfIndex(inst);
const base_ptr_ty = func.typeOf(extra.lhs);
if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
@panic("audit");
}
const base_ptr_mcv = try func.resolveInst(extra.lhs);
const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
@@ -3662,16 +3945,6 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void {
};
defer if (base_ptr_lock) |lock| func.register_manager.unlockReg(lock);
if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
break :result if (func.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv))
base_ptr_mcv
else
try func.copyToNewRegister(inst, base_ptr_mcv);
}
const elem_ty = base_ptr_ty.elemType2(zcu);
const elem_abi_size = elem_ty.abiSize(pt);
const index_ty = func.typeOf(extra.rhs);
const index_mcv = try func.resolveInst(extra.rhs);
const index_lock: ?RegisterLock = switch (index_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
@@ -3679,10 +3952,6 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void {
};
defer if (index_lock) |lock| func.register_manager.unlockReg(lock);
const offset_reg = try func.elemOffset(index_ty, index_mcv, elem_abi_size);
const offset_reg_lock = func.register_manager.lockRegAssumeUnused(offset_reg);
defer func.register_manager.unlockReg(offset_reg_lock);
const result_reg, const result_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(result_lock);
@@ -3690,13 +3959,14 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void {
.ptr_add,
base_ptr_mcv,
base_ptr_ty,
.{ .register = offset_reg },
Type.usize,
index_mcv,
Type.u64,
result_reg,
);
break :result MCValue{ .register = result_reg };
};
return func.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
}
@@ -3729,7 +3999,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
defer func.register_manager.unlockReg(result_lock);
switch (frame_mcv) {
.load_frame => |frame_addr| {
.load_frame => {
if (tag_abi_size <= 8) {
const off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align))
@intCast(layout.payload_size)
@@ -3739,7 +4009,7 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
try func.genCopy(
tag_ty,
.{ .register = result_reg },
.{ .load_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off } },
frame_mcv.offset(off),
);
} else {
return func.fail(
@@ -3756,7 +4026,57 @@ fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void {
fn airClz(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airClz for {}", .{func.target.cpu.arch});
const operand = try func.resolveInst(ty_op.operand);
const ty = func.typeOf(ty_op.operand);
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const src_reg, const src_lock = try func.promoteReg(ty, operand);
defer if (src_lock) |lock| func.register_manager.unlockReg(lock);
const dst_reg: Register = if (func.reuseOperand(
inst,
ty_op.operand,
0,
operand,
) and operand == .register)
operand.register
else
(try func.allocRegOrMem(func.typeOfIndex(inst), inst, true)).register;
const bit_size = ty.bitSize(func.pt);
if (!math.isPowerOfTwo(bit_size)) try func.truncateRegister(ty, src_reg);
if (bit_size > 64) {
return func.fail("TODO: airClz > 64 bits, found {d}", .{bit_size});
}
_ = try func.addInst(.{
.tag = switch (bit_size) {
32 => .clzw,
else => .clz,
},
.data = .{
.r_type = .{
.rs2 = .zero, // rs2 is 0 filled in the spec
.rs1 = src_reg,
.rd = dst_reg,
},
},
});
if (!(bit_size == 32 or bit_size == 64)) {
_ = try func.addInst(.{
.tag = .addi,
.data = .{ .i_type = .{
.rd = dst_reg,
.rs1 = dst_reg,
.imm12 = Immediate.s(-@as(i12, @intCast(64 - bit_size % 64))),
} },
});
}
break :result .{ .register = dst_reg };
};
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
@@ -3768,7 +4088,37 @@ fn airCtz(func: *Func, inst: Air.Inst.Index) !void {
fn airPopcount(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airPopcount for {}", .{func.target.cpu.arch});
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const pt = func.pt;
const operand = try func.resolveInst(ty_op.operand);
const src_ty = func.typeOf(ty_op.operand);
const operand_reg, const operand_lock = try func.promoteReg(src_ty, operand);
defer if (operand_lock) |lock| func.register_manager.unlockReg(lock);
const dst_reg, const dst_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(dst_lock);
const bit_size = src_ty.bitSize(pt);
switch (bit_size) {
32, 64 => {},
1...31, 33...63 => try func.truncateRegister(src_ty, operand_reg),
else => return func.fail("TODO: airPopcount > 64 bits", .{}),
}
_ = try func.addInst(.{
.tag = if (bit_size <= 32) .cpopw else .cpop,
.data = .{
.r_type = .{
.rd = dst_reg,
.rs1 = operand_reg,
.rs2 = @enumFromInt(0b00010), // this is the cpop funct5
},
},
});
break :result .{ .register = dst_reg };
};
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
@@ -3785,6 +4135,13 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
.Int => if (ty.zigTypeTag(zcu) == .Vector) {
return func.fail("TODO implement airAbs for {}", .{ty.fmt(pt)});
} else {
const int_info = scalar_ty.intInfo(zcu);
const int_bits = int_info.bits;
switch (int_bits) {
32, 64 => {},
else => return func.fail("TODO: airAbs Int size {d}", .{int_bits}),
}
const return_mcv = try func.copyToNewRegister(inst, operand);
const operand_reg = return_mcv.register;
@@ -3792,18 +4149,20 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
defer func.register_manager.unlockReg(temp_lock);
_ = try func.addInst(.{
.tag = .srai,
.ops = .rri,
.tag = switch (int_bits) {
32 => .sraiw,
64 => .srai,
else => unreachable,
},
.data = .{ .i_type = .{
.rd = temp_reg,
.rs1 = operand_reg,
.imm12 = Immediate.u(63),
.imm12 = Immediate.u(int_bits - 1),
} },
});
_ = try func.addInst(.{
.tag = .xor,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = operand_reg,
.rs1 = operand_reg,
@@ -3812,8 +4171,11 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
});
_ = try func.addInst(.{
.tag = .sub,
.ops = .rrr,
.tag = switch (int_bits) {
32 => .subw,
64 => .sub,
else => unreachable,
},
.data = .{ .r_type = .{
.rd = operand_reg,
.rs1 = operand_reg,
@@ -3825,14 +4187,14 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
},
.Float => {
const float_bits = scalar_ty.floatBits(zcu.getTarget());
switch (float_bits) {
const mnem: Mnemonic = switch (float_bits) {
16 => return func.fail("TODO: airAbs 16-bit float", .{}),
32 => {},
64 => {},
32 => .fsgnjxs,
64 => .fsgnjxd,
80 => return func.fail("TODO: airAbs 80-bit float", .{}),
128 => return func.fail("TODO: airAbs 128-bit float", .{}),
else => unreachable,
}
};
const return_mcv = try func.copyToNewRegister(inst, operand);
const operand_reg = return_mcv.register;
@@ -3840,13 +4202,12 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
assert(operand_reg.class() == .float);
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_fabs,
.tag = mnem,
.data = .{
.fabs = .{
.r_type = .{
.rd = operand_reg,
.rs = operand_reg,
.bits = float_bits,
.rs1 = operand_reg,
.rs2 = operand_reg,
},
},
});
@@ -3869,54 +4230,56 @@ fn airByteSwap(func: *Func, inst: Air.Inst.Index) !void {
const ty = func.typeOf(ty_op.operand);
const operand = try func.resolveInst(ty_op.operand);
const int_bits = ty.intInfo(zcu).bits;
switch (ty.zigTypeTag(zcu)) {
.Int => {
const int_bits = ty.intInfo(zcu).bits;
// bytes are no-op
if (int_bits == 8 and func.reuseOperand(inst, ty_op.operand, 0, operand)) {
return func.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
}
// bytes are no-op
if (int_bits == 8 and func.reuseOperand(inst, ty_op.operand, 0, operand)) {
return func.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
}
const dest_mcv = try func.copyToNewRegister(inst, operand);
const dest_reg = dest_mcv.register;
const dest_mcv = try func.copyToNewRegister(inst, operand);
const dest_reg = dest_mcv.register;
switch (int_bits) {
16 => {
const temp_reg, const temp_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(temp_lock);
switch (int_bits) {
16 => {
const temp_reg, const temp_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(temp_lock);
_ = try func.addInst(.{
.tag = .srli,
.ops = .rri,
.data = .{ .i_type = .{
.imm12 = Immediate.s(8),
.rd = temp_reg,
.rs1 = dest_reg,
} },
});
_ = try func.addInst(.{
.tag = .srli,
.data = .{ .i_type = .{
.imm12 = Immediate.s(8),
.rd = temp_reg,
.rs1 = dest_reg,
} },
});
_ = try func.addInst(.{
.tag = .slli,
.ops = .rri,
.data = .{ .i_type = .{
.imm12 = Immediate.s(8),
.rd = dest_reg,
.rs1 = dest_reg,
} },
});
_ = try func.addInst(.{
.tag = .@"or",
.ops = .rri,
.data = .{ .r_type = .{
.rd = dest_reg,
.rs1 = dest_reg,
.rs2 = temp_reg,
} },
});
_ = try func.addInst(.{
.tag = .slli,
.data = .{ .i_type = .{
.imm12 = Immediate.s(8),
.rd = dest_reg,
.rs1 = dest_reg,
} },
});
_ = try func.addInst(.{
.tag = .@"or",
.data = .{ .r_type = .{
.rd = dest_reg,
.rs1 = dest_reg,
.rs2 = temp_reg,
} },
});
},
else => return func.fail("TODO: {d} bits for airByteSwap", .{int_bits}),
}
break :result dest_mcv;
},
else => return func.fail("TODO: {d} bits for airByteSwap", .{int_bits}),
else => return func.fail("TODO: airByteSwap {}", .{ty.fmt(pt)}),
}
break :result dest_mcv;
};
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
@@ -3960,7 +4323,6 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
.sqrt => {
_ = try func.addInst(.{
.tag = if (operand_bit_size == 64) .fsqrtd else .fsqrts,
.ops = .rrr,
.data = .{
.r_type = .{
.rd = dst_reg,
@@ -3970,6 +4332,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
},
});
},
else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}),
}
},
@@ -4054,9 +4417,15 @@ fn airLoad(func: *Func, inst: Air.Inst.Index) !void {
const elem_size = elem_ty.abiSize(pt);
const dst_mcv: MCValue = blk: {
// Pointer is 8 bytes, and if the element is more than that, we cannot reuse it.
if (elem_size <= 8 and func.reuseOperand(inst, ty_op.operand, 0, ptr)) {
// The MCValue that holds the pointer can be re-used as the value.
// The MCValue that holds the pointer can be re-used as the value.
// - "ptr" is 8 bytes, and if the element is more than that, we cannot reuse it.
//
// - "ptr" will be stored in an integer register, so the type that we're gonna
// load into it must also be a type that can be inside of an integer register
if (elem_size <= 8 and
(if (ptr == .register) func.typeRegClass(elem_ty) == ptr.register.class() else true) and
func.reuseOperand(inst, ty_op.operand, 0, ptr))
{
break :blk ptr;
} else {
break :blk try func.allocRegOrMem(elem_ty, inst, true);
@@ -4090,12 +4459,14 @@ fn load(func: *Func, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro
.register_offset,
.lea_frame,
.lea_symbol,
.lea_tlv,
=> try func.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
.memory,
.indirect,
.load_symbol,
.load_frame,
.load_tlv,
=> {
const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv);
const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg);
@@ -4117,15 +4488,16 @@ fn airStore(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
const ptr = try func.resolveInst(bin_op.lhs);
const value = try func.resolveInst(bin_op.rhs);
const ptr_ty = func.typeOf(bin_op.lhs);
const value_ty = func.typeOf(bin_op.rhs);
try func.store(ptr, value, ptr_ty, value_ty);
try func.store(ptr, value, ptr_ty);
return func.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
}
/// Loads `value` into the "payload" of `pointer`.
fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty: Type) !void {
fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type) !void {
const zcu = func.pt.zcu;
const src_ty = ptr_ty.childType(zcu);
log.debug("storing {}:{} in {}:{}", .{ src_mcv, src_ty.fmt(func.pt), ptr_mcv, ptr_ty.fmt(func.pt) });
switch (ptr_mcv) {
@@ -4141,12 +4513,14 @@ fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty:
.register_offset,
.lea_symbol,
.lea_frame,
.lea_tlv,
=> try func.genCopy(src_ty, ptr_mcv.deref(), src_mcv),
.memory,
.indirect,
.load_symbol,
.load_frame,
.load_tlv,
=> {
const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv);
const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg);
@@ -4154,7 +4528,7 @@ fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty:
try func.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);
},
.air_ref => |ptr_ref| try func.store(try func.resolveInst(ptr_ref), src_mcv, ptr_ty, src_ty),
.air_ref => |ptr_ref| try func.store(try func.resolveInst(ptr_ref), src_mcv, ptr_ty),
}
}
@@ -4176,17 +4550,14 @@ fn structFieldPtr(func: *Func, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
const zcu = pt.zcu;
const ptr_field_ty = func.typeOfIndex(inst);
const ptr_container_ty = func.typeOf(operand);
const ptr_container_ty_info = ptr_container_ty.ptrInfo(zcu);
const container_ty = ptr_container_ty.childType(zcu);
const field_offset: i32 = if (zcu.typeToPackedStruct(container_ty)) |struct_obj|
if (ptr_field_ty.ptrInfo(zcu).packed_offset.host_size == 0)
@divExact(pt.structPackedFieldBitOffset(struct_obj, index) +
ptr_container_ty_info.packed_offset.bit_offset, 8)
else
0
else
@intCast(container_ty.structFieldOffset(index, pt));
const field_offset: i32 = switch (container_ty.containerLayout(zcu)) {
.auto, .@"extern" => @intCast(container_ty.structFieldOffset(index, pt)),
.@"packed" => @divExact(@as(i32, ptr_container_ty.ptrInfo(zcu).packed_offset.bit_offset) +
(if (zcu.typeToStruct(container_ty)) |struct_obj| pt.structPackedFieldBitOffset(struct_obj, index) else 0) -
ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8),
};
const src_mcv = try func.resolveInst(operand);
const dst_mcv = if (switch (src_mcv) {
@@ -4229,7 +4600,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void {
const dst_reg = if (field_off == 0)
(try func.copyToNewRegister(inst, src_mcv)).register
else
try func.copyToTmpRegister(Type.usize, .{ .register = src_reg });
try func.copyToTmpRegister(Type.u64, .{ .register = src_reg });
const dst_mcv: MCValue = .{ .register = dst_reg };
const dst_lock = func.register_manager.lockReg(dst_reg);
@@ -4238,7 +4609,6 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void {
if (field_off > 0) {
_ = try func.addInst(.{
.tag = .srli,
.ops = .rri,
.data = .{ .i_type = .{
.imm12 = Immediate.u(@intCast(field_off)),
.rd = dst_reg,
@@ -4289,8 +4659,8 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void {
const hi_mcv =
dst_mcv.address().offset(@intCast(field_bit_size / 64 * 8)).deref();
try func.genSetReg(Type.usize, tmp_reg, hi_mcv);
try func.genCopy(Type.usize, hi_mcv, .{ .register = tmp_reg });
try func.genSetReg(Type.u64, tmp_reg, hi_mcv);
try func.genCopy(Type.u64, hi_mcv, .{ .register = tmp_reg });
}
break :result dst_mcv;
}
@@ -4314,7 +4684,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void {
const zcu = pt.zcu;
const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;
const ty = arg.ty.toType();
const owner_decl = zcu.funcOwnerDeclIndex(func.func_index);
const owner_decl = func.owner.getDecl(zcu);
if (arg.name == .none) return;
const name = func.air.nullTerminatedString(@intFromEnum(arg.name));
@@ -4358,8 +4728,7 @@ fn airArg(func: *Func, inst: Air.Inst.Index) !void {
fn airTrap(func: *Func) !void {
_ = try func.addInst(.{
.tag = .unimp,
.ops = .none,
.data = undefined,
.data = .none,
});
return func.finishAirBookkeeping();
}
@@ -4367,21 +4736,20 @@ fn airTrap(func: *Func) !void {
fn airBreakpoint(func: *Func) !void {
_ = try func.addInst(.{
.tag = .ebreak,
.ops = .none,
.data = undefined,
.data = .none,
});
return func.finishAirBookkeeping();
}
fn airRetAddr(func: *Func, inst: Air.Inst.Index) !void {
const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true);
try func.genCopy(Type.usize, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } });
try func.genCopy(Type.u64, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } });
return func.finishAir(inst, dst_mcv, .{ .none, .none, .none });
}
fn airFrameAddress(func: *Func, inst: Air.Inst.Index) !void {
const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true);
try func.genCopy(Type.usize, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } });
try func.genCopy(Type.u64, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } });
return func.finishAir(inst, dst_mcv, .{ .none, .none, .none });
}
@@ -4396,15 +4764,11 @@ fn airFence(func: *Func, inst: Air.Inst.Index) !void {
};
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_fence,
.data = .{
.fence = .{
.pred = pred,
.succ = succ,
.fm = if (order == .acq_rel) .tso else .none,
},
},
.tag = if (order == .acq_rel) .fencetso else .fence,
.data = .{ .fence = .{
.pred = pred,
.succ = succ,
} },
});
return func.finishAirBookkeeping();
}
@@ -4540,7 +4904,7 @@ fn genCall(
.indirect => |reg_off| {
const ret_ty = Type.fromInterned(fn_info.return_type);
const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(ret_ty, pt));
try func.genSetReg(Type.usize, reg_off.reg, .{
try func.genSetReg(Type.u64, reg_off.reg, .{
.lea_frame = .{ .index = frame_index, .off = -reg_off.off },
});
call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } };
@@ -4558,7 +4922,7 @@ fn genCall(
dst_reg,
src_arg,
),
.indirect => |reg_off| try func.genSetReg(Type.usize, reg_off.reg, .{
.indirect => |reg_off| try func.genSetReg(Type.u64, reg_off.reg, .{
.lea_frame = .{ .index = frame_index, .off = -reg_off.off },
}),
else => return func.fail("TODO: genCall actual set {s}", .{@tagName(dst_arg)}),
@@ -4586,10 +4950,9 @@ fn genCall(
if (func.mod.pic) {
return func.fail("TODO: genCall pic", .{});
} else {
try func.genSetReg(Type.usize, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } });
try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } });
_ = try func.addInst(.{
.tag = .jalr,
.ops = .rri,
.data = .{ .i_type = .{
.rd = .ra,
.rs1 = .ra,
@@ -4603,18 +4966,17 @@ fn genCall(
const owner_decl = zcu.declPtr(extern_func.decl);
const lib_name = extern_func.lib_name.toSlice(&zcu.intern_pool);
const decl_name = owner_decl.name.toSlice(&zcu.intern_pool);
const atom_index = try func.symbolIndex();
const atom_index = try func.owner.getSymbolIndex(func);
if (func.bin_file.cast(link.File.Elf)) |elf_file| {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_extern_fn_reloc,
.data = .{ .reloc = .{
.atom_index = atom_index,
.sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name),
} },
});
} else unreachable; // not a valid riscv64 format
const elf_file = func.bin_file.cast(link.File.Elf).?;
_ = try func.addInst(.{
.tag = .pseudo_extern_fn_reloc,
.data = .{ .reloc = .{
.register = .ra,
.atom_index = atom_index,
.sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name),
} },
});
},
else => return func.fail("TODO implement calling bitcasted functions", .{}),
}
@@ -4622,11 +4984,10 @@ fn genCall(
assert(func.typeOf(callee).zigTypeTag(zcu) == .Pointer);
const addr_reg, const addr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(addr_lock);
try func.genSetReg(Type.usize, addr_reg, .{ .air_ref = callee });
try func.genSetReg(Type.u64, addr_reg, .{ .air_ref = callee });
_ = try func.addInst(.{
.tag = .jalr,
.ops = .rri,
.data = .{ .i_type = .{
.rd = .ra,
.rs1 = addr_reg,
@@ -4638,6 +4999,10 @@ fn genCall(
.lib => return func.fail("TODO: lib func calls", .{}),
}
// reset the vector settings as they might have changed in the function
func.avl = null;
func.vtype = null;
return call_info.return_value.short;
}
@@ -4683,7 +5048,7 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
const lock = func.register_manager.lockRegAssumeUnused(reg_off.reg);
defer func.register_manager.unlockReg(lock);
try func.genSetReg(Type.usize, reg_off.reg, func.ret_mcv.long);
try func.genSetReg(Type.u64, reg_off.reg, func.ret_mcv.long);
try func.genSetMem(
.{ .reg = reg_off.reg },
reg_off.off,
@@ -4699,9 +5064,11 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
// Just add space for an instruction, reloced this later
const index = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_j,
.data = .{ .inst = undefined },
.tag = .pseudo_j,
.data = .{ .j_type = .{
.rd = .zero,
.inst = undefined,
} },
});
try func.exitlude_jump_relocs.append(func.gpa, index);
@@ -4723,9 +5090,11 @@ fn airRetLoad(func: *Func, inst: Air.Inst.Index) !void {
// Just add space for an instruction, reloced this later
const index = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_j,
.data = .{ .inst = undefined },
.tag = .pseudo_j,
.data = .{ .j_type = .{
.rd = .zero,
.inst = undefined,
} },
});
try func.exitlude_jump_relocs.append(func.gpa, index);
@@ -4751,14 +5120,14 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
.Enum => lhs_ty.intTagType(zcu),
.Int => lhs_ty,
.Bool => Type.u1,
.Pointer => Type.usize,
.Pointer => Type.u64,
.ErrorSet => Type.anyerror,
.Optional => blk: {
const payload_ty = lhs_ty.optionalChild(zcu);
if (!payload_ty.hasRuntimeBitsIgnoreComptime(pt)) {
break :blk Type.u1;
} else if (lhs_ty.isPtrLikeOptional(zcu)) {
break :blk Type.usize;
break :blk Type.u64;
} else {
return func.fail("TODO riscv cmp non-pointer optionals", .{});
}
@@ -4805,8 +5174,7 @@ fn airDbgStmt(func: *Func, inst: Air.Inst.Index) !void {
const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_dbg_line_column,
.tag = .pseudo_dbg_line_column,
.data = .{ .pseudo_dbg_line_column = .{
.line = dbg_stmt.line,
.column = dbg_stmt.column,
@@ -4868,7 +5236,7 @@ fn genVarDbgInfo(
break :blk .nop;
},
};
try dw.genVarDbgInfo(name, ty, zcu.funcOwnerDeclIndex(func.func_index), is_ptr, loc);
try dw.genVarDbgInfo(name, ty, func.owner.getDecl(zcu), is_ptr, loc);
},
.plan9 => {},
.none => {},
@@ -4924,7 +5292,6 @@ fn condBr(func: *Func, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
return try func.addInst(.{
.tag = .beq,
.ops = .rr_inst,
.data = .{
.b_type = .{
.rs1 = cond_reg,
@@ -4955,19 +5322,18 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
.dead,
.undef,
.immediate,
.register_pair,
.register_offset,
.lea_frame,
.lea_symbol,
.reserved_frame,
.air_ref,
=> return func.fail("TODO: hmm {}", .{opt_mcv}),
.register_pair,
=> unreachable,
.register => |opt_reg| {
if (some_info.off == 0) {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_compare,
.tag = .pseudo_compare,
.data = .{
.compare = .{
.op = .eq,
@@ -4984,9 +5350,27 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
return return_mcv;
}
assert(some_info.ty.ip_index == .bool_type);
const opt_abi_size: u32 = @intCast(opt_ty.abiSize(pt));
_ = opt_abi_size;
return func.fail("TODO: isNull some_info.off != 0 register", .{});
const bit_offset: u7 = @intCast(some_info.off * 8);
try func.genBinOp(
.shr,
.{ .register = opt_reg },
Type.u64,
.{ .immediate = bit_offset },
Type.u8,
return_reg,
);
try func.truncateRegister(Type.u8, return_reg);
try func.genBinOp(
.cmp_eq,
.{ .register = return_reg },
Type.u64,
.{ .immediate = 0 },
Type.u8,
return_reg,
);
return return_mcv;
},
.load_frame => {
@@ -4998,8 +5382,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
defer func.register_manager.unlockReg(opt_reg_lock);
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_compare,
.tag = .pseudo_compare,
.data = .{
.compare = .{
.op = .eq,
@@ -5048,8 +5431,7 @@ fn airIsNonNull(func: *Func, inst: Air.Inst.Index) !void {
assert(result == .register);
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_not,
.tag = .pseudo_not,
.data = .{
.rr = .{
.rd = result.register,
@@ -5181,8 +5563,7 @@ fn isNonErr(func: *Func, inst: Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MC
switch (is_err_res) {
.register => |reg| {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_not,
.tag = .pseudo_not,
.data = .{
.rr = .{
.rd = reg,
@@ -5249,11 +5630,11 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void {
/// Send control flow to the `index` of `func.code`.
fn jump(func: *Func, index: Mir.Inst.Index) !Mir.Inst.Index {
return func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_j,
.data = .{
.tag = .pseudo_j,
.data = .{ .j_type = .{
.rd = .zero,
.inst = index,
},
} },
});
}
@@ -5296,7 +5677,6 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
const condition = try func.resolveInst(pl_op.operand);
const condition_ty = func.typeOf(pl_op.operand);
const switch_br = func.air.extraData(Air.SwitchBr, pl_op.payload);
var extra_index: usize = switch_br.end;
@@ -5304,6 +5684,8 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.data.cases_len + 1);
defer func.gpa.free(liveness.deaths);
const condition = try func.resolveInst(pl_op.operand);
// If the condition dies here in this switch instruction, process
// that death now instead of later as this has an effect on
// whether it needs to be spilled in the branches
@@ -5326,9 +5708,14 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
defer func.gpa.free(relocs);
for (items, relocs, 0..) |item, *reloc, i| {
// switch branches must be comptime-known, so this is stored in an immediate
const item_mcv = try func.resolveInst(item);
const cond_lock = switch (condition) {
.register => func.register_manager.lockRegAssumeUnused(condition.register),
else => null,
};
defer if (cond_lock) |lock| func.register_manager.unlockReg(lock);
const cmp_reg, const cmp_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(cmp_lock);
@@ -5343,8 +5730,7 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
if (!(i < relocs.len - 1)) {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_not,
.tag = .pseudo_not,
.data = .{ .rr = .{
.rd = cmp_reg,
.rs = cmp_reg,
@@ -5391,18 +5777,14 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
fn performReloc(func: *Func, inst: Mir.Inst.Index) void {
const tag = func.mir_instructions.items(.tag)[inst];
const ops = func.mir_instructions.items(.ops)[inst];
const target: Mir.Inst.Index = @intCast(func.mir_instructions.len);
switch (tag) {
.bne,
.beq,
.bne,
=> func.mir_instructions.items(.data)[inst].b_type.inst = target,
.jal => func.mir_instructions.items(.data)[inst].j_type.inst = target,
.pseudo => switch (ops) {
.pseudo_j => func.mir_instructions.items(.data)[inst].inst = target,
else => std.debug.panic("TODO: performReloc {s}", .{@tagName(ops)}),
},
.pseudo_j => func.mir_instructions.items(.data)[inst].j_type.inst = target,
else => std.debug.panic("TODO: performReloc {s}", .{@tagName(tag)}),
}
}
@@ -5489,7 +5871,6 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void {
_ = try func.addInst(.{
.tag = if (tag == .bool_or) .@"or" else .@"and",
.ops = .rrr,
.data = .{ .r_type = .{
.rd = result_reg,
.rs1 = lhs_reg,
@@ -5501,7 +5882,6 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void {
if (func.wantSafety()) {
_ = try func.addInst(.{
.tag = .andi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = result_reg,
.rs1 = result_reg,
@@ -5518,7 +5898,6 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void {
fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = func.air.extraData(Air.Asm, ty_pl.payload);
const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
const clobbers_len: u31 = @truncate(extra.data.flags);
var extra_i: usize = extra.end;
const outputs: []const Air.Inst.Ref =
@@ -5527,87 +5906,444 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
const inputs: []const Air.Inst.Ref = @ptrCast(func.air.extra[extra_i..][0..extra.data.inputs_len]);
extra_i += inputs.len;
const dead = !is_volatile and func.liveness.isUnused(inst);
const result: MCValue = if (dead) .unreach else result: {
if (outputs.len > 1) {
return func.fail("TODO implement codegen for asm with more than 1 output", .{});
}
var result: MCValue = .none;
var args = std.ArrayList(MCValue).init(func.gpa);
try args.ensureTotalCapacity(outputs.len + inputs.len);
defer {
for (args.items) |arg| if (arg.getReg()) |reg| func.register_manager.unlockReg(.{
.tracked_index = RegisterManager.indexOfRegIntoTracked(reg) orelse continue,
});
args.deinit();
}
var arg_map = std.StringHashMap(u8).init(func.gpa);
try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len));
defer arg_map.deinit();
const output_constraint: ?[]const u8 = for (outputs) |output| {
if (output != .none) {
return func.fail("TODO implement codegen for non-expr asm", .{});
}
const extra_bytes = std.mem.sliceAsBytes(func.air.extra[extra_i..]);
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra[extra_i..]), 0);
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
var outputs_extra_i = extra_i;
for (outputs) |output| {
const extra_bytes = mem.sliceAsBytes(func.air.extra[extra_i..]);
const constraint = mem.sliceTo(mem.sliceAsBytes(func.air.extra[extra_i..]), 0);
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
break constraint;
} else null;
for (inputs) |input| {
const input_bytes = std.mem.sliceAsBytes(func.air.extra[extra_i..]);
const constraint = std.mem.sliceTo(input_bytes, 0);
const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
return func.fail("unrecognized asm input constraint: '{s}'", .{constraint});
}
const reg_name = constraint[1 .. constraint.len - 1];
const reg = parseRegName(reg_name) orelse
return func.fail("unrecognized register: '{s}'", .{reg_name});
const arg_mcv = try func.resolveInst(input);
try func.register_manager.getReg(reg, null);
try func.genSetReg(func.typeOf(input), reg, arg_mcv);
}
{
var clobber_i: u32 = 0;
while (clobber_i < clobbers_len) : (clobber_i += 1) {
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra[extra_i..]), 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
extra_i += clobber.len / 4 + 1;
if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) {
// nothing really to do
} else {
try func.register_manager.getReg(parseRegName(clobber) orelse
return func.fail("invalid clobber: '{s}'", .{clobber}), null);
const is_read = switch (constraint[0]) {
'=' => false,
'+' => read: {
if (output == .none) return func.fail(
"read-write constraint unsupported for asm result: '{s}'",
.{constraint},
);
break :read true;
},
else => return func.fail("invalid constraint: '{s}'", .{constraint}),
};
const is_early_clobber = constraint[1] == '&';
const rest = constraint[@as(usize, 1) + @intFromBool(is_early_clobber) ..];
const arg_mcv: MCValue = arg_mcv: {
const arg_maybe_reg: ?Register = if (mem.eql(u8, rest, "m"))
if (output != .none) null else return func.fail(
"memory constraint unsupported for asm result: '{s}'",
.{constraint},
)
else if (mem.startsWith(u8, rest, "{") and mem.endsWith(u8, rest, "}"))
parseRegName(rest["{".len .. rest.len - "}".len]) orelse
return func.fail("invalid register constraint: '{s}'", .{constraint})
else if (rest.len == 1 and std.ascii.isDigit(rest[0])) {
const index = std.fmt.charToDigit(rest[0], 10) catch unreachable;
if (index >= args.items.len) return func.fail("constraint out of bounds: '{s}'", .{
constraint,
});
break :arg_mcv args.items[index];
} else return func.fail("invalid constraint: '{s}'", .{constraint});
break :arg_mcv if (arg_maybe_reg) |reg| .{ .register = reg } else arg: {
const ptr_mcv = try func.resolveInst(output);
switch (ptr_mcv) {
.immediate => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
break :arg ptr_mcv.deref(),
.register, .register_offset, .lea_frame => break :arg ptr_mcv.deref(),
else => {},
}
break :arg .{ .indirect = .{ .reg = try func.copyToTmpRegister(Type.usize, ptr_mcv) } };
};
};
if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
_ = func.register_manager.lockReg(reg);
};
if (!mem.eql(u8, name, "_"))
arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
args.appendAssumeCapacity(arg_mcv);
if (output == .none) result = arg_mcv;
if (is_read) try func.load(arg_mcv, .{ .air_ref = output }, func.typeOf(output));
}
for (inputs) |input| {
const input_bytes = mem.sliceAsBytes(func.air.extra[extra_i..]);
const constraint = mem.sliceTo(input_bytes, 0);
const name = mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
const ty = func.typeOf(input);
const input_mcv = try func.resolveInst(input);
const arg_mcv: MCValue = if (mem.eql(u8, constraint, "X"))
input_mcv
else if (mem.startsWith(u8, constraint, "{") and mem.endsWith(u8, constraint, "}")) arg: {
const reg = parseRegName(constraint["{".len .. constraint.len - "}".len]) orelse
return func.fail("invalid register constraint: '{s}'", .{constraint});
try func.register_manager.getReg(reg, null);
try func.genSetReg(ty, reg, input_mcv);
break :arg .{ .register = reg };
} else if (mem.eql(u8, constraint, "r")) arg: {
switch (input_mcv) {
.register => break :arg input_mcv,
else => {},
}
const temp_reg = try func.copyToTmpRegister(ty, input_mcv);
break :arg .{ .register = temp_reg };
} else return func.fail("invalid input constraint: '{s}'", .{constraint});
if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
_ = func.register_manager.lockReg(reg);
};
if (!mem.eql(u8, name, "_"))
arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
args.appendAssumeCapacity(arg_mcv);
}
{
var clobber_i: u32 = 0;
while (clobber_i < clobbers_len) : (clobber_i += 1) {
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra[extra_i..]), 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
extra_i += clobber.len / 4 + 1;
if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) {
// nothing really to do
} else {
try func.register_manager.getReg(parseRegName(clobber) orelse
return func.fail("invalid clobber: '{s}'", .{clobber}), null);
}
}
}
const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len];
const Label = struct {
target: Mir.Inst.Index = undefined,
pending_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
if (std.meta.stringToEnum(Mir.Inst.Tag, asm_source)) |tag| {
_ = try func.addInst(.{
.tag = tag,
.ops = .none,
.data = undefined,
});
} else {
return func.fail("TODO: asm_source {s}", .{asm_source});
}
const Kind = enum { definition, reference };
if (output_constraint) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return func.fail("unrecognized asm output constraint: '{s}'", .{output});
}
const reg_name = output[2 .. output.len - 1];
const reg = parseRegName(reg_name) orelse
return func.fail("unrecognized register: '{s}'", .{reg_name});
break :result .{ .register = reg };
} else {
break :result .{ .none = {} };
fn isValid(kind: Kind, name: []const u8) bool {
for (name, 0..) |c, i| switch (c) {
else => return false,
'$' => if (i == 0) return false,
'.' => {},
'0'...'9' => if (i == 0) switch (kind) {
.definition => if (name.len != 1) return false,
.reference => {
if (name.len != 2) return false;
switch (name[1]) {
else => return false,
'B', 'F', 'b', 'f' => {},
}
},
},
'@', 'A'...'Z', '_', 'a'...'z' => {},
};
return name.len > 0;
}
};
var labels: std.StringHashMapUnmanaged(Label) = .{};
defer {
var label_it = labels.valueIterator();
while (label_it.next()) |label| label.pending_relocs.deinit(func.gpa);
labels.deinit(func.gpa);
}
const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len];
var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;");
next_line: while (line_it.next()) |line| {
var mnem_it = mem.tokenizeAny(u8, line, " \t");
const mnem_str = while (mnem_it.next()) |mnem_str| {
if (mem.startsWith(u8, mnem_str, "#")) continue :next_line;
if (mem.startsWith(u8, mnem_str, "//")) continue :next_line;
if (!mem.endsWith(u8, mnem_str, ":")) break mnem_str;
const label_name = mnem_str[0 .. mnem_str.len - ":".len];
if (!Label.isValid(.definition, label_name))
return func.fail("invalid label: '{s}'", .{label_name});
const label_gop = try labels.getOrPut(func.gpa, label_name);
if (!label_gop.found_existing) label_gop.value_ptr.* = .{} else {
const anon = std.ascii.isDigit(label_name[0]);
if (!anon and label_gop.value_ptr.pending_relocs.items.len == 0)
return func.fail("redefined label: '{s}'", .{label_name});
for (label_gop.value_ptr.pending_relocs.items) |pending_reloc|
func.performReloc(pending_reloc);
if (anon)
label_gop.value_ptr.pending_relocs.clearRetainingCapacity()
else
label_gop.value_ptr.pending_relocs.clearAndFree(func.gpa);
}
label_gop.value_ptr.target = @intCast(func.mir_instructions.len);
} else continue;
const instruction: union(enum) { mnem: Mnemonic, pseudo: Pseudo } =
if (std.meta.stringToEnum(Mnemonic, mnem_str)) |mnem|
.{ .mnem = mnem }
else if (std.meta.stringToEnum(Pseudo, mnem_str)) |pseudo|
.{ .pseudo = pseudo }
else
return func.fail("invalid mnem str '{s}'", .{mnem_str});
const Operand = union(enum) {
none,
reg: Register,
imm: Immediate,
inst: Mir.Inst.Index,
sym: SymbolOffset,
};
var ops: [4]Operand = .{.none} ** 4;
var last_op = false;
var op_it = mem.splitAny(u8, mnem_it.rest(), ",(");
next_op: for (&ops) |*op| {
const op_str = while (!last_op) {
const full_str = op_it.next() orelse break :next_op;
const code_str = if (mem.indexOfScalar(u8, full_str, '#') orelse
mem.indexOf(u8, full_str, "//")) |comment|
code: {
last_op = true;
break :code full_str[0..comment];
} else full_str;
const trim_str = mem.trim(u8, code_str, " \t*");
if (trim_str.len > 0) break trim_str;
} else break;
if (parseRegName(op_str)) |reg| {
op.* = .{ .reg = reg };
} else if (std.fmt.parseInt(i12, op_str, 10)) |int| {
op.* = .{ .imm = Immediate.s(int) };
} else |_| if (mem.startsWith(u8, op_str, "%[")) {
const mod_index = mem.indexOf(u8, op_str, "]@");
const modifier = if (mod_index) |index|
op_str[index + "]@".len ..]
else
"";
op.* = switch (args.items[
arg_map.get(op_str["%[".len .. mod_index orelse op_str.len - "]".len]) orelse
return func.fail("no matching constraint: '{s}'", .{op_str})
]) {
.load_symbol => |sym_off| if (mem.eql(u8, modifier, "plt")) blk: {
assert(sym_off.off == 0);
break :blk .{ .sym = sym_off };
} else return func.fail("invalid modifier: '{s}'", .{modifier}),
.register => |reg| if (modifier.len == 0)
.{ .reg = reg }
else
return func.fail("invalid modified '{s}'", .{modifier}),
else => return func.fail("invalid constraint: '{s}'", .{op_str}),
};
} else if (mem.endsWith(u8, op_str, ")")) {
const reg = op_str[0 .. op_str.len - ")".len];
const addr_reg = parseRegName(reg) orelse
return func.fail("expected valid register, found '{s}'", .{reg});
op.* = .{ .reg = addr_reg };
} else if (Label.isValid(.reference, op_str)) {
const anon = std.ascii.isDigit(op_str[0]);
const label_gop = try labels.getOrPut(func.gpa, op_str[0..if (anon) 1 else op_str.len]);
if (!label_gop.found_existing) label_gop.value_ptr.* = .{};
if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing)
return func.fail("undefined label: '{s}'", .{op_str});
const pending_relocs = &label_gop.value_ptr.pending_relocs;
if (if (anon)
op_str[1] == 'f' or op_str[1] == 'F'
else
!label_gop.found_existing or pending_relocs.items.len > 0)
try pending_relocs.append(func.gpa, @intCast(func.mir_instructions.len));
op.* = .{ .inst = label_gop.value_ptr.target };
} else return func.fail("invalid operand: '{s}'", .{op_str});
} else if (op_it.next()) |op_str| return func.fail("extra operand: '{s}'", .{op_str});
switch (instruction) {
.mnem => |mnem| {
_ = (switch (ops[0]) {
.none => try func.addInst(.{
.tag = mnem,
.data = .none,
}),
.reg => |reg1| switch (ops[1]) {
.reg => |reg2| switch (ops[2]) {
.imm => |imm1| try func.addInst(.{
.tag = mnem,
.data = .{ .i_type = .{
.rd = reg1,
.rs1 = reg2,
.imm12 = imm1,
} },
}),
else => error.InvalidInstruction,
},
.imm => |imm1| switch (ops[2]) {
.reg => |reg2| switch (mnem) {
.sd => try func.addInst(.{
.tag = mnem,
.data = .{ .i_type = .{
.rd = reg2,
.rs1 = reg1,
.imm12 = imm1,
} },
}),
.ld => try func.addInst(.{
.tag = mnem,
.data = .{ .i_type = .{
.rd = reg1,
.rs1 = reg2,
.imm12 = imm1,
} },
}),
else => error.InvalidInstruction,
},
else => error.InvalidInstruction,
},
.none => switch (mnem) {
.jalr => try func.addInst(.{
.tag = mnem,
.data = .{ .i_type = .{
.rd = .ra,
.rs1 = reg1,
.imm12 = Immediate.s(0),
} },
}),
else => error.InvalidInstruction,
},
else => error.InvalidInstruction,
},
else => error.InvalidInstruction,
}) catch |err| {
switch (err) {
error.InvalidInstruction => return func.fail(
"invalid instruction: {s} {s} {s} {s} {s}",
.{
@tagName(mnem),
@tagName(ops[0]),
@tagName(ops[1]),
@tagName(ops[2]),
@tagName(ops[3]),
},
),
else => |e| return e,
}
};
},
.pseudo => |pseudo| {
(@as(error{InvalidInstruction}!void, switch (pseudo) {
.li => blk: {
if (ops[0] != .reg or ops[1] != .imm) {
break :blk error.InvalidInstruction;
}
const reg = ops[0].reg;
const imm = ops[1].imm;
try func.genSetReg(Type.usize, reg, .{ .immediate = imm.asBits(u64) });
},
.mv => blk: {
if (ops[0] != .reg or ops[1] != .reg) {
break :blk error.InvalidInstruction;
}
const dst = ops[0].reg;
const src = ops[1].reg;
if (dst.class() != .int or src.class() != .int) {
return func.fail("pseudo instruction 'mv' only works on integer registers", .{});
}
try func.genSetReg(Type.usize, dst, .{ .register = src });
},
.tail => blk: {
if (ops[0] != .sym) {
break :blk error.InvalidInstruction;
}
const sym_offset = ops[0].sym;
assert(sym_offset.off == 0);
const random_link_reg, const lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(lock);
_ = try func.addInst(.{
.tag = .pseudo_extern_fn_reloc,
.data = .{ .reloc = .{
.register = random_link_reg,
.atom_index = try func.owner.getSymbolIndex(func),
.sym_index = sym_offset.sym,
} },
});
},
.ret => _ = try func.addInst(.{
.tag = .jalr,
.data = .{ .i_type = .{
.rd = .zero,
.rs1 = .ra,
.imm12 = Immediate.s(0),
} },
}),
.beqz => blk: {
if (ops[0] != .reg or ops[1] != .inst) {
break :blk error.InvalidInstruction;
}
_ = try func.addInst(.{
.tag = .beq,
.data = .{ .b_type = .{
.rs1 = ops[0].reg,
.rs2 = .zero,
.inst = ops[1].inst,
} },
});
},
})) catch |err| {
switch (err) {
error.InvalidInstruction => return func.fail(
"invalid instruction: {s} {s} {s} {s} {s}",
.{
@tagName(pseudo),
@tagName(ops[0]),
@tagName(ops[1]),
@tagName(ops[2]),
@tagName(ops[3]),
},
),
else => |e| return e,
}
};
},
}
}
var label_it = labels.iterator();
while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0)
return func.fail("undefined label: '{s}'", .{label.key_ptr.*});
for (outputs, args.items[0..outputs.len]) |output, arg_mcv| {
const extra_bytes = mem.sliceAsBytes(func.air.extra[outputs_extra_i..]);
const constraint =
mem.sliceTo(mem.sliceAsBytes(func.air.extra[outputs_extra_i..]), 0);
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
// This equation accounts for the fact that even if we have exactly 4 bytes
// for the string, we still use the next u32 for the null terminator.
outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
if (output == .none) continue;
if (arg_mcv != .register) continue;
if (constraint.len == 2 and std.ascii.isDigit(constraint[1])) continue;
try func.store(.{ .air_ref = output }, arg_mcv, func.typeOf(output));
}
simple: {
var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
@@ -5668,12 +6404,19 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
ty,
src_mcv,
),
.load_tlv => {
const addr_reg, const addr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(addr_lock);
try func.genSetReg(ty, addr_reg, dst_mcv.address());
try func.genCopy(ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);
},
.memory => return func.fail("TODO: genCopy memory", .{}),
.register_pair => |dst_regs| {
const src_info: ?struct { addr_reg: Register, addr_lock: ?RegisterLock } = switch (src_mcv) {
.register_pair, .memory, .indirect, .load_frame => null,
.load_symbol => src: {
const src_addr_reg, const src_addr_lock = try func.promoteReg(Type.usize, src_mcv.address());
const src_addr_reg, const src_addr_lock = try func.promoteReg(Type.u64, src_mcv.address());
errdefer func.register_manager.unlockReg(src_addr_lock);
break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock };
@@ -5725,14 +6468,23 @@ fn genInlineMemcpy(
const src = regs[2];
const dst = regs[3];
try func.genSetReg(Type.usize, count, len);
try func.genSetReg(Type.usize, src, src_ptr);
try func.genSetReg(Type.usize, dst, dst_ptr);
try func.genSetReg(Type.u64, count, len);
try func.genSetReg(Type.u64, src, src_ptr);
try func.genSetReg(Type.u64, dst, dst_ptr);
// if count is 0, there's nothing to copy
_ = try func.addInst(.{
.tag = .beq,
.data = .{ .b_type = .{
.rs1 = count,
.rs2 = .zero,
.inst = @intCast(func.mir_instructions.len + 9),
} },
});
// lb tmp, 0(src)
const first_inst = try func.addInst(.{
.tag = .lb,
.ops = .rri,
.data = .{
.i_type = .{
.rd = tmp,
@@ -5745,7 +6497,6 @@ fn genInlineMemcpy(
// sb tmp, 0(dst)
_ = try func.addInst(.{
.tag = .sb,
.ops = .rri,
.data = .{
.i_type = .{
.rd = dst,
@@ -5758,7 +6509,6 @@ fn genInlineMemcpy(
// dec count by 1
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{
.i_type = .{
.rd = count,
@@ -5771,7 +6521,6 @@ fn genInlineMemcpy(
// branch if count is 0
_ = try func.addInst(.{
.tag = .beq,
.ops = .rr_inst,
.data = .{
.b_type = .{
.inst = @intCast(func.mir_instructions.len + 4), // points after the last inst
@@ -5784,7 +6533,6 @@ fn genInlineMemcpy(
// increment the pointers
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{
.i_type = .{
.rd = src,
@@ -5796,7 +6544,6 @@ fn genInlineMemcpy(
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{
.i_type = .{
.rd = dst,
@@ -5808,9 +6555,11 @@ fn genInlineMemcpy(
// jump back to start of loop
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_j,
.data = .{ .inst = first_inst },
.tag = .pseudo_j,
.data = .{ .j_type = .{
.rd = .zero,
.inst = first_inst,
} },
});
}
@@ -5828,14 +6577,13 @@ fn genInlineMemset(
const src = regs[1];
const dst = regs[2];
try func.genSetReg(Type.usize, count, len);
try func.genSetReg(Type.usize, src, src_value);
try func.genSetReg(Type.usize, dst, dst_ptr);
try func.genSetReg(Type.u64, count, len);
try func.genSetReg(Type.u64, src, src_value);
try func.genSetReg(Type.u64, dst, dst_ptr);
// sb src, 0(dst)
const first_inst = try func.addInst(.{
.tag = .sb,
.ops = .rri,
.data = .{
.i_type = .{
.rd = dst,
@@ -5848,7 +6596,6 @@ fn genInlineMemset(
// dec count by 1
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{
.i_type = .{
.rd = count,
@@ -5861,7 +6608,6 @@ fn genInlineMemset(
// branch if count is 0
_ = try func.addInst(.{
.tag = .beq,
.ops = .rr_inst,
.data = .{
.b_type = .{
.inst = @intCast(func.mir_instructions.len + 4), // points after the last inst
@@ -5874,7 +6620,6 @@ fn genInlineMemset(
// increment the pointers
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{
.i_type = .{
.rd = dst,
@@ -5886,11 +6631,11 @@ fn genInlineMemset(
// jump back to start of loop
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_j,
.data = .{
.tag = .pseudo_j,
.data = .{ .j_type = .{
.rd = .zero,
.inst = first_inst,
},
} },
});
}
@@ -5936,7 +6681,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = .zero,
@@ -5950,7 +6694,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
_ = try func.addInst(.{
.tag = .lui,
.ops = .ri,
.data = .{ .u_type = .{
.rd = reg,
.imm20 = Immediate.s(hi20),
@@ -5958,7 +6701,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
});
_ = try func.addInst(.{
.tag = .addi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = reg,
@@ -5981,7 +6723,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
_ = try func.addInst(.{
.tag = .slli,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = reg,
@@ -5991,7 +6732,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
_ = try func.addInst(.{
.tag = .add,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = reg,
.rs1 = reg,
@@ -6028,15 +6768,16 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
// mv reg, src_reg
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_mv,
.tag = .pseudo_mv,
.data = .{ .rr = .{
.rd = reg,
.rs = src_reg,
} },
});
},
.register_pair => return func.fail("genSetReg should we allow reg -> reg_pair?", .{}),
// useful in cases like slice_ptr, which can easily reuse the operand
// but we need to get only the pointer out.
.register_pair => |pair| try func.genSetReg(ty, reg, .{ .register = pair[0] }),
.load_frame => |frame| {
if (reg.class() == .vector) {
// vectors don't support an offset memory load so we need to put the true
@@ -6048,8 +6789,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
try func.genCopy(ty, .{ .register = reg }, .{ .indirect = .{ .reg = addr_reg } });
} else {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_load_rm,
.tag = .pseudo_load_rm,
.data = .{ .rm = .{
.r = reg,
.m = .{
@@ -6069,7 +6809,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
_ = try func.addInst(.{
.tag = .ld,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = reg,
@@ -6079,8 +6818,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
},
.lea_frame, .register_offset => {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_lea_rm,
.tag = .pseudo_lea_rm,
.data = .{
.rm = .{
.r = reg,
@@ -6108,7 +6846,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
});
},
.indirect => |reg_off| {
const load_tag: Mir.Inst.Tag = switch (reg.class()) {
const load_tag: Mnemonic = switch (reg.class()) {
.float => switch (abi_size) {
1 => unreachable, // Zig does not support 8-bit floats
2 => return func.fail("TODO: genSetReg indirect 16-bit float", .{}),
@@ -6147,8 +6885,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
});
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_load_rm,
.tag = .pseudo_load_rm,
.data = .{ .rm = .{
.r = reg,
.m = .{
@@ -6168,7 +6905,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
_ = try func.addInst(.{
.tag = load_tag,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = reg_off.reg,
@@ -6178,16 +6914,15 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
},
.lea_symbol => |sym_off| {
assert(sym_off.off == 0);
const atom_index = try func.symbolIndex();
const atom_index = try func.owner.getSymbolIndex(func);
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_load_symbol,
.data = .{ .payload = try func.addExtra(Mir.LoadSymbolPayload{
.register = reg.encodeId(),
.tag = .pseudo_load_symbol,
.data = .{ .reloc = .{
.register = reg,
.atom_index = atom_index,
.sym_index = sym_off.sym,
}) },
} },
});
},
.load_symbol => {
@@ -6197,6 +6932,25 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
try func.genSetReg(ty, addr_reg, src_mcv.address());
try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } });
},
.lea_tlv => |sym| {
const atom_index = try func.owner.getSymbolIndex(func);
_ = try func.addInst(.{
.tag = .pseudo_load_tlv,
.data = .{ .reloc = .{
.register = reg,
.atom_index = atom_index,
.sym_index = sym,
} },
});
},
.load_tlv => {
const addr_reg, const addr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(addr_lock);
try func.genSetReg(ty, addr_reg, src_mcv.address());
try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } });
},
.air_ref => |ref| try func.genSetReg(ty, reg, try func.resolveInst(ref)),
else => return func.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}),
}
@@ -6216,7 +6970,6 @@ fn genSetMem(
const dst_ptr_mcv: MCValue = switch (base) {
.reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } },
.frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } },
.reloc => |base_symbol| .{ .lea_symbol = .{ .sym = base_symbol.sym_index, .off = disp } },
};
switch (src_mcv) {
.none,
@@ -6260,7 +7013,7 @@ fn genSetMem(
},
.register => |reg| {
if (reg.class() == .vector) {
const addr_reg = try func.copyToTmpRegister(Type.usize, dst_ptr_mcv);
const addr_reg = try func.copyToTmpRegister(Type.u64, dst_ptr_mcv);
const num_elem = ty.vectorLen(zcu);
const elem_size = ty.childType(zcu).bitSize(pt);
@@ -6279,8 +7032,7 @@ fn genSetMem(
});
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_store_rm,
.tag = .pseudo_store_rm,
.data = .{ .rm = .{
.r = reg,
.m = .{
@@ -6319,8 +7071,7 @@ fn genSetMem(
}));
const frame_mcv: MCValue = .{ .load_frame = .{ .index = frame_index } };
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_store_rm,
.tag = .pseudo_store_rm,
.data = .{ .rm = .{
.r = reg,
.m = .{
@@ -6335,8 +7086,7 @@ fn genSetMem(
try func.genSetMem(base, disp, ty, frame_mcv);
try func.freeValue(frame_mcv);
} else _ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_store_rm,
.tag = .pseudo_store_rm,
.data = .{ .rm = .{
.r = reg,
.m = .{
@@ -6366,6 +7116,7 @@ fn genSetMem(
return func.genSetMem(base, disp, ty, .{ .register = reg });
},
.air_ref => |src_ref| try func.genSetMem(base, disp, ty, try func.resolveInst(src_ref)),
else => return func.fail("TODO: genSetMem {s}", .{@tagName(src_mcv)}),
}
}
@@ -6398,7 +7149,7 @@ fn airBitCast(func: *Func, inst: Air.Inst.Index) !void {
const src_lock = if (src_mcv.getReg()) |reg| func.register_manager.lockReg(reg) else null;
defer if (src_lock) |lock| func.register_manager.unlockReg(lock);
const dst_mcv = if (dst_ty.abiSize(pt) <= src_ty.abiSize(pt) and
const dst_mcv = if (dst_ty.abiSize(pt) <= src_ty.abiSize(pt) and src_mcv != .register_pair and
func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true);
try func.genCopy(switch (math.order(dst_ty.abiSize(pt), src_ty.abiSize(pt))) {
@@ -6437,7 +7188,7 @@ fn airArrayToSlice(func: *Func, inst: Air.Inst.Index) !void {
try func.genSetMem(
.{ .frame = frame_index },
@intCast(ptr_ty.abiSize(pt)),
Type.usize,
Type.u64,
.{ .immediate = array_len },
);
@@ -6447,107 +7198,409 @@ fn airArrayToSlice(func: *Func, inst: Air.Inst.Index) !void {
fn airFloatFromInt(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airFloatFromInt for {}", .{
func.target.cpu.arch,
});
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const pt = func.pt;
const zcu = pt.zcu;
const operand = try func.resolveInst(ty_op.operand);
const src_ty = func.typeOf(ty_op.operand);
const dst_ty = ty_op.ty.toType();
const src_reg, const src_lock = try func.promoteReg(src_ty, operand);
defer if (src_lock) |lock| func.register_manager.unlockReg(lock);
const is_unsigned = dst_ty.isUnsignedInt(zcu);
const src_bits = src_ty.bitSize(pt);
const dst_bits = dst_ty.bitSize(pt);
switch (src_bits) {
32, 64 => {},
else => try func.truncateRegister(src_ty, src_reg),
}
const int_mod: Mir.FcvtOp = switch (src_bits) {
8, 16, 32 => if (is_unsigned) .wu else .w,
64 => if (is_unsigned) .lu else .l,
else => return func.fail("TODO: airFloatFromInt src size: {d}", .{src_bits}),
};
const float_mod: enum { s, d } = switch (dst_bits) {
32 => .s,
64 => .d,
else => return func.fail("TODO: airFloatFromInt dst size {d}", .{dst_bits}),
};
const dst_reg, const dst_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(dst_lock);
_ = try func.addInst(.{
.tag = switch (float_mod) {
.s => switch (int_mod) {
.l => .fcvtsl,
.lu => .fcvtslu,
.w => .fcvtsw,
.wu => .fcvtswu,
},
.d => switch (int_mod) {
.l => .fcvtdl,
.lu => .fcvtdlu,
.w => .fcvtdw,
.wu => .fcvtdwu,
},
},
.data = .{ .rr = .{
.rd = dst_reg,
.rs = src_reg,
} },
});
break :result .{ .register = dst_reg };
};
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airIntFromFloat(func: *Func, inst: Air.Inst.Index) !void {
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airIntFromFloat for {}", .{
func.target.cpu.arch,
});
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const pt = func.pt;
const zcu = pt.zcu;
const operand = try func.resolveInst(ty_op.operand);
const src_ty = func.typeOf(ty_op.operand);
const dst_ty = ty_op.ty.toType();
const is_unsigned = dst_ty.isUnsignedInt(zcu);
const src_bits = src_ty.bitSize(pt);
const dst_bits = dst_ty.bitSize(pt);
const float_mod: enum { s, d } = switch (src_bits) {
32 => .s,
64 => .d,
else => return func.fail("TODO: airIntFromFloat src size {d}", .{src_bits}),
};
const int_mod: Mir.FcvtOp = switch (dst_bits) {
32 => if (is_unsigned) .wu else .w,
8, 16, 64 => if (is_unsigned) .lu else .l,
else => return func.fail("TODO: airIntFromFloat dst size: {d}", .{dst_bits}),
};
const src_reg, const src_lock = try func.promoteReg(src_ty, operand);
defer if (src_lock) |lock| func.register_manager.unlockReg(lock);
const dst_reg, const dst_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(dst_lock);
_ = try func.addInst(.{
.tag = switch (float_mod) {
.s => switch (int_mod) {
.l => .fcvtls,
.lu => .fcvtlus,
.w => .fcvtws,
.wu => .fcvtwus,
},
.d => switch (int_mod) {
.l => .fcvtld,
.lu => .fcvtlud,
.w => .fcvtwd,
.wu => .fcvtwud,
},
},
.data = .{ .rr = .{
.rd = dst_reg,
.rs = src_reg,
} },
});
break :result .{ .register = dst_reg };
};
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void {
fn airCmpxchg(func: *Func, inst: Air.Inst.Index, strength: enum { weak, strong }) !void {
_ = strength; // TODO: do something with this
const pt = func.pt;
const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = func.air.extraData(Air.Block, ty_pl.payload);
_ = extra;
return func.fail("TODO implement airCmpxchg for {}", .{
func.target.cpu.arch,
const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
const ptr_ty = func.typeOf(extra.ptr);
const val_ty = func.typeOf(extra.expected_value);
const val_abi_size: u32 = @intCast(val_ty.abiSize(pt));
switch (val_abi_size) {
1, 2, 4, 8 => {},
else => return func.fail("TODO: airCmpxchg Int size {}", .{val_abi_size}),
}
const lr_order: struct { aq: Mir.Barrier, rl: Mir.Barrier } = switch (extra.successOrder()) {
.unordered,
=> unreachable,
.monotonic,
.release,
=> .{ .aq = .none, .rl = .none },
.acquire,
.acq_rel,
=> .{ .aq = .aq, .rl = .none },
.seq_cst => .{ .aq = .aq, .rl = .rl },
};
const sc_order: struct { aq: Mir.Barrier, rl: Mir.Barrier } = switch (extra.failureOrder()) {
.unordered,
.release,
.acq_rel,
=> unreachable,
.monotonic,
.acquire,
.seq_cst,
=> switch (extra.successOrder()) {
.release,
.seq_cst,
=> .{ .aq = .none, .rl = .rl },
else => .{ .aq = .none, .rl = .none },
},
};
const ptr_mcv = try func.resolveInst(extra.ptr);
const ptr_reg, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv);
defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock);
const exp_mcv = try func.resolveInst(extra.expected_value);
const exp_reg, const exp_lock = try func.promoteReg(val_ty, exp_mcv);
defer if (exp_lock) |lock| func.register_manager.unlockReg(lock);
try func.truncateRegister(val_ty, exp_reg);
const new_mcv = try func.resolveInst(extra.new_value);
const new_reg, const new_lock = try func.promoteReg(val_ty, new_mcv);
defer if (new_lock) |lock| func.register_manager.unlockReg(lock);
try func.truncateRegister(val_ty, new_reg);
const branch_reg, const branch_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(branch_lock);
const fallthrough_reg, const fallthrough_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(fallthrough_lock);
const jump_back = try func.addInst(.{
.tag = if (val_ty.bitSize(pt) <= 32) .lrw else .lrd,
.data = .{ .amo = .{
.aq = lr_order.aq,
.rl = lr_order.rl,
.rd = branch_reg,
.rs1 = ptr_reg,
.rs2 = .zero,
} },
});
// return func.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
try func.truncateRegister(val_ty, branch_reg);
const jump_forward = try func.addInst(.{
.tag = .bne,
.data = .{ .b_type = .{
.rs1 = branch_reg,
.rs2 = exp_reg,
.inst = undefined,
} },
});
_ = try func.addInst(.{
.tag = if (val_ty.bitSize(pt) <= 32) .scw else .scd,
.data = .{ .amo = .{
.aq = sc_order.aq,
.rl = sc_order.rl,
.rd = fallthrough_reg,
.rs1 = ptr_reg,
.rs2 = new_reg,
} },
});
try func.truncateRegister(Type.bool, fallthrough_reg);
_ = try func.addInst(.{
.tag = .bne,
.data = .{ .b_type = .{
.rs1 = fallthrough_reg,
.rs2 = .zero,
.inst = jump_back,
} },
});
func.performReloc(jump_forward);
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, false);
const tmp_reg, const tmp_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(tmp_lock);
try func.genBinOp(
.cmp_neq,
.{ .register = branch_reg },
val_ty,
.{ .register = exp_reg },
val_ty,
tmp_reg,
);
try func.genCopy(val_ty, dst_mcv, .{ .register = branch_reg });
try func.genCopy(
Type.bool,
dst_mcv.address().offset(@intCast(val_abi_size)).deref(),
.{ .register = tmp_reg },
);
break :result dst_mcv;
};
return func.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
}
fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
const pt = func.pt;
const zcu = pt.zcu;
const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
const op = extra.op();
const order = extra.ordering();
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const op = extra.op();
const order = extra.ordering();
const ptr_ty = func.typeOf(pl_op.operand);
const ptr_mcv = try func.resolveInst(pl_op.operand);
const ptr_ty = func.typeOf(pl_op.operand);
const ptr_mcv = try func.resolveInst(pl_op.operand);
const val_ty = func.typeOf(extra.operand);
const val_size = val_ty.abiSize(pt);
const val_mcv = try func.resolveInst(extra.operand);
const val_ty = func.typeOf(extra.operand);
const val_size = val_ty.abiSize(pt);
const val_mcv = try func.resolveInst(extra.operand);
if (!math.isPowerOfTwo(val_size))
return func.fail("TODO: airAtomicRmw non-pow 2", .{});
if (!math.isPowerOfTwo(val_size))
return func.fail("TODO: airAtomicRmw non-pow 2", .{});
switch (val_ty.zigTypeTag(pt.zcu)) {
.Int => {},
inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),
else => unreachable,
}
switch (val_ty.zigTypeTag(pt.zcu)) {
.Enum, .Int => {},
inline .Bool, .Float, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}),
else => unreachable,
}
switch (val_size) {
1, 2 => return func.fail("TODO: airAtomicRmw Int {}", .{val_size}),
4, 8 => {},
else => unreachable,
}
const method: enum { amo, loop } = switch (val_size) {
1, 2 => .loop,
4, 8 => .amo,
else => unreachable,
};
const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv);
defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock);
const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv);
defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock);
const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv);
defer if (val_lock) |lock| func.register_manager.unlockReg(lock);
const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv);
defer if (val_lock) |lock| func.register_manager.unlockReg(lock);
const result_mcv = try func.allocRegOrMem(val_ty, inst, true);
assert(result_mcv == .register); // should fit into 8 bytes
const result_mcv = try func.allocRegOrMem(val_ty, inst, true);
assert(result_mcv == .register); // should fit into 8 bytes
const result_reg = result_mcv.register;
const aq, const rl = switch (order) {
.unordered => unreachable,
.monotonic => .{ false, false },
.acquire => .{ true, false },
.release => .{ false, true },
.acq_rel => .{ true, true },
.seq_cst => .{ true, true },
const aq, const rl = switch (order) {
.unordered => unreachable,
.monotonic => .{ false, false },
.acquire => .{ true, false },
.release => .{ false, true },
.acq_rel => .{ true, true },
.seq_cst => .{ true, true },
};
switch (method) {
.amo => {
const is_d = val_ty.abiSize(pt) == 8;
const is_un = val_ty.isUnsignedInt(zcu);
const mnem: Mnemonic = switch (op) {
// zig fmt: off
.Xchg => if (is_d) .amoswapd else .amoswapw,
.Add => if (is_d) .amoaddd else .amoaddw,
.And => if (is_d) .amoandd else .amoandw,
.Or => if (is_d) .amoord else .amoorw,
.Xor => if (is_d) .amoxord else .amoxorw,
.Max => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw,
.Min => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw,
else => return func.fail("TODO: airAtomicRmw amo {s}", .{@tagName(op)}),
// zig fmt: on
};
_ = try func.addInst(.{
.tag = mnem,
.data = .{ .amo = .{
.rd = result_reg,
.rs1 = ptr_register,
.rs2 = val_register,
.aq = if (aq) .aq else .none,
.rl = if (rl) .rl else .none,
} },
});
},
.loop => {
// where we'll jump back when the sc fails
const jump_back = try func.addInst(.{
.tag = .lrw,
.data = .{ .amo = .{
.rd = result_reg,
.rs1 = ptr_register,
.rs2 = .zero,
.aq = if (aq) .aq else .none,
.rl = if (rl) .rl else .none,
} },
});
const after_reg, const after_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(after_lock);
switch (op) {
.Add, .Sub => |tag| {
_ = try func.genBinOp(
switch (tag) {
.Add => .add,
.Sub => .sub,
else => unreachable,
},
.{ .register = result_reg },
val_ty,
.{ .register = val_register },
val_ty,
after_reg,
);
},
else => return func.fail("TODO: airAtomicRmw loop {s}", .{@tagName(op)}),
}
_ = try func.addInst(.{
.tag = .scw,
.data = .{ .amo = .{
.rd = after_reg,
.rs1 = ptr_register,
.rs2 = after_reg,
.aq = if (aq) .aq else .none,
.rl = if (rl) .rl else .none,
} },
});
_ = try func.addInst(.{
.tag = .bne,
.data = .{ .b_type = .{
.inst = jump_back,
.rs1 = after_reg,
.rs2 = .zero,
} },
});
},
}
break :result result_mcv;
};
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_amo,
.data = .{ .amo = .{
.rd = result_mcv.register,
.rs1 = ptr_register,
.rs2 = val_register,
.aq = if (aq) .aq else .none,
.rl = if (rl) .rl else .none,
.op = switch (op) {
.Xchg => .SWAP,
.Add => .ADD,
.Sub => return func.fail("TODO: airAtomicRmw SUB", .{}),
.And => .AND,
.Nand => return func.fail("TODO: airAtomicRmw NAND", .{}),
.Or => .OR,
.Xor => .XOR,
.Max => .MAX,
.Min => .MIN,
},
.ty = val_ty,
} },
});
return func.finishAir(inst, result_mcv, .{ pl_op.operand, extra.operand, .none });
return func.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none });
}
fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
const zcu = func.pt.zcu;
const pt = func.pt;
const zcu = pt.zcu;
const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
const order: std.builtin.AtomicOrder = atomic_load.order;
@@ -6555,20 +7608,19 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
const elem_ty = ptr_ty.childType(zcu);
const ptr_mcv = try func.resolveInst(atomic_load.ptr);
const bit_size = elem_ty.bitSize(pt);
if (bit_size > 64) return func.fail("TODO: airAtomicStore > 64 bits", .{});
const result_mcv = try func.allocRegOrMem(elem_ty, inst, true);
assert(result_mcv == .register); // should be less than 8 bytes
if (order == .seq_cst) {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_fence,
.data = .{
.fence = .{
.pred = .rw,
.succ = .rw,
.fm = .none,
},
},
.tag = .fence,
.data = .{ .fence = .{
.pred = .rw,
.succ = .rw,
} },
});
}
@@ -6581,15 +7633,11 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
// Make sure all previous reads happen before any reading or writing accurs.
.seq_cst, .acquire => {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_fence,
.data = .{
.fence = .{
.pred = .r,
.succ = .rw,
.fm = .none,
},
},
.tag = .fence,
.data = .{ .fence = .{
.pred = .r,
.succ = .rw,
} },
});
},
else => unreachable,
@@ -6607,25 +7655,24 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr
const val_ty = func.typeOf(bin_op.rhs);
const val_mcv = try func.resolveInst(bin_op.rhs);
const bit_size = val_ty.bitSize(func.pt);
if (bit_size > 64) return func.fail("TODO: airAtomicStore > 64 bits", .{});
switch (order) {
.unordered, .monotonic => {},
.release, .seq_cst => {
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_fence,
.data = .{
.fence = .{
.pred = .rw,
.succ = .w,
.fm = .none,
},
},
.tag = .fence,
.data = .{ .fence = .{
.pred = .rw,
.succ = .w,
} },
});
},
else => unreachable,
}
try func.store(ptr_mcv, val_mcv, ptr_ty, val_ty);
try func.store(ptr_mcv, val_mcv, ptr_ty);
return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
}
@@ -6689,15 +7736,15 @@ fn airMemset(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
const len = dst_ptr_ty.childType(zcu).arrayLen(zcu);
assert(len != 0); // prevented by Sema
try func.store(dst_ptr, src_val, elem_ptr_ty, elem_ty);
try func.store(dst_ptr, src_val, elem_ptr_ty);
const second_elem_ptr_reg, const second_elem_ptr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(second_elem_ptr_lock);
const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg };
try func.genSetReg(Type.usize, second_elem_ptr_reg, .{ .register_offset = .{
.reg = try func.copyToTmpRegister(Type.usize, dst_ptr),
try func.genSetReg(Type.u64, second_elem_ptr_reg, .{ .register_offset = .{
.reg = try func.copyToTmpRegister(Type.u64, dst_ptr),
.off = elem_abi_size,
} });
@@ -6711,123 +7758,94 @@ fn airMemset(func: *Func, inst: Air.Inst.Index, safety: bool) !void {
}
fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void {
_ = inst;
return func.fail("TODO implement airMemcpy for {}", .{func.target.cpu.arch});
const pt = func.pt;
const zcu = pt.zcu;
const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const dst_ptr = try func.resolveInst(bin_op.lhs);
const src_ptr = try func.resolveInst(bin_op.rhs);
const dst_ty = func.typeOf(bin_op.lhs);
const len_mcv: MCValue = switch (dst_ty.ptrSize(zcu)) {
.Slice => len: {
const len_reg, const len_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(len_lock);
const elem_size = dst_ty.childType(zcu).abiSize(pt);
try func.genBinOp(
.mul,
.{ .immediate = elem_size },
Type.u64,
dst_ptr.address().offset(8).deref(),
Type.u64,
len_reg,
);
break :len .{ .register = len_reg };
},
.One => len: {
const array_ty = dst_ty.childType(zcu);
break :len .{ .immediate = array_ty.arrayLen(zcu) * array_ty.childType(zcu).abiSize(pt) };
},
else => |size| return func.fail("TODO: airMemcpy size {s}", .{@tagName(size)}),
};
const len_lock: ?RegisterLock = switch (len_mcv) {
.register => |reg| func.register_manager.lockRegAssumeUnused(reg),
else => null,
};
defer if (len_lock) |lock| func.register_manager.unlockReg(lock);
try func.genInlineMemcpy(dst_ptr, src_ptr, len_mcv);
return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airTagName(func: *Func, inst: Air.Inst.Index) !void {
const pt = func.pt;
const zcu = pt.zcu;
const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
const operand = try func.resolveInst(un_op);
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else {
_ = operand;
return func.fail("TODO implement airTagName for riscv64", .{});
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const enum_ty = func.typeOf(un_op);
// TODO: work out the bugs
if (true) return func.fail("TODO: airTagName", .{});
const param_regs = abi.Registers.Integer.function_arg_regs;
const dst_mcv = try func.allocRegOrMem(Type.u64, inst, false);
try func.genSetReg(Type.u64, param_regs[0], dst_mcv.address());
const operand = try func.resolveInst(un_op);
try func.genSetReg(enum_ty, param_regs[1], operand);
const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(zcu), zcu);
const elf_file = func.bin_file.cast(link.File.Elf).?;
const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
return func.fail("{s} creating lazy symbol", .{@errorName(err)});
const sym = elf_file.symbol(sym_index);
if (func.mod.pic) {
return func.fail("TODO: airTagName pic", .{});
} else {
try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } });
_ = try func.addInst(.{
.tag = .jalr,
.data = .{ .i_type = .{
.rd = .ra,
.rs1 = .ra,
.imm12 = Immediate.s(0),
} },
});
}
break :result dst_mcv;
};
return func.finishAir(inst, result, .{ un_op, .none, .none });
}
fn airErrorName(func: *Func, inst: Air.Inst.Index) !void {
const pt = func.pt;
const zcu = pt.zcu;
const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
const err_ty = func.typeOf(un_op);
const err_mcv = try func.resolveInst(un_op);
const err_reg = try func.copyToTmpRegister(err_ty, err_mcv);
const err_lock = func.register_manager.lockRegAssumeUnused(err_reg);
defer func.register_manager.unlockReg(err_lock);
const addr_reg, const addr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(addr_lock);
// this is now the base address of the error name table
const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, zcu);
if (func.bin_file.cast(link.File.Elf)) |elf_file| {
const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
return func.fail("{s} creating lazy symbol", .{@errorName(err)});
const sym = elf_file.symbol(sym_index);
try func.genSetReg(Type.usize, addr_reg, .{ .load_symbol = .{ .sym = sym.esym_index } });
} else {
return func.fail("TODO: riscv non-elf", .{});
}
const start_reg, const start_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(start_lock);
const end_reg, const end_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(end_lock);
// const tmp_reg, const tmp_lock = try func.allocReg(.int);
// defer func.register_manager.unlockReg(tmp_lock);
// we move the base address forward by the following formula: base + (errno * 8)
// shifting left by 4 is the same as multiplying by 8
_ = try func.addInst(.{
.tag = .slli,
.ops = .rri,
.data = .{ .i_type = .{
.imm12 = Immediate.u(4),
.rd = err_reg,
.rs1 = err_reg,
} },
});
_ = try func.addInst(.{
.tag = .add,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = addr_reg,
.rs1 = addr_reg,
.rs2 = err_reg,
} },
});
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_load_rm,
.data = .{
.rm = .{
.r = start_reg,
.m = .{
.base = .{ .reg = addr_reg },
.mod = .{ .size = .dword, .unsigned = true },
},
},
},
});
_ = try func.addInst(.{
.tag = .pseudo,
.ops = .pseudo_load_rm,
.data = .{
.rm = .{
.r = end_reg,
.m = .{
.base = .{ .reg = addr_reg },
.mod = .{ .size = .dword, .unsigned = true },
},
},
},
});
const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, false);
const frame = dst_mcv.load_frame;
try func.genSetMem(
.{ .frame = frame.index },
frame.off,
Type.usize,
.{ .register = start_reg },
);
try func.genSetMem(
.{ .frame = frame.index },
frame.off + 8,
Type.usize,
.{ .register = end_reg },
);
return func.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
_ = inst;
return func.fail("TODO: airErrorName", .{});
}
fn airSplat(func: *Func, inst: Air.Inst.Index) !void {
@@ -7013,9 +8031,10 @@ fn getResolvedInstValue(func: *Func, inst: Air.Inst.Index) *InstTracking {
fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
const pt = func.pt;
const zcu = pt.zcu;
const gpa = func.gpa;
const owner_decl_index = pt.zcu.funcOwnerDeclIndex(func.func_index);
const owner_decl_index = func.owner.getDecl(zcu);
const lf = func.bin_file;
const src_loc = func.src_loc;
@@ -7047,9 +8066,10 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
.none => .none,
.undef => unreachable,
.load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
.load_tlv => |sym_index| .{ .lea_tlv = sym_index },
.immediate => |imm| .{ .immediate = imm },
.memory => |addr| .{ .memory = addr },
.load_got, .load_direct, .load_tlv => {
.load_got, .load_direct => {
return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)});
},
},
+91 -72
View File
@@ -40,7 +40,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.source = start_offset,
.target = target,
.offset = 0,
.enc = std.meta.activeTag(lowered_inst.encoding.data),
.fmt = std.meta.activeTag(lowered_inst),
}),
.load_symbol_reloc => |symbol| {
const is_obj_or_static_lib = switch (emit.lower.output_mode) {
@@ -49,46 +49,70 @@ pub fn emitMir(emit: *Emit) Error!void {
.Lib => emit.lower.link_mode == .static,
};
if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index);
const sym = elf_file.symbol(sym_index);
const elf_file = emit.bin_file.cast(link.File.Elf).?;
var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index);
const sym = elf_file.symbol(sym_index);
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
_ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
hi_r_type = Elf.R_ZIG_GOT_HI20;
lo_r_type = Elf.R_ZIG_GOT_LO12;
}
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
_ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
.r_addend = 0,
});
hi_r_type = Elf.R_ZIG_GOT_HI20;
lo_r_type = Elf.R_ZIG_GOT_LO12;
}
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset + 4,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
.r_addend = 0,
});
} else unreachable;
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
.r_addend = 0,
});
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset + 4,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
.r_addend = 0,
});
},
.load_tlv_reloc => |symbol| {
const elf_file = emit.bin_file.cast(link.File.Elf).?;
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
const R_RISCV = std.elf.R_RISCV;
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
.r_addend = 0,
});
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset + 4,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
.r_addend = 0,
});
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset + 8,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
.r_addend = 0,
});
},
.call_extern_fn_reloc => |symbol| {
if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
const elf_file = emit.bin_file.cast(link.File.Elf).?;
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
.r_addend = 0,
});
} else return emit.fail("TODO: call_extern_fn_reloc non-ELF", .{});
try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
.r_addend = 0,
});
},
};
}
@@ -98,40 +122,37 @@ pub fn emitMir(emit: *Emit) Error!void {
const mir_inst = emit.lower.mir.instructions.get(mir_index);
switch (mir_inst.tag) {
else => unreachable,
.pseudo => switch (mir_inst.ops) {
else => unreachable,
.pseudo_dbg_prologue_end => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setPrologueEnd();
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine(
mir_inst.data.pseudo_dbg_line_column.line,
mir_inst.data.pseudo_dbg_line_column.column,
),
.pseudo_dbg_epilogue_begin => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setEpilogueBegin();
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.pseudo_dead => {},
.pseudo_dbg_prologue_end => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setPrologueEnd();
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine(
mir_inst.data.pseudo_dbg_line_column.line,
mir_inst.data.pseudo_dbg_line_column.column,
),
.pseudo_dbg_epilogue_begin => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setEpilogueBegin();
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.pseudo_dead => {},
}
}
}
@@ -151,8 +172,8 @@ const Reloc = struct {
target: Mir.Inst.Index,
/// Offset of the relocation within the instruction.
offset: u32,
/// Encoding of the instruction, used to determine how to modify it.
enc: Encoding.InstEnc,
/// Format of the instruction, used to determine how to modify it.
fmt: encoding.Lir.Format,
};
fn fixupRelocs(emit: *Emit) Error!void {
@@ -164,12 +185,10 @@ fn fixupRelocs(emit: *Emit) Error!void {
const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4];
log.debug("disp: {x}", .{disp});
switch (reloc.enc) {
switch (reloc.fmt) {
.J => riscv_util.writeInstJ(code, @bitCast(disp)),
.B => riscv_util.writeInstB(code, @bitCast(disp)),
else => return emit.fail("tried to reloc encoding type {s}", .{@tagName(reloc.enc)}),
else => return emit.fail("tried to reloc format type {s}", .{@tagName(reloc.fmt)}),
}
}
}
@@ -209,5 +228,5 @@ const Emit = @This();
const Lower = @import("Lower.zig");
const Mir = @import("Mir.zig");
const riscv_util = @import("../../link/riscv.zig");
const Encoding = @import("Encoding.zig");
const Elf = @import("../../link/Elf.zig");
const encoding = @import("encoding.zig");
-1119
View File
@@ -1,1119 +0,0 @@
mnemonic: Mnemonic,
data: Data,
const OpCode = enum(u7) {
LOAD = 0b0000011,
LOAD_FP = 0b0000111,
MISC_MEM = 0b0001111,
OP_IMM = 0b0010011,
AUIPC = 0b0010111,
OP_IMM_32 = 0b0011011,
STORE = 0b0100011,
STORE_FP = 0b0100111,
AMO = 0b0101111,
OP_V = 0b1010111,
OP = 0b0110011,
OP_32 = 0b0111011,
LUI = 0b0110111,
MADD = 0b1000011,
MSUB = 0b1000111,
NMSUB = 0b1001011,
NMADD = 0b1001111,
OP_FP = 0b1010011,
OP_IMM_64 = 0b1011011,
BRANCH = 0b1100011,
JALR = 0b1100111,
JAL = 0b1101111,
SYSTEM = 0b1110011,
OP_64 = 0b1111011,
NONE = 0b00000000,
};
const FpFmt = enum(u2) {
/// 32-bit single-precision
S = 0b00,
/// 64-bit double-precision
D = 0b01,
// H = 0b10, unused in the G extension
/// 128-bit quad-precision
Q = 0b11,
};
const AmoWidth = enum(u3) {
W = 0b010,
D = 0b011,
};
const FenceMode = enum(u4) {
none = 0b0000,
tso = 0b1000,
};
const Enc = struct {
opcode: OpCode,
data: union(enum) {
/// funct3 + funct7
ff: struct {
funct3: u3,
funct7: u7,
},
amo: struct {
funct5: u5,
width: AmoWidth,
},
fence: struct {
funct3: u3,
fm: FenceMode,
},
/// funct5 + rm + fmt
fmt: struct {
funct5: u5,
rm: u3,
fmt: FpFmt,
},
/// funct3
f: struct {
funct3: u3,
},
/// typ + funct3 + has_5
sh: struct {
typ: u6,
funct3: u3,
has_5: bool,
},
vecls: struct {
width: VecWidth,
umop: Umop,
vm: bool,
mop: Mop,
mew: bool,
nf: u3,
},
vecmath: struct {
vm: bool,
funct6: u6,
funct3: VecType,
},
/// U-type
none,
},
const Mop = enum(u2) {
unit = 0b00,
unord = 0b01,
stride = 0b10,
ord = 0b11,
};
const Umop = enum(u5) {
unit = 0b00000,
whole = 0b01000,
mask = 0b01011,
fault = 0b10000,
};
const VecWidth = enum(u3) {
// zig fmt: off
@"8" = 0b000,
@"16" = 0b101,
@"32" = 0b110,
@"64" = 0b111,
// zig fmt: on
};
const VecType = enum(u3) {
OPIVV = 0b000,
OPFVV = 0b001,
OPMVV = 0b010,
OPIVI = 0b011,
OPIVX = 0b100,
OPFVF = 0b101,
OPMVX = 0b110,
};
};
pub const Mnemonic = enum {
// base mnemonics
// I Type
ld,
lw,
lwu,
lh,
lhu,
lb,
lbu,
sltiu,
xori,
andi,
slli,
srli,
srai,
slliw,
srliw,
sraiw,
addi,
jalr,
vsetivli,
vsetvli,
// U Type
lui,
auipc,
// S Type
sd,
sw,
sh,
sb,
// J Type
jal,
// B Type
beq,
// R Type
add,
addw,
sub,
subw,
@"and",
@"or",
slt,
sltu,
xor,
sll,
srl,
sra,
sllw,
srlw,
sraw,
// System
ecall,
ebreak,
unimp,
csrrs,
// M extension
mul,
mulw,
mulh,
mulhu,
mulhsu,
div,
divu,
divw,
divuw,
rem,
remu,
remw,
remuw,
// F extension (32-bit float)
fadds,
fsubs,
fmuls,
fdivs,
fmins,
fmaxs,
fsqrts,
flw,
fsw,
feqs,
flts,
fles,
fsgnjns,
fsgnjxs,
// D extension (64-bit float)
faddd,
fsubd,
fmuld,
fdivd,
fmind,
fmaxd,
fsqrtd,
fld,
fsd,
feqd,
fltd,
fled,
fsgnjnd,
fsgnjxd,
// V Extension
vle8v,
vle16v,
vle32v,
vle64v,
vse8v,
vse16v,
vse32v,
vse64v,
vsoxei8v,
vaddvv,
vsubvv,
vfaddvv,
vfsubvv,
vadcvv,
vmvvx,
vslidedownvx,
// MISC
fence,
fencetso,
// AMO
amoswapw,
amoaddw,
amoandw,
amoorw,
amoxorw,
amomaxw,
amominw,
amomaxuw,
amominuw,
amoswapd,
amoaddd,
amoandd,
amoord,
amoxord,
amomaxd,
amomind,
amomaxud,
amominud,
// TODO: Q extension
pub fn encoding(mnem: Mnemonic) Enc {
return switch (mnem) {
// zig fmt: off
// OP
.add => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
.sub => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } },
.@"and" => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } },
.@"or" => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } },
.xor => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } },
.sltu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } },
.slt => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } },
.mul => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
.mulh => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000001 } } },
.mulhsu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000001 } } },
.mulhu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000001 } } },
.div => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } },
.divu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } },
.rem => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } },
.remu => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } },
.sll => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
.srl => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } },
.sra => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } },
// OP_IMM
.addi => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b000 } } },
.andi => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b111 } } },
.xori => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b100 } } },
.sltiu => .{ .opcode = .OP_IMM, .data = .{ .f = .{ .funct3 = 0b011 } } },
.slli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = true } } },
.srli => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } },
.srai => .{ .opcode = .OP_IMM, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } },
// OP_IMM_32
.slliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = false } } },
.srliw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } },
.sraiw => .{ .opcode = .OP_IMM_32, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } },
// OP_32
.addw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
.subw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } },
.mulw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
.divw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } },
.divuw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } },
.remw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } },
.remuw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } },
.sllw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
.srlw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } },
.sraw => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } },
// OP_FP
.fadds => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } },
.faddd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } },
.fsubs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .S, .rm = 0b111 } } },
.fsubd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .D, .rm = 0b111 } } },
.fmuls => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .S, .rm = 0b111 } } },
.fmuld => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .D, .rm = 0b111 } } },
.fdivs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .S, .rm = 0b111 } } },
.fdivd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .D, .rm = 0b111 } } },
.fmins => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b000 } } },
.fmind => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b000 } } },
.fmaxs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b001 } } },
.fmaxd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b001 } } },
.fsqrts => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .S, .rm = 0b111 } } },
.fsqrtd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .D, .rm = 0b111 } } },
.fles => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b000 } } },
.fled => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b000 } } },
.flts => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b001 } } },
.fltd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b001 } } },
.feqs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } },
.feqd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } },
.fsgnjns => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b000 } } },
.fsgnjnd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b000 } } },
.fsgnjxs => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b0010} } },
.fsgnjxd => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b0010} } },
// LOAD
.lb => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b000 } } },
.lh => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b001 } } },
.lw => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b010 } } },
.ld => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b011 } } },
.lbu => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b100 } } },
.lhu => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b101 } } },
.lwu => .{ .opcode = .LOAD, .data = .{ .f = .{ .funct3 = 0b110 } } },
// STORE
.sb => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b000 } } },
.sh => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b001 } } },
.sw => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b010 } } },
.sd => .{ .opcode = .STORE, .data = .{ .f = .{ .funct3 = 0b011 } } },
// LOAD_FP
.flw => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },
.fld => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },
.vle8v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vle16v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vle32v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vle64v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
// STORE_FP
.fsw => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } },
.fsd => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } },
.vse8v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vse16v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vse32v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vse64v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vsoxei8v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .ord, .mew = false, .nf = 0b000 } } },
// JALR
.jalr => .{ .opcode = .JALR, .data = .{ .f = .{ .funct3 = 0b000 } } },
// LUI
.lui => .{ .opcode = .LUI, .data = .{ .none = {} } },
// AUIPC
.auipc => .{ .opcode = .AUIPC, .data = .{ .none = {} } },
// JAL
.jal => .{ .opcode = .JAL, .data = .{ .none = {} } },
// BRANCH
.beq => .{ .opcode = .BRANCH, .data = .{ .f = .{ .funct3 = 0b000 } } },
// SYSTEM
.ecall => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
.ebreak => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } },
.csrrs => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b010 } } },
// NONE
.unimp => .{ .opcode = .NONE, .data = .{ .f = .{ .funct3 = 0b000 } } },
// MISC_MEM
.fence => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } },
.fencetso => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso } } },
// AMO
.amoaddw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } },
.amoswapw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } },
// LR.W
// SC.W
.amoxorw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } },
.amoandw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } },
.amoorw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } },
.amominw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10000 } } },
.amomaxw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10100 } } },
.amominuw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } },
.amomaxuw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } },
.amoaddd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } },
.amoswapd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } },
// LR.D
// SC.D
.amoxord => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } },
.amoandd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } },
.amoord => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } },
.amomind => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10000 } } },
.amomaxd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10100 } } },
.amominud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } },
.amomaxud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },
// OP_V
.vsetivli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } },
.vsetvli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } },
.vaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } },
.vsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } },
.vfaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } },
.vfsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } },
.vadcvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } },
.vmvvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } },
.vslidedownvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b001111, .funct3 = .OPIVX } } },
// zig fmt: on
};
}
};
pub const InstEnc = enum {
R,
R4,
I,
S,
B,
U,
J,
fence,
amo,
system,
pub fn fromMnemonic(mnem: Mnemonic) InstEnc {
return switch (mnem) {
.addi,
.jalr,
.sltiu,
.xori,
.andi,
.slli,
.srli,
.srai,
.slliw,
.srliw,
.sraiw,
.ld,
.lw,
.lwu,
.lh,
.lhu,
.lb,
.lbu,
.flw,
.fld,
.csrrs,
.vsetivli,
.vsetvli,
=> .I,
.lui,
.auipc,
=> .U,
.sd,
.sw,
.sh,
.sb,
.fsd,
.fsw,
=> .S,
.jal,
=> .J,
.beq,
=> .B,
.slt,
.sltu,
.sll,
.srl,
.sra,
.sllw,
.srlw,
.sraw,
.div,
.divu,
.divw,
.divuw,
.rem,
.remu,
.remw,
.remuw,
.xor,
.@"and",
.@"or",
.add,
.addw,
.sub,
.subw,
.mul,
.mulw,
.mulh,
.mulhu,
.mulhsu,
.fadds,
.faddd,
.fsubs,
.fsubd,
.fmuls,
.fmuld,
.fdivs,
.fdivd,
.fmins,
.fmind,
.fmaxs,
.fmaxd,
.fsqrts,
.fsqrtd,
.fles,
.fled,
.flts,
.fltd,
.feqs,
.feqd,
.fsgnjns,
.fsgnjnd,
.fsgnjxs,
.fsgnjxd,
.vle8v,
.vle16v,
.vle32v,
.vle64v,
.vse8v,
.vse16v,
.vse32v,
.vse64v,
.vsoxei8v,
.vaddvv,
.vsubvv,
.vfaddvv,
.vfsubvv,
.vadcvv,
.vmvvx,
.vslidedownvx,
=> .R,
.ecall,
.ebreak,
.unimp,
=> .system,
.fence,
.fencetso,
=> .fence,
.amoswapw,
.amoaddw,
.amoandw,
.amoorw,
.amoxorw,
.amomaxw,
.amominw,
.amomaxuw,
.amominuw,
.amoswapd,
.amoaddd,
.amoandd,
.amoord,
.amoxord,
.amomaxd,
.amomind,
.amomaxud,
.amominud,
=> .amo,
};
}
pub fn opsList(enc: InstEnc) [5]std.meta.FieldEnum(Operand) {
return switch (enc) {
// zig fmt: off
.R => .{ .reg, .reg, .reg, .none, .none, },
.R4 => .{ .reg, .reg, .reg, .reg, .none, },
.I => .{ .reg, .reg, .imm, .none, .none, },
.S => .{ .reg, .reg, .imm, .none, .none, },
.B => .{ .reg, .reg, .imm, .none, .none, },
.U => .{ .reg, .imm, .none, .none, .none, },
.J => .{ .reg, .imm, .none, .none, .none, },
.system => .{ .none, .none, .none, .none, .none, },
.fence => .{ .barrier, .barrier, .none, .none, .none, },
.amo => .{ .reg, .reg, .reg, .barrier, .barrier },
// zig fmt: on
};
}
};
pub const Data = union(InstEnc) {
R: packed struct {
opcode: u7,
rd: u5,
funct3: u3,
rs1: u5,
rs2: u5,
funct7: u7,
},
R4: packed struct {
opcode: u7,
rd: u5,
funct3: u3,
rs1: u5,
rs2: u5,
funct2: u2,
rs3: u5,
},
I: packed struct {
opcode: u7,
rd: u5,
funct3: u3,
rs1: u5,
imm0_11: u12,
},
S: packed struct {
opcode: u7,
imm0_4: u5,
funct3: u3,
rs1: u5,
rs2: u5,
imm5_11: u7,
},
B: packed struct {
opcode: u7,
imm11: u1,
imm1_4: u4,
funct3: u3,
rs1: u5,
rs2: u5,
imm5_10: u6,
imm12: u1,
},
U: packed struct {
opcode: u7,
rd: u5,
imm12_31: u20,
},
J: packed struct {
opcode: u7,
rd: u5,
imm12_19: u8,
imm11: u1,
imm1_10: u10,
imm20: u1,
},
fence: packed struct {
opcode: u7,
rd: u5 = 0,
funct3: u3,
rs1: u5 = 0,
succ: u4,
pred: u4,
fm: u4,
},
amo: packed struct {
opcode: u7,
rd: u5,
funct3: u3,
rs1: u5,
rs2: u5,
rl: bool,
aq: bool,
funct5: u5,
},
system: u32,
comptime {
for (std.meta.fields(Data)) |field| {
assert(@bitSizeOf(field.type) == 32);
}
}
pub fn toU32(self: Data) u32 {
return switch (self) {
.fence => |v| @as(u32, @intCast(v.opcode)) + (@as(u32, @intCast(v.rd)) << 7) + (@as(u32, @intCast(v.funct3)) << 12) + (@as(u32, @intCast(v.rs1)) << 15) + (@as(u32, @intCast(v.succ)) << 20) + (@as(u32, @intCast(v.pred)) << 24) + (@as(u32, @intCast(v.fm)) << 28),
inline else => |v| @bitCast(v),
.system => unreachable,
};
}
pub fn construct(mnem: Mnemonic, ops: []const Operand) !Data {
const inst_enc = InstEnc.fromMnemonic(mnem);
const enc = mnem.encoding();
// special mnemonics
switch (mnem) {
.ecall,
.ebreak,
.unimp,
=> {
assert(ops.len == 0);
return .{
.I = .{
.rd = Register.zero.encodeId(),
.rs1 = Register.zero.encodeId(),
.imm0_11 = switch (mnem) {
.ecall => 0x000,
.ebreak => 0x001,
.unimp => 0x000,
else => unreachable,
},
.opcode = @intFromEnum(enc.opcode),
.funct3 = enc.data.f.funct3,
},
};
},
.csrrs => {
assert(ops.len == 3);
const csr = ops[0].csr;
const rs1 = ops[1].reg;
const rd = ops[2].reg;
return .{
.I = .{
.rd = rd.encodeId(),
.rs1 = rs1.encodeId(),
.imm0_11 = @intFromEnum(csr),
.opcode = @intFromEnum(enc.opcode),
.funct3 = enc.data.f.funct3,
},
};
},
else => {},
}
switch (inst_enc) {
.R => {
assert(ops.len == 3);
return .{
.R = switch (enc.data) {
.ff => |ff| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = @intFromEnum(enc.opcode),
.funct3 = ff.funct3,
.funct7 = ff.funct7,
},
.fmt => |fmt| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = @intFromEnum(enc.opcode),
.funct3 = fmt.rm,
.funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt),
},
.vecls => |vec| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = @intFromEnum(vec.umop),
.opcode = @intFromEnum(enc.opcode),
.funct3 = @intFromEnum(vec.width),
.funct7 = (@as(u7, vec.nf) << 4) | (@as(u7, @intFromBool(vec.mew)) << 3) | (@as(u7, @intFromEnum(vec.mop)) << 1) | @intFromBool(vec.vm),
},
.vecmath => |vec| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = @intFromEnum(enc.opcode),
.funct3 = @intFromEnum(vec.funct3),
.funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm),
},
else => unreachable,
},
};
},
.S => {
assert(ops.len == 3);
const umm = ops[2].imm.asBits(u12);
return .{
.S = .{
.imm0_4 = @truncate(umm),
.rs1 = ops[0].reg.encodeId(),
.rs2 = ops[1].reg.encodeId(),
.imm5_11 = @truncate(umm >> 5),
.opcode = @intFromEnum(enc.opcode),
.funct3 = enc.data.f.funct3,
},
};
},
.I => {
assert(ops.len == 3);
return .{
.I = switch (enc.data) {
.f => |f| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.imm0_11 = ops[2].imm.asBits(u12),
.opcode = @intFromEnum(enc.opcode),
.funct3 = f.funct3,
},
.sh => |sh| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.imm0_11 = (@as(u12, sh.typ) << 6) |
if (sh.has_5) ops[2].imm.asBits(u6) else (@as(u6, 0) | ops[2].imm.asBits(u5)),
.opcode = @intFromEnum(enc.opcode),
.funct3 = sh.funct3,
},
else => unreachable,
},
};
},
.U => {
assert(ops.len == 2);
return .{
.U = .{
.rd = ops[0].reg.encodeId(),
.imm12_31 = ops[1].imm.asBits(u20),
.opcode = @intFromEnum(enc.opcode),
},
};
},
.J => {
assert(ops.len == 2);
const umm = ops[1].imm.asBits(u21);
assert(umm % 4 == 0); // misaligned jump target
return .{
.J = .{
.rd = ops[0].reg.encodeId(),
.imm1_10 = @truncate(umm >> 1),
.imm11 = @truncate(umm >> 11),
.imm12_19 = @truncate(umm >> 12),
.imm20 = @truncate(umm >> 20),
.opcode = @intFromEnum(enc.opcode),
},
};
},
.B => {
assert(ops.len == 3);
const umm = ops[2].imm.asBits(u13);
assert(umm % 4 == 0); // misaligned branch target
return .{
.B = .{
.rs1 = ops[0].reg.encodeId(),
.rs2 = ops[1].reg.encodeId(),
.imm1_4 = @truncate(umm >> 1),
.imm5_10 = @truncate(umm >> 5),
.imm11 = @truncate(umm >> 11),
.imm12 = @truncate(umm >> 12),
.opcode = @intFromEnum(enc.opcode),
.funct3 = enc.data.f.funct3,
},
};
},
.fence => {
assert(ops.len == 2);
const succ = ops[0].barrier;
const pred = ops[1].barrier;
return .{
.fence = .{
.succ = @intFromEnum(succ),
.pred = @intFromEnum(pred),
.opcode = @intFromEnum(enc.opcode),
.funct3 = enc.data.fence.funct3,
.fm = @intFromEnum(enc.data.fence.fm),
},
};
},
.amo => {
assert(ops.len == 5);
const rd = ops[0].reg;
const rs1 = ops[1].reg;
const rs2 = ops[2].reg;
const rl = ops[3].barrier;
const aq = ops[4].barrier;
return .{
.amo = .{
.rd = rd.encodeId(),
.rs1 = rs1.encodeId(),
.rs2 = rs2.encodeId(),
// TODO: https://github.com/ziglang/zig/issues/20113
.rl = if (rl == .rl) true else false,
.aq = if (aq == .aq) true else false,
.opcode = @intFromEnum(enc.opcode),
.funct3 = @intFromEnum(enc.data.amo.width),
.funct5 = enc.data.amo.funct5,
},
};
},
else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),
}
}
};
pub fn findByMnemonic(mnem: Mnemonic, ops: []const Operand) !?Encoding {
if (!verifyOps(mnem, ops)) return null;
return .{
.mnemonic = mnem,
.data = try Data.construct(mnem, ops),
};
}
fn verifyOps(mnem: Mnemonic, ops: []const Operand) bool {
const inst_enc = InstEnc.fromMnemonic(mnem);
const list = std.mem.sliceTo(&inst_enc.opsList(), .none);
for (list, ops) |l, o| if (l != std.meta.activeTag(o)) return false;
return true;
}
const std = @import("std");
const assert = std.debug.assert;
const log = std.log.scoped(.encoding);
const Encoding = @This();
const bits = @import("bits.zig");
const Register = bits.Register;
const encoder = @import("encoder.zig");
const Instruction = encoder.Instruction;
const Operand = Instruction.Operand;
const OperandEnum = std.meta.FieldEnum(Operand);
+431 -454
View File
@@ -34,6 +34,8 @@ pub const Reloc = struct {
/// Relocs the lowered_inst_index and the next instruction.
load_symbol_reloc: bits.Symbol,
/// Relocs the lowered_inst_index and the next two instructions.
load_tlv_reloc: bits.Symbol,
/// Relocs the lowered_inst_index and the next instruction.
call_extern_fn_reloc: bits.Symbol,
};
@@ -61,453 +63,418 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
log.debug("lowerMir {}", .{inst});
switch (inst.tag) {
else => try lower.generic(inst),
.pseudo => switch (inst.ops) {
.pseudo_dbg_line_column,
.pseudo_dbg_epilogue_begin,
.pseudo_dbg_prologue_end,
.pseudo_dead,
=> {},
.pseudo_dbg_line_column,
.pseudo_dbg_epilogue_begin,
.pseudo_dbg_prologue_end,
.pseudo_dead,
=> {},
.pseudo_load_rm, .pseudo_store_rm => {
const rm = inst.data.rm;
.pseudo_load_rm, .pseudo_store_rm => {
const rm = inst.data.rm;
const frame_loc: Mir.FrameLoc = if (options.allow_frame_locs)
rm.m.toFrameLoc(lower.mir)
else
.{ .base = .s0, .disp = 0 };
const frame_loc: Mir.FrameLoc = if (options.allow_frame_locs)
rm.m.toFrameLoc(lower.mir)
else
.{ .base = .s0, .disp = 0 };
switch (inst.ops) {
.pseudo_load_rm => {
const dest_reg = rm.r;
const dest_reg_class = dest_reg.class();
switch (inst.tag) {
.pseudo_load_rm => {
const dest_reg = rm.r;
const dest_reg_class = dest_reg.class();
const src_size = rm.m.mod.size;
const unsigned = rm.m.mod.unsigned;
const src_size = rm.m.mod.size;
const unsigned = rm.m.mod.unsigned;
const tag: Encoding.Mnemonic = switch (dest_reg_class) {
.int => switch (src_size) {
.byte => if (unsigned) .lbu else .lb,
.hword => if (unsigned) .lhu else .lh,
.word => if (unsigned) .lwu else .lw,
.dword => .ld,
},
.float => switch (src_size) {
.byte => unreachable, // Zig does not support 8-bit floats
.hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),
.word => .flw,
.dword => .fld,
},
.vector => switch (src_size) {
.byte => .vle8v,
.hword => .vle32v,
.word => .vle32v,
.dword => .vle64v,
},
};
switch (dest_reg_class) {
.int, .float => {
try lower.emit(tag, &.{
.{ .reg = rm.r },
.{ .reg = frame_loc.base },
.{ .imm = Immediate.s(frame_loc.disp) },
});
},
.vector => {
assert(frame_loc.disp == 0);
try lower.emit(tag, &.{
.{ .reg = rm.r },
.{ .reg = frame_loc.base },
.{ .reg = .zero },
});
},
}
},
.pseudo_store_rm => {
const src_reg = rm.r;
const src_reg_class = src_reg.class();
const dest_size = rm.m.mod.size;
const tag: Encoding.Mnemonic = switch (src_reg_class) {
.int => switch (dest_size) {
.byte => .sb,
.hword => .sh,
.word => .sw,
.dword => .sd,
},
.float => switch (dest_size) {
.byte => unreachable, // Zig does not support 8-bit floats
.hword => return lower.fail("TODO: lowerMir pseudo_store_rm support 16-bit floats", .{}),
.word => .fsw,
.dword => .fsd,
},
.vector => switch (dest_size) {
.byte => .vse8v,
.hword => .vse16v,
.word => .vse32v,
.dword => .vse64v,
},
};
switch (src_reg_class) {
.int, .float => {
try lower.emit(tag, &.{
.{ .reg = frame_loc.base },
.{ .reg = rm.r },
.{ .imm = Immediate.s(frame_loc.disp) },
});
},
.vector => {
assert(frame_loc.disp == 0);
try lower.emit(tag, &.{
.{ .reg = rm.r },
.{ .reg = frame_loc.base },
.{ .reg = .zero },
});
},
}
},
else => unreachable,
}
},
.pseudo_mv => {
const rr = inst.data.rr;
const dst_class = rr.rd.class();
const src_class = rr.rs.class();
switch (src_class) {
.float => switch (dst_class) {
.float => {
try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .reg = rr.rs },
});
const mnem: Mnemonic = switch (dest_reg_class) {
.int => switch (src_size) {
.byte => if (unsigned) .lbu else .lb,
.hword => if (unsigned) .lhu else .lh,
.word => if (unsigned) .lwu else .lw,
.dword => .ld,
},
.int, .vector => return lower.fail("TODO: lowerMir pseudo_mv float -> {s}", .{@tagName(dst_class)}),
},
.int => switch (dst_class) {
.int => {
try lower.emit(.addi, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .imm = Immediate.s(0) },
.float => switch (src_size) {
.byte => unreachable, // Zig does not support 8-bit floats
.hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}),
.word => .flw,
.dword => .fld,
},
.vector => switch (src_size) {
.byte => .vle8v,
.hword => .vle32v,
.word => .vle32v,
.dword => .vle64v,
},
};
switch (dest_reg_class) {
.int, .float => {
try lower.emit(mnem, &.{
.{ .reg = rm.r },
.{ .reg = frame_loc.base },
.{ .imm = Immediate.s(frame_loc.disp) },
});
},
.vector => {
try lower.emit(.vmvvx, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .reg = .x0 },
});
},
.float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}),
},
.vector => switch (dst_class) {
.int => {
try lower.emit(.vadcvv, &.{
.{ .reg = rr.rd },
assert(frame_loc.disp == 0);
try lower.emit(mnem, &.{
.{ .reg = rm.r },
.{ .reg = frame_loc.base },
.{ .reg = .zero },
.{ .reg = rr.rs },
});
},
.float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}),
},
}
},
}
},
.pseudo_store_rm => {
const src_reg = rm.r;
const src_reg_class = src_reg.class();
.pseudo_j => {
try lower.emit(.jal, &.{
.{ .reg = .zero },
.{ .imm = lower.reloc(.{ .inst = inst.data.inst }) },
});
},
const dest_size = rm.m.mod.size;
.pseudo_spill_regs => try lower.pushPopRegList(true, inst.data.reg_list),
.pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list),
.pseudo_load_symbol => {
const payload = inst.data.payload;
const data = lower.mir.extraData(Mir.LoadSymbolPayload, payload).data;
const dst_reg: bits.Register = @enumFromInt(data.register);
assert(dst_reg.class() == .int);
try lower.emit(.lui, &.{
.{ .reg = dst_reg },
.{ .imm = lower.reloc(.{
.load_symbol_reloc = .{
.atom_index = data.atom_index,
.sym_index = data.sym_index,
const mnem: Mnemonic = switch (src_reg_class) {
.int => switch (dest_size) {
.byte => .sb,
.hword => .sh,
.word => .sw,
.dword => .sd,
},
}) },
});
.float => switch (dest_size) {
.byte => unreachable, // Zig does not support 8-bit floats
.hword => return lower.fail("TODO: lowerMir pseudo_store_rm support 16-bit floats", .{}),
.word => .fsw,
.dword => .fsd,
},
.vector => switch (dest_size) {
.byte => .vse8v,
.hword => .vse16v,
.word => .vse32v,
.dword => .vse64v,
},
};
// the above reloc implies this one
try lower.emit(.addi, &.{
.{ .reg = dst_reg },
.{ .reg = dst_reg },
.{ .imm = Immediate.s(0) },
});
},
.pseudo_lea_rm => {
const rm = inst.data.rm;
assert(rm.r.class() == .int);
const frame: Mir.FrameLoc = if (options.allow_frame_locs)
rm.m.toFrameLoc(lower.mir)
else
.{ .base = .s0, .disp = 0 };
try lower.emit(.addi, &.{
.{ .reg = rm.r },
.{ .reg = frame.base },
.{ .imm = Immediate.s(frame.disp) },
});
},
.pseudo_fabs => {
const fabs = inst.data.fabs;
assert(fabs.rs.class() == .float and fabs.rd.class() == .float);
const mnem: Encoding.Mnemonic = switch (fabs.bits) {
16 => return lower.fail("TODO: airAbs Float 16", .{}),
32 => .fsgnjxs,
64 => .fsgnjxd,
80 => return lower.fail("TODO: airAbs Float 80", .{}),
128 => return lower.fail("TODO: airAbs Float 128", .{}),
else => unreachable,
};
try lower.emit(mnem, &.{
.{ .reg = fabs.rs },
.{ .reg = fabs.rd },
.{ .reg = fabs.rd },
});
},
.pseudo_compare => {
const compare = inst.data.compare;
const op = compare.op;
const rd = compare.rd;
const rs1 = compare.rs1;
const rs2 = compare.rs2;
const class = rs1.class();
const ty = compare.ty;
const size = std.math.ceilPowerOfTwo(u64, ty.bitSize(pt)) catch {
return lower.fail("pseudo_compare size {}", .{ty.bitSize(pt)});
};
const is_unsigned = ty.isUnsignedInt(pt.zcu);
const less_than: Encoding.Mnemonic = if (is_unsigned) .sltu else .slt;
switch (class) {
.int => switch (op) {
.eq => {
try lower.emit(.xor, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.sltiu, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
switch (src_reg_class) {
.int, .float => {
try lower.emit(mnem, &.{
.{ .reg = frame_loc.base },
.{ .reg = rm.r },
.{ .imm = Immediate.s(frame_loc.disp) },
});
},
.neq => {
try lower.emit(.xor, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.sltu, &.{
.{ .reg = rd },
.vector => {
assert(frame_loc.disp == 0);
try lower.emit(mnem, &.{
.{ .reg = rm.r },
.{ .reg = frame_loc.base },
.{ .reg = .zero },
.{ .reg = rd },
});
},
.gt => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.gte => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
}
},
else => unreachable,
}
},
try lower.emit(.xori, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
.lt => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.lte => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
.pseudo_mv => {
const rr = inst.data.rr;
try lower.emit(.xori, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
const dst_class = rr.rd.class();
const src_class = rr.rs.class();
switch (src_class) {
.float => switch (dst_class) {
.float => {
try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .reg = rr.rs },
});
},
.float => switch (op) {
// eq
.eq => {
try lower.emit(if (size == 64) .feqd else .feqs, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
// !(eq)
.neq => {
try lower.emit(if (size == 64) .feqd else .feqs, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.xori, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
.lt => {
try lower.emit(if (size == 64) .fltd else .flts, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.lte => {
try lower.emit(if (size == 64) .fled else .fles, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.gt => {
try lower.emit(if (size == 64) .fltd else .flts, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
},
.gte => {
try lower.emit(if (size == 64) .fled else .fles, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
},
.int, .vector => return lower.fail("TODO: lowerMir pseudo_mv float -> {s}", .{@tagName(dst_class)}),
},
.int => switch (dst_class) {
.int => {
try lower.emit(.addi, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .imm = Immediate.s(0) },
});
},
.vector => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}),
}
},
.vector => {
try lower.emit(.vmvvx, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .reg = .x0 },
});
},
.float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}),
},
.vector => switch (dst_class) {
.int => {
try lower.emit(.vadcvv, &.{
.{ .reg = rr.rd },
.{ .reg = .zero },
.{ .reg = rr.rs },
});
},
.float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}),
},
}
},
.pseudo_not => {
const rr = inst.data.rr;
assert(rr.rs.class() == .int and rr.rd.class() == .int);
.pseudo_j => {
const j_type = inst.data.j_type;
try lower.emit(.jal, &.{
.{ .reg = j_type.rd },
.{ .imm = lower.reloc(.{ .inst = j_type.inst }) },
});
},
// mask out any other bits that aren't the boolean
try lower.emit(.andi, &.{
.{ .reg = rr.rs },
.{ .reg = rr.rs },
.{ .imm = Immediate.s(1) },
});
.pseudo_spill_regs => try lower.pushPopRegList(true, inst.data.reg_list),
.pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list),
try lower.emit(.sltiu, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .imm = Immediate.s(1) },
});
},
.pseudo_load_symbol => {
const payload = inst.data.reloc;
const dst_reg = payload.register;
assert(dst_reg.class() == .int);
.pseudo_extern_fn_reloc => {
const inst_reloc = inst.data.reloc;
try lower.emit(.lui, &.{
.{ .reg = dst_reg },
.{ .imm = lower.reloc(.{
.load_symbol_reloc = .{
.atom_index = payload.atom_index,
.sym_index = payload.sym_index,
},
}) },
});
try lower.emit(.auipc, &.{
.{ .reg = .ra },
.{ .imm = lower.reloc(
.{ .call_extern_fn_reloc = .{
.atom_index = inst_reloc.atom_index,
.sym_index = inst_reloc.sym_index,
} },
) },
});
// the reloc above implies this one
try lower.emit(.addi, &.{
.{ .reg = dst_reg },
.{ .reg = dst_reg },
.{ .imm = Immediate.s(0) },
});
},
try lower.emit(.jalr, &.{
.{ .reg = .ra },
.{ .reg = .ra },
.{ .imm = Immediate.s(0) },
});
},
.pseudo_load_tlv => {
const payload = inst.data.reloc;
const dst_reg = payload.register;
assert(dst_reg.class() == .int);
.pseudo_amo => {
const amo = inst.data.amo;
const is_d = amo.ty.abiSize(pt) == 8;
const is_un = amo.ty.isUnsignedInt(pt.zcu);
try lower.emit(.lui, &.{
.{ .reg = dst_reg },
.{ .imm = lower.reloc(.{
.load_tlv_reloc = .{
.atom_index = payload.atom_index,
.sym_index = payload.sym_index,
},
}) },
});
const mnem: Encoding.Mnemonic = switch (amo.op) {
// zig fmt: off
.SWAP => if (is_d) .amoswapd else .amoswapw,
.ADD => if (is_d) .amoaddd else .amoaddw,
.AND => if (is_d) .amoandd else .amoandw,
.OR => if (is_d) .amoord else .amoorw,
.XOR => if (is_d) .amoxord else .amoxorw,
.MAX => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw,
.MIN => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw,
// zig fmt: on
};
try lower.emit(.add, &.{
.{ .reg = dst_reg },
.{ .reg = dst_reg },
.{ .reg = .tp },
});
try lower.emit(mnem, &.{
.{ .reg = inst.data.amo.rd },
.{ .reg = inst.data.amo.rs1 },
.{ .reg = inst.data.amo.rs2 },
.{ .barrier = inst.data.amo.rl },
.{ .barrier = inst.data.amo.aq },
});
},
try lower.emit(.addi, &.{
.{ .reg = dst_reg },
.{ .reg = dst_reg },
.{ .imm = Immediate.s(0) },
});
},
.pseudo_fence => {
const fence = inst.data.fence;
.pseudo_lea_rm => {
const rm = inst.data.rm;
assert(rm.r.class() == .int);
try lower.emit(switch (fence.fm) {
.tso => .fencetso,
.none => .fence,
}, &.{
.{ .barrier = fence.succ },
.{ .barrier = fence.pred },
});
},
const frame: Mir.FrameLoc = if (options.allow_frame_locs)
rm.m.toFrameLoc(lower.mir)
else
.{ .base = .s0, .disp = 0 };
else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}),
try lower.emit(.addi, &.{
.{ .reg = rm.r },
.{ .reg = frame.base },
.{ .imm = Immediate.s(frame.disp) },
});
},
.pseudo_compare => {
const compare = inst.data.compare;
const op = compare.op;
const rd = compare.rd;
const rs1 = compare.rs1;
const rs2 = compare.rs2;
const class = rs1.class();
const ty = compare.ty;
const size = std.math.ceilPowerOfTwo(u64, ty.bitSize(pt)) catch {
return lower.fail("pseudo_compare size {}", .{ty.bitSize(pt)});
};
const is_unsigned = ty.isUnsignedInt(pt.zcu);
const less_than: Mnemonic = if (is_unsigned) .sltu else .slt;
switch (class) {
.int => switch (op) {
.eq => {
try lower.emit(.xor, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.sltiu, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
.neq => {
try lower.emit(.xor, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.sltu, &.{
.{ .reg = rd },
.{ .reg = .zero },
.{ .reg = rd },
});
},
.gt => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
},
.gte => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.xori, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
.lt => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.lte => {
try lower.emit(less_than, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
try lower.emit(.xori, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
},
.float => switch (op) {
// eq
.eq => {
try lower.emit(if (size == 64) .feqd else .feqs, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
// !(eq)
.neq => {
try lower.emit(if (size == 64) .feqd else .feqs, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
try lower.emit(.xori, &.{
.{ .reg = rd },
.{ .reg = rd },
.{ .imm = Immediate.s(1) },
});
},
.lt => {
try lower.emit(if (size == 64) .fltd else .flts, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.lte => {
try lower.emit(if (size == 64) .fled else .fles, &.{
.{ .reg = rd },
.{ .reg = rs1 },
.{ .reg = rs2 },
});
},
.gt => {
try lower.emit(if (size == 64) .fltd else .flts, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
},
.gte => {
try lower.emit(if (size == 64) .fled else .fles, &.{
.{ .reg = rd },
.{ .reg = rs2 },
.{ .reg = rs1 },
});
},
},
.vector => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}),
}
},
.pseudo_not => {
const rr = inst.data.rr;
assert(rr.rs.class() == .int and rr.rd.class() == .int);
// mask out any other bits that aren't the boolean
try lower.emit(.andi, &.{
.{ .reg = rr.rs },
.{ .reg = rr.rs },
.{ .imm = Immediate.s(1) },
});
try lower.emit(.sltiu, &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
.{ .imm = Immediate.s(1) },
});
},
.pseudo_extern_fn_reloc => {
const inst_reloc = inst.data.reloc;
const link_reg = inst_reloc.register;
try lower.emit(.auipc, &.{
.{ .reg = link_reg },
.{ .imm = lower.reloc(
.{ .call_extern_fn_reloc = .{
.atom_index = inst_reloc.atom_index,
.sym_index = inst_reloc.sym_index,
} },
) },
});
try lower.emit(.jalr, &.{
.{ .reg = link_reg },
.{ .reg = link_reg },
.{ .imm = Immediate.s(0) },
});
},
}
@@ -518,49 +485,57 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
}
fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
const mnemonic = std.meta.stringToEnum(Encoding.Mnemonic, @tagName(inst.tag)) orelse {
return lower.fail("generic inst name '{s}' with op {s} doesn't match with a mnemonic", .{
@tagName(inst.tag),
@tagName(inst.ops),
});
};
try lower.emit(mnemonic, switch (inst.ops) {
const mnemonic = inst.tag;
try lower.emit(mnemonic, switch (inst.data) {
.none => &.{},
.ri => &.{
.{ .reg = inst.data.u_type.rd },
.{ .imm = inst.data.u_type.imm20 },
.u_type => |u| &.{
.{ .reg = u.rd },
.{ .imm = u.imm20 },
},
.rr => &.{
.{ .reg = inst.data.rr.rd },
.{ .reg = inst.data.rr.rs },
.i_type => |i| &.{
.{ .reg = i.rd },
.{ .reg = i.rs1 },
.{ .imm = i.imm12 },
},
.rri => &.{
.{ .reg = inst.data.i_type.rd },
.{ .reg = inst.data.i_type.rs1 },
.{ .imm = inst.data.i_type.imm12 },
.rr => |rr| &.{
.{ .reg = rr.rd },
.{ .reg = rr.rs },
},
.rr_inst => &.{
.{ .reg = inst.data.b_type.rs1 },
.{ .reg = inst.data.b_type.rs2 },
.{ .imm = lower.reloc(.{ .inst = inst.data.b_type.inst }) },
.b_type => |b| &.{
.{ .reg = b.rs1 },
.{ .reg = b.rs2 },
.{ .imm = lower.reloc(.{ .inst = b.inst }) },
},
.rrr => &.{
.{ .reg = inst.data.r_type.rd },
.{ .reg = inst.data.r_type.rs1 },
.{ .reg = inst.data.r_type.rs2 },
.r_type => |r| &.{
.{ .reg = r.rd },
.{ .reg = r.rs1 },
.{ .reg = r.rs2 },
},
.csr => &.{
.{ .csr = inst.data.csr.csr },
.{ .reg = inst.data.csr.rs1 },
.{ .reg = inst.data.csr.rd },
.csr => |csr| &.{
.{ .csr = csr.csr },
.{ .reg = csr.rs1 },
.{ .reg = csr.rd },
},
else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),
.amo => |amo| &.{
.{ .reg = amo.rd },
.{ .reg = amo.rs1 },
.{ .reg = amo.rs2 },
.{ .barrier = amo.rl },
.{ .barrier = amo.aq },
},
.fence => |fence| &.{
.{ .barrier = fence.succ },
.{ .barrier = fence.pred },
},
else => return lower.fail("TODO: generic lower {s}", .{@tagName(inst.data)}),
});
}
fn emit(lower: *Lower, mnemonic: Encoding.Mnemonic, ops: []const Instruction.Operand) !void {
lower.result_insts[lower.result_insts_len] =
try Instruction.new(mnemonic, ops);
fn emit(lower: *Lower, mnemonic: Mnemonic, ops: []const Instruction.Operand) !void {
const lir = encoding.Lir.fromMnem(mnemonic);
const inst = Instruction.fromLir(lir, ops);
lower.result_insts[lower.result_insts_len] = inst;
lower.result_insts_len += 1;
}
@@ -582,7 +557,7 @@ fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.Register
const reg = abi.Registers.all_preserved[i];
const reg_class = reg.class();
const load_inst: Encoding.Mnemonic, const store_inst: Encoding.Mnemonic = switch (reg_class) {
const load_inst: Mnemonic, const store_inst: Mnemonic = switch (reg_class) {
.int => .{ .ld, .sd },
.float => .{ .fld, .fsd },
.vector => unreachable,
@@ -620,20 +595,22 @@ fn hasFeature(lower: *Lower, feature: std.Target.riscv.Feature) bool {
}
const Lower = @This();
const abi = @import("abi.zig");
const assert = std.debug.assert;
const bits = @import("bits.zig");
const encoder = @import("encoder.zig");
const link = @import("../../link.zig");
const Encoding = @import("Encoding.zig");
const std = @import("std");
const assert = std.debug.assert;
const log = std.log.scoped(.lower);
const Air = @import("../../Air.zig");
const Allocator = std.mem.Allocator;
const ErrorMsg = Zcu.ErrorMsg;
const Mir = @import("Mir.zig");
const link = @import("../../link.zig");
const Air = @import("../../Air.zig");
const Zcu = @import("../../Zcu.zig");
const Instruction = encoder.Instruction;
const Mir = @import("Mir.zig");
const abi = @import("abi.zig");
const bits = @import("bits.zig");
const encoding = @import("encoding.zig");
const Mnemonic = @import("mnem.zig").Mnemonic;
const Immediate = bits.Immediate;
const Instruction = encoding.Instruction;
+19 -281
View File
@@ -1,164 +1,17 @@
//! Machine Intermediate Representation.
//! This data is produced by RISCV64 Codegen or RISCV64 assembly parsing
//! These instructions have a 1:1 correspondence with machine code instructions
//! for the target. MIR can be lowered to source-annotated textual assembly code
//! instructions, or it can be lowered to machine code.
//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
//! so that, for example, the smaller encodings of jump instructions can be used.
//! This data is produced by CodeGen.zig
instructions: std.MultiArrayList(Inst).Slice,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []const u32,
frame_locs: std.MultiArrayList(FrameLoc).Slice,
pub const Inst = struct {
tag: Tag,
tag: Mnemonic,
data: Data,
ops: Ops,
/// The position of an MIR instruction within the `Mir` instructions array.
pub const Index = u32;
pub const Tag = enum(u16) {
// base extension
addi,
addiw,
jalr,
lui,
@"and",
andi,
xori,
xor,
@"or",
ebreak,
ecall,
unimp,
add,
addw,
sub,
subw,
sltu,
slt,
slli,
srli,
srai,
slliw,
srliw,
sraiw,
sll,
srl,
sra,
sllw,
srlw,
sraw,
jal,
beq,
bne,
nop,
ld,
lw,
lh,
lb,
sd,
sw,
sh,
sb,
// M extension
mul,
mulw,
div,
divu,
divw,
divuw,
rem,
remu,
remw,
remuw,
// F extension (32-bit float)
fadds,
fsubs,
fmuls,
fdivs,
fabss,
fmins,
fmaxs,
fsqrts,
flw,
fsw,
feqs,
flts,
fles,
// D extension (64-bit float)
faddd,
fsubd,
fmuld,
fdivd,
fabsd,
fmind,
fmaxd,
fsqrtd,
fld,
fsd,
feqd,
fltd,
fled,
// Zicsr Extension Instructions
csrrs,
// V Extension Instructions
vsetvli,
vsetivli,
vsetvl,
vaddvv,
vfaddvv,
vsubvv,
vfsubvv,
vslidedownvx,
/// A pseudo-instruction. Used for anything that isn't 1:1 with an
/// assembly instruction.
pseudo,
};
/// All instructions have a 4-byte payload, which is contained within
/// this union. `Ops` determines which union field is active, as well as
/// how to interpret the data within.
pub const Data = union {
nop: void,
inst: Index,
payload: u32,
pub const Data = union(enum) {
none: void,
r_type: struct {
rd: Register,
rs1: Register,
@@ -188,10 +41,6 @@ pub const Inst = struct {
rd: Register,
inst: Inst.Index,
},
pseudo_dbg_line_column: struct {
line: u32,
column: u32,
},
rm: struct {
r: Register,
m: Memory,
@@ -202,11 +51,6 @@ pub const Inst = struct {
rd: Register,
rs: Register,
},
fabs: struct {
rd: Register,
rs: Register,
bits: u16,
},
compare: struct {
rd: Register,
rs1: Register,
@@ -222,16 +66,13 @@ pub const Inst = struct {
ty: Type,
},
reloc: struct {
register: Register,
atom_index: u32,
sym_index: u32,
},
fence: struct {
pred: Barrier,
succ: Barrier,
fm: enum {
none,
tso,
},
},
amo: struct {
rd: Register,
@@ -239,123 +80,32 @@ pub const Inst = struct {
rs2: Register,
aq: Barrier,
rl: Barrier,
op: AmoOp,
ty: Type,
},
csr: struct {
csr: CSR,
rs1: Register,
rd: Register,
},
};
pub const Ops = enum {
/// No data associated with this instruction (only mnemonic is used).
none,
/// Two registers
rr,
/// Three registers
rrr,
/// Two registers + immediate, uses the i_type payload.
rri,
//extern_fn_reloc/ Two registers + another instruction.
rr_inst,
/// Register + Memory
rm,
/// Register + Immediate
ri,
/// Another instruction.
inst,
/// Control and Status Register Instruction.
csr,
/// Pseudo-instruction that will generate a backpatched
/// function prologue.
pseudo_prologue,
/// Pseudo-instruction that will generate a backpatched
/// function epilogue
pseudo_epilogue,
/// Pseudo-instruction: End of prologue
pseudo_dbg_prologue_end,
/// Pseudo-instruction: Beginning of epilogue
pseudo_dbg_epilogue_begin,
/// Pseudo-instruction: Update debug line
pseudo_dbg_line_column,
/// Pseudo-instruction that loads from memory into a register.
///
/// Uses `rm` payload.
pseudo_load_rm,
/// Pseudo-instruction that stores from a register into memory
///
/// Uses `rm` payload.
pseudo_store_rm,
/// Pseudo-instruction that loads the address of memory into a register.
///
/// Uses `rm` payload.
pseudo_lea_rm,
/// Jumps. Uses `inst` payload.
pseudo_j,
/// Floating point absolute value.
pseudo_fabs,
/// Dead inst, ignored by the emitter.
pseudo_dead,
/// Loads the address of a value that hasn't yet been allocated in memory.
///
/// uses the Mir.LoadSymbolPayload payload.
pseudo_load_symbol,
/// Moves the value of rs1 to rd.
///
/// uses the `rr` payload.
pseudo_mv,
pseudo_restore_regs,
pseudo_spill_regs,
pseudo_compare,
/// NOT operation on booleans. Does an `andi reg, reg, 1` to mask out any other bits from the boolean.
pseudo_not,
/// Generates an auipc + jalr pair, with a R_RISCV_CALL_PLT reloc
pseudo_extern_fn_reloc,
/// IORW, IORW
pseudo_fence,
/// Ordering, Src, Addr, Dest
pseudo_amo,
pseudo_dbg_line_column: struct {
line: u32,
column: u32,
},
};
pub fn format(
inst: Inst,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
assert(fmt.len == 0);
_ = options;
try writer.print("Tag: {s}, Ops: {s}", .{ @tagName(inst.tag), @tagName(inst.ops) });
try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) });
}
};
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
mir.instructions.deinit(gpa);
mir.frame_locs.deinit(gpa);
gpa.free(mir.extra);
mir.* = undefined;
}
@@ -386,25 +136,12 @@ pub const AmoOp = enum(u5) {
MIN,
};
/// Returns the requested data, as well as the new index which is at the start of the
/// trailers for the object.
pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
const fields = std.meta.fields(T);
var i: usize = index;
var result: T = undefined;
inline for (fields) |field| {
@field(result, field.name) = switch (field.type) {
u32 => mir.extra[i],
i32 => @as(i32, @bitCast(mir.extra[i])),
else => @compileError("bad field type"),
};
i += 1;
}
return .{
.data = result,
.end = i,
};
}
pub const FcvtOp = enum(u5) {
w = 0b00000,
wu = 0b00001,
l = 0b00010,
lu = 0b00011,
};
pub const LoadSymbolPayload = struct {
register: u32,
@@ -453,10 +190,10 @@ const Mir = @This();
const std = @import("std");
const builtin = @import("builtin");
const Type = @import("../../Type.zig");
const bits = @import("bits.zig");
const assert = std.debug.assert;
const bits = @import("bits.zig");
const Register = bits.Register;
const CSR = bits.CSR;
const Immediate = bits.Immediate;
@@ -464,3 +201,4 @@ const Memory = bits.Memory;
const FrameIndex = bits.FrameIndex;
const FrameAddr = @import("CodeGen.zig").FrameAddr;
const IntegerBitSet = std.bit_set.IntegerBitSet;
const Mnemonic = @import("mnem.zig").Mnemonic;
+4 -5
View File
@@ -125,10 +125,7 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
result[0] = .integer;
return result;
}
result[0] = .integer;
if (ty.optionalChild(zcu).abiSize(pt) == 0) return result;
result[1] = .integer;
return result;
return memory_class;
},
.Int, .Enum, .ErrorSet => {
const int_bits = ty.intInfo(pt.zcu).bits;
@@ -167,7 +164,7 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
return memory_class;
},
.Struct => {
.Struct, .Union => {
const layout = ty.containerLayout(pt.zcu);
const ty_size = ty.abiSize(pt);
@@ -200,6 +197,8 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass {
result[0] = .integer;
return result;
}
// we should pass vector registers of size <= 128 through 2 integer registers
// but we haven't implemented seperating vector registers into register_pairs
return memory_class;
},
else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),
+2 -6
View File
@@ -5,7 +5,6 @@ const testing = std.testing;
const Target = std.Target;
const Zcu = @import("../../Zcu.zig");
const Encoding = @import("Encoding.zig");
const Mir = @import("Mir.zig");
const abi = @import("abi.zig");
@@ -16,7 +15,6 @@ pub const Memory = struct {
pub const Base = union(enum) {
reg: Register,
frame: FrameIndex,
reloc: Symbol,
};
pub const Mod = struct {
@@ -83,7 +81,6 @@ pub const Memory = struct {
.disp = base_loc.disp + offset,
};
},
.reloc => unreachable,
}
}
};
@@ -193,7 +190,7 @@ pub const Register = enum(u8) {
/// The goal of this function is to return the same ID for `zero` and `x0` but two
/// seperate IDs for `x0` and `f0`. We will assume that each register set has 32 registers
/// and is repeated twice, once for the named version, once for the number version.
pub fn id(reg: Register) u8 {
pub fn id(reg: Register) std.math.IntFittingRange(0, @typeInfo(Register).Enum.fields.len) {
const base = switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero),
@@ -251,8 +248,7 @@ pub const FrameIndex = enum(u32) {
/// This index referes to a frame dedicated to setting up args for function called
/// in this function. Useful for aligning args separately.
call_frame,
/// This index referes to the frame where callee saved registers are spilled and restore
/// from.
/// This index referes to the frame where callee saved registers are spilled and restored from.
spill_frame,
/// Other indices are used for local variable stack slots
_,
-80
View File
@@ -1,80 +0,0 @@
pub const Instruction = struct {
encoding: Encoding,
ops: [5]Operand = .{.none} ** 5,
pub const Operand = union(enum) {
none,
reg: Register,
csr: CSR,
mem: Memory,
imm: Immediate,
barrier: Mir.Barrier,
};
pub fn new(mnemonic: Encoding.Mnemonic, ops: []const Operand) !Instruction {
const encoding = (try Encoding.findByMnemonic(mnemonic, ops)) orelse {
std.log.err("no encoding found for: {s} [{s} {s} {s} {s} {s}]", .{
@tagName(mnemonic),
@tagName(if (ops.len > 0) ops[0] else .none),
@tagName(if (ops.len > 1) ops[1] else .none),
@tagName(if (ops.len > 2) ops[2] else .none),
@tagName(if (ops.len > 3) ops[3] else .none),
@tagName(if (ops.len > 4) ops[4] else .none),
});
return error.InvalidInstruction;
};
var result_ops: [5]Operand = .{.none} ** 5;
@memcpy(result_ops[0..ops.len], ops);
return .{
.encoding = encoding,
.ops = result_ops,
};
}
pub fn encode(inst: Instruction, writer: anytype) !void {
try writer.writeInt(u32, inst.encoding.data.toU32(), .little);
}
pub fn format(
inst: Instruction,
comptime fmt: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
std.debug.assert(fmt.len == 0);
const encoding = inst.encoding;
try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
var i: u32 = 0;
while (i < inst.ops.len and inst.ops[i] != .none) : (i += 1) {
if (i != inst.ops.len and i != 0) try writer.writeAll(", ");
switch (@as(Instruction.Operand, inst.ops[i])) {
.none => unreachable, // it's sliced out above
.reg => |reg| try writer.writeAll(@tagName(reg)),
.imm => |imm| try writer.print("{d}", .{imm.asSigned(64)}),
.mem => try writer.writeAll("mem"),
.barrier => |barrier| try writer.writeAll(@tagName(barrier)),
.csr => |csr| try writer.writeAll(@tagName(csr)),
}
}
}
};
const std = @import("std");
const Lower = @import("Lower.zig");
const Mir = @import("Mir.zig");
const bits = @import("bits.zig");
const Encoding = @import("Encoding.zig");
const Register = bits.Register;
const CSR = bits.CSR;
const Memory = bits.Memory;
const Immediate = bits.Immediate;
const log = std.log.scoped(.encode);
+729
View File
@@ -0,0 +1,729 @@
//! This file is responsible for going from MIR, which is emitted by CodeGen
//! and converting it into Instructions, which can be used as needed.
//!
//! Here we encode how mnemonics relate to opcodes and where their operands go.
/// Lower Instruction Representation
///
/// This format encodes a specific instruction, however it's still abstracted
/// away from the true encoding it'll be in. It's meant to make the process of
/// indicating unique encoding data easier.
pub const Lir = struct {
opcode: OpCode,
format: Format,
data: Data,
pub const Format = enum {
R,
I,
S,
B,
U,
J,
extra,
};
const Data = union(enum) {
none,
f: struct { funct3: u3 },
ff: struct {
funct3: u3,
funct7: u7,
},
sh: struct {
typ: u6,
funct3: u3,
has_5: bool,
},
fmt: struct {
funct5: u5,
rm: u3,
fmt: FpFmt,
},
fcvt: struct {
funct5: u5,
rm: u3,
fmt: FpFmt,
width: Mir.FcvtOp,
},
vecls: struct {
width: VecWidth,
umop: Umop,
vm: bool,
mop: Mop,
mew: bool,
nf: u3,
},
vecmath: struct {
vm: bool,
funct6: u6,
funct3: VecType,
},
amo: struct {
funct5: u5,
width: AmoWidth,
},
fence: struct {
funct3: u3,
fm: FenceMode,
},
/// the mnemonic has some special properities that can't be handled in a generic fashion
extra: Mnemonic,
};
const OpCode = enum(u7) {
LOAD = 0b0000011,
LOAD_FP = 0b0000111,
MISC_MEM = 0b0001111,
OP_IMM = 0b0010011,
AUIPC = 0b0010111,
OP_IMM_32 = 0b0011011,
STORE = 0b0100011,
STORE_FP = 0b0100111,
AMO = 0b0101111,
OP_V = 0b1010111,
OP = 0b0110011,
OP_32 = 0b0111011,
LUI = 0b0110111,
MADD = 0b1000011,
MSUB = 0b1000111,
NMSUB = 0b1001011,
NMADD = 0b1001111,
OP_FP = 0b1010011,
OP_IMM_64 = 0b1011011,
BRANCH = 0b1100011,
JALR = 0b1100111,
JAL = 0b1101111,
SYSTEM = 0b1110011,
OP_64 = 0b1111011,
NONE = 0b00000000,
};
const FpFmt = enum(u2) {
/// 32-bit single-precision
S = 0b00,
/// 64-bit double-precision
D = 0b01,
// H = 0b10, unused in the G extension
/// 128-bit quad-precision
Q = 0b11,
};
const AmoWidth = enum(u3) {
W = 0b010,
D = 0b011,
};
const FenceMode = enum(u4) {
none = 0b0000,
tso = 0b1000,
};
const Mop = enum(u2) {
// zig fmt: off
unit = 0b00,
unord = 0b01,
stride = 0b10,
ord = 0b11,
// zig fmt: on
};
const Umop = enum(u5) {
// zig fmt: off
unit = 0b00000,
whole = 0b01000,
mask = 0b01011,
fault = 0b10000,
// zig fmt: on
};
const VecWidth = enum(u3) {
// zig fmt: off
@"8" = 0b000,
@"16" = 0b101,
@"32" = 0b110,
@"64" = 0b111,
// zig fmt: on
};
const VecType = enum(u3) {
OPIVV = 0b000,
OPFVV = 0b001,
OPMVV = 0b010,
OPIVI = 0b011,
OPIVX = 0b100,
OPFVF = 0b101,
OPMVX = 0b110,
};
pub fn fromMnem(mnem: Mnemonic) Lir {
return switch (mnem) {
// zig fmt: off
// OP
.add => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
.sub => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } },
.@"and" => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } },
.@"or" => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } },
.xor => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } },
.sltu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } },
.slt => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } },
.mul => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
.mulh => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000001 } } },
.mulhsu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000001 } } },
.mulhu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000001 } } },
.div => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } },
.divu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } },
.rem => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } },
.remu => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } },
.sll => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
.srl => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } },
.sra => .{ .opcode = .OP, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } },
// OP_IMM
.addi => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } },
.andi => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } },
.xori => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b100 } } },
.sltiu => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } },
.slli => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = true } } },
.srli => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = true } } },
.srai => .{ .opcode = .OP_IMM, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = true } } },
.clz => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
.cpop => .{ .opcode = .OP_IMM, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
// OP_IMM_32
.slliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b001, .has_5 = false } } },
.srliw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b000000, .funct3 = 0b101, .has_5 = false } } },
.sraiw => .{ .opcode = .OP_IMM_32, .format = .I, .data = .{ .sh = .{ .typ = 0b010000, .funct3 = 0b101, .has_5 = false } } },
.clzw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
.cpopw => .{ .opcode = .OP_IMM_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
// OP_32
.addw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
.subw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } },
.mulw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
.divw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000001 } } },
.divuw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000001 } } },
.remw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000001 } } },
.remuw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000001 } } },
.sllw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
.srlw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0000000 } } },
.sraw => .{ .opcode = .OP_32, .format = .R, .data = .{ .ff = .{ .funct3 = 0b101, .funct7 = 0b0100000 } } },
// OP_FP
.fadds => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } },
.faddd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } },
.fsubs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .S, .rm = 0b111 } } },
.fsubd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00001, .fmt = .D, .rm = 0b111 } } },
.fmuls => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .S, .rm = 0b111 } } },
.fmuld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00010, .fmt = .D, .rm = 0b111 } } },
.fdivs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .S, .rm = 0b111 } } },
.fdivd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00011, .fmt = .D, .rm = 0b111 } } },
.fmins => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b000 } } },
.fmind => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b000 } } },
.fmaxs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .S, .rm = 0b001 } } },
.fmaxd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00101, .fmt = .D, .rm = 0b001 } } },
.fsqrts => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .S, .rm = 0b111 } } },
.fsqrtd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b01011, .fmt = .D, .rm = 0b111 } } },
.fles => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b000 } } },
.fled => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b000 } } },
.flts => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b001 } } },
.fltd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b001 } } },
.feqs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } },
.feqd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } },
.fsgnjns => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b000 } } },
.fsgnjnd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b000 } } },
.fsgnjxs => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b010 } } },
.fsgnjxd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .D, .rm = 0b010 } } },
.fcvtws => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .w } } },
.fcvtwus => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .wu } } },
.fcvtls => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .l } } },
.fcvtlus => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .S, .rm = 0b111, .width = .lu } } },
.fcvtwd => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .w } } },
.fcvtwud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .wu } } },
.fcvtld => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .l } } },
.fcvtlud => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11000, .fmt = .D, .rm = 0b111, .width = .lu } } },
.fcvtsw => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .w } } },
.fcvtswu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .wu } } },
.fcvtsl => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .l } } },
.fcvtslu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .S, .rm = 0b111, .width = .lu } } },
.fcvtdw => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .w } } },
.fcvtdwu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .wu } } },
.fcvtdl => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .l } } },
.fcvtdlu => .{ .opcode = .OP_FP, .format = .R, .data = .{ .fcvt = .{ .funct5 = 0b11010, .fmt = .D, .rm = 0b111, .width = .lu } } },
// LOAD
.lb => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } },
.lh => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b001 } } },
.lw => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } },
.ld => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } },
.lbu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b100 } } },
.lhu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b101 } } },
.lwu => .{ .opcode = .LOAD, .format = .I, .data = .{ .f = .{ .funct3 = 0b110 } } },
// STORE
.sb => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b000 } } },
.sh => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b001 } } },
.sw => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b010 } } },
.sd => .{ .opcode = .STORE, .format = .S, .data = .{ .f = .{ .funct3 = 0b011 } } },
// LOAD_FP
.flw => .{ .opcode = .LOAD_FP, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } },
.fld => .{ .opcode = .LOAD_FP, .format = .I, .data = .{ .f = .{ .funct3 = 0b011 } } },
.vle8v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vle16v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vle32v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vle64v => .{ .opcode = .LOAD_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
// STORE_FP
.fsw => .{ .opcode = .STORE_FP, .format = .S, .data = .{ .f = .{ .funct3 = 0b010 } } },
.fsd => .{ .opcode = .STORE_FP, .format = .S, .data = .{ .f = .{ .funct3 = 0b011 } } },
.vse8v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vse16v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vse32v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
.vse64v => .{ .opcode = .STORE_FP, .format = .R, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } },
// JALR
.jalr => .{ .opcode = .JALR, .format = .I, .data = .{ .f = .{ .funct3 = 0b000 } } },
// LUI
.lui => .{ .opcode = .LUI, .format = .U, .data = .{ .none = {} } },
// AUIPC
.auipc => .{ .opcode = .AUIPC, .format = .U, .data = .{ .none = {} } },
// JAL
.jal => .{ .opcode = .JAL, .format = .J, .data = .{ .none = {} } },
// BRANCH
.beq => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b000 } } },
.bne => .{ .opcode = .BRANCH, .format = .B, .data = .{ .f = .{ .funct3 = 0b001 } } },
// SYSTEM
.ecall => .{ .opcode = .SYSTEM, .format = .extra, .data = .{ .extra = .ecall } },
.ebreak => .{ .opcode = .SYSTEM, .format = .extra, .data = .{ .extra = .ebreak } },
.csrrs => .{ .opcode = .SYSTEM, .format = .I, .data = .{ .f = .{ .funct3 = 0b010 } } },
// NONE
.unimp => .{ .opcode = .NONE, .format = .extra, .data = .{ .extra = .unimp } },
// MISC_MEM
.fence => .{ .opcode = .MISC_MEM, .format = .I, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } },
.fencetso => .{ .opcode = .MISC_MEM, .format = .I, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso } } },
// AMO
.amoaddw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } },
.amoswapw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } },
.lrw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00010 } } },
.scw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00011 } } },
.amoxorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } },
.amoandw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } },
.amoorw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } },
.amominw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10000 } } },
.amomaxw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10100 } } },
.amominuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } },
.amomaxuw => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } },
.amoaddd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } },
.amoswapd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } },
.lrd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00010 } } },
.scd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00011 } } },
.amoxord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } },
.amoandd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } },
.amoord => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } },
.amomind => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10000 } } },
.amomaxd => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10100 } } },
.amominud => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } },
.amomaxud => .{ .opcode = .AMO, .format = .R, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } },
// OP_V
.vsetivli => .{ .opcode = .OP_V, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } },
.vsetvli => .{ .opcode = .OP_V, .format = .I, .data = .{ .f = .{ .funct3 = 0b111 } } },
.vaddvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } },
.vsubvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } },
.vmulvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100101, .funct3 = .OPIVV } } },
.vfaddvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } },
.vfsubvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } },
.vfmulvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100100, .funct3 = .OPFVV } } },
.vadcvv => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } },
.vmvvx => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } },
.vslidedownvx => .{ .opcode = .OP_V, .format = .R, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b001111, .funct3 = .OPIVX } } },
.pseudo_prologue,
.pseudo_epilogue,
.pseudo_dbg_prologue_end,
.pseudo_dbg_epilogue_begin,
.pseudo_dbg_line_column,
.pseudo_load_rm,
.pseudo_store_rm,
.pseudo_lea_rm,
.pseudo_j,
.pseudo_dead,
.pseudo_load_symbol,
.pseudo_load_tlv,
.pseudo_mv,
.pseudo_restore_regs,
.pseudo_spill_regs,
.pseudo_compare,
.pseudo_not,
.pseudo_extern_fn_reloc,
.nop,
=> std.debug.panic("lir: didn't catch pseudo {s}", .{@tagName(mnem)}),
// zig fmt: on
};
}
};
/// This is the final form of the instruction. Lir is transformed into
/// this, which is then bitcast into a u32.
pub const Instruction = union(Lir.Format) {
R: packed struct(u32) {
opcode: u7,
rd: u5,
funct3: u3,
rs1: u5,
rs2: u5,
funct7: u7,
},
I: packed struct(u32) {
opcode: u7,
rd: u5,
funct3: u3,
rs1: u5,
imm0_11: u12,
},
S: packed struct(u32) {
opcode: u7,
imm0_4: u5,
funct3: u3,
rs1: u5,
rs2: u5,
imm5_11: u7,
},
B: packed struct(u32) {
opcode: u7,
imm11: u1,
imm1_4: u4,
funct3: u3,
rs1: u5,
rs2: u5,
imm5_10: u6,
imm12: u1,
},
U: packed struct(u32) {
opcode: u7,
rd: u5,
imm12_31: u20,
},
J: packed struct(u32) {
opcode: u7,
rd: u5,
imm12_19: u8,
imm11: u1,
imm1_10: u10,
imm20: u1,
},
extra: u32,
comptime {
for (std.meta.fields(Instruction)) |field| {
assert(@bitSizeOf(field.type) == 32);
}
}
pub const Operand = union(enum) {
none,
reg: Register,
csr: CSR,
mem: Memory,
imm: Immediate,
barrier: Mir.Barrier,
};
pub fn toU32(inst: Instruction) u32 {
return switch (inst) {
inline else => |v| @bitCast(v),
};
}
pub fn encode(inst: Instruction, writer: anytype) !void {
try writer.writeInt(u32, inst.toU32(), .little);
}
pub fn fromLir(lir: Lir, ops: []const Operand) Instruction {
const opcode: u7 = @intFromEnum(lir.opcode);
switch (lir.format) {
.R => {
return .{
.R = switch (lir.data) {
.ff => |ff| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = opcode,
.funct3 = ff.funct3,
.funct7 = ff.funct7,
},
.fmt => |fmt| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = opcode,
.funct3 = fmt.rm,
.funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt),
},
.fcvt => |fcvt| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = @intFromEnum(fcvt.width),
.opcode = opcode,
.funct3 = fcvt.rm,
.funct7 = (@as(u7, fcvt.funct5) << 2) | @intFromEnum(fcvt.fmt),
},
.vecls => |vec| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = @intFromEnum(vec.umop),
.opcode = opcode,
.funct3 = @intFromEnum(vec.width),
.funct7 = (@as(u7, vec.nf) << 4) | (@as(u7, @intFromBool(vec.mew)) << 3) | (@as(u7, @intFromEnum(vec.mop)) << 1) | @intFromBool(vec.vm),
},
.vecmath => |vec| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = opcode,
.funct3 = @intFromEnum(vec.funct3),
.funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm),
},
.amo => |amo| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.rs2 = ops[2].reg.encodeId(),
.opcode = opcode,
.funct3 = @intFromEnum(amo.width),
.funct7 = @as(u7, amo.funct5) << 2 |
@as(u7, @intFromBool(ops[3].barrier == .rl)) << 1 |
@as(u7, @intFromBool(ops[4].barrier == .aq)),
},
else => unreachable,
},
};
},
.S => {
assert(ops.len == 3);
const umm = ops[2].imm.asBits(u12);
return .{
.S = .{
.imm0_4 = @truncate(umm),
.rs1 = ops[0].reg.encodeId(),
.rs2 = ops[1].reg.encodeId(),
.imm5_11 = @truncate(umm >> 5),
.opcode = opcode,
.funct3 = lir.data.f.funct3,
},
};
},
.I => {
return .{
.I = switch (lir.data) {
.f => |f| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.imm0_11 = ops[2].imm.asBits(u12),
.opcode = opcode,
.funct3 = f.funct3,
},
.sh => |sh| .{
.rd = ops[0].reg.encodeId(),
.rs1 = ops[1].reg.encodeId(),
.imm0_11 = (@as(u12, sh.typ) << 6) |
if (sh.has_5) ops[2].imm.asBits(u6) else (@as(u6, 0) | ops[2].imm.asBits(u5)),
.opcode = opcode,
.funct3 = sh.funct3,
},
.fence => |fence| .{
.rd = 0,
.rs1 = 0,
.funct3 = 0,
.imm0_11 = (@as(u12, @intFromEnum(fence.fm)) << 8) |
(@as(u12, @intFromEnum(ops[1].barrier)) << 4) |
@as(u12, @intFromEnum(ops[0].barrier)),
.opcode = opcode,
},
else => unreachable,
},
};
},
.U => {
assert(ops.len == 2);
return .{
.U = .{
.rd = ops[0].reg.encodeId(),
.imm12_31 = ops[1].imm.asBits(u20),
.opcode = opcode,
},
};
},
.J => {
assert(ops.len == 2);
const umm = ops[1].imm.asBits(u21);
// the RISC-V spec says the target index of a jump
// must be a multiple of 2
assert(umm % 2 == 0);
return .{
.J = .{
.rd = ops[0].reg.encodeId(),
.imm1_10 = @truncate(umm >> 1),
.imm11 = @truncate(umm >> 11),
.imm12_19 = @truncate(umm >> 12),
.imm20 = @truncate(umm >> 20),
.opcode = opcode,
},
};
},
.B => {
assert(ops.len == 3);
const umm = ops[2].imm.asBits(u13);
// the RISC-V spec says the target index of a branch
// must be a multiple of 2
assert(umm % 2 == 0);
return .{
.B = .{
.rs1 = ops[0].reg.encodeId(),
.rs2 = ops[1].reg.encodeId(),
.imm1_4 = @truncate(umm >> 1),
.imm5_10 = @truncate(umm >> 5),
.imm11 = @truncate(umm >> 11),
.imm12 = @truncate(umm >> 12),
.opcode = opcode,
.funct3 = lir.data.f.funct3,
},
};
},
.extra => {
assert(ops.len == 0);
return .{
.I = .{
.rd = Register.zero.encodeId(),
.rs1 = Register.zero.encodeId(),
.imm0_11 = switch (lir.data.extra) {
.ecall => 0x000,
.ebreak => 0x001,
.unimp => 0x000,
else => unreachable,
},
.opcode = opcode,
.funct3 = 0b000,
},
};
},
}
}
};
const std = @import("std");
const assert = std.debug.assert;
const log = std.log.scoped(.format);
const bits = @import("bits.zig");
const Mir = @import("Mir.zig");
const Mnemonic = @import("mnem.zig").Mnemonic;
const Lower = @import("Lower.zig");
const Register = bits.Register;
const CSR = bits.CSR;
const Memory = bits.Memory;
const Immediate = bits.Immediate;
+257
View File
@@ -0,0 +1,257 @@
pub const Mnemonic = enum(u16) {
// Arithmetics
addi,
add,
addw,
sub,
subw,
// Bits
xori,
xor,
@"or",
@"and",
andi,
slt,
sltu,
sltiu,
slli,
srli,
srai,
slliw,
srliw,
sraiw,
sll,
srl,
sra,
sllw,
srlw,
sraw,
// Control Flow
jalr,
jal,
beq,
bne,
// Memory
lui,
auipc,
ld,
lw,
lh,
lb,
lbu,
lhu,
lwu,
sd,
sw,
sh,
sb,
// System
ebreak,
ecall,
unimp,
nop,
// M extension
mul,
mulh,
mulhu,
mulhsu,
mulw,
div,
divu,
divw,
divuw,
rem,
remu,
remw,
remuw,
// F extension (32-bit float)
fadds,
fsubs,
fmuls,
fdivs,
fmins,
fmaxs,
fsqrts,
flw,
fsw,
feqs,
flts,
fles,
// D extension (64-bit float)
faddd,
fsubd,
fmuld,
fdivd,
fmind,
fmaxd,
fsqrtd,
fld,
fsd,
feqd,
fltd,
fled,
fcvtws,
fcvtwus,
fcvtls,
fcvtlus,
fcvtwd,
fcvtwud,
fcvtld,
fcvtlud,
fcvtsw,
fcvtswu,
fcvtsl,
fcvtslu,
fcvtdw,
fcvtdwu,
fcvtdl,
fcvtdlu,
fsgnjns,
fsgnjnd,
fsgnjxs,
fsgnjxd,
// Zicsr Extension Instructions
csrrs,
// V Extension Instructions
vsetvli,
vsetivli,
vaddvv,
vfaddvv,
vsubvv,
vfsubvv,
vmulvv,
vfmulvv,
vslidedownvx,
vle8v,
vle16v,
vle32v,
vle64v,
vse8v,
vse16v,
vse32v,
vse64v,
vadcvv,
vmvvx,
// Zbb Extension Instructions
clz,
clzw,
cpop,
cpopw,
// A Extension Instructions
fence,
fencetso,
lrw,
scw,
amoswapw,
amoaddw,
amoandw,
amoorw,
amoxorw,
amomaxw,
amominw,
amomaxuw,
amominuw,
lrd,
scd,
amoswapd,
amoaddd,
amoandd,
amoord,
amoxord,
amomaxd,
amomind,
amomaxud,
amominud,
// Pseudo-instructions. Used for anything that isn't 1:1 with an
// assembly instruction.
/// Pseudo-instruction that will generate a backpatched
/// function prologue.
pseudo_prologue,
/// Pseudo-instruction that will generate a backpatched
/// function epilogue
pseudo_epilogue,
/// Pseudo-instruction: End of prologue
pseudo_dbg_prologue_end,
/// Pseudo-instruction: Beginning of epilogue
pseudo_dbg_epilogue_begin,
/// Pseudo-instruction: Update debug line
pseudo_dbg_line_column,
/// Pseudo-instruction that loads from memory into a register.
pseudo_load_rm,
/// Pseudo-instruction that stores from a register into memory
pseudo_store_rm,
/// Pseudo-instruction that loads the address of memory into a register.
pseudo_lea_rm,
/// Jumps. Uses `inst` payload.
pseudo_j,
/// Dead inst, ignored by the emitter.
pseudo_dead,
/// Loads the address of a value that hasn't yet been allocated in memory.
pseudo_load_symbol,
/// Loads the address of a TLV.
pseudo_load_tlv,
/// Moves the value of rs1 to rd.
pseudo_mv,
pseudo_restore_regs,
pseudo_spill_regs,
pseudo_compare,
pseudo_not,
pseudo_extern_fn_reloc,
};
pub const Pseudo = enum(u8) {
li,
mv,
tail,
beqz,
ret,
};
+3 -1
View File
@@ -106,7 +106,9 @@ pub fn generateLazyFunction(
const target = namespace.fileScope(zcu).mod.resolved_target.result;
switch (target_util.zigBackend(target, false)) {
else => unreachable,
inline .stage2_x86_64 => |backend| {
inline .stage2_x86_64,
.stage2_riscv64,
=> |backend| {
dev.check(devFeatureForBackend(backend));
return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
},
+7 -33
View File
@@ -25,47 +25,27 @@ pub fn writeAddend(
}
pub fn writeInstU(code: *[4]u8, value: u32) void {
var data = Encoding.Data{
.U = mem.bytesToValue(std.meta.TagPayload(
Encoding.Data,
Encoding.Data.U,
), code),
};
var data: Instruction = .{ .U = mem.bytesToValue(std.meta.TagPayload(Instruction, .U), code) };
const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800);
data.U.imm12_31 = bitSlice(compensated, 31, 12);
mem.writeInt(u32, code, data.toU32(), .little);
}
pub fn writeInstI(code: *[4]u8, value: u32) void {
var data = Encoding.Data{
.I = mem.bytesToValue(std.meta.TagPayload(
Encoding.Data,
Encoding.Data.I,
), code),
};
var data: Instruction = .{ .I = mem.bytesToValue(std.meta.TagPayload(Instruction, .I), code) };
data.I.imm0_11 = bitSlice(value, 11, 0);
mem.writeInt(u32, code, data.toU32(), .little);
}
pub fn writeInstS(code: *[4]u8, value: u32) void {
var data = Encoding.Data{
.S = mem.bytesToValue(std.meta.TagPayload(
Encoding.Data,
Encoding.Data.S,
), code),
};
var data: Instruction = .{ .S = mem.bytesToValue(std.meta.TagPayload(Instruction, .S), code) };
data.S.imm0_4 = bitSlice(value, 4, 0);
data.S.imm5_11 = bitSlice(value, 11, 5);
mem.writeInt(u32, code, data.toU32(), .little);
}
pub fn writeInstJ(code: *[4]u8, value: u32) void {
var data = Encoding.Data{
.J = mem.bytesToValue(std.meta.TagPayload(
Encoding.Data,
Encoding.Data.J,
), code),
};
var data: Instruction = .{ .J = mem.bytesToValue(std.meta.TagPayload(Instruction, .J), code) };
data.J.imm1_10 = bitSlice(value, 10, 1);
data.J.imm11 = bitSlice(value, 11, 11);
data.J.imm12_19 = bitSlice(value, 19, 12);
@@ -74,12 +54,7 @@ pub fn writeInstJ(code: *[4]u8, value: u32) void {
}
pub fn writeInstB(code: *[4]u8, value: u32) void {
var data = Encoding.Data{
.B = mem.bytesToValue(std.meta.TagPayload(
Encoding.Data,
Encoding.Data.B,
), code),
};
var data: Instruction = .{ .B = mem.bytesToValue(std.meta.TagPayload(Instruction, .B), code) };
data.B.imm1_4 = bitSlice(value, 4, 1);
data.B.imm5_10 = bitSlice(value, 10, 5);
data.B.imm11 = bitSlice(value, 11, 11);
@@ -109,9 +84,8 @@ pub const RiscvEflags = packed struct(u32) {
_unused: u8,
};
const encoder = @import("../arch/riscv64/encoder.zig");
const Encoding = @import("../arch/riscv64/Encoding.zig");
const mem = std.mem;
const std = @import("std");
pub const Instruction = encoder.Instruction;
const encoding = @import("../arch/riscv64/encoding.zig");
const Instruction = encoding.Instruction;
+1
View File
@@ -6,6 +6,7 @@ test "@abs integers" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try comptime testAbsIntegers();
try testAbsIntegers();
-3
View File
@@ -16,7 +16,6 @@ test "global variable alignment" {
}
test "large alignment of local constant" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
@@ -26,7 +25,6 @@ test "large alignment of local constant" {
}
test "slicing array of length 1 can not assume runtime index is always zero" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
@@ -511,7 +509,6 @@ test "read 128-bit field from default aligned struct in global memory" {
}
test "struct field explicit alignment" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-20
View File
@@ -22,7 +22,6 @@ test "arrays" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var array: [5]u32 = undefined;
@@ -160,7 +159,6 @@ test "array len field" {
test "array with sentinels" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest(is_ct: bool) !void {
@@ -532,7 +530,6 @@ test "sentinel element count towards the ABI size calculation" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -614,7 +611,6 @@ test "type coercion of pointer to anon struct literal to pointer to array" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const U = union {
@@ -667,7 +663,6 @@ test "array init of container level array variable" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
var pair: [2]usize = .{ 1, 2 };
@@ -688,8 +683,6 @@ test "array init of container level array variable" {
}
test "runtime initialized sentinel-terminated array literal" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var c: u16 = 300;
_ = &c;
const f = &[_:0x9999]u16{c};
@@ -776,8 +769,6 @@ test "array init with no result pointer sets field result types" {
}
test "runtime side-effects in comptime-known array init" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var side_effects: u4 = 0;
const init = [4]u4{
blk: {
@@ -802,8 +793,6 @@ test "runtime side-effects in comptime-known array init" {
}
test "slice initialized through reference to anonymous array init provides result types" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var my_u32: u32 = 123;
var my_u64: u64 = 456;
_ = .{ &my_u32, &my_u64 };
@@ -817,8 +806,6 @@ test "slice initialized through reference to anonymous array init provides resul
}
test "sentinel-terminated slice initialized through reference to anonymous array init provides result types" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var my_u32: u32 = 123;
var my_u64: u64 = 456;
_ = .{ &my_u32, &my_u64 };
@@ -869,8 +856,6 @@ test "many-item sentinel-terminated pointer initialized through reference to ano
}
test "pointer to array initialized through reference to anonymous array init provides result types" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var my_u32: u32 = 123;
var my_u64: u64 = 456;
_ = .{ &my_u32, &my_u64 };
@@ -884,8 +869,6 @@ test "pointer to array initialized through reference to anonymous array init pro
}
test "pointer to sentinel-terminated array initialized through reference to anonymous array init provides result types" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var my_u32: u32 = 123;
var my_u64: u64 = 456;
_ = .{ &my_u32, &my_u64 };
@@ -912,7 +895,6 @@ test "copied array element doesn't alias source" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var x: [10][10]u32 = undefined;
@@ -925,7 +907,6 @@ test "copied array element doesn't alias source" {
test "array initialized with string literal" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
a: u32,
@@ -993,7 +974,6 @@ test "accessing multidimensional global array at comptime" {
if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const array = [_][]const []const u8{
-5
View File
@@ -15,7 +15,6 @@ test "cmpxchg" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testCmpxchg();
try comptime testCmpxchg();
@@ -108,7 +107,6 @@ test "cmpxchg with ignored result" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var x: i32 = 1234;
@@ -153,7 +151,6 @@ test "cmpxchg on a global variable" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
// https://github.com/ziglang/zig/issues/10627
@@ -169,7 +166,6 @@ test "atomic load and rmw with enum" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Value = enum(u8) { a, b, c };
var x = Value.a;
@@ -205,7 +201,6 @@ test "atomicrmw with floats" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
// https://github.com/ziglang/zig/issues/10627
-17
View File
@@ -16,8 +16,6 @@ test "empty function with comments" {
}
test "truncate" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(testTruncate(0x10fd) == 0xfd);
comptime assert(testTruncate(0x10fd) == 0xfd);
}
@@ -27,7 +25,6 @@ fn testTruncate(x: u32) u8 {
test "truncate to non-power-of-two integers" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testTrunc(u32, u1, 0b10101, 0b1);
try testTrunc(u32, u1, 0b10110, 0b0);
@@ -45,7 +42,6 @@ test "truncate to non-power-of-two integers from 128-bit" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testTrunc(u128, u1, 0xffffffff_ffffffff_ffffffff_01010101, 0x01);
try testTrunc(u128, u1, 0xffffffff_ffffffff_ffffffff_01010110, 0x00);
@@ -224,7 +220,6 @@ const OpaqueB = opaque {};
test "opaque types" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(*OpaqueA != *OpaqueB);
@@ -376,7 +371,6 @@ test "take address of parameter" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testTakeAddressOfParameter(12.34);
}
@@ -401,7 +395,6 @@ test "array 2D const double ptr" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const rect_2d_vertexes = [_][1]f32{
[_]f32{1.0},
@@ -414,7 +407,6 @@ test "array 2D const double ptr with offset" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const rect_2d_vertexes = [_][2]f32{
[_]f32{ 3.0, 4.239 },
@@ -427,7 +419,6 @@ test "array 3D const double ptr with offset" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const rect_3d_vertexes = [_][2][2]f32{
[_][2]f32{
@@ -622,7 +613,6 @@ var global_ptr = &gdt[0];
test "global constant is loaded with a runtime-known index" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -641,7 +631,6 @@ test "global constant is loaded with a runtime-known index" {
test "multiline string literal is null terminated" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const s1 =
\\one
@@ -656,7 +645,6 @@ test "string escapes" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expectEqualStrings("\"", "\x22");
try expectEqualStrings("\'", "\x27");
@@ -789,7 +777,6 @@ test "discarding the result of various expressions" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo() !u32 {
@@ -1072,7 +1059,6 @@ test "returning an opaque type from a function" {
test "orelse coercion as function argument" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Loc = struct { start: i32 = -1 };
const Container = struct {
@@ -1186,8 +1172,6 @@ fn testUnsignedCmp(comptime T: type) !void {
}
test "integer compare <= 64 bits" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
inline for (.{ u8, u16, u32, u64, usize, u10, u20, u30, u60 }) |T| {
try testUnsignedCmp(T);
try comptime testUnsignedCmp(T);
@@ -1324,7 +1308,6 @@ test "break out of block based on comptime known values" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const source = "A-";
-1
View File
@@ -65,7 +65,6 @@ test "sharded table" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
// realistic 16-way sharding
try testShardedTable(u32, 4, 8);
-3
View File
@@ -10,7 +10,6 @@ const native_endian = builtin.target.cpu.arch.endian();
test "@bitCast iX -> uX (32, 64)" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const bit_values = [_]usize{ 32, 64 };
@@ -165,7 +164,6 @@ test "@bitCast packed structs at runtime and comptime" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Full = packed struct {
number: u16,
@@ -491,7 +489,6 @@ test "@bitCast of packed struct of bools all true" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const P = packed struct {
b0: bool,
-2
View File
@@ -342,7 +342,6 @@ test "inline call preserves tail call" {
test "inline call doesn't re-evaluate non generic struct" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo(f: struct { a: u8, b: u8 }) !void {
@@ -441,7 +440,6 @@ test "non-anytype generic parameters provide result type" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn f(comptime T: type, y: T) !void {
-22
View File
@@ -24,7 +24,6 @@ test "peer type resolution: ?T and T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(peerTypeTAndOptionalT(true, false).? == 0);
try expect(peerTypeTAndOptionalT(false, false).? == 3);
@@ -104,7 +103,6 @@ test "@floatFromInt" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -163,7 +161,6 @@ test "@intFromFloat" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testIntFromFloats();
try comptime testIntFromFloats();
@@ -303,7 +300,6 @@ test "peer result null and comptime_int" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn blah(n: i32) ?i32 {
@@ -372,7 +368,6 @@ test "return u8 coercing into ?u32 return type" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -450,7 +445,6 @@ test "implicitly cast from T to anyerror!?T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try castToOptionalTypeError(1);
try comptime castToOptionalTypeError(1);
@@ -602,7 +596,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {
test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const window_name = [1][*]const u8{"window name"};
const x: [*]const ?[*]const u8 = &window_name;
@@ -668,7 +661,6 @@ test "@floatCast cast down" {
test "peer type resolution: unreachable, error set, unreachable" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Error = error{
FileDescriptorAlreadyPresentInSet,
@@ -763,7 +755,6 @@ test "peer type resolution: error union and error set" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const a: error{Three} = undefined;
const b: error{ One, Two }!u32 = undefined;
@@ -957,7 +948,6 @@ test "peer cast [:x]T to [*:x]T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -1023,7 +1013,6 @@ test "variable initialization uses result locations properly with regards to the
test "cast between C pointer with different but compatible types" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo(arg: [*]c_ushort) u16 {
@@ -1219,7 +1208,6 @@ test "implicitly cast from [N]T to ?[]const T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi"));
@@ -1544,7 +1532,6 @@ test "cast typed undefined to int" {
test "implicit cast from [:0]T to [*c]T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var a: [:0]const u8 = "foo";
_ = &a;
@@ -1753,7 +1740,6 @@ test "peer type resolution: array and vector with same child type" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var arr: [2]u32 = .{ 0, 1 };
var vec: @Vector(2, u32) = .{ 2, 3 };
@@ -1845,7 +1831,6 @@ test "peer type resolution: three-way resolution combines error set and optional
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const E = error{Foo};
var a: E = error.Foo;
@@ -1913,7 +1898,6 @@ test "peer type resolution: optional fixed-width int and comptime_int" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var a: ?i32 = 42;
_ = &a;
@@ -1960,7 +1944,6 @@ test "peer type resolution: vector and tuple" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var vec: @Vector(3, i32) = .{ 1, 2, 3 };
_ = &vec;
@@ -1985,7 +1968,6 @@ test "peer type resolution: vector and array and tuple" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var vec: @Vector(2, i8) = .{ 10, 20 };
var arr: [2]i8 = .{ 30, 40 };
@@ -2094,7 +2076,6 @@ test "peer type resolution: many compatible pointers" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var buf = "foo-3".*;
@@ -2216,7 +2197,6 @@ test "peer type resolution: pointer attributes are combined correctly" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var buf_a align(4) = "foo".*;
var buf_b align(4) = "bar".*;
@@ -2277,7 +2257,6 @@ test "cast builtins can wrap result in optional" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const MyEnum = enum(u32) { _ };
@@ -2586,7 +2565,6 @@ test "result information is preserved through many nested structures" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
-2
View File
@@ -35,8 +35,6 @@ test "coerce i8 to i32 and @intCast back" {
}
test "coerce non byte-sized integers accross 32bits boundary" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
{
var v: u21 = 6417;
_ = &v;
+2 -1
View File
@@ -53,7 +53,6 @@ test "return variable while defer expression in scope to modify it" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -116,6 +115,7 @@ test "errdefer with payload" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo() !i32 {
@@ -138,6 +138,7 @@ test "reference to errdefer payload" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo() !i32 {
-2
View File
@@ -23,8 +23,6 @@ test "simple destructure" {
}
test "destructure with comptime syntax" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
{
-6
View File
@@ -618,7 +618,6 @@ test "enum with specified tag values" {
test "non-exhaustive enum" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const E = enum(u8) { a, b, _ };
@@ -934,7 +933,6 @@ const Bar = enum { A, B, C, D };
test "enum literal casting to error union with payload enum" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var bar: error{B}!Bar = undefined;
bar = .B; // should never cast to the error set
@@ -1076,7 +1074,6 @@ test "enum literal casting to optional" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var bar: ?Bar = undefined;
bar = .B;
@@ -1105,7 +1102,6 @@ test "bit field access with enum fields" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var data = bit_field_1;
try expect(getA(&data) == A.Two);
@@ -1223,8 +1219,6 @@ test "enum tag from a local variable" {
}
test "auto-numbered enum with signed tag type" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const E = enum(i32) { a, b };
try std.testing.expectEqual(@as(i32, 0), @intFromEnum(E.a));
-10
View File
@@ -319,7 +319,6 @@ test "error inference with an empty set" {
test "error union peer type resolution" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testErrorUnionPeerTypeResolution(1);
}
@@ -403,7 +402,6 @@ test "nested error union function call in optional unwrap" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const Foo = struct {
@@ -450,7 +448,6 @@ test "nested error union function call in optional unwrap" {
test "return function call to error set from error union function" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn errorable() anyerror!i32 {
@@ -469,7 +466,6 @@ test "optional error set is the same size as error set" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
comptime assert(@sizeOf(?anyerror) == @sizeOf(anyerror));
comptime assert(@alignOf(?anyerror) == @alignOf(anyerror));
@@ -917,7 +913,6 @@ test "field access of anyerror results in smaller error set" {
test "optional error union return type" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo() ?anyerror!u32 {
@@ -932,7 +927,6 @@ test "optional error union return type" {
test "optional error set return type" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const E = error{ A, B };
const S = struct {
@@ -946,8 +940,6 @@ test "optional error set return type" {
}
test "optional error set function parameter" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest(a: ?anyerror) !void {
try std.testing.expect(a.? == error.OutOfMemory);
@@ -977,7 +969,6 @@ test "returning an error union containing a type with no runtime bits" {
test "try used in recursive function with inferred error set" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const Value = union(enum) {
@@ -1079,7 +1070,6 @@ test "result location initialization of error union with OPV payload" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
x: u0,
-1
View File
@@ -782,7 +782,6 @@ test "array concatenation peer resolves element types - pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var a = [2]u3{ 1, 7 };
var b = [3]u8{ 200, 225, 255 };
-1
View File
@@ -134,7 +134,6 @@ test "cmp f16" {
test "cmp f32/f64" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testCmp(f32);
try comptime testCmp(f32);
-2
View File
@@ -181,7 +181,6 @@ test "function with complex callconv and return type expressions" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(fComplexCallconvRet(3).x == 9);
}
@@ -451,7 +450,6 @@ test "implicit cast function to function ptr" {
test "method call with optional and error union first param" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
x: i32 = 1234,
-4
View File
@@ -112,7 +112,6 @@ test "for with null and T peer types and inferred result location type" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest(slice: []const u8) !void {
@@ -154,7 +153,6 @@ test "for loop with pointer elem var" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const source = "abcdefg";
var target: [source.len]u8 = undefined;
@@ -228,7 +226,6 @@ test "else continue outer for" {
test "for loop with else branch" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
{
var x = [_]u32{ 1, 2 };
@@ -489,7 +486,6 @@ test "inferred alloc ptr of for loop" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
{
var cond = false;
-6
View File
@@ -19,7 +19,6 @@ fn checkSize(comptime T: type) usize {
test "simple generic fn" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(max(i32, 3, -1) == 3);
try expect(max(u8, 1, 100) == 100);
@@ -56,7 +55,6 @@ test "fn with comptime args" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(gimmeTheBigOne(1234, 5678) == 5678);
try expect(shouldCallSameInstance(34, 12) == 34);
@@ -67,7 +65,6 @@ test "anytype params" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(max_i32(12, 34) == 34);
try expect(max_f64(1.2, 3.4) == 3.4);
@@ -404,8 +401,6 @@ test "generic struct as parameter type" {
}
test "slice as parameter type" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn internComptimeString(comptime str: []const u8) *const []const u8 {
return &struct {
@@ -503,7 +498,6 @@ test "union in struct captures argument" {
test "function argument tuple used as struct field" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn DeleagateWithContext(comptime Function: type) type {
-1
View File
@@ -18,7 +18,6 @@ test "store to global vector" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(vpos[1] == 0.0);
vpos = @Vector(2, f32){ 0.0, 1.0 };
-6
View File
@@ -82,7 +82,6 @@ test "const result loc, runtime if cond, else unreachable" {
test "if copies its payload" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -119,7 +118,6 @@ test "if peer expressions inferred optional type" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var self: []const u8 = "abcdef";
var index: usize = 0;
@@ -147,8 +145,6 @@ test "if-else expression with runtime condition result location is inferred opti
}
test "result location with inferred type ends up being pointer to comptime_int" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var a: ?u32 = 1234;
var b: u32 = 2000;
_ = .{ &a, &b };
@@ -194,8 +190,6 @@ test "if value shouldn't be load-elided if used later (structs)" {
}
test "if value shouldn't be load-elided if used later (optionals)" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var a: ?i32 = 1;
var b: ?i32 = 1;
-9
View File
@@ -65,7 +65,6 @@ test "@clz" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testClz();
try comptime testClz();
@@ -663,8 +662,6 @@ fn rem(comptime T: type, a: T, b: T) T {
}
test "unsigned wrapping" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testUnsignedWrappingEval(maxInt(u32));
try comptime testUnsignedWrappingEval(maxInt(u32));
}
@@ -676,8 +673,6 @@ fn testUnsignedWrappingEval(x: u32) !void {
}
test "signed wrapping" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testSignedWrappingEval(maxInt(i32));
try comptime testSignedWrappingEval(maxInt(i32));
}
@@ -725,7 +720,6 @@ fn negateWrap(comptime T: type, x: T) T {
test "unsigned 64-bit division" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) {
// https://github.com/ziglang/zig/issues/16846
@@ -838,7 +832,6 @@ test "@addWithOverflow" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testAddWithOverflow(u8, 250, 100, 94, 1);
try testAddWithOverflow(u8, 100, 150, 250, 0);
@@ -899,7 +892,6 @@ test "@addWithOverflow > 64 bits" {
test "small int addition" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var x: u2 = 0;
try expect(x == 0);
@@ -927,7 +919,6 @@ fn testMulWithOverflow(comptime T: type, a: T, b: T, mul: T, bit: u1) !void {
test "basic @mulWithOverflow" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMulWithOverflow(u8, 86, 3, 2, 1);
try testMulWithOverflow(u8, 85, 3, 255, 0);
-4
View File
@@ -7,7 +7,6 @@ test "memcpy and memset intrinsics" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemcpyMemset();
try comptime testMemcpyMemset();
@@ -29,7 +28,6 @@ test "@memcpy with both operands single-ptr-to-array, one is null-terminated" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemcpyBothSinglePtrArrayOneIsNullTerminated();
try comptime testMemcpyBothSinglePtrArrayOneIsNullTerminated();
@@ -50,7 +48,6 @@ test "@memcpy dest many pointer" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemcpyDestManyPtr();
try comptime testMemcpyDestManyPtr();
@@ -73,7 +70,6 @@ test "@memcpy slice" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemcpySlice();
try comptime testMemcpySlice();
-3
View File
@@ -53,7 +53,6 @@ test "maybe return" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try maybeReturnImpl();
try comptime maybeReturnImpl();
@@ -73,7 +72,6 @@ fn foo(x: ?i32) ?bool {
test "test null runtime" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testTestNullRuntime(null);
}
@@ -188,7 +186,6 @@ test "unwrap optional which is field of global var" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
struct_with_optional.field = null;
if (struct_with_optional.field) |payload| {
+1 -9
View File
@@ -9,7 +9,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
test "passing an optional integer as a parameter" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn entry() bool {
@@ -134,7 +133,6 @@ test "nested optional field in struct" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S2 = struct {
y: u8,
@@ -260,7 +258,6 @@ test "unwrap function call with optional pointer return value" {
test "nested orelse" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn entry() !void {
@@ -287,7 +284,6 @@ test "nested orelse" {
test "self-referential struct through a slice of optional" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const Node = struct {
@@ -344,7 +340,6 @@ test "0-bit child type coerced to optional return ptr result location" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -535,7 +530,6 @@ test "Optional slice size is optimized" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expect(@sizeOf(?[]u8) == @sizeOf([]u8));
var a: ?[]const u8 = null;
@@ -549,7 +543,6 @@ test "Optional slice passed to function" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn foo(a: ?[]const u8) !void {
@@ -566,7 +559,6 @@ test "Optional slice passed to function" {
test "peer type resolution in nested if expressions" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Thing = struct { n: i32 };
var a = false;
@@ -594,6 +586,7 @@ test "cast slice to const slice nested in error union and optional" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn inner() !?[]u8 {
@@ -632,7 +625,6 @@ test "result location initialization of optional with OPV payload" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
x: u0,
-11
View File
@@ -258,7 +258,6 @@ test "nested packed struct unaligned" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
const S1 = packed struct {
@@ -331,7 +330,6 @@ test "byte-aligned field pointer offsets" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const A = packed struct {
@@ -434,7 +432,6 @@ test "nested packed struct field pointers" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
const S2 = packed struct {
@@ -962,7 +959,6 @@ test "pointer to container level packed struct field" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = packed struct(u32) {
test_bit: bool,
@@ -1008,8 +1004,6 @@ test "bitcast back and forth" {
}
test "field access of packed struct smaller than its abi size inside struct initialized with rls" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
// Originally reported at https://github.com/ziglang/zig/issues/14200
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
@@ -1028,8 +1022,6 @@ test "field access of packed struct smaller than its abi size inside struct init
}
test "modify nested packed struct aligned field" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
// Originally reported at https://github.com/ziglang/zig/issues/14632
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
@@ -1096,7 +1088,6 @@ test "packed struct used as part of anon decl name" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = packed struct { a: u0 = 0 };
var a: u8 = 0;
@@ -1164,7 +1155,6 @@ test "assignment to non-byte-aligned field in packed struct" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Frame = packed struct {
num: u20,
@@ -1275,7 +1265,6 @@ test "2-byte packed struct argument in C calling convention" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = packed struct(u16) {
x: u15 = 0,
-2
View File
@@ -8,7 +8,6 @@ test "flags in packed union" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testFlagsInPackedUnion();
try comptime testFlagsInPackedUnion();
@@ -51,7 +50,6 @@ test "flags in packed union at offset" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testFlagsInPackedUnionAtOffset();
try comptime testFlagsInPackedUnionAtOffset();
+1 -5
View File
@@ -125,7 +125,6 @@ test "initialize const optional C pointer to null" {
test "assigning integer to C pointer" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var x: i32 = 0;
var y: i32 = 1;
@@ -143,7 +142,6 @@ test "assigning integer to C pointer" {
test "C pointer comparison and arithmetic" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -230,6 +228,7 @@ test "implicit cast error unions with non-optional to optional pointer" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -343,7 +342,6 @@ test "array initialization types" {
test "null terminated pointer" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -517,7 +515,6 @@ test "element pointer to slice" {
test "element pointer arithmetic to slice" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -572,7 +569,6 @@ test "ptrCast comptime known slice to C pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const s: [:0]const u8 = "foo";
var p: [*c]const u8 = @ptrCast(s);
-1
View File
@@ -8,7 +8,6 @@ test "@popCount integers" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try comptime testPopCountIntegers();
try testPopCountIntegers();
-6
View File
@@ -30,7 +30,6 @@ comptime {
test "slicing" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var array: [20]i32 = undefined;
@@ -256,7 +255,6 @@ test "C pointer slice access" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var buf: [10]u32 = [1]u32{42} ** 10;
const c_ptr = @as([*c]const u32, @ptrCast(&buf));
@@ -836,7 +834,6 @@ test "global slice field access" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
var slice: []const u8 = undefined;
@@ -892,7 +889,6 @@ test "empty slice ptr is non null" {
test "slice decays to many pointer" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var buf: [8]u8 = "abcdefg\x00".*;
const p: [*:0]const u8 = buf[0..7 :0];
@@ -903,7 +899,6 @@ test "write through pointer to optional slice arg" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn bar(foo: *?[]const u8) !void {
@@ -956,7 +951,6 @@ test "slicing slices gives correct result" {
if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const foo = "1234";
const bar = foo[0..4];
-3
View File
@@ -17,7 +17,6 @@ test "@src" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try doTheTest();
}
@@ -38,8 +37,6 @@ test "@src used as a comptime parameter" {
}
test "@src in tuple passed to anytype function" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn Foo(a: anytype) u32 {
return a[0].line;
-2
View File
@@ -34,7 +34,6 @@ const ptr_type_name: [*:0]const u8 = type_name;
test "@typeName() returns a string literal" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name));
@@ -65,7 +64,6 @@ fn testFnForSrc() std.builtin.SourceLocation {
test "@src() returns a struct containing 0-terminated string slices" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const src = testFnForSrc();
try std.testing.expect([:0]const u8 == @TypeOf(src.file));
-19
View File
@@ -68,7 +68,6 @@ const SmallStruct = struct {
test "lower unnamed constants" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var foo = SmallStruct{ .a = 1, .b = 255 };
try expect(foo.first() == 1);
@@ -419,7 +418,6 @@ test "packed struct 24bits" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO
if (comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -506,7 +504,6 @@ test "packed struct fields are ordered from LSB to MSB" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var all: u64 = 0x7765443322221111;
var bytes: [8]u8 align(@alignOf(Bitfields)) = undefined;
@@ -527,7 +524,6 @@ test "implicit cast packed struct field to const ptr" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const LevelUpMove = packed struct {
move_id: u9,
@@ -593,7 +589,6 @@ test "bit field access" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var data = bit_field_1;
try expect(getA(&data) == 1);
@@ -650,7 +645,6 @@ test "packed array 24bits" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
comptime {
try expect(@sizeOf([9]Foo32Bits) == 9 * 4);
@@ -718,7 +712,6 @@ test "pointer to packed struct member in a stack variable" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = packed struct {
a: u2,
@@ -1103,7 +1096,6 @@ test "packed struct with undefined initializers" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const P = packed struct {
@@ -1133,7 +1125,6 @@ test "packed struct with undefined initializers" {
test "for loop over pointers to struct, getting field from struct pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const Foo = struct {
@@ -1243,7 +1234,6 @@ test "typed init through error unions and optionals" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
a: u32,
@@ -1465,8 +1455,6 @@ test "struct field has a pointer to an aligned version of itself" {
}
test "struct has only one reference" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn optionalStructParam(_: ?struct { x: u8 }) void {}
fn errorUnionStructParam(_: error{}!struct { x: u8 }) void {}
@@ -1573,7 +1561,6 @@ test "no dependency loop on optional field wrapped in generic function" {
test "optional field init with tuple" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
a: ?struct { b: u32 },
@@ -1588,7 +1575,6 @@ test "optional field init with tuple" {
test "if inside struct init inside if" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const MyStruct = struct { x: u32 };
const b: u32 = 5;
@@ -1770,8 +1756,6 @@ test "struct init with no result pointer sets field result types" {
}
test "runtime side-effects in comptime-known struct init" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var side_effects: u4 = 0;
const S = struct { a: u4, b: u4, c: u4, d: u4 };
const init = S{
@@ -1797,8 +1781,6 @@ test "runtime side-effects in comptime-known struct init" {
}
test "pointer to struct initialized through reference to anonymous initializer provides result types" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct { a: u8, b: u16, c: *const anyopaque };
var my_u16: u16 = 0xABCD;
_ = &my_u16;
@@ -1995,7 +1977,6 @@ test "runtime call in nested initializer" {
if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Holder = struct {
array: []const u8,
+2 -10
View File
@@ -316,7 +316,6 @@ test "switch on union with some prongs capturing" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const X = union(enum) {
a,
@@ -427,6 +426,7 @@ test "else prong of switch on error set excludes other cases" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -462,6 +462,7 @@ test "switch prongs with error set cases make a new error set type for capture v
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -516,7 +517,6 @@ test "switch with null and T peer types and inferred result location type" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest(c: u8) !void {
@@ -537,7 +537,6 @@ test "switch prongs with cases with identical payload types" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Union = union(enum) {
A: usize,
@@ -781,8 +780,6 @@ test "comptime inline switch" {
}
test "switch capture peer type resolution" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const U = union(enum) {
a: u32,
b: u64,
@@ -798,8 +795,6 @@ test "switch capture peer type resolution" {
}
test "switch capture peer type resolution for in-memory coercible payloads" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T1 = c_int;
const T2 = @Type(@typeInfo(T1));
@@ -821,7 +816,6 @@ test "switch capture peer type resolution for in-memory coercible payloads" {
test "switch pointer capture peer type resolution" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T1 = c_int;
const T2 = @Type(@typeInfo(T1));
@@ -924,8 +918,6 @@ test "switch prong captures range" {
}
test "prong with inline call to unreachable" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const U = union(enum) {
void: void,
bool: bool,
@@ -18,7 +18,6 @@ test "switch prong implicit cast" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const result = switch (foo(2) catch unreachable) {
FormValue.One => false,
-3
View File
@@ -6,7 +6,6 @@ test "thread local variable" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
.x86_64, .x86 => {},
else => return error.SkipZigTest,
@@ -29,7 +28,6 @@ test "pointer to thread local array" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
.x86_64, .x86 => {},
else => return error.SkipZigTest,
@@ -47,7 +45,6 @@ test "reference a global threadlocal variable" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
.x86_64, .x86 => {},
else => return error.SkipZigTest,
+1
View File
@@ -51,6 +51,7 @@ test "`try`ing an if/else expression" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn getError() !void {
-5
View File
@@ -280,7 +280,6 @@ test "tuple in tuple passed to generic function" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn pair(x: f32, y: f32) std.meta.Tuple(&.{ f32, f32 }) {
@@ -300,7 +299,6 @@ test "coerce tuple to tuple" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T = std.meta.Tuple(&.{u8});
const S = struct {
@@ -315,7 +313,6 @@ test "tuple type with void field" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T = std.meta.Tuple(&[_]type{void});
const x = T{{}};
@@ -352,7 +349,6 @@ test "zero sized struct in tuple handled correctly" {
test "tuple type with void field and a runtime field" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T = std.meta.Tuple(&[_]type{ usize, void });
var t: T = .{ 5, {} };
@@ -409,7 +405,6 @@ test "nested runtime conditionals in tuple initializer" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var data: u8 = 0;
_ = &data;
-2
View File
@@ -7,7 +7,6 @@ const expectEqualStrings = testing.expectEqualStrings;
test "tuple declaration type info" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
{
const T = struct { comptime u32 align(2) = 1, []const u8 };
@@ -36,7 +35,6 @@ test "tuple declaration type info" {
test "Tuple declaration usage" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T = struct { u32, []const u8 };
var t: T = .{ 1, "foo" };
-5
View File
@@ -565,8 +565,6 @@ test "StructField.is_comptime" {
}
test "typeInfo resolves usingnamespace declarations" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const A = struct {
pub const f1 = 42;
};
@@ -592,7 +590,6 @@ test "value from struct @typeInfo default_value can be loaded at comptime" {
test "@typeInfo decls and usingnamespace" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const A = struct {
pub const x = 5;
@@ -633,8 +630,6 @@ test "type info of tuple of string literal default value" {
}
test "@typeInfo only contains pub decls" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const other = struct {
const std = @import("std");
-8
View File
@@ -16,7 +16,6 @@ test "anon fn param" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/9339
try expectEqualStringsIgnoreDigits(
@@ -42,7 +41,6 @@ test "anon field init" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const Foo = .{
@@ -69,7 +67,6 @@ test "basic" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try expectEqualStrings("i64", @typeName(i64));
try expectEqualStrings("*usize", @typeName(*usize));
@@ -91,7 +88,6 @@ test "top level decl" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
try expectEqualStrings(
@@ -142,7 +138,6 @@ test "fn param" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/675
@@ -223,7 +218,6 @@ test "local variable" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const Foo = struct { a: u32 };
@@ -243,7 +237,6 @@ test "comptime parameters not converted to anytype in function type" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const T = fn (fn (type) void, void) void;
try expectEqualStrings("fn (comptime fn (comptime type) void, void) void", @typeName(T));
@@ -253,7 +246,6 @@ test "anon name strategy used in sub expression" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const S = struct {
-9
View File
@@ -100,7 +100,6 @@ const FooExtern = extern union {
test "basic extern unions" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var foo = FooExtern{ .int = 1 };
try expect(foo.int == 1);
@@ -172,7 +171,6 @@ test "constant tagged union with payload" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var empty = TaggedUnionWithPayload{ .Empty = {} };
var full = TaggedUnionWithPayload{ .Full = 13 };
@@ -656,7 +654,6 @@ test "union(enum(u32)) with specified and unspecified tag values" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
comptime assert(Tag(Tag(MultipleChoice2)) == u32);
try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
@@ -808,7 +805,6 @@ test "return union init with void payload" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn entry() !void {
@@ -971,7 +967,6 @@ test "function call result coerces from tagged union to the tag" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const Arch = union(enum) {
@@ -1136,7 +1131,6 @@ test "@unionInit on union with tag but no fields" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
const Type = enum(u8) { no_op = 105 };
@@ -1700,7 +1694,6 @@ test "packed union field pointer has correct alignment" {
test "union with 128 bit integer" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const ValueTag = enum { int, other };
@@ -1917,7 +1910,6 @@ test "reinterpret packed union" {
test "reinterpret packed union inside packed struct" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const U = packed union {
a: u7,
@@ -2196,7 +2188,6 @@ test "copied union field doesn't alias source" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const U = union(enum) {
array: [10]u32,
-1
View File
@@ -21,7 +21,6 @@ test "enum with members" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const a = ET{ .SINT = -42 };
const b = ET{ .UINT = 42 };
+3 -10
View File
@@ -102,7 +102,6 @@ test "vector float operators" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
@@ -119,7 +118,7 @@ test "vector float operators" {
try expectEqual(v + x, .{ 11, 22, 33, 44 });
try expectEqual(v - x, .{ 9, 18, 27, 36 });
try expectEqual(v * x, .{ 10, 40, 90, 160 });
try expectEqual(-x, .{ -1, -2, -3, -4 });
if (builtin.zig_backend != .stage2_riscv64) try expectEqual(-x, .{ -1, -2, -3, -4 });
}
};
@@ -129,6 +128,8 @@ test "vector float operators" {
try S.doTheTest(f64);
try comptime S.doTheTest(f64);
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try S.doTheTest(f16);
try comptime S.doTheTest(f16);
@@ -394,7 +395,6 @@ test "load vector elements via comptime index" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -416,7 +416,6 @@ test "store vector elements via comptime index" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -444,7 +443,6 @@ test "load vector elements via runtime index" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -467,7 +465,6 @@ test "store vector elements via runtime index" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -1239,7 +1236,6 @@ test "loading the second vector from a slice of vectors" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@setRuntimeSafety(false);
var small_bases = [2]@Vector(2, u8){
@@ -1326,7 +1322,6 @@ test "zero multiplicand" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const zeros = @Vector(2, u32){ 0.0, 0.0 };
var ones = @Vector(2, u32){ 1.0, 1.0 };
@@ -1487,7 +1482,6 @@ test "store vector with memset" {
test "addition of vectors represented as strings" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const V = @Vector(3, u8);
const foo: V = "foo".*;
@@ -1514,7 +1508,6 @@ test "vector pointer is indexable" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const V = @Vector(2, u32);
-5
View File
@@ -106,7 +106,6 @@ fn testBreakOuter() void {
test "while copies its payload" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@@ -208,7 +207,6 @@ test "while on optional with else result follow else prong" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const result = while (returnNull()) |value| {
break value;
@@ -220,7 +218,6 @@ test "while on optional with else result follow break prong" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const result = while (returnOptional(10)) |value| {
break value;
@@ -293,7 +290,6 @@ test "while optional 2 break statements and an else" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn entry(opt_t: ?bool, f: bool) !void {
@@ -392,7 +388,6 @@ test "breaking from a loop in an if statement" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {
fn retOpt() ?u32 {
+1 -1
View File
@@ -439,7 +439,7 @@ const test_targets = blk: {
.target = std.Target.Query.parse(
.{
.arch_os_abi = "riscv64-linux-musl",
.cpu_features = "baseline+v",
.cpu_features = "baseline+v+zbb",
},
) catch @panic("OOM"),
.use_llvm = false,