Skip to content

Commit

Permalink
Replace hints with optimized code generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
reitermarkus committed May 9, 2024
1 parent cd9662b commit 4a436dc
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 75 deletions.
138 changes: 81 additions & 57 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::MemFlags;

use rustc_hir as hir;
use rustc_middle::mir::{self, AggregateKind, Operand};
use rustc_middle::ty::cast::{CastTy, IntTy};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
Expand Down Expand Up @@ -237,21 +236,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
OperandValue::Immediate(imm) => {
let OperandValueKind::Immediate(in_scalar) = operand_kind else {
let OperandValueKind::Immediate(from_scalar) = operand_kind else {
bug!("Found {operand_kind:?} for operand {operand:?}");
};
if let OperandValueKind::Immediate(out_scalar) = cast_kind
&& in_scalar.size(self.cx) == out_scalar.size(self.cx)
if let OperandValueKind::Immediate(to_scalar) = cast_kind
&& from_scalar.size(self.cx) == to_scalar.size(self.cx)
{
let operand_bty = bx.backend_type(operand.layout);
let cast_bty = bx.backend_type(cast);
let from_backend_ty = bx.backend_type(operand.layout);
let to_backend_ty = bx.backend_type(cast);
Some(OperandValue::Immediate(self.transmute_immediate(
bx,
imm,
in_scalar,
operand_bty,
out_scalar,
cast_bty,
from_scalar,
from_backend_ty,
to_scalar,
to_backend_ty,
)))
} else {
None
Expand Down Expand Up @@ -280,6 +279,60 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}

/// Cast one of the immediates from an [`OperandValue::Immediate`]
/// or an [`OperandValue::Pair`] to an immediate of the target type.
fn cast_immediate(
&self,
bx: &mut Bx,
mut imm: Bx::Value,
from_scalar: abi::Scalar,
from_backend_ty: Bx::Type,
to_scalar: abi::Scalar,
to_backend_ty: Bx::Type,
) -> Option<Bx::Value> {
use abi::Primitive::*;

// When scalars are passed by value, there's no metadata recording their
// valid ranges. For example, `char`s are passed as just `i32`, with no
// way for LLVM to know that they're 0x10FFFF at most. Thus we assume
// the range of the input value too, not just the output range.
self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty);

imm = match (from_scalar.primitive(), to_scalar.primitive()) {
(Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed),
(F16 | F32 | F64 | F128, F16 | F32 | F64 | F128) => {
let srcsz = bx.cx().float_width(from_backend_ty);
let dstsz = bx.cx().float_width(to_backend_ty);
if dstsz > srcsz {
bx.fpext(imm, to_backend_ty)
} else if srcsz > dstsz {
bx.fptrunc(imm, to_backend_ty)
} else {
imm
}
}
(Int(_, is_signed), F16 | F32 | F64 | F128) => {
if is_signed {
bx.sitofp(imm, to_backend_ty)
} else {
bx.uitofp(imm, to_backend_ty)
}
}
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
(Int(_, is_signed), Pointer(..)) => {
let usize_imm = bx.intcast(imm, bx.cx().type_isize(), is_signed);
bx.inttoptr(usize_imm, to_backend_ty)
}
(F16 | F32 | F64 | F128, Int(_, is_signed)) => {
bx.cast_float_to_int(is_signed, imm, to_backend_ty)
}
_ => return None,
};
self.assume_scalar_range(bx, imm, to_scalar, to_backend_ty);
imm = bx.to_immediate_scalar(imm, to_scalar);
Some(imm)
}

/// Transmutes one of the immediates from an [`OperandValue::Immediate`]
/// or an [`OperandValue::Pair`] to an immediate of the target type.
///
Expand Down Expand Up @@ -508,62 +561,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| mir::CastKind::IntToFloat
| mir::CastKind::PtrToPtr
| mir::CastKind::FnPtrToPtr

// Since int2ptr can have arbitrary integer types as input (so we have to do
// sign extension and all that), it is currently best handled in the same code
// path as the other integer-to-X casts.
| mir::CastKind::PointerWithExposedProvenance => {
let imm = operand.immediate();
let operand_kind = self.value_kind(operand.layout);
let OperandValueKind::Immediate(from_scalar) = operand_kind else {
bug!("Found {operand_kind:?} for operand {operand:?}");
};
let from_backend_ty = bx.cx().immediate_backend_type(operand.layout);

assert!(bx.cx().is_backend_immediate(cast));
let ll_t_out = bx.cx().immediate_backend_type(cast);
let to_backend_ty = bx.cx().immediate_backend_type(cast);
if operand.layout.abi.is_uninhabited() {
let val = OperandValue::Immediate(bx.cx().const_poison(ll_t_out));
let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty));
return OperandRef { val, layout: cast };
}
let r_t_in =
CastTy::from_ty(operand.layout.ty).expect("bad input type for cast");
let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast");
let ll_t_in = bx.cx().immediate_backend_type(operand.layout);
let llval = operand.immediate();

let newval = match (r_t_in, r_t_out) {
(CastTy::Int(i), CastTy::Int(_)) => {
bx.intcast(llval, ll_t_out, i.is_signed())
}
(CastTy::Float, CastTy::Float) => {
let srcsz = bx.cx().float_width(ll_t_in);
let dstsz = bx.cx().float_width(ll_t_out);
if dstsz > srcsz {
bx.fpext(llval, ll_t_out)
} else if srcsz > dstsz {
bx.fptrunc(llval, ll_t_out)
} else {
llval
}
}
(CastTy::Int(i), CastTy::Float) => {
if i.is_signed() {
bx.sitofp(llval, ll_t_out)
} else {
bx.uitofp(llval, ll_t_out)
}
}
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
bx.pointercast(llval, ll_t_out)
}
(CastTy::Int(i), CastTy::Ptr(_)) => {
let usize_llval =
bx.intcast(llval, bx.cx().type_isize(), i.is_signed());
bx.inttoptr(usize_llval, ll_t_out)
}
(CastTy::Float, CastTy::Int(IntTy::I)) => {
bx.cast_float_to_int(true, llval, ll_t_out)
}
(CastTy::Float, CastTy::Int(_)) => {
bx.cast_float_to_int(false, llval, ll_t_out)
}
_ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty),
let cast_kind = self.value_kind(cast);
let OperandValueKind::Immediate(to_scalar) = cast_kind else {
bug!("Found {cast_kind:?} for operand {cast:?}");
};
OperandValue::Immediate(newval)

self.cast_immediate(bx, imm, from_scalar, from_backend_ty, to_scalar, to_backend_ty)
.map(OperandValue::Immediate)
.unwrap_or_else(|| {
bug!("Unsupported cast of {operand:?} to {cast:?}");
})
}
mir::CastKind::Transmute => {
self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {
Expand Down
21 changes: 3 additions & 18 deletions library/core/src/char/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,7 @@ impl From<char> for u32 {
/// ```
#[inline]
fn from(c: char) -> Self {
let c = c as Self;

// SAFETY: `char::MAX` is the highest valid Unicode code point.
unsafe { core::hint::assert_unchecked(c <= char::MAX as Self) };

c
c as Self
}
}

Expand All @@ -74,12 +69,7 @@ impl From<char> for u64 {
fn from(c: char) -> Self {
// The char is casted to the value of the code point, then zero-extended to 64 bit.
// See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics]
let c = c as Self;

// SAFETY: `char::MAX` is the highest valid Unicode code point.
unsafe { core::hint::assert_unchecked(c <= char::MAX as Self) };

c
c as Self
}
}

Expand All @@ -100,12 +90,7 @@ impl From<char> for u128 {
fn from(c: char) -> Self {
// The char is casted to the value of the code point, then zero-extended to 128 bit.
// See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics]
let c = c as Self;

// SAFETY: `char::MAX` is the highest valid Unicode code point.
unsafe { core::hint::assert_unchecked(c <= char::MAX as Self) };

c
c as Self
}
}

Expand Down

0 comments on commit 4a436dc

Please sign in to comment.