Skip to content

Commit bd6b84a

Browse files
committed
Add auto-destructuring for structs
- Remove redundant bitcasts at callsite edit (squash with struct)
1 parent b717dd2 commit bd6b84a

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
@@ -67,7 +67,6 @@ impl<'a, 'll> SBuilder<'a, 'll> {
6767
) -> &'ll Value {
6868
debug!("call {:?} with args ({:?})", llfn, args);
6969

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

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

388389
fn unreachable(&mut self) {
@@ -1404,7 +1405,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14041405
) -> &'ll Value {
14051406
debug!("call {:?} with args ({:?})", llfn, args);
14061407

1407-
let args = self.check_call("call", llty, llfn, args);
1408+
let args = self.cast_arguments("call", llty, llfn, args, fn_abi.is_some());
14081409
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
14091410
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
14101411
if let Some(funclet_bundle) = funclet_bundle {
@@ -1434,8 +1435,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14341435
};
14351436
if let Some(fn_abi) = fn_abi {
14361437
fn_abi.apply_attrs_callsite(self, call, llfn);
1438+
self.cast_return(fn_abi, llfn, call)
1439+
} else {
1440+
call
14371441
}
1438-
call
14391442
}
14401443

14411444
fn zext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
@@ -1602,47 +1605,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16021605
ret.expect("LLVM does not have support for catchret")
16031606
}
16041607

1605-
fn check_call<'b>(
1606-
&mut self,
1607-
typ: &str,
1608-
fn_ty: &'ll Type,
1609-
llfn: &'ll Value,
1610-
args: &'b [&'ll Value],
1611-
) -> Cow<'b, [&'ll Value]> {
1612-
assert!(
1613-
self.cx.type_kind(fn_ty) == TypeKind::Function,
1614-
"builder::{typ} not passed a function, but {fn_ty:?}"
1615-
);
1616-
1617-
let param_tys = self.cx.func_params_types(fn_ty);
1618-
1619-
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
1620-
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
1621-
1622-
if all_args_match {
1623-
return Cow::Borrowed(args);
1624-
}
1625-
1626-
let casted_args: Vec<_> = iter::zip(param_tys, args)
1627-
.enumerate()
1628-
.map(|(i, (expected_ty, &actual_val))| {
1629-
let actual_ty = self.cx.val_ty(actual_val);
1630-
if expected_ty != actual_ty {
1631-
debug!(
1632-
"type mismatch in function call of {:?}. \
1633-
Expected {:?} for param {}, got {:?}; injecting bitcast",
1634-
llfn, expected_ty, i, actual_ty
1635-
);
1636-
self.bitcast(actual_val, expected_ty)
1637-
} else {
1638-
actual_val
1639-
}
1640-
})
1641-
.collect();
1642-
1643-
Cow::Owned(casted_args)
1644-
}
1645-
16461608
pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value {
16471609
unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) }
16481610
}
@@ -1714,6 +1676,93 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17141676
self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, None)
17151677
}
17161678

1679+
fn autocast(
1680+
&mut self,
1681+
llfn: &'ll Value,
1682+
val: &'ll Value,
1683+
src_ty: &'ll Type,
1684+
dest_ty: &'ll Type,
1685+
is_argument: bool,
1686+
) -> &'ll Value {
1687+
let (rust_ty, llvm_ty) = if is_argument { (src_ty, dest_ty) } else { (dest_ty, src_ty) };
1688+
1689+
if rust_ty == llvm_ty {
1690+
return val;
1691+
}
1692+
1693+
match self.type_kind(llvm_ty) {
1694+
TypeKind::Struct => {
1695+
let mut ret = self.const_poison(dest_ty);
1696+
for (idx, (src_element_ty, dest_element_ty)) in
1697+
iter::zip(self.struct_element_types(src_ty), self.struct_element_types(dest_ty))
1698+
.enumerate()
1699+
{
1700+
let elt = self.extract_value(val, idx as u64);
1701+
let casted_elt =
1702+
self.autocast(llfn, elt, src_element_ty, dest_element_ty, is_argument);
1703+
ret = self.insert_value(ret, casted_elt, idx as u64);
1704+
}
1705+
ret
1706+
}
1707+
_ => unreachable!(),
1708+
}
1709+
}
1710+
1711+
fn cast_arguments<'b>(
1712+
&mut self,
1713+
typ: &str,
1714+
fn_ty: &'ll Type,
1715+
llfn: &'ll Value,
1716+
args: &'b [&'ll Value],
1717+
has_fnabi: bool,
1718+
) -> Cow<'b, [&'ll Value]> {
1719+
assert_eq!(
1720+
self.type_kind(fn_ty),
1721+
TypeKind::Function,
1722+
"{typ} not passed a function, but {fn_ty:?}"
1723+
);
1724+
1725+
let param_tys = self.func_params_types(fn_ty);
1726+
1727+
let mut casted_args = Cow::Borrowed(args);
1728+
1729+
for (idx, (dest_ty, &arg)) in iter::zip(param_tys, args).enumerate() {
1730+
let src_ty = self.val_ty(arg);
1731+
assert!(
1732+
self.equate_ty(src_ty, dest_ty),
1733+
"Cannot match `{dest_ty:?}` (expected) with `{src_ty:?}` (found) in `{llfn:?}`"
1734+
);
1735+
1736+
let casted_arg = self.autocast(llfn, arg, src_ty, dest_ty, true);
1737+
if arg != casted_arg {
1738+
assert!(
1739+
has_fnabi,
1740+
"Should inject autocasts in function call of {llfn:?}, but not able to get Rust signature"
1741+
);
1742+
1743+
casted_args.to_mut()[idx] = casted_arg;
1744+
}
1745+
}
1746+
1747+
casted_args
1748+
}
1749+
1750+
fn cast_return(
1751+
&mut self,
1752+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1753+
llfn: &'ll Value,
1754+
ret: &'ll Value,
1755+
) -> &'ll Value {
1756+
let src_ty = self.val_ty(ret);
1757+
let dest_ty = fn_abi.llvm_return_type(self);
1758+
assert!(
1759+
self.equate_ty(dest_ty, src_ty),
1760+
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{llfn:?}`"
1761+
);
1762+
1763+
self.autocast(llfn, ret, src_ty, dest_ty, false)
1764+
}
1765+
17171766
pub(crate) fn landing_pad(
17181767
&mut self,
17191768
ty: &'ll Type,
@@ -1743,7 +1792,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17431792
) -> &'ll Value {
17441793
debug!("invoke {:?} with args ({:?})", llfn, args);
17451794

1746-
let args = self.check_call("callbr", llty, llfn, args);
1795+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
17471796
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
17481797
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
17491798
if let Some(funclet_bundle) = funclet_bundle {
@@ -1776,8 +1825,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17761825
};
17771826
if let Some(fn_abi) = fn_abi {
17781827
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1828+
self.cast_return(fn_abi, llfn, callbr)
1829+
} else {
1830+
callbr
17791831
}
1780-
callbr
17811832
}
17821833

17831834
// 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
@@ -1731,6 +1731,9 @@ unsafe extern "C" {
17311731
Packed: Bool,
17321732
);
17331733

1734+
pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint;
1735+
pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type);
1736+
17341737
pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
17351738

17361739
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
@@ -79,6 +79,16 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
7979
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
8080
unsafe { llvm::LLVMIsFunctionVarArg(ty) == True }
8181
}
82+
83+
pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
84+
unsafe {
85+
let n_args = llvm::LLVMCountStructElementTypes(ty) as usize;
86+
let mut args = Vec::with_capacity(n_args);
87+
llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr());
88+
args.set_len(n_args);
89+
args
90+
}
91+
}
8292
}
8393
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
8494
pub(crate) fn type_bool(&self) -> &'ll Type {

0 commit comments

Comments
 (0)