From 7740476a43a7dc67c2b27eb2d981368a6c88832e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 7 Sep 2023 22:06:37 +0200 Subject: [PATCH] explain PassMode::Cast --- compiler/rustc_codegen_llvm/src/abi.rs | 18 +++++++------ compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +-- compiler/rustc_codegen_ssa/src/mir/block.rs | 10 ++++---- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 4 +-- compiler/rustc_target/src/abi/call/mod.rs | 25 +++++++++++++------ compiler/rustc_target/src/abi/call/x86.rs | 2 +- 7 files changed, 38 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 64587f98b8a..883c5f5fe54 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -211,7 +211,7 @@ fn store( OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst) } else if self.is_unsized_indirect() { bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); - } else if let PassMode::Cast(cast, _) = &self.mode { + } else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode { // FIXME(eddyb): Figure out when the simpler Store is safe, clang // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. let can_store_through_cast_ptr = false; @@ -279,7 +279,7 @@ fn store_fn_arg( } PassMode::Direct(_) | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } - | PassMode::Cast(..) => { + | PassMode::Cast { .. } => { let next_arg = next(); self.store(bx, next_arg, dst); } @@ -332,7 +332,7 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { let llreturn_ty = match &self.ret.mode { PassMode::Ignore => cx.type_void(), PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx), - PassMode::Cast(cast, _) => cast.llvm_type(cx), + PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx), PassMode::Indirect { .. } => { llargument_tys.push(cx.type_ptr()); cx.type_void() @@ -390,7 +390,9 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Cast(cast, pad_i32) => { + PassMode::Cast { cast, pad_i32 } => { + // `Cast` means "transmute to `CastType`"; that only makes sense for sized types. + assert!(arg.layout.is_sized()); // add padding if *pad_i32 { llargument_tys.push(Reg::i32().llvm_type(cx)); @@ -448,7 +450,7 @@ fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { let sret = llvm::CreateStructRetAttr(cx.llcx, self.ret.layout.llvm_type(cx)); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]); } - PassMode::Cast(cast, _) => { + PassMode::Cast { cast, pad_i32: _ } => { cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn); } _ => {} @@ -474,7 +476,7 @@ fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { apply(a); apply(b); } - PassMode::Cast(cast, pad_i32) => { + PassMode::Cast { cast, pad_i32 } => { if *pad_i32 { apply(&ArgAttributes::new()); } @@ -510,7 +512,7 @@ fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll V let sret = llvm::CreateStructRetAttr(bx.cx.llcx, self.ret.layout.llvm_type(bx)); attributes::apply_to_callsite(callsite, llvm::AttributePlace::Argument(i), &[sret]); } - PassMode::Cast(cast, _) => { + PassMode::Cast { cast, pad_i32: _ } => { cast.attrs.apply_attrs_to_callsite( llvm::AttributePlace::ReturnValue, &bx.cx, @@ -553,7 +555,7 @@ fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll V apply(bx.cx, a); apply(bx.cx, b); } - PassMode::Cast(cast, pad_i32) => { + PassMode::Cast { cast, pad_i32 } => { if *pad_i32 { apply(bx.cx, &ArgAttributes::new()); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a9b06030e70..9289c37d763 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -165,7 +165,7 @@ fn codegen_intrinsic_call( sym::volatile_load | sym::unaligned_volatile_load => { let tp_ty = fn_args.type_at(0); let ptr = args[0].immediate(); - let load = if let PassMode::Cast(ty, _) = &fn_abi.ret.mode { + let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode { let llty = ty.llvm_type(self); self.volatile_load(llty, ptr) } else { @@ -386,7 +386,7 @@ fn codegen_intrinsic_call( }; if !fn_abi.ret.is_ignore() { - if let PassMode::Cast(_, _) = &fn_abi.ret.mode { + if let PassMode::Cast { .. } = &fn_abi.ret.mode { self.store(llval, result.llval, result.align); } else { OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index d8f6b4ed836..6138fda7a8d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -416,7 +416,7 @@ fn codegen_return_terminator(&mut self, bx: &mut Bx) { } } - PassMode::Cast(cast_ty, _) => { + PassMode::Cast { cast: cast_ty, pad_i32: _ } => { let op = match self.locals[mir::RETURN_PLACE] { LocalRef::Operand(op) => op, LocalRef::PendingOperand => bug!("use of return before def"), @@ -1310,7 +1310,7 @@ fn codegen_argument( ) { match arg.mode { PassMode::Ignore => return, - PassMode::Cast(_, true) => { + PassMode::Cast { pad_i32: true, .. } => { // Fill padding with undef value, where applicable. llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32()))); } @@ -1347,7 +1347,7 @@ fn codegen_argument( op.val.store(bx, scratch); (scratch.llval, scratch.align, true) } - PassMode::Cast(..) => { + PassMode::Cast { .. } => { let scratch = PlaceRef::alloca(bx, arg.layout); op.val.store(bx, scratch); (scratch.llval, scratch.align, true) @@ -1400,7 +1400,7 @@ fn codegen_argument( if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if let PassMode::Cast(ty, _) = &arg.mode { + if let PassMode::Cast { cast: ty, .. } = &arg.mode { let llty = bx.cast_backend_type(ty); llval = bx.load(llty, llval, align.min(arg.layout.align.abi)); } else { @@ -1744,7 +1744,7 @@ fn store_return( } DirectOperand(index) => { // If there is a cast, we have to store and reload. - let op = if let PassMode::Cast(..) = ret_abi.mode { + let op = if let PassMode::Cast { .. } = ret_abi.mode { let tmp = PlaceRef::alloca(bx, ret_abi.layout); tmp.storage_live(bx); bx.store_arg(&ret_abi, llval, tmp); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 8821fb21fd0..8efef440522 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -462,7 +462,7 @@ pub fn codegen_intrinsic_call( }; if !fn_abi.ret.is_ignore() { - if let PassMode::Cast(..) = &fn_abi.ret.mode { + if let PassMode::Cast { .. } = &fn_abi.ret.mode { bx.store(llval, result.llval, result.align); } else { OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index c4408f2db4c..8445de6c678 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -327,7 +327,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( for i in 0..tupled_arg_tys.len() { let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast(_, true) = arg.mode { + if let PassMode::Cast { pad_i32: true, .. } = arg.mode { llarg_idx += 1; } let pr_field = place.project_field(bx, i); @@ -351,7 +351,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast(_, true) = arg.mode { + if let PassMode::Cast { pad_i32: true, .. } = arg.mode { llarg_idx += 1; } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 1a4a46ceb40..c528e7b363f 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -45,10 +45,9 @@ pub enum PassMode { /// /// The argument has a layout abi of `ScalarPair`. Pair(ArgAttributes, ArgAttributes), - /// Pass the argument after casting it, to either a single uniform or a - /// pair of registers. The bool indicates if a `Reg::i32()` dummy argument - /// is emitted before the real argument. - Cast(Box, bool), + /// Pass the argument after casting it. See the `CastTarget` docs for details. The bool + /// indicates if a `Reg::i32()` dummy argument is emitted before the real argument. + Cast { pad_i32: bool, cast: Box }, /// Pass the argument indirectly via a hidden pointer. /// The `extra_attrs` value, if any, is for the extra data (vtable or length) /// which indicates that it refers to an unsized rvalue. @@ -64,10 +63,13 @@ impl PassMode { /// so that needs to be compared as well! pub fn eq_abi(&self, other: &Self) -> bool { match (self, other) { - (PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type + (PassMode::Ignore, PassMode::Ignore) => true, (PassMode::Direct(a1), PassMode::Direct(a2)) => a1.eq_abi(a2), (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => a1.eq_abi(a2) && b1.eq_abi(b2), - (PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1.eq_abi(c2) && pad1 == pad2, + ( + PassMode::Cast { cast: c1, pad_i32: pad1 }, + PassMode::Cast { cast: c2, pad_i32: pad2 }, + ) => c1.eq_abi(c2) && pad1 == pad2, ( PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 }, PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 }, @@ -255,6 +257,13 @@ pub fn align(&self, cx: &C) -> Align { } } +/// Describes the type used for `PassMode::Cast`. +/// +/// Passing arguments in this mode works as follows: the registers in the `prefix` (the ones that +/// are `Some`) get laid out one after the other (using `repr(C)` layout rules). Then the +/// `rest.unit` register type gets repeated often enough to cover `rest.size`. This describes the +/// actual type used for the call; the Rust type of the argument is then transmuted to this ABI type +/// (and all data in the padding between the registers is dropped). #[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct CastTarget { pub prefix: [Option; 8], @@ -607,11 +616,11 @@ pub fn extend_integer_width_to(&mut self, bits: u64) { } pub fn cast_to>(&mut self, target: T) { - self.mode = PassMode::Cast(Box::new(target.into()), false); + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; } pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: bool) { - self.mode = PassMode::Cast(Box::new(target.into()), pad_i32); + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; } pub fn is_indirect(&self) -> bool { diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index b738c3133d9..42a914296c3 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -148,7 +148,7 @@ fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool PassMode::Direct(ref mut attrs) => attrs, PassMode::Pair(..) | PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } - | PassMode::Cast(..) => { + | PassMode::Cast { .. } => { unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode) } };