-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Add CastKind::Transmute
to MIR
#108442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CastKind::Transmute
to MIR
#108442
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -13,7 +13,7 @@ use rustc_middle::ty::cast::{CastTy, IntTy}; | |||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; | ||||||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; | ||||||||
use rustc_span::source_map::{Span, DUMMY_SP}; | ||||||||
use rustc_target::abi::VariantIdx; | ||||||||
use rustc_target::abi::{self, VariantIdx}; | ||||||||
|
||||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | ||||||||
#[instrument(level = "trace", skip(self, bx))] | ||||||||
|
@@ -72,6 +72,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |||||||
} | ||||||||
} | ||||||||
|
||||||||
mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => { | ||||||||
let src = self.codegen_operand(bx, operand); | ||||||||
self.codegen_transmute(bx, src, dest); | ||||||||
} | ||||||||
|
||||||||
mir::Rvalue::Repeat(ref elem, count) => { | ||||||||
let cg_elem = self.codegen_operand(bx, elem); | ||||||||
|
||||||||
|
@@ -143,6 +148,52 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |||||||
} | ||||||||
} | ||||||||
|
||||||||
fn codegen_transmute( | ||||||||
&mut self, | ||||||||
bx: &mut Bx, | ||||||||
src: OperandRef<'tcx, Bx::Value>, | ||||||||
dst: PlaceRef<'tcx, Bx::Value>, | ||||||||
) { | ||||||||
// The MIR validator enforces no unsized transmutes. | ||||||||
debug_assert!(src.layout.is_sized()); | ||||||||
debug_assert!(dst.layout.is_sized()); | ||||||||
|
||||||||
if src.layout.size != dst.layout.size | ||||||||
|| src.layout.abi == abi::Abi::Uninhabited | ||||||||
|| dst.layout.abi == abi::Abi::Uninhabited | ||||||||
{ | ||||||||
// In all of these cases it's UB to run this transmute, but that's | ||||||||
// known statically so might as well trap for it, rather than just | ||||||||
// making it unreachable. | ||||||||
bx.abort(); | ||||||||
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
return; | ||||||||
} | ||||||||
|
||||||||
let size_in_bytes = src.layout.size.bytes(); | ||||||||
if size_in_bytes == 0 { | ||||||||
Comment on lines
+172
to
+173
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could also be
Suggested change
|
||||||||
// Nothing to write | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
match src.val { | ||||||||
OperandValue::Ref(src_llval, meta, src_align) => { | ||||||||
debug_assert_eq!(meta, None); | ||||||||
// For a place-to-place transmute, call `memcpy` directly so that | ||||||||
// both arguments get the best-available alignment information. | ||||||||
let bytes = bx.cx().const_usize(size_in_bytes); | ||||||||
let flags = MemFlags::empty(); | ||||||||
bx.memcpy(dst.llval, dst.align, src_llval, src_align, bytes, flags); | ||||||||
} | ||||||||
OperandValue::Immediate(_) | OperandValue::Pair(_, _) => { | ||||||||
// When we have immediate(s), the alignment of the source is irrelevant, | ||||||||
// so we can store them using the destination's alignment. | ||||||||
let llty = bx.backend_type(src.layout); | ||||||||
let cast_ptr = bx.pointercast(dst.llval, bx.type_ptr_to(llty)); | ||||||||
src.val.store(bx, PlaceRef::new_sized_aligned(cast_ptr, src.layout, dst.align)); | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
pub fn codegen_rvalue_unsized( | ||||||||
&mut self, | ||||||||
bx: &mut Bx, | ||||||||
|
@@ -344,6 +395,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |||||||
}; | ||||||||
OperandValue::Immediate(newval) | ||||||||
} | ||||||||
mir::CastKind::Transmute => { | ||||||||
bug!("Transmute operand {:?} in `codegen_rvalue_operand`", operand); | ||||||||
} | ||||||||
}; | ||||||||
OperandRef { val, layout: cast } | ||||||||
} | ||||||||
|
@@ -673,6 +727,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | ||||||||
pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { | ||||||||
match *rvalue { | ||||||||
mir::Rvalue::Cast(mir::CastKind::Transmute, ..) => | ||||||||
// FIXME: Now that transmute is an Rvalue, it would be nice if | ||||||||
// it could create `Immediate`s for scalars, where possible. | ||||||||
false, | ||||||||
mir::Rvalue::Ref(..) | | ||||||||
mir::Rvalue::CopyForDeref(..) | | ||||||||
mir::Rvalue::AddressOf(..) | | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -621,6 +621,33 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | |
); | ||
} | ||
} | ||
CastKind::Transmute => { | ||
if let MirPhase::Runtime(..) = self.mir_phase { | ||
// Unlike `mem::transmute`, a MIR `Transmute` is well-formed | ||
// for any two `Sized` types, just potentially UB to run. | ||
|
||
if !op_ty.is_sized(self.tcx, self.param_env) { | ||
self.fail( | ||
location, | ||
format!("Cannot transmute from non-`Sized` type {op_ty:?}"), | ||
); | ||
} | ||
if !target_type.is_sized(self.tcx, self.param_env) { | ||
self.fail( | ||
location, | ||
format!("Cannot transmute to non-`Sized` type {target_type:?}"), | ||
); | ||
} | ||
} else { | ||
self.fail( | ||
location, | ||
format!( | ||
"Transmute is not supported in non-runtime phase {:?}.", | ||
self.mir_phase | ||
), | ||
); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, thinking about it more, I actually don't think we should have any check like this. Transmutes are presumably things that optimizations want to introduce, and making these kinds of cases not well-formed just feels like a footgun for those opts. It's probably fine to keep this in for now, but can you include a comment that indicates that we can and should relax this restriction if some opts run into the need for it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, relaxing it in future sounds reasonable. I've added the comment, since relaxing it will probably need a bunch of |
||
} | ||
} | ||
Rvalue::Repeat(_, _) | ||
|
Uh oh!
There was an error while loading. Please reload this page.