Skip to content

Commit

Permalink
riscv: switch progress + by-ref return progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexicon226 committed Jun 13, 2024
1 parent 4aa1544 commit 004d0c8
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 55 deletions.
159 changes: 148 additions & 11 deletions src/arch/riscv64/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {

.field_parent_ptr => try self.airFieldParentPtr(inst),

.switch_br => try self.airSwitch(inst),
.switch_br => try self.airSwitchBr(inst),
.slice_ptr => try self.airSlicePtr(inst),
.slice_len => try self.airSliceLen(inst),

Expand Down Expand Up @@ -1960,7 +1960,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 => {
.Int, .Enum => {
assert(lhs_ty.eql(rhs_ty, zcu));
const int_info = lhs_ty.intInfo(zcu);
if (int_info.bits <= 64) {
Expand Down Expand Up @@ -3682,7 +3682,6 @@ 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);
Expand Down Expand Up @@ -4160,12 +4159,97 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
self.finishAirBookkeeping();
}

fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
const condition = pl_op.operand;
_ = condition;
return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
// return self.finishAir(inst, .dead, .{ condition, .none, .none });
const condition = try self.resolveInst(pl_op.operand);
const condition_ty = self.typeOf(pl_op.operand);
const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
var extra_index: usize = switch_br.end;
var case_i: u32 = 0;
const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.data.cases_len + 1);
defer self.gpa.free(liveness.deaths);

// If the condition dies here in this switch instruction, process
// that death now instead of later as this has an effect on
// whether it needs to be spilled in the branches
if (self.liveness.operandDies(inst, 0)) {
if (pl_op.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
}

self.scope_generation += 1;
const state = try self.saveState();

while (case_i < switch_br.data.cases_len) : (case_i += 1) {
const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
const items: []const Air.Inst.Ref =
@ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
const case_body: []const Air.Inst.Index =
@ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
extra_index = case.end + items.len + case_body.len;

var relocs = try self.gpa.alloc(Mir.Inst.Index, items.len);
defer self.gpa.free(relocs);

for (items, relocs, 0..) |item, *reloc, i| {
// switch branches must be comptime-known, so this is stored in an immediate
const item_mcv = try self.resolveInst(item);

const cmp_mcv: MCValue = try self.binOp(
.cmp_neq,
condition,
condition_ty,
item_mcv,
condition_ty,
);

const cmp_reg = try self.copyToTmpRegister(Type.bool, cmp_mcv);

if (!(i < relocs.len - 1)) {
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_not,
.data = .{ .rr = .{
.rd = cmp_reg,
.rs = cmp_reg,
} },
});
}

reloc.* = try self.condBr(condition_ty, .{ .register = cmp_reg });
}

for (liveness.deaths[case_i]) |operand| try self.processDeath(operand);

for (relocs[0 .. relocs.len - 1]) |reloc| self.performReloc(reloc);
try self.genBody(case_body);
try self.restoreState(state, &.{}, .{
.emit_instructions = false,
.update_tracking = true,
.resurrect = true,
.close_scope = true,
});

self.performReloc(relocs[relocs.len - 1]);
}

if (switch_br.data.else_body_len > 0) {
const else_body: []const Air.Inst.Index =
@ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);

const else_deaths = liveness.deaths.len - 1;
for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand);

try self.genBody(else_body);
try self.restoreState(state, &.{}, .{
.emit_instructions = false,
.update_tracking = true,
.resurrect = true,
.close_scope = true,
});
}

// We already took care of pl_op.operand earlier, so there's nothing left to do
self.finishAirBookkeeping();
}

fn performReloc(self: *Self, inst: Mir.Inst.Index) void {
Expand Down Expand Up @@ -4249,9 +4333,60 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {

fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const air_tags = self.air.instructions.items(.tag);
_ = air_tags;
const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
const tag: Air.Inst.Tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];

const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
const lhs_ty = Type.bool;
const rhs_ty = Type.bool;

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 result_reg, const result_lock = try self.allocReg();
defer self.register_manager.unlockReg(result_lock);

_ = try self.addInst(.{
.tag = if (tag == .bool_or) .@"or" else .@"and",
.ops = .rrr,
.data = .{ .r_type = .{
.rd = result_reg,
.rs1 = lhs_reg,
.rs2 = rhs_reg,
} },
});

// safety truncate
if (self.wantSafety()) {
_ = try self.addInst(.{
.tag = .andi,
.ops = .rri,
.data = .{ .i_type = .{
.rd = result_reg,
.rs1 = result_reg,
.imm12 = Immediate.s(1),
} },
});
}

break :result .{ .register = result_reg };
};
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}

Expand Down Expand Up @@ -5265,7 +5400,9 @@ fn resolveCallingConventionValues(
},
.memory => {
const param_int_regs = abi.function_arg_regs;

const param_int_reg = param_int_regs[param_int_reg_i];
param_int_reg_i += 1;

arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } };
arg_mcv_i += 1;
Expand Down
3 changes: 3 additions & 0 deletions src/arch/riscv64/Encoding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub const Mnemonic = enum {
// R Type
add,
@"and",
@"or",
sub,
slt,
mul,
Expand All @@ -55,6 +56,7 @@ pub const Mnemonic = enum {
.add => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0000000 },
.sltu => .{ .opcode = 0b0110011, .funct3 = 0b011, .funct7 = 0b0000000 },
.@"and" => .{ .opcode = 0b0110011, .funct3 = 0b111, .funct7 = 0b0000000 },
.@"or" => .{ .opcode = 0b0110011, .funct3 = 0b110, .funct7 = 0b0000000 },
.sub => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0100000 },

.ld => .{ .opcode = 0b0000011, .funct3 = 0b011, .funct7 = null },
Expand Down Expand Up @@ -152,6 +154,7 @@ pub const InstEnc = enum {
.add,
.sub,
.@"and",
.@"or",
=> .R,

.ecall,
Expand Down
3 changes: 0 additions & 3 deletions src/arch/riscv64/Mir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ pub const Inst = struct {
/// Branch if not equal, Uses b_type
bne,

/// Boolean NOT, Uses rr payload
not,

/// Generates a NO-OP, uses nop payload
nop,

Expand Down
1 change: 0 additions & 1 deletion test/behavior/align.zig
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,6 @@ test "alignment of slice element" {
}

test "sub-aligned pointer field access" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
Expand Down
1 change: 0 additions & 1 deletion test/behavior/cast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,6 @@ test "peer resolution of string literals" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

const S = struct {
const E = enum { a, b, c, d };
Expand Down
2 changes: 0 additions & 2 deletions test/behavior/enum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,6 @@ fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
test "enum with specified tag values" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

try testEnumWithSpecifiedTagValues(MultipleChoice.C);
try comptime testEnumWithSpecifiedTagValues(MultipleChoice.C);
Expand Down Expand Up @@ -749,7 +748,6 @@ test "cast integer literal to enum" {
test "enum with specified and unspecified tag values" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
try comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
Expand Down
5 changes: 0 additions & 5 deletions test/behavior/eval.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,6 @@ test "comptime break operand passing through runtime condition converted to runt
test "comptime break operand passing through runtime switch converted to runtime break" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

const S = struct {
fn doTheTest(runtime: u8) !void {
Expand Down Expand Up @@ -1631,8 +1630,6 @@ test "struct in comptime false branch is not evaluated" {
}

test "result of nested switch assigned to variable" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var zds: u32 = 0;
zds = switch (zds) {
0 => switch (zds) {
Expand Down Expand Up @@ -1667,8 +1664,6 @@ test "inline for loop of functions returning error unions" {
}

test "if inside a switch" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var condition = true;
var wave_type: u32 = 0;
_ = .{ &condition, &wave_type };
Expand Down
6 changes: 0 additions & 6 deletions test/behavior/inline_switch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const builtin = @import("builtin");
test "inline scalar prongs" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var x: usize = 0;
switch (x) {
Expand All @@ -21,7 +20,6 @@ test "inline scalar prongs" {
test "inline prong ranges" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var x: usize = 0;
_ = &x;
Expand All @@ -37,7 +35,6 @@ const E = enum { a, b, c, d };
test "inline switch enums" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var x: E = .a;
_ = &x;
Expand Down Expand Up @@ -106,7 +103,6 @@ test "inline else error" {
test "inline else enum" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

const E2 = enum(u8) { a = 2, b = 3, c = 4, d = 5 };
var a: E2 = .a;
Expand All @@ -120,7 +116,6 @@ test "inline else enum" {
test "inline else int with gaps" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var a: u8 = 0;
_ = &a;
Expand All @@ -139,7 +134,6 @@ test "inline else int with gaps" {
test "inline else int all values" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

var a: u2 = 0;
_ = &a;
Expand Down
1 change: 0 additions & 1 deletion test/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ test "reference a variable in an if after an if in the 2nd switch prong" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

try foo(true, Num.Two, false, "aoeu");
try expect(!ok);
Expand Down
Loading

0 comments on commit 004d0c8

Please sign in to comment.