Skip to content

Commit 29dca05

Browse files
committed
Add auto-destructuring for structs
- Remove redundant bitcasts at callsite
1 parent 3309954 commit 29dca05

File tree

7 files changed

+157
-57
lines changed

7 files changed

+157
-57
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 34 additions & 4 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::llvm_util;
2829
use crate::type_::Type;
@@ -359,6 +360,36 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
359360
);
360361
}
361362

363+
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
364+
pub(crate) fn equate_ty(&self, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool {
365+
if rust_ty == llvm_ty {
366+
return true;
367+
}
368+
369+
// Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust
370+
// due to auto field-alignment in non-packed structs (packed structs are represented in LLVM
371+
// as, well, packed structs, so they won't match with those either)
372+
if self.type_kind(llvm_ty) == TypeKind::Struct
373+
&& self.type_kind(rust_ty) == TypeKind::Struct
374+
{
375+
let rust_element_tys = self.struct_element_types(rust_ty);
376+
let llvm_element_tys = self.struct_element_types(llvm_ty);
377+
378+
if rust_element_tys.len() != llvm_element_tys.len() {
379+
return false;
380+
}
381+
382+
iter::zip(rust_element_tys, llvm_element_tys).all(
383+
|(rust_element_ty, llvm_element_ty)| {
384+
self.equate_ty(rust_element_ty, llvm_element_ty)
385+
},
386+
)
387+
} else {
388+
false
389+
}
390+
}
391+
}
392+
362393
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
363394
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
364395
match &self.ret.mode {
@@ -445,10 +476,9 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
445476
return false;
446477
}
447478

448-
// todo: add bypasses for types not accessible from Rust here
449479
iter::once((rust_return_ty, llvm_return_ty))
450480
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
451-
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
481+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
452482
}
453483

454484
fn llvm_type(
@@ -657,7 +687,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
657687
// if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
658688
let name = llvm::get_value_name(llfn);
659689
if name.starts_with(b"llvm.")
660-
&& let Some(intrinsic) = llvm::Intrinsic::lookup(name)
690+
&& let Some(intrinsic) = llvm::Intrinsic::lookup(&name)
661691
{
662692
// FIXME: also do this for overloaded intrinsics
663693
if !intrinsic.is_overloaded() {

compiler/rustc_codegen_llvm/src/builder.rs

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

71-
let args = self.check_call("call", llty, llfn, args);
7271
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
7372
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
7473
if let Some(funclet_bundle) = funclet_bundle {
@@ -415,7 +414,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
415414
) -> &'ll Value {
416415
debug!("invoke {:?} with args ({:?})", llfn, args);
417416

418-
let args = self.check_call("invoke", llty, llfn, args);
417+
let args = self.cast_arguments("invoke", llty, llfn, args, fn_abi.is_some());
419418
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
420419
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
421420
if let Some(funclet_bundle) = funclet_bundle {
@@ -447,8 +446,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
447446
};
448447
if let Some(fn_abi) = fn_abi {
449448
fn_abi.apply_attrs_callsite(self, invoke, llfn);
449+
self.cast_return(fn_abi, llfn, invoke)
450+
} else {
451+
invoke
450452
}
451-
invoke
452453
}
453454

454455
fn unreachable(&mut self) {
@@ -1401,7 +1402,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14011402
) -> &'ll Value {
14021403
debug!("call {:?} with args ({:?})", llfn, args);
14031404

1404-
let args = self.check_call("call", llty, llfn, args);
1405+
let args = self.cast_arguments("call", llty, llfn, args, fn_abi.is_some());
14051406
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
14061407
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
14071408
if let Some(funclet_bundle) = funclet_bundle {
@@ -1431,8 +1432,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14311432
};
14321433
if let Some(fn_abi) = fn_abi {
14331434
fn_abi.apply_attrs_callsite(self, call, llfn);
1435+
self.cast_return(fn_abi, llfn, call)
1436+
} else {
1437+
call
14341438
}
1435-
call
14361439
}
14371440

14381441
fn tail_call(
@@ -1622,47 +1625,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16221625
ret.expect("LLVM does not have support for catchret")
16231626
}
16241627

1625-
fn check_call<'b>(
1626-
&mut self,
1627-
typ: &str,
1628-
fn_ty: &'ll Type,
1629-
llfn: &'ll Value,
1630-
args: &'b [&'ll Value],
1631-
) -> Cow<'b, [&'ll Value]> {
1632-
assert!(
1633-
self.cx.type_kind(fn_ty) == TypeKind::Function,
1634-
"builder::{typ} not passed a function, but {fn_ty:?}"
1635-
);
1636-
1637-
let param_tys = self.cx.func_params_types(fn_ty);
1638-
1639-
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
1640-
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
1641-
1642-
if all_args_match {
1643-
return Cow::Borrowed(args);
1644-
}
1645-
1646-
let casted_args: Vec<_> = iter::zip(param_tys, args)
1647-
.enumerate()
1648-
.map(|(i, (expected_ty, &actual_val))| {
1649-
let actual_ty = self.cx.val_ty(actual_val);
1650-
if expected_ty != actual_ty {
1651-
debug!(
1652-
"type mismatch in function call of {:?}. \
1653-
Expected {:?} for param {}, got {:?}; injecting bitcast",
1654-
llfn, expected_ty, i, actual_ty
1655-
);
1656-
self.bitcast(actual_val, expected_ty)
1657-
} else {
1658-
actual_val
1659-
}
1660-
})
1661-
.collect();
1662-
1663-
Cow::Owned(casted_args)
1664-
}
1665-
16661628
pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value {
16671629
unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) }
16681630
}
@@ -1718,6 +1680,93 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
17181680
}
17191681
}
17201682
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
1683+
fn autocast(
1684+
&mut self,
1685+
llfn: &'ll Value,
1686+
val: &'ll Value,
1687+
src_ty: &'ll Type,
1688+
dest_ty: &'ll Type,
1689+
is_argument: bool,
1690+
) -> &'ll Value {
1691+
let (rust_ty, llvm_ty) = if is_argument { (src_ty, dest_ty) } else { (dest_ty, src_ty) };
1692+
1693+
if rust_ty == llvm_ty {
1694+
return val;
1695+
}
1696+
1697+
match self.type_kind(llvm_ty) {
1698+
TypeKind::Struct => {
1699+
let mut ret = self.const_poison(dest_ty);
1700+
for (idx, (src_element_ty, dest_element_ty)) in
1701+
iter::zip(self.struct_element_types(src_ty), self.struct_element_types(dest_ty))
1702+
.enumerate()
1703+
{
1704+
let elt = self.extract_value(val, idx as u64);
1705+
let casted_elt =
1706+
self.autocast(llfn, elt, src_element_ty, dest_element_ty, is_argument);
1707+
ret = self.insert_value(ret, casted_elt, idx as u64);
1708+
}
1709+
ret
1710+
}
1711+
_ => unreachable!(),
1712+
}
1713+
}
1714+
1715+
fn cast_arguments<'b>(
1716+
&mut self,
1717+
typ: &str,
1718+
fn_ty: &'ll Type,
1719+
llfn: &'ll Value,
1720+
args: &'b [&'ll Value],
1721+
has_fnabi: bool,
1722+
) -> Cow<'b, [&'ll Value]> {
1723+
assert_eq!(
1724+
self.type_kind(fn_ty),
1725+
TypeKind::Function,
1726+
"{typ} not passed a function, but {fn_ty:?}"
1727+
);
1728+
1729+
let param_tys = self.func_params_types(fn_ty);
1730+
1731+
let mut casted_args = Cow::Borrowed(args);
1732+
1733+
for (idx, (dest_ty, &arg)) in iter::zip(param_tys, args).enumerate() {
1734+
let src_ty = self.val_ty(arg);
1735+
assert!(
1736+
self.equate_ty(src_ty, dest_ty),
1737+
"Cannot match `{dest_ty:?}` (expected) with `{src_ty:?}` (found) in `{llfn:?}`"
1738+
);
1739+
1740+
let casted_arg = self.autocast(llfn, arg, src_ty, dest_ty, true);
1741+
if arg != casted_arg {
1742+
assert!(
1743+
has_fnabi,
1744+
"Should inject autocasts in function call of {llfn:?}, but not able to get Rust signature"
1745+
);
1746+
1747+
casted_args.to_mut()[idx] = casted_arg;
1748+
}
1749+
}
1750+
1751+
casted_args
1752+
}
1753+
1754+
fn cast_return(
1755+
&mut self,
1756+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1757+
llfn: &'ll Value,
1758+
ret: &'ll Value,
1759+
) -> &'ll Value {
1760+
let src_ty = self.val_ty(ret);
1761+
let dest_ty = fn_abi.llvm_return_type(self);
1762+
assert!(
1763+
self.equate_ty(dest_ty, src_ty),
1764+
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{llfn:?}`"
1765+
);
1766+
1767+
self.autocast(llfn, ret, src_ty, dest_ty, false)
1768+
}
1769+
17211770
pub(crate) fn landing_pad(
17221771
&mut self,
17231772
ty: &'ll Type,
@@ -1747,7 +1796,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17471796
) -> &'ll Value {
17481797
debug!("invoke {:?} with args ({:?})", llfn, args);
17491798

1750-
let args = self.check_call("callbr", llty, llfn, args);
1799+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
17511800
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
17521801
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
17531802
if let Some(funclet_bundle) = funclet_bundle {
@@ -1780,8 +1829,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17801829
};
17811830
if let Some(fn_abi) = fn_abi {
17821831
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1832+
self.cast_return(fn_abi, llfn, callbr)
1833+
} else {
1834+
callbr
17831835
}
1784-
callbr
17851836
}
17861837

17871838
// Emits CFI pointer type membership tests.

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
187187
if let Some(new_llfn) = new_llfn {
188188
self.tcx.dcx().note(format!(
189189
"Using deprecated intrinsic `{name}`, `{}` can be used instead",
190-
str::from_utf8(llvm::get_value_name(new_llfn)).unwrap()
190+
str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap()
191191
));
192192
} else if self.tcx.sess.opts.verbose {
193193
self.tcx.dcx().note(format!(

compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ unsafe extern "C" {
4646
pub(crate) fn LLVMDumpModule(M: &Module);
4747
pub(crate) fn LLVMDumpValue(V: &Value);
4848
pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint;
49-
pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type;
5049
pub(crate) fn LLVMGetParams(Fnc: &Value, params: *mut &Value);
5150
pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>;
5251
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ unsafe extern "C" {
11101110
) -> &'a Type;
11111111
pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint;
11121112
pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type);
1113+
pub(crate) fn LLVMGetReturnType(FunctionTy: &Type) -> &Type;
11131114
pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool;
11141115

11151116
// Operations on struct types
@@ -1787,6 +1788,9 @@ unsafe extern "C" {
17871788
Packed: Bool,
17881789
);
17891790

1791+
pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint;
1792+
pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type);
1793+
17901794
pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
17911795

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

compiler/rustc_codegen_llvm/src/llvm/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ impl Intrinsic {
305305
}
306306

307307
pub(crate) fn is_overloaded(self) -> bool {
308-
unsafe { LLVMIntrinsicIsOverloaded(self.id) == True }
308+
unsafe { LLVMIntrinsicIsOverloaded(self.id).is_true() }
309309
}
310310

311311
pub(crate) fn get_type<'ll>(self, llcx: &'ll Context, type_params: &[&'ll Type]) -> &'ll Type {

compiler/rustc_codegen_llvm/src/type_.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
7070
unsafe { llvm::LLVMVectorType(ty, len as c_uint) }
7171
}
7272

73+
pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type {
74+
unsafe {
75+
llvm::LLVMGetReturnType(ty)
76+
}
77+
}
78+
7379
pub(crate) fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
7480
unsafe {
7581
let n_args = llvm::LLVMCountParamTypes(ty) as usize;
@@ -81,7 +87,17 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
8187
}
8288

8389
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
84-
unsafe { llvm::LLVMIsFunctionVarArg(ty) == True }
90+
unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() }
91+
}
92+
93+
pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
94+
unsafe {
95+
let n_args = llvm::LLVMCountStructElementTypes(ty) as usize;
96+
let mut args = Vec::with_capacity(n_args);
97+
llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr());
98+
args.set_len(n_args);
99+
args
100+
}
85101
}
86102
}
87103
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
@@ -297,7 +313,7 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
297313
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
298314
fn_ptr: &'ll Value,
299315
) -> &'ll Type {
300-
fn_abi.llvm_type(self, llvm::get_value_name(fn_ptr), false).fn_ty()
316+
fn_abi.llvm_type(self, &llvm::get_value_name(fn_ptr), false).fn_ty()
301317
}
302318
fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
303319
fn_abi.ptr_to_llvm_type(self)

0 commit comments

Comments
 (0)