authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-21 22:01:07+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-22 21:56:34+01:00
log87a0d754be9a26aeabb90f25d2971d263680fe9e
tree7c07b06a7adc78ade9871130922cf2ef326d250b
parent2d3462c79a09f7c2abf96b79910663067a7bb91e

x64: add basic, naive impl of switch


2 files changed, 144 insertions(+), 5 deletions(-)

src/arch/x86_64/CodeGen.zig+142-4
......@@ -3797,12 +3797,150 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
37973797 return self.finishAir(inst, result, .{ .none, .none, .none });
37983798}
37993799
3800fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u32 {
3801 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
3802 switch (condition) {
3803 .none => unreachable,
3804 .undef => unreachable,
3805 .dead, .unreach => unreachable,
3806 .compare_flags_signed => unreachable,
3807 .compare_flags_unsigned => unreachable,
3808 .register => |cond_reg| {
3809 switch (case) {
3810 .none => unreachable,
3811 .undef => unreachable,
3812 .dead, .unreach => unreachable,
3813 .immediate => |imm| {
3814 _ = try self.addInst(.{
3815 .tag = .@"test",
3816 .ops = (Mir.Ops{
3817 .reg1 = registerAlias(cond_reg, abi_size),
3818 }).encode(),
3819 .data = .{ .imm = @intCast(u32, imm) },
3820 });
3821 return self.addInst(.{
3822 .tag = .cond_jmp_eq_ne,
3823 .ops = (Mir.Ops{
3824 .flags = 0b00,
3825 }).encode(),
3826 .data = .{ .inst = undefined },
3827 });
3828 },
3829 .register => |reg| {
3830 _ = try self.addInst(.{
3831 .tag = .@"test",
3832 .ops = (Mir.Ops{
3833 .reg1 = registerAlias(cond_reg, abi_size),
3834 .reg2 = registerAlias(reg, abi_size),
3835 }).encode(),
3836 .data = undefined,
3837 });
3838 return self.addInst(.{
3839 .tag = .cond_jmp_eq_ne,
3840 .ops = (Mir.Ops{
3841 .flags = 0b00,
3842 }).encode(),
3843 .data = .{ .inst = undefined },
3844 });
3845 },
3846 .stack_offset => {
3847 if (abi_size <= 8) {
3848 const reg = try self.copyToTmpRegister(ty, case);
3849 return self.genCondSwitchMir(ty, condition, .{ .register = reg });
3850 }
3851
3852 return self.fail("TODO implement switch mir when case is stack offset with abi larger than 8 bytes", .{});
3853 },
3854 else => {
3855 return self.fail("TODO implement switch mir when case is {}", .{case});
3856 },
3857 }
3858 },
3859 else => {
3860 return self.fail("TODO implemenent switch mir when condition is {}", .{condition});
3861 },
3862 }
3863}
3864
38003865fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
38013866 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3802 const condition = pl_op.operand;
3803 _ = condition;
3804 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
3805 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
3867 const condition = try self.resolveInst(pl_op.operand);
3868 const condition_ty = self.air.typeOf(pl_op.operand);
3869 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
3870 var extra_index: usize = switch_br.end;
3871 var case_i: u32 = 0;
3872 const liveness = try self.liveness.getSwitchBr(
3873 self.gpa,
3874 inst,
3875 switch_br.data.cases_len + 1,
3876 );
3877 defer self.gpa.free(liveness.deaths);
3878
3879 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
3880 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
3881 const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
3882 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
3883 extra_index = case.end + items.len + case_body.len;
3884
3885 var relocs = try self.gpa.alloc(u32, items.len);
3886 defer self.gpa.free(relocs);
3887
3888 for (items) |item, item_i| {
3889 const item_mcv = try self.resolveInst(item);
3890 relocs[item_i] = try self.genCondSwitchMir(condition_ty, condition, item_mcv);
3891 }
3892
3893 // Capture the state of register and stack allocation state so that we can revert to it.
3894 const parent_next_stack_offset = self.next_stack_offset;
3895 const parent_free_registers = self.register_manager.free_registers;
3896 var parent_stack = try self.stack.clone(self.gpa);
3897 defer parent_stack.deinit(self.gpa);
3898 const parent_registers = self.register_manager.registers;
3899
3900 try self.branch_stack.append(.{});
3901 errdefer {
3902 _ = self.branch_stack.pop();
3903 }
3904
3905 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
3906 for (liveness.deaths[case_i]) |operand| {
3907 self.processDeath(operand);
3908 }
3909
3910 try self.genBody(case_body);
3911
3912 // Revert to the previous register and stack allocation state.
3913 var saved_case_branch = self.branch_stack.pop();
3914 defer saved_case_branch.deinit(self.gpa);
3915
3916 self.register_manager.registers = parent_registers;
3917 self.stack.deinit(self.gpa);
3918 self.stack = parent_stack;
3919 parent_stack = .{};
3920
3921 self.next_stack_offset = parent_next_stack_offset;
3922 self.register_manager.free_registers = parent_free_registers;
3923
3924 for (relocs) |reloc| {
3925 try self.performReloc(reloc);
3926 }
3927 }
3928
3929 if (switch_br.data.else_body_len > 0) {
3930 return self.fail("TODO handle else branch in switch", .{});
3931 }
3932
3933 // const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
3934 // const else_branch = self.branch_stack.addOneAssumeCapacity();
3935 // else_branch.* = .{};
3936
3937 // if (else_body.len != 0) {
3938 // try self.genBody(else_body);
3939 // } else {
3940
3941 // }
3942
3943 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
38063944}
38073945
38083946fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
src/arch/x86_64/Mir.zig+2-1
......@@ -316,7 +316,8 @@ pub const Inst = struct {
316316 syscall,
317317
318318 /// ops flags: form:
319 /// 0b00 reg1, imm32
319 /// 0b00 reg1, imm32 if reg2 == .none
320 /// 0b00 reg1, reg2
320321 /// TODO handle more cases
321322 @"test",
322323