riscv: PRO member function calls

this is enough progress for us to be able to call `stdout.write`!
This commit is contained in:
David Rubin 2024-05-02 02:07:28 -07:00
parent c457f35da5
commit 55b28c7e44
No known key found for this signature in database
GPG key ID: C326E694CED89F6D
2 changed files with 291 additions and 79 deletions

View file

@ -932,6 +932,25 @@ fn gen(self: *Self) !void {
const backpatch_fp_add = try self.addPseudo(.pseudo_dead);
const backpatch_spill_callee_preserved_regs = try self.addPseudo(.pseudo_dead);
switch (self.ret_mcv.long) {
.none, .unreach => {},
.indirect => {
// The address where to store the return value for the caller is in a
// register which the callee is free to clobber. Therefore, we purposely
// spill it to stack immediately.
const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(Type.usize, mod));
try self.genSetMem(
.{ .frame = frame_index },
0,
Type.usize,
self.ret_mcv.long.address().offset(-self.ret_mcv.short.indirect.off),
);
self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } };
tracking_log.debug("spill {} to {}", .{ self.ret_mcv.long, frame_index });
},
else => unreachable,
}
try self.genBody(self.air.getMainBody());
for (self.exitlude_jump_relocs.items) |jmp_reloc| {
@ -1306,12 +1325,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
while (it.next()) |index| {
const tracked_inst = self.register_manager.registers[index];
tracking_log.debug("tracked inst: {}", .{tracked_inst});
const tracking = self.getResolvedInstValue(tracked_inst);
for (tracking.getRegs()) |reg| {
if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break;
} else return self.fail(
\\%{} takes up these regs: {any}, however those regs don't use it
, .{ index, tracking.getRegs() });
} else return std.debug.panic(
\\%{} takes up these regs: {any}, however these regs {any}, don't use it
, .{ tracked_inst, tracking.getRegs(), RegisterManager.regAtTrackedIndex(@intCast(index)) });
}
}
}
@ -1540,7 +1560,7 @@ fn symbolIndex(self: *Self) !u32 {
const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
break :blk atom_index;
},
else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}),
else => return self.fail("TODO symbolIndex {s}", .{@tagName(self.bin_file.tag)}),
};
}
@ -1961,7 +1981,7 @@ fn binOp(
switch (lhs_ty.zigTypeTag(zcu)) {
.Float => return self.fail("TODO binary operations on floats", .{}),
.Vector => return self.fail("TODO binary operations on vectors", .{}),
.Int, .Enum => {
.Int, .Enum, .ErrorSet => {
assert(lhs_ty.eql(rhs_ty, zcu));
const int_info = lhs_ty.intInfo(zcu);
if (int_info.bits <= 64) {
@ -2304,8 +2324,127 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
}
fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
_ = inst;
return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
const zcu = self.bin_file.comp.module.?;
const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
const lhs = try self.resolveInst(extra.lhs);
const rhs = try self.resolveInst(extra.rhs);
const lhs_ty = self.typeOf(extra.lhs);
const rhs_ty = self.typeOf(extra.rhs);
const int_info = lhs_ty.intInfo(zcu);
if (!math.isPowerOfTwo(int_info.bits) or !(int_info.bits >= 8)) {
return self.fail("TODO: airSubWithOverflow non-power of 2 and less than 8 bits", .{});
}
const tuple_ty = self.typeOfIndex(inst);
const result_mcv = try self.allocRegOrMem(inst, false);
const offset = result_mcv.load_frame;
const lhs_reg, const lhs_lock = blk: {
if (lhs == .register) break :blk .{ lhs.register, null };
const lhs_reg, const lhs_lock = try self.allocReg();
try self.genSetReg(lhs_ty, lhs_reg, lhs);
break :blk .{ lhs_reg, lhs_lock };
};
defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
const rhs_reg, const rhs_lock = blk: {
if (rhs == .register) break :blk .{ rhs.register, null };
const rhs_reg, const rhs_lock = try self.allocReg();
try self.genSetReg(rhs_ty, rhs_reg, rhs);
break :blk .{ rhs_reg, rhs_lock };
};
defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
const dest_reg, const dest_lock = try self.allocReg();
defer self.register_manager.unlockReg(dest_lock);
switch (int_info.signedness) {
.unsigned => return self.fail("TODO: airSubWithOverflow unsigned", .{}),
.signed => {
switch (int_info.bits) {
64 => {
// result
_ = try self.addInst(.{
.tag = .sub,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = dest_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});
try self.genSetMem(
.{ .frame = offset.index },
offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))),
lhs_ty,
.{ .register = dest_reg },
);
// overflow check
const overflow_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = 0 });
_ = try self.addInst(.{
.tag = .slt,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = overflow_reg,
.rs1 = overflow_reg,
.rs2 = rhs_reg,
} },
});
_ = try self.addInst(.{
.tag = .slt,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = rhs_reg,
.rs1 = rhs_reg,
.rs2 = lhs_reg,
} },
});
_ = try self.addInst(.{
.tag = .xor,
.ops = .rrr,
.data = .{ .r_type = .{
.rd = lhs_reg,
.rs1 = overflow_reg,
.rs2 = rhs_reg,
} },
});
const overflow_mcv = try self.binOp(
.cmp_neq,
.{ .register = overflow_reg },
Type.usize,
.{ .register = rhs_reg },
Type.usize,
);
try self.genSetMem(
.{ .frame = offset.index },
offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))),
Type.u1,
overflow_mcv,
);
break :result result_mcv;
},
else => |int_bits| return self.fail("TODO: airSubWithOverflow signed {}", .{int_bits}),
}
},
}
};
return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
}
fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
@ -2644,8 +2783,25 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
/// T to E!T
fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
const zcu = self.bin_file.comp.module.?;
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
const eu_ty = ty_op.ty.toType();
const pl_ty = eu_ty.errorUnionPayload(zcu);
const err_ty = eu_ty.errorUnionSet(zcu);
const operand = try self.resolveInst(ty_op.operand);
const result: MCValue = result: {
if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .{ .immediate = 0 };
const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(eu_ty, zcu));
const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
try self.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, operand);
try self.genSetMem(.{ .frame = frame_index }, err_off, err_ty, .{ .immediate = 0 });
break :result .{ .load_frame = .{ .index = frame_index } };
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
@ -3361,8 +3517,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
.rs1 = dst_reg,
} },
});
return self.fail("TODO: airStructFieldVal register with field_off > 0", .{});
}
break :result if (field_off == 0) dst_mcv else try self.copyToNewRegister(inst, dst_mcv);
@ -3444,7 +3598,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
}
fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const zcu = self.bin_file.comp.module.?;
var arg_index = self.arg_index;
// we skip over args that have no bits
@ -3453,31 +3606,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
const src_mcv = self.args[arg_index];
const arg_ty = self.typeOfIndex(inst);
const dst_mcv = switch (src_mcv) {
.register => dst: {
const frame = try self.allocFrameIndex(FrameAlloc.init(.{
.size = Type.usize.abiSize(zcu),
.alignment = Type.usize.abiAlignment(zcu),
}));
const dst_mcv: MCValue = .{ .load_frame = .{ .index = frame } };
try self.genCopy(Type.usize, dst_mcv, src_mcv);
break :dst dst_mcv;
},
.register_pair => dst: {
const frame = try self.allocFrameIndex(FrameAlloc.init(.{
.size = Type.usize.abiSize(zcu) * 2,
.alignment = Type.usize.abiAlignment(zcu),
}));
const dst_mcv: MCValue = .{ .load_frame = .{ .index = frame } };
const dst_mcv = try self.allocRegOrMem(inst, false);
try self.genCopy(arg_ty, dst_mcv, src_mcv);
break :dst dst_mcv;
},
.load_frame => src_mcv,
else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
};
try self.genArgDbgInfo(inst, src_mcv);
break :result dst_mcv;
@ -3612,7 +3744,68 @@ fn genCall(
stack_frame_align.* = stack_frame_align.max(needed_call_frame.abi_align);
}
for (call_info.args, 0..) |mc_arg, arg_i| try self.genCopy(arg_tys[arg_i], mc_arg, args[arg_i]);
var reg_locks = std.ArrayList(?RegisterLock).init(allocator);
defer reg_locks.deinit();
try reg_locks.ensureTotalCapacity(8);
defer for (reg_locks.items) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
const frame_indices = try allocator.alloc(FrameIndex, args.len);
defer allocator.free(frame_indices);
switch (call_info.return_value.long) {
.none, .unreach => {},
.indirect => |reg_off| try self.register_manager.getReg(reg_off.reg, null),
else => unreachable,
}
for (call_info.args, args, arg_tys, frame_indices) |dst_arg, src_arg, arg_ty, *frame_index| {
switch (dst_arg) {
.none => {},
.register => |reg| {
try self.register_manager.getReg(reg, null);
try reg_locks.append(self.register_manager.lockReg(reg));
},
.register_pair => |regs| {
for (regs) |reg| try self.register_manager.getReg(reg, null);
try reg_locks.appendSlice(&self.register_manager.lockRegs(2, regs));
},
.indirect => |reg_off| {
frame_index.* = try self.allocFrameIndex(FrameAlloc.initType(arg_ty, zcu));
try self.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg);
try self.register_manager.getReg(reg_off.reg, null);
try reg_locks.append(self.register_manager.lockReg(reg_off.reg));
},
else => return self.fail("TODO: genCall set arg {s}", .{@tagName(dst_arg)}),
}
}
switch (call_info.return_value.long) {
.none, .unreach => {},
.indirect => |reg_off| {
const ret_ty = Type.fromInterned(fn_info.return_type);
const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(ret_ty, zcu));
try self.genSetReg(Type.usize, reg_off.reg, .{
.lea_frame = .{ .index = frame_index, .off = -reg_off.off },
});
call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } };
try reg_locks.append(self.register_manager.lockReg(reg_off.reg));
},
else => unreachable,
}
for (call_info.args, arg_tys, args, frame_indices) |dst_arg, arg_ty, src_arg, frame_index| {
switch (dst_arg) {
.register_pair => try self.genCopy(arg_ty, dst_arg, src_arg),
.register => |dst_reg| try self.genSetReg(
arg_ty,
dst_reg,
src_arg,
),
.indirect => |reg_off| try self.genSetReg(Type.usize, reg_off.reg, .{
.lea_frame = .{ .index = frame_index, .off = -reg_off.off },
}),
else => return self.fail("TODO: genCall actual set {s}", .{@tagName(dst_arg)}),
}
}
// Due to incremental compilation, how function calls are generated depends
// on linking.
@ -3715,9 +3908,10 @@ fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
defer self.register_manager.unlockReg(lock);
try self.genSetReg(Type.usize, reg_off.reg, self.ret_mcv.long);
try self.genCopy(
try self.genSetMem(
.{ .reg = reg_off.reg },
reg_off.off,
ret_ty,
.{ .register_offset = reg_off },
.{ .air_ref = un_op },
);
},
@ -3745,6 +3939,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
switch (self.ret_mcv.short) {
.none => {},
.register, .register_pair => try self.load(self.ret_mcv.short, ptr, ptr_ty),
.indirect => |reg_off| try self.genSetReg(ptr_ty, reg_off.reg, ptr),
else => unreachable,
}
self.ret_mcv.liveOut(self, inst);
@ -4058,7 +4253,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue)
_ = maybe_inst;
const err_off = errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu);
const err_off: u31 = @intCast(errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu));
switch (eu_mcv) {
.register => |reg| {
@ -4081,15 +4276,25 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue)
);
}
return_mcv = try self.binOp(
return try self.binOp(
.cmp_neq,
return_mcv,
Type.u16,
.{ .immediate = 0 },
Type.u16,
);
return return_mcv;
},
.load_frame => |frame_addr| {
return self.binOp(
.cmp_neq,
.{ .load_frame = .{
.index = frame_addr.index,
.off = frame_addr.off + err_off,
} },
Type.anyerror,
.{ .immediate = 0 },
Type.anyerror,
);
},
else => return self.fail("TODO implement isErr for {}", .{eu_mcv}),
}
@ -4839,7 +5044,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
const zcu = self.bin_file.comp.module.?;
const abi_size: u32 = @intCast(ty.abiSize(zcu));
if (abi_size > 8) return self.fail("tried to set reg with size {}", .{abi_size});
if (abi_size > 8) return std.debug.panic("tried to set reg with size {}", .{abi_size});
switch (src_mcv) {
.dead => unreachable,
@ -4924,7 +5129,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
if (src_reg.id() == reg.id())
return;
// mov reg, src_reg
// mv reg, src_reg
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_mv,
@ -4934,20 +5139,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
} },
});
},
.register_pair => |pair| try self.genSetReg(ty, reg, .{ .register = pair[0] }),
.memory => |addr| {
try self.genSetReg(ty, reg, .{ .immediate = addr });
_ = try self.addInst(.{
.tag = .ld,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = reg,
.imm12 = Immediate.s(0),
} },
});
},
.register_pair => return self.fail("genSetReg should we allow reg -> reg_pair?", .{}),
.load_frame => |frame| {
_ = try self.addInst(.{
.tag = .pseudo,
@ -4966,13 +5158,36 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
} },
});
},
.lea_frame => |frame| {
.memory => |addr| {
try self.genSetReg(ty, reg, .{ .immediate = addr });
_ = try self.addInst(.{
.tag = .ld,
.ops = .rri,
.data = .{ .i_type = .{
.rd = reg,
.rs1 = reg,
.imm12 = Immediate.s(0),
} },
});
},
.lea_frame, .register_offset => {
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_lea_rm,
.data = .{ .rm = .{
.r = reg,
.m = .{
.m = switch (src_mcv) {
.register_offset => |reg_off| .{
.base = .{ .reg = reg_off.reg },
.mod = .{
.rm = .{
.size = self.memSize(ty),
.disp = reg_off.off,
},
},
},
.lea_frame => |frame| .{
.base = .{ .frame = frame.index },
.mod = .{
.rm = .{
@ -4981,13 +5196,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
},
},
},
else => unreachable,
},
} },
});
},
.load_symbol => {
try self.genSetReg(ty, reg, src_mcv.address());
try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = reg } });
},
.indirect => |reg_off| {
const load_tag: Mir.Inst.Tag = switch (abi_size) {
1 => .lb,
@ -5022,6 +5235,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
}) },
});
},
.load_symbol => {
try self.genSetReg(ty, reg, src_mcv.address());
try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = reg } });
},
.air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)),
else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}),
}
@ -5052,7 +5269,6 @@ fn genSetMem(
src_mcv,
.{ .immediate = abi_size },
),
.register_offset,
.memory,
.indirect,
@ -5100,14 +5316,15 @@ fn genSetMem(
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_store_rm,
.data = .{
.rm = .{ .r = reg, .m = .{
.data = .{ .rm = .{
.r = reg,
.m = .{
.base = .{ .frame = frame_index },
.mod = .{ .rm = .{
.size = Memory.Size.fromByteSize(src_size),
} },
} },
},
} },
});
try self.genSetMem(base, disp, ty, frame_mcv);
try self.freeValue(frame_mcv);

View file

@ -149,12 +149,7 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
// anyerror!void can fit into one register
if (payload_bits == 0) return result;
if (payload_bits <= 64) {
result[1] = .integer;
return result;
}
std.debug.panic("TODO: classifySystem ErrorUnion > 64 bit payload", .{});
return memory_class;
},
.Struct => {
const layout = ty.containerLayout(zcu);