Skip to content

Commit 978ae46

Browse files
committed
implement checked_binop
1 parent de7ba8e commit 978ae46

File tree

1 file changed

+70
-18
lines changed

1 file changed

+70
-18
lines changed

crates/rustc_codegen_spirv/src/builder/builder_methods.rs

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,28 +1318,80 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
13181318
fn checked_binop(
13191319
&mut self,
13201320
oop: OverflowOp,
1321-
_ty: Ty<'_>,
1321+
ty: Ty<'_>,
13221322
lhs: Self::Value,
13231323
rhs: Self::Value,
13241324
) -> (Self::Value, Self::Value) {
1325-
// NOTE(eddyb) this needs to be `undef`, not `false`/`true`, because
1326-
// we don't want the user's boolean constants to keep the zombie alive.
1327-
let bool = SpirvType::Bool.def(self.span(), self);
1328-
let overflowed = self.undef(bool);
1329-
let result = match oop {
1330-
OverflowOp::Add => (self.add(lhs, rhs), overflowed),
1331-
OverflowOp::Sub => (self.sub(lhs, rhs), overflowed),
1332-
OverflowOp::Mul => (self.mul(lhs, rhs), overflowed),
1325+
// adopted partially from https://github.com/ziglang/zig/blob/master/src/codegen/spirv.zig
1326+
let is_add = match oop {
1327+
OverflowOp::Add => true,
1328+
OverflowOp::Sub => false,
1329+
OverflowOp::Mul => {
1330+
// NOTE(eddyb) this needs to be `undef`, not `false`/`true`, because
1331+
// we don't want the user's boolean constants to keep the zombie alive.
1332+
let bool = SpirvType::Bool.def(self.span(), self);
1333+
let overflowed = self.undef(bool);
1334+
1335+
let result = (self.mul(lhs, rhs), overflowed);
1336+
self.zombie(result.1.def(self), "checked mul is not supported yet");
1337+
return result;
1338+
}
13331339
};
1334-
self.zombie(
1335-
result.1.def(self),
1336-
match oop {
1337-
OverflowOp::Add => "checked add is not supported yet",
1338-
OverflowOp::Sub => "checked sub is not supported yet",
1339-
OverflowOp::Mul => "checked mul is not supported yet",
1340-
},
1341-
);
1342-
result
1340+
let signed = match ty.kind() {
1341+
ty::Int(_) => true,
1342+
ty::Uint(_) => false,
1343+
other => self.fatal(format!(
1344+
"Unexpected {} type: {other:#?}",
1345+
match oop {
1346+
OverflowOp::Add => "checked add",
1347+
OverflowOp::Sub => "checked sub",
1348+
OverflowOp::Mul => "checked mul",
1349+
}
1350+
)),
1351+
};
1352+
1353+
let result = if is_add {
1354+
self.add(lhs, rhs)
1355+
} else {
1356+
self.sub(lhs, rhs)
1357+
};
1358+
1359+
let overflowed = if signed {
1360+
// when adding, overflow could happen if
1361+
// - rhs is positive and result < lhs; or
1362+
// - rhs is negative and result > lhs
1363+
// this is equivalent to (rhs < 0) == (result > lhs)
1364+
//
1365+
// when subtracting, overflow happens if
1366+
// - rhs is positive and result > lhs; or
1367+
// - rhs is negative and result < lhs
1368+
// this is equivalent to (rhs < 0) == (result < lhs)
1369+
let rhs_lt_zero = self.icmp(IntPredicate::IntSLT, rhs, self.constant_int(rhs.ty, 0));
1370+
let result_gt_lhs = self.icmp(
1371+
if is_add {
1372+
IntPredicate::IntSGT
1373+
} else {
1374+
IntPredicate::IntSLT
1375+
},
1376+
result,
1377+
lhs,
1378+
);
1379+
self.icmp(IntPredicate::IntEQ, rhs_lt_zero, result_gt_lhs)
1380+
} else {
1381+
// for unsigned addition, overflow occured if the result is less than any of the operands.
1382+
// for subtraction, overflow occured if the result is greater.
1383+
self.icmp(
1384+
if is_add {
1385+
IntPredicate::IntULT
1386+
} else {
1387+
IntPredicate::IntUGT
1388+
},
1389+
result,
1390+
lhs,
1391+
)
1392+
};
1393+
1394+
(result, overflowed)
13431395
}
13441396

13451397
// rustc has the concept of an immediate vs. memory type - bools are compiled to LLVM bools as

0 commit comments

Comments
 (0)