| author | |
| committer | |
| log | 9d9b5a11e873cc15e3f1b6e506ecf22c8380c87d |
| tree | f9fbf35c57d927db32236129c8dbd24550edee7b |
| parent | ddc399440dd8bcb814c288e43a30f873bfbaca39 |
| parent | 5a4fe39fbbf9668b7fa14da774cb3de1c49604e7 |
| signature |
more RISC-V backend progress34 files changed, 1627 insertions(+), 496 deletions(-)
lib/std/simd.zig+32-4| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | //! SIMD (Single Instruction; Multiple Data) convenience functions. |
| 2 | 2 | //! |
| 3 | 3 | //! May offer a potential boost in performance on some targets by performing |
| 4 | //! the same operations on multiple elements at once. | |
| 4 | //! the same operation on multiple elements at once. | |
| 5 | 5 | //! |
| 6 | 6 | //! Some functions are known to not work on MIPS. |
| 7 | 7 | |
| ... | ... | @@ -10,7 +10,6 @@ const builtin = @import("builtin"); |
| 10 | 10 | |
| 11 | 11 | pub fn suggestVectorLengthForCpu(comptime T: type, comptime cpu: std.Target.Cpu) ?comptime_int { |
| 12 | 12 | // This is guesswork, if you have better suggestions can add it or edit the current here |
| 13 | // This can run in comptime only, but stage 1 fails at it, stage 2 can understand it | |
| 14 | 13 | const element_bit_size = @max(8, std.math.ceilPowerOfTwo(u16, @bitSizeOf(T)) catch unreachable); |
| 15 | 14 | const vector_bit_size: u16 = blk: { |
| 16 | 15 | if (cpu.arch.isX86()) { |
| ... | ... | @@ -37,8 +36,37 @@ pub fn suggestVectorLengthForCpu(comptime T: type, comptime cpu: std.Target.Cpu) |
| 37 | 36 | // the 2048 bits or using just 64 per vector or something in between |
| 38 | 37 | if (std.Target.mips.featureSetHas(cpu.features, std.Target.mips.Feature.mips3d)) break :blk 64; |
| 39 | 38 | } else if (cpu.arch.isRISCV()) { |
| 40 | // in risc-v the Vector Extension allows configurable vector sizes, but a standard size of 128 is a safe estimate | |
| 41 | if (std.Target.riscv.featureSetHas(cpu.features, .v)) break :blk 128; | |
| 39 | // In RISC-V Vector Registers are length agnostic so there's no good way to determine the best size. | |
| 40 | // The usual vector length in most RISC-V cpus is 256 bits, however it can get to multiple kB. | |
| 41 | if (std.Target.riscv.featureSetHas(cpu.features, .v)) { | |
| 42 | var vec_bit_length: u32 = 256; | |
| 43 | if (std.Target.riscv.featureSetHas(cpu.features, .zvl32b)) { | |
| 44 | vec_bit_length = 32; | |
| 45 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl64b)) { | |
| 46 | vec_bit_length = 64; | |
| 47 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl128b)) { | |
| 48 | vec_bit_length = 128; | |
| 49 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl256b)) { | |
| 50 | vec_bit_length = 256; | |
| 51 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl512b)) { | |
| 52 | vec_bit_length = 512; | |
| 53 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl1024b)) { | |
| 54 | vec_bit_length = 1024; | |
| 55 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl2048b)) { | |
| 56 | vec_bit_length = 2048; | |
| 57 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl4096b)) { | |
| 58 | vec_bit_length = 4096; | |
| 59 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl8192b)) { | |
| 60 | vec_bit_length = 8192; | |
| 61 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl16384b)) { | |
| 62 | vec_bit_length = 16384; | |
| 63 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl32768b)) { | |
| 64 | vec_bit_length = 32768; | |
| 65 | } else if (std.Target.riscv.featureSetHas(cpu.features, .zvl65536b)) { | |
| 66 | vec_bit_length = 65536; | |
| 67 | } | |
| 68 | break :blk vec_bit_length; | |
| 69 | } | |
| 42 | 70 | } else if (cpu.arch.isSPARC()) { |
| 43 | 71 | // TODO: Test Sparc capability to handle bigger vectors |
| 44 | 72 | // In theory Sparc have 32 registers of 64 bits which can use in parallel |
lib/std/start.zig+20-1| ... | ... | @@ -221,7 +221,26 @@ fn riscv_start() callconv(.C) noreturn { |
| 221 | 221 | } |
| 222 | 222 | break :ret root.main(); |
| 223 | 223 | }, |
| 224 | else => @compileError("expected return type of main to be 'void', 'noreturn', 'u8'"), | |
| 224 | .ErrorUnion => ret: { | |
| 225 | const result = root.main() catch { | |
| 226 | const stderr = std.io.getStdErr().writer(); | |
| 227 | stderr.writeAll("failed with error\n") catch { | |
| 228 | @panic("failed to print when main returned error"); | |
| 229 | }; | |
| 230 | break :ret 1; | |
| 231 | }; | |
| 232 | switch (@typeInfo(@TypeOf(result))) { | |
| 233 | .Void => break :ret 0, | |
| 234 | .Int => |info| { | |
| 235 | if (info.bits != 8 or info.signedness == .signed) { | |
| 236 | @compileError(bad_main_ret); | |
| 237 | } | |
| 238 | return result; | |
| 239 | }, | |
| 240 | else => @compileError(bad_main_ret), | |
| 241 | } | |
| 242 | }, | |
| 243 | else => @compileError(bad_main_ret), | |
| 225 | 244 | }); |
| 226 | 245 | } |
| 227 | 246 |
src/arch/riscv64/CodeGen.zig+830-239| ... | ... | @@ -1,8 +1,12 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const build_options = @import("build_options"); | |
| 4 | ||
| 3 | 5 | const mem = std.mem; |
| 4 | 6 | const math = std.math; |
| 5 | 7 | const assert = std.debug.assert; |
| 8 | const Allocator = mem.Allocator; | |
| 9 | ||
| 6 | 10 | const Air = @import("../../Air.zig"); |
| 7 | 11 | const Mir = @import("Mir.zig"); |
| 8 | 12 | const Emit = @import("Emit.zig"); |
| ... | ... | @@ -14,18 +18,16 @@ const Zcu = @import("../../Zcu.zig"); |
| 14 | 18 | const Package = @import("../../Package.zig"); |
| 15 | 19 | const InternPool = @import("../../InternPool.zig"); |
| 16 | 20 | const Compilation = @import("../../Compilation.zig"); |
| 21 | const trace = @import("../../tracy.zig").trace; | |
| 22 | const codegen = @import("../../codegen.zig"); | |
| 23 | ||
| 17 | 24 | const ErrorMsg = Zcu.ErrorMsg; |
| 18 | 25 | const Target = std.Target; |
| 19 | const Allocator = mem.Allocator; | |
| 20 | const trace = @import("../../tracy.zig").trace; | |
| 21 | const DW = std.dwarf; | |
| 22 | const leb128 = std.leb; | |
| 26 | ||
| 23 | 27 | const log = std.log.scoped(.riscv_codegen); |
| 24 | 28 | const tracking_log = std.log.scoped(.tracking); |
| 25 | 29 | const verbose_tracking_log = std.log.scoped(.verbose_tracking); |
| 26 | 30 | const wip_mir_log = std.log.scoped(.wip_mir); |
| 27 | const build_options = @import("build_options"); | |
| 28 | const codegen = @import("../../codegen.zig"); | |
| 29 | 31 | const Alignment = InternPool.Alignment; |
| 30 | 32 | |
| 31 | 33 | const CodeGenError = codegen.CodeGenError; |
| ... | ... | @@ -37,6 +39,7 @@ const abi = @import("abi.zig"); |
| 37 | 39 | const Lower = @import("Lower.zig"); |
| 38 | 40 | |
| 39 | 41 | const Register = bits.Register; |
| 42 | const CSR = bits.CSR; | |
| 40 | 43 | const Immediate = bits.Immediate; |
| 41 | 44 | const Memory = bits.Memory; |
| 42 | 45 | const FrameIndex = bits.FrameIndex; |
| ... | ... | @@ -45,15 +48,16 @@ const RegisterLock = RegisterManager.RegisterLock; |
| 45 | 48 | |
| 46 | 49 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 47 | 50 | |
| 48 | gpa: Allocator, | |
| 49 | 51 | pt: Zcu.PerThread, |
| 50 | 52 | air: Air, |
| 51 | mod: *Package.Module, | |
| 52 | 53 | liveness: Liveness, |
| 54 | zcu: *Zcu, | |
| 53 | 55 | bin_file: *link.File, |
| 56 | gpa: Allocator, | |
| 57 | ||
| 58 | mod: *Package.Module, | |
| 54 | 59 | target: *const std.Target, |
| 55 | 60 | func_index: InternPool.Index, |
| 56 | code: *std.ArrayList(u8), | |
| 57 | 61 | debug_output: DebugInfoOutput, |
| 58 | 62 | err_msg: ?*ErrorMsg, |
| 59 | 63 | args: []MCValue, |
| ... | ... | @@ -62,9 +66,7 @@ fn_type: Type, |
| 62 | 66 | arg_index: usize, |
| 63 | 67 | src_loc: Zcu.LazySrcLoc, |
| 64 | 68 | |
| 65 | /// MIR Instructions | |
| 66 | 69 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 67 | /// MIR extra data | |
| 68 | 70 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 69 | 71 | |
| 70 | 72 | /// Byte offset within the source file of the ending curly. |
| ... | ... | @@ -87,6 +89,10 @@ exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, |
| 87 | 89 | /// across each runtime branch upon joining. |
| 88 | 90 | branch_stack: *std.ArrayList(Branch), |
| 89 | 91 | |
| 92 | // Currently set vector properties, null means they haven't been set yet in the function. | |
| 93 | avl: ?u64, | |
| 94 | vtype: ?bits.VType, | |
| 95 | ||
| 90 | 96 | // Key is the block instruction |
| 91 | 97 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 92 | 98 | register_manager: RegisterManager = .{}, |
| ... | ... | @@ -117,8 +123,9 @@ const MCValue = union(enum) { |
| 117 | 123 | /// No more references to this value remain. |
| 118 | 124 | /// The payload is the value of scope_generation at the point where the death occurred |
| 119 | 125 | dead: u32, |
| 120 | /// The value is undefined. | |
| 121 | undef, | |
| 126 | /// The value is undefined. Contains a symbol index to an undefined constant. Null means | |
| 127 | /// set the undefined value via immediate instead of a load. | |
| 128 | undef: ?u32, | |
| 122 | 129 | /// A pointer-sized integer that fits in a register. |
| 123 | 130 | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 124 | 131 | immediate: u64, |
| ... | ... | @@ -725,16 +732,16 @@ pub fn generate( |
| 725 | 732 | } |
| 726 | 733 | try branch_stack.append(.{}); |
| 727 | 734 | |
| 728 | var function = Func{ | |
| 735 | var function: Func = .{ | |
| 729 | 736 | .gpa = gpa, |
| 730 | 737 | .air = air, |
| 731 | 738 | .pt = pt, |
| 732 | 739 | .mod = mod, |
| 740 | .zcu = zcu, | |
| 741 | .bin_file = bin_file, | |
| 733 | 742 | .liveness = liveness, |
| 734 | 743 | .target = target, |
| 735 | .bin_file = bin_file, | |
| 736 | 744 | .func_index = func_index, |
| 737 | .code = code, | |
| 738 | 745 | .debug_output = debug_output, |
| 739 | 746 | .err_msg = null, |
| 740 | 747 | .args = undefined, // populated after `resolveCallingConventionValues` |
| ... | ... | @@ -746,6 +753,8 @@ pub fn generate( |
| 746 | 753 | .end_di_line = func.rbrace_line, |
| 747 | 754 | .end_di_column = func.rbrace_column, |
| 748 | 755 | .scope_generation = 0, |
| 756 | .avl = null, | |
| 757 | .vtype = null, | |
| 749 | 758 | }; |
| 750 | 759 | defer { |
| 751 | 760 | function.frame_allocs.deinit(gpa); |
| ... | ... | @@ -817,7 +826,7 @@ pub fn generate( |
| 817 | 826 | else => |e| return e, |
| 818 | 827 | }; |
| 819 | 828 | |
| 820 | var mir = Mir{ | |
| 829 | var mir: Mir = .{ | |
| 821 | 830 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 822 | 831 | .extra = try function.mir_extra.toOwnedSlice(gpa), |
| 823 | 832 | .frame_locs = function.frame_locs.toOwnedSlice(), |
| ... | ... | @@ -825,7 +834,6 @@ pub fn generate( |
| 825 | 834 | defer mir.deinit(gpa); |
| 826 | 835 | |
| 827 | 836 | var emit: Emit = .{ |
| 828 | .bin_file = bin_file, | |
| 829 | 837 | .lower = .{ |
| 830 | 838 | .pt = pt, |
| 831 | 839 | .allocator = gpa, |
| ... | ... | @@ -836,6 +844,7 @@ pub fn generate( |
| 836 | 844 | .link_mode = comp.config.link_mode, |
| 837 | 845 | .pic = mod.pic, |
| 838 | 846 | }, |
| 847 | .bin_file = bin_file, | |
| 839 | 848 | .debug_output = debug_output, |
| 840 | 849 | .code = code, |
| 841 | 850 | .prev_di_pc = 0, |
| ... | ... | @@ -896,7 +905,7 @@ fn formatWipMir( |
| 896 | 905 | .pic = comp.root_mod.pic, |
| 897 | 906 | }; |
| 898 | 907 | var first = true; |
| 899 | for ((lower.lowerMir(data.inst) catch |err| switch (err) { | |
| 908 | for ((lower.lowerMir(data.inst, .{ .allow_frame_locs = false }) catch |err| switch (err) { | |
| 900 | 909 | error.LowerFail => { |
| 901 | 910 | defer { |
| 902 | 911 | lower.err_msg.?.deinit(data.func.gpa); |
| ... | ... | @@ -924,7 +933,7 @@ fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) |
| 924 | 933 | } |
| 925 | 934 | |
| 926 | 935 | const FormatDeclData = struct { |
| 927 | mod: *Zcu, | |
| 936 | zcu: *Zcu, | |
| 928 | 937 | decl_index: InternPool.DeclIndex, |
| 929 | 938 | }; |
| 930 | 939 | fn formatDecl( |
| ... | ... | @@ -933,11 +942,11 @@ fn formatDecl( |
| 933 | 942 | _: std.fmt.FormatOptions, |
| 934 | 943 | writer: anytype, |
| 935 | 944 | ) @TypeOf(writer).Error!void { |
| 936 | try writer.print("{}", .{data.mod.declPtr(data.decl_index).fqn.fmt(&data.mod.intern_pool)}); | |
| 945 | try writer.print("{}", .{data.zcu.declPtr(data.decl_index).fqn.fmt(&data.zcu.intern_pool)}); | |
| 937 | 946 | } |
| 938 | 947 | fn fmtDecl(func: *Func, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) { |
| 939 | 948 | return .{ .data = .{ |
| 940 | .mod = func.pt.zcu, | |
| 949 | .zcu = func.zcu, | |
| 941 | 950 | .decl_index = decl_index, |
| 942 | 951 | } }; |
| 943 | 952 | } |
| ... | ... | @@ -989,13 +998,9 @@ fn addInst(func: *Func, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| 989 | 998 | .pseudo_dbg_prologue_end, |
| 990 | 999 | .pseudo_dbg_line_column, |
| 991 | 1000 | .pseudo_dbg_epilogue_begin, |
| 992 | .pseudo_store_rm, | |
| 993 | .pseudo_load_rm, | |
| 994 | .pseudo_lea_rm, | |
| 995 | .pseudo_mv, | |
| 996 | 1001 | .pseudo_dead, |
| 997 | 1002 | => false, |
| 998 | }) wip_mir_log.debug("{}", .{func.fmtWipMir(result_index)}) else wip_mir_log.debug(" | uses-mem", .{}); | |
| 1003 | }) wip_mir_log.debug("{}", .{func.fmtWipMir(result_index)}); | |
| 999 | 1004 | return result_index; |
| 1000 | 1005 | } |
| 1001 | 1006 | |
| ... | ... | @@ -1042,9 +1047,84 @@ pub fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 { |
| 1042 | 1047 | return result; |
| 1043 | 1048 | } |
| 1044 | 1049 | |
| 1050 | /// Returns a temporary register that contains the value of the `reg` csr. | |
| 1051 | /// | |
| 1052 | /// Caller's duty to lock the return register is needed. | |
| 1053 | fn getCsr(func: *Func, csr: CSR) !Register { | |
| 1054 | assert(func.hasFeature(.zicsr)); | |
| 1055 | const dst_reg = try func.register_manager.allocReg(null, func.regTempClassForType(Type.usize)); | |
| 1056 | _ = try func.addInst(.{ | |
| 1057 | .tag = .csrrs, | |
| 1058 | .ops = .csr, | |
| 1059 | .data = .{ | |
| 1060 | .csr = .{ | |
| 1061 | .csr = csr, | |
| 1062 | .rd = dst_reg, | |
| 1063 | .rs1 = .x0, | |
| 1064 | }, | |
| 1065 | }, | |
| 1066 | }); | |
| 1067 | return dst_reg; | |
| 1068 | } | |
| 1069 | ||
| 1070 | fn setVl(func: *Func, dst_reg: Register, avl: u64, options: bits.VType) !void { | |
| 1071 | if (func.avl == avl) if (func.vtype) |vtype| { | |
| 1072 | // it's already set, we don't need to do anything | |
| 1073 | if (@as(u8, @bitCast(vtype)) == @as(u8, @bitCast(options))) return; | |
| 1074 | }; | |
| 1075 | ||
| 1076 | func.avl = avl; | |
| 1077 | func.vtype = options; | |
| 1078 | ||
| 1079 | if (avl == 0) { | |
| 1080 | // the caller means to do "vsetvli zero, zero ..." which keeps the avl to whatever it was before | |
| 1081 | const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options)); | |
| 1082 | _ = try func.addInst(.{ | |
| 1083 | .tag = .vsetvli, | |
| 1084 | .ops = .rri, | |
| 1085 | .data = .{ .i_type = .{ | |
| 1086 | .rd = dst_reg, | |
| 1087 | .rs1 = .zero, | |
| 1088 | .imm12 = Immediate.u(options_int), | |
| 1089 | } }, | |
| 1090 | }); | |
| 1091 | } else { | |
| 1092 | // if the avl can fit into u5 we can use vsetivli otherwise use vsetvli | |
| 1093 | if (avl <= std.math.maxInt(u5)) { | |
| 1094 | const options_int: u12 = (~@as(u12, 0) << 10) | @as(u8, @bitCast(options)); | |
| 1095 | _ = try func.addInst(.{ | |
| 1096 | .tag = .vsetivli, | |
| 1097 | .ops = .rri, | |
| 1098 | .data = .{ | |
| 1099 | .i_type = .{ | |
| 1100 | .rd = dst_reg, | |
| 1101 | .rs1 = @enumFromInt(avl), | |
| 1102 | .imm12 = Immediate.u(options_int), | |
| 1103 | }, | |
| 1104 | }, | |
| 1105 | }); | |
| 1106 | } else { | |
| 1107 | const options_int: u12 = @as(u12, 0) | @as(u8, @bitCast(options)); | |
| 1108 | const temp_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = avl }); | |
| 1109 | _ = try func.addInst(.{ | |
| 1110 | .tag = .vsetvli, | |
| 1111 | .ops = .rri, | |
| 1112 | .data = .{ .i_type = .{ | |
| 1113 | .rd = dst_reg, | |
| 1114 | .rs1 = temp_reg, | |
| 1115 | .imm12 = Immediate.u(options_int), | |
| 1116 | } }, | |
| 1117 | }); | |
| 1118 | } | |
| 1119 | } | |
| 1120 | } | |
| 1121 | ||
| 1045 | 1122 | const required_features = [_]Target.riscv.Feature{ |
| 1046 | 1123 | .d, |
| 1047 | 1124 | .m, |
| 1125 | .a, | |
| 1126 | .zicsr, | |
| 1127 | .v, | |
| 1048 | 1128 | }; |
| 1049 | 1129 | |
| 1050 | 1130 | fn gen(func: *Func) !void { |
| ... | ... | @@ -1102,7 +1182,19 @@ fn gen(func: *Func) !void { |
| 1102 | 1182 | const backpatch_ra_restore = try func.addPseudo(.pseudo_dead); |
| 1103 | 1183 | const backpatch_fp_restore = try func.addPseudo(.pseudo_dead); |
| 1104 | 1184 | const backpatch_stack_alloc_restore = try func.addPseudo(.pseudo_dead); |
| 1105 | try func.addPseudoNone(.pseudo_ret); | |
| 1185 | ||
| 1186 | // ret | |
| 1187 | _ = try func.addInst(.{ | |
| 1188 | .tag = .jalr, | |
| 1189 | .ops = .rri, | |
| 1190 | .data = .{ | |
| 1191 | .i_type = .{ | |
| 1192 | .rd = .zero, | |
| 1193 | .rs1 = .ra, | |
| 1194 | .imm12 = Immediate.s(0), | |
| 1195 | }, | |
| 1196 | }, | |
| 1197 | }); | |
| 1106 | 1198 | |
| 1107 | 1199 | const frame_layout = try func.computeFrameLayout(); |
| 1108 | 1200 | const need_save_reg = frame_layout.save_reg_list.count() > 0; |
| ... | ... | @@ -1273,7 +1365,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1273 | 1365 | .round, |
| 1274 | 1366 | .trunc_float, |
| 1275 | 1367 | .neg, |
| 1276 | => try func.airUnaryMath(inst), | |
| 1368 | => try func.airUnaryMath(inst, tag), | |
| 1277 | 1369 | |
| 1278 | 1370 | .add_with_overflow => try func.airAddWithOverflow(inst), |
| 1279 | 1371 | .sub_with_overflow => try func.airSubWithOverflow(inst), |
| ... | ... | @@ -1319,7 +1411,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1319 | 1411 | .breakpoint => try func.airBreakpoint(), |
| 1320 | 1412 | .ret_addr => try func.airRetAddr(inst), |
| 1321 | 1413 | .frame_addr => try func.airFrameAddress(inst), |
| 1322 | .fence => try func.airFence(), | |
| 1414 | .fence => try func.airFence(inst), | |
| 1323 | 1415 | .cond_br => try func.airCondBr(inst), |
| 1324 | 1416 | .dbg_stmt => try func.airDbgStmt(inst), |
| 1325 | 1417 | .fptrunc => try func.airFptrunc(inst), |
| ... | ... | @@ -1631,7 +1723,7 @@ fn computeFrameLayout(func: *Func) !FrameLayout { |
| 1631 | 1723 | |
| 1632 | 1724 | // The total frame size is calculated by the amount of s registers you need to save * 8, as each |
| 1633 | 1725 | // register is 8 bytes, the total allocation sizes, and 16 more register for the spilled ra and s0 |
| 1634 | // register. Finally we align the frame size to the align of the base pointer. | |
| 1726 | // register. Finally we align the frame size to the alignment of the base pointer. | |
| 1635 | 1727 | const args_frame_size = frame_size[@intFromEnum(FrameIndex.args_frame)]; |
| 1636 | 1728 | const spill_frame_size = frame_size[@intFromEnum(FrameIndex.spill_frame)]; |
| 1637 | 1729 | const call_frame_size = frame_size[@intFromEnum(FrameIndex.call_frame)]; |
| ... | ... | @@ -1791,14 +1883,9 @@ fn symbolIndex(func: *Func) !u32 { |
| 1791 | 1883 | const pt = func.pt; |
| 1792 | 1884 | const zcu = pt.zcu; |
| 1793 | 1885 | const decl_index = zcu.funcOwnerDeclIndex(func.func_index); |
| 1794 | return switch (func.bin_file.tag) { | |
| 1795 | .elf => blk: { | |
| 1796 | const elf_file = func.bin_file.cast(link.File.Elf).?; | |
| 1797 | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); | |
| 1798 | break :blk atom_index; | |
| 1799 | }, | |
| 1800 | else => return func.fail("TODO symbolIndex {s}", .{@tagName(func.bin_file.tag)}), | |
| 1801 | }; | |
| 1886 | const elf_file = func.bin_file.cast(link.File.Elf).?; | |
| 1887 | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); | |
| 1888 | return atom_index; | |
| 1802 | 1889 | } |
| 1803 | 1890 | |
| 1804 | 1891 | fn allocFrameIndex(func: *Func, alloc: FrameAlloc) !FrameIndex { |
| ... | ... | @@ -1843,48 +1930,44 @@ fn typeRegClass(func: *Func, ty: Type) abi.RegisterClass { |
| 1843 | 1930 | const zcu = pt.zcu; |
| 1844 | 1931 | return switch (ty.zigTypeTag(zcu)) { |
| 1845 | 1932 | .Float => .float, |
| 1846 | .Vector => @panic("TODO: typeRegClass for Vectors"), | |
| 1847 | inline else => .int, | |
| 1933 | .Vector => .vector, | |
| 1934 | else => .int, | |
| 1848 | 1935 | }; |
| 1849 | 1936 | } |
| 1850 | 1937 | |
| 1851 | 1938 | fn regGeneralClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet { |
| 1852 | const pt = func.pt; | |
| 1853 | const zcu = pt.zcu; | |
| 1854 | return switch (ty.zigTypeTag(zcu)) { | |
| 1939 | return switch (ty.zigTypeTag(func.pt.zcu)) { | |
| 1855 | 1940 | .Float => abi.Registers.Float.general_purpose, |
| 1856 | .Vector => @panic("TODO: regGeneralClassForType for Vectors"), | |
| 1941 | .Vector => abi.Registers.Vector.general_purpose, | |
| 1857 | 1942 | else => abi.Registers.Integer.general_purpose, |
| 1858 | 1943 | }; |
| 1859 | 1944 | } |
| 1860 | 1945 | |
| 1861 | 1946 | fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet { |
| 1862 | const pt = func.pt; | |
| 1863 | const zcu = pt.zcu; | |
| 1864 | return switch (ty.zigTypeTag(zcu)) { | |
| 1947 | return switch (ty.zigTypeTag(func.pt.zcu)) { | |
| 1865 | 1948 | .Float => abi.Registers.Float.temporary, |
| 1866 | .Vector => @panic("TODO: regTempClassForType for Vectors"), | |
| 1949 | .Vector => abi.Registers.Vector.general_purpose, // there are no temporary vector registers | |
| 1867 | 1950 | else => abi.Registers.Integer.temporary, |
| 1868 | 1951 | }; |
| 1869 | 1952 | } |
| 1870 | 1953 | |
| 1871 | 1954 | fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1872 | 1955 | const pt = func.pt; |
| 1956 | const zcu = pt.zcu; | |
| 1873 | 1957 | |
| 1874 | const abi_size = math.cast(u32, elem_ty.abiSize(pt)) orelse { | |
| 1875 | return func.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(pt)}); | |
| 1876 | }; | |
| 1877 | ||
| 1878 | const min_size: u32 = switch (elem_ty.zigTypeTag(pt.zcu)) { | |
| 1879 | .Float => 4, | |
| 1880 | .Vector => @panic("allocRegOrMem Vector"), | |
| 1881 | else => 8, | |
| 1958 | const bit_size = elem_ty.bitSize(pt); | |
| 1959 | const min_size: u64 = switch (elem_ty.zigTypeTag(zcu)) { | |
| 1960 | .Float => if (func.hasFeature(.d)) 64 else 32, | |
| 1961 | .Vector => 256, // TODO: calculate it from avl * vsew | |
| 1962 | else => 64, | |
| 1882 | 1963 | }; |
| 1883 | 1964 | |
| 1884 | if (reg_ok and abi_size <= min_size) { | |
| 1965 | if (reg_ok and bit_size <= min_size) { | |
| 1885 | 1966 | if (func.register_manager.tryAllocReg(inst, func.regGeneralClassForType(elem_ty))) |reg| { |
| 1886 | 1967 | return .{ .register = reg }; |
| 1887 | 1968 | } |
| 1969 | } else if (reg_ok and elem_ty.zigTypeTag(zcu) == .Vector) { | |
| 1970 | return func.fail("did you forget to extend vector registers before allocating", .{}); | |
| 1888 | 1971 | } |
| 1889 | 1972 | |
| 1890 | 1973 | const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(elem_ty, pt)); |
| ... | ... | @@ -1897,10 +1980,13 @@ fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool |
| 1897 | 1980 | fn allocReg(func: *Func, reg_class: abi.RegisterClass) !struct { Register, RegisterLock } { |
| 1898 | 1981 | if (reg_class == .float and !func.hasFeature(.f)) |
| 1899 | 1982 | std.debug.panic("allocReg class == float where F isn't enabled", .{}); |
| 1983 | if (reg_class == .vector and !func.hasFeature(.v)) | |
| 1984 | std.debug.panic("allocReg class == vector where V isn't enabled", .{}); | |
| 1900 | 1985 | |
| 1901 | 1986 | const class = switch (reg_class) { |
| 1902 | 1987 | .int => abi.Registers.Integer.general_purpose, |
| 1903 | 1988 | .float => abi.Registers.Float.general_purpose, |
| 1989 | .vector => abi.Registers.Vector.general_purpose, | |
| 1904 | 1990 | }; |
| 1905 | 1991 | |
| 1906 | 1992 | const reg = try func.register_manager.allocReg(null, class); |
| ... | ... | @@ -1916,7 +2002,8 @@ fn promoteReg(func: *Func, ty: Type, operand: MCValue) !struct { Register, ?Regi |
| 1916 | 2002 | return .{ op_reg, func.register_manager.lockReg(operand.register) }; |
| 1917 | 2003 | } |
| 1918 | 2004 | |
| 1919 | const reg, const lock = try func.allocReg(func.typeRegClass(ty)); | |
| 2005 | const class = func.typeRegClass(ty); | |
| 2006 | const reg, const lock = try func.allocReg(class); | |
| 1920 | 2007 | try func.genSetReg(ty, reg, operand); |
| 1921 | 2008 | return .{ reg, lock }; |
| 1922 | 2009 | } |
| ... | ... | @@ -2087,19 +2174,17 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void { |
| 2087 | 2174 | const operand = try func.resolveInst(ty_op.operand); |
| 2088 | 2175 | const ty = func.typeOf(ty_op.operand); |
| 2089 | 2176 | |
| 2090 | switch (ty.zigTypeTag(zcu)) { | |
| 2091 | .Bool => { | |
| 2092 | const operand_reg = blk: { | |
| 2093 | if (operand == .register) break :blk operand.register; | |
| 2094 | break :blk try func.copyToTmpRegister(ty, operand); | |
| 2095 | }; | |
| 2177 | const operand_reg, const operand_lock = try func.promoteReg(ty, operand); | |
| 2178 | defer if (operand_lock) |lock| func.register_manager.unlockReg(lock); | |
| 2096 | 2179 | |
| 2097 | const dst_reg: Register = | |
| 2098 | if (func.reuseOperand(inst, ty_op.operand, 0, operand) and operand == .register) | |
| 2099 | operand.register | |
| 2100 | else | |
| 2101 | (try func.allocRegOrMem(func.typeOfIndex(inst), inst, true)).register; | |
| 2180 | const dst_reg: Register = | |
| 2181 | if (func.reuseOperand(inst, ty_op.operand, 0, operand) and operand == .register) | |
| 2182 | operand.register | |
| 2183 | else | |
| 2184 | (try func.allocRegOrMem(func.typeOfIndex(inst), inst, true)).register; | |
| 2102 | 2185 | |
| 2186 | switch (ty.zigTypeTag(zcu)) { | |
| 2187 | .Bool => { | |
| 2103 | 2188 | _ = try func.addInst(.{ |
| 2104 | 2189 | .tag = .pseudo, |
| 2105 | 2190 | .ops = .pseudo_not, |
| ... | ... | @@ -2110,12 +2195,34 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void { |
| 2110 | 2195 | }, |
| 2111 | 2196 | }, |
| 2112 | 2197 | }); |
| 2198 | }, | |
| 2199 | .Int => { | |
| 2200 | const size = ty.bitSize(pt); | |
| 2201 | if (!math.isPowerOfTwo(size)) | |
| 2202 | return func.fail("TODO: airNot non-pow 2 int size", .{}); | |
| 2113 | 2203 | |
| 2114 | break :result .{ .register = dst_reg }; | |
| 2204 | switch (size) { | |
| 2205 | 32, 64 => { | |
| 2206 | _ = try func.addInst(.{ | |
| 2207 | .tag = .xori, | |
| 2208 | .ops = .rri, | |
| 2209 | .data = .{ | |
| 2210 | .i_type = .{ | |
| 2211 | .rd = dst_reg, | |
| 2212 | .rs1 = operand_reg, | |
| 2213 | .imm12 = Immediate.s(-1), | |
| 2214 | }, | |
| 2215 | }, | |
| 2216 | }); | |
| 2217 | }, | |
| 2218 | 8, 16 => return func.fail("TODO: airNot 8 or 16, {}", .{size}), | |
| 2219 | else => unreachable, | |
| 2220 | } | |
| 2115 | 2221 | }, |
| 2116 | .Int => return func.fail("TODO: airNot ints", .{}), | |
| 2117 | 2222 | else => unreachable, |
| 2118 | 2223 | } |
| 2224 | ||
| 2225 | break :result .{ .register = dst_reg }; | |
| 2119 | 2226 | }; |
| 2120 | 2227 | return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2121 | 2228 | } |
| ... | ... | @@ -2205,7 +2312,11 @@ fn binOp( |
| 2205 | 2312 | return func.fail("binOp libcall runtime-float ops", .{}); |
| 2206 | 2313 | } |
| 2207 | 2314 | |
| 2208 | if (lhs_ty.bitSize(pt) > 64) return func.fail("TODO: binOp >= 64 bits", .{}); | |
| 2315 | // don't have support for certain sizes of addition | |
| 2316 | switch (lhs_ty.zigTypeTag(pt.zcu)) { | |
| 2317 | .Vector => {}, // works differently and fails in a different place | |
| 2318 | else => if (lhs_ty.bitSize(pt) > 64) return func.fail("TODO: binOp >= 64 bits", .{}), | |
| 2319 | } | |
| 2209 | 2320 | |
| 2210 | 2321 | const lhs_mcv = try func.resolveInst(lhs_air); |
| 2211 | 2322 | const rhs_mcv = try func.resolveInst(rhs_air); |
| ... | ... | @@ -2353,6 +2464,51 @@ fn genBinOp( |
| 2353 | 2464 | }, |
| 2354 | 2465 | }); |
| 2355 | 2466 | }, |
| 2467 | .Vector => { | |
| 2468 | const num_elem = lhs_ty.vectorLen(zcu); | |
| 2469 | const elem_size = lhs_ty.childType(zcu).bitSize(pt); | |
| 2470 | ||
| 2471 | const child_ty = lhs_ty.childType(zcu); | |
| 2472 | ||
| 2473 | const mir_tag: Mir.Inst.Tag = switch (tag) { | |
| 2474 | .add => switch (child_ty.zigTypeTag(zcu)) { | |
| 2475 | .Int => .vaddvv, | |
| 2476 | .Float => .vfaddvv, | |
| 2477 | else => unreachable, | |
| 2478 | }, | |
| 2479 | .sub => switch (child_ty.zigTypeTag(zcu)) { | |
| 2480 | .Int => .vsubvv, | |
| 2481 | .Float => .vfsubvv, | |
| 2482 | else => unreachable, | |
| 2483 | }, | |
| 2484 | else => return func.fail("TODO: genBinOp {s} Vector", .{@tagName(tag)}), | |
| 2485 | }; | |
| 2486 | ||
| 2487 | try func.setVl(.zero, num_elem, .{ | |
| 2488 | .vsew = switch (elem_size) { | |
| 2489 | 8 => .@"8", | |
| 2490 | 16 => .@"16", | |
| 2491 | 32 => .@"32", | |
| 2492 | 64 => .@"64", | |
| 2493 | else => unreachable, | |
| 2494 | }, | |
| 2495 | .vlmul = .m1, | |
| 2496 | .vma = true, | |
| 2497 | .vta = true, | |
| 2498 | }); | |
| 2499 | ||
| 2500 | _ = try func.addInst(.{ | |
| 2501 | .tag = mir_tag, | |
| 2502 | .ops = .rrr, | |
| 2503 | .data = .{ | |
| 2504 | .r_type = .{ | |
| 2505 | .rd = dst_reg, | |
| 2506 | .rs1 = rhs_reg, | |
| 2507 | .rs2 = lhs_reg, | |
| 2508 | }, | |
| 2509 | }, | |
| 2510 | }); | |
| 2511 | }, | |
| 2356 | 2512 | else => unreachable, |
| 2357 | 2513 | } |
| 2358 | 2514 | }, |
| ... | ... | @@ -2636,78 +2792,55 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2636 | 2792 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2637 | 2793 | |
| 2638 | 2794 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2639 | const lhs_ty = func.typeOf(extra.lhs); | |
| 2640 | ||
| 2641 | const int_info = lhs_ty.intInfo(zcu); | |
| 2642 | ||
| 2643 | const tuple_ty = func.typeOfIndex(inst); | |
| 2644 | const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false); | |
| 2645 | const offset = result_mcv.load_frame; | |
| 2646 | ||
| 2647 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { | |
| 2648 | const add_result = try func.binOp(null, .add, extra.lhs, extra.rhs); | |
| 2649 | const add_result_reg = try func.copyToTmpRegister(lhs_ty, add_result); | |
| 2650 | const add_result_reg_lock = func.register_manager.lockRegAssumeUnused(add_result_reg); | |
| 2651 | defer func.register_manager.unlockReg(add_result_reg_lock); | |
| 2795 | const ty = func.typeOf(extra.lhs); | |
| 2796 | switch (ty.zigTypeTag(zcu)) { | |
| 2797 | .Vector => return func.fail("TODO implement add with overflow for Vector type", .{}), | |
| 2798 | .Int => { | |
| 2799 | const int_info = ty.intInfo(zcu); | |
| 2652 | 2800 | |
| 2653 | const shift_amount: u6 = @intCast(Type.usize.bitSize(pt) - int_info.bits); | |
| 2801 | const tuple_ty = func.typeOfIndex(inst); | |
| 2802 | const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false); | |
| 2803 | const offset = result_mcv.load_frame; | |
| 2654 | 2804 | |
| 2655 | const shift_reg, const shift_lock = try func.allocReg(.int); | |
| 2656 | defer func.register_manager.unlockReg(shift_lock); | |
| 2805 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { | |
| 2806 | const add_result = try func.binOp(null, .add, extra.lhs, extra.rhs); | |
| 2657 | 2807 | |
| 2658 | _ = try func.addInst(.{ | |
| 2659 | .tag = .slli, | |
| 2660 | .ops = .rri, | |
| 2661 | .data = .{ | |
| 2662 | .i_type = .{ | |
| 2663 | .rd = shift_reg, | |
| 2664 | .rs1 = add_result_reg, | |
| 2665 | .imm12 = Immediate.u(shift_amount), | |
| 2666 | }, | |
| 2667 | }, | |
| 2668 | }); | |
| 2808 | const add_result_reg = try func.copyToTmpRegister(ty, add_result); | |
| 2809 | const add_result_reg_lock = func.register_manager.lockRegAssumeUnused(add_result_reg); | |
| 2810 | defer func.register_manager.unlockReg(add_result_reg_lock); | |
| 2669 | 2811 | |
| 2670 | _ = try func.addInst(.{ | |
| 2671 | .tag = if (int_info.signedness == .unsigned) .srli else .srai, | |
| 2672 | .ops = .rri, | |
| 2673 | .data = .{ | |
| 2674 | .i_type = .{ | |
| 2675 | .rd = shift_reg, | |
| 2676 | .rs1 = shift_reg, | |
| 2677 | .imm12 = Immediate.u(shift_amount), | |
| 2678 | }, | |
| 2679 | }, | |
| 2680 | }); | |
| 2681 | ||
| 2682 | try func.genSetMem( | |
| 2683 | .{ .frame = offset.index }, | |
| 2684 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))), | |
| 2685 | lhs_ty, | |
| 2686 | add_result, | |
| 2687 | ); | |
| 2812 | try func.genSetMem( | |
| 2813 | .{ .frame = offset.index }, | |
| 2814 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, pt))), | |
| 2815 | ty, | |
| 2816 | add_result, | |
| 2817 | ); | |
| 2688 | 2818 | |
| 2689 | const overflow_reg, const overflow_lock = try func.allocReg(.int); | |
| 2690 | defer func.register_manager.unlockReg(overflow_lock); | |
| 2819 | const overflow_reg, const overflow_lock = try func.allocReg(.int); | |
| 2820 | defer func.register_manager.unlockReg(overflow_lock); | |
| 2691 | 2821 | |
| 2692 | try func.genBinOp( | |
| 2693 | .cmp_neq, | |
| 2694 | .{ .register = shift_reg }, | |
| 2695 | lhs_ty, | |
| 2696 | .{ .register = add_result_reg }, | |
| 2697 | lhs_ty, | |
| 2698 | overflow_reg, | |
| 2699 | ); | |
| 2822 | try func.genBinOp( | |
| 2823 | .cmp_neq, | |
| 2824 | .{ .register = add_result_reg }, | |
| 2825 | ty, | |
| 2826 | .{ .register = add_result_reg }, | |
| 2827 | ty, | |
| 2828 | overflow_reg, | |
| 2829 | ); | |
| 2700 | 2830 | |
| 2701 | try func.genSetMem( | |
| 2702 | .{ .frame = offset.index }, | |
| 2703 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, pt))), | |
| 2704 | Type.u1, | |
| 2705 | .{ .register = overflow_reg }, | |
| 2706 | ); | |
| 2831 | try func.genSetMem( | |
| 2832 | .{ .frame = offset.index }, | |
| 2833 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, pt))), | |
| 2834 | Type.u1, | |
| 2835 | .{ .register = overflow_reg }, | |
| 2836 | ); | |
| 2707 | 2837 | |
| 2708 | break :result result_mcv; | |
| 2709 | } else { | |
| 2710 | return func.fail("TODO: less than 8 bit or non-pow 2 addition", .{}); | |
| 2838 | break :result result_mcv; | |
| 2839 | } else { | |
| 2840 | return func.fail("TODO: less than 8 bit or non-pow 2 addition", .{}); | |
| 2841 | } | |
| 2842 | }, | |
| 2843 | else => unreachable, | |
| 2711 | 2844 | } |
| 2712 | 2845 | }; |
| 2713 | 2846 | |
| ... | ... | @@ -3229,7 +3362,7 @@ fn airWrapErrUnionErr(func: *Func, inst: Air.Inst.Index) !void { |
| 3229 | 3362 | const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(eu_ty, pt)); |
| 3230 | 3363 | const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, pt)); |
| 3231 | 3364 | const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, pt)); |
| 3232 | try func.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, .undef); | |
| 3365 | try func.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, .{ .undef = null }); | |
| 3233 | 3366 | const operand = try func.resolveInst(ty_op.operand); |
| 3234 | 3367 | try func.genSetMem(.{ .frame = frame_index }, err_off, err_ty, operand); |
| 3235 | 3368 | break :result .{ .load_frame = .{ .index = frame_index } }; |
| ... | ... | @@ -3451,20 +3584,54 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3451 | 3584 | else => try func.genSetReg(Type.usize, addr_reg, array_mcv.address()), |
| 3452 | 3585 | } |
| 3453 | 3586 | |
| 3587 | const dst_mcv = try func.allocRegOrMem(result_ty, inst, false); | |
| 3588 | ||
| 3589 | if (array_ty.isVector(zcu)) { | |
| 3590 | // we need to load the vector, vslidedown to get the element we want | |
| 3591 | // and store that element at in a load frame. | |
| 3592 | ||
| 3593 | const src_reg, const src_lock = try func.allocReg(.vector); | |
| 3594 | defer func.register_manager.unlockReg(src_lock); | |
| 3595 | ||
| 3596 | // load the vector into a temporary register | |
| 3597 | try func.genCopy(array_ty, .{ .register = src_reg }, .{ .indirect = .{ .reg = addr_reg } }); | |
| 3598 | ||
| 3599 | // we need to construct a 1xbitSize vector because of how lane splitting works in RISC-V | |
| 3600 | const single_ty = try pt.vectorType(.{ .child = elem_ty.toIntern(), .len = 1 }); | |
| 3601 | ||
| 3602 | // we can do a shortcut here where we don't need a vslicedown | |
| 3603 | // and can just copy to the frame index. | |
| 3604 | if (!(index_mcv == .immediate and index_mcv.immediate == 0)) { | |
| 3605 | const index_reg = try func.copyToTmpRegister(Type.usize, index_mcv); | |
| 3606 | ||
| 3607 | _ = try func.addInst(.{ | |
| 3608 | .tag = .vslidedownvx, | |
| 3609 | .ops = .rrr, | |
| 3610 | .data = .{ .r_type = .{ | |
| 3611 | .rd = src_reg, | |
| 3612 | .rs1 = index_reg, | |
| 3613 | .rs2 = src_reg, | |
| 3614 | } }, | |
| 3615 | }); | |
| 3616 | } | |
| 3617 | ||
| 3618 | try func.genCopy(single_ty, dst_mcv, .{ .register = src_reg }); | |
| 3619 | break :result dst_mcv; | |
| 3620 | } | |
| 3621 | ||
| 3454 | 3622 | const offset_reg = try func.elemOffset(index_ty, index_mcv, elem_abi_size); |
| 3455 | 3623 | const offset_lock = func.register_manager.lockRegAssumeUnused(offset_reg); |
| 3456 | 3624 | defer func.register_manager.unlockReg(offset_lock); |
| 3457 | ||
| 3458 | const dst_mcv = try func.allocRegOrMem(result_ty, inst, false); | |
| 3459 | 3625 | _ = try func.addInst(.{ |
| 3460 | 3626 | .tag = .add, |
| 3461 | 3627 | .ops = .rrr, |
| 3462 | 3628 | .data = .{ .r_type = .{ |
| 3463 | 3629 | .rd = addr_reg, |
| 3464 | .rs1 = offset_reg, | |
| 3465 | .rs2 = addr_reg, | |
| 3630 | .rs1 = addr_reg, | |
| 3631 | .rs2 = offset_reg, | |
| 3466 | 3632 | } }, |
| 3467 | 3633 | }); |
| 3634 | ||
| 3468 | 3635 | try func.genCopy(elem_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }); |
| 3469 | 3636 | break :result dst_mcv; |
| 3470 | 3637 | }; |
| ... | ... | @@ -3541,9 +3708,50 @@ fn airSetUnionTag(func: *Func, inst: Air.Inst.Index) !void { |
| 3541 | 3708 | } |
| 3542 | 3709 | |
| 3543 | 3710 | fn airGetUnionTag(func: *Func, inst: Air.Inst.Index) !void { |
| 3711 | const pt = func.pt; | |
| 3544 | 3712 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3545 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airGetUnionTag for {}", .{func.target.cpu.arch}); | |
| 3546 | return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 3713 | ||
| 3714 | const tag_ty = func.typeOfIndex(inst); | |
| 3715 | const union_ty = func.typeOf(ty_op.operand); | |
| 3716 | const layout = union_ty.unionGetLayout(pt); | |
| 3717 | ||
| 3718 | if (layout.tag_size == 0) { | |
| 3719 | return func.finishAir(inst, .none, .{ ty_op.operand, .none, .none }); | |
| 3720 | } | |
| 3721 | ||
| 3722 | const operand = try func.resolveInst(ty_op.operand); | |
| 3723 | ||
| 3724 | const frame_mcv = try func.allocRegOrMem(union_ty, null, false); | |
| 3725 | try func.genCopy(union_ty, frame_mcv, operand); | |
| 3726 | ||
| 3727 | const tag_abi_size = tag_ty.abiSize(pt); | |
| 3728 | const result_reg, const result_lock = try func.allocReg(.int); | |
| 3729 | defer func.register_manager.unlockReg(result_lock); | |
| 3730 | ||
| 3731 | switch (frame_mcv) { | |
| 3732 | .load_frame => |frame_addr| { | |
| 3733 | if (tag_abi_size <= 8) { | |
| 3734 | const off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align)) | |
| 3735 | @intCast(layout.payload_size) | |
| 3736 | else | |
| 3737 | 0; | |
| 3738 | ||
| 3739 | try func.genCopy( | |
| 3740 | tag_ty, | |
| 3741 | .{ .register = result_reg }, | |
| 3742 | .{ .load_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off } }, | |
| 3743 | ); | |
| 3744 | } else { | |
| 3745 | return func.fail( | |
| 3746 | "TODO implement get_union_tag for ABI larger than 8 bytes and operand {}, tag {}", | |
| 3747 | .{ frame_mcv, tag_ty.fmt(pt) }, | |
| 3748 | ); | |
| 3749 | } | |
| 3750 | }, | |
| 3751 | else => return func.fail("TODO: airGetUnionTag {s}", .{@tagName(operand)}), | |
| 3752 | } | |
| 3753 | ||
| 3754 | return func.finishAir(inst, .{ .register = result_reg }, .{ ty_op.operand, .none, .none }); | |
| 3547 | 3755 | } |
| 3548 | 3756 | |
| 3549 | 3757 | fn airClz(func: *Func, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3719,13 +3927,65 @@ fn airBitReverse(func: *Func, inst: Air.Inst.Index) !void { |
| 3719 | 3927 | return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3720 | 3928 | } |
| 3721 | 3929 | |
| 3722 | fn airUnaryMath(func: *Func, inst: Air.Inst.Index) !void { | |
| 3723 | const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)]; | |
| 3930 | fn airUnaryMath(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | |
| 3931 | const pt = func.pt; | |
| 3724 | 3932 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 3725 | const result: MCValue = if (func.liveness.isUnused(inst)) | |
| 3726 | .unreach | |
| 3727 | else | |
| 3728 | return func.fail("TODO implementairUnaryMath {s} for {}", .{ @tagName(tag), func.target.cpu.arch }); | |
| 3933 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { | |
| 3934 | const ty = func.typeOf(un_op); | |
| 3935 | ||
| 3936 | const operand = try func.resolveInst(un_op); | |
| 3937 | const operand_bit_size = ty.bitSize(pt); | |
| 3938 | ||
| 3939 | if (!math.isPowerOfTwo(operand_bit_size)) | |
| 3940 | return func.fail("TODO: airUnaryMath non-pow 2", .{}); | |
| 3941 | ||
| 3942 | const operand_reg, const operand_lock = try func.promoteReg(ty, operand); | |
| 3943 | defer if (operand_lock) |lock| func.register_manager.unlockReg(lock); | |
| 3944 | ||
| 3945 | const dst_class = func.typeRegClass(ty); | |
| 3946 | const dst_reg, const dst_lock = try func.allocReg(dst_class); | |
| 3947 | defer func.register_manager.unlockReg(dst_lock); | |
| 3948 | ||
| 3949 | switch (ty.zigTypeTag(pt.zcu)) { | |
| 3950 | .Float => { | |
| 3951 | assert(dst_class == .float); | |
| 3952 | ||
| 3953 | switch (operand_bit_size) { | |
| 3954 | 16, 80, 128 => return func.fail("TODO: airUnaryMath Float bit-size {}", .{operand_bit_size}), | |
| 3955 | 32, 64 => {}, | |
| 3956 | else => unreachable, | |
| 3957 | } | |
| 3958 | ||
| 3959 | switch (tag) { | |
| 3960 | .sqrt => { | |
| 3961 | _ = try func.addInst(.{ | |
| 3962 | .tag = if (operand_bit_size == 64) .fsqrtd else .fsqrts, | |
| 3963 | .ops = .rrr, | |
| 3964 | .data = .{ | |
| 3965 | .r_type = .{ | |
| 3966 | .rd = dst_reg, | |
| 3967 | .rs1 = operand_reg, | |
| 3968 | .rs2 = .f0, // unused, spec says it's 0 | |
| 3969 | }, | |
| 3970 | }, | |
| 3971 | }); | |
| 3972 | }, | |
| 3973 | else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}), | |
| 3974 | } | |
| 3975 | }, | |
| 3976 | .Int => { | |
| 3977 | assert(dst_class == .int); | |
| 3978 | ||
| 3979 | switch (tag) { | |
| 3980 | else => return func.fail("TODO: airUnaryMath Float {s}", .{@tagName(tag)}), | |
| 3981 | } | |
| 3982 | }, | |
| 3983 | else => return func.fail("TODO: airUnaryMath ty: {}", .{ty.fmt(pt)}), | |
| 3984 | } | |
| 3985 | ||
| 3986 | break :result MCValue{ .register = dst_reg }; | |
| 3987 | }; | |
| 3988 | ||
| 3729 | 3989 | return func.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3730 | 3990 | } |
| 3731 | 3991 | |
| ... | ... | @@ -3987,6 +4247,10 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3987 | 4247 | }); |
| 3988 | 4248 | } |
| 3989 | 4249 | |
| 4250 | if (field_off == 0) { | |
| 4251 | try func.truncateRegister(field_ty, dst_reg); | |
| 4252 | } | |
| 4253 | ||
| 3990 | 4254 | break :result if (field_off == 0) dst_mcv else try func.copyToNewRegister(inst, dst_mcv); |
| 3991 | 4255 | }, |
| 3992 | 4256 | .load_frame => { |
| ... | ... | @@ -4121,9 +4385,28 @@ fn airFrameAddress(func: *Func, inst: Air.Inst.Index) !void { |
| 4121 | 4385 | return func.finishAir(inst, dst_mcv, .{ .none, .none, .none }); |
| 4122 | 4386 | } |
| 4123 | 4387 | |
| 4124 | fn airFence(func: *Func) !void { | |
| 4125 | return func.fail("TODO implement fence() for {}", .{func.target.cpu.arch}); | |
| 4126 | //return func.finishAirBookkeeping(); | |
| 4388 | fn airFence(func: *Func, inst: Air.Inst.Index) !void { | |
| 4389 | const order = func.air.instructions.items(.data)[@intFromEnum(inst)].fence; | |
| 4390 | const pred: Mir.Barrier, const succ: Mir.Barrier = switch (order) { | |
| 4391 | .unordered, .monotonic => unreachable, | |
| 4392 | .acquire => .{ .r, .rw }, | |
| 4393 | .release => .{ .rw, .r }, | |
| 4394 | .acq_rel => .{ .rw, .rw }, | |
| 4395 | .seq_cst => .{ .rw, .rw }, | |
| 4396 | }; | |
| 4397 | ||
| 4398 | _ = try func.addInst(.{ | |
| 4399 | .tag = .pseudo, | |
| 4400 | .ops = .pseudo_fence, | |
| 4401 | .data = .{ | |
| 4402 | .fence = .{ | |
| 4403 | .pred = pred, | |
| 4404 | .succ = succ, | |
| 4405 | .fm = if (order == .acq_rel) .tso else .none, | |
| 4406 | }, | |
| 4407 | }, | |
| 4408 | }); | |
| 4409 | return func.finishAirBookkeeping(); | |
| 4127 | 4410 | } |
| 4128 | 4411 | |
| 4129 | 4412 | fn airCall(func: *Func, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| ... | ... | @@ -4374,7 +4657,27 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void { |
| 4374 | 4657 | .none => {}, |
| 4375 | 4658 | .register, |
| 4376 | 4659 | .register_pair, |
| 4377 | => try func.genCopy(ret_ty, func.ret_mcv.short, .{ .air_ref = un_op }), | |
| 4660 | => { | |
| 4661 | if (ret_ty.isVector(zcu)) { | |
| 4662 | const bit_size = ret_ty.totalVectorBits(pt); | |
| 4663 | ||
| 4664 | // set the vtype to hold the entire vector's contents in a single element | |
| 4665 | try func.setVl(.zero, 0, .{ | |
| 4666 | .vsew = switch (bit_size) { | |
| 4667 | 8 => .@"8", | |
| 4668 | 16 => .@"16", | |
| 4669 | 32 => .@"32", | |
| 4670 | 64 => .@"64", | |
| 4671 | else => unreachable, | |
| 4672 | }, | |
| 4673 | .vlmul = .m1, | |
| 4674 | .vma = true, | |
| 4675 | .vta = true, | |
| 4676 | }); | |
| 4677 | } | |
| 4678 | ||
| 4679 | try func.genCopy(ret_ty, func.ret_mcv.short, .{ .air_ref = un_op }); | |
| 4680 | }, | |
| 4378 | 4681 | .indirect => |reg_off| { |
| 4379 | 4682 | try func.register_manager.getReg(reg_off.reg, null); |
| 4380 | 4683 | const lock = func.register_manager.lockRegAssumeUnused(reg_off.reg); |
| ... | ... | @@ -5224,8 +5527,6 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { |
| 5224 | 5527 | const inputs: []const Air.Inst.Ref = @ptrCast(func.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 5225 | 5528 | extra_i += inputs.len; |
| 5226 | 5529 | |
| 5227 | log.debug("airAsm input: {any}", .{inputs}); | |
| 5228 | ||
| 5229 | 5530 | const dead = !is_volatile and func.liveness.isUnused(inst); |
| 5230 | 5531 | const result: MCValue = if (dead) .unreach else result: { |
| 5231 | 5532 | if (outputs.len > 1) { |
| ... | ... | @@ -5599,18 +5900,34 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5599 | 5900 | const zcu = pt.zcu; |
| 5600 | 5901 | const abi_size: u32 = @intCast(ty.abiSize(pt)); |
| 5601 | 5902 | |
| 5602 | if (abi_size > 8) return std.debug.panic("tried to set reg with size {}", .{abi_size}); | |
| 5603 | ||
| 5903 | const max_size: u32 = switch (reg.class()) { | |
| 5904 | .int => 64, | |
| 5905 | .float => if (func.hasFeature(.d)) 64 else 32, | |
| 5906 | .vector => 64, // TODO: calculate it from avl * vsew | |
| 5907 | }; | |
| 5908 | if (abi_size > max_size) return std.debug.panic("tried to set reg with size {}", .{abi_size}); | |
| 5604 | 5909 | const dst_reg_class = reg.class(); |
| 5605 | 5910 | |
| 5606 | 5911 | switch (src_mcv) { |
| 5607 | .dead => unreachable, | |
| 5608 | .unreach, .none => return, // Nothing to do. | |
| 5609 | .undef => { | |
| 5912 | .unreach, | |
| 5913 | .none, | |
| 5914 | .dead, | |
| 5915 | => unreachable, | |
| 5916 | .undef => |sym_index| { | |
| 5610 | 5917 | if (!func.wantSafety()) |
| 5611 | return; // The already existing value will do just fine. | |
| 5612 | // Write the debug undefined value. | |
| 5613 | return func.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); | |
| 5918 | return; | |
| 5919 | ||
| 5920 | if (sym_index) |index| { | |
| 5921 | return func.genSetReg(ty, reg, .{ .load_symbol = .{ .sym = index } }); | |
| 5922 | } | |
| 5923 | ||
| 5924 | switch (abi_size) { | |
| 5925 | 1 => return func.genSetReg(ty, reg, .{ .immediate = 0xAA }), | |
| 5926 | 2 => return func.genSetReg(ty, reg, .{ .immediate = 0xAAAA }), | |
| 5927 | 3...4 => return func.genSetReg(ty, reg, .{ .immediate = 0xAAAAAAAA }), | |
| 5928 | 5...8 => return func.genSetReg(ty, reg, .{ .immediate = 0xAAAAAAAAAAAAAAAA }), | |
| 5929 | else => unreachable, | |
| 5930 | } | |
| 5614 | 5931 | }, |
| 5615 | 5932 | .immediate => |unsigned_x| { |
| 5616 | 5933 | assert(dst_reg_class == .int); |
| ... | ... | @@ -5688,11 +6005,25 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5688 | 6005 | if (src_reg.id() == reg.id()) |
| 5689 | 6006 | return; |
| 5690 | 6007 | |
| 5691 | const src_reg_class = src_reg.class(); | |
| 5692 | ||
| 5693 | if (src_reg_class == .float and dst_reg_class == .int) { | |
| 5694 | // to move from float -> int, we use FMV.X.W | |
| 5695 | return func.fail("TODO: genSetReg float -> int", .{}); | |
| 6008 | // there is no instruction for loading the contents of a vector register | |
| 6009 | // into an integer register, however we can cheat a bit by setting the element | |
| 6010 | // size to the total size of the vector, and vmv.x.s will work then | |
| 6011 | if (src_reg.class() == .vector) { | |
| 6012 | try func.setVl(.zero, 0, .{ | |
| 6013 | .vsew = switch (ty.totalVectorBits(pt)) { | |
| 6014 | 8 => .@"8", | |
| 6015 | 16 => .@"16", | |
| 6016 | 32 => .@"32", | |
| 6017 | 64 => .@"64", | |
| 6018 | else => |vec_bits| return func.fail("TODO: genSetReg vec -> {s} bits {d}", .{ | |
| 6019 | @tagName(reg.class()), | |
| 6020 | vec_bits, | |
| 6021 | }), | |
| 6022 | }, | |
| 6023 | .vlmul = .m1, | |
| 6024 | .vta = true, | |
| 6025 | .vma = true, | |
| 6026 | }); | |
| 5696 | 6027 | } |
| 5697 | 6028 | |
| 5698 | 6029 | // mv reg, src_reg |
| ... | ... | @@ -5707,21 +6038,31 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5707 | 6038 | }, |
| 5708 | 6039 | .register_pair => return func.fail("genSetReg should we allow reg -> reg_pair?", .{}), |
| 5709 | 6040 | .load_frame => |frame| { |
| 5710 | _ = try func.addInst(.{ | |
| 5711 | .tag = .pseudo, | |
| 5712 | .ops = .pseudo_load_rm, | |
| 5713 | .data = .{ .rm = .{ | |
| 5714 | .r = reg, | |
| 5715 | .m = .{ | |
| 5716 | .base = .{ .frame = frame.index }, | |
| 5717 | .mod = .{ | |
| 5718 | .size = func.memSize(ty), | |
| 5719 | .unsigned = ty.isUnsignedInt(zcu), | |
| 5720 | .disp = frame.off, | |
| 6041 | if (reg.class() == .vector) { | |
| 6042 | // vectors don't support an offset memory load so we need to put the true | |
| 6043 | // address into a register before loading from it. | |
| 6044 | const addr_reg, const addr_lock = try func.allocReg(.int); | |
| 6045 | defer func.register_manager.unlockReg(addr_lock); | |
| 6046 | ||
| 6047 | try func.genCopy(ty, .{ .register = addr_reg }, src_mcv.address()); | |
| 6048 | try func.genCopy(ty, .{ .register = reg }, .{ .indirect = .{ .reg = addr_reg } }); | |
| 6049 | } else { | |
| 6050 | _ = try func.addInst(.{ | |
| 6051 | .tag = .pseudo, | |
| 6052 | .ops = .pseudo_load_rm, | |
| 6053 | .data = .{ .rm = .{ | |
| 6054 | .r = reg, | |
| 6055 | .m = .{ | |
| 6056 | .base = .{ .frame = frame.index }, | |
| 6057 | .mod = .{ | |
| 6058 | .size = func.memSize(ty), | |
| 6059 | .unsigned = ty.isUnsignedInt(zcu), | |
| 6060 | .disp = frame.off, | |
| 6061 | }, | |
| 5721 | 6062 | }, |
| 5722 | }, | |
| 5723 | } }, | |
| 5724 | }); | |
| 6063 | } }, | |
| 6064 | }); | |
| 6065 | } | |
| 5725 | 6066 | }, |
| 5726 | 6067 | .memory => |addr| { |
| 5727 | 6068 | try func.genSetReg(ty, reg, .{ .immediate = addr }); |
| ... | ... | @@ -5740,45 +6081,89 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5740 | 6081 | _ = try func.addInst(.{ |
| 5741 | 6082 | .tag = .pseudo, |
| 5742 | 6083 | .ops = .pseudo_lea_rm, |
| 5743 | .data = .{ .rm = .{ | |
| 5744 | .r = reg, | |
| 5745 | .m = switch (src_mcv) { | |
| 5746 | .register_offset => |reg_off| .{ | |
| 5747 | .base = .{ .reg = reg_off.reg }, | |
| 5748 | .mod = .{ | |
| 5749 | .size = func.memSize(ty), | |
| 5750 | .disp = reg_off.off, | |
| 5751 | .unsigned = false, | |
| 6084 | .data = .{ | |
| 6085 | .rm = .{ | |
| 6086 | .r = reg, | |
| 6087 | .m = switch (src_mcv) { | |
| 6088 | .register_offset => |reg_off| .{ | |
| 6089 | .base = .{ .reg = reg_off.reg }, | |
| 6090 | .mod = .{ | |
| 6091 | .size = .byte, // the size doesn't matter | |
| 6092 | .disp = reg_off.off, | |
| 6093 | .unsigned = false, | |
| 6094 | }, | |
| 5752 | 6095 | }, |
| 5753 | }, | |
| 5754 | .lea_frame => |frame| .{ | |
| 5755 | .base = .{ .frame = frame.index }, | |
| 5756 | .mod = .{ | |
| 5757 | .size = func.memSize(ty), | |
| 5758 | .disp = frame.off, | |
| 5759 | .unsigned = false, | |
| 6096 | .lea_frame => |frame| .{ | |
| 6097 | .base = .{ .frame = frame.index }, | |
| 6098 | .mod = .{ | |
| 6099 | .size = .byte, // the size doesn't matter | |
| 6100 | .disp = frame.off, | |
| 6101 | .unsigned = false, | |
| 6102 | }, | |
| 5760 | 6103 | }, |
| 6104 | else => unreachable, | |
| 5761 | 6105 | }, |
| 5762 | else => unreachable, | |
| 5763 | 6106 | }, |
| 5764 | } }, | |
| 6107 | }, | |
| 5765 | 6108 | }); |
| 5766 | 6109 | }, |
| 5767 | 6110 | .indirect => |reg_off| { |
| 5768 | const float_class = dst_reg_class == .float; | |
| 6111 | const load_tag: Mir.Inst.Tag = switch (reg.class()) { | |
| 6112 | .float => switch (abi_size) { | |
| 6113 | 1 => unreachable, // Zig does not support 8-bit floats | |
| 6114 | 2 => return func.fail("TODO: genSetReg indirect 16-bit float", .{}), | |
| 6115 | 4 => .flw, | |
| 6116 | 8 => .fld, | |
| 6117 | else => return std.debug.panic("TODO: genSetReg for float size {d}", .{abi_size}), | |
| 6118 | }, | |
| 6119 | .int => switch (abi_size) { | |
| 6120 | 1 => .lb, | |
| 6121 | 2 => .lh, | |
| 6122 | 4 => .lw, | |
| 6123 | 8 => .ld, | |
| 6124 | else => return std.debug.panic("TODO: genSetReg for int size {d}", .{abi_size}), | |
| 6125 | }, | |
| 6126 | .vector => { | |
| 6127 | assert(reg_off.off == 0); | |
| 6128 | ||
| 6129 | // There is no vector instruction for loading with an offset to a base register, | |
| 6130 | // so we need to get an offset register containing the address of the vector first | |
| 6131 | // and load from it. | |
| 6132 | const len = ty.vectorLen(zcu); | |
| 6133 | const elem_ty = ty.childType(zcu); | |
| 6134 | const elem_size = elem_ty.abiSize(pt); | |
| 6135 | ||
| 6136 | try func.setVl(.zero, len, .{ | |
| 6137 | .vsew = switch (elem_size) { | |
| 6138 | 1 => .@"8", | |
| 6139 | 2 => .@"16", | |
| 6140 | 4 => .@"32", | |
| 6141 | 8 => .@"64", | |
| 6142 | else => unreachable, | |
| 6143 | }, | |
| 6144 | .vlmul = .m1, | |
| 6145 | .vma = true, | |
| 6146 | .vta = true, | |
| 6147 | }); | |
| 5769 | 6148 | |
| 5770 | const load_tag: Mir.Inst.Tag = switch (abi_size) { | |
| 5771 | 1 => if (float_class) | |
| 5772 | unreachable // Zig does not support 8-bit floats | |
| 5773 | else | |
| 5774 | .lb, | |
| 5775 | 2 => if (float_class) | |
| 5776 | return func.fail("TODO: genSetReg indirect 16-bit float", .{}) | |
| 5777 | else | |
| 5778 | .lh, | |
| 5779 | 4 => if (float_class) .flw else .lw, | |
| 5780 | 8 => if (float_class) .fld else .ld, | |
| 5781 | else => return std.debug.panic("TODO: genSetReg for size {d}", .{abi_size}), | |
| 6149 | _ = try func.addInst(.{ | |
| 6150 | .tag = .pseudo, | |
| 6151 | .ops = .pseudo_load_rm, | |
| 6152 | .data = .{ .rm = .{ | |
| 6153 | .r = reg, | |
| 6154 | .m = .{ | |
| 6155 | .base = .{ .reg = reg_off.reg }, | |
| 6156 | .mod = .{ | |
| 6157 | .size = func.memSize(elem_ty), | |
| 6158 | .unsigned = false, | |
| 6159 | .disp = 0, | |
| 6160 | }, | |
| 6161 | }, | |
| 6162 | } }, | |
| 6163 | }); | |
| 6164 | ||
| 6165 | return; | |
| 6166 | }, | |
| 5782 | 6167 | }; |
| 5783 | 6168 | |
| 5784 | 6169 | _ = try func.addInst(.{ |
| ... | ... | @@ -5793,7 +6178,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5793 | 6178 | }, |
| 5794 | 6179 | .lea_symbol => |sym_off| { |
| 5795 | 6180 | assert(sym_off.off == 0); |
| 5796 | ||
| 5797 | 6181 | const atom_index = try func.symbolIndex(); |
| 5798 | 6182 | |
| 5799 | 6183 | _ = try func.addInst(.{ |
| ... | ... | @@ -5826,6 +6210,8 @@ fn genSetMem( |
| 5826 | 6210 | src_mcv: MCValue, |
| 5827 | 6211 | ) InnerError!void { |
| 5828 | 6212 | const pt = func.pt; |
| 6213 | const zcu = pt.zcu; | |
| 6214 | ||
| 5829 | 6215 | const abi_size: u32 = @intCast(ty.abiSize(pt)); |
| 5830 | 6216 | const dst_ptr_mcv: MCValue = switch (base) { |
| 5831 | 6217 | .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, |
| ... | ... | @@ -5838,11 +6224,17 @@ fn genSetMem( |
| 5838 | 6224 | .dead, |
| 5839 | 6225 | .reserved_frame, |
| 5840 | 6226 | => unreachable, |
| 5841 | .undef => try func.genInlineMemset( | |
| 5842 | dst_ptr_mcv, | |
| 5843 | src_mcv, | |
| 5844 | .{ .immediate = abi_size }, | |
| 5845 | ), | |
| 6227 | .undef => |sym_index| { | |
| 6228 | if (sym_index) |index| { | |
| 6229 | return func.genSetMem(base, disp, ty, .{ .load_symbol = .{ .sym = index } }); | |
| 6230 | } | |
| 6231 | ||
| 6232 | try func.genInlineMemset( | |
| 6233 | dst_ptr_mcv, | |
| 6234 | src_mcv, | |
| 6235 | .{ .immediate = abi_size }, | |
| 6236 | ); | |
| 6237 | }, | |
| 5846 | 6238 | .register_offset, |
| 5847 | 6239 | .memory, |
| 5848 | 6240 | .indirect, |
| ... | ... | @@ -5853,12 +6245,12 @@ fn genSetMem( |
| 5853 | 6245 | => switch (abi_size) { |
| 5854 | 6246 | 0 => {}, |
| 5855 | 6247 | 1, 2, 4, 8 => { |
| 5856 | // no matter what type, it should use an integer register | |
| 5857 | const src_reg = try func.copyToTmpRegister(Type.usize, src_mcv); | |
| 5858 | const src_lock = func.register_manager.lockRegAssumeUnused(src_reg); | |
| 6248 | const reg = try func.register_manager.allocReg(null, abi.Registers.Integer.temporary); | |
| 6249 | const src_lock = func.register_manager.lockRegAssumeUnused(reg); | |
| 5859 | 6250 | defer func.register_manager.unlockReg(src_lock); |
| 5860 | 6251 | |
| 5861 | try func.genSetMem(base, disp, ty, .{ .register = src_reg }); | |
| 6252 | try func.genSetReg(ty, reg, src_mcv); | |
| 6253 | try func.genSetMem(base, disp, ty, .{ .register = reg }); | |
| 5862 | 6254 | }, |
| 5863 | 6255 | else => try func.genInlineMemcpy( |
| 5864 | 6256 | dst_ptr_mcv, |
| ... | ... | @@ -5867,6 +6259,44 @@ fn genSetMem( |
| 5867 | 6259 | ), |
| 5868 | 6260 | }, |
| 5869 | 6261 | .register => |reg| { |
| 6262 | if (reg.class() == .vector) { | |
| 6263 | const addr_reg = try func.copyToTmpRegister(Type.usize, dst_ptr_mcv); | |
| 6264 | ||
| 6265 | const num_elem = ty.vectorLen(zcu); | |
| 6266 | const elem_size = ty.childType(zcu).bitSize(pt); | |
| 6267 | ||
| 6268 | try func.setVl(.zero, num_elem, .{ | |
| 6269 | .vsew = switch (elem_size) { | |
| 6270 | 8 => .@"8", | |
| 6271 | 16 => .@"16", | |
| 6272 | 32 => .@"32", | |
| 6273 | 64 => .@"64", | |
| 6274 | else => unreachable, | |
| 6275 | }, | |
| 6276 | .vlmul = .m1, | |
| 6277 | .vma = true, | |
| 6278 | .vta = true, | |
| 6279 | }); | |
| 6280 | ||
| 6281 | _ = try func.addInst(.{ | |
| 6282 | .tag = .pseudo, | |
| 6283 | .ops = .pseudo_store_rm, | |
| 6284 | .data = .{ .rm = .{ | |
| 6285 | .r = reg, | |
| 6286 | .m = .{ | |
| 6287 | .base = .{ .reg = addr_reg }, | |
| 6288 | .mod = .{ | |
| 6289 | .disp = 0, | |
| 6290 | .size = func.memSize(ty.childType(zcu)), | |
| 6291 | .unsigned = false, | |
| 6292 | }, | |
| 6293 | }, | |
| 6294 | } }, | |
| 6295 | }); | |
| 6296 | ||
| 6297 | return; | |
| 6298 | } | |
| 6299 | ||
| 5870 | 6300 | const mem_size = switch (base) { |
| 5871 | 6301 | .frame => |base_fi| mem_size: { |
| 5872 | 6302 | assert(disp >= 0); |
| ... | ... | @@ -6042,19 +6472,161 @@ fn airCmpxchg(func: *Func, inst: Air.Inst.Index) !void { |
| 6042 | 6472 | } |
| 6043 | 6473 | |
| 6044 | 6474 | fn airAtomicRmw(func: *Func, inst: Air.Inst.Index) !void { |
| 6045 | _ = inst; | |
| 6046 | return func.fail("TODO implement airCmpxchg for {}", .{func.target.cpu.arch}); | |
| 6475 | const pt = func.pt; | |
| 6476 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 6477 | const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 6478 | ||
| 6479 | const op = extra.op(); | |
| 6480 | const order = extra.ordering(); | |
| 6481 | ||
| 6482 | const ptr_ty = func.typeOf(pl_op.operand); | |
| 6483 | const ptr_mcv = try func.resolveInst(pl_op.operand); | |
| 6484 | ||
| 6485 | const val_ty = func.typeOf(extra.operand); | |
| 6486 | const val_size = val_ty.abiSize(pt); | |
| 6487 | const val_mcv = try func.resolveInst(extra.operand); | |
| 6488 | ||
| 6489 | if (!math.isPowerOfTwo(val_size)) | |
| 6490 | return func.fail("TODO: airAtomicRmw non-pow 2", .{}); | |
| 6491 | ||
| 6492 | switch (val_ty.zigTypeTag(pt.zcu)) { | |
| 6493 | .Int => {}, | |
| 6494 | inline .Bool, .Float, .Enum, .Pointer => |ty| return func.fail("TODO: airAtomicRmw {s}", .{@tagName(ty)}), | |
| 6495 | else => unreachable, | |
| 6496 | } | |
| 6497 | ||
| 6498 | switch (val_size) { | |
| 6499 | 1, 2 => return func.fail("TODO: airAtomicRmw Int {}", .{val_size}), | |
| 6500 | 4, 8 => {}, | |
| 6501 | else => unreachable, | |
| 6502 | } | |
| 6503 | ||
| 6504 | const ptr_register, const ptr_lock = try func.promoteReg(ptr_ty, ptr_mcv); | |
| 6505 | defer if (ptr_lock) |lock| func.register_manager.unlockReg(lock); | |
| 6506 | ||
| 6507 | const val_register, const val_lock = try func.promoteReg(val_ty, val_mcv); | |
| 6508 | defer if (val_lock) |lock| func.register_manager.unlockReg(lock); | |
| 6509 | ||
| 6510 | const result_mcv = try func.allocRegOrMem(val_ty, inst, true); | |
| 6511 | assert(result_mcv == .register); // should fit into 8 bytes | |
| 6512 | ||
| 6513 | const aq, const rl = switch (order) { | |
| 6514 | .unordered => unreachable, | |
| 6515 | .monotonic => .{ false, false }, | |
| 6516 | .acquire => .{ true, false }, | |
| 6517 | .release => .{ false, true }, | |
| 6518 | .acq_rel => .{ true, true }, | |
| 6519 | .seq_cst => .{ true, true }, | |
| 6520 | }; | |
| 6521 | ||
| 6522 | _ = try func.addInst(.{ | |
| 6523 | .tag = .pseudo, | |
| 6524 | .ops = .pseudo_amo, | |
| 6525 | .data = .{ .amo = .{ | |
| 6526 | .rd = result_mcv.register, | |
| 6527 | .rs1 = ptr_register, | |
| 6528 | .rs2 = val_register, | |
| 6529 | .aq = if (aq) .aq else .none, | |
| 6530 | .rl = if (rl) .rl else .none, | |
| 6531 | .op = switch (op) { | |
| 6532 | .Xchg => .SWAP, | |
| 6533 | .Add => .ADD, | |
| 6534 | .Sub => return func.fail("TODO: airAtomicRmw SUB", .{}), | |
| 6535 | .And => .AND, | |
| 6536 | .Nand => return func.fail("TODO: airAtomicRmw NAND", .{}), | |
| 6537 | .Or => .OR, | |
| 6538 | .Xor => .XOR, | |
| 6539 | .Max => .MAX, | |
| 6540 | .Min => .MIN, | |
| 6541 | }, | |
| 6542 | .ty = val_ty, | |
| 6543 | } }, | |
| 6544 | }); | |
| 6545 | ||
| 6546 | return func.finishAir(inst, result_mcv, .{ pl_op.operand, extra.operand, .none }); | |
| 6047 | 6547 | } |
| 6048 | 6548 | |
| 6049 | 6549 | fn airAtomicLoad(func: *Func, inst: Air.Inst.Index) !void { |
| 6050 | _ = inst; | |
| 6051 | return func.fail("TODO implement airAtomicLoad for {}", .{func.target.cpu.arch}); | |
| 6550 | const zcu = func.pt.zcu; | |
| 6551 | const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; | |
| 6552 | const order: std.builtin.AtomicOrder = atomic_load.order; | |
| 6553 | ||
| 6554 | const ptr_ty = func.typeOf(atomic_load.ptr); | |
| 6555 | const elem_ty = ptr_ty.childType(zcu); | |
| 6556 | const ptr_mcv = try func.resolveInst(atomic_load.ptr); | |
| 6557 | ||
| 6558 | const result_mcv = try func.allocRegOrMem(elem_ty, inst, true); | |
| 6559 | assert(result_mcv == .register); // should be less than 8 bytes | |
| 6560 | ||
| 6561 | if (order == .seq_cst) { | |
| 6562 | _ = try func.addInst(.{ | |
| 6563 | .tag = .pseudo, | |
| 6564 | .ops = .pseudo_fence, | |
| 6565 | .data = .{ | |
| 6566 | .fence = .{ | |
| 6567 | .pred = .rw, | |
| 6568 | .succ = .rw, | |
| 6569 | .fm = .none, | |
| 6570 | }, | |
| 6571 | }, | |
| 6572 | }); | |
| 6573 | } | |
| 6574 | ||
| 6575 | try func.load(result_mcv, ptr_mcv, ptr_ty); | |
| 6576 | ||
| 6577 | switch (order) { | |
| 6578 | // Don't guarnetee other memory operations to be ordered after the load. | |
| 6579 | .unordered => {}, | |
| 6580 | .monotonic => {}, | |
| 6581 | // Make sure all previous reads happen before any reading or writing accurs. | |
| 6582 | .seq_cst, .acquire => { | |
| 6583 | _ = try func.addInst(.{ | |
| 6584 | .tag = .pseudo, | |
| 6585 | .ops = .pseudo_fence, | |
| 6586 | .data = .{ | |
| 6587 | .fence = .{ | |
| 6588 | .pred = .r, | |
| 6589 | .succ = .rw, | |
| 6590 | .fm = .none, | |
| 6591 | }, | |
| 6592 | }, | |
| 6593 | }); | |
| 6594 | }, | |
| 6595 | else => unreachable, | |
| 6596 | } | |
| 6597 | ||
| 6598 | return func.finishAir(inst, result_mcv, .{ atomic_load.ptr, .none, .none }); | |
| 6052 | 6599 | } |
| 6053 | 6600 | |
| 6054 | 6601 | fn airAtomicStore(func: *Func, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { |
| 6055 | _ = inst; | |
| 6056 | _ = order; | |
| 6057 | return func.fail("TODO implement airAtomicStore for {}", .{func.target.cpu.arch}); | |
| 6602 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6603 | ||
| 6604 | const ptr_ty = func.typeOf(bin_op.lhs); | |
| 6605 | const ptr_mcv = try func.resolveInst(bin_op.lhs); | |
| 6606 | ||
| 6607 | const val_ty = func.typeOf(bin_op.rhs); | |
| 6608 | const val_mcv = try func.resolveInst(bin_op.rhs); | |
| 6609 | ||
| 6610 | switch (order) { | |
| 6611 | .unordered, .monotonic => {}, | |
| 6612 | .release, .seq_cst => { | |
| 6613 | _ = try func.addInst(.{ | |
| 6614 | .tag = .pseudo, | |
| 6615 | .ops = .pseudo_fence, | |
| 6616 | .data = .{ | |
| 6617 | .fence = .{ | |
| 6618 | .pred = .rw, | |
| 6619 | .succ = .w, | |
| 6620 | .fm = .none, | |
| 6621 | }, | |
| 6622 | }, | |
| 6623 | }); | |
| 6624 | }, | |
| 6625 | else => unreachable, | |
| 6626 | } | |
| 6627 | ||
| 6628 | try func.store(ptr_mcv, val_mcv, ptr_ty, val_ty); | |
| 6629 | return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 6058 | 6630 | } |
| 6059 | 6631 | |
| 6060 | 6632 | fn airMemset(func: *Func, inst: Air.Inst.Index, safety: bool) !void { |
| ... | ... | @@ -6441,18 +7013,39 @@ fn getResolvedInstValue(func: *Func, inst: Air.Inst.Index) *InstTracking { |
| 6441 | 7013 | |
| 6442 | 7014 | fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { |
| 6443 | 7015 | const pt = func.pt; |
| 6444 | const zcu = pt.zcu; | |
| 7016 | const gpa = func.gpa; | |
| 7017 | ||
| 7018 | const owner_decl_index = pt.zcu.funcOwnerDeclIndex(func.func_index); | |
| 7019 | const lf = func.bin_file; | |
| 7020 | const src_loc = func.src_loc; | |
| 7021 | ||
| 7022 | if (val.isUndef(pt.zcu)) { | |
| 7023 | const local_sym_index = lf.lowerUnnamedConst(pt, val, owner_decl_index) catch |err| { | |
| 7024 | const msg = try ErrorMsg.create(gpa, src_loc, "lowering unnamed undefined constant failed: {s}", .{@errorName(err)}); | |
| 7025 | func.err_msg = msg; | |
| 7026 | return error.CodegenFail; | |
| 7027 | }; | |
| 7028 | switch (lf.tag) { | |
| 7029 | .elf => { | |
| 7030 | const elf_file = lf.cast(link.File.Elf).?; | |
| 7031 | const local = elf_file.symbol(local_sym_index); | |
| 7032 | return MCValue{ .undef = local.esym_index }; | |
| 7033 | }, | |
| 7034 | else => unreachable, | |
| 7035 | } | |
| 7036 | } | |
| 7037 | ||
| 6445 | 7038 | const result = try codegen.genTypedValue( |
| 6446 | func.bin_file, | |
| 7039 | lf, | |
| 6447 | 7040 | pt, |
| 6448 | func.src_loc, | |
| 7041 | src_loc, | |
| 6449 | 7042 | val, |
| 6450 | zcu.funcOwnerDeclIndex(func.func_index), | |
| 7043 | owner_decl_index, | |
| 6451 | 7044 | ); |
| 6452 | 7045 | const mcv: MCValue = switch (result) { |
| 6453 | 7046 | .mcv => |mcv| switch (mcv) { |
| 6454 | 7047 | .none => .none, |
| 6455 | .undef => .undef, | |
| 7048 | .undef => unreachable, | |
| 6456 | 7049 | .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, |
| 6457 | 7050 | .immediate => |imm| .{ .immediate = imm }, |
| 6458 | 7051 | .memory => |addr| .{ .memory = addr }, |
| ... | ... | @@ -6670,9 +7263,7 @@ fn parseRegName(name: []const u8) ?Register { |
| 6670 | 7263 | } |
| 6671 | 7264 | |
| 6672 | 7265 | fn typeOf(func: *Func, inst: Air.Inst.Ref) Type { |
| 6673 | const pt = func.pt; | |
| 6674 | const zcu = pt.zcu; | |
| 6675 | return func.air.typeOf(inst, &zcu.intern_pool); | |
| 7266 | return func.air.typeOf(inst, &func.pt.zcu.intern_pool); | |
| 6676 | 7267 | } |
| 6677 | 7268 | |
| 6678 | 7269 | fn typeOfIndex(func: *Func, inst: Air.Inst.Index) Type { |
src/arch/riscv64/Emit.zig+2-2| ... | ... | @@ -26,7 +26,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 26 | 26 | mir_index, |
| 27 | 27 | @intCast(emit.code.items.len), |
| 28 | 28 | ); |
| 29 | const lowered = try emit.lower.lowerMir(mir_index); | |
| 29 | const lowered = try emit.lower.lowerMir(mir_index, .{ .allow_frame_locs = true }); | |
| 30 | 30 | var lowered_relocs = lowered.relocs; |
| 31 | 31 | for (lowered.insts, 0..) |lowered_inst, lowered_index| { |
| 32 | 32 | const start_offset: u32 = @intCast(emit.code.items.len); |
| ... | ... | @@ -75,7 +75,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 75 | 75 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, |
| 76 | 76 | .r_addend = 0, |
| 77 | 77 | }); |
| 78 | } else return emit.fail("TODO: load_symbol_reloc non-ELF", .{}); | |
| 78 | } else unreachable; | |
| 79 | 79 | }, |
| 80 | 80 | .call_extern_fn_reloc => |symbol| { |
| 81 | 81 | if (emit.bin_file.cast(link.File.Elf)) |elf_file| { |
src/arch/riscv64/Encoding.zig+376-40| ... | ... | @@ -2,37 +2,55 @@ mnemonic: Mnemonic, |
| 2 | 2 | data: Data, |
| 3 | 3 | |
| 4 | 4 | const OpCode = enum(u7) { |
| 5 | OP = 0b0110011, | |
| 5 | LOAD = 0b0000011, | |
| 6 | LOAD_FP = 0b0000111, | |
| 7 | MISC_MEM = 0b0001111, | |
| 6 | 8 | OP_IMM = 0b0010011, |
| 9 | AUIPC = 0b0010111, | |
| 7 | 10 | OP_IMM_32 = 0b0011011, |
| 8 | OP_32 = 0b0111011, | |
| 9 | ||
| 10 | BRANCH = 0b1100011, | |
| 11 | LOAD = 0b0000011, | |
| 12 | 11 | STORE = 0b0100011, |
| 13 | SYSTEM = 0b1110011, | |
| 14 | ||
| 15 | OP_FP = 0b1010011, | |
| 16 | LOAD_FP = 0b0000111, | |
| 17 | 12 | STORE_FP = 0b0100111, |
| 18 | ||
| 19 | JALR = 0b1100111, | |
| 20 | AUIPC = 0b0010111, | |
| 13 | AMO = 0b0101111, | |
| 14 | OP_V = 0b1010111, | |
| 15 | OP = 0b0110011, | |
| 16 | OP_32 = 0b0111011, | |
| 21 | 17 | LUI = 0b0110111, |
| 18 | MADD = 0b1000011, | |
| 19 | MSUB = 0b1000111, | |
| 20 | NMSUB = 0b1001011, | |
| 21 | NMADD = 0b1001111, | |
| 22 | OP_FP = 0b1010011, | |
| 23 | OP_IMM_64 = 0b1011011, | |
| 24 | BRANCH = 0b1100011, | |
| 25 | JALR = 0b1100111, | |
| 22 | 26 | JAL = 0b1101111, |
| 23 | NONE = 0b0000000, | |
| 27 | SYSTEM = 0b1110011, | |
| 28 | OP_64 = 0b1111011, | |
| 29 | NONE = 0b00000000, | |
| 24 | 30 | }; |
| 25 | 31 | |
| 26 | const Fmt = enum(u2) { | |
| 32 | const FpFmt = enum(u2) { | |
| 27 | 33 | /// 32-bit single-precision |
| 28 | 34 | S = 0b00, |
| 29 | 35 | /// 64-bit double-precision |
| 30 | 36 | D = 0b01, |
| 31 | _reserved = 0b10, | |
| 37 | ||
| 38 | // H = 0b10, unused in the G extension | |
| 39 | ||
| 32 | 40 | /// 128-bit quad-precision |
| 33 | 41 | Q = 0b11, |
| 34 | 42 | }; |
| 35 | 43 | |
| 44 | const AmoWidth = enum(u3) { | |
| 45 | W = 0b010, | |
| 46 | D = 0b011, | |
| 47 | }; | |
| 48 | ||
| 49 | const FenceMode = enum(u4) { | |
| 50 | none = 0b0000, | |
| 51 | tso = 0b1000, | |
| 52 | }; | |
| 53 | ||
| 36 | 54 | const Enc = struct { |
| 37 | 55 | opcode: OpCode, |
| 38 | 56 | |
| ... | ... | @@ -42,11 +60,19 @@ const Enc = struct { |
| 42 | 60 | funct3: u3, |
| 43 | 61 | funct7: u7, |
| 44 | 62 | }, |
| 63 | amo: struct { | |
| 64 | funct5: u5, | |
| 65 | width: AmoWidth, | |
| 66 | }, | |
| 67 | fence: struct { | |
| 68 | funct3: u3, | |
| 69 | fm: FenceMode, | |
| 70 | }, | |
| 45 | 71 | /// funct5 + rm + fmt |
| 46 | 72 | fmt: struct { |
| 47 | 73 | funct5: u5, |
| 48 | 74 | rm: u3, |
| 49 | fmt: Fmt, | |
| 75 | fmt: FpFmt, | |
| 50 | 76 | }, |
| 51 | 77 | /// funct3 |
| 52 | 78 | f: struct { |
| ... | ... | @@ -58,9 +84,55 @@ const Enc = struct { |
| 58 | 84 | funct3: u3, |
| 59 | 85 | has_5: bool, |
| 60 | 86 | }, |
| 87 | vecls: struct { | |
| 88 | width: VecWidth, | |
| 89 | umop: Umop, | |
| 90 | vm: bool, | |
| 91 | mop: Mop, | |
| 92 | mew: bool, | |
| 93 | nf: u3, | |
| 94 | }, | |
| 95 | vecmath: struct { | |
| 96 | vm: bool, | |
| 97 | funct6: u6, | |
| 98 | funct3: VecType, | |
| 99 | }, | |
| 61 | 100 | /// U-type |
| 62 | 101 | none, |
| 63 | 102 | }, |
| 103 | ||
| 104 | const Mop = enum(u2) { | |
| 105 | unit = 0b00, | |
| 106 | unord = 0b01, | |
| 107 | stride = 0b10, | |
| 108 | ord = 0b11, | |
| 109 | }; | |
| 110 | ||
| 111 | const Umop = enum(u5) { | |
| 112 | unit = 0b00000, | |
| 113 | whole = 0b01000, | |
| 114 | mask = 0b01011, | |
| 115 | fault = 0b10000, | |
| 116 | }; | |
| 117 | ||
| 118 | const VecWidth = enum(u3) { | |
| 119 | // zig fmt: off | |
| 120 | @"8" = 0b000, | |
| 121 | @"16" = 0b101, | |
| 122 | @"32" = 0b110, | |
| 123 | @"64" = 0b111, | |
| 124 | // zig fmt: on | |
| 125 | }; | |
| 126 | ||
| 127 | const VecType = enum(u3) { | |
| 128 | OPIVV = 0b000, | |
| 129 | OPFVV = 0b001, | |
| 130 | OPMVV = 0b010, | |
| 131 | OPIVI = 0b011, | |
| 132 | OPIVX = 0b100, | |
| 133 | OPFVF = 0b101, | |
| 134 | OPMVX = 0b110, | |
| 135 | }; | |
| 64 | 136 | }; |
| 65 | 137 | |
| 66 | 138 | pub const Mnemonic = enum { |
| ... | ... | @@ -90,6 +162,9 @@ pub const Mnemonic = enum { |
| 90 | 162 | addi, |
| 91 | 163 | jalr, |
| 92 | 164 | |
| 165 | vsetivli, | |
| 166 | vsetvli, | |
| 167 | ||
| 93 | 168 | // U Type |
| 94 | 169 | lui, |
| 95 | 170 | auipc, |
| ... | ... | @@ -130,6 +205,8 @@ pub const Mnemonic = enum { |
| 130 | 205 | ebreak, |
| 131 | 206 | unimp, |
| 132 | 207 | |
| 208 | csrrs, | |
| 209 | ||
| 133 | 210 | // M extension |
| 134 | 211 | mul, |
| 135 | 212 | mulw, |
| ... | ... | @@ -192,6 +269,58 @@ pub const Mnemonic = enum { |
| 192 | 269 | fsgnjnd, |
| 193 | 270 | fsgnjxd, |
| 194 | 271 | |
| 272 | // V Extension | |
| 273 | vle8v, | |
| 274 | vle16v, | |
| 275 | vle32v, | |
| 276 | vle64v, | |
| 277 | ||
| 278 | vse8v, | |
| 279 | vse16v, | |
| 280 | vse32v, | |
| 281 | vse64v, | |
| 282 | ||
| 283 | vsoxei8v, | |
| 284 | ||
| 285 | vaddvv, | |
| 286 | vsubvv, | |
| 287 | ||
| 288 | vfaddvv, | |
| 289 | vfsubvv, | |
| 290 | ||
| 291 | vadcvv, | |
| 292 | ||
| 293 | vmvvx, | |
| 294 | ||
| 295 | vslidedownvx, | |
| 296 | ||
| 297 | // MISC | |
| 298 | fence, | |
| 299 | fencetso, | |
| 300 | ||
| 301 | // AMO | |
| 302 | amoswapw, | |
| 303 | amoaddw, | |
| 304 | amoandw, | |
| 305 | amoorw, | |
| 306 | amoxorw, | |
| 307 | amomaxw, | |
| 308 | amominw, | |
| 309 | amomaxuw, | |
| 310 | amominuw, | |
| 311 | ||
| 312 | amoswapd, | |
| 313 | amoaddd, | |
| 314 | amoandd, | |
| 315 | amoord, | |
| 316 | amoxord, | |
| 317 | amomaxd, | |
| 318 | amomind, | |
| 319 | amomaxud, | |
| 320 | amominud, | |
| 321 | ||
| 322 | // TODO: Q extension | |
| 323 | ||
| 195 | 324 | pub fn encoding(mnem: Mnemonic) Enc { |
| 196 | 325 | return switch (mnem) { |
| 197 | 326 | // zig fmt: off |
| ... | ... | @@ -322,14 +451,25 @@ pub const Mnemonic = enum { |
| 322 | 451 | // LOAD_FP |
| 323 | 452 | |
| 324 | 453 | .flw => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b010 } } }, |
| 325 | .fld => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } }, | |
| 454 | .fld => .{ .opcode = .LOAD_FP, .data = .{ .f = .{ .funct3 = 0b011 } } }, | |
| 455 | ||
| 456 | .vle8v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 457 | .vle16v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 458 | .vle32v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 459 | .vle64v => .{ .opcode = .LOAD_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 326 | 460 | |
| 327 | 461 | |
| 328 | 462 | // STORE_FP |
| 329 | 463 | |
| 330 | .fsw => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } }, | |
| 331 | .fsd => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } }, | |
| 464 | .fsw => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b010 } } }, | |
| 465 | .fsd => .{ .opcode = .STORE_FP, .data = .{ .f = .{ .funct3 = 0b011 } } }, | |
| 332 | 466 | |
| 467 | .vse8v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 468 | .vse16v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"16", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 469 | .vse32v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"32", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 470 | .vse64v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"64", .umop = .unit, .vm = true, .mop = .unit, .mew = false, .nf = 0b000 } } }, | |
| 471 | ||
| 472 | .vsoxei8v => .{ .opcode = .STORE_FP, .data = .{ .vecls = .{ .width = .@"8", .umop = .unit, .vm = true, .mop = .ord, .mew = false, .nf = 0b000 } } }, | |
| 333 | 473 | |
| 334 | 474 | // JALR |
| 335 | 475 | |
| ... | ... | @@ -360,6 +500,8 @@ pub const Mnemonic = enum { |
| 360 | 500 | |
| 361 | 501 | .ecall => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } }, |
| 362 | 502 | .ebreak => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b000 } } }, |
| 503 | ||
| 504 | .csrrs => .{ .opcode = .SYSTEM, .data = .{ .f = .{ .funct3 = 0b010 } } }, | |
| 363 | 505 | |
| 364 | 506 | |
| 365 | 507 | // NONE |
| ... | ... | @@ -367,6 +509,52 @@ pub const Mnemonic = enum { |
| 367 | 509 | .unimp => .{ .opcode = .NONE, .data = .{ .f = .{ .funct3 = 0b000 } } }, |
| 368 | 510 | |
| 369 | 511 | |
| 512 | // MISC_MEM | |
| 513 | ||
| 514 | .fence => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .none } } }, | |
| 515 | .fencetso => .{ .opcode = .MISC_MEM, .data = .{ .fence = .{ .funct3 = 0b000, .fm = .tso } } }, | |
| 516 | ||
| 517 | ||
| 518 | // AMO | |
| 519 | ||
| 520 | .amoaddw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00000 } } }, | |
| 521 | .amoswapw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00001 } } }, | |
| 522 | // LR.W | |
| 523 | // SC.W | |
| 524 | .amoxorw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b00100 } } }, | |
| 525 | .amoandw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01100 } } }, | |
| 526 | .amoorw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b01000 } } }, | |
| 527 | .amominw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10000 } } }, | |
| 528 | .amomaxw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b10100 } } }, | |
| 529 | .amominuw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11000 } } }, | |
| 530 | .amomaxuw => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .W, .funct5 = 0b11100 } } }, | |
| 531 | ||
| 532 | .amoaddd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00000 } } }, | |
| 533 | .amoswapd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00001 } } }, | |
| 534 | // LR.D | |
| 535 | // SC.D | |
| 536 | .amoxord => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b00100 } } }, | |
| 537 | .amoandd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01100 } } }, | |
| 538 | .amoord => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b01000 } } }, | |
| 539 | .amomind => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10000 } } }, | |
| 540 | .amomaxd => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b10100 } } }, | |
| 541 | .amominud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11000 } } }, | |
| 542 | .amomaxud => .{ .opcode = .AMO, .data = .{ .amo = .{ .width = .D, .funct5 = 0b11100 } } }, | |
| 543 | ||
| 544 | // OP_V | |
| 545 | .vsetivli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } }, | |
| 546 | .vsetvli => .{ .opcode = .OP_V, .data = .{ .f = .{ .funct3 = 0b111 } } }, | |
| 547 | .vaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPIVV } } }, | |
| 548 | .vsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPIVV } } }, | |
| 549 | ||
| 550 | .vfaddvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000000, .funct3 = .OPFVV } } }, | |
| 551 | .vfsubvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b000010, .funct3 = .OPFVV } } }, | |
| 552 | ||
| 553 | .vadcvv => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010000, .funct3 = .OPMVV } } }, | |
| 554 | .vmvvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b010111, .funct3 = .OPIVX } } }, | |
| 555 | ||
| 556 | .vslidedownvx => .{ .opcode = .OP_V, .data = .{ .vecmath = .{ .vm = true, .funct6 = 0b001111, .funct3 = .OPIVX } } }, | |
| 557 | ||
| 370 | 558 | // zig fmt: on |
| 371 | 559 | }; |
| 372 | 560 | } |
| ... | ... | @@ -380,8 +568,8 @@ pub const InstEnc = enum { |
| 380 | 568 | B, |
| 381 | 569 | U, |
| 382 | 570 | J, |
| 383 | ||
| 384 | /// extras that have unusual op counts | |
| 571 | fence, | |
| 572 | amo, | |
| 385 | 573 | system, |
| 386 | 574 | |
| 387 | 575 | pub fn fromMnemonic(mnem: Mnemonic) InstEnc { |
| ... | ... | @@ -410,6 +598,10 @@ pub const InstEnc = enum { |
| 410 | 598 | |
| 411 | 599 | .flw, |
| 412 | 600 | .fld, |
| 601 | ||
| 602 | .csrrs, | |
| 603 | .vsetivli, | |
| 604 | .vsetvli, | |
| 413 | 605 | => .I, |
| 414 | 606 | |
| 415 | 607 | .lui, |
| ... | ... | @@ -503,26 +695,73 @@ pub const InstEnc = enum { |
| 503 | 695 | |
| 504 | 696 | .fsgnjxs, |
| 505 | 697 | .fsgnjxd, |
| 698 | ||
| 699 | .vle8v, | |
| 700 | .vle16v, | |
| 701 | .vle32v, | |
| 702 | .vle64v, | |
| 703 | ||
| 704 | .vse8v, | |
| 705 | .vse16v, | |
| 706 | .vse32v, | |
| 707 | .vse64v, | |
| 708 | ||
| 709 | .vsoxei8v, | |
| 710 | ||
| 711 | .vaddvv, | |
| 712 | .vsubvv, | |
| 713 | .vfaddvv, | |
| 714 | .vfsubvv, | |
| 715 | .vadcvv, | |
| 716 | .vmvvx, | |
| 717 | .vslidedownvx, | |
| 506 | 718 | => .R, |
| 507 | 719 | |
| 508 | 720 | .ecall, |
| 509 | 721 | .ebreak, |
| 510 | 722 | .unimp, |
| 511 | 723 | => .system, |
| 724 | ||
| 725 | .fence, | |
| 726 | .fencetso, | |
| 727 | => .fence, | |
| 728 | ||
| 729 | .amoswapw, | |
| 730 | .amoaddw, | |
| 731 | .amoandw, | |
| 732 | .amoorw, | |
| 733 | .amoxorw, | |
| 734 | .amomaxw, | |
| 735 | .amominw, | |
| 736 | .amomaxuw, | |
| 737 | .amominuw, | |
| 738 | ||
| 739 | .amoswapd, | |
| 740 | .amoaddd, | |
| 741 | .amoandd, | |
| 742 | .amoord, | |
| 743 | .amoxord, | |
| 744 | .amomaxd, | |
| 745 | .amomind, | |
| 746 | .amomaxud, | |
| 747 | .amominud, | |
| 748 | => .amo, | |
| 512 | 749 | }; |
| 513 | 750 | } |
| 514 | 751 | |
| 515 | pub fn opsList(enc: InstEnc) [4]std.meta.FieldEnum(Operand) { | |
| 752 | pub fn opsList(enc: InstEnc) [5]std.meta.FieldEnum(Operand) { | |
| 516 | 753 | return switch (enc) { |
| 517 | 754 | // zig fmt: off |
| 518 | .R => .{ .reg, .reg, .reg, .none }, | |
| 519 | .R4 => .{ .reg, .reg, .reg, .reg }, | |
| 520 | .I => .{ .reg, .reg, .imm, .none }, | |
| 521 | .S => .{ .reg, .reg, .imm, .none }, | |
| 522 | .B => .{ .reg, .reg, .imm, .none }, | |
| 523 | .U => .{ .reg, .imm, .none, .none }, | |
| 524 | .J => .{ .reg, .imm, .none, .none }, | |
| 525 | .system => .{ .none, .none, .none, .none }, | |
| 755 | .R => .{ .reg, .reg, .reg, .none, .none, }, | |
| 756 | .R4 => .{ .reg, .reg, .reg, .reg, .none, }, | |
| 757 | .I => .{ .reg, .reg, .imm, .none, .none, }, | |
| 758 | .S => .{ .reg, .reg, .imm, .none, .none, }, | |
| 759 | .B => .{ .reg, .reg, .imm, .none, .none, }, | |
| 760 | .U => .{ .reg, .imm, .none, .none, .none, }, | |
| 761 | .J => .{ .reg, .imm, .none, .none, .none, }, | |
| 762 | .system => .{ .none, .none, .none, .none, .none, }, | |
| 763 | .fence => .{ .barrier, .barrier, .none, .none, .none, }, | |
| 764 | .amo => .{ .reg, .reg, .reg, .barrier, .barrier }, | |
| 526 | 765 | // zig fmt: on |
| 527 | 766 | }; |
| 528 | 767 | } |
| ... | ... | @@ -584,20 +823,38 @@ pub const Data = union(InstEnc) { |
| 584 | 823 | imm1_10: u10, |
| 585 | 824 | imm20: u1, |
| 586 | 825 | }, |
| 587 | system: void, | |
| 826 | fence: packed struct { | |
| 827 | opcode: u7, | |
| 828 | rd: u5 = 0, | |
| 829 | funct3: u3, | |
| 830 | rs1: u5 = 0, | |
| 831 | succ: u4, | |
| 832 | pred: u4, | |
| 833 | fm: u4, | |
| 834 | }, | |
| 835 | amo: packed struct { | |
| 836 | opcode: u7, | |
| 837 | rd: u5, | |
| 838 | funct3: u3, | |
| 839 | rs1: u5, | |
| 840 | rs2: u5, | |
| 841 | rl: bool, | |
| 842 | aq: bool, | |
| 843 | funct5: u5, | |
| 844 | }, | |
| 845 | system: u32, | |
| 846 | ||
| 847 | comptime { | |
| 848 | for (std.meta.fields(Data)) |field| { | |
| 849 | assert(@bitSizeOf(field.type) == 32); | |
| 850 | } | |
| 851 | } | |
| 588 | 852 | |
| 589 | 853 | pub fn toU32(self: Data) u32 { |
| 590 | 854 | return switch (self) { |
| 591 | // zig fmt: off | |
| 592 | .R => |v| @bitCast(v), | |
| 593 | .R4 => |v| @bitCast(v), | |
| 594 | .I => |v| @bitCast(v), | |
| 595 | .S => |v| @bitCast(v), | |
| 596 | .B => |v| @as(u32, @intCast(v.opcode)) + (@as(u32, @intCast(v.imm11)) << 7) + (@as(u32, @intCast(v.imm1_4)) << 8) + (@as(u32, @intCast(v.funct3)) << 12) + (@as(u32, @intCast(v.rs1)) << 15) + (@as(u32, @intCast(v.rs2)) << 20) + (@as(u32, @intCast(v.imm5_10)) << 25) + (@as(u32, @intCast(v.imm12)) << 31), | |
| 597 | .U => |v| @bitCast(v), | |
| 598 | .J => |v| @bitCast(v), | |
| 855 | .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), | |
| 856 | inline else => |v| @bitCast(v), | |
| 599 | 857 | .system => unreachable, |
| 600 | // zig fmt: on | |
| 601 | 858 | }; |
| 602 | 859 | } |
| 603 | 860 | |
| ... | ... | @@ -628,6 +885,25 @@ pub const Data = union(InstEnc) { |
| 628 | 885 | }, |
| 629 | 886 | }; |
| 630 | 887 | }, |
| 888 | .csrrs => { | |
| 889 | assert(ops.len == 3); | |
| 890 | ||
| 891 | const csr = ops[0].csr; | |
| 892 | const rs1 = ops[1].reg; | |
| 893 | const rd = ops[2].reg; | |
| 894 | ||
| 895 | return .{ | |
| 896 | .I = .{ | |
| 897 | .rd = rd.encodeId(), | |
| 898 | .rs1 = rs1.encodeId(), | |
| 899 | ||
| 900 | .imm0_11 = @intFromEnum(csr), | |
| 901 | ||
| 902 | .opcode = @intFromEnum(enc.opcode), | |
| 903 | .funct3 = enc.data.f.funct3, | |
| 904 | }, | |
| 905 | }; | |
| 906 | }, | |
| 631 | 907 | else => {}, |
| 632 | 908 | } |
| 633 | 909 | |
| ... | ... | @@ -654,6 +930,25 @@ pub const Data = union(InstEnc) { |
| 654 | 930 | .funct3 = fmt.rm, |
| 655 | 931 | .funct7 = (@as(u7, fmt.funct5) << 2) | @intFromEnum(fmt.fmt), |
| 656 | 932 | }, |
| 933 | .vecls => |vec| .{ | |
| 934 | .rd = ops[0].reg.encodeId(), | |
| 935 | .rs1 = ops[1].reg.encodeId(), | |
| 936 | ||
| 937 | .rs2 = @intFromEnum(vec.umop), | |
| 938 | ||
| 939 | .opcode = @intFromEnum(enc.opcode), | |
| 940 | .funct3 = @intFromEnum(vec.width), | |
| 941 | .funct7 = (@as(u7, vec.nf) << 4) | (@as(u7, @intFromBool(vec.mew)) << 3) | (@as(u7, @intFromEnum(vec.mop)) << 1) | @intFromBool(vec.vm), | |
| 942 | }, | |
| 943 | .vecmath => |vec| .{ | |
| 944 | .rd = ops[0].reg.encodeId(), | |
| 945 | .rs1 = ops[1].reg.encodeId(), | |
| 946 | .rs2 = ops[2].reg.encodeId(), | |
| 947 | ||
| 948 | .opcode = @intFromEnum(enc.opcode), | |
| 949 | .funct3 = @intFromEnum(vec.funct3), | |
| 950 | .funct7 = (@as(u7, vec.funct6) << 1) | @intFromBool(vec.vm), | |
| 951 | }, | |
| 657 | 952 | else => unreachable, |
| 658 | 953 | }, |
| 659 | 954 | }; |
| ... | ... | @@ -748,7 +1043,48 @@ pub const Data = union(InstEnc) { |
| 748 | 1043 | }, |
| 749 | 1044 | }; |
| 750 | 1045 | }, |
| 1046 | .fence => { | |
| 1047 | assert(ops.len == 2); | |
| 1048 | ||
| 1049 | const succ = ops[0].barrier; | |
| 1050 | const pred = ops[1].barrier; | |
| 1051 | ||
| 1052 | return .{ | |
| 1053 | .fence = .{ | |
| 1054 | .succ = @intFromEnum(succ), | |
| 1055 | .pred = @intFromEnum(pred), | |
| 1056 | ||
| 1057 | .opcode = @intFromEnum(enc.opcode), | |
| 1058 | .funct3 = enc.data.fence.funct3, | |
| 1059 | .fm = @intFromEnum(enc.data.fence.fm), | |
| 1060 | }, | |
| 1061 | }; | |
| 1062 | }, | |
| 1063 | .amo => { | |
| 1064 | assert(ops.len == 5); | |
| 1065 | ||
| 1066 | const rd = ops[0].reg; | |
| 1067 | const rs1 = ops[1].reg; | |
| 1068 | const rs2 = ops[2].reg; | |
| 1069 | const rl = ops[3].barrier; | |
| 1070 | const aq = ops[4].barrier; | |
| 751 | 1071 | |
| 1072 | return .{ | |
| 1073 | .amo = .{ | |
| 1074 | .rd = rd.encodeId(), | |
| 1075 | .rs1 = rs1.encodeId(), | |
| 1076 | .rs2 = rs2.encodeId(), | |
| 1077 | ||
| 1078 | // TODO: https://github.com/ziglang/zig/issues/20113 | |
| 1079 | .rl = if (rl == .rl) true else false, | |
| 1080 | .aq = if (aq == .aq) true else false, | |
| 1081 | ||
| 1082 | .opcode = @intFromEnum(enc.opcode), | |
| 1083 | .funct3 = @intFromEnum(enc.data.amo.width), | |
| 1084 | .funct5 = enc.data.amo.funct5, | |
| 1085 | }, | |
| 1086 | }; | |
| 1087 | }, | |
| 752 | 1088 | else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}), |
| 753 | 1089 | } |
| 754 | 1090 | } |
src/arch/riscv64/Lower.zig+171-59| ... | ... | @@ -40,7 +40,9 @@ pub const Reloc = struct { |
| 40 | 40 | }; |
| 41 | 41 | |
| 42 | 42 | /// The returned slice is overwritten by the next call to lowerMir. |
| 43 | pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { | |
| 43 | pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { | |
| 44 | allow_frame_locs: bool, | |
| 45 | }) Error!struct { | |
| 44 | 46 | insts: []const Instruction, |
| 45 | 47 | relocs: []const Reloc, |
| 46 | 48 | } { |
| ... | ... | @@ -69,64 +71,102 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 69 | 71 | .pseudo_load_rm, .pseudo_store_rm => { |
| 70 | 72 | const rm = inst.data.rm; |
| 71 | 73 | |
| 72 | const frame_loc = rm.m.toFrameLoc(lower.mir); | |
| 74 | const frame_loc: Mir.FrameLoc = if (options.allow_frame_locs) | |
| 75 | rm.m.toFrameLoc(lower.mir) | |
| 76 | else | |
| 77 | .{ .base = .s0, .disp = 0 }; | |
| 73 | 78 | |
| 74 | 79 | switch (inst.ops) { |
| 75 | 80 | .pseudo_load_rm => { |
| 76 | 81 | const dest_reg = rm.r; |
| 77 | 82 | const dest_reg_class = dest_reg.class(); |
| 78 | const float = dest_reg_class == .float; | |
| 79 | 83 | |
| 80 | 84 | const src_size = rm.m.mod.size; |
| 81 | 85 | const unsigned = rm.m.mod.unsigned; |
| 82 | 86 | |
| 83 | const tag: Encoding.Mnemonic = if (!float) | |
| 84 | switch (src_size) { | |
| 87 | const tag: Encoding.Mnemonic = switch (dest_reg_class) { | |
| 88 | .int => switch (src_size) { | |
| 85 | 89 | .byte => if (unsigned) .lbu else .lb, |
| 86 | 90 | .hword => if (unsigned) .lhu else .lh, |
| 87 | 91 | .word => if (unsigned) .lwu else .lw, |
| 88 | 92 | .dword => .ld, |
| 89 | } | |
| 90 | else switch (src_size) { | |
| 91 | .byte => unreachable, // Zig does not support 8-bit floats | |
| 92 | .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}), | |
| 93 | .word => .flw, | |
| 94 | .dword => .fld, | |
| 93 | }, | |
| 94 | .float => switch (src_size) { | |
| 95 | .byte => unreachable, // Zig does not support 8-bit floats | |
| 96 | .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}), | |
| 97 | .word => .flw, | |
| 98 | .dword => .fld, | |
| 99 | }, | |
| 100 | .vector => switch (src_size) { | |
| 101 | .byte => .vle8v, | |
| 102 | .hword => .vle32v, | |
| 103 | .word => .vle32v, | |
| 104 | .dword => .vle64v, | |
| 105 | }, | |
| 95 | 106 | }; |
| 96 | 107 | |
| 97 | try lower.emit(tag, &.{ | |
| 98 | .{ .reg = rm.r }, | |
| 99 | .{ .reg = frame_loc.base }, | |
| 100 | .{ .imm = Immediate.s(frame_loc.disp) }, | |
| 101 | }); | |
| 108 | switch (dest_reg_class) { | |
| 109 | .int, .float => { | |
| 110 | try lower.emit(tag, &.{ | |
| 111 | .{ .reg = rm.r }, | |
| 112 | .{ .reg = frame_loc.base }, | |
| 113 | .{ .imm = Immediate.s(frame_loc.disp) }, | |
| 114 | }); | |
| 115 | }, | |
| 116 | .vector => { | |
| 117 | assert(frame_loc.disp == 0); | |
| 118 | try lower.emit(tag, &.{ | |
| 119 | .{ .reg = rm.r }, | |
| 120 | .{ .reg = frame_loc.base }, | |
| 121 | .{ .reg = .zero }, | |
| 122 | }); | |
| 123 | }, | |
| 124 | } | |
| 102 | 125 | }, |
| 103 | 126 | .pseudo_store_rm => { |
| 104 | 127 | const src_reg = rm.r; |
| 105 | 128 | const src_reg_class = src_reg.class(); |
| 106 | const float = src_reg_class == .float; | |
| 107 | 129 | |
| 108 | // TODO: do we actually need this? are all stores not usize? | |
| 109 | 130 | const dest_size = rm.m.mod.size; |
| 110 | 131 | |
| 111 | const tag: Encoding.Mnemonic = if (!float) | |
| 112 | switch (dest_size) { | |
| 132 | const tag: Encoding.Mnemonic = switch (src_reg_class) { | |
| 133 | .int => switch (dest_size) { | |
| 113 | 134 | .byte => .sb, |
| 114 | 135 | .hword => .sh, |
| 115 | 136 | .word => .sw, |
| 116 | 137 | .dword => .sd, |
| 117 | } | |
| 118 | else switch (dest_size) { | |
| 119 | .byte => unreachable, // Zig does not support 8-bit floats | |
| 120 | .hword => return lower.fail("TODO: lowerMir pseudo_load_rm support 16-bit floats", .{}), | |
| 121 | .word => .fsw, | |
| 122 | .dword => .fsd, | |
| 138 | }, | |
| 139 | .float => switch (dest_size) { | |
| 140 | .byte => unreachable, // Zig does not support 8-bit floats | |
| 141 | .hword => return lower.fail("TODO: lowerMir pseudo_store_rm support 16-bit floats", .{}), | |
| 142 | .word => .fsw, | |
| 143 | .dword => .fsd, | |
| 144 | }, | |
| 145 | .vector => switch (dest_size) { | |
| 146 | .byte => .vse8v, | |
| 147 | .hword => .vse16v, | |
| 148 | .word => .vse32v, | |
| 149 | .dword => .vse64v, | |
| 150 | }, | |
| 123 | 151 | }; |
| 124 | 152 | |
| 125 | try lower.emit(tag, &.{ | |
| 126 | .{ .reg = frame_loc.base }, | |
| 127 | .{ .reg = rm.r }, | |
| 128 | .{ .imm = Immediate.s(frame_loc.disp) }, | |
| 129 | }); | |
| 153 | switch (src_reg_class) { | |
| 154 | .int, .float => { | |
| 155 | try lower.emit(tag, &.{ | |
| 156 | .{ .reg = frame_loc.base }, | |
| 157 | .{ .reg = rm.r }, | |
| 158 | .{ .imm = Immediate.s(frame_loc.disp) }, | |
| 159 | }); | |
| 160 | }, | |
| 161 | .vector => { | |
| 162 | assert(frame_loc.disp == 0); | |
| 163 | try lower.emit(tag, &.{ | |
| 164 | .{ .reg = rm.r }, | |
| 165 | .{ .reg = frame_loc.base }, | |
| 166 | .{ .reg = .zero }, | |
| 167 | }); | |
| 168 | }, | |
| 169 | } | |
| 130 | 170 | }, |
| 131 | 171 | else => unreachable, |
| 132 | 172 | } |
| ... | ... | @@ -138,34 +178,47 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 138 | 178 | const dst_class = rr.rd.class(); |
| 139 | 179 | const src_class = rr.rs.class(); |
| 140 | 180 | |
| 141 | assert(dst_class == src_class); | |
| 142 | ||
| 143 | switch (dst_class) { | |
| 144 | .float => { | |
| 145 | try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{ | |
| 146 | .{ .reg = rr.rd }, | |
| 147 | .{ .reg = rr.rs }, | |
| 148 | .{ .reg = rr.rs }, | |
| 149 | }); | |
| 181 | switch (src_class) { | |
| 182 | .float => switch (dst_class) { | |
| 183 | .float => { | |
| 184 | try lower.emit(if (lower.hasFeature(.d)) .fsgnjnd else .fsgnjns, &.{ | |
| 185 | .{ .reg = rr.rd }, | |
| 186 | .{ .reg = rr.rs }, | |
| 187 | .{ .reg = rr.rs }, | |
| 188 | }); | |
| 189 | }, | |
| 190 | .int, .vector => return lower.fail("TODO: lowerMir pseudo_mv float -> {s}", .{@tagName(dst_class)}), | |
| 150 | 191 | }, |
| 151 | .int => { | |
| 152 | try lower.emit(.addi, &.{ | |
| 153 | .{ .reg = rr.rd }, | |
| 154 | .{ .reg = rr.rs }, | |
| 155 | .{ .imm = Immediate.s(0) }, | |
| 156 | }); | |
| 192 | .int => switch (dst_class) { | |
| 193 | .int => { | |
| 194 | try lower.emit(.addi, &.{ | |
| 195 | .{ .reg = rr.rd }, | |
| 196 | .{ .reg = rr.rs }, | |
| 197 | .{ .imm = Immediate.s(0) }, | |
| 198 | }); | |
| 199 | }, | |
| 200 | .vector => { | |
| 201 | try lower.emit(.vmvvx, &.{ | |
| 202 | .{ .reg = rr.rd }, | |
| 203 | .{ .reg = rr.rs }, | |
| 204 | .{ .reg = .x0 }, | |
| 205 | }); | |
| 206 | }, | |
| 207 | .float => return lower.fail("TODO: lowerMir pseudo_mv int -> {s}", .{@tagName(dst_class)}), | |
| 208 | }, | |
| 209 | .vector => switch (dst_class) { | |
| 210 | .int => { | |
| 211 | try lower.emit(.vadcvv, &.{ | |
| 212 | .{ .reg = rr.rd }, | |
| 213 | .{ .reg = .zero }, | |
| 214 | .{ .reg = rr.rs }, | |
| 215 | }); | |
| 216 | }, | |
| 217 | .float, .vector => return lower.fail("TODO: lowerMir pseudo_mv vector -> {s}", .{@tagName(dst_class)}), | |
| 157 | 218 | }, |
| 158 | 219 | } |
| 159 | 220 | }, |
| 160 | 221 | |
| 161 | .pseudo_ret => { | |
| 162 | try lower.emit(.jalr, &.{ | |
| 163 | .{ .reg = .zero }, | |
| 164 | .{ .reg = .ra }, | |
| 165 | .{ .imm = Immediate.s(0) }, | |
| 166 | }); | |
| 167 | }, | |
| 168 | ||
| 169 | 222 | .pseudo_j => { |
| 170 | 223 | try lower.emit(.jal, &.{ |
| 171 | 224 | .{ .reg = .zero }, |
| ... | ... | @@ -204,7 +257,10 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 204 | 257 | const rm = inst.data.rm; |
| 205 | 258 | assert(rm.r.class() == .int); |
| 206 | 259 | |
| 207 | const frame = rm.m.toFrameLoc(lower.mir); | |
| 260 | const frame: Mir.FrameLoc = if (options.allow_frame_locs) | |
| 261 | rm.m.toFrameLoc(lower.mir) | |
| 262 | else | |
| 263 | .{ .base = .s0, .disp = 0 }; | |
| 208 | 264 | |
| 209 | 265 | try lower.emit(.addi, &.{ |
| 210 | 266 | .{ .reg = rm.r }, |
| ... | ... | @@ -371,6 +427,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 371 | 427 | }); |
| 372 | 428 | }, |
| 373 | 429 | }, |
| 430 | .vector => return lower.fail("TODO: lowerMir pseudo_cmp vector", .{}), | |
| 374 | 431 | } |
| 375 | 432 | }, |
| 376 | 433 | |
| ... | ... | @@ -378,7 +435,14 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 378 | 435 | const rr = inst.data.rr; |
| 379 | 436 | assert(rr.rs.class() == .int and rr.rd.class() == .int); |
| 380 | 437 | |
| 381 | try lower.emit(.xori, &.{ | |
| 438 | // mask out any other bits that aren't the boolean | |
| 439 | try lower.emit(.andi, &.{ | |
| 440 | .{ .reg = rr.rs }, | |
| 441 | .{ .reg = rr.rs }, | |
| 442 | .{ .imm = Immediate.s(1) }, | |
| 443 | }); | |
| 444 | ||
| 445 | try lower.emit(.sltiu, &.{ | |
| 382 | 446 | .{ .reg = rr.rd }, |
| 383 | 447 | .{ .reg = rr.rs }, |
| 384 | 448 | .{ .imm = Immediate.s(1) }, |
| ... | ... | @@ -405,6 +469,44 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 405 | 469 | }); |
| 406 | 470 | }, |
| 407 | 471 | |
| 472 | .pseudo_amo => { | |
| 473 | const amo = inst.data.amo; | |
| 474 | const is_d = amo.ty.abiSize(pt) == 8; | |
| 475 | const is_un = amo.ty.isUnsignedInt(pt.zcu); | |
| 476 | ||
| 477 | const mnem: Encoding.Mnemonic = switch (amo.op) { | |
| 478 | // zig fmt: off | |
| 479 | .SWAP => if (is_d) .amoswapd else .amoswapw, | |
| 480 | .ADD => if (is_d) .amoaddd else .amoaddw, | |
| 481 | .AND => if (is_d) .amoandd else .amoandw, | |
| 482 | .OR => if (is_d) .amoord else .amoorw, | |
| 483 | .XOR => if (is_d) .amoxord else .amoxorw, | |
| 484 | .MAX => if (is_d) if (is_un) .amomaxud else .amomaxd else if (is_un) .amomaxuw else .amomaxw, | |
| 485 | .MIN => if (is_d) if (is_un) .amominud else .amomind else if (is_un) .amominuw else .amominw, | |
| 486 | // zig fmt: on | |
| 487 | }; | |
| 488 | ||
| 489 | try lower.emit(mnem, &.{ | |
| 490 | .{ .reg = inst.data.amo.rd }, | |
| 491 | .{ .reg = inst.data.amo.rs1 }, | |
| 492 | .{ .reg = inst.data.amo.rs2 }, | |
| 493 | .{ .barrier = inst.data.amo.rl }, | |
| 494 | .{ .barrier = inst.data.amo.aq }, | |
| 495 | }); | |
| 496 | }, | |
| 497 | ||
| 498 | .pseudo_fence => { | |
| 499 | const fence = inst.data.fence; | |
| 500 | ||
| 501 | try lower.emit(switch (fence.fm) { | |
| 502 | .tso => .fencetso, | |
| 503 | .none => .fence, | |
| 504 | }, &.{ | |
| 505 | .{ .barrier = fence.succ }, | |
| 506 | .{ .barrier = fence.pred }, | |
| 507 | }); | |
| 508 | }, | |
| 509 | ||
| 408 | 510 | else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}), |
| 409 | 511 | }, |
| 410 | 512 | } |
| ... | ... | @@ -447,6 +549,11 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { |
| 447 | 549 | .{ .reg = inst.data.r_type.rs1 }, |
| 448 | 550 | .{ .reg = inst.data.r_type.rs2 }, |
| 449 | 551 | }, |
| 552 | .csr => &.{ | |
| 553 | .{ .csr = inst.data.csr.csr }, | |
| 554 | .{ .reg = inst.data.csr.rs1 }, | |
| 555 | .{ .reg = inst.data.csr.rd }, | |
| 556 | }, | |
| 450 | 557 | else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}), |
| 451 | 558 | }); |
| 452 | 559 | } |
| ... | ... | @@ -473,17 +580,22 @@ fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.Register |
| 473 | 580 | while (it.next()) |i| { |
| 474 | 581 | const frame = lower.mir.frame_locs.get(@intFromEnum(bits.FrameIndex.spill_frame)); |
| 475 | 582 | const reg = abi.Registers.all_preserved[i]; |
| 583 | ||
| 476 | 584 | const reg_class = reg.class(); |
| 477 | const is_float_reg = reg_class == .float; | |
| 585 | const load_inst: Encoding.Mnemonic, const store_inst: Encoding.Mnemonic = switch (reg_class) { | |
| 586 | .int => .{ .ld, .sd }, | |
| 587 | .float => .{ .fld, .fsd }, | |
| 588 | .vector => unreachable, | |
| 589 | }; | |
| 478 | 590 | |
| 479 | 591 | if (spilling) { |
| 480 | try lower.emit(if (is_float_reg) .fsd else .sd, &.{ | |
| 592 | try lower.emit(store_inst, &.{ | |
| 481 | 593 | .{ .reg = frame.base }, |
| 482 | 594 | .{ .reg = abi.Registers.all_preserved[i] }, |
| 483 | 595 | .{ .imm = Immediate.s(frame.disp + reg_i) }, |
| 484 | 596 | }); |
| 485 | 597 | } else { |
| 486 | try lower.emit(if (is_float_reg) .fld else .ld, &.{ | |
| 598 | try lower.emit(load_inst, &.{ | |
| 487 | 599 | .{ .reg = abi.Registers.all_preserved[i] }, |
| 488 | 600 | .{ .reg = frame.base }, |
| 489 | 601 | .{ .imm = Immediate.s(frame.disp + reg_i) }, |
src/arch/riscv64/Mir.zig+70-48| ... | ... | @@ -31,6 +31,7 @@ pub const Inst = struct { |
| 31 | 31 | @"and", |
| 32 | 32 | andi, |
| 33 | 33 | |
| 34 | xori, | |
| 34 | 35 | xor, |
| 35 | 36 | @"or", |
| 36 | 37 | |
| ... | ... | @@ -133,6 +134,19 @@ pub const Inst = struct { |
| 133 | 134 | fltd, |
| 134 | 135 | fled, |
| 135 | 136 | |
| 137 | // Zicsr Extension Instructions | |
| 138 | csrrs, | |
| 139 | ||
| 140 | // V Extension Instructions | |
| 141 | vsetvli, | |
| 142 | vsetivli, | |
| 143 | vsetvl, | |
| 144 | vaddvv, | |
| 145 | vfaddvv, | |
| 146 | vsubvv, | |
| 147 | vfsubvv, | |
| 148 | vslidedownvx, | |
| 149 | ||
| 136 | 150 | /// A pseudo-instruction. Used for anything that isn't 1:1 with an |
| 137 | 151 | /// assembly instruction. |
| 138 | 152 | pseudo, |
| ... | ... | @@ -142,91 +156,57 @@ pub const Inst = struct { |
| 142 | 156 | /// this union. `Ops` determines which union field is active, as well as |
| 143 | 157 | /// how to interpret the data within. |
| 144 | 158 | pub const Data = union { |
| 145 | /// No additional data | |
| 146 | /// | |
| 147 | /// Used by e.g. ebreak | |
| 148 | 159 | nop: void, |
| 149 | /// Another instruction. | |
| 150 | /// | |
| 151 | /// Used by e.g. b | |
| 152 | 160 | inst: Index, |
| 153 | /// Index into `extra`. Meaning of what can be found there is context-dependent. | |
| 154 | /// | |
| 155 | /// Used by e.g. load_memory | |
| 156 | 161 | payload: u32, |
| 157 | ||
| 158 | 162 | r_type: struct { |
| 159 | 163 | rd: Register, |
| 160 | 164 | rs1: Register, |
| 161 | 165 | rs2: Register, |
| 162 | 166 | }, |
| 163 | ||
| 164 | 167 | i_type: struct { |
| 165 | 168 | rd: Register, |
| 166 | 169 | rs1: Register, |
| 167 | 170 | imm12: Immediate, |
| 168 | 171 | }, |
| 169 | ||
| 170 | 172 | s_type: struct { |
| 171 | 173 | rs1: Register, |
| 172 | 174 | rs2: Register, |
| 173 | 175 | imm5: Immediate, |
| 174 | 176 | imm7: Immediate, |
| 175 | 177 | }, |
| 176 | ||
| 177 | 178 | b_type: struct { |
| 178 | 179 | rs1: Register, |
| 179 | 180 | rs2: Register, |
| 180 | 181 | inst: Inst.Index, |
| 181 | 182 | }, |
| 182 | ||
| 183 | 183 | u_type: struct { |
| 184 | 184 | rd: Register, |
| 185 | 185 | imm20: Immediate, |
| 186 | 186 | }, |
| 187 | ||
| 188 | 187 | j_type: struct { |
| 189 | 188 | rd: Register, |
| 190 | 189 | inst: Inst.Index, |
| 191 | 190 | }, |
| 192 | ||
| 193 | /// Debug info: line and column | |
| 194 | /// | |
| 195 | /// Used by e.g. pseudo_dbg_line | |
| 196 | 191 | pseudo_dbg_line_column: struct { |
| 197 | 192 | line: u32, |
| 198 | 193 | column: u32, |
| 199 | 194 | }, |
| 200 | ||
| 201 | // Custom types to be lowered | |
| 202 | ||
| 203 | /// Register + Memory | |
| 204 | 195 | rm: struct { |
| 205 | 196 | r: Register, |
| 206 | 197 | m: Memory, |
| 207 | 198 | }, |
| 208 | ||
| 209 | 199 | reg_list: Mir.RegisterList, |
| 210 | ||
| 211 | /// A register | |
| 212 | /// | |
| 213 | /// Used by e.g. blr | |
| 214 | 200 | reg: Register, |
| 215 | ||
| 216 | /// Two registers | |
| 217 | /// | |
| 218 | /// Used by e.g. mv | |
| 219 | 201 | rr: struct { |
| 220 | 202 | rd: Register, |
| 221 | 203 | rs: Register, |
| 222 | 204 | }, |
| 223 | ||
| 224 | 205 | fabs: struct { |
| 225 | 206 | rd: Register, |
| 226 | 207 | rs: Register, |
| 227 | 208 | bits: u16, |
| 228 | 209 | }, |
| 229 | ||
| 230 | 210 | compare: struct { |
| 231 | 211 | rd: Register, |
| 232 | 212 | rs1: Register, |
| ... | ... | @@ -241,11 +221,32 @@ pub const Inst = struct { |
| 241 | 221 | }, |
| 242 | 222 | ty: Type, |
| 243 | 223 | }, |
| 244 | ||
| 245 | 224 | reloc: struct { |
| 246 | 225 | atom_index: u32, |
| 247 | 226 | sym_index: u32, |
| 248 | 227 | }, |
| 228 | fence: struct { | |
| 229 | pred: Barrier, | |
| 230 | succ: Barrier, | |
| 231 | fm: enum { | |
| 232 | none, | |
| 233 | tso, | |
| 234 | }, | |
| 235 | }, | |
| 236 | amo: struct { | |
| 237 | rd: Register, | |
| 238 | rs1: Register, | |
| 239 | rs2: Register, | |
| 240 | aq: Barrier, | |
| 241 | rl: Barrier, | |
| 242 | op: AmoOp, | |
| 243 | ty: Type, | |
| 244 | }, | |
| 245 | csr: struct { | |
| 246 | csr: CSR, | |
| 247 | rs1: Register, | |
| 248 | rd: Register, | |
| 249 | }, | |
| 249 | 250 | }; |
| 250 | 251 | |
| 251 | 252 | pub const Ops = enum { |
| ... | ... | @@ -270,6 +271,9 @@ pub const Inst = struct { |
| 270 | 271 | /// Another instruction. |
| 271 | 272 | inst, |
| 272 | 273 | |
| 274 | /// Control and Status Register Instruction. | |
| 275 | csr, | |
| 276 | ||
| 273 | 277 | /// Pseudo-instruction that will generate a backpatched |
| 274 | 278 | /// function prologue. |
| 275 | 279 | pseudo_prologue, |
| ... | ... | @@ -298,11 +302,6 @@ pub const Inst = struct { |
| 298 | 302 | /// Uses `rm` payload. |
| 299 | 303 | pseudo_lea_rm, |
| 300 | 304 | |
| 301 | /// Shorthand for returning, aka jumping to ra register. | |
| 302 | /// | |
| 303 | /// Uses nop payload. | |
| 304 | pseudo_ret, | |
| 305 | ||
| 306 | 305 | /// Jumps. Uses `inst` payload. |
| 307 | 306 | pseudo_j, |
| 308 | 307 | |
| ... | ... | @@ -326,19 +325,19 @@ pub const Inst = struct { |
| 326 | 325 | pseudo_spill_regs, |
| 327 | 326 | |
| 328 | 327 | pseudo_compare, |
| 328 | ||
| 329 | /// NOT operation on booleans. Does an `andi reg, reg, 1` to mask out any other bits from the boolean. | |
| 329 | 330 | pseudo_not, |
| 330 | 331 | |
| 331 | 332 | /// Generates an auipc + jalr pair, with a R_RISCV_CALL_PLT reloc |
| 332 | 333 | pseudo_extern_fn_reloc, |
| 333 | }; | |
| 334 | 334 | |
| 335 | // Make sure we don't accidentally make instructions bigger than expected. | |
| 336 | // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. | |
| 337 | // comptime { | |
| 338 | // if (builtin.mode != .Debug) { | |
| 339 | // assert(@sizeOf(Inst) == 8); | |
| 340 | // } | |
| 341 | // } | |
| 335 | /// IORW, IORW | |
| 336 | pseudo_fence, | |
| 337 | ||
| 338 | /// Ordering, Src, Addr, Dest | |
| 339 | pseudo_amo, | |
| 340 | }; | |
| 342 | 341 | |
| 343 | 342 | pub fn format( |
| 344 | 343 | inst: Inst, |
| ... | ... | @@ -365,6 +364,28 @@ pub const FrameLoc = struct { |
| 365 | 364 | disp: i32, |
| 366 | 365 | }; |
| 367 | 366 | |
| 367 | pub const Barrier = enum(u4) { | |
| 368 | // Fence | |
| 369 | w = 0b0001, | |
| 370 | r = 0b0010, | |
| 371 | rw = 0b0011, | |
| 372 | ||
| 373 | // Amo | |
| 374 | none, | |
| 375 | aq, | |
| 376 | rl, | |
| 377 | }; | |
| 378 | ||
| 379 | pub const AmoOp = enum(u5) { | |
| 380 | SWAP, | |
| 381 | ADD, | |
| 382 | AND, | |
| 383 | OR, | |
| 384 | XOR, | |
| 385 | MAX, | |
| 386 | MIN, | |
| 387 | }; | |
| 388 | ||
| 368 | 389 | /// Returns the requested data, as well as the new index which is at the start of the |
| 369 | 390 | /// trailers for the object. |
| 370 | 391 | pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { |
| ... | ... | @@ -437,6 +458,7 @@ const assert = std.debug.assert; |
| 437 | 458 | |
| 438 | 459 | const bits = @import("bits.zig"); |
| 439 | 460 | const Register = bits.Register; |
| 461 | const CSR = bits.CSR; | |
| 440 | 462 | const Immediate = bits.Immediate; |
| 441 | 463 | const Memory = bits.Memory; |
| 442 | 464 | const FrameIndex = bits.FrameIndex; |
src/arch/riscv64/abi.zig+24-2| ... | ... | @@ -193,6 +193,15 @@ pub fn classifySystem(ty: Type, pt: Zcu.PerThread) [8]SystemClass { |
| 193 | 193 | } |
| 194 | 194 | return memory_class; |
| 195 | 195 | }, |
| 196 | .Vector => { | |
| 197 | // we pass vectors through integer registers if they are small enough to fit. | |
| 198 | const vec_bits = ty.totalVectorBits(pt); | |
| 199 | if (vec_bits <= 64) { | |
| 200 | result[0] = .integer; | |
| 201 | return result; | |
| 202 | } | |
| 203 | return memory_class; | |
| 204 | }, | |
| 196 | 205 | else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}), |
| 197 | 206 | } |
| 198 | 207 | } |
| ... | ... | @@ -254,15 +263,15 @@ fn classifyStruct( |
| 254 | 263 | } |
| 255 | 264 | } |
| 256 | 265 | |
| 257 | const allocatable_registers = Registers.Integer.all_regs ++ Registers.Float.all_regs; | |
| 266 | const allocatable_registers = Registers.Integer.all_regs ++ Registers.Float.all_regs ++ Registers.Vector.all_regs; | |
| 258 | 267 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); |
| 259 | 268 | |
| 260 | // Register classes | |
| 261 | 269 | const RegisterBitSet = RegisterManager.RegisterBitSet; |
| 262 | 270 | |
| 263 | 271 | pub const RegisterClass = enum { |
| 264 | 272 | int, |
| 265 | 273 | float, |
| 274 | vector, | |
| 266 | 275 | }; |
| 267 | 276 | |
| 268 | 277 | pub const Registers = struct { |
| ... | ... | @@ -322,6 +331,19 @@ pub const Registers = struct { |
| 322 | 331 | |
| 323 | 332 | pub const all_regs = callee_preserved_regs ++ function_arg_regs ++ temporary_regs; |
| 324 | 333 | }; |
| 334 | ||
| 335 | pub const Vector = struct { | |
| 336 | pub const general_purpose = initRegBitSet(Integer.all_regs.len + Float.all_regs.len, all_regs.len); | |
| 337 | ||
| 338 | // zig fmt: off | |
| 339 | pub const all_regs = [_]Register{ | |
| 340 | .v0, .v1, .v2, .v3, .v4, .v5, .v6, .v7, | |
| 341 | .v8, .v9, .v10, .v11, .v12, .v13, .v14, .v15, | |
| 342 | .v16, .v17, .v18, .v19, .v20, .v21, .v22, .v23, | |
| 343 | .v24, .v25, .v26, .v27, .v28, .v29, .v30, .v31, | |
| 344 | }; | |
| 345 | // zig fmt: on | |
| 346 | }; | |
| 325 | 347 | }; |
| 326 | 348 | |
| 327 | 349 | fn initRegBitSet(start: usize, length: usize) RegisterBitSet { |
src/arch/riscv64/bits.zig+42-2| ... | ... | @@ -41,7 +41,7 @@ pub const Memory = struct { |
| 41 | 41 | 2...2 => .hword, |
| 42 | 42 | 3...4 => .word, |
| 43 | 43 | 5...8 => .dword, |
| 44 | else => unreachable, | |
| 44 | else => std.debug.panic("fromByteSize {}", .{size}), | |
| 45 | 45 | }; |
| 46 | 46 | } |
| 47 | 47 | |
| ... | ... | @@ -128,6 +128,12 @@ pub const Immediate = union(enum) { |
| 128 | 128 | } |
| 129 | 129 | }; |
| 130 | 130 | |
| 131 | pub const CSR = enum(u12) { | |
| 132 | vl = 0xC20, | |
| 133 | vtype = 0xC21, | |
| 134 | vlenb = 0xC22, | |
| 135 | }; | |
| 136 | ||
| 131 | 137 | pub const Register = enum(u8) { |
| 132 | 138 | // zig fmt: off |
| 133 | 139 | |
| ... | ... | @@ -169,6 +175,13 @@ pub const Register = enum(u8) { |
| 169 | 175 | f16, f17, f18, f19, f20, f21, f22, f23, |
| 170 | 176 | f24, f25, f26, f27, f28, f29, f30, f31, |
| 171 | 177 | |
| 178 | ||
| 179 | // V extension registers | |
| 180 | v0, v1, v2, v3, v4, v5, v6, v7, | |
| 181 | v8, v9, v10, v11, v12, v13, v14, v15, | |
| 182 | v16, v17, v18, v19, v20, v21, v22, v23, | |
| 183 | v24, v25, v26, v27, v28, v29, v30, v31, | |
| 184 | ||
| 172 | 185 | // zig fmt: on |
| 173 | 186 | |
| 174 | 187 | /// in RISC-V registers are stored as 5 bit IDs and a register can have |
| ... | ... | @@ -180,11 +193,12 @@ pub const Register = enum(u8) { |
| 180 | 193 | /// The goal of this function is to return the same ID for `zero` and `x0` but two |
| 181 | 194 | /// seperate IDs for `x0` and `f0`. We will assume that each register set has 32 registers |
| 182 | 195 | /// and is repeated twice, once for the named version, once for the number version. |
| 183 | pub fn id(reg: Register) u7 { | |
| 196 | pub fn id(reg: Register) u8 { | |
| 184 | 197 | const base = switch (@intFromEnum(reg)) { |
| 185 | 198 | // zig fmt: off |
| 186 | 199 | @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero), |
| 187 | 200 | @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => @intFromEnum(Register.ft0), |
| 201 | @intFromEnum(Register.v0) ... @intFromEnum(Register.v31) => @intFromEnum(Register.v0), | |
| 188 | 202 | else => unreachable, |
| 189 | 203 | // zig fmt: on |
| 190 | 204 | }; |
| ... | ... | @@ -207,6 +221,7 @@ pub const Register = enum(u8) { |
| 207 | 221 | // zig fmt: off |
| 208 | 222 | @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => 64, |
| 209 | 223 | @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => if (Target.riscv.featureSetHas(features, .d)) 64 else 32, |
| 224 | @intFromEnum(Register.v0) ... @intFromEnum(Register.v31) => 256, // TODO: look at suggestVectorSize | |
| 210 | 225 | else => unreachable, |
| 211 | 226 | // zig fmt: on |
| 212 | 227 | }; |
| ... | ... | @@ -217,6 +232,7 @@ pub const Register = enum(u8) { |
| 217 | 232 | // zig fmt: off |
| 218 | 233 | @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => .int, |
| 219 | 234 | @intFromEnum(Register.ft0) ... @intFromEnum(Register.f31) => .float, |
| 235 | @intFromEnum(Register.v0) ... @intFromEnum(Register.v31) => .vector, | |
| 220 | 236 | else => unreachable, |
| 221 | 237 | // zig fmt: on |
| 222 | 238 | }; |
| ... | ... | @@ -272,3 +288,27 @@ pub const Symbol = struct { |
| 272 | 288 | /// Index into the linker's symbol table. |
| 273 | 289 | sym_index: u32, |
| 274 | 290 | }; |
| 291 | ||
| 292 | pub const VType = packed struct(u8) { | |
| 293 | vlmul: VlMul, | |
| 294 | vsew: VSew, | |
| 295 | vta: bool, | |
| 296 | vma: bool, | |
| 297 | }; | |
| 298 | ||
| 299 | const VSew = enum(u3) { | |
| 300 | @"8" = 0b000, | |
| 301 | @"16" = 0b001, | |
| 302 | @"32" = 0b010, | |
| 303 | @"64" = 0b011, | |
| 304 | }; | |
| 305 | ||
| 306 | const VlMul = enum(u3) { | |
| 307 | mf8 = 0b101, | |
| 308 | mf4 = 0b110, | |
| 309 | mf2 = 0b111, | |
| 310 | m1 = 0b000, | |
| 311 | m2 = 0b001, | |
| 312 | m4 = 0b010, | |
| 313 | m8 = 0b011, | |
| 314 | }; |
src/arch/riscv64/encoder.zig+11-4| ... | ... | @@ -1,26 +1,30 @@ |
| 1 | 1 | pub const Instruction = struct { |
| 2 | 2 | encoding: Encoding, |
| 3 | ops: [3]Operand = .{.none} ** 3, | |
| 3 | ops: [5]Operand = .{.none} ** 5, | |
| 4 | 4 | |
| 5 | 5 | pub const Operand = union(enum) { |
| 6 | 6 | none, |
| 7 | 7 | reg: Register, |
| 8 | csr: CSR, | |
| 8 | 9 | mem: Memory, |
| 9 | 10 | imm: Immediate, |
| 11 | barrier: Mir.Barrier, | |
| 10 | 12 | }; |
| 11 | 13 | |
| 12 | 14 | pub fn new(mnemonic: Encoding.Mnemonic, ops: []const Operand) !Instruction { |
| 13 | 15 | const encoding = (try Encoding.findByMnemonic(mnemonic, ops)) orelse { |
| 14 | std.log.err("no encoding found for: {s} [{s} {s} {s}]", .{ | |
| 16 | std.log.err("no encoding found for: {s} [{s} {s} {s} {s} {s}]", .{ | |
| 15 | 17 | @tagName(mnemonic), |
| 16 | 18 | @tagName(if (ops.len > 0) ops[0] else .none), |
| 17 | 19 | @tagName(if (ops.len > 1) ops[1] else .none), |
| 18 | 20 | @tagName(if (ops.len > 2) ops[2] else .none), |
| 21 | @tagName(if (ops.len > 3) ops[3] else .none), | |
| 22 | @tagName(if (ops.len > 4) ops[4] else .none), | |
| 19 | 23 | }); |
| 20 | 24 | return error.InvalidInstruction; |
| 21 | 25 | }; |
| 22 | 26 | |
| 23 | var result_ops: [3]Operand = .{.none} ** 3; | |
| 27 | var result_ops: [5]Operand = .{.none} ** 5; | |
| 24 | 28 | @memcpy(result_ops[0..ops.len], ops); |
| 25 | 29 | |
| 26 | 30 | return .{ |
| ... | ... | @@ -53,7 +57,9 @@ pub const Instruction = struct { |
| 53 | 57 | .none => unreachable, // it's sliced out above |
| 54 | 58 | .reg => |reg| try writer.writeAll(@tagName(reg)), |
| 55 | 59 | .imm => |imm| try writer.print("{d}", .{imm.asSigned(64)}), |
| 56 | .mem => unreachable, // there is no "mem" operand in the actual instructions | |
| 60 | .mem => try writer.writeAll("mem"), | |
| 61 | .barrier => |barrier| try writer.writeAll(@tagName(barrier)), | |
| 62 | .csr => |csr| try writer.writeAll(@tagName(csr)), | |
| 57 | 63 | } |
| 58 | 64 | } |
| 59 | 65 | } |
| ... | ... | @@ -67,6 +73,7 @@ const bits = @import("bits.zig"); |
| 67 | 73 | const Encoding = @import("Encoding.zig"); |
| 68 | 74 | |
| 69 | 75 | const Register = bits.Register; |
| 76 | const CSR = bits.CSR; | |
| 70 | 77 | const Memory = bits.Memory; |
| 71 | 78 | const Immediate = bits.Immediate; |
| 72 | 79 |
src/arch/x86_64/Lower.zig+2| ... | ... | @@ -65,6 +65,8 @@ pub const Reloc = struct { |
| 65 | 65 | }; |
| 66 | 66 | }; |
| 67 | 67 | |
| 68 | const Options = struct { allow_frame_locs: bool }; | |
| 69 | ||
| 68 | 70 | /// The returned slice is overwritten by the next call to lowerMir. |
| 69 | 71 | pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 70 | 72 | insts: []const Instruction, |
src/codegen.zig+2-1| ... | ... | @@ -987,8 +987,9 @@ pub fn genTypedValue( |
| 987 | 987 | |
| 988 | 988 | log.debug("genTypedValue: val = {}", .{val.fmtValue(pt)}); |
| 989 | 989 | |
| 990 | if (val.isUndef(zcu)) | |
| 990 | if (val.isUndef(zcu)) { | |
| 991 | 991 | return GenResult.mcv(.undef); |
| 992 | } | |
| 992 | 993 | |
| 993 | 994 | const owner_decl = zcu.declPtr(owner_decl_index); |
| 994 | 995 | const namespace = zcu.namespacePtr(owner_decl.src_namespace); |
src/link/Elf/ZigObject.zig+11-7| ... | ... | @@ -540,8 +540,8 @@ inline fn isGlobal(index: Symbol.Index) bool { |
| 540 | 540 | |
| 541 | 541 | pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index { |
| 542 | 542 | const actual_index = index & symbol_mask; |
| 543 | if (isGlobal(index)) return self.global_symbols.items[actual_index]; | |
| 544 | return self.local_symbols.items[actual_index]; | |
| 543 | if (isGlobal(index)) return self.globals()[actual_index]; | |
| 544 | return self.locals()[actual_index]; | |
| 545 | 545 | } |
| 546 | 546 | |
| 547 | 547 | pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym { |
| ... | ... | @@ -1334,11 +1334,15 @@ fn lowerConst( |
| 1334 | 1334 | |
| 1335 | 1335 | const sym_index = try self.addAtom(elf_file); |
| 1336 | 1336 | |
| 1337 | const res = try codegen.generateSymbol(&elf_file.base, pt, src_loc, val, &code_buffer, .{ | |
| 1338 | .none = {}, | |
| 1339 | }, .{ | |
| 1340 | .parent_atom_index = sym_index, | |
| 1341 | }); | |
| 1337 | const res = try codegen.generateSymbol( | |
| 1338 | &elf_file.base, | |
| 1339 | pt, | |
| 1340 | src_loc, | |
| 1341 | val, | |
| 1342 | &code_buffer, | |
| 1343 | .{ .none = {} }, | |
| 1344 | .{ .parent_atom_index = sym_index }, | |
| 1345 | ); | |
| 1342 | 1346 | const code = switch (res) { |
| 1343 | 1347 | .ok => code_buffer.items, |
| 1344 | 1348 | .fail => |em| return .{ .fail = em }, |
test/behavior/array.zig-2| ... | ... | @@ -580,7 +580,6 @@ test "type coercion of anon struct literal to array" { |
| 580 | 580 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 581 | 581 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 582 | 582 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 583 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 584 | 583 | |
| 585 | 584 | const S = struct { |
| 586 | 585 | const U = union { |
| ... | ... | @@ -1011,7 +1010,6 @@ test "union that needs padding bytes inside an array" { |
| 1011 | 1010 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1012 | 1011 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 1013 | 1012 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1014 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1015 | 1013 | |
| 1016 | 1014 | const B = union(enum) { |
| 1017 | 1015 | D: u8, |
test/behavior/atomics.zig-17| ... | ... | @@ -42,7 +42,6 @@ test "fence" { |
| 42 | 42 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 43 | 43 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 44 | 44 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 45 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 46 | 45 | |
| 47 | 46 | var x: i32 = 1234; |
| 48 | 47 | @fence(.seq_cst); |
| ... | ... | @@ -188,21 +187,6 @@ test "atomic store" { |
| 188 | 187 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 189 | 188 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 190 | 189 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 191 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 192 | ||
| 193 | var x: u32 = 0; | |
| 194 | @atomicStore(u32, &x, 1, .seq_cst); | |
| 195 | try expect(@atomicLoad(u32, &x, .seq_cst) == 1); | |
| 196 | @atomicStore(u32, &x, 12345678, .seq_cst); | |
| 197 | try expect(@atomicLoad(u32, &x, .seq_cst) == 12345678); | |
| 198 | } | |
| 199 | ||
| 200 | test "atomic store comptime" { | |
| 201 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 202 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | |
| 203 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 204 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 205 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 206 | 190 | |
| 207 | 191 | try comptime testAtomicStore(); |
| 208 | 192 | try testAtomicStore(); |
| ... | ... | @@ -451,7 +435,6 @@ test "return @atomicStore, using it as a void value" { |
| 451 | 435 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 452 | 436 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 453 | 437 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 454 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 455 | 438 | |
| 456 | 439 | const S = struct { |
| 457 | 440 | const A = struct { |
test/behavior/bitcast.zig-2| ... | ... | @@ -192,7 +192,6 @@ test "@bitCast packed structs at runtime and comptime" { |
| 192 | 192 | test "@bitCast extern structs at runtime and comptime" { |
| 193 | 193 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 194 | 194 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 195 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 196 | 195 | |
| 197 | 196 | const Full = extern struct { |
| 198 | 197 | number: u16, |
| ... | ... | @@ -227,7 +226,6 @@ test "bitcast packed struct to integer and back" { |
| 227 | 226 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 228 | 227 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 229 | 228 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 230 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 231 | 229 | |
| 232 | 230 | const LevelUpMove = packed struct { |
| 233 | 231 | move_id: u9, |
test/behavior/builtin_functions_returning_void_or_noreturn.zig-1| ... | ... | @@ -11,7 +11,6 @@ test { |
| 11 | 11 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 12 | 12 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 13 | 13 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 14 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 15 | 14 | |
| 16 | 15 | var val: u8 = undefined; |
| 17 | 16 | try testing.expectEqual({}, @atomicStore(u8, &val, 0, .unordered)); |
test/behavior/enum.zig-1| ... | ... | @@ -908,7 +908,6 @@ test "enum literal casting to tagged union" { |
| 908 | 908 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 909 | 909 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 910 | 910 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 911 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 912 | 911 | |
| 913 | 912 | const Arch = union(enum) { |
| 914 | 913 | x86_64, |
test/behavior/error.zig-1| ... | ... | @@ -535,7 +535,6 @@ test "return result loc as peer result loc in inferred error set function" { |
| 535 | 535 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 536 | 536 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 537 | 537 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 538 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 539 | 538 | |
| 540 | 539 | const S = struct { |
| 541 | 540 | fn doTheTest() !void { |
test/behavior/eval.zig-2| ... | ... | @@ -395,7 +395,6 @@ test "return 0 from function that has u0 return type" { |
| 395 | 395 | test "statically initialized struct" { |
| 396 | 396 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 397 | 397 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 398 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 399 | 398 | |
| 400 | 399 | st_init_str_foo.x += 1; |
| 401 | 400 | try expect(st_init_str_foo.x == 14); |
| ... | ... | @@ -446,7 +445,6 @@ test "binary math operator in partially inlined function" { |
| 446 | 445 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 447 | 446 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 448 | 447 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 449 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 450 | 448 | |
| 451 | 449 | var s: [4]u32 = undefined; |
| 452 | 450 | var b: [16]u8 = undefined; |
test/behavior/floatop.zig-1| ... | ... | @@ -281,7 +281,6 @@ test "@sqrt f32/f64" { |
| 281 | 281 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 282 | 282 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 283 | 283 | if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; |
| 284 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 285 | 284 | |
| 286 | 285 | try testSqrt(f32); |
| 287 | 286 | try comptime testSqrt(f32); |
test/behavior/inline_switch.zig-1| ... | ... | @@ -49,7 +49,6 @@ test "inline switch unions" { |
| 49 | 49 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 50 | 50 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 51 | 51 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 52 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 53 | 52 | |
| 54 | 53 | var x: U = .a; |
| 55 | 54 | _ = &x; |
test/behavior/math.zig-1| ... | ... | @@ -1269,7 +1269,6 @@ test "@subWithOverflow" { |
| 1269 | 1269 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1270 | 1270 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1271 | 1271 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1272 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1273 | 1272 | |
| 1274 | 1273 | { |
| 1275 | 1274 | var a: u8 = 1; |
test/behavior/optional.zig-1| ... | ... | @@ -397,7 +397,6 @@ test "array of optional unaligned types" { |
| 397 | 397 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 398 | 398 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 399 | 399 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 400 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 401 | 400 | |
| 402 | 401 | const Enum = enum { one, two, three }; |
| 403 | 402 |
test/behavior/packed-struct.zig-1| ... | ... | @@ -785,7 +785,6 @@ test "nested packed struct field access test" { |
| 785 | 785 | test "nested packed struct at non-zero offset" { |
| 786 | 786 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 787 | 787 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 788 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 789 | 788 | |
| 790 | 789 | const Pair = packed struct(u24) { |
| 791 | 790 | a: u16 = 0, |
test/behavior/reflection.zig-1| ... | ... | @@ -28,7 +28,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 { |
| 28 | 28 | test "reflection: @field" { |
| 29 | 29 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 30 | 30 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 31 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 32 | 31 | |
| 33 | 32 | var f = Foo{ |
| 34 | 33 | .one = 42, |
test/behavior/struct.zig-3| ... | ... | @@ -875,7 +875,6 @@ test "packed struct field passed to generic function" { |
| 875 | 875 | test "anonymous struct literal syntax" { |
| 876 | 876 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 877 | 877 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 878 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 879 | 878 | |
| 880 | 879 | const S = struct { |
| 881 | 880 | const Point = struct { |
| ... | ... | @@ -985,7 +984,6 @@ test "struct with union field" { |
| 985 | 984 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 986 | 985 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 987 | 986 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 988 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 989 | 987 | |
| 990 | 988 | const Value = struct { |
| 991 | 989 | ref: u32 = 2, |
| ... | ... | @@ -1368,7 +1366,6 @@ test "store to comptime field" { |
| 1368 | 1366 | test "struct field init value is size of the struct" { |
| 1369 | 1367 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1370 | 1368 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1371 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1372 | 1369 | |
| 1373 | 1370 | const namespace = struct { |
| 1374 | 1371 | const S = extern struct { |
test/behavior/switch.zig-2| ... | ... | @@ -256,7 +256,6 @@ test "switch on enum using pointer capture" { |
| 256 | 256 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 257 | 257 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 258 | 258 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 259 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 260 | 259 | |
| 261 | 260 | try testSwitchEnumPtrCapture(); |
| 262 | 261 | try comptime testSwitchEnumPtrCapture(); |
| ... | ... | @@ -693,7 +692,6 @@ test "switch capture copies its payload" { |
| 693 | 692 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 694 | 693 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 695 | 694 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 696 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 697 | 695 | |
| 698 | 696 | const S = struct { |
| 699 | 697 | fn doTheTest() !void { |
test/behavior/this.zig-1| ... | ... | @@ -27,7 +27,6 @@ test "this refer to module call private fn" { |
| 27 | 27 | test "this refer to container" { |
| 28 | 28 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 29 | 29 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 30 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 31 | 30 | |
| 32 | 31 | var pt: Point(i32) = undefined; |
| 33 | 32 | pt.x = 12; |
test/behavior/tuple.zig-1| ... | ... | @@ -131,7 +131,6 @@ test "tuple initializer for var" { |
| 131 | 131 | test "array-like initializer for tuple types" { |
| 132 | 132 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 133 | 133 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 134 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 135 | 134 | |
| 136 | 135 | const T = @Type(.{ |
| 137 | 136 | .Struct = .{ |
test/behavior/type.zig-1| ... | ... | @@ -383,7 +383,6 @@ test "Type.Union" { |
| 383 | 383 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 384 | 384 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 385 | 385 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 386 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 387 | 386 | |
| 388 | 387 | const Untagged = @Type(.{ |
| 389 | 388 | .Union = .{ |
test/behavior/union.zig-23| ... | ... | @@ -43,7 +43,6 @@ test "basic unions" { |
| 43 | 43 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 44 | 44 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 45 | 45 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 46 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 47 | 46 | |
| 48 | 47 | var foo = Foo{ .int = 1 }; |
| 49 | 48 | try expect(foo.int == 1); |
| ... | ... | @@ -276,7 +275,6 @@ test "comparison between union and enum literal" { |
| 276 | 275 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 277 | 276 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 278 | 277 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 279 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 280 | 278 | |
| 281 | 279 | try testComparison(); |
| 282 | 280 | try comptime testComparison(); |
| ... | ... | @@ -292,7 +290,6 @@ test "cast union to tag type of union" { |
| 292 | 290 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 293 | 291 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 294 | 292 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 295 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 296 | 293 | |
| 297 | 294 | try testCastUnionToTag(); |
| 298 | 295 | try comptime testCastUnionToTag(); |
| ... | ... | @@ -314,7 +311,6 @@ test "cast tag type of union to union" { |
| 314 | 311 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 315 | 312 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 316 | 313 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 317 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 318 | 314 | |
| 319 | 315 | var x: Value2 = Letter2.B; |
| 320 | 316 | _ = &x; |
| ... | ... | @@ -331,7 +327,6 @@ test "implicit cast union to its tag type" { |
| 331 | 327 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 332 | 328 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 333 | 329 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 334 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 335 | 330 | |
| 336 | 331 | var x: Value2 = Letter2.B; |
| 337 | 332 | _ = &x; |
| ... | ... | @@ -353,7 +348,6 @@ test "constant packed union" { |
| 353 | 348 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 354 | 349 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 355 | 350 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 356 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 357 | 351 | |
| 358 | 352 | try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }}); |
| 359 | 353 | } |
| ... | ... | @@ -503,7 +497,6 @@ test "initialize global array of union" { |
| 503 | 497 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 504 | 498 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 505 | 499 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 506 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 507 | 500 | |
| 508 | 501 | glbl_array[1] = FooUnion{ .U1 = 2 }; |
| 509 | 502 | glbl_array[0] = FooUnion{ .U0 = 1 }; |
| ... | ... | @@ -515,7 +508,6 @@ test "update the tag value for zero-sized unions" { |
| 515 | 508 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 516 | 509 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 517 | 510 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 518 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 519 | 511 | |
| 520 | 512 | const S = union(enum) { |
| 521 | 513 | U0: void, |
| ... | ... | @@ -636,7 +628,6 @@ test "tagged union with all void fields but a meaningful tag" { |
| 636 | 628 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 637 | 629 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 638 | 630 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 639 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 640 | 631 | |
| 641 | 632 | const S = struct { |
| 642 | 633 | const B = union(enum) { |
| ... | ... | @@ -758,7 +749,6 @@ test "@intFromEnum works on unions" { |
| 758 | 749 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 759 | 750 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 760 | 751 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 761 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 762 | 752 | |
| 763 | 753 | const Bar = union(enum) { |
| 764 | 754 | A: bool, |
| ... | ... | @@ -874,7 +864,6 @@ test "@unionInit can modify a union type" { |
| 874 | 864 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 875 | 865 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 876 | 866 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 877 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 878 | 867 | |
| 879 | 868 | const UnionInitEnum = union(enum) { |
| 880 | 869 | Boolean: bool, |
| ... | ... | @@ -898,7 +887,6 @@ test "@unionInit can modify a pointer value" { |
| 898 | 887 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 899 | 888 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 900 | 889 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 901 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 902 | 890 | |
| 903 | 891 | const UnionInitEnum = union(enum) { |
| 904 | 892 | Boolean: bool, |
| ... | ... | @@ -1089,7 +1077,6 @@ test "switching on non exhaustive union" { |
| 1089 | 1077 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1090 | 1078 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1091 | 1079 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1092 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1093 | 1080 | |
| 1094 | 1081 | const S = struct { |
| 1095 | 1082 | const E = enum(u8) { |
| ... | ... | @@ -1199,7 +1186,6 @@ test "global variable struct contains union initialized to non-most-aligned fiel |
| 1199 | 1186 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1200 | 1187 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1201 | 1188 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1202 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1203 | 1189 | |
| 1204 | 1190 | const T = struct { |
| 1205 | 1191 | const U = union(enum) { |
| ... | ... | @@ -1352,7 +1338,6 @@ test "noreturn field in union" { |
| 1352 | 1338 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1353 | 1339 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1354 | 1340 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1355 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1356 | 1341 | |
| 1357 | 1342 | const U = union(enum) { |
| 1358 | 1343 | a: u32, |
| ... | ... | @@ -1434,7 +1419,6 @@ test "union field ptr - zero sized payload" { |
| 1434 | 1419 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1435 | 1420 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1436 | 1421 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1437 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1438 | 1422 | |
| 1439 | 1423 | const U = union { |
| 1440 | 1424 | foo: void, |
| ... | ... | @@ -1449,7 +1433,6 @@ test "union field ptr - zero sized field" { |
| 1449 | 1433 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1450 | 1434 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1451 | 1435 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1452 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1453 | 1436 | |
| 1454 | 1437 | const U = union { |
| 1455 | 1438 | foo: void, |
| ... | ... | @@ -1589,7 +1572,6 @@ test "reinterpreting enum value inside packed union" { |
| 1589 | 1572 | |
| 1590 | 1573 | test "access the tag of a global tagged union" { |
| 1591 | 1574 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1592 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1593 | 1575 | |
| 1594 | 1576 | const U = union(enum) { |
| 1595 | 1577 | a, |
| ... | ... | @@ -1601,7 +1583,6 @@ test "access the tag of a global tagged union" { |
| 1601 | 1583 | |
| 1602 | 1584 | test "coerce enum literal to union in result loc" { |
| 1603 | 1585 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1604 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1605 | 1586 | |
| 1606 | 1587 | const U = union(enum) { |
| 1607 | 1588 | a, |
| ... | ... | @@ -1864,7 +1845,6 @@ test "reinterpret extern union" { |
| 1864 | 1845 | |
| 1865 | 1846 | test "reinterpret packed union" { |
| 1866 | 1847 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 1867 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1868 | 1848 | |
| 1869 | 1849 | const U = packed union { |
| 1870 | 1850 | foo: u8, |
| ... | ... | @@ -2044,7 +2024,6 @@ test "extern union initialized via reintepreted struct field initializer" { |
| 2044 | 2024 | |
| 2045 | 2025 | test "packed union initialized via reintepreted struct field initializer" { |
| 2046 | 2026 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 2047 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 2048 | 2027 | |
| 2049 | 2028 | const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd }; |
| 2050 | 2029 | |
| ... | ... | @@ -2065,7 +2044,6 @@ test "packed union initialized via reintepreted struct field initializer" { |
| 2065 | 2044 | |
| 2066 | 2045 | test "store of comptime reinterpreted memory to extern union" { |
| 2067 | 2046 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 2068 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 2069 | 2047 | |
| 2070 | 2048 | const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd }; |
| 2071 | 2049 | |
| ... | ... | @@ -2088,7 +2066,6 @@ test "store of comptime reinterpreted memory to extern union" { |
| 2088 | 2066 | |
| 2089 | 2067 | test "store of comptime reinterpreted memory to packed union" { |
| 2090 | 2068 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 2091 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 2092 | 2069 | |
| 2093 | 2070 | const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd }; |
| 2094 | 2071 |
test/behavior/vector.zig+28-19| ... | ... | @@ -97,29 +97,40 @@ test "vector int operators" { |
| 97 | 97 | |
| 98 | 98 | test "vector float operators" { |
| 99 | 99 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 101 | 100 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 102 | 101 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 103 | 102 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 104 | 103 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 105 | 104 | if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; |
| 106 | 105 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 106 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 107 | 107 | |
| 108 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { | |
| 109 | const S = struct { | |
| 110 | fn doTheTest() !void { | |
| 111 | var v: @Vector(4, T) = [4]T{ 10, 20, 30, 40 }; | |
| 112 | var x: @Vector(4, T) = [4]T{ 1, 2, 3, 4 }; | |
| 113 | _ = .{ &v, &x }; | |
| 114 | try expect(mem.eql(T, &@as([4]T, v + x), &[4]T{ 11, 22, 33, 44 })); | |
| 115 | try expect(mem.eql(T, &@as([4]T, v - x), &[4]T{ 9, 18, 27, 36 })); | |
| 116 | try expect(mem.eql(T, &@as([4]T, v * x), &[4]T{ 10, 40, 90, 160 })); | |
| 117 | try expect(mem.eql(T, &@as([4]T, -x), &[4]T{ -1, -2, -3, -4 })); | |
| 118 | } | |
| 119 | }; | |
| 120 | try S.doTheTest(); | |
| 121 | try comptime S.doTheTest(); | |
| 122 | } | |
| 108 | const S = struct { | |
| 109 | fn doTheTest(T: type) !void { | |
| 110 | var v: @Vector(4, T) = .{ 10, 20, 30, 40 }; | |
| 111 | var x: @Vector(4, T) = .{ 1, 2, 3, 4 }; | |
| 112 | _ = .{ &v, &x }; | |
| 113 | try expectEqual(v + x, .{ 11, 22, 33, 44 }); | |
| 114 | try expectEqual(v - x, .{ 9, 18, 27, 36 }); | |
| 115 | try expectEqual(v * x, .{ 10, 40, 90, 160 }); | |
| 116 | try expectEqual(-x, .{ -1, -2, -3, -4 }); | |
| 117 | } | |
| 118 | }; | |
| 119 | ||
| 120 | try S.doTheTest(f32); | |
| 121 | try comptime S.doTheTest(f32); | |
| 122 | ||
| 123 | try S.doTheTest(f64); | |
| 124 | try comptime S.doTheTest(f64); | |
| 125 | ||
| 126 | try S.doTheTest(f16); | |
| 127 | try comptime S.doTheTest(f16); | |
| 128 | ||
| 129 | try S.doTheTest(f80); | |
| 130 | try comptime S.doTheTest(f80); | |
| 131 | ||
| 132 | try S.doTheTest(f128); | |
| 133 | try comptime S.doTheTest(f128); | |
| 123 | 134 | } |
| 124 | 135 | |
| 125 | 136 | test "vector bit operators" { |
| ... | ... | @@ -1245,7 +1256,6 @@ test "array of vectors is copied" { |
| 1245 | 1256 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1246 | 1257 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1247 | 1258 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1248 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1249 | 1259 | |
| 1250 | 1260 | const Vec3 = @Vector(3, i32); |
| 1251 | 1261 | var points = [_]Vec3{ |
| ... | ... | @@ -1316,6 +1326,7 @@ test "zero multiplicand" { |
| 1316 | 1326 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1317 | 1327 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1318 | 1328 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1329 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1319 | 1330 | |
| 1320 | 1331 | const zeros = @Vector(2, u32){ 0.0, 0.0 }; |
| 1321 | 1332 | var ones = @Vector(2, u32){ 1.0, 1.0 }; |
| ... | ... | @@ -1410,7 +1421,6 @@ test "store to vector in slice" { |
| 1410 | 1421 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1411 | 1422 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1412 | 1423 | if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; |
| 1413 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1414 | 1424 | |
| 1415 | 1425 | var v = [_]@Vector(3, f32){ |
| 1416 | 1426 | .{ 1, 1, 1 }, |
| ... | ... | @@ -1608,7 +1618,6 @@ test "@reduce on bool vector" { |
| 1608 | 1618 | test "bitcast vector to array of smaller vectors" { |
| 1609 | 1619 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1610 | 1620 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1611 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1612 | 1621 | |
| 1613 | 1622 | const u8x32 = @Vector(32, u8); |
| 1614 | 1623 | const u8x64 = @Vector(64, u8); |
test/tests.zig+6-5| ... | ... | @@ -436,11 +436,12 @@ const test_targets = blk: { |
| 436 | 436 | //}, |
| 437 | 437 | |
| 438 | 438 | .{ |
| 439 | .target = .{ | |
| 440 | .cpu_arch = .riscv64, | |
| 441 | .os_tag = .linux, | |
| 442 | .abi = .musl, | |
| 443 | }, | |
| 439 | .target = std.Target.Query.parse( | |
| 440 | .{ | |
| 441 | .arch_os_abi = "riscv64-linux-musl", | |
| 442 | .cpu_features = "baseline+v", | |
| 443 | }, | |
| 444 | ) catch @panic("OOM"), | |
| 444 | 445 | .use_llvm = false, |
| 445 | 446 | .use_lld = false, |
| 446 | 447 | }, |