mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
Merge pull request #20807 from Rexicon226/riscv
riscv: more backend progress
This commit is contained in:
+6
-3
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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.*);
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)}),
|
||||
|
||||
@@ -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
|
||||
_,
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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-";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" };
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user