explain PassMode::Cast
This commit is contained in:
parent
9cbc90c0ae
commit
7740476a43
7 changed files with 38 additions and 27 deletions
|
@ -211,7 +211,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
||||||
OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
|
OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
|
||||||
} else if self.is_unsized_indirect() {
|
} else if self.is_unsized_indirect() {
|
||||||
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
|
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
|
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
|
||||||
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
|
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
|
||||||
let can_store_through_cast_ptr = false;
|
let can_store_through_cast_ptr = false;
|
||||||
|
@ -279,7 +279,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
||||||
}
|
}
|
||||||
PassMode::Direct(_)
|
PassMode::Direct(_)
|
||||||
| PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ }
|
| PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ }
|
||||||
| PassMode::Cast(..) => {
|
| PassMode::Cast { .. } => {
|
||||||
let next_arg = next();
|
let next_arg = next();
|
||||||
self.store(bx, next_arg, dst);
|
self.store(bx, next_arg, dst);
|
||||||
}
|
}
|
||||||
|
@ -332,7 +332,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
let llreturn_ty = match &self.ret.mode {
|
let llreturn_ty = match &self.ret.mode {
|
||||||
PassMode::Ignore => cx.type_void(),
|
PassMode::Ignore => cx.type_void(),
|
||||||
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
|
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 { .. } => {
|
PassMode::Indirect { .. } => {
|
||||||
llargument_tys.push(cx.type_ptr());
|
llargument_tys.push(cx.type_ptr());
|
||||||
cx.type_void()
|
cx.type_void()
|
||||||
|
@ -390,7 +390,9 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
|
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
|
||||||
continue;
|
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
|
// add padding
|
||||||
if *pad_i32 {
|
if *pad_i32 {
|
||||||
llargument_tys.push(Reg::i32().llvm_type(cx));
|
llargument_tys.push(Reg::i32().llvm_type(cx));
|
||||||
|
@ -448,7 +450,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
let sret = llvm::CreateStructRetAttr(cx.llcx, self.ret.layout.llvm_type(cx));
|
let sret = llvm::CreateStructRetAttr(cx.llcx, self.ret.layout.llvm_type(cx));
|
||||||
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]);
|
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);
|
cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -474,7 +476,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
apply(a);
|
apply(a);
|
||||||
apply(b);
|
apply(b);
|
||||||
}
|
}
|
||||||
PassMode::Cast(cast, pad_i32) => {
|
PassMode::Cast { cast, pad_i32 } => {
|
||||||
if *pad_i32 {
|
if *pad_i32 {
|
||||||
apply(&ArgAttributes::new());
|
apply(&ArgAttributes::new());
|
||||||
}
|
}
|
||||||
|
@ -510,7 +512,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
let sret = llvm::CreateStructRetAttr(bx.cx.llcx, self.ret.layout.llvm_type(bx));
|
let sret = llvm::CreateStructRetAttr(bx.cx.llcx, self.ret.layout.llvm_type(bx));
|
||||||
attributes::apply_to_callsite(callsite, llvm::AttributePlace::Argument(i), &[sret]);
|
attributes::apply_to_callsite(callsite, llvm::AttributePlace::Argument(i), &[sret]);
|
||||||
}
|
}
|
||||||
PassMode::Cast(cast, _) => {
|
PassMode::Cast { cast, pad_i32: _ } => {
|
||||||
cast.attrs.apply_attrs_to_callsite(
|
cast.attrs.apply_attrs_to_callsite(
|
||||||
llvm::AttributePlace::ReturnValue,
|
llvm::AttributePlace::ReturnValue,
|
||||||
&bx.cx,
|
&bx.cx,
|
||||||
|
@ -553,7 +555,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
apply(bx.cx, a);
|
apply(bx.cx, a);
|
||||||
apply(bx.cx, b);
|
apply(bx.cx, b);
|
||||||
}
|
}
|
||||||
PassMode::Cast(cast, pad_i32) => {
|
PassMode::Cast { cast, pad_i32 } => {
|
||||||
if *pad_i32 {
|
if *pad_i32 {
|
||||||
apply(bx.cx, &ArgAttributes::new());
|
apply(bx.cx, &ArgAttributes::new());
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
||||||
sym::volatile_load | sym::unaligned_volatile_load => {
|
sym::volatile_load | sym::unaligned_volatile_load => {
|
||||||
let tp_ty = fn_args.type_at(0);
|
let tp_ty = fn_args.type_at(0);
|
||||||
let ptr = args[0].immediate();
|
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);
|
let llty = ty.llvm_type(self);
|
||||||
self.volatile_load(llty, ptr)
|
self.volatile_load(llty, ptr)
|
||||||
} else {
|
} else {
|
||||||
|
@ -386,7 +386,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !fn_abi.ret.is_ignore() {
|
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);
|
self.store(llval, result.llval, result.align);
|
||||||
} else {
|
} else {
|
||||||
OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
|
OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
|
||||||
|
|
|
@ -416,7 +416,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PassMode::Cast(cast_ty, _) => {
|
PassMode::Cast { cast: cast_ty, pad_i32: _ } => {
|
||||||
let op = match self.locals[mir::RETURN_PLACE] {
|
let op = match self.locals[mir::RETURN_PLACE] {
|
||||||
LocalRef::Operand(op) => op,
|
LocalRef::Operand(op) => op,
|
||||||
LocalRef::PendingOperand => bug!("use of return before def"),
|
LocalRef::PendingOperand => bug!("use of return before def"),
|
||||||
|
@ -1310,7 +1310,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
) {
|
) {
|
||||||
match arg.mode {
|
match arg.mode {
|
||||||
PassMode::Ignore => return,
|
PassMode::Ignore => return,
|
||||||
PassMode::Cast(_, true) => {
|
PassMode::Cast { pad_i32: true, .. } => {
|
||||||
// Fill padding with undef value, where applicable.
|
// Fill padding with undef value, where applicable.
|
||||||
llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32())));
|
llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32())));
|
||||||
}
|
}
|
||||||
|
@ -1347,7 +1347,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
op.val.store(bx, scratch);
|
op.val.store(bx, scratch);
|
||||||
(scratch.llval, scratch.align, true)
|
(scratch.llval, scratch.align, true)
|
||||||
}
|
}
|
||||||
PassMode::Cast(..) => {
|
PassMode::Cast { .. } => {
|
||||||
let scratch = PlaceRef::alloca(bx, arg.layout);
|
let scratch = PlaceRef::alloca(bx, arg.layout);
|
||||||
op.val.store(bx, scratch);
|
op.val.store(bx, scratch);
|
||||||
(scratch.llval, scratch.align, true)
|
(scratch.llval, scratch.align, true)
|
||||||
|
@ -1400,7 +1400,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
|
||||||
if by_ref && !arg.is_indirect() {
|
if by_ref && !arg.is_indirect() {
|
||||||
// Have to load the argument, maybe while casting it.
|
// 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);
|
let llty = bx.cast_backend_type(ty);
|
||||||
llval = bx.load(llty, llval, align.min(arg.layout.align.abi));
|
llval = bx.load(llty, llval, align.min(arg.layout.align.abi));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1744,7 +1744,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
DirectOperand(index) => {
|
DirectOperand(index) => {
|
||||||
// If there is a cast, we have to store and reload.
|
// 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);
|
let tmp = PlaceRef::alloca(bx, ret_abi.layout);
|
||||||
tmp.storage_live(bx);
|
tmp.storage_live(bx);
|
||||||
bx.store_arg(&ret_abi, llval, tmp);
|
bx.store_arg(&ret_abi, llval, tmp);
|
||||||
|
|
|
@ -462,7 +462,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !fn_abi.ret.is_ignore() {
|
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);
|
bx.store(llval, result.llval, result.align);
|
||||||
} else {
|
} else {
|
||||||
OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout)
|
OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout)
|
||||||
|
|
|
@ -327,7 +327,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
for i in 0..tupled_arg_tys.len() {
|
for i in 0..tupled_arg_tys.len() {
|
||||||
let arg = &fx.fn_abi.args[idx];
|
let arg = &fx.fn_abi.args[idx];
|
||||||
idx += 1;
|
idx += 1;
|
||||||
if let PassMode::Cast(_, true) = arg.mode {
|
if let PassMode::Cast { pad_i32: true, .. } = arg.mode {
|
||||||
llarg_idx += 1;
|
llarg_idx += 1;
|
||||||
}
|
}
|
||||||
let pr_field = place.project_field(bx, i);
|
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];
|
let arg = &fx.fn_abi.args[idx];
|
||||||
idx += 1;
|
idx += 1;
|
||||||
if let PassMode::Cast(_, true) = arg.mode {
|
if let PassMode::Cast { pad_i32: true, .. } = arg.mode {
|
||||||
llarg_idx += 1;
|
llarg_idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,9 @@ pub enum PassMode {
|
||||||
///
|
///
|
||||||
/// The argument has a layout abi of `ScalarPair`.
|
/// The argument has a layout abi of `ScalarPair`.
|
||||||
Pair(ArgAttributes, ArgAttributes),
|
Pair(ArgAttributes, ArgAttributes),
|
||||||
/// Pass the argument after casting it, to either a single uniform or a
|
/// Pass the argument after casting it. See the `CastTarget` docs for details. The bool
|
||||||
/// pair of registers. The bool indicates if a `Reg::i32()` dummy argument
|
/// indicates if a `Reg::i32()` dummy argument is emitted before the real argument.
|
||||||
/// is emitted before the real argument.
|
Cast { pad_i32: bool, cast: Box<CastTarget> },
|
||||||
Cast(Box<CastTarget>, bool),
|
|
||||||
/// Pass the argument indirectly via a hidden pointer.
|
/// Pass the argument indirectly via a hidden pointer.
|
||||||
/// The `extra_attrs` value, if any, is for the extra data (vtable or length)
|
/// The `extra_attrs` value, if any, is for the extra data (vtable or length)
|
||||||
/// which indicates that it refers to an unsized rvalue.
|
/// which indicates that it refers to an unsized rvalue.
|
||||||
|
@ -64,10 +63,13 @@ impl PassMode {
|
||||||
/// so that needs to be compared as well!
|
/// so that needs to be compared as well!
|
||||||
pub fn eq_abi(&self, other: &Self) -> bool {
|
pub fn eq_abi(&self, other: &Self) -> bool {
|
||||||
match (self, other) {
|
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::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::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: a1, extra_attrs: None, on_stack: s1 },
|
||||||
PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
|
PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
|
||||||
|
@ -255,6 +257,13 @@ impl Uniform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||||
pub struct CastTarget {
|
pub struct CastTarget {
|
||||||
pub prefix: [Option<Reg>; 8],
|
pub prefix: [Option<Reg>; 8],
|
||||||
|
@ -607,11 +616,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
|
pub fn cast_to<T: Into<CastTarget>>(&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<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) {
|
pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&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 {
|
pub fn is_indirect(&self) -> bool {
|
||||||
|
|
|
@ -148,7 +148,7 @@ where
|
||||||
PassMode::Direct(ref mut attrs) => attrs,
|
PassMode::Direct(ref mut attrs) => attrs,
|
||||||
PassMode::Pair(..)
|
PassMode::Pair(..)
|
||||||
| PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ }
|
| PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ }
|
||||||
| PassMode::Cast(..) => {
|
| PassMode::Cast { .. } => {
|
||||||
unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
|
unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue