Skip to content

Commit 45283e4

Browse files
committed
Add auto-destructuring for structs
- Remove redundant bitcasts at callsite
1 parent c0d9026 commit 45283e4

File tree

4 files changed

+141
-50
lines changed

4 files changed

+141
-50
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_abi::{
77
X86Call,
88
};
99
use rustc_codegen_ssa::MemFlags;
10+
use rustc_codegen_ssa::common::TypeKind;
1011
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
1112
use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
1213
use rustc_codegen_ssa::traits::*;
@@ -22,7 +23,7 @@ use smallvec::SmallVec;
2223

2324
use crate::attributes::{self, llfn_attrs_from_instance};
2425
use crate::builder::Builder;
25-
use crate::context::CodegenCx;
26+
use crate::context::{CodegenCx, GenericCx, SCx};
2627
use crate::llvm::{self, Attribute, AttributePlace};
2728
use crate::type_::Type;
2829
use crate::type_of::LayoutLlvmExt;
@@ -353,6 +354,32 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
353354
);
354355
}
355356

357+
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
358+
pub(crate) fn equate_ty(&self, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool {
359+
if rust_ty == llvm_ty {
360+
return true;
361+
}
362+
363+
match self.type_kind(llvm_ty) {
364+
TypeKind::Struct if self.type_kind(rust_ty) == TypeKind::Struct => {
365+
let rust_element_tys = self.struct_element_types(rust_ty);
366+
let llvm_element_tys = self.struct_element_types(llvm_ty);
367+
368+
if rust_element_tys.len() != llvm_element_tys.len() {
369+
return false;
370+
}
371+
372+
iter::zip(rust_element_tys, llvm_element_tys).all(
373+
|(rust_element_ty, llvm_element_ty)| {
374+
self.equate_ty(rust_element_ty, llvm_element_ty)
375+
},
376+
)
377+
}
378+
_ => false,
379+
}
380+
}
381+
}
382+
356383
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
357384
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
358385
match &self.ret.mode {
@@ -442,7 +469,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
442469
// todo: add bypasses for types not accessible from Rust here
443470
iter::once((rust_return_ty, llvm_return_ty))
444471
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
445-
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
472+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
446473
}
447474

448475
fn llvm_type(

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 99 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ impl<'a, 'll> SBuilder<'a, 'll> {
6666
) -> &'ll Value {
6767
debug!("call {:?} with args ({:?})", llfn, args);
6868

69-
let args = self.check_call("call", llty, llfn, args);
7069
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
7170
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
7271
if let Some(funclet_bundle) = funclet_bundle {
@@ -348,7 +347,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
348347
) -> &'ll Value {
349348
debug!("invoke {:?} with args ({:?})", llfn, args);
350349

351-
let args = self.check_call("invoke", llty, llfn, args);
350+
let args = self.cast_arguments("invoke", llty, llfn, args, fn_abi.is_some());
352351
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
353352
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
354353
if let Some(funclet_bundle) = funclet_bundle {
@@ -380,8 +379,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
380379
};
381380
if let Some(fn_abi) = fn_abi {
382381
fn_abi.apply_attrs_callsite(self, invoke, llfn);
382+
self.cast_return(fn_abi, llfn, invoke)
383+
} else {
384+
invoke
383385
}
384-
invoke
385386
}
386387

387388
fn unreachable(&mut self) {
@@ -1343,7 +1344,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13431344
) -> &'ll Value {
13441345
debug!("call {:?} with args ({:?})", llfn, args);
13451346

1346-
let args = self.check_call("call", llty, llfn, args);
1347+
let args = self.cast_arguments("call", llty, llfn, args, fn_abi.is_some());
13471348
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
13481349
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
13491350
if let Some(funclet_bundle) = funclet_bundle {
@@ -1373,8 +1374,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13731374
};
13741375
if let Some(fn_abi) = fn_abi {
13751376
fn_abi.apply_attrs_callsite(self, call, llfn);
1377+
self.cast_return(fn_abi, llfn, call)
1378+
} else {
1379+
call
13761380
}
1377-
call
13781381
}
13791382

13801383
fn zext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
@@ -1542,47 +1545,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
15421545
ret.expect("LLVM does not have support for catchret")
15431546
}
15441547

1545-
fn check_call<'b>(
1546-
&mut self,
1547-
typ: &str,
1548-
fn_ty: &'ll Type,
1549-
llfn: &'ll Value,
1550-
args: &'b [&'ll Value],
1551-
) -> Cow<'b, [&'ll Value]> {
1552-
assert!(
1553-
self.cx.type_kind(fn_ty) == TypeKind::Function,
1554-
"builder::{typ} not passed a function, but {fn_ty:?}"
1555-
);
1556-
1557-
let param_tys = self.cx.func_params_types(fn_ty);
1558-
1559-
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
1560-
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
1561-
1562-
if all_args_match {
1563-
return Cow::Borrowed(args);
1564-
}
1565-
1566-
let casted_args: Vec<_> = iter::zip(param_tys, args)
1567-
.enumerate()
1568-
.map(|(i, (expected_ty, &actual_val))| {
1569-
let actual_ty = self.cx.val_ty(actual_val);
1570-
if expected_ty != actual_ty {
1571-
debug!(
1572-
"type mismatch in function call of {:?}. \
1573-
Expected {:?} for param {}, got {:?}; injecting bitcast",
1574-
llfn, expected_ty, i, actual_ty
1575-
);
1576-
self.bitcast(actual_val, expected_ty)
1577-
} else {
1578-
actual_val
1579-
}
1580-
})
1581-
.collect();
1582-
1583-
Cow::Owned(casted_args)
1584-
}
1585-
15861548
pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value {
15871549
unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) }
15881550
}
@@ -1634,6 +1596,93 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16341596
}
16351597
}
16361598
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
1599+
fn autocast(
1600+
&mut self,
1601+
llfn: &'ll Value,
1602+
val: &'ll Value,
1603+
src_ty: &'ll Type,
1604+
dest_ty: &'ll Type,
1605+
is_argument: bool,
1606+
) -> &'ll Value {
1607+
let (rust_ty, llvm_ty) = if is_argument { (src_ty, dest_ty) } else { (dest_ty, src_ty) };
1608+
1609+
if rust_ty == llvm_ty {
1610+
return val;
1611+
}
1612+
1613+
match self.type_kind(llvm_ty) {
1614+
TypeKind::Struct => {
1615+
let mut ret = self.const_poison(dest_ty);
1616+
for (idx, (src_element_ty, dest_element_ty)) in
1617+
iter::zip(self.struct_element_types(src_ty), self.struct_element_types(dest_ty))
1618+
.enumerate()
1619+
{
1620+
let elt = self.extract_value(val, idx as u64);
1621+
let casted_elt =
1622+
self.autocast(llfn, elt, src_element_ty, dest_element_ty, is_argument);
1623+
ret = self.insert_value(ret, casted_elt, idx as u64);
1624+
}
1625+
ret
1626+
}
1627+
_ => unreachable!(),
1628+
}
1629+
}
1630+
1631+
fn cast_arguments<'b>(
1632+
&mut self,
1633+
typ: &str,
1634+
fn_ty: &'ll Type,
1635+
llfn: &'ll Value,
1636+
args: &'b [&'ll Value],
1637+
has_fnabi: bool,
1638+
) -> Cow<'b, [&'ll Value]> {
1639+
assert_eq!(
1640+
self.type_kind(fn_ty),
1641+
TypeKind::Function,
1642+
"{typ} not passed a function, but {fn_ty:?}"
1643+
);
1644+
1645+
let param_tys = self.func_params_types(fn_ty);
1646+
1647+
let mut casted_args = Cow::Borrowed(args);
1648+
1649+
for (idx, (dest_ty, &arg)) in iter::zip(param_tys, args).enumerate() {
1650+
let src_ty = self.val_ty(arg);
1651+
assert!(
1652+
self.equate_ty(src_ty, dest_ty),
1653+
"Cannot match `{dest_ty:?}` (expected) with `{src_ty:?}` (found) in `{llfn:?}`"
1654+
);
1655+
1656+
let casted_arg = self.autocast(llfn, arg, src_ty, dest_ty, true);
1657+
if arg != casted_arg {
1658+
assert!(
1659+
has_fnabi,
1660+
"Should inject autocasts in function call of {llfn:?}, but not able to get Rust signature"
1661+
);
1662+
1663+
casted_args.to_mut()[idx] = casted_arg;
1664+
}
1665+
}
1666+
1667+
casted_args
1668+
}
1669+
1670+
fn cast_return(
1671+
&mut self,
1672+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1673+
llfn: &'ll Value,
1674+
ret: &'ll Value,
1675+
) -> &'ll Value {
1676+
let src_ty = self.val_ty(ret);
1677+
let dest_ty = fn_abi.llvm_return_type(self);
1678+
assert!(
1679+
self.equate_ty(dest_ty, src_ty),
1680+
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{llfn:?}`"
1681+
);
1682+
1683+
self.autocast(llfn, ret, src_ty, dest_ty, false)
1684+
}
1685+
16371686
pub(crate) fn landing_pad(
16381687
&mut self,
16391688
ty: &'ll Type,
@@ -1663,7 +1712,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
16631712
) -> &'ll Value {
16641713
debug!("invoke {:?} with args ({:?})", llfn, args);
16651714

1666-
let args = self.check_call("callbr", llty, llfn, args);
1715+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
16671716
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
16681717
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
16691718
if let Some(funclet_bundle) = funclet_bundle {
@@ -1696,8 +1745,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
16961745
};
16971746
if let Some(fn_abi) = fn_abi {
16981747
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1748+
self.cast_return(fn_abi, llfn, callbr)
1749+
} else {
1750+
callbr
16991751
}
1700-
callbr
17011752
}
17021753

17031754
// Emits CFI pointer type membership tests.

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,9 @@ unsafe extern "C" {
17351735
Packed: Bool,
17361736
);
17371737

1738+
pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint;
1739+
pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type);
1740+
17381741
pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
17391742

17401743
pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr);

compiler/rustc_codegen_llvm/src/type_.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
8181
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
8282
unsafe { llvm::LLVMIsFunctionVarArg(ty) == True }
8383
}
84+
85+
pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
86+
unsafe {
87+
let n_args = llvm::LLVMCountStructElementTypes(ty) as usize;
88+
let mut args = Vec::with_capacity(n_args);
89+
llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr());
90+
args.set_len(n_args);
91+
args
92+
}
93+
}
8494
}
8595
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
8696
pub(crate) fn type_bool(&self) -> &'ll Type {

0 commit comments

Comments
 (0)