Skip to content

Commit a8f425b

Browse files
committed
Allow optimizing u32::from::<char>.
1 parent c7716d5 commit a8f425b

File tree

1 file changed

+78
-57
lines changed

1 file changed

+78
-57
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 78 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::MemFlags;
99

1010
use rustc_hir as hir;
1111
use rustc_middle::mir;
12-
use rustc_middle::ty::cast::{CastTy, IntTy};
1312
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
1413
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
1514
use rustc_middle::{bug, span_bug};
@@ -234,21 +233,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
234233
}
235234
}
236235
OperandValue::Immediate(imm) => {
237-
let OperandValueKind::Immediate(in_scalar) = operand_kind else {
236+
let OperandValueKind::Immediate(from_scalar) = operand_kind else {
238237
bug!("Found {operand_kind:?} for operand {operand:?}");
239238
};
240-
if let OperandValueKind::Immediate(out_scalar) = cast_kind
241-
&& in_scalar.size(self.cx) == out_scalar.size(self.cx)
239+
if let OperandValueKind::Immediate(to_scalar) = cast_kind
240+
&& from_scalar.size(self.cx) == to_scalar.size(self.cx)
242241
{
243-
let operand_bty = bx.backend_type(operand.layout);
244-
let cast_bty = bx.backend_type(cast);
242+
let from_backend_ty = bx.backend_type(operand.layout);
243+
let to_backend_ty = bx.backend_type(cast);
245244
Some(OperandValue::Immediate(self.transmute_immediate(
246245
bx,
247246
imm,
248-
in_scalar,
249-
operand_bty,
250-
out_scalar,
251-
cast_bty,
247+
from_scalar,
248+
from_backend_ty,
249+
to_scalar,
250+
to_backend_ty,
252251
)))
253252
} else {
254253
None
@@ -277,6 +276,57 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
277276
}
278277
}
279278

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

0 commit comments

Comments
 (0)