Skip to content

Commit b5cb020

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

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
@@ -4,6 +4,7 @@ use std::{cmp, iter};
44
use libc::c_uint;
55
use rustc_abi::{BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size};
66
use rustc_codegen_ssa::MemFlags;
7+
use rustc_codegen_ssa::common::TypeKind;
78
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
89
use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
910
use rustc_codegen_ssa::traits::*;
@@ -19,7 +20,7 @@ use smallvec::SmallVec;
1920

2021
use crate::attributes::{self, llfn_attrs_from_instance};
2122
use crate::builder::Builder;
22-
use crate::context::CodegenCx;
23+
use crate::context::{CodegenCx, GenericCx, SCx};
2324
use crate::llvm::{self, Attribute, AttributePlace};
2425
use crate::type_::Type;
2526
use crate::type_of::LayoutLlvmExt;
@@ -360,6 +361,32 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
360361
);
361362
}
362363

364+
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
365+
pub(crate) fn equate_ty(&self, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool {
366+
if rust_ty == llvm_ty {
367+
return true;
368+
}
369+
370+
match self.type_kind(llvm_ty) {
371+
TypeKind::Struct if self.type_kind(rust_ty) == TypeKind::Struct => {
372+
let rust_element_tys = self.struct_element_types(rust_ty);
373+
let llvm_element_tys = self.struct_element_types(llvm_ty);
374+
375+
if rust_element_tys.len() != llvm_element_tys.len() {
376+
return false;
377+
}
378+
379+
iter::zip(rust_element_tys, llvm_element_tys).all(
380+
|(rust_element_ty, llvm_element_ty)| {
381+
self.equate_ty(rust_element_ty, llvm_element_ty)
382+
},
383+
)
384+
}
385+
_ => false,
386+
}
387+
}
388+
}
389+
363390
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
364391
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
365392
match &self.ret.mode {
@@ -449,7 +476,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
449476
// todo: add bypasses for types not accessible from Rust here
450477
iter::once((rust_return_ty, llvm_return_ty))
451478
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
452-
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
479+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
453480
}
454481

455482
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 {
@@ -1596,47 +1599,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
15961599
ret.expect("LLVM does not have support for catchret")
15971600
}
15981601

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

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

1740-
let args = self.check_call("callbr", llty, llfn, args);
1789+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
17411790
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
17421791
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
17431792
if let Some(funclet_bundle) = funclet_bundle {
@@ -1770,8 +1819,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17701819
};
17711820
if let Some(fn_abi) = fn_abi {
17721821
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1822+
self.cast_return(fn_abi, llfn, callbr)
1823+
} else {
1824+
callbr
17731825
}
1774-
callbr
17751826
}
17761827

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

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

17351738
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)