diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e27935d3d..83fd4ebece 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index cf8d8df3f0..3e97062982 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.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, }; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index d52f07438f..6e807f1f3b 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -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, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index bc972e86b9..3fd5c405f8 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -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"); diff --git a/src/arch/riscv64/Encoding.zig b/src/arch/riscv64/Encoding.zig deleted file mode 100644 index b1b0712779..0000000000 --- a/src/arch/riscv64/Encoding.zig +++ /dev/null @@ -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); diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index f33fe68fae..c8a4ec942c 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -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; diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index fe4e24e133..bb4fd28536 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -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; diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 60efa077c5..5928449828 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -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), diff --git a/src/arch/riscv64/encoder.zig b/src/arch/riscv64/encoder.zig deleted file mode 100644 index 2ef5ba03ec..0000000000 --- a/src/arch/riscv64/encoder.zig +++ /dev/null @@ -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); diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig new file mode 100644 index 0000000000..a3e20698b6 --- /dev/null +++ b/src/arch/riscv64/encoding.zig @@ -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; diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig new file mode 100644 index 0000000000..afd2f5c78c --- /dev/null +++ b/src/arch/riscv64/mnem.zig @@ -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, +}; diff --git a/src/link/riscv.zig b/src/link/riscv.zig index 5107992b48..bf23010c80 100644 --- a/src/link/riscv.zig +++ b/src/link/riscv.zig @@ -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; diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 33e43740f6..8c51be2685 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -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 diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index fd7e2af850..0c6e655b25 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -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(); diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index fc764f55e3..4ea6f54787 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -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 { diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index ae9cc5ee4d..a11a20f012 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -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 { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 1cc9092fd7..5308ebf358 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -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 { diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 4c0b4a88f4..ac62461b22 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -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 { diff --git a/test/behavior/try.zig b/test/behavior/try.zig index cc76658e93..53fdc48934 100644 --- a/test/behavior/try.zig +++ b/test/behavior/try.zig @@ -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 {