| ... | ... | @@ -85049,10 +85049,132 @@ fn airShlShrBinOp(self: *CodeGen, inst: Air.Inst.Index) !void { |
| 85049 | 85049 | } |
| 85050 | 85050 | |
| 85051 | 85051 | fn airShlSat(self: *CodeGen, inst: Air.Inst.Index) !void { |
| 85052 | const zcu = self.pt.zcu; |
| 85052 | 85053 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 85053 | | _ = bin_op; |
| 85054 | | return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 85055 | | //return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 85054 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 85055 | const rhs_ty = self.typeOf(bin_op.rhs); |
| 85056 | |
| 85057 | const result: MCValue = result: { |
| 85058 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 85059 | .int => { |
| 85060 | const lhs_bits = lhs_ty.bitSize(zcu); |
| 85061 | const rhs_bits = rhs_ty.bitSize(zcu); |
| 85062 | if (!(lhs_bits <= 32 and rhs_bits <= 5) and !(lhs_bits > 32 and lhs_bits <= 64 and rhs_bits <= 6) and !(rhs_bits <= std.math.log2(lhs_bits))) { |
| 85063 | return self.fail("TODO implement shl_sat for {} with lhs bits {}, rhs bits {}", .{ self.target.cpu.arch, lhs_bits, rhs_bits }); |
| 85064 | } |
| 85065 | |
| 85066 | // clobberred by genShiftBinOp |
| 85067 | try self.spillRegisters(&.{.rcx}); |
| 85068 | |
| 85069 | const lhs_mcv = try self.resolveInst(bin_op.lhs); |
| 85070 | var lhs_temp1 = try self.tempInit(lhs_ty, lhs_mcv); |
| 85071 | const rhs_mcv = try self.resolveInst(bin_op.rhs); |
| 85072 | |
| 85073 | const lhs_lock = switch (lhs_mcv) { |
| 85074 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 85075 | else => null, |
| 85076 | }; |
| 85077 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 85078 | |
| 85079 | // shift left |
| 85080 | const dst_mcv = try self.genShiftBinOp(.shl, null, lhs_mcv, rhs_mcv, lhs_ty, rhs_ty); |
| 85081 | switch (dst_mcv) { |
| 85082 | .register => |dst_reg| try self.truncateRegister(lhs_ty, dst_reg), |
| 85083 | .register_pair => |dst_regs| try self.truncateRegister(lhs_ty, dst_regs[1]), |
| 85084 | .load_frame => |frame_addr| { |
| 85085 | const tmp_reg = |
| 85086 | try self.register_manager.allocReg(null, abi.RegisterClass.gp); |
| 85087 | const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 85088 | defer self.register_manager.unlockReg(tmp_lock); |
| 85089 | |
| 85090 | const lhs_bits_u31: u31 = @intCast(lhs_bits); |
| 85091 | const tmp_ty: Type = if (lhs_bits_u31 > 64) .usize else lhs_ty; |
| 85092 | const off = frame_addr.off + (lhs_bits_u31 - 1) / 64 * 8; |
| 85093 | try self.genSetReg( |
| 85094 | tmp_reg, |
| 85095 | tmp_ty, |
| 85096 | .{ .load_frame = .{ .index = frame_addr.index, .off = off } }, |
| 85097 | .{}, |
| 85098 | ); |
| 85099 | try self.truncateRegister(lhs_ty, tmp_reg); |
| 85100 | try self.genSetMem( |
| 85101 | .{ .frame = frame_addr.index }, |
| 85102 | off, |
| 85103 | tmp_ty, |
| 85104 | .{ .register = tmp_reg }, |
| 85105 | .{}, |
| 85106 | ); |
| 85107 | }, |
| 85108 | else => {}, |
| 85109 | } |
| 85110 | const dst_lock = switch (dst_mcv) { |
| 85111 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 85112 | else => null, |
| 85113 | }; |
| 85114 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 85115 | |
| 85116 | // shift right |
| 85117 | const tmp_mcv = try self.genShiftBinOp(.shr, null, dst_mcv, rhs_mcv, lhs_ty, rhs_ty); |
| 85118 | var tmp_temp = try self.tempInit(lhs_ty, tmp_mcv); |
| 85119 | |
| 85120 | // check if overflow happens |
| 85121 | const cc_temp = lhs_temp1.cmpInts(.neq, &tmp_temp, self) catch |err| switch (err) { |
| 85122 | error.SelectFailed => unreachable, |
| 85123 | else => |e| return e, |
| 85124 | }; |
| 85125 | try lhs_temp1.die(self); |
| 85126 | try tmp_temp.die(self); |
| 85127 | const overflow_reloc = try self.genCondBrMir(lhs_ty, cc_temp.tracking(self).short); |
| 85128 | try cc_temp.die(self); |
| 85129 | |
| 85130 | // if overflow, |
| 85131 | // for unsigned integers, the saturating result is just its max |
| 85132 | // for signed integers, |
| 85133 | // if lhs is positive, the result is its max |
| 85134 | // if lhs is negative, it is min |
| 85135 | switch (lhs_ty.intInfo(zcu).signedness) { |
| 85136 | .unsigned => { |
| 85137 | const bound_mcv = try self.genTypedValue(try lhs_ty.maxIntScalar(self.pt, lhs_ty)); |
| 85138 | try self.genCopy(lhs_ty, dst_mcv, bound_mcv, .{}); |
| 85139 | }, |
| 85140 | .signed => { |
| 85141 | // check the sign of lhs |
| 85142 | // TODO: optimize this. |
| 85143 | // we only need the highest bit so shifting the highest part of lhs_mcv |
| 85144 | // is enough to check the signedness. other parts can be skipped here. |
| 85145 | var lhs_temp2 = try self.tempInit(lhs_ty, lhs_mcv); |
| 85146 | var zero_temp = try self.tempInit(lhs_ty, try self.genTypedValue(try self.pt.intValue(lhs_ty, 0))); |
| 85147 | const sign_cc_temp = lhs_temp2.cmpInts(.lt, &zero_temp, self) catch |err| switch (err) { |
| 85148 | error.SelectFailed => unreachable, |
| 85149 | else => |e| return e, |
| 85150 | }; |
| 85151 | try lhs_temp2.die(self); |
| 85152 | try zero_temp.die(self); |
| 85153 | const sign_reloc_condbr = try self.genCondBrMir(lhs_ty, sign_cc_temp.tracking(self).short); |
| 85154 | try sign_cc_temp.die(self); |
| 85155 | |
| 85156 | // if it is negative |
| 85157 | const min_mcv = try self.genTypedValue(try lhs_ty.minIntScalar(self.pt, lhs_ty)); |
| 85158 | try self.genCopy(lhs_ty, dst_mcv, min_mcv, .{}); |
| 85159 | const sign_reloc_br = try self.asmJmpReloc(undefined); |
| 85160 | self.performReloc(sign_reloc_condbr); |
| 85161 | |
| 85162 | // if it is positive |
| 85163 | const max_mcv = try self.genTypedValue(try lhs_ty.maxIntScalar(self.pt, lhs_ty)); |
| 85164 | try self.genCopy(lhs_ty, dst_mcv, max_mcv, .{}); |
| 85165 | self.performReloc(sign_reloc_br); |
| 85166 | }, |
| 85167 | } |
| 85168 | |
| 85169 | self.performReloc(overflow_reloc); |
| 85170 | break :result dst_mcv; |
| 85171 | }, |
| 85172 | else => { |
| 85173 | return self.fail("TODO implement shl_sat for {} op type {}", .{ self.target.cpu.arch, lhs_ty.zigTypeTag(zcu) }); |
| 85174 | }, |
| 85175 | } |
| 85176 | }; |
| 85177 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 85056 | 85178 | } |
| 85057 | 85179 | |
| 85058 | 85180 | fn airOptionalPayload(self: *CodeGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -88437,7 +88559,7 @@ fn genShiftBinOpMir( |
| 88437 | 88559 | ) !void { |
| 88438 | 88560 | const pt = self.pt; |
| 88439 | 88561 | const zcu = pt.zcu; |
| 88440 | | const abi_size: u32 = @intCast(lhs_ty.abiSize(zcu)); |
| 88562 | const abi_size: u31 = @intCast(lhs_ty.abiSize(zcu)); |
| 88441 | 88563 | const shift_abi_size: u32 = @intCast(rhs_ty.abiSize(zcu)); |
| 88442 | 88564 | try self.spillEflagsIfOccupied(); |
| 88443 | 88565 | |
| ... | ... | @@ -88621,7 +88743,17 @@ fn genShiftBinOpMir( |
| 88621 | 88743 | .immediate => {}, |
| 88622 | 88744 | else => self.performReloc(skip), |
| 88623 | 88745 | } |
| 88624 | | } |
| 88746 | } else try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[2].to64(), .{ |
| 88747 | .base = .{ .frame = lhs_mcv.load_frame.index }, |
| 88748 | .mod = .{ .rm = .{ |
| 88749 | .size = .qword, |
| 88750 | .disp = switch (tag[0]) { |
| 88751 | ._l => lhs_mcv.load_frame.off, |
| 88752 | ._r => lhs_mcv.load_frame.off + abi_size - 8, |
| 88753 | else => unreachable, |
| 88754 | }, |
| 88755 | } }, |
| 88756 | }); |
| 88625 | 88757 | switch (rhs_mcv) { |
| 88626 | 88758 | .immediate => |shift_imm| try self.asmRegisterImmediate( |
| 88627 | 88759 | tag, |