Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit e7a5078

Browse files
committed
Implement real saturating behaviour for the saturating_* intrinsics with unsigned ints
1 parent 6414f03 commit e7a5078

File tree

2 files changed

+117
-37
lines changed

2 files changed

+117
-37
lines changed

src/common.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,74 @@ pub fn resolve_value_imm(func: &Function, val: Value) -> Option<u128> {
116116
}
117117
}
118118

119+
pub fn type_min_max_value<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (i64, i64) {
120+
use syntax::ast::UintTy::*;
121+
use syntax::ast::IntTy::*;
122+
123+
let uint_usize_cvt = |uint| {
124+
match uint {
125+
UintTy::Usize => match pointer_ty(tcx) {
126+
types::I16 => UintTy::U16,
127+
types::I32 => UintTy::U32,
128+
types::I64 => UintTy::U64,
129+
ty => unreachable!("{:?}", ty),
130+
}
131+
_ => uint,
132+
}
133+
};
134+
135+
let int_isize_cvt = |int| {
136+
match int {
137+
IntTy::Isize => match pointer_ty(tcx) {
138+
types::I16 => IntTy::I16,
139+
types::I32 => IntTy::I32,
140+
types::I64 => IntTy::I64,
141+
ty => unreachable!("{:?}", ty),
142+
}
143+
_ => int,
144+
}
145+
};
146+
147+
let min = match ty.sty {
148+
ty::Uint(uint) => match uint_usize_cvt(uint) {
149+
U8 | U16 | U32 | U64 => 0i64,
150+
U128 => unimplemented!(),
151+
Usize => unreachable!(),
152+
}
153+
ty::Int(int) => match int_isize_cvt(int) {
154+
I8 => i8::min_value() as i64,
155+
I16 => i16::min_value() as i64,
156+
I32 => i32::min_value() as i64,
157+
I64 => i64::min_value(),
158+
I128 => unimplemented!(),
159+
Isize => unreachable!(),
160+
}
161+
_ => unreachable!(),
162+
};
163+
164+
let max = match ty.sty {
165+
ty::Uint(uint) => match uint_usize_cvt(uint) {
166+
U8 => u8::max_value() as i64,
167+
U16 => u16::max_value() as i64,
168+
U32 => u32::max_value() as i64,
169+
U64 => u64::max_value() as i64,
170+
U128 => unimplemented!(),
171+
Usize => unreachable!(),
172+
}
173+
ty::Int(int) => match int_isize_cvt(int) {
174+
I8 => i8::max_value() as i64,
175+
I16 => i16::max_value() as i64,
176+
I32 => i32::max_value() as i64,
177+
I64 => i64::max_value(),
178+
I128 => unimplemented!(),
179+
Isize => unreachable!(),
180+
}
181+
_ => unreachable!(),
182+
};
183+
184+
(min, max)
185+
}
186+
119187
pub struct FunctionCx<'a, 'tcx: 'a, B: Backend> {
120188
// FIXME use a reference to `CodegenCx` instead of `tcx`, `module` and `constants` and `caches`
121189
pub tcx: TyCtxt<'tcx>,

src/intrinsics.rs

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -470,25 +470,21 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
470470
"mul_with_overflow" => BinOp::Mul,
471471
_ => unimplemented!("intrinsic {}", intrinsic),
472472
};
473-
let res = match T.sty {
474-
ty::Uint(_) => crate::base::trans_checked_int_binop(
475-
fx,
476-
bin_op,
477-
x,
478-
y,
479-
ret.layout().ty,
480-
false,
481-
),
482-
ty::Int(_) => crate::base::trans_checked_int_binop(
483-
fx,
484-
bin_op,
485-
x,
486-
y,
487-
ret.layout().ty,
488-
true,
489-
),
490-
_ => panic!(),
473+
474+
let signed = match T.sty {
475+
ty::Uint(_) => false,
476+
ty::Int(_) => true,
477+
_ => unimplemented!("{} for {:?}", intrinsic, T),
491478
};
479+
480+
let res = crate::base::trans_checked_int_binop(
481+
fx,
482+
bin_op,
483+
x,
484+
y,
485+
ret.layout().ty,
486+
signed,
487+
);
492488
ret.write_cvalue(fx, res);
493489
};
494490
_ if intrinsic.starts_with("overflowing_"), <T> (c x, c y) {
@@ -526,28 +522,44 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
526522
let bin_op = match intrinsic {
527523
"saturating_add" => BinOp::Add,
528524
"saturating_sub" => BinOp::Sub,
529-
"saturating_mul" => BinOp::Mul,
530525
_ => unimplemented!("intrinsic {}", intrinsic),
531526
};
532-
let res = match T.sty {
533-
ty::Uint(_) => crate::base::trans_int_binop(
534-
fx,
535-
bin_op,
536-
x,
537-
y,
538-
ret.layout().ty,
539-
false,
540-
),
541-
ty::Int(_) => crate::base::trans_int_binop(
542-
fx,
543-
bin_op,
544-
x,
545-
y,
546-
ret.layout().ty,
547-
true,
548-
),
549-
_ => panic!(),
527+
528+
let signed = match T.sty {
529+
ty::Uint(_) => false,
530+
ty::Int(_) => true,
531+
_ => unimplemented!("{} for {:?}", intrinsic, T),
550532
};
533+
534+
let checked_res = crate::base::trans_checked_int_binop(
535+
fx,
536+
bin_op,
537+
x,
538+
y,
539+
fx.tcx.mk_tup([T, fx.tcx.types.bool].into_iter()),
540+
signed,
541+
);
542+
543+
let (val, has_overflow) = checked_res.load_scalar_pair(fx);
544+
let clif_ty = fx.clif_type(T).unwrap();
545+
546+
// `select.i8` is not implemented by Cranelift.
547+
let has_overflow = fx.bcx.ins().uextend(types::I32, has_overflow);
548+
549+
let (min, max) = type_min_max_value(fx.tcx, T);
550+
let min = fx.bcx.ins().iconst(clif_ty, min);
551+
let max = fx.bcx.ins().iconst(clif_ty, max);
552+
553+
let val = match (intrinsic, signed) {
554+
("saturating_add", false) => fx.bcx.ins().select(has_overflow, max, val),
555+
("saturating_sub", false) => fx.bcx.ins().select(has_overflow, min, val),
556+
("saturating_add", true) => unimplemented!(),
557+
("saturating_sub", true) => unimplemented!(),
558+
_ => unreachable!(),
559+
};
560+
561+
let res = CValue::by_val(val, fx.layout_of(T));
562+
551563
ret.write_cvalue(fx, res);
552564
};
553565
rotate_left, <T>(v x, v y) {

0 commit comments

Comments
 (0)