authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-03-14 15:34:06-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
log3ccf0fd4c2d024d696bdb1e71a0b36af38ad6bed
treeab05699caca1a13fd5888e75a3296b2426aaf5b2
parent2be3033acda53389cac9f3e9a8ca0a3d41348eef

riscv: basic struct field access

the current implementation only works when the struct is in a register. we use some shifting magic to get the field into the LSB, and from there, given the type provenance, the generated code should never reach into the bits beyond the bit size of the type and interact with the rest of the struct.

4 files changed, 88 insertions(+), 15 deletions(-)

lib/compiler/test_runner.zig+3-1
...@@ -12,7 +12,9 @@ var cmdline_buffer: [4096]u8 = undefined;...@@ -12,7 +12,9 @@ var cmdline_buffer: [4096]u8 = undefined;
12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1313
14pub fn main() void {14pub fn main() void {
15 if (builtin.zig_backend == .stage2_aarch64) {15 if (builtin.zig_backend == .stage2_aarch64 or
16 builtin.zig_backend == .stage2_riscv64)
17 {
16 return mainSimple() catch @panic("test failure");18 return mainSimple() catch @panic("test failure");
17 }19 }
1820
src/arch/riscv64/CodeGen.zig+78-14
...@@ -1586,11 +1586,53 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty:...@@ -1586,11 +1586,53 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty:
15861586
1587fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {1587fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1588 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;1588 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1589 _ = ty_pl;1589 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1590 const operand = extra.struct_operand;
1591 const index = extra.field_index;
1592 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1593 const mod = self.bin_file.comp.module.?;
1594 const src_mcv = try self.resolveInst(operand);
1595 const struct_ty = self.typeOf(operand);
1596 const field_ty = struct_ty.structFieldType(index, mod);
1597 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) break :result .none;
1598
1599 const field_off = @as(u32, @intCast(struct_ty.structFieldOffset(index, mod)));
1600
1601 switch (src_mcv) {
1602 .dead, .unreach => unreachable,
1603 .register => |src_reg| {
1604 const src_reg_lock = self.register_manager.lockRegAssumeUnused(src_reg);
1605 defer self.register_manager.unlockReg(src_reg_lock);
1606
1607 const dst_reg = if (field_off == 0)
1608 (try self.copyToNewRegister(inst, src_mcv)).register
1609 else
1610 try self.copyToTmpRegister(Type.usize, .{ .register = src_reg });
1611
1612 const dst_mcv: MCValue = .{ .register = dst_reg };
1613 const dst_lock = self.register_manager.lockReg(dst_reg);
1614 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
1615
1616 if (field_off > 0) {
1617 _ = try self.addInst(.{
1618 .tag = .srli,
1619 .data = .{
1620 .i_type = .{
1621 .imm12 = @intCast(field_off),
1622 .rd = dst_reg,
1623 .rs1 = dst_reg,
1624 },
1625 },
1626 });
1627 }
15901628
1591 return self.fail("TODO: airStructFieldVal", .{});1629 break :result if (field_off == 0) dst_mcv else try self.copyToNewRegister(inst, dst_mcv);
1630 },
1631 else => return self.fail("TODO: airStructField {s}", .{@tagName(src_mcv)}),
1632 }
1633 };
15921634
1593 // return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });1635 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
1594}1636}
15951637
1596fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {1638fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1626,8 +1668,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1626,8 +1668,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1626 self.arg_index = arg_index + 1;1668 self.arg_index = arg_index + 1;
16271669
1628 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {1670 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
1629 const arg_ty = self.typeOfIndex(inst);
1630 _ = arg_ty;
1631 const src_mcv = self.args[arg_index];1671 const src_mcv = self.args[arg_index];
16321672
1633 const dst_mcv = switch (src_mcv) {1673 const dst_mcv = switch (src_mcv) {
...@@ -2471,12 +2511,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2471,12 +2511,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2471 }2511 }
2472 },2512 },
2473 .stack_offset, .load_symbol => {2513 .stack_offset, .load_symbol => {
2474 if (true)2514 switch (src_val) {
2475 return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)});2515 .stack_offset => |off| if (off == stack_offset) return,
2516 else => {},
2517 }
24762518
2477 if (abi_size <= 8) {2519 if (abi_size <= 8) {
2478 const reg = try self.copyToTmpRegister(ty, src_val);2520 const reg = try self.copyToTmpRegister(ty, src_val);
2479 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });2521 return self.genSetStack(ty, stack_offset, .{ .register = reg });
2480 }2522 }
24812523
2482 const ptr_ty = try mod.singleMutPtrType(ty);2524 const ptr_ty = try mod.singleMutPtrType(ty);
...@@ -2496,7 +2538,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2496,7 +2538,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
24962538
2497 switch (src_val) {2539 switch (src_val) {
2498 .stack_offset => |offset| {2540 .stack_offset => |offset| {
2499 if (offset == stack_offset) return;
2500 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });2541 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });
2501 },2542 },
2502 .load_symbol => |sym_off| {2543 .load_symbol => |sym_off| {
...@@ -2553,11 +2594,34 @@ fn genInlineMemcpy(...@@ -2553,11 +2594,34 @@ fn genInlineMemcpy(
2553) !void {2594) !void {
2554 _ = src;2595 _ = src;
2555 _ = dst;2596 _ = dst;
2556 _ = len;
2557 _ = count;
2558 _ = tmp;
25592597
2560 return self.fail("TODO: genInlineMemcpy", .{});2598 // store 0 in the count
2599 try self.genSetReg(Type.usize, count, .{ .immediate = 0 });
2600
2601 // compare count to length
2602 const compare_inst = try self.addInst(.{
2603 .tag = .cmp_gt,
2604 .data = .{ .r_type = .{
2605 .rd = tmp,
2606 .rs1 = count,
2607 .rs2 = len,
2608 } },
2609 });
2610
2611 // end if true
2612 _ = try self.addInst(.{
2613 .tag = .bne,
2614 .data = .{
2615 .b_type = .{
2616 .inst = @intCast(self.mir_instructions.len + 0), // points after the last inst
2617 .rs1 = .zero,
2618 .rs2 = tmp,
2619 },
2620 },
2621 });
2622 _ = compare_inst;
2623
2624 return self.fail("TODO: finish genInlineMemcpy", .{});
2561}2625}
25622626
2563/// Sets the value of `src_val` into `reg`. Assumes you have a lock on it.2627/// Sets the value of `src_val` into `reg`. Assumes you have a lock on it.
...@@ -2567,7 +2631,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -2567,7 +2631,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
25672631
2568 switch (src_val) {2632 switch (src_val) {
2569 .dead => unreachable,2633 .dead => unreachable,
2570 .ptr_stack_offset => return self.fail("TODO genSetReg ptr_stack_offset", .{}),2634 .ptr_stack_offset => |off| try self.genSetReg(ty, reg, .{ .stack_offset = off }),
2571 .unreach, .none => return, // Nothing to do.2635 .unreach, .none => return, // Nothing to do.
2572 .undef => {2636 .undef => {
2573 if (!self.wantSafety())2637 if (!self.wantSafety())
src/arch/riscv64/Emit.zig+4
...@@ -98,6 +98,8 @@ pub fn emitMir(...@@ -98,6 +98,8 @@ pub fn emitMir(
98 .sh => try emit.mirIType(inst),98 .sh => try emit.mirIType(inst),
99 .sb => try emit.mirIType(inst),99 .sb => try emit.mirIType(inst),
100100
101 .srli => try emit.mirIType(inst),
102
101 .ldr_ptr_stack => try emit.mirIType(inst),103 .ldr_ptr_stack => try emit.mirIType(inst),
102104
103 .load_symbol => try emit.mirLoadSymbol(inst),105 .load_symbol => try emit.mirLoadSymbol(inst),
...@@ -224,6 +226,8 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -224,6 +226,8 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
224 try emit.writeInstruction(Instruction.subw(i_type.rs1, i_type.rs1, i_type.rd));226 try emit.writeInstruction(Instruction.subw(i_type.rs1, i_type.rs1, i_type.rd));
225 },227 },
226228
229 .srli => try emit.writeInstruction(Instruction.srli(i_type.rd, i_type.rs1, @intCast(i_type.imm12))),
230
227 else => unreachable,231 else => unreachable,
228 }232 }
229}233}
src/arch/riscv64/Mir.zig+3
...@@ -41,6 +41,9 @@ pub const Inst = struct {...@@ -41,6 +41,9 @@ pub const Inst = struct {
41 /// Absolute Value, uses i_type payload.41 /// Absolute Value, uses i_type payload.
42 abs,42 abs,
4343
44 /// Logical Right Shift, uses i_type payload
45 srli,
46
44 jal,47 jal,
45 /// Jumps. Uses `inst` payload.48 /// Jumps. Uses `inst` payload.
46 j,49 j,