Skip to content

Commit ec2df79

Browse files
committed
x86_64: Implement integer saturating left shifting codegen
Simliarly to shl_with_overflow, we first SHL/SAL the integer, then SHR/SAR it back to compare if overflow happens. If overflow happened, set result to the upper limit to make it saturating. Bug: ziglang#17645
1 parent f38d7a9 commit ec2df79

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

Diff for: src/arch/x86_64/CodeGen.zig

+78-3
Original file line numberDiff line numberDiff line change
@@ -12868,10 +12868,85 @@ fn airShlShrBinOp(self: *CodeGen, inst: Air.Inst.Index) !void {
1286812868
}
1286912869

1287012870
fn airShlSat(self: *CodeGen, inst: Air.Inst.Index) !void {
12871+
const zcu = self.pt.zcu;
1287112872
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
12872-
_ = bin_op;
12873-
return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
12874-
//return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12873+
const lhs_ty = self.typeOf(bin_op.lhs);
12874+
const rhs_ty = self.typeOf(bin_op.rhs);
12875+
12876+
const result: MCValue = result: {
12877+
switch (lhs_ty.zigTypeTag(zcu)) {
12878+
.int => {
12879+
try self.spillRegisters(&.{.rcx});
12880+
try self.register_manager.getKnownReg(.rcx, null);
12881+
const lhs_mcv = try self.resolveInst(bin_op.lhs);
12882+
const rhs_mcv = try self.resolveInst(bin_op.rhs);
12883+
12884+
// 1. shift left
12885+
const dst_mcv = try self.genShiftBinOp(Air.Inst.Tag.shl, null, lhs_mcv, rhs_mcv, lhs_ty, rhs_ty);
12886+
switch (dst_mcv) {
12887+
.register => |dst_reg| try self.truncateRegister(lhs_ty, dst_reg),
12888+
.register_pair => |dst_regs| try self.truncateRegister(lhs_ty, dst_regs[1]),
12889+
.load_frame => |frame_addr| {
12890+
const tmp_reg =
12891+
try self.register_manager.allocReg(null, abi.RegisterClass.gp);
12892+
const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
12893+
defer self.register_manager.unlockReg(tmp_lock);
12894+
12895+
const lhs_bits: u31 = @intCast(lhs_ty.bitSize(zcu));
12896+
const tmp_ty: Type = if (lhs_bits > 64) .usize else lhs_ty;
12897+
const off = frame_addr.off + (lhs_bits - 1) / 64 * 8;
12898+
try self.genSetReg(
12899+
tmp_reg,
12900+
tmp_ty,
12901+
.{ .load_frame = .{ .index = frame_addr.index, .off = off } },
12902+
.{},
12903+
);
12904+
try self.truncateRegister(lhs_ty, tmp_reg);
12905+
try self.genSetMem(
12906+
.{ .frame = frame_addr.index },
12907+
off,
12908+
tmp_ty,
12909+
.{ .register = tmp_reg },
12910+
.{},
12911+
);
12912+
},
12913+
else => {},
12914+
}
12915+
const dst_lock = switch (dst_mcv) {
12916+
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
12917+
else => null,
12918+
};
12919+
defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
12920+
12921+
// 2. shift right
12922+
const tmp_mcv = try self.genShiftBinOp(Air.Inst.Tag.shr, null, dst_mcv, rhs_mcv, lhs_ty, rhs_ty);
12923+
const tmp_lock = switch (tmp_mcv) {
12924+
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
12925+
else => null,
12926+
};
12927+
defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
12928+
12929+
// 3. check if overflow happens
12930+
try self.genBinOpMir(.{ ._, .cmp }, lhs_ty, tmp_mcv, lhs_mcv);
12931+
const reloc = try self.genCondBrMir(lhs_ty, .{ .eflags = Condition.ne });
12932+
12933+
// 4. if overflow, set the value to upper limit
12934+
const bound_val = try lhs_ty.maxIntScalar(self.pt, lhs_ty);
12935+
const bound_mcv = try self.genTypedValue(bound_val);
12936+
switch (dst_mcv) {
12937+
.register, .register_pair, .load_frame, .memory => try self.genCopy(lhs_ty, dst_mcv, bound_mcv, CopyOptions{}),
12938+
else => return self.fail("TODO implement shl_sat for {} dest type {}", .{ self.target.cpu.arch, lhs_ty.zigTypeTag(zcu) }),
12939+
}
12940+
12941+
self.performReloc(reloc);
12942+
break :result dst_mcv;
12943+
},
12944+
else => {
12945+
return self.fail("TODO implement shl_sat for {} op type {}", .{ self.target.cpu.arch, lhs_ty.zigTypeTag(zcu) });
12946+
},
12947+
}
12948+
};
12949+
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1287512950
}
1287612951

1287712952
fn airOptionalPayload(self: *CodeGen, inst: Air.Inst.Index) !void {

Diff for: test/behavior/bit_shifting.zig

-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ test "comptime shift safety check" {
111111
}
112112

113113
test "Saturating Shift Left where lhs is of a computed type" {
114-
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
115114
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
116115
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
117116
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

0 commit comments

Comments
 (0)