mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
riscv: clean up and unify encoding logic
This commit is contained in:
parent
574028ed5e
commit
1a7d89a84d
@ -539,10 +539,10 @@ set(ZIG_STAGE2_SOURCES
|
|||||||
src/arch/riscv64/bits.zig
|
src/arch/riscv64/bits.zig
|
||||||
src/arch/riscv64/CodeGen.zig
|
src/arch/riscv64/CodeGen.zig
|
||||||
src/arch/riscv64/Emit.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/Lower.zig
|
||||||
src/arch/riscv64/Mir.zig
|
src/arch/riscv64/Mir.zig
|
||||||
|
src/arch/riscv64/mnem.zig
|
||||||
src/arch/sparc64/CodeGen.zig
|
src/arch/sparc64/CodeGen.zig
|
||||||
src/arch/sparc64/Emit.zig
|
src/arch/sparc64/Emit.zig
|
||||||
src/arch/sparc64/Mir.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?
|
// is the backend capable of using std.fmt.format to print a summary at the end?
|
||||||
const print_summary = switch (builtin.zig_backend) {
|
const print_summary = switch (builtin.zig_backend) {
|
||||||
.stage2_riscv64 => true,
|
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
|||||||
.source = start_offset,
|
.source = start_offset,
|
||||||
.target = target,
|
.target = target,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.enc = std.meta.activeTag(lowered_inst.encoding.data),
|
.fmt = std.meta.activeTag(lowered_inst),
|
||||||
}),
|
}),
|
||||||
.load_symbol_reloc => |symbol| {
|
.load_symbol_reloc => |symbol| {
|
||||||
const is_obj_or_static_lib = switch (emit.lower.output_mode) {
|
const is_obj_or_static_lib = switch (emit.lower.output_mode) {
|
||||||
@ -49,7 +49,8 @@ pub fn emitMir(emit: *Emit) Error!void {
|
|||||||
.Lib => emit.lower.link_mode == .static,
|
.Lib => emit.lower.link_mode == .static,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (emit.bin_file.cast(link.File.Elf)) |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 atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
|
||||||
const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index);
|
const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index);
|
||||||
const sym = elf_file.symbol(sym_index);
|
const sym = elf_file.symbol(sym_index);
|
||||||
@ -75,10 +76,9 @@ pub fn emitMir(emit: *Emit) Error!void {
|
|||||||
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
|
||||||
.r_addend = 0,
|
.r_addend = 0,
|
||||||
});
|
});
|
||||||
} else unreachable;
|
|
||||||
},
|
},
|
||||||
.call_extern_fn_reloc => |symbol| {
|
.call_extern_fn_reloc => |symbol| {
|
||||||
if (emit.bin_file.cast(link.File.Elf)) |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 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);
|
||||||
@ -88,7 +88,6 @@ pub fn emitMir(emit: *Emit) Error!void {
|
|||||||
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
|
||||||
.r_addend = 0,
|
.r_addend = 0,
|
||||||
});
|
});
|
||||||
} else return emit.fail("TODO: call_extern_fn_reloc non-ELF", .{});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -97,8 +96,6 @@ pub fn emitMir(emit: *Emit) Error!void {
|
|||||||
if (lowered.insts.len == 0) {
|
if (lowered.insts.len == 0) {
|
||||||
const mir_inst = emit.lower.mir.instructions.get(mir_index);
|
const mir_inst = emit.lower.mir.instructions.get(mir_index);
|
||||||
switch (mir_inst.tag) {
|
switch (mir_inst.tag) {
|
||||||
else => unreachable,
|
|
||||||
.pseudo => switch (mir_inst.ops) {
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
.pseudo_dbg_prologue_end => {
|
.pseudo_dbg_prologue_end => {
|
||||||
switch (emit.debug_output) {
|
switch (emit.debug_output) {
|
||||||
@ -131,7 +128,6 @@ pub fn emitMir(emit: *Emit) Error!void {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.pseudo_dead => {},
|
.pseudo_dead => {},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,8 +147,8 @@ const Reloc = struct {
|
|||||||
target: Mir.Inst.Index,
|
target: Mir.Inst.Index,
|
||||||
/// Offset of the relocation within the instruction.
|
/// Offset of the relocation within the instruction.
|
||||||
offset: u32,
|
offset: u32,
|
||||||
/// Encoding of the instruction, used to determine how to modify it.
|
/// Format of the instruction, used to determine how to modify it.
|
||||||
enc: Encoding.InstEnc,
|
fmt: encoding.Lir.Format,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn fixupRelocs(emit: *Emit) Error!void {
|
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 disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
|
||||||
const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4];
|
const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4];
|
||||||
|
|
||||||
log.debug("disp: {x}", .{disp});
|
switch (reloc.fmt) {
|
||||||
|
|
||||||
switch (reloc.enc) {
|
|
||||||
.J => riscv_util.writeInstJ(code, @bitCast(disp)),
|
.J => riscv_util.writeInstJ(code, @bitCast(disp)),
|
||||||
.B => riscv_util.writeInstB(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 Lower = @import("Lower.zig");
|
||||||
const Mir = @import("Mir.zig");
|
const Mir = @import("Mir.zig");
|
||||||
const riscv_util = @import("../../link/riscv.zig");
|
const riscv_util = @import("../../link/riscv.zig");
|
||||||
const Encoding = @import("Encoding.zig");
|
|
||||||
const Elf = @import("../../link/Elf.zig");
|
const Elf = @import("../../link/Elf.zig");
|
||||||
|
const encoding = @import("encoding.zig");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -61,7 +61,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
log.debug("lowerMir {}", .{inst});
|
log.debug("lowerMir {}", .{inst});
|
||||||
switch (inst.tag) {
|
switch (inst.tag) {
|
||||||
else => try lower.generic(inst),
|
else => try lower.generic(inst),
|
||||||
.pseudo => switch (inst.ops) {
|
|
||||||
.pseudo_dbg_line_column,
|
.pseudo_dbg_line_column,
|
||||||
.pseudo_dbg_epilogue_begin,
|
.pseudo_dbg_epilogue_begin,
|
||||||
.pseudo_dbg_prologue_end,
|
.pseudo_dbg_prologue_end,
|
||||||
@ -76,7 +75,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
else
|
else
|
||||||
.{ .base = .s0, .disp = 0 };
|
.{ .base = .s0, .disp = 0 };
|
||||||
|
|
||||||
switch (inst.ops) {
|
switch (inst.tag) {
|
||||||
.pseudo_load_rm => {
|
.pseudo_load_rm => {
|
||||||
const dest_reg = rm.r;
|
const dest_reg = rm.r;
|
||||||
const dest_reg_class = dest_reg.class();
|
const dest_reg_class = dest_reg.class();
|
||||||
@ -84,7 +83,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
const src_size = rm.m.mod.size;
|
const src_size = rm.m.mod.size;
|
||||||
const unsigned = rm.m.mod.unsigned;
|
const unsigned = rm.m.mod.unsigned;
|
||||||
|
|
||||||
const tag: Encoding.Mnemonic = switch (dest_reg_class) {
|
const mnem: Mnemonic = switch (dest_reg_class) {
|
||||||
.int => switch (src_size) {
|
.int => switch (src_size) {
|
||||||
.byte => if (unsigned) .lbu else .lb,
|
.byte => if (unsigned) .lbu else .lb,
|
||||||
.hword => if (unsigned) .lhu else .lh,
|
.hword => if (unsigned) .lhu else .lh,
|
||||||
@ -107,7 +106,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
|
|
||||||
switch (dest_reg_class) {
|
switch (dest_reg_class) {
|
||||||
.int, .float => {
|
.int, .float => {
|
||||||
try lower.emit(tag, &.{
|
try lower.emit(mnem, &.{
|
||||||
.{ .reg = rm.r },
|
.{ .reg = rm.r },
|
||||||
.{ .reg = frame_loc.base },
|
.{ .reg = frame_loc.base },
|
||||||
.{ .imm = Immediate.s(frame_loc.disp) },
|
.{ .imm = Immediate.s(frame_loc.disp) },
|
||||||
@ -115,7 +114,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
},
|
},
|
||||||
.vector => {
|
.vector => {
|
||||||
assert(frame_loc.disp == 0);
|
assert(frame_loc.disp == 0);
|
||||||
try lower.emit(tag, &.{
|
try lower.emit(mnem, &.{
|
||||||
.{ .reg = rm.r },
|
.{ .reg = rm.r },
|
||||||
.{ .reg = frame_loc.base },
|
.{ .reg = frame_loc.base },
|
||||||
.{ .reg = .zero },
|
.{ .reg = .zero },
|
||||||
@ -129,7 +128,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
|
|
||||||
const dest_size = rm.m.mod.size;
|
const dest_size = rm.m.mod.size;
|
||||||
|
|
||||||
const tag: Encoding.Mnemonic = switch (src_reg_class) {
|
const mnem: Mnemonic = switch (src_reg_class) {
|
||||||
.int => switch (dest_size) {
|
.int => switch (dest_size) {
|
||||||
.byte => .sb,
|
.byte => .sb,
|
||||||
.hword => .sh,
|
.hword => .sh,
|
||||||
@ -152,7 +151,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
|
|
||||||
switch (src_reg_class) {
|
switch (src_reg_class) {
|
||||||
.int, .float => {
|
.int, .float => {
|
||||||
try lower.emit(tag, &.{
|
try lower.emit(mnem, &.{
|
||||||
.{ .reg = frame_loc.base },
|
.{ .reg = frame_loc.base },
|
||||||
.{ .reg = rm.r },
|
.{ .reg = rm.r },
|
||||||
.{ .imm = Immediate.s(frame_loc.disp) },
|
.{ .imm = Immediate.s(frame_loc.disp) },
|
||||||
@ -160,7 +159,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
},
|
},
|
||||||
.vector => {
|
.vector => {
|
||||||
assert(frame_loc.disp == 0);
|
assert(frame_loc.disp == 0);
|
||||||
try lower.emit(tag, &.{
|
try lower.emit(mnem, &.{
|
||||||
.{ .reg = rm.r },
|
.{ .reg = rm.r },
|
||||||
.{ .reg = frame_loc.base },
|
.{ .reg = frame_loc.base },
|
||||||
.{ .reg = .zero },
|
.{ .reg = .zero },
|
||||||
@ -220,9 +219,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.pseudo_j => {
|
.pseudo_j => {
|
||||||
|
const j_type = inst.data.j_type;
|
||||||
try lower.emit(.jal, &.{
|
try lower.emit(.jal, &.{
|
||||||
.{ .reg = .zero },
|
.{ .reg = j_type.rd },
|
||||||
.{ .imm = lower.reloc(.{ .inst = inst.data.inst }) },
|
.{ .imm = lower.reloc(.{ .inst = j_type.inst }) },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -230,22 +230,21 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
.pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list),
|
.pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list),
|
||||||
|
|
||||||
.pseudo_load_symbol => {
|
.pseudo_load_symbol => {
|
||||||
const payload = inst.data.payload;
|
const payload = inst.data.reloc;
|
||||||
const data = lower.mir.extraData(Mir.LoadSymbolPayload, payload).data;
|
const dst_reg = payload.register;
|
||||||
const dst_reg: bits.Register = @enumFromInt(data.register);
|
|
||||||
assert(dst_reg.class() == .int);
|
assert(dst_reg.class() == .int);
|
||||||
|
|
||||||
try lower.emit(.lui, &.{
|
try lower.emit(.lui, &.{
|
||||||
.{ .reg = dst_reg },
|
.{ .reg = dst_reg },
|
||||||
.{ .imm = lower.reloc(.{
|
.{ .imm = lower.reloc(.{
|
||||||
.load_symbol_reloc = .{
|
.load_symbol_reloc = .{
|
||||||
.atom_index = data.atom_index,
|
.atom_index = payload.atom_index,
|
||||||
.sym_index = data.sym_index,
|
.sym_index = payload.sym_index,
|
||||||
},
|
},
|
||||||
}) },
|
}) },
|
||||||
});
|
});
|
||||||
|
|
||||||
// the above reloc implies this one
|
// the reloc above implies this one
|
||||||
try lower.emit(.addi, &.{
|
try lower.emit(.addi, &.{
|
||||||
.{ .reg = dst_reg },
|
.{ .reg = dst_reg },
|
||||||
.{ .reg = dst_reg },
|
.{ .reg = dst_reg },
|
||||||
@ -269,26 +268,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.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 => {
|
.pseudo_compare => {
|
||||||
const compare = inst.data.compare;
|
const compare = inst.data.compare;
|
||||||
const op = compare.op;
|
const op = compare.op;
|
||||||
@ -304,7 +283,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const is_unsigned = ty.isUnsignedInt(pt.zcu);
|
const is_unsigned = ty.isUnsignedInt(pt.zcu);
|
||||||
const less_than: Encoding.Mnemonic = if (is_unsigned) .sltu else .slt;
|
const less_than: Mnemonic = if (is_unsigned) .sltu else .slt;
|
||||||
|
|
||||||
switch (class) {
|
switch (class) {
|
||||||
.int => switch (op) {
|
.int => switch (op) {
|
||||||
@ -472,7 +451,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
const is_d = amo.ty.abiSize(pt) == 8;
|
const is_d = amo.ty.abiSize(pt) == 8;
|
||||||
const is_un = amo.ty.isUnsignedInt(pt.zcu);
|
const is_un = amo.ty.isUnsignedInt(pt.zcu);
|
||||||
|
|
||||||
const mnem: Encoding.Mnemonic = switch (amo.op) {
|
const mnem: Mnemonic = switch (amo.op) {
|
||||||
// zig fmt: off
|
// zig fmt: off
|
||||||
.SWAP => if (is_d) .amoswapd else .amoswapw,
|
.SWAP => if (is_d) .amoswapd else .amoswapw,
|
||||||
.ADD => if (is_d) .amoaddd else .amoaddw,
|
.ADD => if (is_d) .amoaddd else .amoaddw,
|
||||||
@ -504,9 +483,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
.{ .barrier = fence.pred },
|
.{ .barrier = fence.pred },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@ -516,49 +492,46 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
|
fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||||
const mnemonic = std.meta.stringToEnum(Encoding.Mnemonic, @tagName(inst.tag)) orelse {
|
const mnemonic = inst.tag;
|
||||||
return lower.fail("generic inst name '{s}' with op {s} doesn't match with a mnemonic", .{
|
try lower.emit(mnemonic, switch (inst.data) {
|
||||||
@tagName(inst.tag),
|
|
||||||
@tagName(inst.ops),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
try lower.emit(mnemonic, switch (inst.ops) {
|
|
||||||
.none => &.{},
|
.none => &.{},
|
||||||
.ri => &.{
|
.u_type => |u| &.{
|
||||||
.{ .reg = inst.data.u_type.rd },
|
.{ .reg = u.rd },
|
||||||
.{ .imm = inst.data.u_type.imm20 },
|
.{ .imm = u.imm20 },
|
||||||
},
|
},
|
||||||
.rr => &.{
|
.i_type => |i| &.{
|
||||||
.{ .reg = inst.data.rr.rd },
|
.{ .reg = i.rd },
|
||||||
.{ .reg = inst.data.rr.rs },
|
.{ .reg = i.rs1 },
|
||||||
|
.{ .imm = i.imm12 },
|
||||||
},
|
},
|
||||||
.rri => &.{
|
.rr => |rr| &.{
|
||||||
.{ .reg = inst.data.i_type.rd },
|
.{ .reg = rr.rd },
|
||||||
.{ .reg = inst.data.i_type.rs1 },
|
.{ .reg = rr.rs },
|
||||||
.{ .imm = inst.data.i_type.imm12 },
|
|
||||||
},
|
},
|
||||||
.rr_inst => &.{
|
.b_type => |b| &.{
|
||||||
.{ .reg = inst.data.b_type.rs1 },
|
.{ .reg = b.rs1 },
|
||||||
.{ .reg = inst.data.b_type.rs2 },
|
.{ .reg = b.rs2 },
|
||||||
.{ .imm = lower.reloc(.{ .inst = inst.data.b_type.inst }) },
|
.{ .imm = lower.reloc(.{ .inst = b.inst }) },
|
||||||
},
|
},
|
||||||
.rrr => &.{
|
.r_type => |r| &.{
|
||||||
.{ .reg = inst.data.r_type.rd },
|
.{ .reg = r.rd },
|
||||||
.{ .reg = inst.data.r_type.rs1 },
|
.{ .reg = r.rs1 },
|
||||||
.{ .reg = inst.data.r_type.rs2 },
|
.{ .reg = r.rs2 },
|
||||||
},
|
},
|
||||||
.csr => &.{
|
.csr => |csr| &.{
|
||||||
.{ .csr = inst.data.csr.csr },
|
.{ .csr = csr.csr },
|
||||||
.{ .reg = inst.data.csr.rs1 },
|
.{ .reg = csr.rs1 },
|
||||||
.{ .reg = inst.data.csr.rd },
|
.{ .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 {
|
fn emit(lower: *Lower, mnemonic: Mnemonic, ops: []const Instruction.Operand) !void {
|
||||||
lower.result_insts[lower.result_insts_len] =
|
const lir = encoding.Lir.fromMnem(mnemonic);
|
||||||
try Instruction.new(mnemonic, ops);
|
const inst = Instruction.fromLir(lir, ops);
|
||||||
|
|
||||||
|
lower.result_insts[lower.result_insts_len] = inst;
|
||||||
lower.result_insts_len += 1;
|
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 = abi.Registers.all_preserved[i];
|
||||||
|
|
||||||
const reg_class = reg.class();
|
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 },
|
.int => .{ .ld, .sd },
|
||||||
.float => .{ .fld, .fsd },
|
.float => .{ .fld, .fsd },
|
||||||
.vector => unreachable,
|
.vector => unreachable,
|
||||||
@ -618,20 +591,22 @@ fn hasFeature(lower: *Lower, feature: std.Target.riscv.Feature) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Lower = @This();
|
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 std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
const log = std.log.scoped(.lower);
|
const log = std.log.scoped(.lower);
|
||||||
|
|
||||||
const Air = @import("../../Air.zig");
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ErrorMsg = Zcu.ErrorMsg;
|
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 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 Immediate = bits.Immediate;
|
||||||
|
const Instruction = encoding.Instruction;
|
||||||
|
|||||||
@ -1,170 +1,17 @@
|
|||||||
//! Machine Intermediate Representation.
|
//! Machine Intermediate Representation.
|
||||||
//! This data is produced by RISCV64 Codegen or RISCV64 assembly parsing
|
//! This data is produced by CodeGen.zig
|
||||||
//! 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.
|
|
||||||
|
|
||||||
instructions: std.MultiArrayList(Inst).Slice,
|
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,
|
frame_locs: std.MultiArrayList(FrameLoc).Slice,
|
||||||
|
|
||||||
pub const Inst = struct {
|
pub const Inst = struct {
|
||||||
tag: Tag,
|
tag: Mnemonic,
|
||||||
data: Data,
|
data: Data,
|
||||||
ops: Ops,
|
|
||||||
|
|
||||||
/// The position of an MIR instruction within the `Mir` instructions array.
|
|
||||||
pub const Index = u32;
|
pub const Index = u32;
|
||||||
|
|
||||||
pub const Tag = enum(u16) {
|
pub const Data = union(enum) {
|
||||||
|
none: void,
|
||||||
// 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,
|
|
||||||
r_type: struct {
|
r_type: struct {
|
||||||
rd: Register,
|
rd: Register,
|
||||||
rs1: Register,
|
rs1: Register,
|
||||||
@ -194,10 +41,6 @@ pub const Inst = struct {
|
|||||||
rd: Register,
|
rd: Register,
|
||||||
inst: Inst.Index,
|
inst: Inst.Index,
|
||||||
},
|
},
|
||||||
pseudo_dbg_line_column: struct {
|
|
||||||
line: u32,
|
|
||||||
column: u32,
|
|
||||||
},
|
|
||||||
rm: struct {
|
rm: struct {
|
||||||
r: Register,
|
r: Register,
|
||||||
m: Memory,
|
m: Memory,
|
||||||
@ -208,11 +51,6 @@ pub const Inst = struct {
|
|||||||
rd: Register,
|
rd: Register,
|
||||||
rs: Register,
|
rs: Register,
|
||||||
},
|
},
|
||||||
fabs: struct {
|
|
||||||
rd: Register,
|
|
||||||
rs: Register,
|
|
||||||
bits: u16,
|
|
||||||
},
|
|
||||||
compare: struct {
|
compare: struct {
|
||||||
rd: Register,
|
rd: Register,
|
||||||
rs1: Register,
|
rs1: Register,
|
||||||
@ -228,6 +66,7 @@ pub const Inst = struct {
|
|||||||
ty: Type,
|
ty: Type,
|
||||||
},
|
},
|
||||||
reloc: struct {
|
reloc: struct {
|
||||||
|
register: Register,
|
||||||
atom_index: u32,
|
atom_index: u32,
|
||||||
sym_index: u32,
|
sym_index: u32,
|
||||||
},
|
},
|
||||||
@ -253,115 +92,26 @@ pub const Inst = struct {
|
|||||||
rs1: Register,
|
rs1: Register,
|
||||||
rd: Register,
|
rd: Register,
|
||||||
},
|
},
|
||||||
};
|
pseudo_dbg_line_column: struct {
|
||||||
|
line: u32,
|
||||||
pub const Ops = enum {
|
column: u32,
|
||||||
/// 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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn format(
|
pub fn format(
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
options: std.fmt.FormatOptions,
|
_: std.fmt.FormatOptions,
|
||||||
writer: anytype,
|
writer: anytype,
|
||||||
) !void {
|
) !void {
|
||||||
assert(fmt.len == 0);
|
assert(fmt.len == 0);
|
||||||
_ = options;
|
try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) });
|
||||||
|
|
||||||
try writer.print("Tag: {s}, Ops: {s}", .{ @tagName(inst.tag), @tagName(inst.ops) });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
|
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
|
||||||
mir.instructions.deinit(gpa);
|
mir.instructions.deinit(gpa);
|
||||||
mir.frame_locs.deinit(gpa);
|
mir.frame_locs.deinit(gpa);
|
||||||
gpa.free(mir.extra);
|
|
||||||
mir.* = undefined;
|
mir.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,25 +142,12 @@ pub const AmoOp = enum(u5) {
|
|||||||
MIN,
|
MIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns the requested data, as well as the new index which is at the start of the
|
pub const FcvtOp = enum(u5) {
|
||||||
/// trailers for the object.
|
w = 0b00000,
|
||||||
pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
|
wu = 0b00001,
|
||||||
const fields = std.meta.fields(T);
|
l = 0b00010,
|
||||||
var i: usize = index;
|
lu = 0b00011,
|
||||||
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 LoadSymbolPayload = struct {
|
pub const LoadSymbolPayload = struct {
|
||||||
register: u32,
|
register: u32,
|
||||||
@ -459,10 +196,10 @@ const Mir = @This();
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const Type = @import("../../Type.zig");
|
const Type = @import("../../Type.zig");
|
||||||
|
const bits = @import("bits.zig");
|
||||||
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
const bits = @import("bits.zig");
|
|
||||||
const Register = bits.Register;
|
const Register = bits.Register;
|
||||||
const CSR = bits.CSR;
|
const CSR = bits.CSR;
|
||||||
const Immediate = bits.Immediate;
|
const Immediate = bits.Immediate;
|
||||||
@ -470,3 +207,4 @@ const Memory = bits.Memory;
|
|||||||
const FrameIndex = bits.FrameIndex;
|
const FrameIndex = bits.FrameIndex;
|
||||||
const FrameAddr = @import("CodeGen.zig").FrameAddr;
|
const FrameAddr = @import("CodeGen.zig").FrameAddr;
|
||||||
const IntegerBitSet = std.bit_set.IntegerBitSet;
|
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 Target = std.Target;
|
||||||
|
|
||||||
const Zcu = @import("../../Zcu.zig");
|
const Zcu = @import("../../Zcu.zig");
|
||||||
const Encoding = @import("Encoding.zig");
|
|
||||||
const Mir = @import("Mir.zig");
|
const Mir = @import("Mir.zig");
|
||||||
const abi = @import("abi.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
|
/// 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
|
/// 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.
|
/// 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)) {
|
const base = switch (@intFromEnum(reg)) {
|
||||||
// zig fmt: off
|
// zig fmt: off
|
||||||
@intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero),
|
@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);
|
|
||||||
716
src/arch/riscv64/encoding.zig
Normal file
716
src/arch/riscv64/encoding.zig
Normal file
@ -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;
|
||||||
232
src/arch/riscv64/mnem.zig
Normal file
232
src/arch/riscv64/mnem.zig
Normal file
@ -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,
|
||||||
|
};
|
||||||
@ -25,47 +25,27 @@ pub fn writeAddend(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeInstU(code: *[4]u8, value: u32) void {
|
pub fn writeInstU(code: *[4]u8, value: u32) void {
|
||||||
var data = Encoding.Data{
|
var data: Instruction = .{ .U = mem.bytesToValue(std.meta.TagPayload(Instruction, .U), code) };
|
||||||
.U = mem.bytesToValue(std.meta.TagPayload(
|
|
||||||
Encoding.Data,
|
|
||||||
Encoding.Data.U,
|
|
||||||
), code),
|
|
||||||
};
|
|
||||||
const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800);
|
const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800);
|
||||||
data.U.imm12_31 = bitSlice(compensated, 31, 12);
|
data.U.imm12_31 = bitSlice(compensated, 31, 12);
|
||||||
mem.writeInt(u32, code, data.toU32(), .little);
|
mem.writeInt(u32, code, data.toU32(), .little);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeInstI(code: *[4]u8, value: u32) void {
|
pub fn writeInstI(code: *[4]u8, value: u32) void {
|
||||||
var data = Encoding.Data{
|
var data: Instruction = .{ .I = mem.bytesToValue(std.meta.TagPayload(Instruction, .I), code) };
|
||||||
.I = mem.bytesToValue(std.meta.TagPayload(
|
|
||||||
Encoding.Data,
|
|
||||||
Encoding.Data.I,
|
|
||||||
), code),
|
|
||||||
};
|
|
||||||
data.I.imm0_11 = bitSlice(value, 11, 0);
|
data.I.imm0_11 = bitSlice(value, 11, 0);
|
||||||
mem.writeInt(u32, code, data.toU32(), .little);
|
mem.writeInt(u32, code, data.toU32(), .little);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeInstS(code: *[4]u8, value: u32) void {
|
pub fn writeInstS(code: *[4]u8, value: u32) void {
|
||||||
var data = Encoding.Data{
|
var data: Instruction = .{ .S = mem.bytesToValue(std.meta.TagPayload(Instruction, .S), code) };
|
||||||
.S = mem.bytesToValue(std.meta.TagPayload(
|
|
||||||
Encoding.Data,
|
|
||||||
Encoding.Data.S,
|
|
||||||
), code),
|
|
||||||
};
|
|
||||||
data.S.imm0_4 = bitSlice(value, 4, 0);
|
data.S.imm0_4 = bitSlice(value, 4, 0);
|
||||||
data.S.imm5_11 = bitSlice(value, 11, 5);
|
data.S.imm5_11 = bitSlice(value, 11, 5);
|
||||||
mem.writeInt(u32, code, data.toU32(), .little);
|
mem.writeInt(u32, code, data.toU32(), .little);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeInstJ(code: *[4]u8, value: u32) void {
|
pub fn writeInstJ(code: *[4]u8, value: u32) void {
|
||||||
var data = Encoding.Data{
|
var data: Instruction = .{ .J = mem.bytesToValue(std.meta.TagPayload(Instruction, .J), code) };
|
||||||
.J = mem.bytesToValue(std.meta.TagPayload(
|
|
||||||
Encoding.Data,
|
|
||||||
Encoding.Data.J,
|
|
||||||
), code),
|
|
||||||
};
|
|
||||||
data.J.imm1_10 = bitSlice(value, 10, 1);
|
data.J.imm1_10 = bitSlice(value, 10, 1);
|
||||||
data.J.imm11 = bitSlice(value, 11, 11);
|
data.J.imm11 = bitSlice(value, 11, 11);
|
||||||
data.J.imm12_19 = bitSlice(value, 19, 12);
|
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 {
|
pub fn writeInstB(code: *[4]u8, value: u32) void {
|
||||||
var data = Encoding.Data{
|
var data: Instruction = .{ .B = mem.bytesToValue(std.meta.TagPayload(Instruction, .B), code) };
|
||||||
.B = mem.bytesToValue(std.meta.TagPayload(
|
|
||||||
Encoding.Data,
|
|
||||||
Encoding.Data.B,
|
|
||||||
), code),
|
|
||||||
};
|
|
||||||
data.B.imm1_4 = bitSlice(value, 4, 1);
|
data.B.imm1_4 = bitSlice(value, 4, 1);
|
||||||
data.B.imm5_10 = bitSlice(value, 10, 5);
|
data.B.imm5_10 = bitSlice(value, 10, 5);
|
||||||
data.B.imm11 = bitSlice(value, 11, 11);
|
data.B.imm11 = bitSlice(value, 11, 11);
|
||||||
@ -109,9 +84,8 @@ pub const RiscvEflags = packed struct(u32) {
|
|||||||
_unused: u8,
|
_unused: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const encoder = @import("../arch/riscv64/encoder.zig");
|
|
||||||
const Encoding = @import("../arch/riscv64/Encoding.zig");
|
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const std = @import("std");
|
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" {
|
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_arm) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_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_aarch64) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
try comptime vector8();
|
try comptime vector8();
|
||||||
try 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_arm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn foo() !i32 {
|
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_wasm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn foo() !i32 {
|
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_aarch64) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_arm) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn inner() !?[]u8 {
|
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_aarch64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn doTheTest() !void {
|
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_aarch64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn doTheTest() !void {
|
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_aarch64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn doTheTest() !void {
|
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_aarch64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_arm) 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_spirv64) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn getError() !void {
|
fn getError() !void {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user