mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
riscv: clean up and unify encoding logic
This commit is contained in:
+2
-2
@@ -539,10 +539,10 @@ set(ZIG_STAGE2_SOURCES
|
||||
src/arch/riscv64/bits.zig
|
||||
src/arch/riscv64/CodeGen.zig
|
||||
src/arch/riscv64/Emit.zig
|
||||
src/arch/riscv64/encoder.zig
|
||||
src/arch/riscv64/Encoding.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
|
||||
|
||||
@@ -271,7 +271,6 @@ pub fn mainSimple() anyerror!void {
|
||||
};
|
||||
// is the backend capable of using std.fmt.format to print a summary at the end?
|
||||
const print_summary = switch (builtin.zig_backend) {
|
||||
.stage2_riscv64 => true,
|
||||
else => false,
|
||||
};
|
||||
|
||||
|
||||
+217
-306
@@ -20,6 +20,7 @@ const InternPool = @import("../../InternPool.zig");
|
||||
const Compilation = @import("../../Compilation.zig");
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
const codegen = @import("../../codegen.zig");
|
||||
const Mnemonic = @import("mnem.zig").Mnemonic;
|
||||
|
||||
const ErrorMsg = Zcu.ErrorMsg;
|
||||
const Target = std.Target;
|
||||
@@ -65,7 +66,6 @@ arg_index: usize,
|
||||
src_loc: Zcu.LazySrcLoc,
|
||||
|
||||
mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
|
||||
mir_extra: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
owner: Owner,
|
||||
|
||||
@@ -794,7 +794,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)});
|
||||
@@ -855,7 +854,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);
|
||||
@@ -940,10 +938,7 @@ pub fn generateLazy(
|
||||
.avl = null,
|
||||
.vtype = null,
|
||||
};
|
||||
defer {
|
||||
function.mir_instructions.deinit(gpa);
|
||||
function.mir_extra.deinit(gpa);
|
||||
}
|
||||
defer function.mir_instructions.deinit(gpa);
|
||||
|
||||
function.genLazy(lazy_sym) catch |err| switch (err) {
|
||||
error.CodegenFail => return Result{ .fail = function.err_msg.? },
|
||||
@@ -955,7 +950,6 @@ pub fn generateLazy(
|
||||
|
||||
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);
|
||||
@@ -1022,7 +1016,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,
|
||||
@@ -1120,7 +1113,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,
|
||||
@@ -1131,49 +1124,13 @@ 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.
|
||||
@@ -1182,14 +1139,11 @@ fn getCsr(func: *Func, csr: CSR) !Register {
|
||||
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;
|
||||
}
|
||||
@@ -1208,7 +1162,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,
|
||||
@@ -1221,7 +1174,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,
|
||||
@@ -1235,7 +1187,6 @@ fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void {
|
||||
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,
|
||||
@@ -1270,7 +1221,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);
|
||||
@@ -1300,11 +1251,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);
|
||||
@@ -1314,7 +1265,6 @@ fn gen(func: *Func) !void {
|
||||
// ret
|
||||
_ = try func.addInst(.{
|
||||
.tag = .jalr,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = .zero,
|
||||
@@ -1329,7 +1279,6 @@ fn gen(func: *Func) !void {
|
||||
|
||||
func.mir_instructions.set(backpatch_stack_alloc, .{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = .sp,
|
||||
.rs1 = .sp,
|
||||
@@ -1337,8 +1286,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 = .{
|
||||
@@ -1348,8 +1296,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 = .{
|
||||
@@ -1359,8 +1306,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 = .{
|
||||
@@ -1370,8 +1316,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 = .{
|
||||
@@ -1382,7 +1327,6 @@ fn gen(func: *Func) !void {
|
||||
});
|
||||
func.mir_instructions.set(backpatch_fp_add, .{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = .s0,
|
||||
.rs1 = .sp,
|
||||
@@ -1391,7 +1335,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,
|
||||
@@ -1401,27 +1344,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,
|
||||
@@ -1493,9 +1433,11 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void {
|
||||
);
|
||||
|
||||
exitlude_jump_reloc.* = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_j,
|
||||
.data = .{ .inst = undefined },
|
||||
.tag = .pseudo_j,
|
||||
.data = .{ .j_type = .{
|
||||
.rd = .zero,
|
||||
.inst = undefined,
|
||||
} },
|
||||
});
|
||||
func.performReloc(skip_reloc);
|
||||
|
||||
@@ -1508,7 +1450,7 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void {
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .jalr,
|
||||
.ops = .rri,
|
||||
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = .zero,
|
||||
@@ -2041,7 +1983,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
|
||||
.signed => {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .slli,
|
||||
.ops = .rri,
|
||||
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = reg,
|
||||
@@ -2052,7 +1994,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
|
||||
});
|
||||
_ = try func.addInst(.{
|
||||
.tag = .srai,
|
||||
.ops = .rri,
|
||||
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = reg,
|
||||
@@ -2067,7 +2009,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,
|
||||
@@ -2079,7 +2021,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
|
||||
} else {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .slli,
|
||||
.ops = .rri,
|
||||
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = reg,
|
||||
@@ -2090,7 +2032,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void {
|
||||
});
|
||||
_ = try func.addInst(.{
|
||||
.tag = .srli,
|
||||
.ops = .rri,
|
||||
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = reg,
|
||||
@@ -2411,8 +2353,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,
|
||||
@@ -2430,7 +2371,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,
|
||||
@@ -2628,7 +2568,7 @@ fn genBinOp(
|
||||
|
||||
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,
|
||||
@@ -2656,8 +2596,7 @@ fn genBinOp(
|
||||
};
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = mir_tag,
|
||||
.ops = .rrr,
|
||||
.tag = mnem,
|
||||
.data = .{
|
||||
.r_type = .{
|
||||
.rd = dst_reg,
|
||||
@@ -2668,7 +2607,7 @@ fn genBinOp(
|
||||
});
|
||||
},
|
||||
.Float => {
|
||||
const mir_tag: Mir.Inst.Tag = switch (tag) {
|
||||
const mir_tag: Mnemonic = switch (tag) {
|
||||
.add => switch (bit_size) {
|
||||
32 => .fadds,
|
||||
64 => .faddd,
|
||||
@@ -2689,7 +2628,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = mir_tag,
|
||||
.ops = .rrr,
|
||||
.data = .{
|
||||
.r_type = .{
|
||||
.rd = dst_reg,
|
||||
@@ -2705,7 +2643,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,
|
||||
@@ -2739,7 +2677,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = mir_tag,
|
||||
.ops = .rrr,
|
||||
.data = .{
|
||||
.r_type = .{
|
||||
.rd = dst_reg,
|
||||
@@ -2764,7 +2701,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .add,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = tmp_reg,
|
||||
.rs1 = rhs_reg,
|
||||
@@ -2774,7 +2710,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .sltu,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = tmp_reg,
|
||||
@@ -2785,7 +2720,6 @@ fn genBinOp(
|
||||
// neg dst_reg, dst_reg
|
||||
_ = try func.addInst(.{
|
||||
.tag = .sub,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = .zero,
|
||||
@@ -2795,7 +2729,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .@"or",
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = dst_reg,
|
||||
@@ -2850,7 +2783,6 @@ fn genBinOp(
|
||||
.bit_or, .bool_or => .@"or",
|
||||
else => unreachable,
|
||||
},
|
||||
.ops = .rrr,
|
||||
.data = .{
|
||||
.r_type = .{
|
||||
.rd = dst_reg,
|
||||
@@ -2876,7 +2808,7 @@ fn genBinOp(
|
||||
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) {
|
||||
1...31, 33...64 => .sll,
|
||||
32 => .sllw,
|
||||
@@ -2892,7 +2824,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = mir_tag,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = lhs_reg,
|
||||
@@ -2910,8 +2841,7 @@ fn genBinOp(
|
||||
.cmp_gte,
|
||||
=> {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_compare,
|
||||
.tag = .pseudo_compare,
|
||||
.data = .{
|
||||
.compare = .{
|
||||
.op = switch (tag) {
|
||||
@@ -2966,7 +2896,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = if (int_info.signedness == .unsigned) .sltu else .slt,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = mask_reg,
|
||||
.rs1 = lhs_reg,
|
||||
@@ -2976,7 +2905,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .sub,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = mask_reg,
|
||||
.rs1 = .zero,
|
||||
@@ -2986,7 +2914,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .xor,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = lhs_reg,
|
||||
@@ -2996,7 +2923,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .@"and",
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = mask_reg,
|
||||
.rs1 = dst_reg,
|
||||
@@ -3006,7 +2932,6 @@ fn genBinOp(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .xor,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = if (tag == .min) rhs_reg else lhs_reg,
|
||||
@@ -3103,7 +3028,6 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void {
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .add,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rs1 = rhs_reg,
|
||||
.rs2 = lhs_reg,
|
||||
@@ -3209,7 +3133,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,
|
||||
@@ -3231,7 +3154,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,
|
||||
@@ -3241,7 +3163,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,
|
||||
@@ -3251,7 +3172,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,
|
||||
@@ -3843,7 +3763,6 @@ fn genSliceElemPtr(func: *Func, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .add,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = addr_reg,
|
||||
.rs1 = addr_reg,
|
||||
@@ -3907,7 +3826,6 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void {
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .vslidedownvx,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = src_reg,
|
||||
.rs1 = index_reg,
|
||||
@@ -3925,7 +3843,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,
|
||||
@@ -4080,7 +3997,6 @@ fn airClz(func: *Func, inst: Air.Inst.Index) !void {
|
||||
32 => .clzw,
|
||||
else => .clz,
|
||||
},
|
||||
.ops = .rrr,
|
||||
.data = .{
|
||||
.r_type = .{
|
||||
.rs2 = .zero, // rs2 is 0 filled in the spec
|
||||
@@ -4093,7 +4009,6 @@ fn airClz(func: *Func, inst: Air.Inst.Index) !void {
|
||||
if (!(bit_size == 32 or bit_size == 64)) {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = dst_reg,
|
||||
.rs1 = dst_reg,
|
||||
@@ -4151,7 +4066,6 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
|
||||
64 => .srai,
|
||||
else => unreachable,
|
||||
},
|
||||
.ops = .rri,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = temp_reg,
|
||||
.rs1 = operand_reg,
|
||||
@@ -4161,7 +4075,6 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .xor,
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = operand_reg,
|
||||
.rs1 = operand_reg,
|
||||
@@ -4175,7 +4088,6 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void {
|
||||
64 => .sub,
|
||||
else => unreachable,
|
||||
},
|
||||
.ops = .rrr,
|
||||
.data = .{ .r_type = .{
|
||||
.rd = operand_reg,
|
||||
.rs1 = operand_reg,
|
||||
@@ -4187,14 +4099,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;
|
||||
@@ -4202,13 +4114,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,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -4231,54 +4142,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 });
|
||||
}
|
||||
@@ -4322,7 +4235,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,
|
||||
@@ -4332,6 +4244,7 @@ fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}),
|
||||
}
|
||||
},
|
||||
@@ -4538,17 +4451,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) {
|
||||
@@ -4600,7 +4510,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,
|
||||
@@ -4720,8 +4629,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();
|
||||
}
|
||||
@@ -4729,8 +4637,7 @@ fn airTrap(func: *Func) !void {
|
||||
fn airBreakpoint(func: *Func) !void {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .ebreak,
|
||||
.ops = .none,
|
||||
.data = undefined,
|
||||
.data = .none,
|
||||
});
|
||||
return func.finishAirBookkeeping();
|
||||
}
|
||||
@@ -4758,8 +4665,7 @@ fn airFence(func: *Func, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_fence,
|
||||
.tag = .pseudo_fence,
|
||||
.data = .{
|
||||
.fence = .{
|
||||
.pred = pred,
|
||||
@@ -4951,7 +4857,6 @@ fn genCall(
|
||||
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,
|
||||
@@ -4967,16 +4872,15 @@ fn genCall(
|
||||
const decl_name = owner_decl.name.toSlice(&zcu.intern_pool);
|
||||
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", .{}),
|
||||
}
|
||||
@@ -4988,7 +4892,6 @@ fn genCall(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .jalr,
|
||||
.ops = .rri,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = .ra,
|
||||
.rs1 = addr_reg,
|
||||
@@ -5065,9 +4968,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);
|
||||
@@ -5089,9 +4994,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);
|
||||
@@ -5171,8 +5078,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,
|
||||
@@ -5290,7 +5196,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,
|
||||
@@ -5332,8 +5237,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
|
||||
.register => |opt_reg| {
|
||||
if (some_info.off == 0) {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_compare,
|
||||
.tag = .pseudo_compare,
|
||||
.data = .{
|
||||
.compare = .{
|
||||
.op = .eq,
|
||||
@@ -5382,8 +5286,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,
|
||||
@@ -5432,8 +5335,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,
|
||||
@@ -5565,8 +5467,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,
|
||||
@@ -5633,11 +5534,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,
|
||||
},
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5727,8 +5628,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,
|
||||
@@ -5775,18 +5675,13 @@ 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,
|
||||
=> 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)}),
|
||||
}
|
||||
}
|
||||
@@ -5873,7 +5768,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,
|
||||
@@ -5885,7 +5779,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,
|
||||
@@ -5970,11 +5863,10 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
|
||||
|
||||
const asm_source = std.mem.sliceAsBytes(func.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
if (std.meta.stringToEnum(Mir.Inst.Tag, asm_source)) |tag| {
|
||||
if (std.meta.stringToEnum(Mnemonic, asm_source)) |tag| {
|
||||
_ = try func.addInst(.{
|
||||
.tag = tag,
|
||||
.ops = .none,
|
||||
.data = undefined,
|
||||
.data = .none,
|
||||
});
|
||||
} else {
|
||||
return func.fail("TODO: asm_source {s}", .{asm_source});
|
||||
@@ -6116,7 +6008,6 @@ fn genInlineMemcpy(
|
||||
// if count is 0, there's nothing to copy
|
||||
_ = try func.addInst(.{
|
||||
.tag = .beq,
|
||||
.ops = .rr_inst,
|
||||
.data = .{ .b_type = .{
|
||||
.rs1 = count,
|
||||
.rs2 = .zero,
|
||||
@@ -6127,7 +6018,6 @@ fn genInlineMemcpy(
|
||||
// lb tmp, 0(src)
|
||||
const first_inst = try func.addInst(.{
|
||||
.tag = .lb,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = tmp,
|
||||
@@ -6140,7 +6030,6 @@ fn genInlineMemcpy(
|
||||
// sb tmp, 0(dst)
|
||||
_ = try func.addInst(.{
|
||||
.tag = .sb,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = dst,
|
||||
@@ -6153,7 +6042,6 @@ fn genInlineMemcpy(
|
||||
// dec count by 1
|
||||
_ = try func.addInst(.{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = count,
|
||||
@@ -6166,7 +6054,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
|
||||
@@ -6179,7 +6066,6 @@ fn genInlineMemcpy(
|
||||
// increment the pointers
|
||||
_ = try func.addInst(.{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = src,
|
||||
@@ -6191,7 +6077,6 @@ fn genInlineMemcpy(
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = dst,
|
||||
@@ -6203,9 +6088,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,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6230,7 +6117,6 @@ fn genInlineMemset(
|
||||
// sb src, 0(dst)
|
||||
const first_inst = try func.addInst(.{
|
||||
.tag = .sb,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = dst,
|
||||
@@ -6243,7 +6129,6 @@ fn genInlineMemset(
|
||||
// dec count by 1
|
||||
_ = try func.addInst(.{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = count,
|
||||
@@ -6256,7 +6141,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
|
||||
@@ -6269,7 +6153,6 @@ fn genInlineMemset(
|
||||
// increment the pointers
|
||||
_ = try func.addInst(.{
|
||||
.tag = .addi,
|
||||
.ops = .rri,
|
||||
.data = .{
|
||||
.i_type = .{
|
||||
.rd = dst,
|
||||
@@ -6281,11 +6164,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,
|
||||
},
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6331,7 +6214,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,
|
||||
@@ -6345,7 +6227,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),
|
||||
@@ -6353,7 +6234,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,
|
||||
@@ -6376,7 +6256,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,
|
||||
@@ -6386,7 +6265,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,
|
||||
@@ -6423,8 +6301,7 @@ 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,
|
||||
@@ -6445,8 +6322,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 = .{
|
||||
@@ -6466,7 +6342,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,
|
||||
@@ -6476,8 +6351,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,
|
||||
@@ -6505,7 +6379,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", .{}),
|
||||
@@ -6544,8 +6418,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 = .{
|
||||
@@ -6565,7 +6438,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,
|
||||
@@ -6578,13 +6450,12 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
|
||||
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 => {
|
||||
@@ -6676,8 +6547,7 @@ fn genSetMem(
|
||||
});
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_store_rm,
|
||||
.tag = .pseudo_store_rm,
|
||||
.data = .{ .rm = .{
|
||||
.r = reg,
|
||||
.m = .{
|
||||
@@ -6716,8 +6586,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 = .{
|
||||
@@ -6732,8 +6601,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 = .{
|
||||
@@ -6852,9 +6720,59 @@ fn airFloatFromInt(func: *Func, inst: Air.Inst.Index) !void {
|
||||
|
||||
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 = func.typeOfIndex(inst);
|
||||
|
||||
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,
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -6917,8 +6835,7 @@ fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_amo,
|
||||
.tag = .pseudo_amo,
|
||||
.data = .{ .amo = .{
|
||||
.rd = result_mcv.register,
|
||||
.rs1 = ptr_register,
|
||||
@@ -6961,15 +6878,12 @@ fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void {
|
||||
|
||||
if (order == .seq_cst) {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_fence,
|
||||
.data = .{
|
||||
.fence = .{
|
||||
.pred = .rw,
|
||||
.succ = .rw,
|
||||
.fm = .none,
|
||||
},
|
||||
},
|
||||
.tag = .pseudo_fence,
|
||||
.data = .{ .fence = .{
|
||||
.pred = .rw,
|
||||
.succ = .rw,
|
||||
.fm = .none,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6982,8 +6896,7 @@ 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,
|
||||
.tag = .pseudo_fence,
|
||||
.data = .{
|
||||
.fence = .{
|
||||
.pred = .r,
|
||||
@@ -7015,8 +6928,7 @@ fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOr
|
||||
.unordered, .monotonic => {},
|
||||
.release, .seq_cst => {
|
||||
_ = try func.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = .pseudo_fence,
|
||||
.tag = .pseudo_fence,
|
||||
.data = .{
|
||||
.fence = .{
|
||||
.pred = .rw,
|
||||
@@ -7183,7 +7095,6 @@ fn airTagName(func: *Func, inst: Air.Inst.Index) !void {
|
||||
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,
|
||||
|
||||
+66
-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,45 @@ 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,
|
||||
});
|
||||
},
|
||||
.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 +97,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 +147,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 +160,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 +203,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,1136 +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,
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: this is basically a copy of the MIR table, we should be able to de-dupe them somehow.
|
||||
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,
|
||||
|
||||
vmulvv,
|
||||
vfmulvv,
|
||||
|
||||
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
|
||||
|
||||
// Zbb Extension
|
||||
clz,
|
||||
clzw,
|
||||
|
||||
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 } } },
|
||||
|
||||
.clz => .{ .opcode = .OP_IMM, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
|
||||
|
||||
// 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 } } },
|
||||
|
||||
.clzw => .{ .opcode = .OP_IMM_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0110000 } } },
|
||||
|
||||
// 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 } } },
|
||||
.vmulvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100101, .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 } } },
|
||||
.vfmulvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b100100, .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,
|
||||
.vmulvv,
|
||||
.vfaddvv,
|
||||
.vfsubvv,
|
||||
.vfmulvv,
|
||||
.vadcvv,
|
||||
.vmvvx,
|
||||
.vslidedownvx,
|
||||
|
||||
.clz,
|
||||
.clzw,
|
||||
=> .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);
|
||||
+428
-453
@@ -61,451 +61,427 @@ 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 = 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 },
|
||||
});
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
|
||||
try lower.emit(.xori, &.{
|
||||
.{ .reg = rd },
|
||||
.{ .reg = rd },
|
||||
.{ .imm = Immediate.s(1) },
|
||||
});
|
||||
},
|
||||
.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 },
|
||||
});
|
||||
},
|
||||
.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_lea_rm => {
|
||||
const rm = inst.data.rm;
|
||||
assert(rm.r.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);
|
||||
const frame: Mir.FrameLoc = if (options.allow_frame_locs)
|
||||
rm.m.toFrameLoc(lower.mir)
|
||||
else
|
||||
.{ .base = .s0, .disp = 0 };
|
||||
|
||||
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(.addi, &.{
|
||||
.{ .reg = rm.r },
|
||||
.{ .reg = frame.base },
|
||||
.{ .imm = Immediate.s(frame.disp) },
|
||||
});
|
||||
},
|
||||
|
||||
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 },
|
||||
});
|
||||
},
|
||||
.pseudo_compare => {
|
||||
const compare = inst.data.compare;
|
||||
const op = compare.op;
|
||||
|
||||
.pseudo_fence => {
|
||||
const fence = inst.data.fence;
|
||||
const rd = compare.rd;
|
||||
const rs1 = compare.rs1;
|
||||
const rs2 = compare.rs2;
|
||||
|
||||
try lower.emit(switch (fence.fm) {
|
||||
.tso => .fencetso,
|
||||
.none => .fence,
|
||||
}, &.{
|
||||
.{ .barrier = fence.succ },
|
||||
.{ .barrier = fence.pred },
|
||||
});
|
||||
},
|
||||
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)});
|
||||
};
|
||||
|
||||
else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}),
|
||||
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;
|
||||
|
||||
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,
|
||||
} },
|
||||
) },
|
||||
});
|
||||
|
||||
try lower.emit(.jalr, &.{
|
||||
.{ .reg = .ra },
|
||||
.{ .reg = .ra },
|
||||
.{ .imm = Immediate.s(0) },
|
||||
});
|
||||
},
|
||||
|
||||
.pseudo_amo => {
|
||||
const amo = inst.data.amo;
|
||||
const is_d = amo.ty.abiSize(pt) == 8;
|
||||
const is_un = amo.ty.isUnsignedInt(pt.zcu);
|
||||
|
||||
const mnem: 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(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 },
|
||||
});
|
||||
},
|
||||
|
||||
.pseudo_fence => {
|
||||
const fence = inst.data.fence;
|
||||
|
||||
try lower.emit(switch (fence.fm) {
|
||||
.tso => .fencetso,
|
||||
.none => .fence,
|
||||
}, &.{
|
||||
.{ .barrier = fence.succ },
|
||||
.{ .barrier = fence.pred },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@@ -516,49 +492,46 @@ 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)}),
|
||||
else => return lower.fail("TODO: generic lower {s}", .{@tagName(mnemonic)}),
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -580,7 +553,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,
|
||||
@@ -618,20 +591,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,170 +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,
|
||||
vmulvv,
|
||||
vfmulvv,
|
||||
vslidedownvx,
|
||||
|
||||
// Zbb Extension Instructions
|
||||
clz,
|
||||
clzw,
|
||||
|
||||
/// 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,
|
||||
@@ -194,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,
|
||||
@@ -208,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,
|
||||
@@ -228,6 +66,7 @@ pub const Inst = struct {
|
||||
ty: Type,
|
||||
},
|
||||
reloc: struct {
|
||||
register: Register,
|
||||
atom_index: u32,
|
||||
sym_index: u32,
|
||||
},
|
||||
@@ -253,115 +92,26 @@ pub const Inst = struct {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -392,25 +142,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,
|
||||
@@ -459,10 +196,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;
|
||||
@@ -470,3 +207,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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -193,7 +192,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),
|
||||
|
||||
@@ -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,716 @@
|
||||
//! 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 } } },
|
||||
|
||||
// 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 } } },
|
||||
|
||||
// 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 } } },
|
||||
|
||||
// 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 } } },
|
||||
|
||||
|
||||
// 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 } } },
|
||||
// LR.W
|
||||
// SC.W
|
||||
.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 } } },
|
||||
// LR.D
|
||||
// SC.D
|
||||
.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_mv,
|
||||
.pseudo_restore_regs,
|
||||
.pseudo_spill_regs,
|
||||
.pseudo_compare,
|
||||
.pseudo_not,
|
||||
.pseudo_extern_fn_reloc,
|
||||
.pseudo_fence,
|
||||
.pseudo_amo,
|
||||
.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,232 @@
|
||||
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,
|
||||
|
||||
// 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,
|
||||
|
||||
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,
|
||||
|
||||
// A Extension Instructions
|
||||
fence,
|
||||
fencetso,
|
||||
|
||||
amoswapw,
|
||||
amoaddw,
|
||||
amoandw,
|
||||
amoorw,
|
||||
amoxorw,
|
||||
amomaxw,
|
||||
amominw,
|
||||
amomaxuw,
|
||||
amominuw,
|
||||
|
||||
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,
|
||||
|
||||
/// Moves the value of rs1 to rd.
|
||||
pseudo_mv,
|
||||
|
||||
pseudo_restore_regs,
|
||||
pseudo_spill_regs,
|
||||
|
||||
pseudo_compare,
|
||||
pseudo_not,
|
||||
pseudo_extern_fn_reloc,
|
||||
pseudo_fence,
|
||||
pseudo_amo,
|
||||
};
|
||||
+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;
|
||||
|
||||
@@ -510,7 +510,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
|
||||
|
||||
@@ -100,6 +100,7 @@ test "@byteSwap vectors 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_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
try comptime vector8();
|
||||
try vector8();
|
||||
|
||||
@@ -116,6 +116,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 +139,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 {
|
||||
|
||||
@@ -591,6 +591,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 {
|
||||
|
||||
@@ -228,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 {
|
||||
|
||||
@@ -427,6 +427,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 +463,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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user