Skip to content

Commit 3942254

Browse files
committed
Auto merge of #124905 - reitermarkus:u32-from-char-opt, r=scottmcm
Allow optimizing `u32::from::<char>`. Extracted from #124307. This allows optimizing the panicking branch in the `escape_unicode` function, see https://rust.godbolt.org/z/61YhKrhvP.
2 parents ad3c5a3 + 2f2eb22 commit 3942254

File tree

2 files changed

+114
-57
lines changed

2 files changed

+114
-57
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+79-57
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::traits::*;
88
use crate::MemFlags;
99

1010
use rustc_middle::mir;
11-
use rustc_middle::ty::cast::{CastTy, IntTy};
1211
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
1312
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
1413
use rustc_middle::{bug, span_bug};
@@ -238,21 +237,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
238237
}
239238
}
240239
OperandValue::Immediate(imm) => {
241-
let OperandValueKind::Immediate(in_scalar) = operand_kind else {
240+
let OperandValueKind::Immediate(from_scalar) = operand_kind else {
242241
bug!("Found {operand_kind:?} for operand {operand:?}");
243242
};
244-
if let OperandValueKind::Immediate(out_scalar) = cast_kind
245-
&& in_scalar.size(self.cx) == out_scalar.size(self.cx)
243+
if let OperandValueKind::Immediate(to_scalar) = cast_kind
244+
&& from_scalar.size(self.cx) == to_scalar.size(self.cx)
246245
{
247-
let operand_bty = bx.backend_type(operand.layout);
248-
let cast_bty = bx.backend_type(cast);
246+
let from_backend_ty = bx.backend_type(operand.layout);
247+
let to_backend_ty = bx.backend_type(cast);
249248
Some(OperandValue::Immediate(self.transmute_immediate(
250249
bx,
251250
imm,
252-
in_scalar,
253-
operand_bty,
254-
out_scalar,
255-
cast_bty,
251+
from_scalar,
252+
from_backend_ty,
253+
to_scalar,
254+
to_backend_ty,
256255
)))
257256
} else {
258257
None
@@ -281,6 +280,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
281280
}
282281
}
283282

283+
/// Cast one of the immediates from an [`OperandValue::Immediate`]
284+
/// or an [`OperandValue::Pair`] to an immediate of the target type.
285+
///
286+
/// Returns `None` if the cast is not possible.
287+
fn cast_immediate(
288+
&self,
289+
bx: &mut Bx,
290+
mut imm: Bx::Value,
291+
from_scalar: abi::Scalar,
292+
from_backend_ty: Bx::Type,
293+
to_scalar: abi::Scalar,
294+
to_backend_ty: Bx::Type,
295+
) -> Option<Bx::Value> {
296+
use abi::Primitive::*;
297+
298+
// When scalars are passed by value, there's no metadata recording their
299+
// valid ranges. For example, `char`s are passed as just `i32`, with no
300+
// way for LLVM to know that they're 0x10FFFF at most. Thus we assume
301+
// the range of the input value too, not just the output range.
302+
self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
303+
304+
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
305+
(Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed),
306+
(Float(_), Float(_)) => {
307+
let srcsz = bx.cx().float_width(from_backend_ty);
308+
let dstsz = bx.cx().float_width(to_backend_ty);
309+
if dstsz > srcsz {
310+
bx.fpext(imm, to_backend_ty)
311+
} else if srcsz > dstsz {
312+
bx.fptrunc(imm, to_backend_ty)
313+
} else {
314+
imm
315+
}
316+
}
317+
(Int(_, is_signed), Float(_)) => {
318+
if is_signed {
319+
bx.sitofp(imm, to_backend_ty)
320+
} else {
321+
bx.uitofp(imm, to_backend_ty)
322+
}
323+
}
324+
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
325+
(Int(_, is_signed), Pointer(..)) => {
326+
let usize_imm = bx.intcast(imm, bx.cx().type_isize(), is_signed);
327+
bx.inttoptr(usize_imm, to_backend_ty)
328+
}
329+
(Float(_), Int(_, is_signed)) => bx.cast_float_to_int(is_signed, imm, to_backend_ty),
330+
_ => return None,
331+
};
332+
Some(imm)
333+
}
334+
284335
/// Transmutes one of the immediates from an [`OperandValue::Immediate`]
285336
/// or an [`OperandValue::Pair`] to an immediate of the target type.
286337
///
@@ -487,62 +538,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
487538
| mir::CastKind::IntToFloat
488539
| mir::CastKind::PtrToPtr
489540
| mir::CastKind::FnPtrToPtr
490-
491541
// Since int2ptr can have arbitrary integer types as input (so we have to do
492542
// sign extension and all that), it is currently best handled in the same code
493543
// path as the other integer-to-X casts.
494544
| mir::CastKind::PointerWithExposedProvenance => {
545+
let imm = operand.immediate();
546+
let operand_kind = self.value_kind(operand.layout);
547+
let OperandValueKind::Immediate(from_scalar) = operand_kind else {
548+
bug!("Found {operand_kind:?} for operand {operand:?}");
549+
};
550+
let from_backend_ty = bx.cx().immediate_backend_type(operand.layout);
551+
495552
assert!(bx.cx().is_backend_immediate(cast));
496-
let ll_t_out = bx.cx().immediate_backend_type(cast);
553+
let to_backend_ty = bx.cx().immediate_backend_type(cast);
497554
if operand.layout.abi.is_uninhabited() {
498-
let val = OperandValue::Immediate(bx.cx().const_poison(ll_t_out));
555+
let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty));
499556
return OperandRef { val, layout: cast };
500557
}
501-
let r_t_in =
502-
CastTy::from_ty(operand.layout.ty).expect("bad input type for cast");
503-
let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast");
504-
let ll_t_in = bx.cx().immediate_backend_type(operand.layout);
505-
let llval = operand.immediate();
506-
507-
let newval = match (r_t_in, r_t_out) {
508-
(CastTy::Int(i), CastTy::Int(_)) => {
509-
bx.intcast(llval, ll_t_out, i.is_signed())
510-
}
511-
(CastTy::Float, CastTy::Float) => {
512-
let srcsz = bx.cx().float_width(ll_t_in);
513-
let dstsz = bx.cx().float_width(ll_t_out);
514-
if dstsz > srcsz {
515-
bx.fpext(llval, ll_t_out)
516-
} else if srcsz > dstsz {
517-
bx.fptrunc(llval, ll_t_out)
518-
} else {
519-
llval
520-
}
521-
}
522-
(CastTy::Int(i), CastTy::Float) => {
523-
if i.is_signed() {
524-
bx.sitofp(llval, ll_t_out)
525-
} else {
526-
bx.uitofp(llval, ll_t_out)
527-
}
528-
}
529-
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
530-
bx.pointercast(llval, ll_t_out)
531-
}
532-
(CastTy::Int(i), CastTy::Ptr(_)) => {
533-
let usize_llval =
534-
bx.intcast(llval, bx.cx().type_isize(), i.is_signed());
535-
bx.inttoptr(usize_llval, ll_t_out)
536-
}
537-
(CastTy::Float, CastTy::Int(IntTy::I)) => {
538-
bx.cast_float_to_int(true, llval, ll_t_out)
539-
}
540-
(CastTy::Float, CastTy::Int(_)) => {
541-
bx.cast_float_to_int(false, llval, ll_t_out)
542-
}
543-
_ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty),
558+
let cast_kind = self.value_kind(cast);
559+
let OperandValueKind::Immediate(to_scalar) = cast_kind else {
560+
bug!("Found {cast_kind:?} for operand {cast:?}");
544561
};
545-
OperandValue::Immediate(newval)
562+
563+
self.cast_immediate(bx, imm, from_scalar, from_backend_ty, to_scalar, to_backend_ty)
564+
.map(OperandValue::Immediate)
565+
.unwrap_or_else(|| {
566+
bug!("Unsupported cast of {operand:?} to {cast:?}");
567+
})
546568
}
547569
mir::CastKind::Transmute => {
548570
self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {

tests/codegen/cast-optimized.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//@ compile-flags: -O -Z merge-functions=disabled
2+
#![crate_type = "lib"]
3+
4+
// This tests that LLVM can optimize based on the niches in the source or
5+
// destination types for casts.
6+
7+
// CHECK-LABEL: @u32_index
8+
#[no_mangle]
9+
pub fn u32_index(c: u32) -> [bool; 22] {
10+
let mut array = [false; 22];
11+
12+
let index = 32 - c.leading_zeros();
13+
14+
// CHECK: call core::panicking::panic
15+
array[index as usize] = true;
16+
17+
array
18+
}
19+
20+
// CHECK-LABEL: @char_as_u32_index
21+
#[no_mangle]
22+
pub fn char_as_u32_index(c: char) -> [bool; 22] {
23+
// CHECK: %[[B:.+]] = icmp ult i32 %c, 1114112
24+
// CHECK: call void @llvm.assume(i1 %[[B]])
25+
let c = c as u32;
26+
27+
let mut array = [false; 22];
28+
29+
let index = 32 - c.leading_zeros();
30+
31+
// CHECK-NOT: call core::panicking::panic
32+
array[index as usize] = true;
33+
34+
array
35+
}

0 commit comments

Comments
 (0)