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