authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-04-03 00:15:56-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
log6740c1f0849dd2615859e4d65df355087165e073
tree71e28e23a53dea1dd6a882a2c737ed1ad3a3c14c
parent9d0bb6371df31dd25e86b7ef4161852740f39f07

riscv: big rewrite to use latest liveness

this one is even harder to document then the last large overhaul. TLDR; - split apart Emit.zig into an Emit.zig and a Lower.zig - created seperate files for the encoding, and now adding a new instruction is as simple as just adding it to a couple of switch statements and providing the encoding. - relocs are handled in a more sane maner, and we have a clear defining boundary between lea_symbol and load_symbol now. - a lot of different abstractions for things like the stack, memory, registers, and others. - we're using x86_64's FrameIndex now, which simplifies a lot of the tougher design process. - a lot more that I don't have the energy to document. at this point, just read the commit itself :p

16 files changed, 3010 insertions(+), 2092 deletions(-)

lib/compiler/test_runner.zig+5-1
......@@ -252,12 +252,16 @@ pub fn mainSimple() anyerror!void {
252252
253253pub fn mainExtraSimple() !void {
254254 var pass_count: u8 = 0;
255 var skip_count: u8 = 0;
256 var fail_count: u8 = 0;
255257
256258 for (builtin.test_functions) |test_fn| {
257259 test_fn.func() catch |err| {
258260 if (err != error.SkipZigTest) {
259 @panic(test_fn.name);
261 fail_count += 1;
262 continue;
260263 }
264 skip_count += 1;
261265 continue;
262266 };
263267 pass_count += 1;
lib/std/builtin.zig+1-9
......@@ -775,15 +775,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
775775 }
776776
777777 if (builtin.zig_backend == .stage2_riscv64) {
778 asm volatile ("ecall"
779 :
780 : [number] "{a7}" (64),
781 [arg1] "{a0}" (1),
782 [arg2] "{a1}" (@intFromPtr(msg.ptr)),
783 [arg3] "{a2}" (msg.len),
784 : "rcx", "r11", "memory"
785 );
786 std.posix.exit(127);
778 unreachable;
787779 }
788780
789781 switch (builtin.os.tag) {
lib/std/start.zig+1-2
......@@ -208,8 +208,7 @@ fn wasi_start() callconv(.C) void {
208208}
209209
210210fn riscv_start() callconv(.C) noreturn {
211 const code = @call(.always_inline, callMain, .{});
212 std.process.exit(code);
211 std.process.exit(@call(.always_inline, callMain, .{}));
213212}
214213
215214fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize {
src/arch/riscv64/CodeGen.zig+1768-1000
......@@ -19,7 +19,8 @@ const Allocator = mem.Allocator;
1919const trace = @import("../../tracy.zig").trace;
2020const DW = std.dwarf;
2121const leb128 = std.leb;
22const log = std.log.scoped(.codegen);
22const log = std.log.scoped(.riscv_codegen);
23const tracking_log = std.log.scoped(.tracking);
2324const build_options = @import("build_options");
2425const codegen = @import("../../codegen.zig");
2526const Alignment = InternPool.Alignment;
......@@ -31,6 +32,9 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
3132const bits = @import("bits.zig");
3233const abi = @import("abi.zig");
3334const Register = bits.Register;
35const Immediate = bits.Immediate;
36const Memory = bits.Memory;
37const FrameIndex = bits.FrameIndex;
3438const RegisterManager = abi.RegisterManager;
3539const RegisterLock = RegisterManager.RegisterLock;
3640const callee_preserved_regs = abi.callee_preserved_regs;
......@@ -58,11 +62,10 @@ code: *std.ArrayList(u8),
5862debug_output: DebugInfoOutput,
5963err_msg: ?*ErrorMsg,
6064args: []MCValue,
61ret_mcv: MCValue,
65ret_mcv: InstTracking,
6266fn_type: Type,
6367arg_index: usize,
6468src_loc: Module.SrcLoc,
65stack_align: Alignment,
6669
6770/// MIR Instructions
6871mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
......@@ -73,6 +76,8 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{},
7376end_di_line: u32,
7477end_di_column: u32,
7578
79scope_generation: u32,
80
7681/// The value is an offset into the `Function` `code` from the beginning.
7782/// To perform the reloc, write 32-bit signed little-endian integer
7883/// which is a relative jump, based on the address following the reloc.
......@@ -91,14 +96,12 @@ branch_stack: *std.ArrayList(Branch),
9196blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
9297register_manager: RegisterManager = .{},
9398
94/// Maps offset to what is stored there.
95stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
99const_tracking: ConstTrackingMap = .{},
100inst_tracking: InstTrackingMap = .{},
96101
97/// Offset from the stack base, representing the end of the stack frame.
98max_end_stack: u32 = 0,
99/// Represents the current end stack offset. If there is no existing slot
100/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
101next_stack_offset: u32 = 0,
102frame_allocs: std.MultiArrayList(FrameAlloc) = .{},
103free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{},
104frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{},
102105
103106/// Debug field, used to find bugs in the compiler.
104107air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
......@@ -107,6 +110,7 @@ const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}
107110
108111const SymbolOffset = struct { sym: u32, off: i32 = 0 };
109112const RegisterOffset = struct { reg: Register, off: i32 = 0 };
113pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
110114
111115const MCValue = union(enum) {
112116 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
......@@ -116,7 +120,8 @@ const MCValue = union(enum) {
116120 /// Control flow will not allow this value to be observed.
117121 unreach,
118122 /// No more references to this value remain.
119 dead,
123 /// The payload is the value of scope_generation at the point where the death occurred
124 dead: u32,
120125 /// The value is undefined.
121126 undef,
122127 /// A pointer-sized integer that fits in a register.
......@@ -125,7 +130,7 @@ const MCValue = union(enum) {
125130 /// The value doesn't exist in memory yet.
126131 load_symbol: SymbolOffset,
127132 /// The address of the memory location not-yet-allocated by the linker.
128 addr_symbol: SymbolOffset,
133 lea_symbol: SymbolOffset,
129134 /// The value is in a target-specific register.
130135 register: Register,
131136 /// The value is split across two registers
......@@ -133,16 +138,21 @@ const MCValue = union(enum) {
133138 /// The value is in memory at a hard-coded address.
134139 /// If the type is a pointer, it means the pointer address is at this memory location.
135140 memory: u64,
136 /// The value is one of the stack variables.
137 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
138 stack_offset: u32,
139 /// The value is a pointer to one of the stack variables (payload is stack offset).
140 ptr_stack_offset: u32,
141 /// The value stored at an offset from a frame index
142 /// Payload is a frame address.
143 load_frame: FrameAddr,
144 /// The address of an offset from a frame index
145 /// Payload is a frame address.
146 lea_frame: FrameAddr,
141147 air_ref: Air.Inst.Ref,
142148 /// The value is in memory at a constant offset from the address in a register.
143149 indirect: RegisterOffset,
144150 /// The value is a constant offset from the value in a register.
145151 register_offset: RegisterOffset,
152 /// This indicates that we have already allocated a frame index for this instruction,
153 /// but it has not been spilled there yet in the current control flow.
154 /// Payload is a frame index.
155 reserved_frame: FrameIndex,
146156
147157 fn isMemory(mcv: MCValue) bool {
148158 return switch (mcv) {
......@@ -166,16 +176,17 @@ const MCValue = union(enum) {
166176
167177 .immediate,
168178 .memory,
169 .ptr_stack_offset,
179 .lea_frame,
170180 .undef,
171 .addr_symbol,
181 .lea_symbol,
172182 .air_ref,
183 .reserved_frame,
173184 => false,
174185
175186 .register,
176187 .register_pair,
177188 .register_offset,
178 .stack_offset,
189 .load_frame,
179190 .load_symbol,
180191 .indirect,
181192 => true,
......@@ -188,18 +199,19 @@ const MCValue = union(enum) {
188199 .unreach,
189200 .dead,
190201 .immediate,
191 .ptr_stack_offset,
202 .lea_frame,
192203 .register_offset,
193204 .register_pair,
194205 .register,
195206 .undef,
196207 .air_ref,
197 .addr_symbol,
208 .lea_symbol,
209 .reserved_frame,
198210 => unreachable, // not in memory
199211
200 .load_symbol => |sym_off| .{ .addr_symbol = sym_off },
212 .load_symbol => |sym_off| .{ .lea_symbol = sym_off },
201213 .memory => |addr| .{ .immediate = addr },
202 .stack_offset => |off| .{ .ptr_stack_offset = off },
214 .load_frame => |off| .{ .lea_frame = off },
203215 .indirect => |reg_off| switch (reg_off.off) {
204216 0 => .{ .register = reg_off.reg },
205217 else => .{ .register_offset = reg_off },
......@@ -216,16 +228,17 @@ const MCValue = union(enum) {
216228 .indirect,
217229 .undef,
218230 .air_ref,
219 .stack_offset,
231 .load_frame,
220232 .register_pair,
221233 .load_symbol,
234 .reserved_frame,
222235 => unreachable, // not a pointer
223236
224237 .immediate => |addr| .{ .memory = addr },
225 .ptr_stack_offset => |off| .{ .stack_offset = off },
238 .lea_frame => |off| .{ .load_frame = off },
226239 .register => |reg| .{ .indirect = .{ .reg = reg } },
227240 .register_offset => |reg_off| .{ .indirect = reg_off },
228 .addr_symbol => |sym_off| .{ .load_symbol = sym_off },
241 .lea_symbol => |sym_off| .{ .load_symbol = sym_off },
229242 };
230243 }
231244
......@@ -236,13 +249,14 @@ const MCValue = union(enum) {
236249 .dead,
237250 .undef,
238251 .air_ref,
252 .reserved_frame,
239253 => unreachable, // not valid
240254 .register_pair,
241255 .memory,
242256 .indirect,
243 .stack_offset,
257 .load_frame,
244258 .load_symbol,
245 .addr_symbol,
259 .lea_symbol,
246260 => switch (off) {
247261 0 => mcv,
248262 else => unreachable, // not offsettable
......@@ -250,7 +264,26 @@ const MCValue = union(enum) {
250264 .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) },
251265 .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } },
252266 .register_offset => |reg_off| .{ .register_offset = .{ .reg = reg_off.reg, .off = reg_off.off + off } },
253 .ptr_stack_offset => |stack_off| .{ .ptr_stack_offset = @intCast(@as(i64, @intCast(stack_off)) +% off) },
267 .lea_frame => |frame_addr| .{
268 .lea_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off },
269 },
270 };
271 }
272
273 fn getReg(mcv: MCValue) ?Register {
274 return switch (mcv) {
275 .register => |reg| reg,
276 .register_offset, .indirect => |ro| ro.reg,
277 else => null,
278 };
279 }
280
281 fn getRegs(mcv: *const MCValue) []const Register {
282 return switch (mcv.*) {
283 .register => |*reg| @as(*const [1]Register, reg),
284 .register_pair => |*regs| regs,
285 .register_offset, .indirect => |*ro| @as(*const [1]Register, &ro.reg),
286 else => &.{},
254287 };
255288 }
256289};
......@@ -264,6 +297,265 @@ const Branch = struct {
264297 }
265298};
266299
300const InstTrackingMap = std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InstTracking);
301const ConstTrackingMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, InstTracking);
302const InstTracking = struct {
303 long: MCValue,
304 short: MCValue,
305
306 fn init(result: MCValue) InstTracking {
307 return .{ .long = switch (result) {
308 .none,
309 .unreach,
310 .undef,
311 .immediate,
312 .memory,
313 .load_frame,
314 .lea_frame,
315 .load_symbol,
316 .lea_symbol,
317 => result,
318 .dead,
319 .reserved_frame,
320 .air_ref,
321 => unreachable,
322 .register,
323 .register_pair,
324 .register_offset,
325 .indirect,
326 => .none,
327 }, .short = result };
328 }
329
330 fn getReg(self: InstTracking) ?Register {
331 return self.short.getReg();
332 }
333
334 fn getRegs(self: *const InstTracking) []const Register {
335 return self.short.getRegs();
336 }
337
338 fn spill(self: *InstTracking, function: *Self, inst: Air.Inst.Index) !void {
339 if (std.meta.eql(self.long, self.short)) return; // Already spilled
340 // Allocate or reuse frame index
341 switch (self.long) {
342 .none => self.long = try function.allocRegOrMem(inst, false),
343 .load_frame => {},
344 .reserved_frame => |index| self.long = .{ .load_frame = .{ .index = index } },
345 else => unreachable,
346 }
347 tracking_log.debug("spill %{d} from {} to {}", .{ inst, self.short, self.long });
348 try function.genCopy(function.typeOfIndex(inst), self.long, self.short);
349 }
350
351 fn reuseFrame(self: *InstTracking) void {
352 switch (self.long) {
353 .reserved_frame => |index| self.long = .{ .load_frame = .{ .index = index } },
354 else => {},
355 }
356 self.short = switch (self.long) {
357 .none,
358 .unreach,
359 .undef,
360 .immediate,
361 .memory,
362 .load_frame,
363 .lea_frame,
364 .load_symbol,
365 .lea_symbol,
366 => self.long,
367 .dead,
368 .register,
369 .register_pair,
370 .register_offset,
371 .indirect,
372 .reserved_frame,
373 .air_ref,
374 => unreachable,
375 };
376 }
377
378 fn trackSpill(self: *InstTracking, function: *Self, inst: Air.Inst.Index) !void {
379 try function.freeValue(self.short);
380 self.reuseFrame();
381 tracking_log.debug("%{d} => {} (spilled)", .{ inst, self.* });
382 }
383
384 fn verifyMaterialize(self: InstTracking, target: InstTracking) void {
385 switch (self.long) {
386 .none,
387 .unreach,
388 .undef,
389 .immediate,
390 .memory,
391 .lea_frame,
392 .load_symbol,
393 .lea_symbol,
394 => assert(std.meta.eql(self.long, target.long)),
395 .load_frame,
396 .reserved_frame,
397 => switch (target.long) {
398 .none,
399 .load_frame,
400 .reserved_frame,
401 => {},
402 else => unreachable,
403 },
404 .dead,
405 .register,
406 .register_pair,
407 .register_offset,
408 .indirect,
409 .air_ref,
410 => unreachable,
411 }
412 }
413
414 fn materialize(
415 self: *InstTracking,
416 function: *Self,
417 inst: Air.Inst.Index,
418 target: InstTracking,
419 ) !void {
420 self.verifyMaterialize(target);
421 try self.materializeUnsafe(function, inst, target);
422 }
423
424 fn materializeUnsafe(
425 self: InstTracking,
426 function: *Self,
427 inst: Air.Inst.Index,
428 target: InstTracking,
429 ) !void {
430 const ty = function.typeOfIndex(inst);
431 if ((self.long == .none or self.long == .reserved_frame) and target.long == .load_frame)
432 try function.genCopy(ty, target.long, self.short);
433 try function.genCopy(ty, target.short, self.short);
434 }
435
436 fn trackMaterialize(self: *InstTracking, inst: Air.Inst.Index, target: InstTracking) void {
437 self.verifyMaterialize(target);
438 // Don't clobber reserved frame indices
439 self.long = if (target.long == .none) switch (self.long) {
440 .load_frame => |addr| .{ .reserved_frame = addr.index },
441 .reserved_frame => self.long,
442 else => target.long,
443 } else target.long;
444 self.short = target.short;
445 tracking_log.debug("%{d} => {} (materialize)", .{ inst, self.* });
446 }
447
448 fn resurrect(self: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void {
449 switch (self.short) {
450 .dead => |die_generation| if (die_generation >= scope_generation) {
451 self.reuseFrame();
452 tracking_log.debug("%{d} => {} (resurrect)", .{ inst, self.* });
453 },
454 else => {},
455 }
456 }
457
458 fn die(self: *InstTracking, function: *Self, inst: Air.Inst.Index) !void {
459 if (self.short == .dead) return;
460 try function.freeValue(self.short);
461 self.short = .{ .dead = function.scope_generation };
462 tracking_log.debug("%{d} => {} (death)", .{ inst, self.* });
463 }
464
465 fn reuse(
466 self: *InstTracking,
467 function: *Self,
468 new_inst: ?Air.Inst.Index,
469 old_inst: Air.Inst.Index,
470 ) void {
471 self.short = .{ .dead = function.scope_generation };
472 if (new_inst) |inst|
473 tracking_log.debug("%{d} => {} (reuse %{d})", .{ inst, self.*, old_inst })
474 else
475 tracking_log.debug("tmp => {} (reuse %{d})", .{ self.*, old_inst });
476 }
477
478 fn liveOut(self: *InstTracking, function: *Self, inst: Air.Inst.Index) void {
479 for (self.getRegs()) |reg| {
480 if (function.register_manager.isRegFree(reg)) {
481 tracking_log.debug("%{d} => {} (live-out)", .{ inst, self.* });
482 continue;
483 }
484
485 const index = RegisterManager.indexOfRegIntoTracked(reg).?;
486 const tracked_inst = function.register_manager.registers[index];
487 const tracking = function.getResolvedInstValue(tracked_inst);
488
489 // Disable death.
490 var found_reg = false;
491 var remaining_reg: Register = .zero;
492 for (tracking.getRegs()) |tracked_reg| if (tracked_reg.id() == reg.id()) {
493 assert(!found_reg);
494 found_reg = true;
495 } else {
496 assert(remaining_reg == .zero);
497 remaining_reg = tracked_reg;
498 };
499 assert(found_reg);
500 tracking.short = switch (remaining_reg) {
501 .zero => .{ .dead = function.scope_generation },
502 else => .{ .register = remaining_reg },
503 };
504
505 // Perform side-effects of freeValue manually.
506 function.register_manager.freeReg(reg);
507
508 tracking_log.debug("%{d} => {} (live-out %{d})", .{ inst, self.*, tracked_inst });
509 }
510 }
511
512 pub fn format(
513 self: InstTracking,
514 comptime _: []const u8,
515 _: std.fmt.FormatOptions,
516 writer: anytype,
517 ) @TypeOf(writer).Error!void {
518 if (!std.meta.eql(self.long, self.short)) try writer.print("|{}| ", .{self.long});
519 try writer.print("{}", .{self.short});
520 }
521};
522
523const FrameAlloc = struct {
524 abi_size: u31,
525 spill_pad: u3,
526 abi_align: Alignment,
527 ref_count: u16,
528
529 fn init(alloc_abi: struct { size: u64, pad: u3 = 0, alignment: Alignment }) FrameAlloc {
530 return .{
531 .abi_size = @intCast(alloc_abi.size),
532 .spill_pad = alloc_abi.pad,
533 .abi_align = alloc_abi.alignment,
534 .ref_count = 0,
535 };
536 }
537 fn initType(ty: Type, zcu: *Module) FrameAlloc {
538 return init(.{
539 .size = ty.abiSize(zcu),
540 .alignment = ty.abiAlignment(zcu),
541 });
542 }
543 fn initSpill(ty: Type, zcu: *Module) FrameAlloc {
544 const abi_size = ty.abiSize(zcu);
545 const spill_size = if (abi_size < 8)
546 math.ceilPowerOfTwoAssert(u64, abi_size)
547 else
548 std.mem.alignForward(u64, abi_size, 8);
549 return init(.{
550 .size = spill_size,
551 .pad = @intCast(spill_size - abi_size),
552 .alignment = ty.abiAlignment(zcu).maxStrict(
553 Alignment.fromNonzeroByteUnits(@min(spill_size, 8)),
554 ),
555 });
556 }
557};
558
267559const StackAllocation = struct {
268560 inst: ?Air.Inst.Index,
269561 /// TODO: make the size inferred from the bits of the inst
......@@ -271,36 +563,127 @@ const StackAllocation = struct {
271563};
272564
273565const BlockData = struct {
274 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
275 /// The first break instruction encounters `null` here and chooses a
276 /// machine code value for the block result, populating this field.
277 /// Following break instructions encounter that value and use it for
278 /// the location to store their block results.
279 mcv: MCValue,
566 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
567 state: State,
568
569 fn deinit(self: *BlockData, gpa: Allocator) void {
570 self.relocs.deinit(gpa);
571 self.* = undefined;
572 }
280573};
281574
282const BigTomb = struct {
283 function: *Self,
284 inst: Air.Inst.Index,
285 lbt: Liveness.BigTomb,
575const State = struct {
576 registers: RegisterManager.TrackedRegisters,
577 reg_tracking: [RegisterManager.RegisterBitSet.bit_length]InstTracking,
578 free_registers: RegisterManager.RegisterBitSet,
579 inst_tracking_len: u32,
580 scope_generation: u32,
581};
582
583fn initRetroactiveState(self: *Self) State {
584 var state: State = undefined;
585 state.inst_tracking_len = @intCast(self.inst_tracking.count());
586 state.scope_generation = self.scope_generation;
587 return state;
588}
286589
287 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
288 const dies = bt.lbt.feed();
289 const op_index = op_ref.toIndex() orelse return;
290 if (!dies) return;
291 bt.function.processDeath(op_index);
590fn saveRetroactiveState(self: *Self, state: *State) !void {
591 const free_registers = self.register_manager.free_registers;
592 var it = free_registers.iterator(.{ .kind = .unset });
593 while (it.next()) |index| {
594 const tracked_inst = self.register_manager.registers[index];
595 state.registers[index] = tracked_inst;
596 state.reg_tracking[index] = self.inst_tracking.get(tracked_inst).?;
292597 }
598 state.free_registers = free_registers;
599}
600
601fn saveState(self: *Self) !State {
602 var state = self.initRetroactiveState();
603 try self.saveRetroactiveState(&state);
604 return state;
605}
606
607fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, comptime opts: struct {
608 emit_instructions: bool,
609 update_tracking: bool,
610 resurrect: bool,
611 close_scope: bool,
612}) !void {
613 if (opts.close_scope) {
614 for (
615 self.inst_tracking.keys()[state.inst_tracking_len..],
616 self.inst_tracking.values()[state.inst_tracking_len..],
617 ) |inst, *tracking| try tracking.die(self, inst);
618 self.inst_tracking.shrinkRetainingCapacity(state.inst_tracking_len);
619 }
620
621 if (opts.resurrect) for (
622 self.inst_tracking.keys()[0..state.inst_tracking_len],
623 self.inst_tracking.values()[0..state.inst_tracking_len],
624 ) |inst, *tracking| tracking.resurrect(inst, state.scope_generation);
625 for (deaths) |death| try self.processDeath(death);
626
627 const ExpectedContents = [@typeInfo(RegisterManager.TrackedRegisters).Array.len]RegisterLock;
628 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =
629 if (opts.update_tracking)
630 {} else std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
631
632 var reg_locks = if (opts.update_tracking) {} else try std.ArrayList(RegisterLock).initCapacity(
633 stack.get(),
634 @typeInfo(ExpectedContents).Array.len,
635 );
636 defer if (!opts.update_tracking) {
637 for (reg_locks.items) |lock| self.register_manager.unlockReg(lock);
638 reg_locks.deinit();
639 };
293640
294 fn finishAir(bt: *BigTomb, result: MCValue) void {
295 const is_used = !bt.function.liveness.isUnused(bt.inst);
296 if (is_used) {
297 log.debug("%{d} => {}", .{ bt.inst, result });
298 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
299 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
641 for (0..state.registers.len) |index| {
642 const current_maybe_inst = if (self.register_manager.free_registers.isSet(index))
643 null
644 else
645 self.register_manager.registers[index];
646 const target_maybe_inst = if (state.free_registers.isSet(index))
647 null
648 else
649 state.registers[index];
650 if (std.debug.runtime_safety) if (target_maybe_inst) |target_inst|
651 assert(self.inst_tracking.getIndex(target_inst).? < state.inst_tracking_len);
652 if (opts.emit_instructions) {
653 if (current_maybe_inst) |current_inst| {
654 try self.inst_tracking.getPtr(current_inst).?.spill(self, current_inst);
655 }
656 if (target_maybe_inst) |target_inst| {
657 const target_tracking = self.inst_tracking.getPtr(target_inst).?;
658 try target_tracking.materialize(self, target_inst, state.reg_tracking[index]);
659 }
300660 }
301 bt.function.finishAirBookkeeping();
661 if (opts.update_tracking) {
662 if (current_maybe_inst) |current_inst| {
663 try self.inst_tracking.getPtr(current_inst).?.trackSpill(self, current_inst);
664 }
665 {
666 const reg = RegisterManager.regAtTrackedIndex(@intCast(index));
667 self.register_manager.freeReg(reg);
668 self.register_manager.getRegAssumeFree(reg, target_maybe_inst);
669 }
670 if (target_maybe_inst) |target_inst| {
671 self.inst_tracking.getPtr(target_inst).?.trackMaterialize(
672 target_inst,
673 state.reg_tracking[index],
674 );
675 }
676 } else if (target_maybe_inst) |_|
677 try reg_locks.append(self.register_manager.lockRegIndexAssumeUnused(@intCast(index)));
302678 }
303};
679
680 if (opts.update_tracking and std.debug.runtime_safety) {
681 assert(self.register_manager.free_registers.eql(state.free_registers));
682 var used_reg_it = state.free_registers.iterator(.{ .kind = .unset });
683 while (used_reg_it.next()) |index|
684 assert(self.register_manager.registers[index] == state.registers[index]);
685 }
686}
304687
305688const Self = @This();
306689
......@@ -310,7 +693,7 @@ const CallView = enum(u1) {
310693};
311694
312695pub fn generate(
313 lf: *link.File,
696 bin_file: *link.File,
314697 src_loc: Module.SrcLoc,
315698 func_index: InternPool.Index,
316699 air: Air,
......@@ -318,14 +701,17 @@ pub fn generate(
318701 code: *std.ArrayList(u8),
319702 debug_output: DebugInfoOutput,
320703) CodeGenError!Result {
321 const gpa = lf.comp.gpa;
322 const zcu = lf.comp.module.?;
704 const comp = bin_file.comp;
705 const gpa = comp.gpa;
706 const zcu = comp.module.?;
707 const ip = &zcu.intern_pool;
323708 const func = zcu.funcInfo(func_index);
324709 const fn_owner_decl = zcu.declPtr(func.owner_decl);
325710 assert(fn_owner_decl.has_tv);
326711 const fn_type = fn_owner_decl.typeOf(zcu);
327712 const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace);
328713 const target = &namespace.file_scope.mod.resolved_target.result;
714 const mod = namespace.file_scope.mod;
329715
330716 var branch_stack = std.ArrayList(Branch).init(gpa);
331717 defer {
......@@ -340,7 +726,7 @@ pub fn generate(
340726 .air = air,
341727 .liveness = liveness,
342728 .target = target,
343 .bin_file = lf,
729 .bin_file = bin_file,
344730 .func_index = func_index,
345731 .code = code,
346732 .debug_output = debug_output,
......@@ -351,15 +737,39 @@ pub fn generate(
351737 .arg_index = 0,
352738 .branch_stack = &branch_stack,
353739 .src_loc = src_loc,
354 .stack_align = undefined,
355740 .end_di_line = func.rbrace_line,
356741 .end_di_column = func.rbrace_column,
742 .scope_generation = 0,
357743 };
358 defer function.stack.deinit(gpa);
359 defer function.blocks.deinit(gpa);
360 defer function.exitlude_jump_relocs.deinit(gpa);
744 defer {
745 function.frame_allocs.deinit(gpa);
746 function.free_frame_indices.deinit(gpa);
747 function.frame_locs.deinit(gpa);
748 var block_it = function.blocks.valueIterator();
749 while (block_it.next()) |block| block.deinit(gpa);
750 function.blocks.deinit(gpa);
751 function.inst_tracking.deinit(gpa);
752 function.const_tracking.deinit(gpa);
753 function.exitlude_jump_relocs.deinit(gpa);
754 function.mir_instructions.deinit(gpa);
755 function.mir_extra.deinit(gpa);
756 }
757
758 try function.frame_allocs.resize(gpa, FrameIndex.named_count);
759 function.frame_allocs.set(
760 @intFromEnum(FrameIndex.stack_frame),
761 FrameAlloc.init(.{
762 .size = 0,
763 .alignment = func.analysis(ip).stack_alignment.max(.@"1"),
764 }),
765 );
766 function.frame_allocs.set(
767 @intFromEnum(FrameIndex.call_frame),
768 FrameAlloc.init(.{ .size = 0, .alignment = .@"1" }),
769 );
361770
362 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
771 const fn_info = zcu.typeToFunc(fn_type).?;
772 var call_info = function.resolveCallingConventionValues(fn_info) catch |err| switch (err) {
363773 error.CodegenFail => return Result{ .fail = function.err_msg.? },
364774 error.OutOfRegisters => return Result{
365775 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
......@@ -371,8 +781,25 @@ pub fn generate(
371781
372782 function.args = call_info.args;
373783 function.ret_mcv = call_info.return_value;
374 function.stack_align = call_info.stack_align;
375 function.max_end_stack = call_info.stack_byte_count;
784 function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), FrameAlloc.init(.{
785 .size = Type.usize.abiSize(zcu),
786 .alignment = Type.usize.abiAlignment(zcu).min(call_info.stack_align),
787 }));
788 function.frame_allocs.set(@intFromEnum(FrameIndex.base_ptr), FrameAlloc.init(.{
789 .size = Type.usize.abiSize(zcu),
790 .alignment = Alignment.min(
791 call_info.stack_align,
792 Alignment.fromNonzeroByteUnits(function.target.stackAlignment()),
793 ),
794 }));
795 function.frame_allocs.set(@intFromEnum(FrameIndex.args_frame), FrameAlloc.init(.{
796 .size = call_info.stack_byte_count,
797 .alignment = call_info.stack_align,
798 }));
799 function.frame_allocs.set(@intFromEnum(FrameIndex.spill_frame), FrameAlloc.init(.{
800 .size = 0,
801 .alignment = Type.usize.abiAlignment(zcu),
802 }));
376803
377804 function.gen() catch |err| switch (err) {
378805 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -382,41 +809,47 @@ pub fn generate(
382809 else => |e| return e,
383810 };
384811
385 // Create list of registers to save in the prologue.
386 var save_reg_list = Mir.RegisterList{};
387 for (callee_preserved_regs) |reg| {
388 if (function.register_manager.isRegAllocated(reg)) {
389 save_reg_list.push(&callee_preserved_regs, reg);
390 }
391 }
392
393812 var mir = Mir{
394813 .instructions = function.mir_instructions.toOwnedSlice(),
395814 .extra = try function.mir_extra.toOwnedSlice(gpa),
815 .frame_locs = function.frame_locs.toOwnedSlice(),
396816 };
397817 defer mir.deinit(gpa);
398818
399819 var emit = Emit{
400 .mir = mir,
401 .bin_file = lf,
820 .lower = .{
821 .bin_file = bin_file,
822 .allocator = gpa,
823 .mir = mir,
824 .cc = fn_info.cc,
825 .src_loc = src_loc,
826 .output_mode = comp.config.output_mode,
827 .link_mode = comp.config.link_mode,
828 .pic = mod.pic,
829 },
402830 .debug_output = debug_output,
403 .target = target,
404 .src_loc = src_loc,
405831 .code = code,
406832 .prev_di_pc = 0,
407833 .prev_di_line = func.lbrace_line,
408834 .prev_di_column = func.lbrace_column,
409 .code_offset_mapping = .{},
410 // need to at least decrease the sp by -8
411 .stack_size = @max(8, mem.alignForward(u32, function.max_end_stack, 16)),
412 .save_reg_list = save_reg_list,
413 .output_mode = lf.comp.config.output_mode,
414 .link_mode = lf.comp.config.link_mode,
415835 };
416836 defer emit.deinit();
417837
418838 emit.emitMir() catch |err| switch (err) {
419 error.EmitFail => return Result{ .fail = emit.err_msg.? },
839 error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },
840 error.InvalidInstruction => |e| {
841 const msg = switch (e) {
842 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
843 };
844 return Result{
845 .fail = try ErrorMsg.create(
846 gpa,
847 src_loc,
848 "{s} This is a bug in the Zig compiler.",
849 .{msg},
850 ),
851 };
852 },
420853 else => |e| return e,
421854 };
422855
......@@ -438,9 +871,26 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
438871}
439872
440873fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
441 return try self.addInst(.{
874 return self.addInst(.{
442875 .tag = .nop,
443 .data = .{ .nop = {} },
876 .ops = .none,
877 .data = undefined,
878 });
879}
880
881fn addPseudoNone(self: *Self, ops: Mir.Inst.Ops) !void {
882 _ = try self.addInst(.{
883 .tag = .pseudo,
884 .ops = ops,
885 .data = undefined,
886 });
887}
888
889fn addPseudo(self: *Self, ops: Mir.Inst.Ops) !Mir.Inst.Index {
890 return self.addInst(.{
891 .tag = .pseudo,
892 .ops = ops,
893 .data = undefined,
444894 });
445895}
446896
......@@ -464,22 +914,132 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
464914}
465915
466916fn gen(self: *Self) !void {
467 _ = try self.addInst(.{
468 .tag = .psuedo_prologue,
469 .data = .{ .nop = {} }, // Backpatched later.
470 });
917 const mod = self.bin_file.comp.module.?;
918 const fn_info = mod.typeToFunc(self.fn_type).?;
471919
472 _ = try self.addInst(.{
473 .tag = .dbg_prologue_end,
474 .data = .{ .nop = {} },
475 });
920 if (fn_info.cc != .Naked) {
921 try self.addPseudoNone(.pseudo_dbg_prologue_end);
922
923 const backpatch_stack_alloc = try self.addPseudo(.pseudo_dead);
924 const backpatch_ra_spill = try self.addPseudo(.pseudo_dead);
925 const backpatch_fp_spill = try self.addPseudo(.pseudo_dead);
926 const backpatch_fp_add = try self.addPseudo(.pseudo_dead);
927 const backpatch_spill_callee_preserved_regs = try self.addPseudo(.pseudo_dead);
928
929 try self.genBody(self.air.getMainBody());
930
931 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
932 self.mir_instructions.items(.data)[jmp_reloc].inst =
933 @intCast(self.mir_instructions.len);
934 }
935
936 try self.addPseudoNone(.pseudo_dbg_epilogue_begin);
937
938 const backpatch_restore_callee_preserved_regs = try self.addPseudo(.pseudo_dead);
939 const backpatch_ra_restore = try self.addPseudo(.pseudo_dead);
940 const backpatch_fp_restore = try self.addPseudo(.pseudo_dead);
941 const backpatch_stack_alloc_restore = try self.addPseudo(.pseudo_dead);
942 try self.addPseudoNone(.pseudo_ret);
476943
477 try self.genBody(self.air.getMainBody());
944 const frame_layout = try self.computeFrameLayout();
945 const need_save_reg = frame_layout.save_reg_list.count() > 0;
946
947 self.mir_instructions.set(backpatch_stack_alloc, .{
948 .tag = .addi,
949 .ops = .rri,
950 .data = .{ .i_type = .{
951 .rd = .sp,
952 .rs1 = .sp,
953 .imm12 = Immediate.s(-@as(i32, @intCast(frame_layout.stack_adjust))),
954 } },
955 });
956 self.mir_instructions.set(backpatch_ra_spill, .{
957 .tag = .pseudo,
958 .ops = .pseudo_store_rm,
959 .data = .{ .rm = .{
960 .r = .ra,
961 .m = .{
962 .base = .{ .frame = .ret_addr },
963 .mod = .{ .rm = .{ .size = .dword } },
964 },
965 } },
966 });
967 self.mir_instructions.set(backpatch_ra_restore, .{
968 .tag = .pseudo,
969 .ops = .pseudo_load_rm,
970 .data = .{ .rm = .{
971 .r = .ra,
972 .m = .{
973 .base = .{ .frame = .ret_addr },
974 .mod = .{ .rm = .{ .size = .dword } },
975 },
976 } },
977 });
978 self.mir_instructions.set(backpatch_fp_spill, .{
979 .tag = .pseudo,
980 .ops = .pseudo_store_rm,
981 .data = .{ .rm = .{
982 .r = .s0,
983 .m = .{
984 .base = .{ .frame = .base_ptr },
985 .mod = .{ .rm = .{ .size = .dword } },
986 },
987 } },
988 });
989 self.mir_instructions.set(backpatch_fp_restore, .{
990 .tag = .pseudo,
991 .ops = .pseudo_load_rm,
992 .data = .{ .rm = .{
993 .r = .s0,
994 .m = .{
995 .base = .{ .frame = .base_ptr },
996 .mod = .{ .rm = .{ .size = .dword } },
997 },
998 } },
999 });
1000 self.mir_instructions.set(backpatch_fp_add, .{
1001 .tag = .addi,
1002 .ops = .rri,
1003 .data = .{ .i_type = .{
1004 .rd = .s0,
1005 .rs1 = .sp,
1006 .imm12 = Immediate.s(@intCast(frame_layout.stack_adjust)),
1007 } },
1008 });
1009 self.mir_instructions.set(backpatch_stack_alloc_restore, .{
1010 .tag = .addi,
1011 .ops = .rri,
1012 .data = .{ .i_type = .{
1013 .rd = .sp,
1014 .rs1 = .sp,
1015 .imm12 = Immediate.s(@intCast(frame_layout.stack_adjust)),
1016 } },
1017 });
1018
1019 if (need_save_reg) {
1020 self.mir_instructions.set(backpatch_spill_callee_preserved_regs, .{
1021 .tag = .pseudo,
1022 .ops = .pseudo_spill_regs,
1023 .data = .{ .reg_list = frame_layout.save_reg_list },
1024 });
1025
1026 self.mir_instructions.set(backpatch_restore_callee_preserved_regs, .{
1027 .tag = .pseudo,
1028 .ops = .pseudo_restore_regs,
1029 .data = .{ .reg_list = frame_layout.save_reg_list },
1030 });
1031 }
1032 } else {
1033 try self.addPseudoNone(.pseudo_dbg_prologue_end);
1034 try self.genBody(self.air.getMainBody());
1035 try self.addPseudoNone(.pseudo_dbg_epilogue_begin);
1036 }
4781037
4791038 // Drop them off at the rbrace.
4801039 _ = try self.addInst(.{
481 .tag = .dbg_line,
482 .data = .{ .dbg_line_column = .{
1040 .tag = .pseudo,
1041 .ops = .pseudo_dbg_line_column,
1042 .data = .{ .pseudo_dbg_line_column = .{
4831043 .line = self.end_di_line,
4841044 .column = self.end_di_column,
4851045 } },
......@@ -487,18 +1047,15 @@ fn gen(self: *Self) !void {
4871047}
4881048
4891049fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
490 const mod = self.bin_file.comp.module.?;
491 const ip = &mod.intern_pool;
1050 const zcu = self.bin_file.comp.module.?;
1051 const ip = &zcu.intern_pool;
4921052 const air_tags = self.air.instructions.items(.tag);
4931053
4941054 for (body) |inst| {
495 // TODO: remove now-redundant isUnused calls from AIR handler functions
496 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip))
497 continue;
1055 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue;
4981056
4991057 const old_air_bookkeeping = self.air_bookkeeping;
500 try self.ensureProcessDeathCapacity(Liveness.bpi);
501
1058 try self.inst_tracking.ensureUnusedCapacity(self.gpa, 1);
5021059 switch (air_tags[@intFromEnum(inst)]) {
5031060 // zig fmt: off
5041061 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
......@@ -731,30 +1288,58 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
7311288 .work_group_id => unreachable,
7321289 // zig fmt: on
7331290 }
1291
1292 assert(!self.register_manager.lockedRegsExist());
1293
7341294 if (std.debug.runtime_safety) {
7351295 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
7361296 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
7371297 }
1298
1299 { // check consistency of tracked registers
1300 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
1301 while (it.next()) |index| {
1302 const tracked_inst = self.register_manager.registers[index];
1303 const tracking = self.getResolvedInstValue(tracked_inst);
1304 for (tracking.getRegs()) |reg| {
1305 if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break;
1306 } else return self.fail(
1307 \\%{} takes up these regs: {any}, however those regs don't use it
1308 , .{ index, tracking.getRegs() });
1309 }
1310 }
7381311 }
7391312 }
7401313}
7411314
1315fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) !void {
1316 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst);
1317}
1318
1319fn getValueIfFree(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {
1320 for (value.getRegs()) |reg| if (self.register_manager.isRegFree(reg))
1321 self.register_manager.getRegAssumeFree(reg, inst);
1322}
1323
1324fn freeValue(self: *Self, value: MCValue) !void {
1325 switch (value) {
1326 .register => |reg| self.register_manager.freeReg(reg),
1327 .register_pair => |regs| for (regs) |reg| self.register_manager.freeReg(reg),
1328 .register_offset => |reg_off| self.register_manager.freeReg(reg_off.reg),
1329 else => {}, // TODO process stack allocation death
1330 }
1331}
1332
7421333fn feed(self: *Self, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) !void {
743 if (bt.feed()) if (operand.toIndex()) |inst| self.processDeath(inst);
1334 if (bt.feed()) if (operand.toIndex()) |inst| {
1335 log.debug("feed inst: %{}", .{inst});
1336 try self.processDeath(inst);
1337 };
7441338}
7451339
7461340/// Asserts there is already capacity to insert into top branch inst_table.
747fn processDeath(self: *Self, inst: Air.Inst.Index) void {
748 // When editing this function, note that the logic must synchronize with `reuseOperand`.
749 const prev_value = self.getResolvedInstValue(inst);
750 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
751 branch.inst_table.putAssumeCapacity(inst, .dead);
752 switch (prev_value) {
753 .register => |reg| {
754 self.register_manager.freeReg(reg);
755 },
756 else => {}, // TODO process stack allocation death by freeing it to be reused later
757 }
1341fn processDeath(self: *Self, inst: Air.Inst.Index) !void {
1342 try self.inst_tracking.getPtr(inst).?.die(self, inst);
7581343}
7591344
7601345/// Called when there are no operands, and the instruction is always unreferenced.
......@@ -769,23 +1354,12 @@ fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void {
7691354 .none, .dead, .unreach => {},
7701355 else => unreachable, // Why didn't the result die?
7711356 } else {
772 log.debug("%{d} => {}", .{ inst, result });
773 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
774 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
775
776 switch (result) {
777 .register => |reg| {
778 // In some cases (such as bitcast), an operand
779 // may be the same MCValue as the result. If
780 // that operand died and was a register, it
781 // was freed by processDeath. We have to
782 // "re-allocate" the register.
783 if (self.register_manager.isRegFree(reg)) {
784 self.register_manager.getRegAssumeFree(reg, inst);
785 }
786 },
787 else => {},
788 }
1357 tracking_log.debug("%{d} => {} (birth)", .{ inst, result });
1358 self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(result));
1359 // In some cases, an operand may be reused as the result.
1360 // If that operand died and was a register, it was freed by
1361 // processDeath, so we have to "re-allocate" the register.
1362 self.getValueIfFree(result, inst);
7891363 }
7901364 self.finishAirBookkeeping();
7911365}
......@@ -801,43 +1375,153 @@ fn finishAir(
8011375 const dies = @as(u1, @truncate(tomb_bits)) != 0;
8021376 tomb_bits >>= 1;
8031377 if (!dies) continue;
804 self.processDeath(op.toIndexAllowNone() orelse continue);
1378 try self.processDeath(op.toIndexAllowNone() orelse continue);
8051379 }
8061380 self.finishAirResult(inst, result);
8071381}
8081382
1383const FrameLayout = struct {
1384 stack_adjust: u32,
1385 save_reg_list: Mir.RegisterList,
1386};
1387
1388fn setFrameLoc(
1389 self: *Self,
1390 frame_index: FrameIndex,
1391 base: Register,
1392 offset: *i32,
1393 comptime aligned: bool,
1394) void {
1395 const frame_i = @intFromEnum(frame_index);
1396 if (aligned) {
1397 const alignment: InternPool.Alignment = self.frame_allocs.items(.abi_align)[frame_i];
1398 offset.* = if (math.sign(offset.*) < 0)
1399 -1 * @as(i32, @intCast(alignment.backward(@intCast(@abs(offset.*)))))
1400 else
1401 @intCast(alignment.forward(@intCast(@abs(offset.*))));
1402 }
1403 self.frame_locs.set(frame_i, .{ .base = base, .disp = offset.* });
1404 offset.* += self.frame_allocs.items(.abi_size)[frame_i];
1405}
1406
1407fn computeFrameLayout(self: *Self) !FrameLayout {
1408 const frame_allocs_len = self.frame_allocs.len;
1409 try self.frame_locs.resize(self.gpa, frame_allocs_len);
1410 const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count);
1411 defer self.gpa.free(stack_frame_order);
1412
1413 const frame_size = self.frame_allocs.items(.abi_size);
1414 const frame_align = self.frame_allocs.items(.abi_align);
1415
1416 for (stack_frame_order, FrameIndex.named_count..) |*frame_order, frame_index|
1417 frame_order.* = @enumFromInt(frame_index);
1418
1419 {
1420 const SortContext = struct {
1421 frame_align: @TypeOf(frame_align),
1422 pub fn lessThan(context: @This(), lhs: FrameIndex, rhs: FrameIndex) bool {
1423 return context.frame_align[@intFromEnum(lhs)].compare(.gt, context.frame_align[@intFromEnum(rhs)]);
1424 }
1425 };
1426 const sort_context = SortContext{ .frame_align = frame_align };
1427 mem.sort(FrameIndex, stack_frame_order, sort_context, SortContext.lessThan);
1428 }
1429
1430 var save_reg_list = Mir.RegisterList{};
1431 for (callee_preserved_regs) |reg| {
1432 if (self.register_manager.isRegAllocated(reg)) {
1433 save_reg_list.push(&callee_preserved_regs, reg);
1434 }
1435 }
1436
1437 const total_alloc_size: i32 = blk: {
1438 var i: i32 = 0;
1439 for (stack_frame_order) |frame_index| {
1440 i += frame_size[@intFromEnum(frame_index)];
1441 }
1442 break :blk i;
1443 };
1444 const saved_reg_size = save_reg_list.size();
1445
1446 frame_size[@intFromEnum(FrameIndex.spill_frame)] = @intCast(saved_reg_size);
1447
1448 // The total frame size is calculated by the amount of s registers you need to save * 8, as each
1449 // register is 8 bytes, the total allocation sizes, and 16 more register for the spilled ra and s0
1450 // register. Finally we align the frame size to the align of the base pointer.
1451 const acc_frame_size: i32 = std.mem.alignForward(
1452 i32,
1453 total_alloc_size + 16 + frame_size[@intFromEnum(FrameIndex.args_frame)] + frame_size[@intFromEnum(FrameIndex.spill_frame)],
1454 @intCast(frame_align[@intFromEnum(FrameIndex.base_ptr)].toByteUnits().?),
1455 );
1456 log.debug("frame size: {}", .{acc_frame_size});
1457
1458 // store the ra at total_size - 8, so it's the very first thing in the stack
1459 // relative to the fp
1460 self.frame_locs.set(
1461 @intFromEnum(FrameIndex.ret_addr),
1462 .{ .base = .sp, .disp = acc_frame_size - 8 },
1463 );
1464 self.frame_locs.set(
1465 @intFromEnum(FrameIndex.base_ptr),
1466 .{ .base = .sp, .disp = acc_frame_size - 16 },
1467 );
1468
1469 // now we grow the stack frame from the bottom of total frame in order to
1470 // not need to know the size of the first allocation. Stack offsets point at the "bottom"
1471 // of variables.
1472 var s0_offset: i32 = -acc_frame_size;
1473 self.setFrameLoc(.stack_frame, .s0, &s0_offset, true);
1474 for (stack_frame_order) |frame_index| self.setFrameLoc(frame_index, .s0, &s0_offset, true);
1475 self.setFrameLoc(.args_frame, .s0, &s0_offset, true);
1476 self.setFrameLoc(.call_frame, .s0, &s0_offset, true);
1477 self.setFrameLoc(.spill_frame, .s0, &s0_offset, true);
1478
1479 return .{
1480 .stack_adjust = @intCast(acc_frame_size),
1481 .save_reg_list = save_reg_list,
1482 };
1483}
1484
8091485fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
8101486 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
8111487 try table.ensureUnusedCapacity(self.gpa, additional_count);
8121488}
8131489
814fn splitType(self: *Self, ty: Type) ![2]Type {
1490fn memSize(self: *Self, ty: Type) Memory.Size {
8151491 const mod = self.bin_file.comp.module.?;
816 const classes = mem.sliceTo(&abi.classifySystemV(ty, mod), .none);
1492 return switch (ty.zigTypeTag(mod)) {
1493 .Float => Memory.Size.fromBitSize(ty.floatBits(self.target.*)),
1494 else => Memory.Size.fromSize(@intCast(ty.abiSize(mod))),
1495 };
1496}
1497
1498fn splitType(self: *Self, ty: Type) ![2]Type {
1499 const zcu = self.bin_file.comp.module.?;
1500 const classes = mem.sliceTo(&abi.classifySystem(ty, zcu), .none);
8171501 var parts: [2]Type = undefined;
8181502 if (classes.len == 2) for (&parts, classes, 0..) |*part, class, part_i| {
8191503 part.* = switch (class) {
8201504 .integer => switch (part_i) {
8211505 0 => Type.u64,
8221506 1 => part: {
823 const elem_size = ty.abiAlignment(mod).minStrict(.@"8").toByteUnitsOptional().?;
824 const elem_ty = try mod.intType(.unsigned, @intCast(elem_size * 8));
825 break :part switch (@divExact(ty.abiSize(mod) - 8, elem_size)) {
1507 const elem_size = ty.abiAlignment(zcu).minStrict(.@"8").toByteUnits().?;
1508 const elem_ty = try zcu.intType(.unsigned, @intCast(elem_size * 8));
1509 break :part switch (@divExact(ty.abiSize(zcu) - 8, elem_size)) {
8261510 1 => elem_ty,
827 else => |len| try mod.arrayType(.{ .len = len, .child = elem_ty.toIntern() }),
1511 else => |len| try zcu.arrayType(.{ .len = len, .child = elem_ty.toIntern() }),
8281512 };
8291513 },
8301514 else => unreachable,
8311515 },
8321516 else => break,
8331517 };
834 } else if (parts[0].abiSize(mod) + parts[1].abiSize(mod) == ty.abiSize(mod)) return parts;
835 return self.fail("TODO implement splitType for {}", .{ty.fmt(mod)});
1518 } else if (parts[0].abiSize(zcu) + parts[1].abiSize(zcu) == ty.abiSize(zcu)) return parts;
1519 return self.fail("TODO implement splitType for {}", .{ty.fmt(zcu)});
8361520}
8371521
8381522fn symbolIndex(self: *Self) !u32 {
839 const mod = self.bin_file.comp.module.?;
840 const decl_index = mod.funcOwnerDeclIndex(self.func_index);
1523 const zcu = self.bin_file.comp.module.?;
1524 const decl_index = zcu.funcOwnerDeclIndex(self.func_index);
8411525 return switch (self.bin_file.tag) {
8421526 .elf => blk: {
8431527 const elf_file = self.bin_file.cast(link.File.Elf).?;
......@@ -848,41 +1532,49 @@ fn symbolIndex(self: *Self) !u32 {
8481532 };
8491533}
8501534
851fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: Alignment) !u32 {
852 self.stack_align = self.stack_align.max(abi_align);
853 // TODO find a free slot instead of always appending
854 const offset: u32 = @intCast(abi_align.forward(self.next_stack_offset));
855 self.next_stack_offset = offset + abi_size;
856 if (self.next_stack_offset > self.max_end_stack)
857 self.max_end_stack = self.next_stack_offset;
858 try self.stack.putNoClobber(self.gpa, offset, .{
859 .inst = inst,
860 .size = abi_size,
861 });
862 return offset;
1535fn allocFrameIndex(self: *Self, alloc: FrameAlloc) !FrameIndex {
1536 const frame_allocs_slice = self.frame_allocs.slice();
1537 const frame_size = frame_allocs_slice.items(.abi_size);
1538 const frame_align = frame_allocs_slice.items(.abi_align);
1539
1540 const stack_frame_align = &frame_align[@intFromEnum(FrameIndex.stack_frame)];
1541 stack_frame_align.* = stack_frame_align.max(alloc.abi_align);
1542
1543 for (self.free_frame_indices.keys(), 0..) |frame_index, free_i| {
1544 const abi_size = frame_size[@intFromEnum(frame_index)];
1545 if (abi_size != alloc.abi_size) continue;
1546 const abi_align = &frame_align[@intFromEnum(frame_index)];
1547 abi_align.* = abi_align.max(alloc.abi_align);
1548
1549 _ = self.free_frame_indices.swapRemoveAt(free_i);
1550 return frame_index;
1551 }
1552 const frame_index: FrameIndex = @enumFromInt(self.frame_allocs.len);
1553 try self.frame_allocs.append(self.gpa, alloc);
1554 log.debug("allocated frame {}", .{frame_index});
1555 return frame_index;
8631556}
8641557
8651558/// Use a pointer instruction as the basis for allocating stack memory.
866fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
867 const mod = self.bin_file.comp.module.?;
868 const elem_ty = self.typeOfIndex(inst).childType(mod);
869 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {
870 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
871 };
872 // TODO swap this for inst.ty.ptrAlign
873 const abi_align = elem_ty.abiAlignment(mod);
874 return self.allocMem(inst, abi_size, abi_align);
1559fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !FrameIndex {
1560 const zcu = self.bin_file.comp.module.?;
1561 const ptr_ty = self.typeOfIndex(inst);
1562 const val_ty = ptr_ty.childType(zcu);
1563 return self.allocFrameIndex(FrameAlloc.init(.{
1564 .size = math.cast(u32, val_ty.abiSize(zcu)) orelse {
1565 return self.fail("type '{}' too big to fit into stack frame", .{val_ty.fmt(zcu)});
1566 },
1567 .alignment = ptr_ty.ptrAlignment(zcu).max(.@"1"),
1568 }));
8751569}
8761570
8771571fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
878 const mod = self.bin_file.comp.module.?;
1572 const zcu = self.bin_file.comp.module.?;
8791573 const elem_ty = self.typeOfIndex(inst);
8801574
881 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {
882 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
1575 const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse {
1576 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(zcu)});
8831577 };
884 const abi_align = elem_ty.abiAlignment(mod);
885 self.stack_align = self.stack_align.max(abi_align);
8861578
8871579 if (reg_ok) {
8881580 // Make sure the type can fit in a register before we try to allocate one.
......@@ -894,8 +1586,9 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
8941586 }
8951587 }
8961588 }
897 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
898 return .{ .stack_offset = stack_offset };
1589
1590 const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(elem_ty, zcu));
1591 return .{ .load_frame = .{ .index = frame_index } };
8991592}
9001593
9011594/// Allocates a register from the general purpose set and returns the Register and the Lock.
......@@ -938,19 +1631,12 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi
9381631}
9391632
9401633pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
941 const mod = self.bin_file.comp.module.?;
942 const elem_ty = self.typeOfIndex(inst);
943
944 // there isn't anything to spill
945 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return;
946
947 const stack_mcv = try self.allocRegOrMem(inst, false);
948 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
949 const reg_mcv = self.getResolvedInstValue(inst);
950 assert(reg == reg_mcv.register);
951 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
952 try branch.inst_table.put(self.gpa, inst, stack_mcv);
953 try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1634 const tracking = self.inst_tracking.getPtr(inst) orelse return;
1635 for (tracking.getRegs()) |tracked_reg| {
1636 if (tracked_reg.id() == reg.id()) break;
1637 } else unreachable; // spilled reg not tracked with spilled instruciton
1638 try tracking.spill(self, inst);
1639 try tracking.trackSpill(self, inst);
9541640}
9551641
9561642/// Copies a value to a register without tracking the register. The register is not considered
......@@ -972,39 +1658,48 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa
9721658}
9731659
9741660fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
975 const stack_offset = try self.allocMemPtr(inst);
976 log.debug("airAlloc offset: {}", .{stack_offset});
977 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1661 const result = MCValue{ .lea_frame = .{ .index = try self.allocMemPtr(inst) } };
1662 return self.finishAir(inst, result, .{ .none, .none, .none });
9781663}
9791664
9801665fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
981 const stack_offset = try self.allocMemPtr(inst);
982 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1666 const result: MCValue = switch (self.ret_mcv.long) {
1667 else => unreachable,
1668 .none => .{ .lea_frame = .{ .index = try self.allocMemPtr(inst) } },
1669 .load_frame => .{ .register_offset = .{
1670 .reg = (try self.copyToNewRegister(
1671 inst,
1672 self.ret_mcv.long,
1673 )).register,
1674 .off = self.ret_mcv.short.indirect.off,
1675 } },
1676 };
1677 return self.finishAir(inst, result, .{ .none, .none, .none });
9831678}
9841679
9851680fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
9861681 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
987 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
1682 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
9881683 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
9891684}
9901685
9911686fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
9921687 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
993 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
1688 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
9941689 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
9951690}
9961691
9971692fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
998 const mod = self.bin_file.comp.module.?;
1693 const zcu = self.bin_file.comp.module.?;
9991694 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
10001695 const src_ty = self.typeOf(ty_op.operand);
10011696 const dst_ty = self.typeOfIndex(inst);
10021697
10031698 const result: MCValue = result: {
1004 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod));
1699 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(zcu));
10051700
1006 const src_int_info = src_ty.intInfo(mod);
1007 const dst_int_info = dst_ty.intInfo(mod);
1701 const src_int_info = src_ty.intInfo(zcu);
1702 const dst_int_info = dst_ty.intInfo(zcu);
10081703 const extend = switch (src_int_info.signedness) {
10091704 .signed => dst_int_info,
10101705 .unsigned => src_int_info,
......@@ -1019,7 +1714,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
10191714
10201715 const src_storage_bits: u16 = switch (src_mcv) {
10211716 .register => 64,
1022 .stack_offset => src_int_info.bits,
1717 .load_frame => src_int_info.bits,
10231718 else => return self.fail("airIntCast from {s}", .{@tagName(src_mcv)}),
10241719 };
10251720
......@@ -1042,7 +1737,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
10421737
10431738 break :result dst_mcv;
10441739 } orelse return self.fail("TODO implement airIntCast from {} to {}", .{
1045 src_ty.fmt(mod), dst_ty.fmt(mod),
1740 src_ty.fmt(zcu), dst_ty.fmt(zcu),
10461741 });
10471742
10481743 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -1051,7 +1746,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
10511746fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
10521747 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
10531748 if (self.liveness.isUnused(inst))
1054 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1749 return self.finishAir(inst, .unreach, .{ ty_op.operand, .none, .none });
10551750
10561751 const operand = try self.resolveInst(ty_op.operand);
10571752 _ = operand;
......@@ -1062,19 +1757,19 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
10621757fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void {
10631758 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
10641759 const operand = try self.resolveInst(un_op);
1065 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
1760 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else operand;
10661761 return self.finishAir(inst, result, .{ un_op, .none, .none });
10671762}
10681763
10691764fn airNot(self: *Self, inst: Air.Inst.Index) !void {
10701765 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1071 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1072 const mod = self.bin_file.comp.module.?;
1766 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
1767 const zcu = self.bin_file.comp.module.?;
10731768
10741769 const operand = try self.resolveInst(ty_op.operand);
10751770 const ty = self.typeOf(ty_op.operand);
10761771
1077 switch (ty.zigTypeTag(mod)) {
1772 switch (ty.zigTypeTag(zcu)) {
10781773 .Bool => {
10791774 const operand_reg = blk: {
10801775 if (operand == .register) break :blk operand.register;
......@@ -1089,6 +1784,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
10891784
10901785 _ = try self.addInst(.{
10911786 .tag = .not,
1787 .ops = .rr,
10921788 .data = .{
10931789 .rr = .{
10941790 .rs = operand_reg,
......@@ -1108,20 +1804,20 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
11081804
11091805fn airMin(self: *Self, inst: Air.Inst.Index) !void {
11101806 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1111 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
1807 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
11121808 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11131809}
11141810
11151811fn airMax(self: *Self, inst: Air.Inst.Index) !void {
11161812 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1117 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
1813 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
11181814 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11191815}
11201816
11211817fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
11221818 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
11231819 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1124 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
1820 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
11251821 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11261822}
11271823
......@@ -1132,7 +1828,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
11321828 const lhs_ty = self.typeOf(bin_op.lhs);
11331829 const rhs_ty = self.typeOf(bin_op.rhs);
11341830
1135 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
1831 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
11361832 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11371833}
11381834
......@@ -1175,7 +1871,8 @@ fn binOp(
11751871 lhs_ty: Type,
11761872 rhs_ty: Type,
11771873) InnerError!MCValue {
1178 const mod = self.bin_file.comp.module.?;
1874 const zcu = self.bin_file.comp.module.?;
1875
11791876 switch (tag) {
11801877 // Arithmetic operations on integers and floats
11811878 .add,
......@@ -1188,12 +1885,12 @@ fn binOp(
11881885 .cmp_lt,
11891886 .cmp_lte,
11901887 => {
1191 switch (lhs_ty.zigTypeTag(mod)) {
1888 switch (lhs_ty.zigTypeTag(zcu)) {
11921889 .Float => return self.fail("TODO binary operations on floats", .{}),
11931890 .Vector => return self.fail("TODO binary operations on vectors", .{}),
11941891 .Int => {
1195 assert(lhs_ty.eql(rhs_ty, mod));
1196 const int_info = lhs_ty.intInfo(mod);
1892 assert(lhs_ty.eql(rhs_ty, zcu));
1893 const int_info = lhs_ty.intInfo(zcu);
11971894 if (int_info.bits <= 64) {
11981895 if (rhs == .immediate and supportImmediate(tag)) {
11991896 return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
......@@ -1210,14 +1907,14 @@ fn binOp(
12101907 .ptr_add,
12111908 .ptr_sub,
12121909 => {
1213 switch (lhs_ty.zigTypeTag(mod)) {
1910 switch (lhs_ty.zigTypeTag(zcu)) {
12141911 .Pointer => {
12151912 const ptr_ty = lhs_ty;
1216 const elem_ty = switch (ptr_ty.ptrSize(mod)) {
1217 .One => ptr_ty.childType(mod).childType(mod), // ptr to array, so get array element type
1218 else => ptr_ty.childType(mod),
1913 const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
1914 .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type
1915 else => ptr_ty.childType(zcu),
12191916 };
1220 const elem_size = elem_ty.abiSize(mod);
1917 const elem_size = elem_ty.abiSize(zcu);
12211918
12221919 if (elem_size == 1) {
12231920 const base_tag: Air.Inst.Tag = switch (tag) {
......@@ -1256,11 +1953,11 @@ fn binOp(
12561953 .shr,
12571954 .shl,
12581955 => {
1259 switch (lhs_ty.zigTypeTag(mod)) {
1956 switch (lhs_ty.zigTypeTag(zcu)) {
12601957 .Float => return self.fail("TODO binary operations on floats", .{}),
12611958 .Vector => return self.fail("TODO binary operations on vectors", .{}),
12621959 .Int => {
1263 const int_info = lhs_ty.intInfo(mod);
1960 const int_info = lhs_ty.intInfo(zcu);
12641961 if (int_info.bits <= 64) {
12651962 if (rhs == .immediate) {
12661963 return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
......@@ -1332,6 +2029,7 @@ fn binOpRegister(
13322029
13332030 _ = try self.addInst(.{
13342031 .tag = mir_tag,
2032 .ops = .rrr,
13352033 .data = .{
13362034 .r_type = .{
13372035 .rd = dest_reg,
......@@ -1402,24 +2100,26 @@ fn binOpImm(
14022100 => {
14032101 _ = try self.addInst(.{
14042102 .tag = mir_tag,
2103 .ops = .rri,
14052104 .data = .{ .i_type = .{
14062105 .rd = dest_reg,
14072106 .rs1 = lhs_reg,
1408 .imm12 = math.cast(i12, rhs.immediate) orelse {
2107 .imm12 = Immediate.s(math.cast(i12, rhs.immediate) orelse {
14092108 return self.fail("TODO: binOpImm larger than i12 i_type payload", .{});
1410 },
2109 }),
14112110 } },
14122111 });
14132112 },
14142113 .addiw => {
14152114 _ = try self.addInst(.{
14162115 .tag = mir_tag,
2116 .ops = .rri,
14172117 .data = .{ .i_type = .{
14182118 .rd = dest_reg,
14192119 .rs1 = lhs_reg,
1420 .imm12 = -(math.cast(i12, rhs.immediate) orelse {
2120 .imm12 = Immediate.s(-(math.cast(i12, rhs.immediate) orelse {
14212121 return self.fail("TODO: binOpImm larger than i12 i_type payload", .{});
1422 }),
2122 })),
14232123 } },
14242124 });
14252125 },
......@@ -1428,6 +2128,7 @@ fn binOpImm(
14282128
14292129 _ = try self.addInst(.{
14302130 .tag = mir_tag,
2131 .ops = .rrr,
14312132 .data = .{ .r_type = .{
14322133 .rd = dest_reg,
14332134 .rs1 = imm_reg,
......@@ -1449,8 +2150,8 @@ fn binOpMir(
14492150 dst_mcv: MCValue,
14502151 src_mcv: MCValue,
14512152) !void {
1452 const mod = self.bin_file.comp.module.?;
1453 const abi_size: u32 = @intCast(ty.abiSize(mod));
2153 const zcu = self.bin_file.comp.module.?;
2154 const abi_size: u32 = @intCast(ty.abiSize(zcu));
14542155
14552156 _ = abi_size;
14562157 _ = maybe_inst;
......@@ -1461,6 +2162,7 @@ fn binOpMir(
14612162
14622163 _ = try self.addInst(.{
14632164 .tag = mir_tag,
2165 .ops = .rrr,
14642166 .data = .{
14652167 .r_type = .{
14662168 .rd = dst_reg,
......@@ -1483,25 +2185,25 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void
14832185 const lhs_ty = self.typeOf(bin_op.lhs);
14842186 const rhs_ty = self.typeOf(bin_op.rhs);
14852187
1486 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
2188 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
14872189 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14882190}
14892191
14902192fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
14912193 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1492 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
2194 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
14932195 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14942196}
14952197
14962198fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
14972199 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1498 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
2200 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
14992201 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15002202}
15012203
15022204fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
15032205 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1504 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2206 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
15052207 // RISCV arthemtic instructions already wrap, so this is simply a sub binOp with
15062208 // no overflow checks.
15072209 const lhs = try self.resolveInst(bin_op.lhs);
......@@ -1516,34 +2218,34 @@ fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
15162218
15172219fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
15182220 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1519 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
2221 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
15202222 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15212223}
15222224
15232225fn airMul(self: *Self, inst: Air.Inst.Index) !void {
15242226 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1525 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch});
2227 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch});
15262228 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15272229}
15282230
15292231fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
15302232 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1531 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
2233 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
15322234 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15332235}
15342236
15352237fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
15362238 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1537 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
2239 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
15382240 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15392241}
15402242
15412243fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1542 const mod = self.bin_file.comp.module.?;
2244 const zcu = self.bin_file.comp.module.?;
15432245 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
15442246 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
15452247
1546 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2248 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
15472249 const lhs = try self.resolveInst(extra.lhs);
15482250 const rhs = try self.resolveInst(extra.rhs);
15492251 const lhs_ty = self.typeOf(extra.lhs);
......@@ -1554,16 +2256,21 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15542256 defer self.register_manager.unlockReg(add_result_lock);
15552257
15562258 const tuple_ty = self.typeOfIndex(inst);
1557 const int_info = lhs_ty.intInfo(mod);
2259 const int_info = lhs_ty.intInfo(zcu);
15582260
15592261 // TODO: optimization, set this to true. needs the other struct access stuff to support
15602262 // accessing registers.
15612263 const result_mcv = try self.allocRegOrMem(inst, false);
1562 const offset = result_mcv.stack_offset;
1563
1564 const result_offset = tuple_ty.structFieldOffset(0, mod) + offset;
2264 const offset = result_mcv.load_frame;
15652265
1566 try self.genSetStack(lhs_ty, @intCast(result_offset), add_result_mcv);
2266 try self.genSetStack(
2267 lhs_ty,
2268 .{
2269 .index = offset.index,
2270 .off = offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))),
2271 },
2272 add_result_mcv,
2273 );
15672274
15682275 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
15692276 if (int_info.signedness == .unsigned) {
......@@ -1585,10 +2292,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15852292
15862293 _ = try self.addInst(.{
15872294 .tag = .andi,
2295 .ops = .rri,
15882296 .data = .{ .i_type = .{
15892297 .rd = overflow_reg,
15902298 .rs1 = add_reg,
1591 .imm12 = @intCast(max_val),
2299 .imm12 = Immediate.s(max_val),
15922300 } },
15932301 });
15942302
......@@ -1601,8 +2309,14 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16012309 lhs_ty,
16022310 );
16032311
1604 const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset;
1605 try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv);
2312 try self.genSetStack(
2313 Type.u1,
2314 .{
2315 .index = offset.index,
2316 .off = offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))),
2317 },
2318 overflow_mcv,
2319 );
16062320
16072321 break :result result_mcv;
16082322 },
......@@ -1629,18 +2343,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16292343 //const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
16302344 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
16312345 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1632 const mod = self.bin_file.comp.module.?;
1633 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2346 const zcu = self.bin_file.comp.module.?;
2347 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
16342348 const lhs = try self.resolveInst(extra.lhs);
16352349 const rhs = try self.resolveInst(extra.rhs);
16362350 const lhs_ty = self.typeOf(extra.lhs);
16372351 const rhs_ty = self.typeOf(extra.rhs);
16382352
1639 switch (lhs_ty.zigTypeTag(mod)) {
2353 switch (lhs_ty.zigTypeTag(zcu)) {
16402354 else => |x| return self.fail("TODO: airMulWithOverflow {s}", .{@tagName(x)}),
16412355 .Int => {
1642 assert(lhs_ty.eql(rhs_ty, mod));
1643 const int_info = lhs_ty.intInfo(mod);
2356 assert(lhs_ty.eql(rhs_ty, zcu));
2357 const int_info = lhs_ty.intInfo(zcu);
16442358 switch (int_info.bits) {
16452359 1...32 => {
16462360 if (self.hasFeature(.m)) {
......@@ -1654,11 +2368,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16542368 // TODO: optimization, set this to true. needs the other struct access stuff to support
16552369 // accessing registers.
16562370 const result_mcv = try self.allocRegOrMem(inst, false);
1657 const offset = result_mcv.stack_offset;
16582371
1659 const result_offset = tuple_ty.structFieldOffset(0, mod) + offset;
2372 const result_off: i32 = @intCast(tuple_ty.structFieldOffset(0, zcu));
2373 const overflow_off: i32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
16602374
1661 try self.genSetStack(lhs_ty, @intCast(result_offset), dest);
2375 try self.genSetStack(lhs_ty, result_mcv.offset(result_off).load_frame, dest);
16622376
16632377 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
16642378 if (int_info.signedness == .unsigned) {
......@@ -1680,10 +2394,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16802394
16812395 _ = try self.addInst(.{
16822396 .tag = .andi,
2397 .ops = .rri,
16832398 .data = .{ .i_type = .{
16842399 .rd = overflow_reg,
16852400 .rs1 = add_reg,
1686 .imm12 = @intCast(max_val),
2401 .imm12 = Immediate.s(max_val),
16872402 } },
16882403 });
16892404
......@@ -1696,8 +2411,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16962411 lhs_ty,
16972412 );
16982413
1699 const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset;
1700 try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv);
2414 try self.genSetStack(
2415 lhs_ty,
2416 result_mcv.offset(overflow_off).load_frame,
2417 overflow_mcv,
2418 );
17012419
17022420 break :result result_mcv;
17032421 },
......@@ -1730,43 +2448,43 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
17302448
17312449fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
17322450 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1733 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
2451 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
17342452 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17352453}
17362454
17372455fn airRem(self: *Self, inst: Air.Inst.Index) !void {
17382456 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1739 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
2457 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
17402458 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17412459}
17422460
17432461fn airMod(self: *Self, inst: Air.Inst.Index) !void {
17442462 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1745 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
2463 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement zcu for {}", .{self.target.cpu.arch});
17462464 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17472465}
17482466
17492467fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
17502468 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1751 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch});
2469 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch});
17522470 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17532471}
17542472
17552473fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
17562474 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1757 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch});
2475 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch});
17582476 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17592477}
17602478
17612479fn airXor(self: *Self, inst: Air.Inst.Index) !void {
17622480 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1763 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch});
2481 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch});
17642482 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17652483}
17662484
17672485fn airShl(self: *Self, inst: Air.Inst.Index) !void {
17682486 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1769 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2487 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
17702488 const lhs = try self.resolveInst(bin_op.lhs);
17712489 const rhs = try self.resolveInst(bin_op.rhs);
17722490 const lhs_ty = self.typeOf(bin_op.lhs);
......@@ -1779,52 +2497,52 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void {
17792497
17802498fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
17812499 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1782 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
2500 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
17832501 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17842502}
17852503
17862504fn airShr(self: *Self, inst: Air.Inst.Index) !void {
17872505 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1788 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
2506 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
17892507 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17902508}
17912509
17922510fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
17932511 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1794 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
2512 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
17952513 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
17962514}
17972515
17982516fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
17992517 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1800 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
2518 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
18012519 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
18022520}
18032521
18042522fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
18052523 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1806 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
2524 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
18072525 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
18082526}
18092527
18102528fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
18112529 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1812 const mod = self.bin_file.comp.module.?;
2530 const zcu = self.bin_file.comp.module.?;
18132531 const err_union_ty = self.typeOf(ty_op.operand);
1814 const err_ty = err_union_ty.errorUnionSet(mod);
1815 const payload_ty = err_union_ty.errorUnionPayload(mod);
2532 const err_ty = err_union_ty.errorUnionSet(zcu);
2533 const payload_ty = err_union_ty.errorUnionPayload(zcu);
18162534 const operand = try self.resolveInst(ty_op.operand);
18172535
18182536 const result: MCValue = result: {
1819 if (err_ty.errorSetIsEmpty(mod)) {
2537 if (err_ty.errorSetIsEmpty(zcu)) {
18202538 break :result .{ .immediate = 0 };
18212539 }
18222540
1823 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
2541 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
18242542 break :result operand;
18252543 }
18262544
1827 const err_off: u32 = @intCast(errUnionErrorOffset(payload_ty, mod));
2545 const err_off: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu));
18282546
18292547 switch (operand) {
18302548 .register => |reg| {
......@@ -1865,16 +2583,18 @@ fn genUnwrapErrUnionPayloadMir(
18652583 err_union_ty: Type,
18662584 err_union: MCValue,
18672585) !MCValue {
1868 const mod = self.bin_file.comp.module.?;
1869
1870 const payload_ty = err_union_ty.errorUnionPayload(mod);
2586 const zcu = self.bin_file.comp.module.?;
2587 const payload_ty = err_union_ty.errorUnionPayload(zcu);
18712588
18722589 const result: MCValue = result: {
1873 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) break :result .none;
2590 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none;
18742591
1875 const payload_off: u32 = @intCast(errUnionPayloadOffset(payload_ty, mod));
2592 const payload_off: u31 = @intCast(errUnionPayloadOffset(payload_ty, zcu));
18762593 switch (err_union) {
1877 .stack_offset => |off| break :result .{ .stack_offset = off + payload_off },
2594 .load_frame => |frame_addr| break :result .{ .load_frame = .{
2595 .index = frame_addr.index,
2596 .off = frame_addr.off + payload_off,
2597 } },
18782598 .register => |reg| {
18792599 const eu_lock = self.register_manager.lockReg(reg);
18802600 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -1904,26 +2624,26 @@ fn genUnwrapErrUnionPayloadMir(
19042624// *(E!T) -> E
19052625fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
19062626 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1907 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
2627 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
19082628 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19092629}
19102630
19112631// *(E!T) -> *T
19122632fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
19132633 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1914 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
2634 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
19152635 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19162636}
19172637
19182638fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
19192639 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1920 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
2640 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
19212641 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19222642}
19232643
19242644fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
19252645 const result: MCValue = if (self.liveness.isUnused(inst))
1926 .dead
2646 .unreach
19272647 else
19282648 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
19292649 return self.finishAir(inst, result, .{ .none, .none, .none });
......@@ -1941,12 +2661,12 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
19412661
19422662fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
19432663 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1944 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1945 const mod = self.bin_file.comp.module.?;
2664 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
2665 const zcu = self.bin_file.comp.module.?;
19462666 const optional_ty = self.typeOfIndex(inst);
19472667
19482668 // Optional with a zero-bit payload type is just a boolean true
1949 if (optional_ty.abiSize(mod) == 1)
2669 if (optional_ty.abiSize(zcu) == 1)
19502670 break :result MCValue{ .immediate = 1 };
19512671
19522672 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
......@@ -1957,29 +2677,29 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
19572677/// T to E!T
19582678fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
19592679 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1960 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
2680 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
19612681 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19622682}
19632683
19642684/// E to E!T
19652685fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1966 const mod = self.bin_file.comp.module.?;
2686 const zcu = self.bin_file.comp.module.?;
19672687 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
19682688
19692689 const eu_ty = ty_op.ty.toType();
1970 const pl_ty = eu_ty.errorUnionPayload(mod);
1971 const err_ty = eu_ty.errorUnionSet(mod);
2690 const pl_ty = eu_ty.errorUnionPayload(zcu);
2691 const err_ty = eu_ty.errorUnionSet(zcu);
19722692
19732693 const result: MCValue = result: {
1974 if (!pl_ty.hasRuntimeBitsIgnoreComptime(mod)) break :result try self.resolveInst(ty_op.operand);
2694 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result try self.resolveInst(ty_op.operand);
19752695
1976 const stack_off = try self.allocMem(null, @intCast(eu_ty.abiSize(mod)), eu_ty.abiAlignment(mod));
1977 const pl_off: u32 = @intCast(errUnionPayloadOffset(pl_ty, mod));
1978 const err_off: u32 = @intCast(errUnionErrorOffset(pl_ty, mod));
1979 try self.genSetStack(pl_ty, stack_off + pl_off, .undef);
2696 const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(eu_ty, zcu));
2697 const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
2698 const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
2699 try self.genSetStack(pl_ty, .{ .index = frame_index, .off = pl_off }, .undef);
19802700 const operand = try self.resolveInst(ty_op.operand);
1981 try self.genSetStack(err_ty, stack_off + err_off, operand);
1982 break :result .{ .stack_offset = stack_off };
2701 try self.genSetStack(err_ty, .{ .index = frame_index, .off = err_off }, operand);
2702 break :result .{ .load_frame = .{ .index = frame_index } };
19832703 };
19842704 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19852705}
......@@ -2001,67 +2721,41 @@ fn genTry(
20012721 operand_ty: Type,
20022722 operand_is_ptr: bool,
20032723) !MCValue {
2004 const liveness_condbr = self.liveness.getCondBr(inst);
2005
20062724 _ = operand_is_ptr;
20072725
2726 const liveness_cond_br = self.liveness.getCondBr(inst);
2727
20082728 const operand_mcv = try self.resolveInst(operand);
20092729 const is_err_mcv = try self.isErr(null, operand_ty, operand_mcv);
20102730
2011 const cond_reg = try self.register_manager.allocReg(inst, gp);
2012 const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg);
2013 defer self.register_manager.unlockReg(cond_reg_lock);
2014
20152731 // A branch to the false section. Uses beq. 1 is the default "true" state.
2016 const reloc = try self.condBr(Type.anyerror, is_err_mcv, cond_reg);
2732 const reloc = try self.condBr(Type.anyerror, is_err_mcv);
20172733
20182734 if (self.liveness.operandDies(inst, 0)) {
2019 if (operand.toIndex()) |op_inst| self.processDeath(op_inst);
2020 }
2021
2022 // Save state
2023 const parent_next_stack_offset = self.next_stack_offset;
2024 const parent_free_registers = self.register_manager.free_registers;
2025 var parent_stack = try self.stack.clone(self.gpa);
2026 defer parent_stack.deinit(self.gpa);
2027 const parent_registers = self.register_manager.registers;
2028
2029 try self.branch_stack.append(.{});
2030 errdefer {
2031 _ = self.branch_stack.pop();
2735 if (operand.toIndex()) |operand_inst| try self.processDeath(operand_inst);
20322736 }
20332737
2034 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
2035 for (liveness_condbr.else_deaths) |op| {
2036 self.processDeath(op);
2037 }
2738 self.scope_generation += 1;
2739 const state = try self.saveState();
20382740
2741 for (liveness_cond_br.else_deaths) |death| try self.processDeath(death);
20392742 try self.genBody(body);
2743 try self.restoreState(state, &.{}, .{
2744 .emit_instructions = false,
2745 .update_tracking = true,
2746 .resurrect = true,
2747 .close_scope = true,
2748 });
20402749
2041 // Restore state
2042 var saved_then_branch = self.branch_stack.pop();
2043 defer saved_then_branch.deinit(self.gpa);
2044
2045 self.register_manager.registers = parent_registers;
2046
2047 self.stack.deinit(self.gpa);
2048 self.stack = parent_stack;
2049 parent_stack = .{};
2050
2051 self.next_stack_offset = parent_next_stack_offset;
2052 self.register_manager.free_registers = parent_free_registers;
2750 self.performReloc(reloc);
20532751
2054 try self.performReloc(reloc, @intCast(self.mir_instructions.len));
2055
2056 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
2057 for (liveness_condbr.then_deaths) |op| {
2058 self.processDeath(op);
2059 }
2752 for (liveness_cond_br.then_deaths) |death| try self.processDeath(death);
20602753
20612754 const result = if (self.liveness.isUnused(inst))
20622755 .unreach
20632756 else
20642757 try self.genUnwrapErrUnionPayloadMir(operand_ty, operand_mcv);
2758
20652759 return result;
20662760}
20672761
......@@ -2081,11 +2775,14 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
20812775
20822776fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
20832777 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2084 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2778 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
20852779 const src_mcv = try self.resolveInst(ty_op.operand);
20862780 switch (src_mcv) {
2087 .stack_offset => |off| {
2088 const len_mcv: MCValue = .{ .stack_offset = off + 8 };
2781 .load_frame => |frame_addr| {
2782 const len_mcv: MCValue = .{ .load_frame = .{
2783 .index = frame_addr.index,
2784 .off = frame_addr.off + 8,
2785 } };
20892786 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv;
20902787
20912788 const dst_mcv = try self.allocRegOrMem(inst, true);
......@@ -2109,29 +2806,33 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
21092806
21102807fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
21112808 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2112 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch});
2809 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch});
21132810 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
21142811}
21152812
21162813fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
21172814 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2118 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch});
2815 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch});
21192816 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
21202817}
21212818
21222819fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2123 const mod = self.bin_file.comp.module.?;
2820 const zcu = self.bin_file.comp.module.?;
21242821 const is_volatile = false; // TODO
21252822 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
21262823
2127 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2824 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(
2825 inst,
2826 .unreach,
2827 .{ bin_op.lhs, bin_op.rhs, .none },
2828 );
21282829 const result: MCValue = result: {
21292830 const slice_mcv = try self.resolveInst(bin_op.lhs);
21302831 const index_mcv = try self.resolveInst(bin_op.rhs);
21312832
21322833 const slice_ty = self.typeOf(bin_op.lhs);
21332834
2134 const slice_ptr_field_type = slice_ty.slicePtrFieldType(mod);
2835 const slice_ptr_field_type = slice_ty.slicePtrFieldType(zcu);
21352836
21362837 const index_lock: ?RegisterLock = if (index_mcv == .register)
21372838 self.register_manager.lockRegAssumeUnused(index_mcv.register)
......@@ -2140,7 +2841,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
21402841 defer if (index_lock) |reg| self.register_manager.unlockReg(reg);
21412842
21422843 const base_mcv: MCValue = switch (slice_mcv) {
2143 .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) },
2844 .load_frame,
2845 .load_symbol,
2846 => .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, slice_mcv) },
21442847 else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
21452848 };
21462849
......@@ -2156,38 +2859,34 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
21562859fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
21572860 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
21582861 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2159 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
2862 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
21602863 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
21612864}
21622865
21632866fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2164 const mod = self.bin_file.comp.module.?;
2867 const zcu = self.bin_file.comp.module.?;
21652868 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2166 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2869 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
21672870 const array_ty = self.typeOf(bin_op.lhs);
21682871 const array_mcv = try self.resolveInst(bin_op.lhs);
21692872
21702873 const index_mcv = try self.resolveInst(bin_op.rhs);
21712874 const index_ty = self.typeOf(bin_op.rhs);
21722875
2173 const elem_ty = array_ty.childType(mod);
2174 const elem_abi_size = elem_ty.abiSize(mod);
2876 const elem_ty = array_ty.childType(zcu);
2877 const elem_abi_size = elem_ty.abiSize(zcu);
21752878
21762879 const addr_reg, const addr_reg_lock = try self.allocReg();
21772880 defer self.register_manager.unlockReg(addr_reg_lock);
21782881
21792882 switch (array_mcv) {
21802883 .register => {
2181 const stack_offset = try self.allocMem(
2182 null,
2183 @intCast(array_ty.abiSize(mod)),
2184 array_ty.abiAlignment(mod),
2185 );
2186 try self.genSetStack(array_ty, stack_offset, array_mcv);
2187 try self.genSetReg(Type.usize, addr_reg, .{ .ptr_stack_offset = stack_offset });
2884 const frame_index = try self.allocFrameIndex(FrameAlloc.initType(array_ty, zcu));
2885 try self.genSetStack(array_ty, .{ .index = frame_index }, array_mcv);
2886 try self.genSetReg(Type.usize, addr_reg, .{ .lea_frame = .{ .index = frame_index } });
21882887 },
2189 .stack_offset => |off| {
2190 try self.genSetReg(Type.usize, addr_reg, .{ .ptr_stack_offset = off });
2888 .load_frame => |frame_addr| {
2889 try self.genSetReg(Type.usize, addr_reg, .{ .lea_frame = frame_addr });
21912890 },
21922891 else => try self.genSetReg(Type.usize, addr_reg, array_mcv.address()),
21932892 }
......@@ -2213,14 +2912,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
22132912fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
22142913 const is_volatile = false; // TODO
22152914 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2216 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch});
2915 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch});
22172916 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
22182917}
22192918
22202919fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
22212920 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
22222921 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2223 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
2922 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
22242923 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
22252924}
22262925
......@@ -2233,19 +2932,19 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
22332932
22342933fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
22352934 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2236 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
2935 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
22372936 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
22382937}
22392938
22402939fn airClz(self: *Self, inst: Air.Inst.Index) !void {
22412940 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2242 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
2941 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
22432942 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
22442943}
22452944
22462945fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
22472946 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2248 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2947 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
22492948 const operand = try self.resolveInst(ty_op.operand);
22502949 const operand_ty = self.typeOf(ty_op.operand);
22512950
......@@ -2270,8 +2969,8 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
22702969}
22712970
22722971fn ctz(self: *Self, src: Register, dst: Register, ty: Type) !void {
2273 const mod = self.bin_file.comp.module.?;
2274 const length = (ty.abiSize(mod) * 8) - 1;
2972 const zcu = self.bin_file.comp.module.?;
2973 const length = (ty.abiSize(zcu) * 8) - 1;
22752974
22762975 const count_reg, const count_lock = try self.allocReg();
22772976 defer self.register_manager.unlockReg(count_lock);
......@@ -2282,17 +2981,6 @@ fn ctz(self: *Self, src: Register, dst: Register, ty: Type) !void {
22822981 try self.genSetReg(Type.usize, count_reg, .{ .immediate = 0 });
22832982 try self.genSetReg(Type.usize, len_reg, .{ .immediate = length });
22842983
2285 _ = try self.addInst(.{
2286 .tag = .beq,
2287 .data = .{
2288 .b_type = .{
2289 .rs1 = count_reg,
2290 .rs2 = len_reg,
2291 .inst = @intCast(self.mir_instructions.len + 0),
2292 },
2293 },
2294 });
2295
22962984 _ = src;
22972985 _ = dst;
22982986
......@@ -2301,23 +2989,23 @@ fn ctz(self: *Self, src: Register, dst: Register, ty: Type) !void {
23012989
23022990fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
23032991 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2304 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
2992 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
23052993 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
23062994}
23072995
23082996fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
2309 const mod = self.bin_file.comp.module.?;
2997 const zcu = self.bin_file.comp.module.?;
23102998 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2999 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
23123000 const ty = self.typeOf(ty_op.operand);
2313 const scalar_ty = ty.scalarType(mod);
3001 const scalar_ty = ty.scalarType(zcu);
23143002 const operand = try self.resolveInst(ty_op.operand);
23153003
2316 switch (scalar_ty.zigTypeTag(mod)) {
2317 .Int => if (ty.zigTypeTag(mod) == .Vector) {
2318 return self.fail("TODO implement airAbs for {}", .{ty.fmt(mod)});
3004 switch (scalar_ty.zigTypeTag(zcu)) {
3005 .Int => if (ty.zigTypeTag(zcu) == .Vector) {
3006 return self.fail("TODO implement airAbs for {}", .{ty.fmt(zcu)});
23193007 } else {
2320 const int_bits = ty.intInfo(mod).bits;
3008 const int_bits = ty.intInfo(zcu).bits;
23213009
23223010 if (int_bits > 32) {
23233011 return self.fail("TODO: airAbs for larger than 32 bits", .{});
......@@ -2330,18 +3018,19 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
23303018
23313019 _ = try self.addInst(.{
23323020 .tag = .abs,
3021 .ops = .rri,
23333022 .data = .{
23343023 .i_type = .{
23353024 .rs1 = src_mcv.register,
23363025 .rd = temp_reg,
2337 .imm12 = @intCast(int_bits - 1),
3026 .imm12 = Immediate.s(int_bits - 1),
23383027 },
23393028 },
23403029 });
23413030
23423031 break :result src_mcv;
23433032 },
2344 else => return self.fail("TODO: implement airAbs {}", .{scalar_ty.fmt(mod)}),
3033 else => return self.fail("TODO: implement airAbs {}", .{scalar_ty.fmt(zcu)}),
23453034 }
23463035 };
23473036 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -2349,12 +3038,12 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
23493038
23503039fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
23513040 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2352 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2353 const mod = self.bin_file.comp.module.?;
3041 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
3042 const zcu = self.bin_file.comp.module.?;
23543043 const ty = self.typeOf(ty_op.operand);
23553044 const operand = try self.resolveInst(ty_op.operand);
23563045
2357 const int_bits = ty.intInfo(mod).bits;
3046 const int_bits = ty.intInfo(zcu).bits;
23583047
23593048 // bytes are no-op
23603049 if (int_bits == 8 and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
......@@ -2372,14 +3061,16 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
23723061 assert(temp == .register);
23733062 _ = try self.addInst(.{
23743063 .tag = .slli,
3064 .ops = .rri,
23753065 .data = .{ .i_type = .{
2376 .imm12 = 8,
3066 .imm12 = Immediate.s(8),
23773067 .rd = dest_reg,
23783068 .rs1 = dest_reg,
23793069 } },
23803070 });
23813071 _ = try self.addInst(.{
23823072 .tag = .@"or",
3073 .ops = .rri,
23833074 .data = .{ .r_type = .{
23843075 .rd = dest_reg,
23853076 .rs1 = dest_reg,
......@@ -2397,62 +3088,78 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
23973088
23983089fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
23993090 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2400 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
3091 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
24013092 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
24023093}
24033094
24043095fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
24053096 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
24063097 const result: MCValue = if (self.liveness.isUnused(inst))
2407 .dead
3098 .unreach
24083099 else
24093100 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
24103101 return self.finishAir(inst, result, .{ un_op, .none, .none });
24113102}
24123103
2413fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
3104fn reuseOperand(
3105 self: *Self,
3106 inst: Air.Inst.Index,
3107 operand: Air.Inst.Ref,
3108 op_index: Liveness.OperandInt,
3109 mcv: MCValue,
3110) bool {
3111 return self.reuseOperandAdvanced(inst, operand, op_index, mcv, inst);
3112}
3113
3114fn reuseOperandAdvanced(
3115 self: *Self,
3116 inst: Air.Inst.Index,
3117 operand: Air.Inst.Ref,
3118 op_index: Liveness.OperandInt,
3119 mcv: MCValue,
3120 maybe_tracked_inst: ?Air.Inst.Index,
3121) bool {
24143122 if (!self.liveness.operandDies(inst, op_index))
24153123 return false;
24163124
24173125 switch (mcv) {
2418 .register => |reg| {
2419 // If it's in the registers table, need to associate the register with the
3126 .register,
3127 .register_pair,
3128 => for (mcv.getRegs()) |reg| {
3129 // If it's in the registers table, need to associate the register(s) with the
24203130 // new instruction.
2421 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
3131 if (maybe_tracked_inst) |tracked_inst| {
24223132 if (!self.register_manager.isRegFree(reg)) {
2423 self.register_manager.registers[index] = inst;
3133 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
3134 self.register_manager.registers[index] = tracked_inst;
3135 }
24243136 }
2425 }
2426 log.debug("%{d} => {} (reused)", .{ inst, reg });
2427 },
2428 .stack_offset => |off| {
2429 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
3137 } else self.register_manager.freeReg(reg);
24303138 },
3139 .load_frame => |frame_addr| if (frame_addr.index.isNamed()) return false,
24313140 else => return false,
24323141 }
24333142
24343143 // Prevent the operand deaths processing code from deallocating it.
24353144 self.liveness.clearOperandDeath(inst, op_index);
2436
2437 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
2438 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2439 branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead);
3145 const op_inst = operand.toIndex().?;
3146 self.getResolvedInstValue(op_inst).reuse(self, maybe_tracked_inst, op_inst);
24403147
24413148 return true;
24423149}
24433150
24443151fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
2445 const mod = self.bin_file.comp.module.?;
3152 const zcu = self.bin_file.comp.module.?;
24463153 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
24473154 const elem_ty = self.typeOfIndex(inst);
24483155 const result: MCValue = result: {
2449 if (!elem_ty.hasRuntimeBits(mod))
3156 if (!elem_ty.hasRuntimeBits(zcu))
24503157 break :result .none;
24513158
24523159 const ptr = try self.resolveInst(ty_op.operand);
2453 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod);
3160 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(zcu);
24543161 if (self.liveness.isUnused(inst) and !is_volatile)
2455 break :result .dead;
3162 break :result .unreach;
24563163
24573164 const dst_mcv: MCValue = blk: {
24583165 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
......@@ -2462,6 +3169,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
24623169 break :blk try self.allocRegOrMem(inst, true);
24633170 }
24643171 };
3172
24653173 try self.load(dst_mcv, ptr, self.typeOf(ty_op.operand));
24663174 break :result dst_mcv;
24673175 };
......@@ -2469,10 +3177,10 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
24693177}
24703178
24713179fn load(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerError!void {
2472 const mod = self.bin_file.comp.module.?;
2473 const dst_ty = ptr_ty.childType(mod);
3180 const zcu = self.bin_file.comp.module.?;
3181 const dst_ty = ptr_ty.childType(zcu);
24743182
2475 log.debug("loading {}:{} into {}", .{ ptr_mcv, ptr_ty.fmt(mod), dst_mcv });
3183 log.debug("loading {}:{} into {}", .{ ptr_mcv, ptr_ty.fmt(zcu), dst_mcv });
24763184
24773185 switch (ptr_mcv) {
24783186 .none,
......@@ -2480,19 +3188,20 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro
24803188 .unreach,
24813189 .dead,
24823190 .register_pair,
3191 .reserved_frame,
24833192 => unreachable, // not a valid pointer
24843193
24853194 .immediate,
24863195 .register,
24873196 .register_offset,
2488 .ptr_stack_offset,
2489 .addr_symbol,
3197 .lea_frame,
3198 .lea_symbol,
24903199 => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
24913200
24923201 .memory,
24933202 .indirect,
24943203 .load_symbol,
2495 .stack_offset,
3204 .load_frame,
24963205 => {
24973206 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
24983207 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
......@@ -2518,14 +3227,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
25183227
25193228 try self.store(ptr, value, ptr_ty, value_ty);
25203229
2521 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
3230 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
25223231}
25233232
25243233/// Loads `value` into the "payload" of `pointer`.
25253234fn store(self: *Self, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty: Type) !void {
2526 const mod = self.bin_file.comp.module.?;
3235 const zcu = self.bin_file.comp.module.?;
25273236
2528 log.debug("storing {}:{} in {}:{}", .{ src_mcv, src_ty.fmt(mod), ptr_mcv, ptr_ty.fmt(mod) });
3237 log.debug("storing {}:{} in {}:{}", .{ src_mcv, src_ty.fmt(zcu), ptr_mcv, ptr_ty.fmt(zcu) });
25293238
25303239 switch (ptr_mcv) {
25313240 .none => unreachable,
......@@ -2533,18 +3242,19 @@ fn store(self: *Self, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type, src_ty:
25333242 .unreach => unreachable,
25343243 .dead => unreachable,
25353244 .register_pair => unreachable,
3245 .reserved_frame => unreachable,
25363246
25373247 .immediate,
25383248 .register,
25393249 .register_offset,
2540 .addr_symbol,
2541 .ptr_stack_offset,
3250 .lea_symbol,
3251 .lea_frame,
25423252 => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv),
25433253
25443254 .memory,
25453255 .indirect,
25463256 .load_symbol,
2547 .stack_offset,
3257 .load_frame,
25483258 => {
25493259 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
25503260 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
......@@ -2570,24 +3280,24 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
25703280}
25713281
25723282fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
2573 const mod = self.bin_file.comp.module.?;
3283 const zcu = self.bin_file.comp.module.?;
25743284 const ptr_field_ty = self.typeOfIndex(inst);
25753285 const ptr_container_ty = self.typeOf(operand);
2576 const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod);
2577 const container_ty = ptr_container_ty.childType(mod);
3286 const ptr_container_ty_info = ptr_container_ty.ptrInfo(zcu);
3287 const container_ty = ptr_container_ty.childType(zcu);
25783288
2579 const field_offset: i32 = if (mod.typeToPackedStruct(container_ty)) |struct_obj|
2580 if (ptr_field_ty.ptrInfo(mod).packed_offset.host_size == 0)
2581 @divExact(mod.structPackedFieldBitOffset(struct_obj, index) +
3289 const field_offset: i32 = if (zcu.typeToPackedStruct(container_ty)) |struct_obj|
3290 if (ptr_field_ty.ptrInfo(zcu).packed_offset.host_size == 0)
3291 @divExact(zcu.structPackedFieldBitOffset(struct_obj, index) +
25823292 ptr_container_ty_info.packed_offset.bit_offset, 8)
25833293 else
25843294 0
25853295 else
2586 @intCast(container_ty.structFieldOffset(index, mod));
3296 @intCast(container_ty.structFieldOffset(index, zcu));
25873297
25883298 const src_mcv = try self.resolveInst(operand);
25893299 const dst_mcv = if (switch (src_mcv) {
2590 .immediate, .ptr_stack_offset => true,
3300 .immediate, .lea_frame => true,
25913301 .register, .register_offset => self.reuseOperand(inst, operand, 0, src_mcv),
25923302 else => false,
25933303 }) src_mcv else try self.copyToNewRegister(inst, src_mcv);
......@@ -2595,21 +3305,24 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
25953305}
25963306
25973307fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3308 const mod = self.bin_file.comp.module.?;
3309
25983310 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
25993311 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
26003312 const operand = extra.struct_operand;
26013313 const index = extra.field_index;
2602 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2603 const mod = self.bin_file.comp.module.?;
3314
3315 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
3316 const zcu = self.bin_file.comp.module.?;
26043317 const src_mcv = try self.resolveInst(operand);
26053318 const struct_ty = self.typeOf(operand);
2606 const field_ty = struct_ty.structFieldType(index, mod);
2607 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) break :result .none;
3319 const field_ty = struct_ty.structFieldType(index, zcu);
3320 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none;
26083321
2609 const field_off: u32 = switch (struct_ty.containerLayout(mod)) {
2610 .auto, .@"extern" => @intCast(struct_ty.structFieldOffset(index, mod) * 8),
2611 .@"packed" => if (mod.typeToStruct(struct_ty)) |struct_type|
2612 mod.structPackedFieldBitOffset(struct_type, index)
3322 const field_off: u32 = switch (struct_ty.containerLayout(zcu)) {
3323 .auto, .@"extern" => @intCast(struct_ty.structFieldOffset(index, zcu) * 8),
3324 .@"packed" => if (zcu.typeToStruct(struct_ty)) |struct_type|
3325 zcu.structPackedFieldBitOffset(struct_type, index)
26133326 else
26143327 0,
26153328 };
......@@ -2632,13 +3345,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
26323345 if (field_off > 0) {
26333346 _ = try self.addInst(.{
26343347 .tag = .srli,
2635 .data = .{
2636 .i_type = .{
2637 .imm12 = @intCast(field_off),
2638 .rd = dst_reg,
2639 .rs1 = dst_reg,
2640 },
2641 },
3348 .ops = .rri,
3349 .data = .{ .i_type = .{
3350 .imm12 = Immediate.s(@intCast(field_off)),
3351 .rd = dst_reg,
3352 .rs1 = dst_reg,
3353 } },
26423354 });
26433355
26443356 return self.fail("TODO: airStructFieldVal register with field_off > 0", .{});
......@@ -2646,10 +3358,49 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
26463358
26473359 break :result if (field_off == 0) dst_mcv else try self.copyToNewRegister(inst, dst_mcv);
26483360 },
2649 .stack_offset => |off| {
2650 log.debug("airStructFieldVal off: {}", .{field_off});
2651 const field_byte_off: u32 = @divExact(field_off, 8);
2652 break :result MCValue{ .stack_offset = off + field_byte_off };
3361 .load_frame => {
3362 const field_abi_size: u32 = @intCast(field_ty.abiSize(mod));
3363 if (field_off % 8 == 0) {
3364 const field_byte_off = @divExact(field_off, 8);
3365 const off_mcv = src_mcv.address().offset(@intCast(field_byte_off)).deref();
3366 const field_bit_size = field_ty.bitSize(mod);
3367
3368 if (field_abi_size <= 8) {
3369 const int_ty = try mod.intType(
3370 if (field_ty.isAbiInt(mod)) field_ty.intInfo(mod).signedness else .unsigned,
3371 @intCast(field_bit_size),
3372 );
3373
3374 const dst_reg, const dst_lock = try self.allocReg();
3375 const dst_mcv = MCValue{ .register = dst_reg };
3376 defer self.register_manager.unlockReg(dst_lock);
3377
3378 try self.genCopy(int_ty, dst_mcv, off_mcv);
3379 break :result try self.copyToNewRegister(inst, dst_mcv);
3380 }
3381
3382 const container_abi_size: u32 = @intCast(struct_ty.abiSize(mod));
3383 const dst_mcv = if (field_byte_off + field_abi_size <= container_abi_size and
3384 self.reuseOperand(inst, operand, 0, src_mcv))
3385 off_mcv
3386 else dst: {
3387 const dst_mcv = try self.allocRegOrMem(inst, true);
3388 try self.genCopy(field_ty, dst_mcv, off_mcv);
3389 break :dst dst_mcv;
3390 };
3391 if (field_abi_size * 8 > field_bit_size and dst_mcv.isMemory()) {
3392 const tmp_reg, const tmp_lock = try self.allocReg();
3393 defer self.register_manager.unlockReg(tmp_lock);
3394
3395 const hi_mcv =
3396 dst_mcv.address().offset(@intCast(field_bit_size / 64 * 8)).deref();
3397 try self.genSetReg(Type.usize, tmp_reg, hi_mcv);
3398 try self.genCopy(Type.usize, hi_mcv, .{ .register = tmp_reg });
3399 }
3400 break :result dst_mcv;
3401 }
3402
3403 return self.fail("TODO: airStructFieldVal load_frame field_off non multiple of 8", .{});
26533404 },
26543405 else => return self.fail("TODO: airStructField {s}", .{@tagName(src_mcv)}),
26553406 }
......@@ -2664,18 +3415,18 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
26643415}
26653416
26663417fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2667 const mod = self.bin_file.comp.module.?;
3418 const zcu = self.bin_file.comp.module.?;
26683419 const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
26693420 const ty = arg.ty.toType();
2670 const owner_decl = mod.funcOwnerDeclIndex(self.func_index);
2671 const name = mod.getParamName(self.func_index, arg.src_index);
3421 const owner_decl = zcu.funcOwnerDeclIndex(self.func_index);
3422 const name = zcu.getParamName(self.func_index, arg.src_index);
26723423
26733424 switch (self.debug_output) {
26743425 .dwarf => |dw| switch (mcv) {
26753426 .register => |reg| try dw.genArgDbgInfo(name, ty, owner_decl, .{
26763427 .register = reg.dwarfLocOp(),
26773428 }),
2678 .stack_offset => {},
3429 .load_frame => {},
26793430 else => {},
26803431 },
26813432 .plan9 => {},
......@@ -2694,12 +3445,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
26943445 const src_mcv = self.args[arg_index];
26953446
26963447 const dst_mcv = switch (src_mcv) {
2697 .register => |src_reg| dst: {
2698 self.register_manager.getRegAssumeFree(src_reg, null);
2699 break :dst src_mcv;
2700 },
2701 .register_pair => |pair| dst: {
2702 for (pair) |reg| self.register_manager.getRegAssumeFree(reg, null);
3448 .register, .register_pair, .load_frame => dst: {
3449 for (src_mcv.getRegs()) |reg| self.register_manager.getRegAssumeFree(reg, inst);
27033450 break :dst src_mcv;
27043451 },
27053452 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
......@@ -2715,7 +3462,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
27153462fn airTrap(self: *Self) !void {
27163463 _ = try self.addInst(.{
27173464 .tag = .unimp,
2718 .data = .{ .nop = {} },
3465 .ops = .none,
3466 .data = undefined,
27193467 });
27203468 return self.finishAirBookkeeping();
27213469}
......@@ -2723,19 +3471,22 @@ fn airTrap(self: *Self) !void {
27233471fn airBreakpoint(self: *Self) !void {
27243472 _ = try self.addInst(.{
27253473 .tag = .ebreak,
2726 .data = .{ .nop = {} },
3474 .ops = .none,
3475 .data = undefined,
27273476 });
27283477 return self.finishAirBookkeeping();
27293478}
27303479
27313480fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
2732 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{});
2733 return self.finishAir(inst, result, .{ .none, .none, .none });
3481 const dst_mcv = try self.allocRegOrMem(inst, true);
3482 try self.genCopy(Type.usize, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } });
3483 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
27343484}
27353485
27363486fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
2737 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{});
2738 return self.finishAir(inst, result, .{ .none, .none, .none });
3487 const dst_mcv = try self.allocRegOrMem(inst, true);
3488 try self.genCopy(Type.usize, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } });
3489 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
27393490}
27403491
27413492fn airFence(self: *Self) !void {
......@@ -2790,39 +3541,55 @@ fn genCall(
27903541 arg_tys: []const Type,
27913542 args: []const MCValue,
27923543) !MCValue {
2793 const mod = self.bin_file.comp.module.?;
3544 const zcu = self.bin_file.comp.module.?;
27943545
27953546 const fn_ty = switch (info) {
27963547 .air => |callee| fn_info: {
27973548 const callee_ty = self.typeOf(callee);
2798 break :fn_info switch (callee_ty.zigTypeTag(mod)) {
3549 break :fn_info switch (callee_ty.zigTypeTag(zcu)) {
27993550 .Fn => callee_ty,
2800 .Pointer => callee_ty.childType(mod),
3551 .Pointer => callee_ty.childType(zcu),
28013552 else => unreachable,
28023553 };
28033554 },
2804 .lib => |lib| try mod.funcType(.{
3555 .lib => |lib| try zcu.funcType(.{
28053556 .param_types = lib.param_types,
28063557 .return_type = lib.return_type,
28073558 .cc = .C,
28083559 }),
28093560 };
28103561
2811 var call_info = try self.resolveCallingConventionValues(fn_ty, .caller);
3562 const fn_info = zcu.typeToFunc(fn_ty).?;
3563 var call_info = try self.resolveCallingConventionValues(fn_info);
28123564 defer call_info.deinit(self);
28133565
3566 // We need a properly aligned and sized call frame to be able to call this function.
3567 {
3568 const needed_call_frame = FrameAlloc.init(.{
3569 .size = call_info.stack_byte_count,
3570 .alignment = call_info.stack_align,
3571 });
3572 const frame_allocs_slice = self.frame_allocs.slice();
3573 const stack_frame_size =
3574 &frame_allocs_slice.items(.abi_size)[@intFromEnum(FrameIndex.call_frame)];
3575 stack_frame_size.* = @max(stack_frame_size.*, needed_call_frame.abi_size);
3576 const stack_frame_align =
3577 &frame_allocs_slice.items(.abi_align)[@intFromEnum(FrameIndex.call_frame)];
3578 stack_frame_align.* = stack_frame_align.max(needed_call_frame.abi_align);
3579 }
3580
28143581 for (call_info.args, 0..) |mc_arg, arg_i| try self.genCopy(arg_tys[arg_i], mc_arg, args[arg_i]);
28153582
28163583 // Due to incremental compilation, how function calls are generated depends
28173584 // on linking.
28183585 switch (info) {
28193586 .air => |callee| {
2820 if (try self.air.value(callee, mod)) |func_value| {
2821 const func_key = mod.intern_pool.indexToKey(func_value.ip_index);
3587 if (try self.air.value(callee, zcu)) |func_value| {
3588 const func_key = zcu.intern_pool.indexToKey(func_value.ip_index);
28223589 switch (switch (func_key) {
28233590 else => func_key,
28243591 .ptr => |ptr| switch (ptr.addr) {
2825 .decl => |decl| mod.intern_pool.indexToKey(mod.declPtr(decl).val.toIntern()),
3592 .decl => |decl| zcu.intern_pool.indexToKey(zcu.declPtr(decl).val.toIntern()),
28263593 else => func_key,
28273594 },
28283595 }) {
......@@ -2835,10 +3602,11 @@ fn genCall(
28353602 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
28363603 _ = try self.addInst(.{
28373604 .tag = .jalr,
3605 .ops = .rri,
28383606 .data = .{ .i_type = .{
28393607 .rd = .ra,
28403608 .rs1 = .ra,
2841 .imm12 = 0,
3609 .imm12 = Immediate.s(0),
28423610 } },
28433611 });
28443612 } else if (self.bin_file.cast(link.File.Coff)) |_| {
......@@ -2855,16 +3623,17 @@ fn genCall(
28553623 else => return self.fail("TODO implement calling bitcasted functions", .{}),
28563624 }
28573625 } else {
2858 assert(self.typeOf(callee).zigTypeTag(mod) == .Pointer);
3626 assert(self.typeOf(callee).zigTypeTag(zcu) == .Pointer);
28593627 const addr_reg, const addr_lock = try self.allocReg();
28603628 defer self.register_manager.unlockReg(addr_lock);
28613629 try self.genSetReg(Type.usize, addr_reg, .{ .air_ref = callee });
28623630 _ = try self.addInst(.{
28633631 .tag = .jalr,
3632 .ops = .rri,
28643633 .data = .{ .i_type = .{
28653634 .rd = .ra,
28663635 .rs1 = addr_reg,
2867 .imm12 = 0,
3636 .imm12 = Immediate.s(0),
28683637 } },
28693638 });
28703639 }
......@@ -2872,11 +3641,12 @@ fn genCall(
28723641 .lib => return self.fail("TODO: lib func calls", .{}),
28733642 }
28743643
2875 return call_info.return_value;
3644 return call_info.return_value.short;
28763645}
28773646
28783647fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2879 const mod = self.bin_file.comp.module.?;
3648 const zcu = self.bin_file.comp.module.?;
3649 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
28803650
28813651 if (safety) {
28823652 // safe
......@@ -2884,32 +3654,35 @@ fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
28843654 // not safe
28853655 }
28863656
2887 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2888 const operand = try self.resolveInst(un_op);
2889
2890 _ = try self.addInst(.{
2891 .tag = .dbg_epilogue_begin,
2892 .data = .{ .nop = {} },
2893 });
2894
2895 const ret_ty = self.fn_type.fnReturnType(mod);
2896 try self.genCopy(ret_ty, self.ret_mcv, operand);
2897
2898 try self.ret();
2899
2900 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
2901}
3657 const ret_ty = self.fn_type.fnReturnType(zcu);
3658 switch (self.ret_mcv.short) {
3659 .none => {},
3660 .register,
3661 .register_pair,
3662 => try self.genCopy(ret_ty, self.ret_mcv.short, .{ .air_ref = un_op }),
3663 .indirect => |reg_off| {
3664 try self.register_manager.getReg(reg_off.reg, null);
3665 const lock = self.register_manager.lockRegAssumeUnused(reg_off.reg);
3666 defer self.register_manager.unlockReg(lock);
3667
3668 try self.genSetReg(Type.usize, reg_off.reg, self.ret_mcv.long);
3669 try self.genCopy(
3670 ret_ty,
3671 .{ .register_offset = reg_off },
3672 .{ .air_ref = un_op },
3673 );
3674 },
3675 else => unreachable,
3676 }
29023677
2903fn ret(self: *Self) !void {
2904 _ = try self.addInst(.{
2905 .tag = .psuedo_epilogue,
2906 .data = .{ .nop = {} },
2907 });
3678 self.ret_mcv.liveOut(self, inst);
3679 try self.finishAir(inst, .unreach, .{ un_op, .none, .none });
29083680
2909 // Just add space for an instruction, patch this later
3681 // Just add space for an instruction, reloced this later
29103682 const index = try self.addInst(.{
2911 .tag = .ret,
2912 .data = .{ .nop = {} },
3683 .tag = .pseudo,
3684 .ops = .pseudo_j,
3685 .data = .{ .inst = undefined },
29133686 });
29143687
29153688 try self.exitlude_jump_relocs.append(self.gpa, index);
......@@ -2918,37 +3691,49 @@ fn ret(self: *Self) !void {
29183691fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
29193692 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
29203693 const ptr = try self.resolveInst(un_op);
2921 const ptr_ty = self.typeOf(un_op);
29223694
2923 try self.load(self.ret_mcv, ptr, ptr_ty);
3695 const ptr_ty = self.typeOf(un_op);
3696 switch (self.ret_mcv.short) {
3697 .none => {},
3698 .register, .register_pair => try self.load(self.ret_mcv.short, ptr, ptr_ty),
3699 .indirect => |reg_off| try self.genSetReg(ptr_ty, reg_off.reg, ptr),
3700 else => unreachable,
3701 }
3702 self.ret_mcv.liveOut(self, inst);
3703 try self.finishAir(inst, .unreach, .{ un_op, .none, .none });
29243704
2925 try self.ret();
3705 // Just add space for an instruction, reloced this later
3706 const index = try self.addInst(.{
3707 .tag = .pseudo,
3708 .ops = .pseudo_j,
3709 .data = .{ .inst = undefined },
3710 });
29263711
2927 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
3712 try self.exitlude_jump_relocs.append(self.gpa, index);
29283713}
29293714
29303715fn airCmp(self: *Self, inst: Air.Inst.Index) !void {
29313716 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
29323717 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2933 const mod = self.bin_file.comp.module.?;
3718 const zcu = self.bin_file.comp.module.?;
29343719
2935 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3720 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
29363721 const lhs = try self.resolveInst(bin_op.lhs);
29373722 const rhs = try self.resolveInst(bin_op.rhs);
29383723 const lhs_ty = self.typeOf(bin_op.lhs);
29393724
2940 const int_ty = switch (lhs_ty.zigTypeTag(mod)) {
3725 const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
29413726 .Vector => unreachable, // Handled by cmp_vector.
2942 .Enum => lhs_ty.intTagType(mod),
3727 .Enum => lhs_ty.intTagType(zcu),
29433728 .Int => lhs_ty,
29443729 .Bool => Type.u1,
29453730 .Pointer => Type.usize,
29463731 .ErrorSet => Type.u16,
29473732 .Optional => blk: {
2948 const payload_ty = lhs_ty.optionalChild(mod);
2949 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
3733 const payload_ty = lhs_ty.optionalChild(zcu);
3734 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
29503735 break :blk Type.u1;
2951 } else if (lhs_ty.isPtrLikeOptional(mod)) {
3736 } else if (lhs_ty.isPtrLikeOptional(zcu)) {
29523737 break :blk Type.usize;
29533738 } else {
29543739 return self.fail("TODO riscv cmp non-pointer optionals", .{});
......@@ -2958,7 +3743,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void {
29583743 else => unreachable,
29593744 };
29603745
2961 const int_info = int_ty.intInfo(mod);
3746 const int_info = int_ty.intInfo(zcu);
29623747 if (int_info.bits <= 64) {
29633748 break :result try self.binOp(tag, null, lhs, rhs, int_ty, int_ty);
29643749 } else {
......@@ -2978,7 +3763,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
29783763 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
29793764 const operand = try self.resolveInst(un_op);
29803765 _ = operand;
2981 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
3766 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
29823767 return self.finishAir(inst, result, .{ un_op, .none, .none });
29833768}
29843769
......@@ -2986,8 +3771,9 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
29863771 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
29873772
29883773 _ = try self.addInst(.{
2989 .tag = .dbg_line,
2990 .data = .{ .dbg_line_column = .{
3774 .tag = .pseudo,
3775 .ops = .pseudo_dbg_line_column,
3776 .data = .{ .pseudo_dbg_line_column = .{
29913777 .line = dbg_stmt.line,
29923778 .column = dbg_stmt.column,
29933779 } },
......@@ -3023,7 +3809,7 @@ fn genVarDbgInfo(
30233809 mcv: MCValue,
30243810 name: [:0]const u8,
30253811) !void {
3026 const mod = self.bin_file.comp.module.?;
3812 const zcu = self.bin_file.comp.module.?;
30273813 const is_ptr = switch (tag) {
30283814 .dbg_var_ptr => true,
30293815 .dbg_var_val => false,
......@@ -3043,11 +3829,11 @@ fn genVarDbgInfo(
30433829 .undef => .undef,
30443830 .none => .none,
30453831 else => blk: {
3046 log.debug("TODO generate debug info for {}", .{mcv});
3832 log.warn("TODO generate debug info for {}", .{mcv});
30473833 break :blk .nop;
30483834 },
30493835 };
3050 try dw.genVarDbgInfo(name, ty, mod.funcOwnerDeclIndex(self.func_index), is_ptr, loc);
3836 try dw.genVarDbgInfo(name, ty, zcu.funcOwnerDeclIndex(self.func_index), is_ptr, loc);
30513837 },
30523838 .plan9 => {},
30533839 .none => {},
......@@ -3061,146 +3847,49 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
30613847 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
30623848 const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]);
30633849 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);
3064 const liveness_condbr = self.liveness.getCondBr(inst);
3065
3066 const cond_reg = try self.register_manager.allocReg(inst, gp);
3067 const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg);
3068 defer self.register_manager.unlockReg(cond_reg_lock);
3069
3070 // A branch to the false section. Uses beq. 1 is the default "true" state.
3071 const reloc = try self.condBr(cond_ty, cond, cond_reg);
3850 const liveness_cond_br = self.liveness.getCondBr(inst);
30723851
30733852 // If the condition dies here in this condbr instruction, process
30743853 // that death now instead of later as this has an effect on
30753854 // whether it needs to be spilled in the branches
30763855 if (self.liveness.operandDies(inst, 0)) {
3077 if (pl_op.operand.toIndex()) |op_inst| self.processDeath(op_inst);
3856 if (pl_op.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
30783857 }
30793858
3080 // Save state
3081 const parent_next_stack_offset = self.next_stack_offset;
3082 const parent_free_registers = self.register_manager.free_registers;
3083 var parent_stack = try self.stack.clone(self.gpa);
3084 defer parent_stack.deinit(self.gpa);
3085 const parent_registers = self.register_manager.registers;
3859 self.scope_generation += 1;
3860 const state = try self.saveState();
3861 const reloc = try self.condBr(cond_ty, cond);
30863862
3087 try self.branch_stack.append(.{});
3088 errdefer {
3089 _ = self.branch_stack.pop();
3090 }
3091
3092 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
3093 for (liveness_condbr.then_deaths) |operand| {
3094 self.processDeath(operand);
3095 }
3863 for (liveness_cond_br.then_deaths) |death| try self.processDeath(death);
30963864 try self.genBody(then_body);
3865 try self.restoreState(state, &.{}, .{
3866 .emit_instructions = false,
3867 .update_tracking = true,
3868 .resurrect = true,
3869 .close_scope = true,
3870 });
30973871
3098 // Restore state
3099 var saved_then_branch = self.branch_stack.pop();
3100 defer saved_then_branch.deinit(self.gpa);
3101
3102 self.register_manager.registers = parent_registers;
3103
3104 self.stack.deinit(self.gpa);
3105 self.stack = parent_stack;
3106 parent_stack = .{};
3107
3108 self.next_stack_offset = parent_next_stack_offset;
3109 self.register_manager.free_registers = parent_free_registers;
3110
3111 const else_branch = self.branch_stack.addOneAssumeCapacity();
3112 else_branch.* = .{};
3113
3114 try self.performReloc(reloc, @intCast(self.mir_instructions.len));
3872 self.performReloc(reloc);
31153873
3116 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
3117 for (liveness_condbr.else_deaths) |operand| {
3118 self.processDeath(operand);
3119 }
3874 for (liveness_cond_br.else_deaths) |death| try self.processDeath(death);
31203875 try self.genBody(else_body);
3876 try self.restoreState(state, &.{}, .{
3877 .emit_instructions = false,
3878 .update_tracking = true,
3879 .resurrect = true,
3880 .close_scope = true,
3881 });
31213882
3122 // At this point, each branch will possibly have conflicting values for where
3123 // each instruction is stored. They agree, however, on which instructions are alive/dead.
3124 // We use the first ("then") branch as canonical, and here emit
3125 // instructions into the second ("else") branch to make it conform.
3126 // We continue respect the data structure semantic guarantees of the else_branch so
3127 // that we can use all the code emitting abstractions. This is why at the bottom we
3128 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
3129 // rather than assigning it.
3130 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
3131 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
3132 const else_slice = else_branch.inst_table.entries.slice();
3133 const else_keys = else_slice.items(.key);
3134 const else_values = else_slice.items(.value);
3135 for (else_keys, 0..) |else_key, else_idx| {
3136 const else_value = else_values[else_idx];
3137 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
3138 // The instruction's MCValue is overridden in both branches.
3139 log.debug("condBr put branch table (key = %{d}, value = {})", .{ else_key, then_entry.value });
3140 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
3141 if (else_value == .dead) {
3142 assert(then_entry.value == .dead);
3143 continue;
3144 }
3145 break :blk then_entry.value;
3146 } else blk: {
3147 if (else_value == .dead)
3148 continue;
3149 // The instruction is only overridden in the else branch.
3150 var i: usize = self.branch_stack.items.len - 2;
3151 while (true) {
3152 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
3153 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
3154 assert(mcv != .dead);
3155 break :blk mcv;
3156 }
3157 }
3158 };
3159 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
3160 // TODO make sure the destination stack offset / register does not already have something
3161 // going on there.
3162 try self.genCopy(self.typeOfIndex(else_key), canon_mcv, else_value);
3163 // TODO track the new register / stack allocation
3164 }
3165 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
3166 const then_slice = saved_then_branch.inst_table.entries.slice();
3167 const then_keys = then_slice.items(.key);
3168 const then_values = then_slice.items(.value);
3169 for (then_keys, 0..) |then_key, then_idx| {
3170 const then_value = then_values[then_idx];
3171 // We already deleted the items from this table that matched the else_branch.
3172 // So these are all instructions that are only overridden in the then branch.
3173 parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
3174 if (then_value == .dead)
3175 continue;
3176 const parent_mcv = blk: {
3177 var i: usize = self.branch_stack.items.len - 2;
3178 while (true) {
3179 i -= 1;
3180 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
3181 assert(mcv != .dead);
3182 break :blk mcv;
3183 }
3184 }
3185 };
3186 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
3187 // TODO make sure the destination stack offset / register does not already have something
3188 // going on there.
3189 try self.genCopy(self.typeOfIndex(then_key), parent_mcv, then_value);
3190 // TODO track the new register / stack allocation
3191 }
3192
3193 {
3194 var item = self.branch_stack.pop();
3195 item.deinit(self.gpa);
3196 }
3883 // We already took care of pl_op.operand earlier, so there's nothing left to do.
3884 self.finishAirBookkeeping();
31973885}
31983886
3199fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !Mir.Inst.Index {
3200 try self.genSetReg(cond_ty, cond_reg, condition);
3887fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
3888 const cond_reg = try self.copyToTmpRegister(cond_ty, condition);
32013889
32023890 return try self.addInst(.{
32033891 .tag = .beq,
3892 .ops = .rr_inst,
32043893 .data = .{
32053894 .b_type = .{
32063895 .rs1 = cond_reg,
......@@ -3213,7 +3902,7 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !M
32133902
32143903fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
32153904 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3216 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3905 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
32173906 const operand = try self.resolveInst(un_op);
32183907 break :result try self.isNull(operand);
32193908 };
......@@ -3222,7 +3911,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
32223911
32233912fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
32243913 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3225 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3914 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
32263915 const operand_ptr = try self.resolveInst(un_op);
32273916 const operand: MCValue = blk: {
32283917 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
......@@ -3247,7 +3936,7 @@ fn isNull(self: *Self, operand: MCValue) !MCValue {
32473936
32483937fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
32493938 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3250 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3939 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
32513940 const operand = try self.resolveInst(un_op);
32523941 break :result try self.isNonNull(operand);
32533942 };
......@@ -3263,7 +3952,7 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue {
32633952
32643953fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
32653954 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3266 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3955 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
32673956 const operand_ptr = try self.resolveInst(un_op);
32683957 const operand: MCValue = blk: {
32693958 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
......@@ -3281,7 +3970,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
32813970
32823971fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
32833972 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3284 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3973 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
32853974 const operand = try self.resolveInst(un_op);
32863975 const operand_ty = self.typeOf(un_op);
32873976 break :result try self.isErr(inst, operand_ty, operand);
......@@ -3290,9 +3979,9 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
32903979}
32913980
32923981fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3293 const mod = self.bin_file.comp.module.?;
3982 const zcu = self.bin_file.comp.module.?;
32943983 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3295 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3984 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
32963985 const operand_ptr = try self.resolveInst(un_op);
32973986 const operand: MCValue = blk: {
32983987 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
......@@ -3304,7 +3993,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
33043993 };
33053994 try self.load(operand, operand_ptr, self.typeOf(un_op));
33063995 const operand_ptr_ty = self.typeOf(un_op);
3307 const operand_ty = operand_ptr_ty.childType(mod);
3996 const operand_ty = operand_ptr_ty.childType(zcu);
33083997
33093998 break :result try self.isErr(inst, operand_ty, operand);
33103999 };
......@@ -3315,13 +4004,13 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
33154004///
33164005/// Result is in the return register.
33174006fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MCValue {
3318 const mod = self.bin_file.comp.module.?;
3319 const err_ty = eu_ty.errorUnionSet(mod);
3320 if (err_ty.errorSetIsEmpty(mod)) return MCValue{ .immediate = 0 }; // always false
4007 const zcu = self.bin_file.comp.module.?;
4008 const err_ty = eu_ty.errorUnionSet(zcu);
4009 if (err_ty.errorSetIsEmpty(zcu)) return MCValue{ .immediate = 0 }; // always false
33214010
33224011 _ = maybe_inst;
33234012
3324 const err_off = errUnionErrorOffset(eu_ty.errorUnionPayload(mod), mod);
4013 const err_off = errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu);
33254014
33264015 switch (eu_mcv) {
33274016 .register => |reg| {
......@@ -3361,7 +4050,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue)
33614050
33624051fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
33634052 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3364 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4053 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
33654054 const operand = try self.resolveInst(un_op);
33664055 const ty = self.typeOf(un_op);
33674056 break :result try self.isNonErr(inst, ty, operand);
......@@ -3375,6 +4064,7 @@ fn isNonErr(self: *Self, inst: Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MC
33754064 .register => |reg| {
33764065 _ = try self.addInst(.{
33774066 .tag = .not,
4067 .ops = .rr,
33784068 .data = .{
33794069 .rr = .{
33804070 .rd = reg,
......@@ -3394,9 +4084,9 @@ fn isNonErr(self: *Self, inst: Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MC
33944084}
33954085
33964086fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3397 const mod = self.bin_file.comp.module.?;
4087 const zcu = self.bin_file.comp.module.?;
33984088 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3399 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4089 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
34004090 const operand_ptr = try self.resolveInst(un_op);
34014091 const operand: MCValue = blk: {
34024092 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
......@@ -3407,7 +4097,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
34074097 }
34084098 };
34094099 const operand_ptr_ty = self.typeOf(un_op);
3410 const operand_ty = operand_ptr_ty.childType(mod);
4100 const operand_ty = operand_ptr_ty.childType(zcu);
34114101
34124102 try self.load(operand, operand_ptr, self.typeOf(un_op));
34134103 break :result try self.isNonErr(inst, operand_ty, operand);
......@@ -3421,18 +4111,27 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
34214111 const loop = self.air.extraData(Air.Block, ty_pl.payload);
34224112 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]);
34234113
3424 const start_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
4114 self.scope_generation += 1;
4115 const state = try self.saveState();
34254116
4117 const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len);
34264118 try self.genBody(body);
3427 try self.jump(start_index);
4119 try self.restoreState(state, &.{}, .{
4120 .emit_instructions = true,
4121 .update_tracking = false,
4122 .resurrect = false,
4123 .close_scope = true,
4124 });
4125 _ = try self.jump(jmp_target);
34284126
3429 return self.finishAirBookkeeping();
4127 self.finishAirBookkeeping();
34304128}
34314129
34324130/// Send control flow to the `index` of `self.code`.
3433fn jump(self: *Self, index: Mir.Inst.Index) !void {
3434 _ = try self.addInst(.{
3435 .tag = .j,
4131fn jump(self: *Self, index: Mir.Inst.Index) !Mir.Inst.Index {
4132 return self.addInst(.{
4133 .tag = .pseudo,
4134 .ops = .pseudo_j,
34364135 .data = .{
34374136 .inst = index,
34384137 },
......@@ -3446,33 +4145,34 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
34464145}
34474146
34484147fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
3449 try self.blocks.putNoClobber(self.gpa, inst, .{
3450 // A block is a setup to be able to jump to the end.
3451 .relocs = .{},
3452 // It also acts as a receptacle for break operands.
3453 // Here we use `MCValue.none` to represent a null value so that the first
3454 // break instruction will choose a MCValue for the block result and overwrite
3455 // this field. Following break instructions will use that MCValue to put their
3456 // block results.
3457 .mcv = MCValue{ .none = {} },
3458 });
3459 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
4148 // A block is a setup to be able to jump to the end.
4149 const inst_tracking_i = self.inst_tracking.count();
4150 self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(.unreach));
4151
4152 self.scope_generation += 1;
4153 try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() });
4154 const liveness = self.liveness.getBlock(inst);
34604155
34614156 // TODO emit debug info lexical block
34624157 try self.genBody(body);
34634158
3464 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| {
3465 // here we are relocing to point at the instruction after the block.
3466 // [then case]
3467 // [jump to end] // this is reloced
3468 // [else case]
3469 // [jump to end] // this is reloced
3470 // [this isn't generated yet] // point to here
3471 try self.performReloc(reloc, @intCast(self.mir_instructions.len));
4159 var block_data = self.blocks.fetchRemove(inst).?;
4160 defer block_data.value.deinit(self.gpa);
4161 if (block_data.value.relocs.items.len > 0) {
4162 try self.restoreState(block_data.value.state, liveness.deaths, .{
4163 .emit_instructions = false,
4164 .update_tracking = true,
4165 .resurrect = true,
4166 .close_scope = true,
4167 });
4168 for (block_data.value.relocs.items) |reloc| self.performReloc(reloc);
34724169 }
34734170
3474 const result = self.blocks.getPtr(inst).?.mcv;
3475 return self.finishAir(inst, result, .{ .none, .none, .none });
4171 if (std.debug.runtime_safety) assert(self.inst_tracking.getIndex(inst).? == inst_tracking_i);
4172 const tracking = &self.inst_tracking.values()[inst_tracking_i];
4173 if (self.liveness.isUnused(inst)) try tracking.die(self, inst);
4174 self.getValueIfFree(tracking.short, inst);
4175 self.finishAirBookkeeping();
34764176}
34774177
34784178fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3483,8 +4183,10 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
34834183 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
34844184}
34854185
3486fn performReloc(self: *Self, inst: Mir.Inst.Index, target: Mir.Inst.Index) !void {
4186fn performReloc(self: *Self, inst: Mir.Inst.Index) void {
34874187 const tag = self.mir_instructions.items(.tag)[inst];
4188 const ops = self.mir_instructions.items(.ops)[inst];
4189 const target: Mir.Inst.Index = @intCast(self.mir_instructions.len);
34884190
34894191 switch (tag) {
34904192 .bne,
......@@ -3492,52 +4194,81 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index, target: Mir.Inst.Index) !void
34924194 => self.mir_instructions.items(.data)[inst].b_type.inst = target,
34934195 .jal,
34944196 => self.mir_instructions.items(.data)[inst].j_type.inst = target,
3495 .j,
3496 => self.mir_instructions.items(.data)[inst].inst = target,
3497 else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),
4197 .pseudo => switch (ops) {
4198 .pseudo_j => self.mir_instructions.items(.data)[inst].inst = target,
4199 else => std.debug.panic("TODO: performReloc {s}", .{@tagName(ops)}),
4200 },
4201 else => std.debug.panic("TODO: performReloc {s}", .{@tagName(tag)}),
34984202 }
34994203}
35004204
35014205fn airBr(self: *Self, inst: Air.Inst.Index) !void {
3502 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
3503 try self.br(branch.block_inst, branch.operand);
3504 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
3505}
3506
3507fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
3508 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3509 const air_tags = self.air.instructions.items(.tag);
3510 _ = air_tags;
3511 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
3512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3513}
3514
3515fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
3516 const block_data = self.blocks.getPtr(block).?;
3517
35184206 const mod = self.bin_file.comp.module.?;
3519 if (self.typeOf(operand).hasRuntimeBits(mod)) {
3520 const operand_mcv = try self.resolveInst(operand);
3521 const block_mcv = block_data.mcv;
3522 if (block_mcv == .none) {
3523 block_data.mcv = operand_mcv;
3524 } else {
3525 try self.genCopy(self.typeOfIndex(block), block_mcv, operand_mcv);
4207 const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
4208
4209 const block_ty = self.typeOfIndex(br.block_inst);
4210 const block_unused =
4211 !block_ty.hasRuntimeBitsIgnoreComptime(mod) or self.liveness.isUnused(br.block_inst);
4212 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?;
4213 const block_data = self.blocks.getPtr(br.block_inst).?;
4214 const first_br = block_data.relocs.items.len == 0;
4215 const block_result = result: {
4216 if (block_unused) break :result .none;
4217
4218 if (!first_br) try self.getValue(block_tracking.short, null);
4219 const src_mcv = try self.resolveInst(br.operand);
4220
4221 if (self.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) {
4222 if (first_br) break :result src_mcv;
4223
4224 try self.getValue(block_tracking.short, br.block_inst);
4225 // .long = .none to avoid merging operand and block result stack frames.
4226 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv };
4227 try current_tracking.materializeUnsafe(self, br.block_inst, block_tracking.*);
4228 for (current_tracking.getRegs()) |src_reg| self.register_manager.freeReg(src_reg);
4229 break :result block_tracking.short;
35264230 }
4231
4232 const dst_mcv = if (first_br) try self.allocRegOrMem(br.block_inst, true) else dst: {
4233 try self.getValue(block_tracking.short, br.block_inst);
4234 break :dst block_tracking.short;
4235 };
4236 try self.genCopy(block_ty, dst_mcv, try self.resolveInst(br.operand));
4237 break :result dst_mcv;
4238 };
4239
4240 // Process operand death so that it is properly accounted for in the State below.
4241 if (self.liveness.operandDies(inst, 0)) {
4242 if (br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
35274243 }
3528 return self.brVoid(block);
3529}
35304244
3531fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3532 const block_data = self.blocks.getPtr(block).?;
4245 if (first_br) {
4246 block_tracking.* = InstTracking.init(block_result);
4247 try self.saveRetroactiveState(&block_data.state);
4248 } else try self.restoreState(block_data.state, &.{}, .{
4249 .emit_instructions = true,
4250 .update_tracking = false,
4251 .resurrect = false,
4252 .close_scope = false,
4253 });
35334254
35344255 // Emit a jump with a relocation. It will be patched up after the block ends.
3535 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
4256 // Leave the jump offset undefined
4257 const jmp_reloc = try self.jump(undefined);
4258 try block_data.relocs.append(self.gpa, jmp_reloc);
35364259
3537 block_data.relocs.appendAssumeCapacity(try self.addInst(.{
3538 .tag = .j,
3539 .data = .{ .inst = undefined },
3540 }));
4260 // Stop tracking block result without forgetting tracking info
4261 try self.freeValue(block_tracking.short);
4262
4263 self.finishAirBookkeeping();
4264}
4265
4266fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
4267 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4268 const air_tags = self.air.instructions.items(.tag);
4269 _ = air_tags;
4270 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
4271 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
35414272}
35424273
35434274fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3546,13 +4277,16 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
35464277 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
35474278 const clobbers_len: u31 = @truncate(extra.data.flags);
35484279 var extra_i: usize = extra.end;
3549 const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]);
4280 const outputs: []const Air.Inst.Ref =
4281 @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]);
35504282 extra_i += outputs.len;
35514283 const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]);
35524284 extra_i += inputs.len;
35534285
4286 log.debug("airAsm input: {any}", .{inputs});
4287
35544288 const dead = !is_volatile and self.liveness.isUnused(inst);
3555 const result: MCValue = if (dead) .dead else result: {
4289 const result: MCValue = if (dead) .unreach else result: {
35564290 if (outputs.len > 1) {
35574291 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
35584292 }
......@@ -3599,19 +4333,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
35994333 // for the string, we still use the next u32 for the null terminator.
36004334 extra_i += clobber.len / 4 + 1;
36014335
3602 // TODO honor these
4336 if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) {
4337 // nothing really to do
4338 } else {
4339 try self.register_manager.getReg(parseRegName(clobber) orelse
4340 return self.fail("invalid clobber: '{s}'", .{clobber}), null);
4341 }
36034342 }
36044343 }
36054344
36064345 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
36074346
3608 if (mem.eql(u8, asm_source, "ecall")) {
4347 if (std.meta.stringToEnum(Mir.Inst.Tag, asm_source)) |tag| {
36094348 _ = try self.addInst(.{
3610 .tag = .ecall,
3611 .data = .{ .nop = {} },
4349 .tag = tag,
4350 .ops = .none,
4351 .data = undefined,
36124352 });
36134353 } else {
3614 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
4354 return self.fail("TODO: asm_source {s}", .{asm_source});
36154355 }
36164356
36174357 if (output_constraint) |output| {
......@@ -3621,11 +4361,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
36214361 const reg_name = output[2 .. output.len - 1];
36224362 const reg = parseRegName(reg_name) orelse
36234363 return self.fail("unrecognized register: '{s}'", .{reg_name});
3624 break :result MCValue{ .register = reg };
4364 break :result .{ .register = reg };
36254365 } else {
3626 break :result MCValue{ .none = {} };
4366 break :result .{ .none = {} };
36274367 }
36284368 };
4369
36294370 simple: {
36304371 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
36314372 var buf_index: usize = 0;
......@@ -3640,30 +4381,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
36404381 @memcpy(buf[buf_index..][0..inputs.len], inputs);
36414382 return self.finishAir(inst, result, buf);
36424383 }
3643 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3644 for (outputs) |output| {
3645 if (output == .none) continue;
3646
3647 bt.feed(output);
3648 }
3649 for (inputs) |input| {
3650 bt.feed(input);
3651 }
3652 return bt.finishAir(result);
3653}
3654
3655fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
3656 try self.ensureProcessDeathCapacity(operand_count + 1);
3657 return BigTomb{
3658 .function = self,
3659 .inst = inst,
3660 .lbt = self.liveness.iterateBigTomb(inst),
3661 };
4384 var bt = self.liveness.iterateBigTomb(inst);
4385 for (outputs) |output| if (output != .none) try self.feed(&bt, output);
4386 for (inputs) |input| try self.feed(&bt, input);
4387 return self.finishAirResult(inst, result);
36624388}
36634389
36644390/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
36654391fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
3666 const mod = self.bin_file.comp.module.?;
4392 const zcu = self.bin_file.comp.module.?;
36674393
36684394 // There isn't anything to store
36694395 if (dst_mcv == .none) return;
......@@ -3690,11 +4416,11 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
36904416 .off = -dst_reg_off.off,
36914417 } },
36924418 }),
3693 .stack_offset => |off| return self.genSetStack(ty, off, src_mcv),
3694 .memory => |addr| return self.genSetMem(ty, addr, src_mcv),
4419 .load_frame => |frame| return self.genSetStack(ty, frame, src_mcv),
4420 .memory => return self.fail("TODO: genCopy memory", .{}),
36954421 .register_pair => |dst_regs| {
36964422 const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = switch (src_mcv) {
3697 .register_pair, .memory, .indirect, .stack_offset => null,
4423 .register_pair, .memory, .indirect, .load_frame => null,
36984424 .load_symbol => src: {
36994425 const src_addr_reg, const src_addr_lock = try self.allocReg();
37004426 errdefer self.register_manager.unlockReg(src_addr_lock);
......@@ -3708,7 +4434,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
37084434 try self.resolveInst(src_ref),
37094435 ),
37104436 else => return self.fail("TODO implement genCopy for {s} of {}", .{
3711 @tagName(src_mcv), ty.fmt(mod),
4437 @tagName(src_mcv), ty.fmt(zcu),
37124438 }),
37134439 };
37144440 defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock);
......@@ -3717,34 +4443,38 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
37174443 for (dst_regs, try self.splitType(ty), 0..) |dst_reg, dst_ty, part_i| {
37184444 try self.genSetReg(dst_ty, dst_reg, switch (src_mcv) {
37194445 .register_pair => |src_regs| .{ .register = src_regs[part_i] },
3720 .memory, .indirect, .stack_offset => src_mcv.address().offset(part_disp).deref(),
4446 .memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(),
37214447 .load_symbol => .{ .indirect = .{
37224448 .reg = src_info.?.addr_reg,
37234449 .off = part_disp,
37244450 } },
37254451 else => unreachable,
37264452 });
3727 part_disp += @intCast(dst_ty.abiSize(mod));
4453 part_disp += @intCast(dst_ty.abiSize(zcu));
37284454 }
37294455 },
37304456 else => return std.debug.panic("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }),
37314457 }
37324458}
37334459
3734/// Sets the value of `src_mcv` into stack memory at `stack_offset`.
3735fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) InnerError!void {
3736 const mod = self.bin_file.comp.module.?;
3737 const abi_size: u32 = @intCast(ty.abiSize(mod));
4460fn genSetStack(
4461 self: *Self,
4462 ty: Type,
4463 frame: FrameAddr,
4464 src_mcv: MCValue,
4465) InnerError!void {
4466 const zcu = self.bin_file.comp.module.?;
4467 const abi_size: u32 = @intCast(ty.abiSize(zcu));
37384468
37394469 switch (src_mcv) {
37404470 .none => return,
37414471 .dead => unreachable,
37424472 .undef => {
37434473 if (!self.wantSafety()) return;
3744 try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
4474 try self.genSetStack(ty, frame, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
37454475 },
37464476 .immediate,
3747 .ptr_stack_offset,
4477 .lea_frame,
37484478 => {
37494479 // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with
37504480 // a register allocation.
......@@ -3753,26 +4483,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) Inner
37534483
37544484 try self.genSetReg(ty, reg, src_mcv);
37554485
3756 return self.genSetStack(ty, stack_offset, .{ .register = reg });
4486 return self.genSetStack(ty, frame, .{ .register = reg });
37574487 },
37584488 .register => |reg| {
37594489 switch (abi_size) {
37604490 1, 2, 4, 8 => {
3761 const tag: Mir.Inst.Tag = switch (abi_size) {
3762 1 => .sb,
3763 2 => .sh,
3764 4 => .sw,
3765 8 => .sd,
3766 else => unreachable,
3767 };
3768
37694491 _ = try self.addInst(.{
3770 .tag = tag,
3771 .data = .{ .i_type = .{
3772 .rd = reg,
3773 .rs1 = .sp,
3774 .imm12 = math.cast(i12, stack_offset) orelse {
3775 return self.fail("TODO: genSetStack bigger stack values", .{});
4492 .tag = .pseudo,
4493 .ops = .pseudo_store_rm,
4494 .data = .{ .rm = .{
4495 .r = reg,
4496 .m = .{
4497 .base = .{ .frame = frame.index },
4498 .mod = .{
4499 .rm = .{
4500 .size = self.memSize(ty),
4501 .disp = frame.off,
4502 },
4503 },
37764504 },
37774505 } },
37784506 });
......@@ -3780,38 +4508,26 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) Inner
37804508 else => unreachable, // register can hold a max of 8 bytes
37814509 }
37824510 },
3783 .stack_offset,
4511 .load_frame,
37844512 .indirect,
37854513 .load_symbol,
37864514 => {
3787 if (src_mcv == .stack_offset and src_mcv.stack_offset == stack_offset) return;
3788
37894515 if (abi_size <= 8) {
37904516 const reg = try self.copyToTmpRegister(ty, src_mcv);
3791 return self.genSetStack(ty, stack_offset, .{ .register = reg });
4517 return self.genSetStack(ty, frame, .{ .register = reg });
37924518 }
37934519
37944520 try self.genInlineMemcpy(
3795 .{ .ptr_stack_offset = stack_offset },
4521 .{ .lea_frame = frame },
37964522 src_mcv.address(),
37974523 .{ .immediate = abi_size },
37984524 );
37994525 },
3800 .air_ref => |ref| try self.genSetStack(ty, stack_offset, try self.resolveInst(ref)),
4526 .air_ref => |ref| try self.genSetStack(ty, frame, try self.resolveInst(ref)),
38014527 else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_mcv)}),
38024528 }
38034529}
38044530
3805fn genSetMem(self: *Self, ty: Type, addr: u64, src_mcv: MCValue) InnerError!void {
3806 const mod = self.bin_file.comp.module.?;
3807 const abi_size: u32 = @intCast(ty.abiSize(mod));
3808 _ = abi_size;
3809 _ = addr;
3810 _ = src_mcv;
3811
3812 return self.fail("TODO: genSetMem", .{});
3813}
3814
38154531fn genInlineMemcpy(
38164532 self: *Self,
38174533 dst_ptr: MCValue,
......@@ -3834,11 +4550,12 @@ fn genInlineMemcpy(
38344550 // lb tmp, 0(src)
38354551 const first_inst = try self.addInst(.{
38364552 .tag = .lb,
4553 .ops = .rri,
38374554 .data = .{
38384555 .i_type = .{
38394556 .rd = tmp,
38404557 .rs1 = src,
3841 .imm12 = 0,
4558 .imm12 = Immediate.s(0),
38424559 },
38434560 },
38444561 });
......@@ -3846,11 +4563,12 @@ fn genInlineMemcpy(
38464563 // sb tmp, 0(dst)
38474564 _ = try self.addInst(.{
38484565 .tag = .sb,
4566 .ops = .rri,
38494567 .data = .{
38504568 .i_type = .{
38514569 .rd = tmp,
38524570 .rs1 = dst,
3853 .imm12 = 0,
4571 .imm12 = Immediate.s(0),
38544572 },
38554573 },
38564574 });
......@@ -3858,11 +4576,12 @@ fn genInlineMemcpy(
38584576 // dec count by 1
38594577 _ = try self.addInst(.{
38604578 .tag = .addi,
4579 .ops = .rri,
38614580 .data = .{
38624581 .i_type = .{
38634582 .rd = count,
38644583 .rs1 = count,
3865 .imm12 = -1,
4584 .imm12 = Immediate.s(-1),
38664585 },
38674586 },
38684587 });
......@@ -3870,6 +4589,7 @@ fn genInlineMemcpy(
38704589 // branch if count is 0
38714590 _ = try self.addInst(.{
38724591 .tag = .beq,
4592 .ops = .rr_inst,
38734593 .data = .{
38744594 .b_type = .{
38754595 .inst = @intCast(self.mir_instructions.len + 4), // points after the last inst
......@@ -3882,29 +4602,32 @@ fn genInlineMemcpy(
38824602 // increment the pointers
38834603 _ = try self.addInst(.{
38844604 .tag = .addi,
4605 .ops = .rri,
38854606 .data = .{
38864607 .i_type = .{
38874608 .rd = src,
38884609 .rs1 = src,
3889 .imm12 = 1,
4610 .imm12 = Immediate.s(1),
38904611 },
38914612 },
38924613 });
38934614
38944615 _ = try self.addInst(.{
38954616 .tag = .addi,
4617 .ops = .rri,
38964618 .data = .{
38974619 .i_type = .{
38984620 .rd = dst,
38994621 .rs1 = dst,
3900 .imm12 = 1,
4622 .imm12 = Immediate.s(1),
39014623 },
39024624 },
39034625 });
39044626
39054627 // jump back to start of loop
39064628 _ = try self.addInst(.{
3907 .tag = .j,
4629 .tag = .pseudo,
4630 .ops = .pseudo_j,
39084631 .data = .{
39094632 .inst = first_inst,
39104633 },
......@@ -3913,31 +4636,13 @@ fn genInlineMemcpy(
39134636
39144637/// Sets the value of `src_mcv` into `reg`. Assumes you have a lock on it.
39154638fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!void {
3916 const mod = self.bin_file.comp.module.?;
3917 const abi_size: u32 = @intCast(ty.abiSize(mod));
3918
3919 const load_tag: Mir.Inst.Tag = switch (abi_size) {
3920 1 => .lb,
3921 2 => .lh,
3922 4 => .lw,
3923 8 => .ld,
3924 else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}),
3925 };
4639 const zcu = self.bin_file.comp.module.?;
4640 const abi_size: u32 = @intCast(ty.abiSize(zcu));
4641
4642 if (abi_size > 8) return self.fail("tried to set reg with size {}", .{abi_size});
39264643
39274644 switch (src_mcv) {
39284645 .dead => unreachable,
3929 .ptr_stack_offset => |off| {
3930 _ = try self.addInst(.{
3931 .tag = .addi,
3932 .data = .{ .i_type = .{
3933 .rd = reg,
3934 .rs1 = .sp,
3935 .imm12 = math.cast(i12, off) orelse {
3936 return self.fail("TODO: bigger stack sizes", .{});
3937 },
3938 } },
3939 });
3940 },
39414646 .unreach, .none => return, // Nothing to do.
39424647 .undef => {
39434648 if (!self.wantSafety())
......@@ -3950,10 +4655,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
39504655 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
39514656 _ = try self.addInst(.{
39524657 .tag = .addi,
4658 .ops = .rri,
39534659 .data = .{ .i_type = .{
39544660 .rd = reg,
39554661 .rs1 = .zero,
3956 .imm12 = @intCast(x),
4662 .imm12 = Immediate.s(@intCast(x)),
39574663 } },
39584664 });
39594665 } else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
......@@ -3963,17 +4669,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
39634669
39644670 _ = try self.addInst(.{
39654671 .tag = .lui,
4672 .ops = .ri,
39664673 .data = .{ .u_type = .{
39674674 .rd = reg,
3968 .imm20 = hi20,
4675 .imm20 = Immediate.s(hi20),
39694676 } },
39704677 });
39714678 _ = try self.addInst(.{
39724679 .tag = .addi,
4680 .ops = .rri,
39734681 .data = .{ .i_type = .{
39744682 .rd = reg,
39754683 .rs1 = reg,
3976 .imm12 = lo12,
4684 .imm12 = Immediate.s(lo12),
39774685 } },
39784686 });
39794687 } else {
......@@ -3992,15 +4700,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
39924700
39934701 _ = try self.addInst(.{
39944702 .tag = .slli,
4703 .ops = .rri,
39954704 .data = .{ .i_type = .{
3996 .imm12 = 32,
39974705 .rd = reg,
39984706 .rs1 = reg,
4707 .imm12 = Immediate.s(32),
39994708 } },
40004709 });
40014710
40024711 _ = try self.addInst(.{
40034712 .tag = .add,
4713 .ops = .rrr,
40044714 .data = .{ .r_type = .{
40054715 .rd = reg,
40064716 .rs1 = reg,
......@@ -4016,7 +4726,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
40164726
40174727 // mov reg, src_reg
40184728 _ = try self.addInst(.{
4019 .tag = .mv,
4729 .tag = .pseudo,
4730 .ops = .pseudo_mv,
40204731 .data = .{ .rr = .{
40214732 .rd = reg,
40224733 .rs = src_reg,
......@@ -4029,21 +4740,46 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
40294740
40304741 _ = try self.addInst(.{
40314742 .tag = .ld,
4743 .ops = .rri,
40324744 .data = .{ .i_type = .{
40334745 .rd = reg,
40344746 .rs1 = reg,
4035 .imm12 = 0,
4747 .imm12 = Immediate.s(0),
40364748 } },
40374749 });
40384750 },
4039 .stack_offset => |off| {
4751 .load_frame => |frame| {
40404752 _ = try self.addInst(.{
4041 .tag = load_tag,
4042 .data = .{ .i_type = .{
4043 .rd = reg,
4044 .rs1 = .sp,
4045 .imm12 = math.cast(i12, off) orelse {
4046 return self.fail("TODO: genSetReg support larger stack sizes", .{});
4753 .tag = .pseudo,
4754 .ops = .pseudo_load_rm,
4755 .data = .{ .rm = .{
4756 .r = reg,
4757 .m = .{
4758 .base = .{ .frame = frame.index },
4759 .mod = .{
4760 .rm = .{
4761 .size = self.memSize(ty),
4762 .disp = frame.off,
4763 },
4764 },
4765 },
4766 } },
4767 });
4768 },
4769 .lea_frame => |frame| {
4770 _ = try self.addInst(.{
4771 .tag = .pseudo,
4772 .ops = .pseudo_lea_rm,
4773 .data = .{ .rm = .{
4774 .r = reg,
4775 .m = .{
4776 .base = .{ .frame = frame.index },
4777 .mod = .{
4778 .rm = .{
4779 .size = self.memSize(ty),
4780 .disp = frame.off,
4781 },
4782 },
40474783 },
40484784 } },
40494785 });
......@@ -4052,35 +4788,41 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
40524788 try self.genSetReg(ty, reg, src_mcv.address());
40534789 try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = reg } });
40544790 },
4055 .air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)),
40564791 .indirect => |reg_off| {
4792 const load_tag: Mir.Inst.Tag = switch (abi_size) {
4793 1 => .lb,
4794 2 => .lh,
4795 4 => .lw,
4796 8 => .ld,
4797 else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}),
4798 };
4799
40574800 _ = try self.addInst(.{
40584801 .tag = load_tag,
4059 .data = .{
4060 .i_type = .{
4061 .rd = reg,
4062 .rs1 = reg_off.reg,
4063 .imm12 = @intCast(reg_off.off),
4064 },
4065 },
4802 .ops = .rri,
4803 .data = .{ .i_type = .{
4804 .rd = reg,
4805 .rs1 = reg_off.reg,
4806 .imm12 = Immediate.s(reg_off.off),
4807 } },
40664808 });
40674809 },
4068 .addr_symbol => |sym_off| {
4810 .lea_symbol => |sym_off| {
40694811 assert(sym_off.off == 0);
40704812
40714813 const atom_index = try self.symbolIndex();
40724814
40734815 _ = try self.addInst(.{
4074 .tag = .load_symbol,
4075 .data = .{
4076 .payload = try self.addExtra(Mir.LoadSymbolPayload{
4077 .register = reg.id(),
4078 .atom_index = atom_index,
4079 .sym_index = sym_off.sym,
4080 }),
4081 },
4816 .tag = .pseudo,
4817 .ops = .pseudo_load_symbol,
4818 .data = .{ .payload = try self.addExtra(Mir.LoadSymbolPayload{
4819 .register = reg.id(),
4820 .atom_index = atom_index,
4821 .sym_index = sym_off.sym,
4822 }) },
40824823 });
40834824 },
4825 .air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)),
40844826 else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}),
40854827 }
40864828}
......@@ -4100,27 +4842,44 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
41004842}
41014843
41024844fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
4845 const zcu = self.bin_file.comp.module.?;
4846
41034847 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4104 const result = if (self.liveness.isUnused(inst)) .dead else result: {
4105 const operand = try self.resolveInst(ty_op.operand);
4106 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
4848 const result = if (self.liveness.isUnused(inst)) .unreach else result: {
4849 const src_mcv = try self.resolveInst(ty_op.operand);
41074850
4108 const operand_lock = switch (operand) {
4109 .register => |reg| self.register_manager.lockReg(reg),
4110 else => null,
4851 const dst_ty = self.typeOfIndex(inst);
4852 const src_ty = self.typeOf(ty_op.operand);
4853
4854 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
4855 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
4856
4857 const dst_mcv = if (dst_ty.abiSize(zcu) <= src_ty.abiSize(zcu) and
4858 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
4859 const dst_mcv = try self.allocRegOrMem(inst, true);
4860 try self.genCopy(switch (math.order(dst_ty.abiSize(zcu), src_ty.abiSize(zcu))) {
4861 .lt => dst_ty,
4862 .eq => if (!dst_mcv.isMemory() or src_mcv.isMemory()) dst_ty else src_ty,
4863 .gt => src_ty,
4864 }, dst_mcv, src_mcv);
4865 break :dst dst_mcv;
41114866 };
4112 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
41134867
4114 const dest = try self.allocRegOrMem(inst, true);
4115 try self.genCopy(self.typeOfIndex(inst), dest, operand);
4116 break :result dest;
4868 if (dst_ty.isAbiInt(zcu) and src_ty.isAbiInt(zcu) and
4869 dst_ty.intInfo(zcu).signedness == src_ty.intInfo(zcu).signedness) break :result dst_mcv;
4870
4871 const abi_size = dst_ty.abiSize(zcu);
4872 const bit_size = dst_ty.bitSize(zcu);
4873 if (abi_size * 8 <= bit_size) break :result dst_mcv;
4874
4875 return self.fail("TODO: airBitCast {} to {}", .{ src_ty.fmt(zcu), dst_ty.fmt(zcu) });
41174876 };
41184877 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
41194878}
41204879
41214880fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
41224881 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4123 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{
4882 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airArrayToSlice for {}", .{
41244883 self.target.cpu.arch,
41254884 });
41264885 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -4128,7 +4887,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
41284887
41294888fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
41304889 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4131 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
4890 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airFloatFromInt for {}", .{
41324891 self.target.cpu.arch,
41334892 });
41344893 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -4136,7 +4895,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
41364895
41374896fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
41384897 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4139 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
4898 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airIntFromFloat for {}", .{
41404899 self.target.cpu.arch,
41414900 });
41424901 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -4186,7 +4945,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
41864945fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
41874946 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
41884947 const operand = try self.resolveInst(un_op);
4189 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
4948 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else {
41904949 _ = operand;
41914950 return self.fail("TODO implement airTagName for riscv64", .{});
41924951 };
......@@ -4194,7 +4953,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
41944953}
41954954
41964955fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
4197 const mod = self.bin_file.comp.module.?;
4956 const zcu = self.bin_file.comp.module.?;
41984957 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
41994958
42004959 const err_ty = self.typeOf(un_op);
......@@ -4207,7 +4966,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
42074966 const addr_reg, const addr_lock = try self.allocReg();
42084967 defer self.register_manager.unlockReg(addr_lock);
42094968
4210 const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod);
4969 const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, zcu);
42114970 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
42124971 const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
42134972 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
......@@ -4223,69 +4982,45 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
42234982 const end_reg, const end_lock = try self.allocReg();
42244983 defer self.register_manager.unlockReg(end_lock);
42254984
4226 _ = try self.addInst(.{
4227 .tag = .slli,
4228 .data = .{
4229 .i_type = .{
4230 .rd = err_reg,
4231 .rs1 = err_reg,
4232 .imm12 = 4,
4233 },
4234 },
4235 });
4236
4237 try self.binOpMir(
4238 .add,
4239 null,
4240 Type.usize,
4241 .{ .register = err_reg },
4242 .{ .register = addr_reg },
4243 );
4244
4245 try self.genSetReg(Type.usize, start_reg, .{ .indirect = .{ .reg = err_reg } });
4246 try self.genSetReg(Type.usize, end_reg, .{ .indirect = .{ .reg = err_reg, .off = 8 } });
4985 _ = start_reg;
4986 _ = end_reg;
42474987
4248 const dst_mcv = try self.allocRegOrMem(inst, false);
4249
4250 try self.genSetStack(Type.usize, dst_mcv.stack_offset, .{ .register = start_reg });
4251 try self.genSetStack(Type.usize, dst_mcv.stack_offset + 8, .{ .register = end_reg });
4252
4253 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
4988 return self.fail("TODO: airErrorName", .{});
42544989}
42554990
42564991fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
42574992 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4258 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for riscv64", .{});
4993 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airSplat for riscv64", .{});
42594994 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
42604995}
42614996
42624997fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
42634998 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
42644999 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
4265 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for riscv64", .{});
5000 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airSelect for riscv64", .{});
42665001 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
42675002}
42685003
42695004fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
42705005 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4271 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{});
5006 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airShuffle for riscv64", .{});
42725007 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
42735008}
42745009
42755010fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
42765011 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
4277 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for riscv64", .{});
5012 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement airReduce for riscv64", .{});
42785013 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
42795014}
42805015
42815016fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
4282 const mod = self.bin_file.comp.module.?;
5017 const zcu = self.bin_file.comp.module.?;
42835018 const vector_ty = self.typeOfIndex(inst);
4284 const len = vector_ty.vectorLen(mod);
5019 const len = vector_ty.vectorLen(zcu);
42855020 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
42865021 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]);
42875022 const result: MCValue = res: {
4288 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
5023 if (self.liveness.isUnused(inst)) break :res .unreach;
42895024 return self.fail("TODO implement airAggregateInit for riscv64", .{});
42905025 };
42915026
......@@ -4294,11 +5029,9 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
42945029 @memcpy(buf[0..elements.len], elements);
42955030 return self.finishAir(inst, result, buf);
42965031 }
4297 var bt = try self.iterateBigTomb(inst, elements.len);
4298 for (elements) |elem| {
4299 bt.feed(elem);
4300 }
4301 return bt.finishAir(result);
5032 var bt = self.liveness.iterateBigTomb(inst);
5033 for (elements) |elem| try self.feed(&bt, elem);
5034 return self.finishAirResult(inst, result);
43025035}
43035036
43045037fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
......@@ -4313,49 +5046,55 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
43135046 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
43145047 // TODO: RISC-V does have prefetch instruction variants.
43155048 // see here: https://raw.githubusercontent.com/riscv/riscv-CMOs/master/specifications/cmobase-v1.0.1.pdf
4316 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
5049 return self.finishAir(inst, .unreach, .{ prefetch.ptr, .none, .none });
43175050}
43185051
43195052fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
43205053 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
43215054 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
4322 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
5055 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else {
43235056 return self.fail("TODO implement airMulAdd for riscv64", .{});
43245057 };
43255058 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
43265059}
43275060
4328fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4329 const mod = self.bin_file.comp.module.?;
5061fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
5062 const zcu = self.bin_file.comp.module.?;
43305063
43315064 // If the type has no codegen bits, no need to store it.
4332 const inst_ty = self.typeOf(inst);
4333 if (!inst_ty.hasRuntimeBits(mod))
4334 return MCValue{ .none = {} };
4335
4336 const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, mod)).?);
4337 return self.getResolvedInstValue(inst_index);
4338}
4339
4340fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
4341 // Treat each stack item as a "layer" on top of the previous one.
4342 var i: usize = self.branch_stack.items.len;
4343 while (true) {
4344 i -= 1;
4345 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
4346 assert(mcv != .dead);
4347 return mcv;
4348 }
4349 }
5065 const inst_ty = self.typeOf(ref);
5066 if (!inst_ty.hasRuntimeBits(zcu))
5067 return .none;
5068
5069 const mcv = if (ref.toIndex()) |inst| mcv: {
5070 break :mcv self.inst_tracking.getPtr(inst).?.short;
5071 } else mcv: {
5072 const ip_index = ref.toInterned().?;
5073 const gop = try self.const_tracking.getOrPut(self.gpa, ip_index);
5074 if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(
5075 try self.genTypedValue(Value.fromInterned(ip_index)),
5076 );
5077 break :mcv gop.value_ptr.short;
5078 };
5079
5080 return mcv;
5081}
5082
5083fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) *InstTracking {
5084 const tracking = self.inst_tracking.getPtr(inst).?;
5085 return switch (tracking.short) {
5086 .none, .unreach, .dead => unreachable,
5087 else => tracking,
5088 };
43505089}
43515090
43525091fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
4353 const mod = self.bin_file.comp.module.?;
5092 const zcu = self.bin_file.comp.module.?;
43545093 const result = try codegen.genTypedValue(
43555094 self.bin_file,
43565095 self.src_loc,
43575096 val,
4358 mod.funcOwnerDeclIndex(self.func_index),
5097 zcu.funcOwnerDeclIndex(self.func_index),
43595098 );
43605099 const mcv: MCValue = switch (result) {
43615100 .mcv => |mcv| switch (mcv) {
......@@ -4378,8 +5117,8 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
43785117
43795118const CallMCValues = struct {
43805119 args: []MCValue,
4381 return_value: MCValue,
4382 stack_byte_count: u32,
5120 return_value: InstTracking,
5121 stack_byte_count: u31,
43835122 stack_align: Alignment,
43845123
43855124 fn deinit(self: *CallMCValues, func: *Self) void {
......@@ -4389,86 +5128,115 @@ const CallMCValues = struct {
43895128};
43905129
43915130/// Caller must call `CallMCValues.deinit`.
4392fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: CallView) !CallMCValues {
4393 const mod = self.bin_file.comp.module.?;
4394 const ip = &mod.intern_pool;
5131fn resolveCallingConventionValues(
5132 self: *Self,
5133 fn_info: InternPool.Key.FuncType,
5134) !CallMCValues {
5135 const zcu = self.bin_file.comp.module.?;
5136 const ip = &zcu.intern_pool;
5137
5138 const param_types = try self.gpa.alloc(Type, fn_info.param_types.len);
5139 defer self.gpa.free(param_types);
43955140
4396 _ = role;
5141 for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*dest, src| {
5142 dest.* = Type.fromInterned(src);
5143 }
43975144
4398 const fn_info = mod.typeToFunc(fn_ty).?;
43995145 const cc = fn_info.cc;
44005146 var result: CallMCValues = .{
4401 .args = try self.gpa.alloc(MCValue, fn_info.param_types.len),
5147 .args = try self.gpa.alloc(MCValue, param_types.len),
44025148 // These undefined values must be populated before returning from this function.
44035149 .return_value = undefined,
4404 .stack_byte_count = undefined,
5150 .stack_byte_count = 0,
44055151 .stack_align = undefined,
44065152 };
44075153 errdefer self.gpa.free(result.args);
44085154
4409 const ret_ty = fn_ty.fnReturnType(mod);
5155 const ret_ty = Type.fromInterned(fn_info.return_type);
44105156
44115157 switch (cc) {
44125158 .Naked => {
44135159 assert(result.args.len == 0);
4414 result.return_value = .{ .unreach = {} };
4415 result.stack_byte_count = 0;
4416 result.stack_align = .@"1";
4417 return result;
5160 result.return_value = InstTracking.init(.unreach);
5161 result.stack_align = .@"8";
44185162 },
4419 .Unspecified, .C => {
5163 .C, .Unspecified => {
44205164 if (result.args.len > 8) {
4421 return self.fail("TODO: support more than 8 function args", .{});
5165 return self.fail("RISC-V calling convention does not support more than 8 arguments", .{});
44225166 }
44235167
4424 var fa_reg_i: u32 = 0;
5168 var ret_int_reg_i: u32 = 0;
5169 var param_int_reg_i: u32 = 0;
44255170
4426 // spill the needed argument registers
4427 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
4428 const param_ty = Type.fromInterned(ty);
4429 const param_size = param_ty.abiSize(mod);
5171 result.stack_align = .@"16";
44305172
4431 switch (param_size) {
4432 1...8 => {
4433 const arg_reg: Register = abi.function_arg_regs[fa_reg_i];
4434 fa_reg_i += 1;
4435 try self.register_manager.getReg(arg_reg, null);
4436 result_arg.* = .{ .register = arg_reg };
5173 // Return values
5174 if (ret_ty.zigTypeTag(zcu) == .NoReturn) {
5175 result.return_value = InstTracking.init(.unreach);
5176 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
5177 result.return_value = InstTracking.init(.none);
5178 } else {
5179 var ret_tracking: [2]InstTracking = undefined;
5180 var ret_tracking_i: usize = 0;
5181
5182 const classes = mem.sliceTo(&abi.classifySystem(ret_ty, zcu), .none);
5183
5184 for (classes) |class| switch (class) {
5185 .integer => {
5186 const ret_int_reg = abi.function_arg_regs[ret_int_reg_i];
5187 ret_int_reg_i += 1;
5188
5189 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg });
5190 ret_tracking_i += 1;
44375191 },
4438 9...16 => {
4439 const arg_regs: [2]Register = abi.function_arg_regs[fa_reg_i..][0..2].*;
4440 fa_reg_i += 2;
4441 for (arg_regs) |reg| try self.register_manager.getReg(reg, null);
4442 result_arg.* = .{ .register_pair = arg_regs };
5192 else => return self.fail("TODO: C calling convention return class {}", .{class}),
5193 };
5194
5195 result.return_value = switch (ret_tracking_i) {
5196 else => return self.fail("ty {} took {} tracking return indices", .{ ret_ty.fmt(zcu), ret_tracking_i }),
5197 1 => ret_tracking[0],
5198 2 => InstTracking.init(.{ .register_pair = .{
5199 ret_tracking[0].short.register, ret_tracking[1].short.register,
5200 } }),
5201 };
5202 }
5203
5204 for (param_types, result.args) |ty, *arg| {
5205 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
5206
5207 var arg_mcv: [2]MCValue = undefined;
5208 var arg_mcv_i: usize = 0;
5209
5210 const classes = mem.sliceTo(&abi.classifySystem(ty, zcu), .none);
5211
5212 for (classes) |class| switch (class) {
5213 .integer => {
5214 const param_int_regs = abi.function_arg_regs;
5215 if (param_int_reg_i >= param_int_regs.len) break;
5216
5217 const param_int_reg = param_int_regs[param_int_reg_i];
5218 param_int_reg_i += 1;
5219
5220 arg_mcv[arg_mcv_i] = .{ .register = param_int_reg };
5221 arg_mcv_i += 1;
44435222 },
4444 else => return self.fail("TODO: support args of size {}", .{param_size}),
5223 else => return self.fail("TODO: C calling convention arg class {}", .{class}),
5224 } else {
5225 arg.* = switch (arg_mcv_i) {
5226 else => return self.fail("ty {} took {} tracking arg indices", .{ ty.fmt(zcu), arg_mcv_i }),
5227 1 => arg_mcv[0],
5228 2 => .{ .register_pair = .{ arg_mcv[0].register, arg_mcv[1].register } },
5229 };
5230 continue;
44455231 }
4446 }
44475232
4448 result.stack_byte_count = self.max_end_stack;
4449 result.stack_align = .@"16";
5233 return self.fail("TODO: pass args by stack", .{});
5234 }
44505235 },
44515236 else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}),
44525237 }
44535238
4454 if (ret_ty.zigTypeTag(mod) == .NoReturn) {
4455 result.return_value = .{ .unreach = {} };
4456 } else if (!ret_ty.hasRuntimeBits(mod)) {
4457 result.return_value = .{ .none = {} };
4458 } else switch (cc) {
4459 .Naked => unreachable,
4460 .Unspecified, .C => {
4461 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(mod));
4462 if (ret_ty_size <= 8) {
4463 result.return_value = .{ .register = .a0 };
4464 } else if (ret_ty_size <= 16) {
4465 return self.fail("TODO support returning with a0 + a1", .{});
4466 } else {
4467 return self.fail("TODO support return by reference", .{});
4468 }
4469 },
4470 else => return self.fail("TODO implement function return values for {}", .{cc}),
4471 }
5239 result.stack_byte_count = @intCast(result.stack_align.forward(result.stack_byte_count));
44725240 return result;
44735241}
44745242
......@@ -4504,36 +5272,36 @@ fn parseRegName(name: []const u8) ?Register {
45045272}
45055273
45065274fn typeOf(self: *Self, inst: Air.Inst.Ref) Type {
4507 const mod = self.bin_file.comp.module.?;
4508 return self.air.typeOf(inst, &mod.intern_pool);
5275 const zcu = self.bin_file.comp.module.?;
5276 return self.air.typeOf(inst, &zcu.intern_pool);
45095277}
45105278
45115279fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {
4512 const mod = self.bin_file.comp.module.?;
4513 return self.air.typeOfIndex(inst, &mod.intern_pool);
5280 const zcu = self.bin_file.comp.module.?;
5281 return self.air.typeOfIndex(inst, &zcu.intern_pool);
45145282}
45155283
45165284fn hasFeature(self: *Self, feature: Target.riscv.Feature) bool {
45175285 return Target.riscv.featureSetHas(self.target.cpu.features, feature);
45185286}
45195287
4520pub fn errUnionPayloadOffset(payload_ty: Type, mod: *Module) u64 {
4521 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return 0;
4522 const payload_align = payload_ty.abiAlignment(mod);
4523 const error_align = Type.anyerror.abiAlignment(mod);
4524 if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
5288pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Module) u64 {
5289 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return 0;
5290 const payload_align = payload_ty.abiAlignment(zcu);
5291 const error_align = Type.anyerror.abiAlignment(zcu);
5292 if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
45255293 return 0;
45265294 } else {
4527 return payload_align.forward(Type.anyerror.abiSize(mod));
5295 return payload_align.forward(Type.anyerror.abiSize(zcu));
45285296 }
45295297}
45305298
4531pub fn errUnionErrorOffset(payload_ty: Type, mod: *Module) u64 {
4532 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return 0;
4533 const payload_align = payload_ty.abiAlignment(mod);
4534 const error_align = Type.anyerror.abiAlignment(mod);
4535 if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4536 return error_align.forward(payload_ty.abiSize(mod));
5299pub fn errUnionErrorOffset(payload_ty: Type, zcu: *Module) u64 {
5300 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return 0;
5301 const payload_align = payload_ty.abiAlignment(zcu);
5302 const error_align = Type.anyerror.abiAlignment(zcu);
5303 if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
5304 return error_align.forward(payload_ty.abiSize(zcu));
45375305 } else {
45385306 return 0;
45395307 }
src/arch/riscv64/Emit.zig+118-575
......@@ -1,620 +1,163 @@
1//! This file contains the functionality for lowering RISCV64 MIR into
2//! machine code
1//! This file contains the functionality for emitting RISC-V MIR as machine code
32
4mir: Mir,
5bin_file: *link.File,
3lower: Lower,
64debug_output: DebugInfoOutput,
7output_mode: std.builtin.OutputMode,
8link_mode: std.builtin.LinkMode,
9target: *const std.Target,
10err_msg: ?*ErrorMsg = null,
11src_loc: Module.SrcLoc,
125code: *std.ArrayList(u8),
136
14/// List of registers to save in the prologue.
15save_reg_list: Mir.RegisterList,
16
177prev_di_line: u32,
188prev_di_column: u32,
199/// Relative to the beginning of `code`.
2010prev_di_pc: usize,
2111
22/// Function's stack size. Used for backpatching.
23stack_size: u32,
24
25/// For backward branches: stores the code offset of the target
26/// instruction
27///
28/// For forward branches: stores the code offset of the branch
29/// instruction
3012code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
13relocs: std.ArrayListUnmanaged(Reloc) = .{},
3114
32const log = std.log.scoped(.emit);
33
34const InnerError = error{
35 OutOfMemory,
15pub const Error = Lower.Error || error{
3616 EmitFail,
3717};
3818
39pub fn emitMir(
40 emit: *Emit,
41) InnerError!void {
42 const mir_tags = emit.mir.instructions.items(.tag);
43
44 try emit.lowerMir();
45
46 for (mir_tags, 0..) |tag, index| {
47 const inst = @as(u32, @intCast(index));
48 log.debug("emitMir: {s}", .{@tagName(tag)});
49 switch (tag) {
50 .add => try emit.mirRType(inst),
51 .sub => try emit.mirRType(inst),
52 .mul => try emit.mirRType(inst),
53 .@"or" => try emit.mirRType(inst),
54
55 .cmp_eq => try emit.mirRType(inst),
56 .cmp_neq => try emit.mirRType(inst),
57 .cmp_gt => try emit.mirRType(inst),
58 .cmp_gte => try emit.mirRType(inst),
59 .cmp_lt => try emit.mirRType(inst),
60 .cmp_imm_gte => try emit.mirRType(inst),
61 .cmp_imm_eq => try emit.mirIType(inst),
62 .cmp_imm_neq => try emit.mirIType(inst),
63 .cmp_imm_lte => try emit.mirIType(inst),
64 .cmp_imm_lt => try emit.mirIType(inst),
65
66 .beq => try emit.mirBType(inst),
67 .bne => try emit.mirBType(inst),
68
69 .addi => try emit.mirIType(inst),
70 .addiw => try emit.mirIType(inst),
71 .andi => try emit.mirIType(inst),
72 .jalr => try emit.mirIType(inst),
73 .abs => try emit.mirIType(inst),
74
75 .jal => try emit.mirJType(inst),
76
77 .ebreak => try emit.mirSystem(inst),
78 .ecall => try emit.mirSystem(inst),
79 .unimp => try emit.mirSystem(inst),
80
81 .dbg_line => try emit.mirDbgLine(inst),
82 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
83 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
84
85 .psuedo_prologue => try emit.mirPsuedo(inst),
86 .psuedo_epilogue => try emit.mirPsuedo(inst),
87
88 .j => try emit.mirPsuedo(inst),
89
90 .mv => try emit.mirRR(inst),
91 .not => try emit.mirRR(inst),
92
93 .nop => try emit.mirNop(inst),
94 .ret => try emit.mirNop(inst),
95
96 .lui => try emit.mirUType(inst),
97
98 .ld => try emit.mirIType(inst),
99 .lw => try emit.mirIType(inst),
100 .lh => try emit.mirIType(inst),
101 .lb => try emit.mirIType(inst),
102
103 .sd => try emit.mirIType(inst),
104 .sw => try emit.mirIType(inst),
105 .sh => try emit.mirIType(inst),
106 .sb => try emit.mirIType(inst),
107
108 .srlw => try emit.mirRType(inst),
109 .sllw => try emit.mirRType(inst),
110
111 .srli => try emit.mirIType(inst),
112 .slli => try emit.mirIType(inst),
113
114 .ldr_ptr_stack => try emit.mirIType(inst),
115
116 .load_symbol => try emit.mirLoadSymbol(inst),
19pub fn emitMir(emit: *Emit) Error!void {
20 log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len});
21 for (0..emit.lower.mir.instructions.len) |mir_i| {
22 const mir_index: Mir.Inst.Index = @intCast(mir_i);
23 try emit.code_offset_mapping.putNoClobber(
24 emit.lower.allocator,
25 mir_index,
26 @intCast(emit.code.items.len),
27 );
28 const lowered = try emit.lower.lowerMir(mir_index);
29 var lowered_relocs = lowered.relocs;
30 for (lowered.insts, 0..) |lowered_inst, lowered_index| {
31 const start_offset: u32 = @intCast(emit.code.items.len);
32 try lowered_inst.encode(emit.code.writer());
33
34 while (lowered_relocs.len > 0 and
35 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
36 lowered_relocs = lowered_relocs[1..];
37 }) switch (lowered_relocs[0].target) {
38 .inst => |target| try emit.relocs.append(emit.lower.allocator, .{
39 .source = start_offset,
40 .target = target,
41 .offset = 0,
42 .enc = std.meta.activeTag(lowered_inst.encoding.data),
43 }),
44 else => |x| return emit.fail("TODO: emitMir {s}", .{@tagName(x)}),
45 };
46 }
47 std.debug.assert(lowered_relocs.len == 0);
48
49 if (lowered.insts.len == 0) {
50 const mir_inst = emit.lower.mir.instructions.get(mir_index);
51 switch (mir_inst.tag) {
52 else => unreachable,
53 .pseudo => switch (mir_inst.ops) {
54 else => unreachable,
55 .pseudo_dbg_prologue_end => {
56 switch (emit.debug_output) {
57 .dwarf => |dw| {
58 try dw.setPrologueEnd();
59 log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
60 emit.prev_di_line, emit.prev_di_column,
61 });
62 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
63 },
64 .plan9 => {},
65 .none => {},
66 }
67 },
68 .pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine(
69 mir_inst.data.pseudo_dbg_line_column.line,
70 mir_inst.data.pseudo_dbg_line_column.column,
71 ),
72 .pseudo_dbg_epilogue_begin => {
73 switch (emit.debug_output) {
74 .dwarf => |dw| {
75 try dw.setEpilogueBegin();
76 log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
77 emit.prev_di_line, emit.prev_di_column,
78 });
79 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
80 },
81 .plan9 => {},
82 .none => {},
83 }
84 },
85 .pseudo_dead => {},
86 },
87 }
11788 }
11889 }
90 try emit.fixupRelocs();
11991}
12092
12193pub fn deinit(emit: *Emit) void {
122 const comp = emit.bin_file.comp;
123 const gpa = comp.gpa;
124
125 emit.code_offset_mapping.deinit(gpa);
94 emit.relocs.deinit(emit.lower.allocator);
95 emit.code_offset_mapping.deinit(emit.lower.allocator);
12696 emit.* = undefined;
12797}
12898
129fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
130 const endian = emit.target.cpu.arch.endian();
131 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
132}
99const Reloc = struct {
100 /// Offset of the instruction.
101 source: usize,
102 /// Target of the relocation.
103 target: Mir.Inst.Index,
104 /// Offset of the relocation within the instruction.
105 offset: u32,
106 /// Encoding of the instruction, used to determine how to modify it.
107 enc: Encoding.InstEnc,
108};
109
110fn fixupRelocs(emit: *Emit) Error!void {
111 for (emit.relocs.items) |reloc| {
112 log.debug("target inst: {}", .{emit.lower.mir.instructions.get(reloc.target)});
113 const target = emit.code_offset_mapping.get(reloc.target) orelse
114 return emit.fail("relocation target not found!", .{});
133115
134fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
135 @setCold(true);
136 assert(emit.err_msg == null);
137 const comp = emit.bin_file.comp;
138 const gpa = comp.gpa;
139 emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args);
140 return error.EmitFail;
116 const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
117 const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4];
118
119 log.debug("disp: {x}", .{disp});
120
121 switch (reloc.enc) {
122 .J => riscv_util.writeInstJ(code, @bitCast(disp)),
123 else => return emit.fail("tried to reloc encoding type {s}", .{@tagName(reloc.enc)}),
124 }
125 }
141126}
142127
143fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
144 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
128fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
129 const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
145130 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
131 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
146132 switch (emit.debug_output) {
147133 .dwarf => |dw| {
148134 if (column != emit.prev_di_column) try dw.setColumn(column);
149 if (delta_line == 0) return; // TODO: remove this
135 if (delta_line == 0) return; // TODO: fix these edge cases.
150136 try dw.advancePCAndLine(delta_line, delta_pc);
151137 emit.prev_di_line = line;
152138 emit.prev_di_column = column;
153139 emit.prev_di_pc = emit.code.items.len;
154140 },
155 .plan9 => |dbg_out| {
156 if (delta_pc <= 0) return; // only do this when the pc changes
157
158 // increasing the line number
159 try link.File.Plan9.changeLine(&dbg_out.dbg_line, delta_line);
160 // increasing the pc
161 const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
162 if (d_pc_p9 > 0) {
163 // minus one because if its the last one, we want to leave space to change the line which is one pc quanta
164 try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta);
165 if (dbg_out.pcop_change_index) |pci|
166 dbg_out.dbg_line.items[pci] += 1;
167 dbg_out.pcop_change_index = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
168 } else if (d_pc_p9 == 0) {
169 // we don't need to do anything, because adding the pc quanta does it for us
170 } else unreachable;
171 if (dbg_out.start_line == null)
172 dbg_out.start_line = emit.prev_di_line;
173 dbg_out.end_line = line;
174 // only do this if the pc changed
175 emit.prev_di_line = line;
176 emit.prev_di_column = column;
177 emit.prev_di_pc = emit.code.items.len;
178 },
179 .none => {},
180 }
181}
182
183fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
184 const tag = emit.mir.instructions.items(.tag)[inst];
185 const r_type = emit.mir.instructions.items(.data)[inst].r_type;
186
187 const rd = r_type.rd;
188 const rs1 = r_type.rs1;
189 const rs2 = r_type.rs2;
190
191 switch (tag) {
192 .add => try emit.writeInstruction(Instruction.add(rd, rs1, rs2)),
193 .sub => try emit.writeInstruction(Instruction.sub(rd, rs1, rs2)),
194 .mul => try emit.writeInstruction(Instruction.mul(rd, rs1, rs2)),
195 .cmp_gt => {
196 // rs1 > rs2
197 try emit.writeInstruction(Instruction.sltu(rd, rs2, rs1));
198 },
199 .cmp_gte => {
200 // rs1 >= rs2
201 try emit.writeInstruction(Instruction.sltu(rd, rs1, rs2));
202 try emit.writeInstruction(Instruction.xori(rd, rd, 1));
203 },
204 .cmp_eq => {
205 // rs1 == rs2
206
207 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
208 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1)); // seqz
209 },
210 .cmp_neq => {
211 // rs1 != rs2
212
213 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
214 try emit.writeInstruction(Instruction.sltu(rd, .zero, rd)); // snez
215 },
216 .cmp_lt => {
217 // rd = 1 if rs1 < rs2
218 try emit.writeInstruction(Instruction.slt(rd, rs1, rs2));
219 },
220 .sllw => try emit.writeInstruction(Instruction.sllw(rd, rs1, rs2)),
221 .srlw => try emit.writeInstruction(Instruction.srlw(rd, rs1, rs2)),
222 .@"or" => try emit.writeInstruction(Instruction.@"or"(rd, rs1, rs2)),
223 .cmp_imm_gte => {
224 // rd = 1 if rs1 >= imm12
225 // see the docstring of cmp_imm_gte to see why we use r_type here
226
227 // (rs1 >= imm12) == !(imm12 > rs1)
228 try emit.writeInstruction(Instruction.sltu(rd, rs1, rs2));
229 },
230 else => unreachable,
231 }
232}
233
234fn mirBType(emit: *Emit, inst: Mir.Inst.Index) !void {
235 const tag = emit.mir.instructions.items(.tag)[inst];
236 const b_type = emit.mir.instructions.items(.data)[inst].b_type;
237
238 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(b_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
239
240 switch (tag) {
241 .beq => {
242 log.debug("beq: {} offset={}", .{ inst, offset });
243 try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, @intCast(offset)));
244 },
245 .bne => {
246 log.debug("bne: {} offset={}", .{ inst, offset });
247 try emit.writeInstruction(Instruction.bne(b_type.rs1, b_type.rs2, @intCast(offset)));
248 },
249 else => unreachable,
250 }
251}
252
253fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
254 const tag = emit.mir.instructions.items(.tag)[inst];
255 const i_type = emit.mir.instructions.items(.data)[inst].i_type;
256
257 const rd = i_type.rd;
258 const rs1 = i_type.rs1;
259 const imm12 = i_type.imm12;
260
261 switch (tag) {
262 .addi => try emit.writeInstruction(Instruction.addi(rd, rs1, imm12)),
263 .addiw => try emit.writeInstruction(Instruction.addiw(rd, rs1, imm12)),
264 .jalr => try emit.writeInstruction(Instruction.jalr(rd, imm12, rs1)),
265
266 .andi => try emit.writeInstruction(Instruction.andi(rd, rs1, imm12)),
267
268 .ld => try emit.writeInstruction(Instruction.ld(rd, imm12, rs1)),
269 .lw => try emit.writeInstruction(Instruction.lw(rd, imm12, rs1)),
270 .lh => try emit.writeInstruction(Instruction.lh(rd, imm12, rs1)),
271 .lb => try emit.writeInstruction(Instruction.lb(rd, imm12, rs1)),
272
273 .sd => try emit.writeInstruction(Instruction.sd(rd, imm12, rs1)),
274 .sw => try emit.writeInstruction(Instruction.sw(rd, imm12, rs1)),
275 .sh => try emit.writeInstruction(Instruction.sh(rd, imm12, rs1)),
276 .sb => try emit.writeInstruction(Instruction.sb(rd, imm12, rs1)),
277
278 .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(rd, rs1, .sp)),
279
280 .abs => {
281 try emit.writeInstruction(Instruction.sraiw(rd, rs1, @intCast(imm12)));
282 try emit.writeInstruction(Instruction.xor(rs1, rs1, rd));
283 try emit.writeInstruction(Instruction.subw(rs1, rs1, rd));
284 },
285
286 .srli => try emit.writeInstruction(Instruction.srli(rd, rs1, @intCast(imm12))),
287 .slli => try emit.writeInstruction(Instruction.slli(rd, rs1, @intCast(imm12))),
288
289 .cmp_imm_eq => {
290 try emit.writeInstruction(Instruction.xori(rd, rs1, imm12));
291 try emit.writeInstruction(Instruction.sltiu(rd, rd, 1));
292 },
293 .cmp_imm_neq => {
294 try emit.writeInstruction(Instruction.xori(rd, rs1, imm12));
295 try emit.writeInstruction(Instruction.sltu(rd, .x0, rd));
296 },
297
298 .cmp_imm_lt => {
299 try emit.writeInstruction(Instruction.slti(rd, rs1, imm12));
300 },
301
302 .cmp_imm_lte => {
303 try emit.writeInstruction(Instruction.sltiu(rd, rs1, @bitCast(imm12)));
304 },
305
306 else => unreachable,
307 }
308}
309
310fn mirJType(emit: *Emit, inst: Mir.Inst.Index) !void {
311 const tag = emit.mir.instructions.items(.tag)[inst];
312 const j_type = emit.mir.instructions.items(.data)[inst].j_type;
313
314 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(j_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
315
316 switch (tag) {
317 .jal => {
318 log.debug("jal: {} offset={}", .{ inst, offset });
319 try emit.writeInstruction(Instruction.jal(j_type.rd, @intCast(offset)));
320 },
321 else => unreachable,
322 }
323}
324
325fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
326 const tag = emit.mir.instructions.items(.tag)[inst];
327
328 switch (tag) {
329 .ebreak => try emit.writeInstruction(Instruction.ebreak),
330 .ecall => try emit.writeInstruction(Instruction.ecall),
331 .unimp => try emit.writeInstruction(Instruction.unimp),
332 else => unreachable,
333 }
334}
335
336fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
337 const tag = emit.mir.instructions.items(.tag)[inst];
338 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
339
340 switch (tag) {
341 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
342 else => unreachable,
343 }
344}
345
346fn mirDebugPrologueEnd(emit: *Emit) !void {
347 switch (emit.debug_output) {
348 .dwarf => |dw| {
349 try dw.setPrologueEnd();
350 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
351 },
352 .plan9 => {},
353 .none => {},
354 }
355}
356
357fn mirDebugEpilogueBegin(emit: *Emit) !void {
358 switch (emit.debug_output) {
359 .dwarf => |dw| {
360 try dw.setEpilogueBegin();
361 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
362 },
363141 .plan9 => {},
364142 .none => {},
365143 }
366144}
367145
368fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
369 const tag = emit.mir.instructions.items(.tag)[inst];
370 const data = emit.mir.instructions.items(.data)[inst];
371
372 switch (tag) {
373 .psuedo_prologue => {
374 const stack_size: i12 = math.cast(i12, emit.stack_size) orelse {
375 return emit.fail("TODO: mirPsuedo support larger stack sizes", .{});
376 };
377
378 // Decrement sp by (num s registers * 8) + local var space
379 try emit.writeInstruction(Instruction.addi(.sp, .sp, -stack_size));
380
381 // Spill ra
382 try emit.writeInstruction(Instruction.sd(.ra, 0, .sp));
383
384 // Spill callee saved registers.
385 var s_reg_iter = emit.save_reg_list.iterator(.{});
386 var i: i12 = 8;
387 while (s_reg_iter.next()) |reg_i| {
388 const reg = abi.callee_preserved_regs[reg_i];
389 try emit.writeInstruction(Instruction.sd(reg, i, .sp));
390 i += 8;
391 }
392 },
393 .psuedo_epilogue => {
394 const stack_size: i12 = math.cast(i12, emit.stack_size) orelse {
395 return emit.fail("TODO: mirPsuedo support larger stack sizes", .{});
396 };
397
398 // Restore ra
399 try emit.writeInstruction(Instruction.ld(.ra, 0, .sp));
400
401 // Restore spilled callee saved registers
402 var s_reg_iter = emit.save_reg_list.iterator(.{});
403 var i: i12 = 8;
404 while (s_reg_iter.next()) |reg_i| {
405 const reg = abi.callee_preserved_regs[reg_i];
406 try emit.writeInstruction(Instruction.ld(reg, i, .sp));
407 i += 8;
408 }
409
410 // Increment sp back to previous value
411 try emit.writeInstruction(Instruction.addi(.sp, .sp, stack_size));
412 },
413
414 .j => {
415 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(data.inst).?)) - @as(i64, @intCast(emit.code.items.len));
416 try emit.writeInstruction(Instruction.jal(.zero, @intCast(offset)));
417 },
418
419 else => unreachable,
420 }
421}
422
423fn mirRR(emit: *Emit, inst: Mir.Inst.Index) !void {
424 const tag = emit.mir.instructions.items(.tag)[inst];
425 const rr = emit.mir.instructions.items(.data)[inst].rr;
426
427 const rd = rr.rd;
428 const rs = rr.rs;
429
430 switch (tag) {
431 .mv => try emit.writeInstruction(Instruction.addi(rd, rs, 0)),
432 .not => try emit.writeInstruction(Instruction.xori(rd, rs, 1)),
433 else => unreachable,
434 }
435}
436
437fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {
438 const tag = emit.mir.instructions.items(.tag)[inst];
439 const u_type = emit.mir.instructions.items(.data)[inst].u_type;
440
441 switch (tag) {
442 .lui => try emit.writeInstruction(Instruction.lui(u_type.rd, u_type.imm20)),
443 else => unreachable,
444 }
445}
446
447fn mirNop(emit: *Emit, inst: Mir.Inst.Index) !void {
448 const tag = emit.mir.instructions.items(.tag)[inst];
449
450 switch (tag) {
451 .nop => try emit.writeInstruction(Instruction.addi(.zero, .zero, 0)),
452 .ret => try emit.writeInstruction(Instruction.jalr(.zero, 0, .ra)),
453 else => unreachable,
454 }
455}
456
457fn mirLoadSymbol(emit: *Emit, inst: Mir.Inst.Index) !void {
458 const payload = emit.mir.instructions.items(.data)[inst].payload;
459 const data = emit.mir.extraData(Mir.LoadSymbolPayload, payload).data;
460 const reg = @as(Register, @enumFromInt(data.register));
461
462 const start_offset = @as(u32, @intCast(emit.code.items.len));
463 try emit.writeInstruction(Instruction.lui(reg, 0));
464 try emit.writeInstruction(Instruction.addi(reg, reg, 0));
465
466 switch (emit.bin_file.tag) {
467 .elf => {
468 const elf_file = emit.bin_file.cast(link.File.Elf).?;
469 const atom_ptr = elf_file.symbol(data.atom_index).atom(elf_file).?;
470 const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index);
471 const sym = elf_file.symbol(sym_index);
472
473 var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
474 var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
475
476 if (sym.flags.needs_zig_got) {
477 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
478
479 hi_r_type = Elf.R_ZIG_GOT_HI20;
480 lo_r_type = Elf.R_ZIG_GOT_LO12;
481 }
482
483 try atom_ptr.addReloc(elf_file, .{
484 .r_offset = start_offset,
485 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | hi_r_type,
486 .r_addend = 0,
487 });
488
489 try atom_ptr.addReloc(elf_file, .{
490 .r_offset = start_offset + 4,
491 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | lo_r_type,
492 .r_addend = 0,
493 });
494 },
495 else => unreachable,
496 }
497}
498
499fn isStore(tag: Mir.Inst.Tag) bool {
500 return switch (tag) {
501 .sb => true,
502 .sh => true,
503 .sw => true,
504 .sd => true,
505 .addi => true, // needed for ptr_stack_offset stores
506 else => false,
507 };
508}
509
510fn isLoad(tag: Mir.Inst.Tag) bool {
511 return switch (tag) {
512 .lb => true,
513 .lh => true,
514 .lw => true,
515 .ld => true,
516 else => false,
517 };
518}
519
520pub fn isBranch(tag: Mir.Inst.Tag) bool {
521 return switch (tag) {
522 .beq => true,
523 .bne => true,
524 .jal => true,
525 .j => true,
526 else => false,
146fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
147 return switch (emit.lower.fail(format, args)) {
148 error.LowerFail => error.EmitFail,
149 else => |e| e,
527150 };
528151}
529152
530pub fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
531 const tag = emit.mir.instructions.items(.tag)[inst];
532 const data = emit.mir.instructions.items(.data)[inst];
533
534 switch (tag) {
535 .bne,
536 .beq,
537 => return data.b_type.inst,
538 .jal => return data.j_type.inst,
539 .j => return data.inst,
540 else => std.debug.panic("branchTarget {s}", .{@tagName(tag)}),
541 }
542}
543
544fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
545 const tag = emit.mir.instructions.items(.tag)[inst];
546
547 return switch (tag) {
548 .dbg_line,
549 .dbg_epilogue_begin,
550 .dbg_prologue_end,
551 => 0,
552
553 .cmp_eq,
554 .cmp_neq,
555 .cmp_imm_eq,
556 .cmp_imm_neq,
557 .cmp_gte,
558 .load_symbol,
559 .abs,
560 => 8,
561
562 .psuedo_epilogue, .psuedo_prologue => size: {
563 const count = emit.save_reg_list.count() * 4;
564 break :size count + 8;
565 },
566
567 else => 4,
568 };
569}
570
571fn lowerMir(emit: *Emit) !void {
572 const comp = emit.bin_file.comp;
573 const gpa = comp.gpa;
574 const mir_tags = emit.mir.instructions.items(.tag);
575 const mir_datas = emit.mir.instructions.items(.data);
576
577 const proglogue_size: u32 = @intCast(emit.save_reg_list.size());
578 emit.stack_size += proglogue_size;
579
580 for (mir_tags, 0..) |tag, index| {
581 const inst: u32 = @intCast(index);
582
583 if (isStore(tag) or isLoad(tag)) {
584 const data = mir_datas[inst].i_type;
585 if (data.rs1 == .sp) {
586 const offset = mir_datas[inst].i_type.imm12;
587 mir_datas[inst].i_type.imm12 = offset + @as(i12, @intCast(proglogue_size)) + 8;
588 }
589 }
590
591 if (isBranch(tag)) {
592 const target_inst = emit.branchTarget(inst);
593 try emit.code_offset_mapping.put(gpa, target_inst, 0);
594 }
595 }
596 var current_code_offset: usize = 0;
597
598 for (0..mir_tags.len) |index| {
599 const inst = @as(u32, @intCast(index));
600 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
601 offset.* = current_code_offset;
602 }
603 current_code_offset += emit.instructionSize(inst);
604 }
605}
153const link = @import("../../link.zig");
154const log = std.log.scoped(.emit);
155const mem = std.mem;
156const std = @import("std");
606157
158const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
607159const Emit = @This();
608const std = @import("std");
609const math = std.math;
160const Lower = @import("Lower.zig");
610161const Mir = @import("Mir.zig");
611const bits = @import("bits.zig");
612const abi = @import("abi.zig");
613const link = @import("../../link.zig");
614const Module = @import("../../Module.zig");
615const Elf = @import("../../link/Elf.zig");
616const ErrorMsg = Module.ErrorMsg;
617const assert = std.debug.assert;
618const Instruction = bits.Instruction;
619const Register = bits.Register;
620const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
162const riscv_util = @import("../../link/riscv.zig");
163const Encoding = @import("Encoding.zig");
src/arch/riscv64/Encoding.zig created+333
......@@ -0,0 +1,333 @@
1mnemonic: Mnemonic,
2data: Data,
3
4pub const Mnemonic = enum {
5 // R Type
6 add,
7
8 // I Type
9 ld,
10 lw,
11 lwu,
12 lh,
13 lhu,
14 lb,
15 lbu,
16
17 addi,
18 jalr,
19
20 // U Type
21 lui,
22
23 // S Type
24 sd,
25 sw,
26 sh,
27 sb,
28
29 // J Type
30 jal,
31
32 // System
33 ecall,
34 ebreak,
35 unimp,
36
37 pub fn encoding(mnem: Mnemonic) Enc {
38 return switch (mnem) {
39 // zig fmt: off
40 .add => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0000000 },
41
42 .ld => .{ .opcode = 0b0000011, .funct3 = 0b011, .funct7 = null },
43 .lw => .{ .opcode = 0b0000011, .funct3 = 0b010, .funct7 = null },
44 .lwu => .{ .opcode = 0b0000011, .funct3 = 0b110, .funct7 = null },
45 .lh => .{ .opcode = 0b0000011, .funct3 = 0b001, .funct7 = null },
46 .lhu => .{ .opcode = 0b0000011, .funct3 = 0b101, .funct7 = null },
47 .lb => .{ .opcode = 0b0000011, .funct3 = 0b000, .funct7 = null },
48 .lbu => .{ .opcode = 0b0000011, .funct3 = 0b100, .funct7 = null },
49
50
51 .addi => .{ .opcode = 0b0010011, .funct3 = 0b000, .funct7 = null },
52 .jalr => .{ .opcode = 0b1100111, .funct3 = 0b000, .funct7 = null },
53
54 .lui => .{ .opcode = 0b0110111, .funct3 = null, .funct7 = null },
55
56 .sd => .{ .opcode = 0b0100011, .funct3 = 0b011, .funct7 = null },
57 .sw => .{ .opcode = 0b0100011, .funct3 = 0b010, .funct7 = null },
58 .sh => .{ .opcode = 0b0100011, .funct3 = 0b001, .funct7 = null },
59 .sb => .{ .opcode = 0b0100011, .funct3 = 0b000, .funct7 = null },
60
61 .jal => .{ .opcode = 0b1101111, .funct3 = null, .funct7 = null },
62
63 .ecall => .{ .opcode = 0b1110011, .funct3 = 0b000, .funct7 = null },
64 .ebreak => .{ .opcode = 0b1110011, .funct3 = 0b000, .funct7 = null },
65 .unimp => .{ .opcode = 0b0000000, .funct3 = 0b000, .funct7 = null },
66 // zig fmt: on
67 };
68 }
69};
70
71pub const InstEnc = enum {
72 R,
73 I,
74 S,
75 B,
76 U,
77 J,
78
79 /// extras that have unusual op counts
80 system,
81
82 pub fn fromMnemonic(mnem: Mnemonic) InstEnc {
83 return switch (mnem) {
84 .add,
85 => .R,
86
87 .addi,
88 .ld,
89 .lw,
90 .lwu,
91 .lh,
92 .lhu,
93 .lb,
94 .lbu,
95 .jalr,
96 => .I,
97
98 .lui,
99 => .U,
100
101 .sd,
102 .sw,
103 .sh,
104 .sb,
105 => .S,
106
107 .jal,
108 => .J,
109
110 .ecall,
111 .ebreak,
112 .unimp,
113 => .system,
114 };
115 }
116
117 pub fn opsList(enc: InstEnc) [4]std.meta.FieldEnum(Operand) {
118 return switch (enc) {
119 .R => .{ .reg, .reg, .reg, .none },
120 .I => .{ .reg, .reg, .imm, .none },
121 .S => .{ .reg, .reg, .imm, .none },
122 .B => .{ .imm, .reg, .reg, .imm },
123 .U => .{ .reg, .imm, .none, .none },
124 .J => .{ .reg, .imm, .none, .none },
125 .system => .{ .none, .none, .none, .none },
126 };
127 }
128};
129
130pub const Data = union(InstEnc) {
131 R: packed struct {
132 opcode: u7,
133 rd: u5,
134 funct3: u3,
135 rs1: u5,
136 rs2: u5,
137 funct7: u7,
138 },
139 I: packed struct {
140 opcode: u7,
141 rd: u5,
142 funct3: u3,
143 rs1: u5,
144 imm0_11: u12,
145 },
146 S: packed struct {
147 opcode: u7,
148 imm0_4: u5,
149 funct3: u3,
150 rs1: u5,
151 rs2: u5,
152 imm5_11: u7,
153 },
154 B: packed struct {
155 opcode: u7,
156 imm11: u1,
157 imm1_4: u4,
158 funct3: u3,
159 rs1: u5,
160 rs2: u5,
161 imm5_10: u6,
162 imm12: u1,
163 },
164 U: packed struct {
165 opcode: u7,
166 rd: u5,
167 imm12_31: u20,
168 },
169 J: packed struct {
170 opcode: u7,
171 rd: u5,
172 imm12_19: u8,
173 imm11: u1,
174 imm1_10: u10,
175 imm20: u1,
176 },
177 system: void,
178
179 pub fn toU32(self: Data) u32 {
180 return switch (self) {
181 .R => |v| @as(u32, @bitCast(v)),
182 .I => |v| @as(u32, @bitCast(v)),
183 .S => |v| @as(u32, @bitCast(v)),
184 .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),
185 .U => |v| @as(u32, @bitCast(v)),
186 .J => |v| @as(u32, @bitCast(v)),
187 .system => unreachable,
188 };
189 }
190
191 pub fn construct(mnem: Mnemonic, ops: []const Operand) !Data {
192 const inst_enc = InstEnc.fromMnemonic(mnem);
193
194 const enc = mnem.encoding();
195
196 // special mnemonics
197 switch (mnem) {
198 .ecall,
199 .ebreak,
200 .unimp,
201 => {
202 assert(ops.len == 0);
203 return .{
204 .I = .{
205 .rd = Register.zero.id(),
206 .rs1 = Register.zero.id(),
207 .imm0_11 = switch (mnem) {
208 .ecall => 0x000,
209 .ebreak => 0x001,
210 .unimp => 0,
211 else => unreachable,
212 },
213
214 .opcode = enc.opcode,
215 .funct3 = enc.funct3.?,
216 },
217 };
218 },
219 else => {},
220 }
221
222 switch (inst_enc) {
223 .R => {
224 assert(ops.len == 3);
225 return .{
226 .R = .{
227 .rd = ops[0].reg.id(),
228 .rs1 = ops[1].reg.id(),
229 .rs2 = ops[2].reg.id(),
230
231 .opcode = enc.opcode,
232 .funct3 = enc.funct3.?,
233 .funct7 = enc.funct7.?,
234 },
235 };
236 },
237 .S => {
238 assert(ops.len == 3);
239 const umm = ops[2].imm.asBits(u12);
240
241 return .{
242 .S = .{
243 .imm0_4 = @truncate(umm),
244 .rs1 = ops[0].reg.id(),
245 .rs2 = ops[1].reg.id(),
246 .imm5_11 = @truncate(umm >> 5),
247
248 .opcode = enc.opcode,
249 .funct3 = enc.funct3.?,
250 },
251 };
252 },
253 .I => {
254 assert(ops.len == 3);
255 return .{
256 .I = .{
257 .rd = ops[0].reg.id(),
258 .rs1 = ops[1].reg.id(),
259 .imm0_11 = ops[2].imm.asBits(u12),
260
261 .opcode = enc.opcode,
262 .funct3 = enc.funct3.?,
263 },
264 };
265 },
266 .U => {
267 assert(ops.len == 2);
268 return .{
269 .U = .{
270 .rd = ops[0].reg.id(),
271 .imm12_31 = ops[1].imm.asBits(u20),
272
273 .opcode = enc.opcode,
274 },
275 };
276 },
277 .J => {
278 assert(ops.len == 2);
279
280 const umm = ops[1].imm.asBits(u21);
281 assert(umm % 4 == 0); // misaligned jump target
282
283 return .{
284 .J = .{
285 .rd = ops[0].reg.id(),
286 .imm1_10 = @truncate(umm >> 1),
287 .imm11 = @truncate(umm >> 11),
288 .imm12_19 = @truncate(umm >> 12),
289 .imm20 = @truncate(umm >> 20),
290
291 .opcode = enc.opcode,
292 },
293 };
294 },
295
296 else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),
297 }
298 }
299};
300
301pub fn findByMnemonic(mnem: Mnemonic, ops: []const Operand) !?Encoding {
302 if (!verifyOps(mnem, ops)) return null;
303
304 return .{
305 .mnemonic = mnem,
306 .data = try Data.construct(mnem, ops),
307 };
308}
309
310const Enc = struct {
311 opcode: u7,
312 funct3: ?u3,
313 funct7: ?u7,
314};
315
316fn verifyOps(mnem: Mnemonic, ops: []const Operand) bool {
317 const inst_enc = InstEnc.fromMnemonic(mnem);
318 const list = std.mem.sliceTo(&inst_enc.opsList(), .none);
319 for (list, ops) |l, o| if (l != std.meta.activeTag(o)) return false;
320 return true;
321}
322
323const std = @import("std");
324const assert = std.debug.assert;
325const log = std.log.scoped(.encoding);
326
327const Encoding = @This();
328const bits = @import("bits.zig");
329const Register = bits.Register;
330const encoder = @import("encoder.zig");
331const Instruction = encoder.Instruction;
332const Operand = Instruction.Operand;
333const OperandEnum = std.meta.FieldEnum(Operand);
src/arch/riscv64/Lower.zig created+222
......@@ -0,0 +1,222 @@
1//! This file contains the functionality for lowering RISC-V MIR to Instructions
2
3bin_file: *link.File,
4output_mode: std.builtin.OutputMode,
5link_mode: std.builtin.LinkMode,
6pic: bool,
7allocator: Allocator,
8mir: Mir,
9cc: std.builtin.CallingConvention,
10err_msg: ?*ErrorMsg = null,
11src_loc: Module.SrcLoc,
12result_insts_len: u8 = undefined,
13result_relocs_len: u8 = undefined,
14result_insts: [
15 @max(
16 1, // non-pseudo instruction
17 abi.callee_preserved_regs.len, // spill / restore regs,
18 )
19]Instruction = undefined,
20result_relocs: [1]Reloc = undefined,
21
22pub const Error = error{
23 OutOfMemory,
24 LowerFail,
25 InvalidInstruction,
26};
27
28pub const Reloc = struct {
29 lowered_inst_index: u8,
30 target: Target,
31
32 const Target = union(enum) {
33 inst: Mir.Inst.Index,
34 linker_reloc: bits.Symbol,
35 };
36};
37
38/// The returned slice is overwritten by the next call to lowerMir.
39pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
40 insts: []const Instruction,
41 relocs: []const Reloc,
42} {
43 lower.result_insts = undefined;
44 lower.result_relocs = undefined;
45 errdefer lower.result_insts = undefined;
46 errdefer lower.result_relocs = undefined;
47 lower.result_insts_len = 0;
48 lower.result_relocs_len = 0;
49 defer lower.result_insts_len = undefined;
50 defer lower.result_relocs_len = undefined;
51
52 const inst = lower.mir.instructions.get(index);
53 log.debug("lowerMir {}", .{inst});
54 switch (inst.tag) {
55 else => try lower.generic(inst),
56 .pseudo => switch (inst.ops) {
57 .pseudo_dbg_line_column,
58 .pseudo_dbg_epilogue_begin,
59 .pseudo_dbg_prologue_end,
60 .pseudo_dead,
61 => {},
62 .pseudo_load_rm, .pseudo_store_rm => {
63 const rm = inst.data.rm;
64
65 const frame_loc = rm.m.toFrameLoc(lower.mir);
66
67 switch (inst.ops) {
68 .pseudo_load_rm => {
69 const tag: Encoding.Mnemonic = switch (rm.m.mod.rm.size) {
70 .byte => .lb,
71 .hword => .lh,
72 .word => .lw,
73 .dword => .ld,
74 };
75
76 try lower.emit(tag, &.{
77 .{ .reg = rm.r },
78 .{ .reg = frame_loc.base },
79 .{ .imm = Immediate.s(frame_loc.disp) },
80 });
81 },
82 .pseudo_store_rm => {
83 const tag: Encoding.Mnemonic = switch (rm.m.mod.rm.size) {
84 .byte => .sb,
85 .hword => .sh,
86 .word => .sw,
87 .dword => .sd,
88 };
89
90 try lower.emit(tag, &.{
91 .{ .reg = frame_loc.base },
92 .{ .reg = rm.r },
93 .{ .imm = Immediate.s(frame_loc.disp) },
94 });
95 },
96 else => unreachable,
97 }
98 },
99
100 .pseudo_mv => {
101 const rr = inst.data.rr;
102
103 try lower.emit(.addi, &.{
104 .{ .reg = rr.rd },
105 .{ .reg = rr.rs },
106 .{ .imm = Immediate.s(0) },
107 });
108 },
109 .pseudo_ret => {
110 try lower.emit(.jalr, &.{
111 .{ .reg = .zero },
112 .{ .reg = .ra },
113 .{ .imm = Immediate.s(0) },
114 });
115 },
116 .pseudo_j => {
117 try lower.emit(.jal, &.{
118 .{ .reg = .zero },
119 .{ .imm = lower.reloc(.{ .inst = inst.data.inst }) },
120 });
121 },
122
123 .pseudo_spill_regs => try lower.pushPopRegList(true, inst.data.reg_list),
124 .pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list),
125
126 else => return lower.fail("TODO: psuedo {s}", .{@tagName(inst.ops)}),
127 },
128 }
129
130 return .{
131 .insts = lower.result_insts[0..lower.result_insts_len],
132 .relocs = lower.result_relocs[0..lower.result_relocs_len],
133 };
134}
135
136fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
137 const mnemonic = std.meta.stringToEnum(Encoding.Mnemonic, @tagName(inst.tag)) orelse {
138 return lower.fail("generic inst name {s}-{s} doesn't match with a mnemonic", .{
139 @tagName(inst.tag),
140 @tagName(inst.ops),
141 });
142 };
143 try lower.emit(mnemonic, switch (inst.ops) {
144 .none => &.{},
145 .ri => &.{
146 .{ .reg = inst.data.u_type.rd },
147 .{ .imm = inst.data.u_type.imm20 },
148 },
149 .rri => &.{
150 .{ .reg = inst.data.i_type.rd },
151 .{ .reg = inst.data.i_type.rs1 },
152 .{ .imm = inst.data.i_type.imm12 },
153 },
154 else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),
155 });
156}
157
158fn emit(lower: *Lower, mnemonic: Encoding.Mnemonic, ops: []const Instruction.Operand) !void {
159 lower.result_insts[lower.result_insts_len] =
160 try Instruction.new(mnemonic, ops);
161 lower.result_insts_len += 1;
162}
163
164fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
165 lower.result_relocs[lower.result_relocs_len] = .{
166 .lowered_inst_index = lower.result_insts_len,
167 .target = target,
168 };
169 lower.result_relocs_len += 1;
170 return Immediate.s(0);
171}
172
173fn pushPopRegList(lower: *Lower, comptime spilling: bool, reg_list: Mir.RegisterList) !void {
174 var it = reg_list.iterator(.{ .direction = if (spilling) .forward else .reverse });
175
176 var reg_i: u31 = 0;
177 while (it.next()) |i| {
178 const frame = lower.mir.frame_locs.get(@intFromEnum(bits.FrameIndex.spill_frame));
179
180 if (spilling) {
181 try lower.emit(.sd, &.{
182 .{ .reg = frame.base },
183 .{ .reg = abi.callee_preserved_regs[i] },
184 .{ .imm = Immediate.s(frame.disp + reg_i) },
185 });
186 } else {
187 try lower.emit(.ld, &.{
188 .{ .reg = abi.callee_preserved_regs[i] },
189 .{ .reg = frame.base },
190 .{ .imm = Immediate.s(frame.disp + reg_i) },
191 });
192 }
193
194 reg_i += 8;
195 }
196}
197
198pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
199 @setCold(true);
200 assert(lower.err_msg == null);
201 lower.err_msg = try ErrorMsg.create(lower.allocator, lower.src_loc, format, args);
202 return error.LowerFail;
203}
204
205const Lower = @This();
206
207const abi = @import("abi.zig");
208const assert = std.debug.assert;
209const bits = @import("bits.zig");
210const encoder = @import("encoder.zig");
211const link = @import("../../link.zig");
212const Encoding = @import("Encoding.zig");
213const std = @import("std");
214const log = std.log.scoped(.lower);
215
216const Air = @import("../../Air.zig");
217const Allocator = std.mem.Allocator;
218const ErrorMsg = Module.ErrorMsg;
219const Mir = @import("Mir.zig");
220const Module = @import("../../Module.zig");
221const Instruction = encoder.Instruction;
222const Immediate = bits.Immediate;
src/arch/riscv64/Mir.zig+178-85
......@@ -9,22 +9,32 @@
99instructions: std.MultiArrayList(Inst).Slice,
1010/// The meaning of this data is determined by `Inst.Tag` value.
1111extra: []const u32,
12frame_locs: std.MultiArrayList(FrameLoc).Slice,
1213
1314pub const Inst = struct {
1415 tag: Tag,
15 /// The meaning of this depends on `tag`.
1616 data: Data,
17 ops: Ops,
18
19 /// The position of an MIR instruction within the `Mir` instructions array.
20 pub const Index = u32;
1721
1822 pub const Tag = enum(u16) {
23 /// Add immediate. Uses i_type payload.
1924 addi,
25
26 /// Add immediate and produce a sign-extended result.
27 ///
28 /// Uses i-type payload.
2029 addiw,
30
2131 jalr,
2232 lui,
2333 mv,
2434
25 unimp,
2635 ebreak,
2736 ecall,
37 unimp,
2838
2939 /// OR instruction. Uses r_type payload.
3040 @"or",
......@@ -48,9 +58,11 @@ pub const Inst = struct {
4858 /// Register Logical Right Shit, uses r_type payload
4959 srlw,
5060
61 /// Jumps, but stores the address of the instruction following the
62 /// jump in `rd`.
63 ///
64 /// Uses j_type payload.
5165 jal,
52 /// Jumps. Uses `inst` payload.
53 j,
5466
5567 /// Immediate AND, uses i_type payload
5668 andi,
......@@ -93,55 +105,34 @@ pub const Inst = struct {
93105 /// Boolean NOT, Uses rr payload
94106 not,
95107
108 /// Generates a NO-OP, uses nop payload
96109 nop,
97 ret,
98110
99 /// Load double (64 bits)
111 /// Load double (64 bits), uses i_type payload
100112 ld,
101 /// Store double (64 bits)
102 sd,
103 /// Load word (32 bits)
113 /// Load word (32 bits), uses i_type payload
104114 lw,
105 /// Store word (32 bits)
106 sw,
107 /// Load half (16 bits)
115 /// Load half (16 bits), uses i_type payload
108116 lh,
109 /// Store half (16 bits)
110 sh,
111 /// Load byte (8 bits)
117 /// Load byte (8 bits), uses i_type payload
112118 lb,
113 /// Store byte (8 bits)
114 sb,
115
116 /// Pseudo-instruction: End of prologue
117 dbg_prologue_end,
118 /// Pseudo-instruction: Beginning of epilogue
119 dbg_epilogue_begin,
120 /// Pseudo-instruction: Update debug line
121 dbg_line,
122
123 /// Psuedo-instruction that will generate a backpatched
124 /// function prologue.
125 psuedo_prologue,
126 /// Psuedo-instruction that will generate a backpatched
127 /// function epilogue
128 psuedo_epilogue,
129119
130 /// Loads the address of a value that hasn't yet been allocated in memory.
131 ///
132 /// uses the Mir.LoadSymbolPayload payload.
133 load_symbol,
120 /// Store double (64 bits), uses s_type payload
121 sd,
122 /// Store word (32 bits), uses s_type payload
123 sw,
124 /// Store half (16 bits), uses s_type payload
125 sh,
126 /// Store byte (8 bits), uses s_type payload
127 sb,
134128
135 // TODO: add description
136 // this is bad, remove this
137 ldr_ptr_stack,
129 /// A pseudo-instruction. Used for anything that isn't 1:1 with an
130 /// assembly instruction.
131 pseudo,
138132 };
139133
140 /// The position of an MIR instruction within the `Mir` instructions array.
141 pub const Index = u32;
142
143134 /// All instructions have a 4-byte payload, which is contained within
144 /// this union. `Tag` determines which union field is active, as well as
135 /// this union. `Ops` determines which union field is active, as well as
145136 /// how to interpret the data within.
146137 pub const Data = union {
147138 /// No additional data
......@@ -152,74 +143,154 @@ pub const Inst = struct {
152143 ///
153144 /// Used by e.g. b
154145 inst: Index,
155 /// A 16-bit immediate value.
156 ///
157 /// Used by e.g. svc
158 imm16: i16,
159 /// A 12-bit immediate value.
160 ///
161 /// Used by e.g. psuedo_prologue
162 imm12: i12,
163146 /// Index into `extra`. Meaning of what can be found there is context-dependent.
164147 ///
165148 /// Used by e.g. load_memory
166149 payload: u32,
167 /// A register
168 ///
169 /// Used by e.g. blr
170 reg: Register,
171 /// Two registers
172 ///
173 /// Used by e.g. mv
174 rr: struct {
150
151 r_type: struct {
175152 rd: Register,
176 rs: Register,
153 rs1: Register,
154 rs2: Register,
177155 },
178 /// I-Type
179 ///
180 /// Used by e.g. jalr
156
181157 i_type: struct {
182158 rd: Register,
183159 rs1: Register,
184 imm12: i12,
160 imm12: Immediate,
185161 },
186 /// R-Type
187 ///
188 /// Used by e.g. add
189 r_type: struct {
190 rd: Register,
162
163 s_type: struct {
191164 rs1: Register,
192165 rs2: Register,
166 imm5: Immediate,
167 imm7: Immediate,
193168 },
194 /// B-Type
195 ///
196 /// Used by e.g. beq
169
197170 b_type: struct {
198171 rs1: Register,
199172 rs2: Register,
200173 inst: Inst.Index,
201174 },
202 /// J-Type
203 ///
204 /// Used by e.g. jal
205 j_type: struct {
175
176 u_type: struct {
206177 rd: Register,
207 inst: Inst.Index,
178 imm20: Immediate,
208179 },
209 /// U-Type
210 ///
211 /// Used by e.g. lui
212 u_type: struct {
180
181 j_type: struct {
213182 rd: Register,
214 imm20: i20,
183 inst: Inst.Index,
215184 },
185
216186 /// Debug info: line and column
217187 ///
218 /// Used by e.g. dbg_line
219 dbg_line_column: struct {
188 /// Used by e.g. pseudo_dbg_line
189 pseudo_dbg_line_column: struct {
220190 line: u32,
221191 column: u32,
222192 },
193
194 // Custom types to be lowered
195
196 /// Register + Memory
197 rm: struct {
198 r: Register,
199 m: Memory,
200 },
201
202 reg_list: Mir.RegisterList,
203
204 /// A register
205 ///
206 /// Used by e.g. blr
207 reg: Register,
208
209 /// Two registers
210 ///
211 /// Used by e.g. mv
212 rr: struct {
213 rd: Register,
214 rs: Register,
215 },
216 };
217
218 pub const Ops = enum {
219 /// No data associated with this instruction (only mnemonic is used).
220 none,
221 /// Two registers
222 rr,
223 /// Three registers
224 rrr,
225
226 /// Two registers + immediate, uses the i_type payload.
227 rri,
228 /// Two registers + Two Immediates
229 rrii,
230
231 /// Two registers + another instruction.
232 rr_inst,
233
234 /// Register + Memory
235 rm,
236
237 /// Register + Immediate
238 ri,
239
240 /// Another instruction.
241 inst,
242
243 /// Pseudo-instruction that will generate a backpatched
244 /// function prologue.
245 pseudo_prologue,
246 /// Pseudo-instruction that will generate a backpatched
247 /// function epilogue
248 pseudo_epilogue,
249
250 /// Pseudo-instruction: End of prologue
251 pseudo_dbg_prologue_end,
252 /// Pseudo-instruction: Beginning of epilogue
253 pseudo_dbg_epilogue_begin,
254 /// Pseudo-instruction: Update debug line
255 pseudo_dbg_line_column,
256
257 /// Pseudo-instruction that loads from memory into a register.
258 ///
259 /// Uses `rm` payload.
260 pseudo_load_rm,
261 /// Pseudo-instruction that stores from a register into memory
262 ///
263 /// Uses `rm` payload.
264 pseudo_store_rm,
265
266 /// Pseudo-instruction that loads the address of memory into a register.
267 ///
268 /// Uses `rm` payload.
269 pseudo_lea_rm,
270
271 /// Shorthand for returning, aka jumping to ra register.
272 ///
273 /// Uses nop payload.
274 pseudo_ret,
275
276 /// Jumps. Uses `inst` payload.
277 pseudo_j,
278
279 /// Dead inst, ignored by the emitter.
280 pseudo_dead,
281
282 /// Loads the address of a value that hasn't yet been allocated in memory.
283 ///
284 /// uses the Mir.LoadSymbolPayload payload.
285 pseudo_load_symbol,
286
287 /// Moves the value of rs1 to rd.
288 ///
289 /// uses the `rr` payload.
290 pseudo_mv,
291
292 pseudo_restore_regs,
293 pseudo_spill_regs,
223294 };
224295
225296 // Make sure we don't accidentally make instructions bigger than expected.
......@@ -229,14 +300,32 @@ pub const Inst = struct {
229300 // assert(@sizeOf(Inst) == 8);
230301 // }
231302 // }
303
304 pub fn format(
305 inst: Inst,
306 comptime fmt: []const u8,
307 options: std.fmt.FormatOptions,
308 writer: anytype,
309 ) !void {
310 assert(fmt.len == 0);
311 _ = options;
312
313 try writer.print("Tag: {s}, Ops: {s}", .{ @tagName(inst.tag), @tagName(inst.ops) });
314 }
232315};
233316
234317pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
235318 mir.instructions.deinit(gpa);
319 mir.frame_locs.deinit(gpa);
236320 gpa.free(mir.extra);
237321 mir.* = undefined;
238322}
239323
324pub const FrameLoc = struct {
325 base: Register,
326 disp: i32,
327};
328
240329/// Returns the requested data, as well as the new index which is at the start of the
241330/// trailers for the object.
242331pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
......@@ -291,11 +380,11 @@ pub const RegisterList = struct {
291380 return self.bitset.iterator(options);
292381 }
293382
294 pub fn count(self: Self) u32 {
383 pub fn count(self: Self) i32 {
295384 return @intCast(self.bitset.count());
296385 }
297386
298 pub fn size(self: Self) u32 {
387 pub fn size(self: Self) i32 {
299388 return @intCast(self.bitset.count() * 8);
300389 }
301390};
......@@ -307,4 +396,8 @@ const assert = std.debug.assert;
307396
308397const bits = @import("bits.zig");
309398const Register = bits.Register;
399const Immediate = bits.Immediate;
400const Memory = bits.Memory;
401const FrameIndex = bits.FrameIndex;
402const FrameAddr = @import("CodeGen.zig").FrameAddr;
310403const IntegerBitSet = std.bit_set.IntegerBitSet;
src/arch/riscv64/abi.zig+27-3
......@@ -93,7 +93,7 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
9393
9494/// There are a maximum of 8 possible return slots. Returned values are in
9595/// the beginning of the array; unused slots are filled with .none.
96pub fn classifySystemV(ty: Type, mod: *Module) [8]Class {
96pub fn classifySystem(ty: Type, mod: *Module) [8]Class {
9797 var result = [1]Class{.none} ** 8;
9898 switch (ty.zigTypeTag(mod)) {
9999 .Pointer => switch (ty.ptrSize(mod)) {
......@@ -109,18 +109,42 @@ pub fn classifySystemV(ty: Type, mod: *Module) [8]Class {
109109 },
110110 .Optional => {
111111 if (ty.isPtrLikeOptional(mod)) {
112 result[0] = .integer;
112113 return result;
113114 }
114115 result[0] = .integer;
115116 result[1] = .integer;
116117 return result;
117118 },
118 else => return result,
119 .Int, .Enum, .ErrorSet => {
120 const int_bits = ty.intInfo(mod).bits;
121 if (int_bits <= 64) {
122 result[0] = .integer;
123 return result;
124 }
125 if (int_bits <= 128) {
126 result[0] = .integer;
127 result[1] = .integer;
128 return result;
129 }
130 unreachable; // support > 128 bit int arguments
131 },
132 .ErrorUnion => {
133 const payload = ty.errorUnionPayload(mod);
134 const payload_bits = payload.bitSize(mod);
135 if (payload_bits <= 64) {
136 result[0] = .integer;
137 result[1] = .integer;
138 }
139 unreachable; // support > 64 bit error payloads
140 },
141 else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),
119142 }
120143}
121144
122145pub const callee_preserved_regs = [_]Register{
123 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
146 // .s0 is ommited to be used as a frame pointer
147 .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
124148};
125149
126150pub const function_arg_regs = [_]Register{
src/arch/riscv64/bits.zig+161-398
......@@ -2,391 +2,141 @@ const std = @import("std");
22const DW = std.dwarf;
33const assert = std.debug.assert;
44const testing = std.testing;
5const Encoding = @import("Encoding.zig");
6const Mir = @import("Mir.zig");
57
6// TODO: this is only tagged to facilitate the monstrosity.
7// Once packed structs work make it packed.
8pub const Instruction = union(enum) {
9 R: packed struct {
10 opcode: u7,
11 rd: u5,
12 funct3: u3,
13 rs1: u5,
14 rs2: u5,
15 funct7: u7,
16 },
17 I: packed struct {
18 opcode: u7,
19 rd: u5,
20 funct3: u3,
21 rs1: u5,
22 imm0_11: u12,
23 },
24 S: packed struct {
25 opcode: u7,
26 imm0_4: u5,
27 funct3: u3,
28 rs1: u5,
29 rs2: u5,
30 imm5_11: u7,
31 },
32 B: packed struct {
33 opcode: u7,
34 imm11: u1,
35 imm1_4: u4,
36 funct3: u3,
37 rs1: u5,
38 rs2: u5,
39 imm5_10: u6,
40 imm12: u1,
41 },
42 U: packed struct {
43 opcode: u7,
44 rd: u5,
45 imm12_31: u20,
46 },
47 J: packed struct {
48 opcode: u7,
49 rd: u5,
50 imm12_19: u8,
51 imm11: u1,
52 imm1_10: u10,
53 imm20: u1,
54 },
8pub const Memory = struct {
9 base: Base,
10 mod: Mod,
5511
56 // TODO: once packed structs work we can remove this monstrosity.
57 pub fn toU32(self: Instruction) u32 {
58 return switch (self) {
59 .R => |v| @as(u32, @bitCast(v)),
60 .I => |v| @as(u32, @bitCast(v)),
61 .S => |v| @as(u32, @bitCast(v)),
62 .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),
63 .U => |v| @as(u32, @bitCast(v)),
64 .J => |v| @as(u32, @bitCast(v)),
65 };
66 }
12 pub const Base = union(enum) {
13 reg: Register,
14 frame: FrameIndex,
15 reloc: Symbol,
16 };
6717
68 fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
69 return Instruction{
70 .R = .{
71 .opcode = op,
72 .funct3 = fn3,
73 .funct7 = fn7,
74 .rd = rd.id(),
75 .rs1 = r1.id(),
76 .rs2 = r2.id(),
77 },
78 };
79 }
18 pub const Mod = union(enum(u1)) {
19 rm: struct {
20 size: Size,
21 disp: i32 = 0,
22 },
23 off: u64,
24 };
8025
81 // RISC-V is all signed all the time -- convert immediates to unsigned for processing
82 fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
83 const umm = @as(u12, @bitCast(imm));
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 fromSize(size: u32) Size {
37 return switch (size) {
38 1 => .byte,
39 2 => .hword,
40 4 => .word,
41 8 => .dword,
42 else => unreachable,
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 };
8465
85 return Instruction{
86 .I = .{
87 .opcode = op,
88 .funct3 = fn3,
89 .rd = rd.id(),
90 .rs1 = r1.id(),
91 .imm0_11 = umm,
66 /// Asserts `mem` can be represented as a `FrameLoc`.
67 pub fn toFrameLoc(mem: Memory, mir: Mir) Mir.FrameLoc {
68 switch (mem.base) {
69 .reg => |reg| {
70 return .{
71 .base = reg,
72 .disp = switch (mem.mod) {
73 .off => unreachable, // TODO: toFrameLoc disp.off
74 .rm => |rm| rm.disp,
75 },
76 };
9277 },
93 };
78 .frame => |index| return mir.frame_locs.get(@intFromEnum(index)),
79 .reloc => unreachable,
80 }
9481 }
82};
9583
96 fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
97 const umm = @as(u12, @bitCast(imm));
84pub const Immediate = union(enum) {
85 signed: i32,
86 unsigned: u32,
9887
99 return Instruction{
100 .S = .{
101 .opcode = op,
102 .funct3 = fn3,
103 .rs1 = r1.id(),
104 .rs2 = r2.id(),
105 .imm0_4 = @as(u5, @truncate(umm)),
106 .imm5_11 = @as(u7, @truncate(umm >> 5)),
107 },
108 };
88 pub fn u(x: u64) Immediate {
89 return .{ .unsigned = x };
10990 }
11091
111 // Use significance value rather than bit value, same for J-type
112 // -- less burden on callsite, bonus semantic checking
113 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
114 const umm = @as(u13, @bitCast(imm));
115 assert(umm % 4 == 0); // misaligned branch target
116
117 return Instruction{
118 .B = .{
119 .opcode = op,
120 .funct3 = fn3,
121 .rs1 = r1.id(),
122 .rs2 = r2.id(),
123 .imm1_4 = @as(u4, @truncate(umm >> 1)),
124 .imm5_10 = @as(u6, @truncate(umm >> 5)),
125 .imm11 = @as(u1, @truncate(umm >> 11)),
126 .imm12 = @as(u1, @truncate(umm >> 12)),
127 },
128 };
92 pub fn s(x: i32) Immediate {
93 return .{ .signed = x };
12994 }
13095
131 // We have to extract the 20 bits anyway -- let's not make it more painful
132 fn uType(op: u7, rd: Register, imm: i20) Instruction {
133 const umm = @as(u20, @bitCast(imm));
134
135 return Instruction{
136 .U = .{
137 .opcode = op,
138 .rd = rd.id(),
139 .imm12_31 = umm,
96 pub fn asSigned(imm: Immediate, bit_size: u64) i64 {
97 return switch (imm) {
98 .signed => |x| switch (bit_size) {
99 1, 8 => @as(i8, @intCast(x)),
100 16 => @as(i16, @intCast(x)),
101 32, 64 => x,
102 else => unreachable,
103 },
104 .unsigned => |x| switch (bit_size) {
105 1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))),
106 16 => @as(i16, @bitCast(@as(u16, @intCast(x)))),
107 32 => @as(i32, @bitCast(@as(u32, @intCast(x)))),
108 64 => @bitCast(x),
109 else => unreachable,
140110 },
141111 };
142112 }
143113
144 fn jType(op: u7, rd: Register, imm: i21) Instruction {
145 const umm = @as(u21, @bitCast(imm));
146 assert(umm % 2 == 0); // misaligned jump target
147
148 return Instruction{
149 .J = .{
150 .opcode = op,
151 .rd = rd.id(),
152 .imm1_10 = @as(u10, @truncate(umm >> 1)),
153 .imm11 = @as(u1, @truncate(umm >> 11)),
154 .imm12_19 = @as(u8, @truncate(umm >> 12)),
155 .imm20 = @as(u1, @truncate(umm >> 20)),
114 pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
115 return switch (imm) {
116 .signed => |x| switch (bit_size) {
117 1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))),
118 16 => @as(u16, @bitCast(@as(i16, @intCast(x)))),
119 32, 64 => @as(u32, @bitCast(x)),
120 else => unreachable,
121 },
122 .unsigned => |x| switch (bit_size) {
123 1, 8 => @as(u8, @intCast(x)),
124 16 => @as(u16, @intCast(x)),
125 32 => @as(u32, @intCast(x)),
126 64 => x,
127 else => unreachable,
156128 },
157129 };
158130 }
159131
160 // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
161
162 // Arithmetic/Logical, Register-Register
163
164 pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
165 return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
166 }
167
168 pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
169 return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
170 }
171
172 pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
173 return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
174 }
175
176 pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
177 return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
178 }
179
180 pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
181 return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
182 }
183
184 pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
185 return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
186 }
187
188 pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
189 return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
190 }
191
192 pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
193 return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
194 }
195
196 pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
197 return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
198 }
199
200 pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
201 return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
202 }
203
204 // M extension operations
205
206 pub fn mul(rd: Register, r1: Register, r2: Register) Instruction {
207 return rType(0b0110011, 0b000, 0b0000001, rd, r1, r2);
208 }
209
210 // Arithmetic/Logical, Register-Register (32-bit)
211
212 pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
213 return rType(0b0111011, 0b000, rd, r1, r2);
214 }
215
216 pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
217 return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
218 }
219
220 pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
221 return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
222 }
223
224 pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
225 return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
226 }
227
228 pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
229 return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
230 }
231
232 // Arithmetic/Logical, Register-Immediate
233
234 pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
235 return iType(0b0010011, 0b000, rd, r1, imm);
236 }
237
238 pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
239 return iType(0b0010011, 0b111, rd, r1, imm);
240 }
241
242 pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
243 return iType(0b0010011, 0b110, rd, r1, imm);
244 }
245
246 pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
247 return iType(0b0010011, 0b100, rd, r1, imm);
248 }
249
250 pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
251 return iType(0b0010011, 0b001, rd, r1, shamt);
252 }
253
254 pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
255 return iType(0b0010011, 0b101, rd, r1, shamt);
256 }
257
258 pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
259 return iType(0b0010011, 0b101, rd, r1, (@as(i12, 1) << 10) + shamt);
260 }
261
262 pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
263 return iType(0b0010011, 0b010, rd, r1, imm);
264 }
265
266 pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
267 return iType(0b0010011, 0b011, rd, r1, @as(i12, @bitCast(imm)));
268 }
269
270 // Arithmetic/Logical, Register-Immediate (32-bit)
271
272 pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
273 return iType(0b0011011, 0b000, rd, r1, imm);
274 }
275
276 pub fn slliw(rd: Register, r1: Register, shamt: u6) Instruction {
277 return iType(0b0011011, 0b001, rd, r1, shamt);
278 }
279
280 pub fn srliw(rd: Register, r1: Register, shamt: u6) Instruction {
281 return iType(0b0011011, 0b101, rd, r1, shamt);
282 }
283
284 pub fn sraiw(rd: Register, r1: Register, shamt: u6) Instruction {
285 return iType(0b0011011, 0b101, rd, r1, (@as(i12, 1) << 10) + shamt);
286 }
287
288 // Upper Immediate
289
290 pub fn lui(rd: Register, imm: i20) Instruction {
291 return uType(0b0110111, rd, imm);
292 }
293
294 pub fn auipc(rd: Register, imm: i20) Instruction {
295 return uType(0b0010111, rd, imm);
296 }
297
298 // Load
299
300 pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
301 return iType(0b0000011, 0b011, rd, base, offset);
302 }
303
304 pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
305 return iType(0b0000011, 0b010, rd, base, offset);
306 }
307
308 pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
309 return iType(0b0000011, 0b110, rd, base, offset);
310 }
311
312 pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
313 return iType(0b0000011, 0b001, rd, base, offset);
314 }
315
316 pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
317 return iType(0b0000011, 0b101, rd, base, offset);
318 }
319
320 pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
321 return iType(0b0000011, 0b000, rd, base, offset);
322 }
323
324 pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
325 return iType(0b0000011, 0b100, rd, base, offset);
326 }
327
328 // Store
329
330 pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
331 return sType(0b0100011, 0b011, base, rs, offset);
332 }
333
334 pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
335 return sType(0b0100011, 0b010, base, rs, offset);
336 }
337
338 pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
339 return sType(0b0100011, 0b001, base, rs, offset);
340 }
341
342 pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
343 return sType(0b0100011, 0b000, base, rs, offset);
344 }
345
346 // Fence
347 // TODO: implement fence
348
349 // Branch
350
351 pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
352 return bType(0b1100011, 0b000, r1, r2, offset);
353 }
354
355 pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
356 return bType(0b1100011, 0b001, r1, r2, offset);
357 }
358
359 pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
360 return bType(0b1100011, 0b100, r1, r2, offset);
361 }
362
363 pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
364 return bType(0b1100011, 0b101, r1, r2, offset);
365 }
366
367 pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
368 return bType(0b1100011, 0b110, r1, r2, offset);
369 }
370
371 pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
372 return bType(0b1100011, 0b111, r1, r2, offset);
373 }
374
375 // Jump
376
377 pub fn jal(link: Register, offset: i21) Instruction {
378 return jType(0b1101111, link, offset);
379 }
380
381 pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
382 return iType(0b1100111, 0b000, link, base, offset);
132 pub fn asBits(imm: Immediate, comptime T: type) T {
133 const int_info = @typeInfo(T).Int;
134 if (int_info.signedness != .unsigned) @compileError("Immediate.asBits needs unsigned T");
135 return switch (imm) {
136 .signed => |x| @bitCast(@as(std.meta.Int(.signed, int_info.bits), @intCast(x))),
137 .unsigned => |x| @intCast(x),
138 };
383139 }
384
385 // System
386
387 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
388 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
389 pub const unimp = iType(0, 0, .zero, .zero, 0);
390140};
391141
392142pub const Register = enum(u6) {
......@@ -421,39 +171,52 @@ pub const Register = enum(u6) {
421171 }
422172};
423173
424// zig fmt: on
425
426test "serialize instructions" {
427 const Testcase = struct {
428 inst: Instruction,
429 expected: u32,
430 };
431
432 const testcases = [_]Testcase{
433 .{ // add t6, zero, zero
434 .inst = Instruction.add(.t6, .zero, .zero),
435 .expected = 0b0000000_00000_00000_000_11111_0110011,
436 },
437 .{ // sd s0, 0x7f(s0)
438 .inst = Instruction.sd(.s0, 0x7f, .s0),
439 .expected = 0b0000011_01000_01000_011_11111_0100011,
440 },
441 .{ // bne s0, s1, 0x42
442 .inst = Instruction.bne(.s0, .s1, 0x42),
443 .expected = 0b0_000010_01001_01000_001_0001_0_1100011,
444 },
445 .{ // j 0x1a
446 .inst = Instruction.jal(.zero, 0x1a),
447 .expected = 0b0_0000001101_0_00000000_00000_1101111,
448 },
449 .{ // ebreak
450 .inst = Instruction.ebreak,
451 .expected = 0b000000000001_00000_000_00000_1110011,
452 },
453 };
454
455 for (testcases) |case| {
456 const actual = case.inst.toU32();
457 try testing.expectEqual(case.expected, actual);
174pub const FrameIndex = enum(u32) {
175 /// This index refers to the return address.
176 ret_addr,
177 /// This index refers to the frame pointer.
178 base_ptr,
179 /// This index refers to the entire stack frame.
180 stack_frame,
181 /// This index referes to where in the stack frame the args are spilled to.
182 args_frame,
183 /// This index referes to a frame dedicated to setting up args for function called
184 /// in this function. Useful for aligning args separately.
185 call_frame,
186 /// This index referes to the frame where callee saved registers are spilled and restore
187 /// from.
188 spill_frame,
189 /// Other indices are used for local variable stack slots
190 _,
191
192 pub const named_count = @typeInfo(FrameIndex).Enum.fields.len;
193
194 pub fn isNamed(fi: FrameIndex) bool {
195 return @intFromEnum(fi) < named_count;
196 }
197
198 pub fn format(
199 fi: FrameIndex,
200 comptime fmt: []const u8,
201 options: std.fmt.FormatOptions,
202 writer: anytype,
203 ) @TypeOf(writer).Error!void {
204 try writer.writeAll("FrameIndex");
205 if (fi.isNamed()) {
206 try writer.writeByte('.');
207 try writer.writeAll(@tagName(fi));
208 } else {
209 try writer.writeByte('(');
210 try std.fmt.formatType(@intFromEnum(fi), fmt, options, writer, 0);
211 try writer.writeByte(')');
212 }
458213 }
459}
214};
215
216/// A linker symbol not yet allocated in VM.
217pub const Symbol = struct {
218 /// Index of the containing atom.
219 atom_index: u32,
220 /// Index into the linker's symbol table.
221 sym_index: u32,
222};
src/arch/riscv64/encoder.zig created+49
......@@ -0,0 +1,49 @@
1pub const Instruction = struct {
2 encoding: Encoding,
3 ops: [4]Operand = .{.none} ** 4,
4
5 pub const Operand = union(enum) {
6 none,
7 reg: Register,
8 mem: Memory,
9 imm: Immediate,
10 };
11
12 pub fn new(mnemonic: Encoding.Mnemonic, ops: []const Operand) !Instruction {
13 const encoding = (try Encoding.findByMnemonic(mnemonic, ops)) orelse {
14 log.err("no encoding found for: {s} {s} {s} {s} {s}", .{
15 @tagName(mnemonic),
16 @tagName(if (ops.len > 0) ops[0] else .none),
17 @tagName(if (ops.len > 1) ops[1] else .none),
18 @tagName(if (ops.len > 2) ops[2] else .none),
19 @tagName(if (ops.len > 3) ops[3] else .none),
20 });
21 return error.InvalidInstruction;
22 };
23
24 var result_ops: [4]Operand = .{.none} ** 4;
25 @memcpy(result_ops[0..ops.len], ops);
26
27 return .{
28 .encoding = encoding,
29 .ops = result_ops,
30 };
31 }
32
33 pub fn encode(inst: Instruction, writer: anytype) !void {
34 try writer.writeInt(u32, inst.encoding.data.toU32(), .little);
35 }
36};
37
38const std = @import("std");
39
40const Lower = @import("Lower.zig");
41const Mir = @import("Mir.zig");
42const bits = @import("bits.zig");
43const Encoding = @import("Encoding.zig");
44
45const Register = bits.Register;
46const Memory = bits.Memory;
47const Immediate = bits.Immediate;
48
49const log = std.log.scoped(.encode);
src/link/riscv.zig+33-18
......@@ -25,38 +25,52 @@ pub fn writeAddend(
2525}
2626
2727pub fn writeInstU(code: *[4]u8, value: u32) void {
28 var inst = Instruction{
28 var data = Encoding.Data{
2929 .U = mem.bytesToValue(std.meta.TagPayload(
30 Instruction,
31 Instruction.U,
30 Encoding.Data,
31 Encoding.Data.U,
3232 ), code),
3333 };
3434 const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800);
35 inst.U.imm12_31 = bitSlice(compensated, 31, 12);
36 mem.writeInt(u32, code, inst.toU32(), .little);
35 data.U.imm12_31 = bitSlice(compensated, 31, 12);
36 mem.writeInt(u32, code, data.toU32(), .little);
3737}
3838
3939pub fn writeInstI(code: *[4]u8, value: u32) void {
40 var inst = Instruction{
40 var data = Encoding.Data{
4141 .I = mem.bytesToValue(std.meta.TagPayload(
42 Instruction,
43 Instruction.I,
42 Encoding.Data,
43 Encoding.Data.I,
4444 ), code),
4545 };
46 inst.I.imm0_11 = bitSlice(value, 11, 0);
47 mem.writeInt(u32, code, inst.toU32(), .little);
46 data.I.imm0_11 = bitSlice(value, 11, 0);
47 mem.writeInt(u32, code, data.toU32(), .little);
4848}
4949
5050pub fn writeInstS(code: *[4]u8, value: u32) void {
51 var inst = Instruction{
51 var data = Encoding.Data{
5252 .S = mem.bytesToValue(std.meta.TagPayload(
53 Instruction,
54 Instruction.S,
53 Encoding.Data,
54 Encoding.Data.S,
5555 ), code),
5656 };
57 inst.S.imm0_4 = bitSlice(value, 4, 0);
58 inst.S.imm5_11 = bitSlice(value, 11, 5);
59 mem.writeInt(u32, code, inst.toU32(), .little);
57 data.S.imm0_4 = bitSlice(value, 4, 0);
58 data.S.imm5_11 = bitSlice(value, 11, 5);
59 mem.writeInt(u32, code, data.toU32(), .little);
60}
61
62pub fn writeInstJ(code: *[4]u8, value: u32) void {
63 var data = Encoding.Data{
64 .J = mem.bytesToValue(std.meta.TagPayload(
65 Encoding.Data,
66 Encoding.Data.J,
67 ), code),
68 };
69 data.J.imm1_10 = bitSlice(value, 10, 1);
70 data.J.imm11 = bitSlice(value, 11, 11);
71 data.J.imm12_19 = bitSlice(value, 19, 12);
72 data.J.imm20 = bitSlice(value, 20, 20);
73 mem.writeInt(u32, code, data.toU32(), .little);
6074}
6175
6276fn bitSlice(
......@@ -67,8 +81,9 @@ fn bitSlice(
6781 return @truncate((value >> low) & (1 << (high - low + 1)) - 1);
6882}
6983
70const bits = @import("../arch/riscv64/bits.zig");
84const encoder = @import("../arch/riscv64/encoder.zig");
85const Encoding = @import("../arch/riscv64/Encoding.zig");
7186const mem = std.mem;
7287const std = @import("std");
7388
74pub const Instruction = bits.Instruction;
89pub const Instruction = encoder.Instruction;
src/register_manager.zig+1
......@@ -360,6 +360,7 @@ pub fn RegisterManager(
360360 } else self.getRegIndexAssumeFree(tracked_index, inst);
361361 }
362362 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {
363 log.debug("getting reg: {}", .{reg});
363364 return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst);
364365 }
365366 pub fn getKnownReg(
src/target.zig+1-1
......@@ -526,7 +526,7 @@ pub fn backendSupportsFeature(
526526 feature: Feature,
527527) bool {
528528 return switch (feature) {
529 .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64 or cpu_arch == .riscv64,
529 .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64,
530530 .panic_unwrap_error => ofmt == .c or use_llvm,
531531 .safety_check_formatted => ofmt == .c or use_llvm,
532532 .error_return_trace => use_llvm,
test/behavior/align.zig+17
......@@ -16,6 +16,7 @@ test "global variable alignment" {
1616}
1717
1818test "large alignment of local constant" {
19 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2021 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2122 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
......@@ -25,6 +26,7 @@ test "large alignment of local constant" {
2526}
2627
2728test "slicing array of length 1 can not assume runtime index is always zero" {
29 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
2830 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2931 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3032 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
......@@ -42,6 +44,7 @@ test "default alignment allows unspecified in type syntax" {
4244}
4345
4446test "implicitly decreasing pointer alignment" {
47 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
4548 const a: u32 align(4) = 3;
4649 const b: u32 align(8) = 4;
4750 try expect(addUnaligned(&a, &b) == 7);
......@@ -52,6 +55,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
5255}
5356
5457test "@alignCast pointers" {
58 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
5559 var x: u32 align(4) = 1;
5660 expectsOnly1(&x);
5761 try expect(x == 2);
......@@ -223,6 +227,7 @@ fn fnWithAlignedStack() i32 {
223227}
224228
225229test "implicitly decreasing slice alignment" {
230 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
226231 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
227232 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
228233
......@@ -235,6 +240,7 @@ fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
235240}
236241
237242test "specifying alignment allows pointer cast" {
243 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
238244 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
239245 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
240246
......@@ -247,6 +253,7 @@ fn testBytesAlign(b: u8) !void {
247253}
248254
249255test "@alignCast slices" {
256 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
250257 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
251258 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
252259 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -265,6 +272,7 @@ fn sliceExpects4(slice: []align(4) u32) void {
265272}
266273
267274test "return error union with 128-bit integer" {
275 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
268276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
269277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
270278 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -277,6 +285,7 @@ fn give() anyerror!u128 {
277285}
278286
279287test "page aligned array on stack" {
288 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
280289 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
281290 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
282291 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -418,6 +427,7 @@ test "function callconv expression depends on generic parameter" {
418427}
419428
420429test "runtime-known array index has best alignment possible" {
430 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
421431 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
422432
423433 // take full advantage of over-alignment
......@@ -478,6 +488,7 @@ const DefaultAligned = struct {
478488};
479489
480490test "read 128-bit field from default aligned struct in stack memory" {
491 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
481492 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
482493 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
483494 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -497,6 +508,7 @@ var default_aligned_global = DefaultAligned{
497508};
498509
499510test "read 128-bit field from default aligned struct in global memory" {
511 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
500512 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
501513 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
502514 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -506,6 +518,7 @@ test "read 128-bit field from default aligned struct in global memory" {
506518}
507519
508520test "struct field explicit alignment" {
521 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
509522 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
510523 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
511524 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -550,6 +563,7 @@ test "align(@alignOf(T)) T does not force resolution of T" {
550563}
551564
552565test "align(N) on functions" {
566 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
553567 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
554568 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
555569 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -595,6 +609,7 @@ test "comptime alloc alignment" {
595609}
596610
597611test "@alignCast null" {
612 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
598613 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
599614 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
600615
......@@ -610,6 +625,7 @@ test "alignment of slice element" {
610625}
611626
612627test "sub-aligned pointer field access" {
628 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
613629 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
614630 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
615631 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
......@@ -658,6 +674,7 @@ test "alignment of zero-bit types is respected" {
658674}
659675
660676test "zero-bit fields in extern struct pad fields appropriately" {
677 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
661678 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
662679 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
663680 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
test/behavior/array.zig+95
......@@ -7,6 +7,7 @@ const expect = testing.expect;
77const expectEqual = testing.expectEqual;
88
99test "array to slice" {
10 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1011 const a: u32 align(4) = 3;
1112 const b: u32 align(8) = 4;
1213 const a_slice: []align(1) const u32 = @as(*const [1]u32, &a)[0..];
......@@ -19,6 +20,8 @@ test "array to slice" {
1920}
2021
2122test "arrays" {
23 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
2225 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2326 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2427 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -47,6 +50,8 @@ fn getArrayLen(a: []const u32) usize {
4750}
4851
4952test "array concat with undefined" {
53 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
54 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
5055 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5156 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5257
......@@ -70,6 +75,8 @@ test "array concat with undefined" {
7075}
7176
7277test "array concat with tuple" {
78 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
79 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
7380 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7481 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7582 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -86,6 +93,8 @@ test "array concat with tuple" {
8693}
8794
8895test "array init with concat" {
96 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
97 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
8998 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9099
91100 const a = 'a';
......@@ -94,6 +103,8 @@ test "array init with concat" {
94103}
95104
96105test "array init with mult" {
106 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
107 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
97108 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
98109 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
99110
......@@ -106,6 +117,7 @@ test "array init with mult" {
106117}
107118
108119test "array literal with explicit type" {
120 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
109121 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
110122 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
111123
......@@ -116,6 +128,7 @@ test "array literal with explicit type" {
116128}
117129
118130test "array literal with inferred length" {
131 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
119132 const hex_mult = [_]u16{ 4096, 256, 16, 1 };
120133
121134 try expect(hex_mult.len == 4);
......@@ -123,6 +136,7 @@ test "array literal with inferred length" {
123136}
124137
125138test "array dot len const expr" {
139 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
126140 try expect(comptime x: {
127141 break :x some_array.len == 4;
128142 });
......@@ -134,6 +148,7 @@ const ArrayDotLenConstExpr = struct {
134148const some_array = [_]u8{ 0, 1, 2, 3 };
135149
136150test "array literal with specified size" {
151 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
137152 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
138153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
139154 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -145,6 +160,7 @@ test "array literal with specified size" {
145160}
146161
147162test "array len field" {
163 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
148164 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
149165
150166 var arr = [4]u8{ 0, 0, 0, 0 };
......@@ -157,6 +173,8 @@ test "array len field" {
157173}
158174
159175test "array with sentinels" {
176 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
177 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
160178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
161179 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
162180
......@@ -186,6 +204,7 @@ test "array with sentinels" {
186204}
187205
188206test "void arrays" {
207 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
189208 var array: [4]void = undefined;
190209 array[0] = void{};
191210 array[1] = array[2];
......@@ -194,6 +213,8 @@ test "void arrays" {
194213}
195214
196215test "nested arrays of strings" {
216 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
217 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
197218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
198219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
199220 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -209,6 +230,7 @@ test "nested arrays of strings" {
209230}
210231
211232test "nested arrays of integers" {
233 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
212234 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
213235 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
214236
......@@ -224,6 +246,8 @@ test "nested arrays of integers" {
224246}
225247
226248test "implicit comptime in array type size" {
249 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
250 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
227251 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
228252 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
229253
......@@ -237,6 +261,8 @@ fn plusOne(x: u32) u32 {
237261}
238262
239263test "single-item pointer to array indexing and slicing" {
264 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
265 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
240266 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
241267 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
242268
......@@ -263,6 +289,8 @@ fn doSomeMangling(array: *[4]u8) void {
263289}
264290
265291test "implicit cast zero sized array ptr to slice" {
292 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
293 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
266294 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
267295
268296 {
......@@ -278,6 +306,7 @@ test "implicit cast zero sized array ptr to slice" {
278306}
279307
280308test "anonymous list literal syntax" {
309 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
281310 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
282311 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
283312 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -300,6 +329,8 @@ var s_array: [8]Sub = undefined;
300329const Sub = struct { b: u8 };
301330const Str = struct { a: []Sub };
302331test "set global var array via slice embedded in struct" {
332 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
333 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
303334 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
304335 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
305336
......@@ -315,6 +346,8 @@ test "set global var array via slice embedded in struct" {
315346}
316347
317348test "read/write through global variable array of struct fields initialized via array mult" {
349 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
350 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
318351 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
319352 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
320353 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -336,6 +369,8 @@ test "read/write through global variable array of struct fields initialized via
336369}
337370
338371test "implicit cast single-item pointer" {
372 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
373 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
339374 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
340375 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
341376
......@@ -355,6 +390,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
355390}
356391
357392test "comptime evaluating function that takes array by value" {
393 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
358394 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
359395 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
360396
......@@ -366,6 +402,8 @@ test "comptime evaluating function that takes array by value" {
366402}
367403
368404test "runtime initialize array elem and then implicit cast to slice" {
405 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
406 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
369407 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
370408 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
371409
......@@ -376,6 +414,8 @@ test "runtime initialize array elem and then implicit cast to slice" {
376414}
377415
378416test "array literal as argument to function" {
417 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
418 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
379419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
380420 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
381421
......@@ -403,6 +443,8 @@ test "array literal as argument to function" {
403443}
404444
405445test "double nested array to const slice cast in array literal" {
446 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
447 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
406448 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
407449 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
408450 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -464,6 +506,7 @@ test "double nested array to const slice cast in array literal" {
464506}
465507
466508test "anonymous literal in array" {
509 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
467510 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
468511 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
469512 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -490,6 +533,8 @@ test "anonymous literal in array" {
490533}
491534
492535test "access the null element of a null terminated array" {
536 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
537 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
493538 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
494539 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
495540
......@@ -508,6 +553,8 @@ test "access the null element of a null terminated array" {
508553}
509554
510555test "type deduction for array subscript expression" {
556 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
557 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
511558 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
512559 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
513560 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -527,6 +574,8 @@ test "type deduction for array subscript expression" {
527574}
528575
529576test "sentinel element count towards the ABI size calculation" {
577 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
578 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
530579 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
531580 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
532581 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -551,6 +600,8 @@ test "sentinel element count towards the ABI size calculation" {
551600}
552601
553602test "zero-sized array with recursive type definition" {
603 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
604 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
554605 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
555606 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
556607 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
......@@ -574,6 +625,8 @@ test "zero-sized array with recursive type definition" {
574625}
575626
576627test "type coercion of anon struct literal to array" {
628 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
629 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
577630 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
578631 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
579632 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -608,6 +661,8 @@ test "type coercion of anon struct literal to array" {
608661}
609662
610663test "type coercion of pointer to anon struct literal to pointer to array" {
664 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
665 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
611666 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
612667 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
613668 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -642,12 +697,16 @@ test "type coercion of pointer to anon struct literal to pointer to array" {
642697}
643698
644699test "array with comptime-only element type" {
700 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
701 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
645702 const a = [_]type{ u32, i32 };
646703 try testing.expect(a[0] == u32);
647704 try testing.expect(a[1] == i32);
648705}
649706
650707test "tuple to array handles sentinel" {
708 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
709 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
651710 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
652711 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
653712 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -660,6 +719,8 @@ test "tuple to array handles sentinel" {
660719}
661720
662721test "array init of container level array variable" {
722 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
723 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
663724 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
664725 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
665726 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -683,6 +744,8 @@ test "array init of container level array variable" {
683744}
684745
685746test "runtime initialized sentinel-terminated array literal" {
747 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
748 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
686749 var c: u16 = 300;
687750 _ = &c;
688751 const f = &[_:0x9999]u16{c};
......@@ -692,6 +755,8 @@ test "runtime initialized sentinel-terminated array literal" {
692755}
693756
694757test "array of array agregate init" {
758 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
759 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
695760 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
696761 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
697762 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -703,6 +768,8 @@ test "array of array agregate init" {
703768}
704769
705770test "pointer to array has ptr field" {
771 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
772 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
706773 const arr: *const [5]u32 = &.{ 10, 20, 30, 40, 50 };
707774 try std.testing.expect(arr.ptr == @as([*]const u32, arr));
708775 try std.testing.expect(arr.ptr[0] == 10);
......@@ -713,6 +780,8 @@ test "pointer to array has ptr field" {
713780}
714781
715782test "discarded array init preserves result location" {
783 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
784 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
716785 const S = struct {
717786 fn f(p: *u32) u16 {
718787 p.* += 1;
......@@ -731,6 +800,8 @@ test "discarded array init preserves result location" {
731800}
732801
733802test "array init with no result location has result type" {
803 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
804 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
734805 const x = .{ .foo = [2]u16{
735806 @intCast(10),
736807 @intCast(20),
......@@ -742,6 +813,8 @@ test "array init with no result location has result type" {
742813}
743814
744815test "slicing array of zero-sized values" {
816 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
817 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
745818 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
746819 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
747820 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
......@@ -754,6 +827,8 @@ test "slicing array of zero-sized values" {
754827}
755828
756829test "array init with no result pointer sets field result types" {
830 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
831 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
757832 const S = struct {
758833 // A function parameter has a result type, but no result pointer.
759834 fn f(arr: [1]u32) u32 {
......@@ -768,6 +843,8 @@ test "array init with no result pointer sets field result types" {
768843}
769844
770845test "runtime side-effects in comptime-known array init" {
846 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
847 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
771848 var side_effects: u4 = 0;
772849 const init = [4]u4{
773850 blk: {
......@@ -792,6 +869,8 @@ test "runtime side-effects in comptime-known array init" {
792869}
793870
794871test "slice initialized through reference to anonymous array init provides result types" {
872 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
873 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
795874 var my_u32: u32 = 123;
796875 var my_u64: u64 = 456;
797876 _ = .{ &my_u32, &my_u64 };
......@@ -851,6 +930,8 @@ test "many-item sentinel-terminated pointer initialized through reference to ano
851930}
852931
853932test "pointer to array initialized through reference to anonymous array init provides result types" {
933 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
934 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
854935 var my_u32: u32 = 123;
855936 var my_u64: u64 = 456;
856937 _ = .{ &my_u32, &my_u64 };
......@@ -877,6 +958,8 @@ test "pointer to sentinel-terminated array initialized through reference to anon
877958}
878959
879960test "tuple initialized through reference to anonymous array init provides result types" {
961 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
962 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
880963 const Tuple = struct { u64, *const u32 };
881964 const foo: *const Tuple = &.{
882965 @intCast(12345),
......@@ -887,6 +970,8 @@ test "tuple initialized through reference to anonymous array init provides resul
887970}
888971
889972test "copied array element doesn't alias source" {
973 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
974 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
890975 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
891976 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
892977 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -901,6 +986,8 @@ test "copied array element doesn't alias source" {
901986}
902987
903988test "array initialized with string literal" {
989 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
990 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
904991 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
905992
906993 const S = struct {
......@@ -921,6 +1008,8 @@ test "array initialized with string literal" {
9211008}
9221009
9231010test "array initialized with array with sentinel" {
1011 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1012 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
9241013 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9251014
9261015 const S = struct {
......@@ -941,6 +1030,8 @@ test "array initialized with array with sentinel" {
9411030}
9421031
9431032test "store array of array of structs at comptime" {
1033 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1034 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
9441035 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9451036 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9461037 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -966,6 +1057,8 @@ test "store array of array of structs at comptime" {
9661057}
9671058
9681059test "accessing multidimensional global array at comptime" {
1060 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1061 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
9691062 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
9701063 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9711064 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -982,6 +1075,8 @@ test "accessing multidimensional global array at comptime" {
9821075}
9831076
9841077test "union that needs padding bytes inside an array" {
1078 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1079 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
9851080 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9861081 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9871082 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO