Skip to content

Commit 87652fd

Browse files
committed
Fix UB in 128 bit cttz/ctlz intrinsics
1 parent 33966cc commit 87652fd

File tree

4 files changed

+102
-44
lines changed

4 files changed

+102
-44
lines changed

src/builder.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,55 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
444444
);
445445
result.to_rvalue()
446446
}
447+
/// Performs a *bitcount* intrinsic on a given arg, properly handling the zero case.
448+
pub fn checked_bitcount_intrinsic(
449+
&mut self,
450+
name: rustc_span::Symbol,
451+
arg: RValue<'gcc>,
452+
width: u64,
453+
) -> RValue<'gcc> {
454+
// For u/i128, the algorithm implementing `ctlz`/`cttz` already handles the 0 case, by handling it for each half.
455+
if width == 128 {
456+
return match name {
457+
sym::ctlz => self.count_leading_zeroes(width, arg),
458+
sym::cttz => self.count_trailing_zeroes(width, arg),
459+
_ => unreachable!(),
460+
};
461+
}
462+
use rustc_span::sym;
463+
let func = self.current_func.borrow().expect("func");
464+
let then_block = func.new_block("then");
465+
let else_block = func.new_block("else");
466+
let after_block = func.new_block("after");
467+
468+
let result = func.new_local(None, self.u32_type, "zeros");
469+
let zero = self.cx.gcc_zero(arg.get_type());
470+
let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero);
471+
self.llbb().end_with_conditional(None, cond, then_block, else_block);
472+
473+
let zero_result = self.cx.gcc_uint(self.u32_type, width);
474+
then_block.add_assignment(None, result, zero_result);
475+
then_block.end_with_jump(None, after_block);
476+
477+
// NOTE: since jumps were added in a place
478+
// count_leading_zeroes() does not expect, the current block
479+
// in the state need to be updated.
480+
self.switch_to_block(else_block);
481+
482+
let zeros = match name {
483+
sym::ctlz => self.count_leading_zeroes(width, arg),
484+
sym::cttz => self.count_trailing_zeroes(width, arg),
485+
_ => unreachable!(),
486+
};
487+
self.llbb().add_assignment(None, result, zeros);
488+
self.llbb().end_with_jump(None, after_block);
489+
490+
// NOTE: since jumps were added in a place rustc does not
491+
// expect, the current block in the state need to be updated.
492+
self.switch_to_block(after_block);
493+
494+
result.to_rvalue()
495+
}
447496
}
448497

449498
impl<'tcx> HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> {

src/intrinsic/mod.rs

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -224,39 +224,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
224224
match int_type_width_signed(ty, self) {
225225
Some((width, signed)) => match name {
226226
sym::ctlz | sym::cttz => {
227-
let func = self.current_func.borrow().expect("func");
228-
let then_block = func.new_block("then");
229-
let else_block = func.new_block("else");
230-
let after_block = func.new_block("after");
231-
232-
let arg = args[0].immediate();
233-
let result = func.new_local(None, self.u32_type, "zeros");
234-
let zero = self.cx.gcc_zero(arg.get_type());
235-
let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero);
236-
self.llbb().end_with_conditional(None, cond, then_block, else_block);
237-
238-
let zero_result = self.cx.gcc_uint(self.u32_type, width);
239-
then_block.add_assignment(None, result, zero_result);
240-
then_block.end_with_jump(None, after_block);
241-
242-
// NOTE: since jumps were added in a place
243-
// count_leading_zeroes() does not expect, the current block
244-
// in the state need to be updated.
245-
self.switch_to_block(else_block);
246-
247-
let zeros = match name {
248-
sym::ctlz => self.count_leading_zeroes(width, arg),
249-
sym::cttz => self.count_trailing_zeroes(width, arg),
250-
_ => unreachable!(),
251-
};
252-
self.llbb().add_assignment(None, result, zeros);
253-
self.llbb().end_with_jump(None, after_block);
254-
255-
// NOTE: since jumps were added in a place rustc does not
256-
// expect, the current block in the state need to be updated.
257-
self.switch_to_block(after_block);
258-
259-
result.to_rvalue()
227+
self.checked_bitcount_intrinsic(name, args[0].immediate(), width)
260228
}
261229
sym::ctlz_nonzero => self.count_leading_zeroes(width, args[0].immediate()),
262230
sym::cttz_nonzero => self.count_trailing_zeroes(width, args[0].immediate()),
@@ -712,7 +680,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
712680
self.gcc_int_cast(result, result_type)
713681
}
714682

715-
fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
683+
pub fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
716684
// TODO(antoyo): use width?
717685
let arg_type = arg.get_type();
718686
let result_type = self.u32_type;
@@ -743,15 +711,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
743711
let one = self.context.new_rvalue_one(self.usize_type);
744712
let two = self.context.new_rvalue_from_long(self.usize_type, 2);
745713

746-
let clzll = self.context.get_builtin_function("__builtin_clzll");
747-
748714
let first_elem = self.context.new_array_access(None, result, zero);
749-
let first_value = self.gcc_int_cast(self.context.new_call(None, clzll, &[high]), arg_type);
715+
let high_ctlz = self.checked_bitcount_intrinsic(sym::ctlz, high, 64);
716+
let first_value = self.gcc_int_cast(high_ctlz, arg_type);
750717
self.llbb()
751718
.add_assignment(self.location, first_elem, first_value);
752719

753720
let second_elem = self.context.new_array_access(self.location, result, one);
754-
let cast = self.gcc_int_cast(self.context.new_call(self.location, clzll, &[low]), arg_type);
721+
722+
let low_ctlz = self.checked_bitcount_intrinsic(sym::ctlz,low,64 );
723+
let cast = self.gcc_int_cast(low_ctlz, arg_type);
755724
let second_value = self.add(cast, sixty_four);
756725
self.llbb()
757726
.add_assignment(self.location, second_elem, second_value);
@@ -788,7 +757,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
788757
self.context.new_cast(self.location, res, result_type)
789758
}
790759

791-
fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
760+
pub fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
792761
let arg_type = arg.get_type();
793762
let result_type = self.u32_type;
794763
let arg = if arg_type.is_signed(self.cx) {
@@ -826,15 +795,15 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
826795
let one = self.context.new_rvalue_one(self.usize_type);
827796
let two = self.context.new_rvalue_from_long(self.usize_type, 2);
828797

829-
let ctzll = self.context.get_builtin_function("__builtin_ctzll");
830-
831798
let first_elem = self.context.new_array_access(self.location, result, zero);
832-
let first_value = self.gcc_int_cast(self.context.new_call(self.location, ctzll, &[low]), arg_type);
799+
let low_ctzll = self.checked_bitcount_intrinsic(sym::cttz,low , 64);
800+
let first_value = self.gcc_int_cast(low_ctzll, arg_type);
833801
self.llbb()
834802
.add_assignment(self.location, first_elem, first_value);
835803

836804
let second_elem = self.context.new_array_access(self.location, result, one);
837-
let second_value = self.gcc_add(self.gcc_int_cast(self.context.new_call(self.location, ctzll, &[high]), arg_type), sixty_four);
805+
let high_ctzll = self.checked_bitcount_intrinsic(sym::cttz,high , 64);
806+
let second_value = self.gcc_add(self.gcc_int_cast(high_ctzll, arg_type), sixty_four);
838807
self.llbb()
839808
.add_assignment(self.location, second_elem, second_value);
840809

tests/lang_tests_common.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ pub fn main_inner(profile: Profile) {
115115
}
116116
}
117117
match profile {
118-
Profile::Debug => {}
118+
Profile::Debug => {
119+
compiler.args(["-C", "llvm-args=santize-undefined"]);
120+
}
119121
Profile::Release => {
120122
compiler.args(["-C", "opt-level=3", "-C", "lto=no"]);
121123
}

tests/run/bitintrinsics_128.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Compiler:
2+
//
3+
// Run-time:
4+
5+
#![feature(no_core, intrinsics)]
6+
#![no_std]
7+
#![no_core]
8+
#![no_main]
9+
10+
extern crate mini_core;
11+
use intrinsics::black_box;
12+
use mini_core::*;
13+
14+
#[rustc_intrinsic]
15+
pub const fn ctlz<T: Copy>(_x: T) -> u32;
16+
17+
#[rustc_intrinsic]
18+
pub const fn cttz<T: Copy>(_x: T) -> u32;
19+
20+
#[no_mangle]
21+
extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 {
22+
if ctlz(black_box(0_u128)) != 128 {
23+
return 1;
24+
}
25+
if ctlz(black_box(1_u128)) != 127 {
26+
return 2;
27+
}
28+
if cttz(black_box(0_u128)) != 128 {
29+
return 3;
30+
}
31+
if cttz(black_box(1_u128)) != 0 {
32+
return 4;
33+
}
34+
if cttz(black_box(2_u128)) != 1 {
35+
return 4;
36+
}
37+
0
38+
}

0 commit comments

Comments
 (0)