master
  1const std = @import("std");
  2const assert = std.debug.assert;
  3const testing = std.testing;
  4const Target = std.Target;
  5
  6const Zcu = @import("../../Zcu.zig");
  7const Mir = @import("Mir.zig");
  8const abi = @import("abi.zig");
  9
 10pub const Memory = struct {
 11    base: Base,
 12    mod: Mod,
 13
 14    pub const Base = union(enum) {
 15        reg: Register,
 16        frame: FrameIndex,
 17    };
 18
 19    pub const Mod = struct {
 20        size: Size,
 21        unsigned: bool,
 22        disp: i32 = 0,
 23    };
 24
 25    pub const Size = enum(u4) {
 26        /// Byte, 1 byte
 27        byte,
 28        /// Half word, 2 bytes
 29        hword,
 30        /// Word, 4 bytes
 31        word,
 32        /// Double word, 8 Bytes
 33        dword,
 34
 35        pub fn fromByteSize(size: u64) Size {
 36            return switch (size) {
 37                1...1 => .byte,
 38                2...2 => .hword,
 39                3...4 => .word,
 40                5...8 => .dword,
 41                else => std.debug.panic("fromByteSize {}", .{size}),
 42            };
 43        }
 44
 45        pub fn fromBitSize(bit_size: u64) Size {
 46            return switch (bit_size) {
 47                8 => .byte,
 48                16 => .hword,
 49                32 => .word,
 50                64 => .dword,
 51                else => unreachable,
 52            };
 53        }
 54
 55        pub fn bitSize(s: Size) u64 {
 56            return switch (s) {
 57                .byte => 8,
 58                .hword => 16,
 59                .word => 32,
 60                .dword => 64,
 61            };
 62        }
 63    };
 64
 65    /// Asserts `mem` can be represented as a `FrameLoc`.
 66    pub fn toFrameLoc(mem: Memory, mir: Mir) Mir.FrameLoc {
 67        const offset: i32 = mem.mod.disp;
 68
 69        switch (mem.base) {
 70            .reg => |reg| {
 71                return .{
 72                    .base = reg,
 73                    .disp = offset,
 74                };
 75            },
 76            .frame => |index| {
 77                const base_loc = mir.frame_locs.get(@intFromEnum(index));
 78                return .{
 79                    .base = base_loc.base,
 80                    .disp = base_loc.disp + offset,
 81                };
 82            },
 83        }
 84    }
 85};
 86
 87pub const Immediate = union(enum) {
 88    signed: i32,
 89    unsigned: u64,
 90
 91    pub fn u(x: u64) Immediate {
 92        return .{ .unsigned = x };
 93    }
 94
 95    pub fn s(x: i32) Immediate {
 96        return .{ .signed = x };
 97    }
 98
 99    pub fn asSigned(imm: Immediate, bit_size: u64) i64 {
100        return switch (imm) {
101            .signed => |x| switch (bit_size) {
102                1, 8 => @as(i8, @intCast(x)),
103                16 => @as(i16, @intCast(x)),
104                32, 64 => x,
105                else => unreachable,
106            },
107            .unsigned => |x| switch (bit_size) {
108                1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))),
109                16 => @as(i16, @bitCast(@as(u16, @intCast(x)))),
110                32 => @as(i32, @bitCast(@as(u32, @intCast(x)))),
111                64 => @bitCast(x),
112                else => unreachable,
113            },
114        };
115    }
116
117    pub fn asBits(imm: Immediate, comptime T: type) T {
118        const int_info = @typeInfo(T).int;
119        if (int_info.signedness != .unsigned) @compileError("Immediate.asBits needs unsigned T");
120        return switch (imm) {
121            .signed => |x| @bitCast(@as(std.meta.Int(.signed, int_info.bits), @intCast(x))),
122            .unsigned => |x| @intCast(x),
123        };
124    }
125};
126
127pub const CSR = enum(u12) {
128    vl = 0xC20,
129    vtype = 0xC21,
130    vlenb = 0xC22,
131};
132
133pub const Register = enum(u8) {
134    // zig fmt: off
135
136    // base extension registers
137
138    zero, // zero
139    ra, // return address. caller saved
140    sp, // stack pointer. callee saved.
141    gp, // global pointer
142    tp, // thread pointer
143    t0, t1, t2, // temporaries. caller saved.
144    s0, // s0/fp, callee saved.
145    s1, // callee saved.
146    a0, a1, // fn args/return values. caller saved.
147    a2, a3, a4, a5, a6, a7, // fn args. caller saved.
148    s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
149    t3, t4, t5, t6, // caller saved
150
151    x0,  x1,  x2,  x3,  x4,  x5,  x6,  x7,
152    x8,  x9,  x10, x11, x12, x13, x14, x15,
153    x16, x17, x18, x19, x20, x21, x22, x23,
154    x24, x25, x26, x27, x28, x29, x30, x31,
155
156
157    // F extension registers
158
159    ft0, ft1, ft2, ft3, ft4, ft5, ft6, ft7, // float temporaries. caller saved.
160    fs0, fs1, // float saved. callee saved.
161    fa0, fa1, // float arg/ret. caller saved.
162    fa2, fa3, fa4, fa5, fa6, fa7, // float arg. called saved.
163    fs2, fs3, fs4, fs5, fs6, fs7, fs8, fs9, fs10, fs11,  // float saved. callee saved.
164    ft8, ft9, ft10, ft11, // foat temporaries. calller saved.
165
166    // this register is accessed only through API instructions instead of directly
167    // fcsr,
168
169    f0, f1,  f2,  f3,  f4,  f5,  f6,  f7,
170    f8, f9,  f10, f11, f12, f13, f14, f15,
171    f16, f17, f18, f19, f20, f21, f22, f23,
172    f24, f25, f26, f27, f28, f29, f30, f31,
173
174
175    // V extension registers
176    v0,  v1,  v2,  v3,  v4,  v5,  v6,  v7,
177    v8,  v9,  v10, v11, v12, v13, v14, v15,
178    v16, v17, v18, v19, v20, v21, v22, v23,
179    v24, v25, v26, v27, v28, v29, v30, v31,
180
181    // zig fmt: on
182
183    /// in RISC-V registers are stored as 5 bit IDs and a register can have
184    /// two names. Example being `zero` and `x0` are the same register and have the
185    /// same ID, but are two different entries in the enum. We store floating point
186    /// registers in the same enum. RISC-V uses the same IDs for `f0` and `x0` by
187    /// infering which register is being talked about given the instruction it's in.
188    ///
189    /// The goal of this function is to return the same ID for `zero` and `x0` but two
190    /// seperate IDs for `x0` and `f0`. We will assume that each register set has 32 registers
191    /// and is repeated twice, once for the named version, once for the number version.
192    pub fn id(reg: Register) std.math.IntFittingRange(0, @typeInfo(Register).@"enum".fields.len) {
193        const base = switch (@intFromEnum(reg)) {
194            // zig fmt: off
195            @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => @intFromEnum(Register.zero),
196            @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => @intFromEnum(Register.ft0),
197            @intFromEnum(Register.v0)   ... @intFromEnum(Register.v31) => @intFromEnum(Register.v0),
198            else => unreachable,
199            // zig fmt: on
200        };
201
202        return @intCast(base + reg.encodeId());
203    }
204
205    pub fn encodeId(reg: Register) u5 {
206        return @truncate(@intFromEnum(reg));
207    }
208
209    pub fn dwarfNum(reg: Register) u8 {
210        return reg.id();
211    }
212
213    pub fn bitSize(reg: Register, zcu: *const Zcu) u32 {
214        return switch (@intFromEnum(reg)) {
215            // zig fmt: off
216            @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => 64,
217            @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => if (zcu.getTarget().cpu.has(.riscv, .d)) 64 else 32,
218            @intFromEnum(Register.v0)   ... @intFromEnum(Register.v31) => 256, // TODO: look at suggestVectorSize
219            else => unreachable,
220            // zig fmt: on
221        };
222    }
223
224    pub fn class(reg: Register) abi.RegisterClass {
225        return switch (@intFromEnum(reg)) {
226            // zig fmt: off
227            @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => .int,
228            @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => .float,
229            @intFromEnum(Register.v0)   ... @intFromEnum(Register.v31) => .vector,
230            else => unreachable,
231            // zig fmt: on
232        };
233    }
234};
235
236pub const FrameIndex = enum(u32) {
237    /// This index refers to the return address.
238    ret_addr,
239    /// This index refers to the frame pointer.
240    base_ptr,
241    /// This index refers to the entire stack frame.
242    stack_frame,
243    /// This index referes to where in the stack frame the args are spilled to.
244    args_frame,
245    /// This index referes to a frame dedicated to setting up args for function called
246    /// in this function. Useful for aligning args separately.
247    call_frame,
248    /// This index referes to the frame where callee saved registers are spilled and restored from.
249    spill_frame,
250    /// Other indices are used for local variable stack slots
251    _,
252
253    pub const named_count = @typeInfo(FrameIndex).@"enum".fields.len;
254
255    pub fn isNamed(fi: FrameIndex) bool {
256        return @intFromEnum(fi) < named_count;
257    }
258};
259
260/// A linker symbol not yet allocated in VM.
261pub const Symbol = struct {
262    /// Index of the containing atom.
263    atom_index: u32,
264    /// Index into the linker's symbol table.
265    sym_index: u32,
266};
267
268pub const VType = packed struct(u8) {
269    vlmul: VlMul,
270    vsew: VSew,
271    vta: bool,
272    vma: bool,
273};
274
275const VSew = enum(u3) {
276    @"8" = 0b000,
277    @"16" = 0b001,
278    @"32" = 0b010,
279    @"64" = 0b011,
280};
281
282const VlMul = enum(u3) {
283    mf8 = 0b101,
284    mf4 = 0b110,
285    mf2 = 0b111,
286    m1 = 0b000,
287    m2 = 0b001,
288    m4 = 0b010,
289    m8 = 0b011,
290};