Skip to content

Commit 260f098

Browse files
committed
Check for AutoUpgraded intrinsics, and emit a hard error for unknown intrinsics
1 parent c9fdc0b commit 260f098

File tree

6 files changed

+183
-51
lines changed

6 files changed

+183
-51
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 33 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::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 {
@@ -448,7 +479,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
448479

449480
iter::once((rust_return_ty, llvm_return_ty))
450481
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
451-
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
482+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
452483
}
453484

454485
fn llvm_type(

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 {
@@ -1454,8 +1455,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14541455

14551456
if let Some(fn_abi) = fn_abi {
14561457
fn_abi.apply_attrs_callsite(self, call, llfn);
1458+
self.cast_return(fn_abi, llfn, call)
1459+
} else {
1460+
call
14571461
}
1458-
call
14591462
}
14601463

14611464
fn tail_call(
@@ -1645,47 +1648,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16451648
ret.expect("LLVM does not have support for catchret")
16461649
}
16471650

1648-
fn check_call<'b>(
1649-
&mut self,
1650-
typ: &str,
1651-
fn_ty: &'ll Type,
1652-
llfn: &'ll Value,
1653-
args: &'b [&'ll Value],
1654-
) -> Cow<'b, [&'ll Value]> {
1655-
assert!(
1656-
self.cx.type_kind(fn_ty) == TypeKind::Function,
1657-
"builder::{typ} not passed a function, but {fn_ty:?}"
1658-
);
1659-
1660-
let param_tys = self.cx.func_params_types(fn_ty);
1661-
1662-
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
1663-
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
1664-
1665-
if all_args_match {
1666-
return Cow::Borrowed(args);
1667-
}
1668-
1669-
let casted_args: Vec<_> = iter::zip(param_tys, args)
1670-
.enumerate()
1671-
.map(|(i, (expected_ty, &actual_val))| {
1672-
let actual_ty = self.cx.val_ty(actual_val);
1673-
if expected_ty != actual_ty {
1674-
debug!(
1675-
"type mismatch in function call of {:?}. \
1676-
Expected {:?} for param {}, got {:?}; injecting bitcast",
1677-
llfn, expected_ty, i, actual_ty
1678-
);
1679-
self.bitcast(actual_val, expected_ty)
1680-
} else {
1681-
actual_val
1682-
}
1683-
})
1684-
.collect();
1685-
1686-
Cow::Owned(casted_args)
1687-
}
1688-
16891651
pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value {
16901652
unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) }
16911653
}
@@ -1741,6 +1703,93 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
17411703
}
17421704
}
17431705
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
1706+
fn autocast(
1707+
&mut self,
1708+
llfn: &'ll Value,
1709+
val: &'ll Value,
1710+
src_ty: &'ll Type,
1711+
dest_ty: &'ll Type,
1712+
is_argument: bool,
1713+
) -> &'ll Value {
1714+
let (rust_ty, llvm_ty) = if is_argument { (src_ty, dest_ty) } else { (dest_ty, src_ty) };
1715+
1716+
if rust_ty == llvm_ty {
1717+
return val;
1718+
}
1719+
1720+
match self.type_kind(llvm_ty) {
1721+
TypeKind::Struct => {
1722+
let mut ret = self.const_poison(dest_ty);
1723+
for (idx, (src_element_ty, dest_element_ty)) in
1724+
iter::zip(self.struct_element_types(src_ty), self.struct_element_types(dest_ty))
1725+
.enumerate()
1726+
{
1727+
let elt = self.extract_value(val, idx as u64);
1728+
let casted_elt =
1729+
self.autocast(llfn, elt, src_element_ty, dest_element_ty, is_argument);
1730+
ret = self.insert_value(ret, casted_elt, idx as u64);
1731+
}
1732+
ret
1733+
}
1734+
_ => unreachable!(),
1735+
}
1736+
}
1737+
1738+
fn cast_arguments<'b>(
1739+
&mut self,
1740+
typ: &str,
1741+
fn_ty: &'ll Type,
1742+
llfn: &'ll Value,
1743+
args: &'b [&'ll Value],
1744+
has_fnabi: bool,
1745+
) -> Cow<'b, [&'ll Value]> {
1746+
assert_eq!(
1747+
self.type_kind(fn_ty),
1748+
TypeKind::Function,
1749+
"{typ} not passed a function, but {fn_ty:?}"
1750+
);
1751+
1752+
let param_tys = self.func_params_types(fn_ty);
1753+
1754+
let mut casted_args = Cow::Borrowed(args);
1755+
1756+
for (idx, (dest_ty, &arg)) in iter::zip(param_tys, args).enumerate() {
1757+
let src_ty = self.val_ty(arg);
1758+
assert!(
1759+
self.equate_ty(src_ty, dest_ty),
1760+
"Cannot match `{dest_ty:?}` (expected) with `{src_ty:?}` (found) in `{llfn:?}`"
1761+
);
1762+
1763+
let casted_arg = self.autocast(llfn, arg, src_ty, dest_ty, true);
1764+
if arg != casted_arg {
1765+
assert!(
1766+
has_fnabi,
1767+
"Should inject autocasts in function call of {llfn:?}, but not able to get Rust signature"
1768+
);
1769+
1770+
casted_args.to_mut()[idx] = casted_arg;
1771+
}
1772+
}
1773+
1774+
casted_args
1775+
}
1776+
1777+
fn cast_return(
1778+
&mut self,
1779+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1780+
llfn: &'ll Value,
1781+
ret: &'ll Value,
1782+
) -> &'ll Value {
1783+
let src_ty = self.val_ty(ret);
1784+
let dest_ty = fn_abi.llvm_return_type(self);
1785+
assert!(
1786+
self.equate_ty(dest_ty, src_ty),
1787+
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{llfn:?}`"
1788+
);
1789+
1790+
self.autocast(llfn, ret, src_ty, dest_ty, false)
1791+
}
1792+
17441793
pub(crate) fn landing_pad(
17451794
&mut self,
17461795
ty: &'ll Type,
@@ -1770,7 +1819,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17701819
) -> &'ll Value {
17711820
debug!("invoke {:?} with args ({:?})", llfn, args);
17721821

1773-
let args = self.check_call("callbr", llty, llfn, args);
1822+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
17741823
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
17751824
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
17761825
if let Some(funclet_bundle) = funclet_bundle {
@@ -1803,8 +1852,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18031852
};
18041853
if let Some(fn_abi) = fn_abi {
18051854
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1855+
self.cast_return(fn_abi, llfn, callbr)
1856+
} else {
1857+
callbr
18061858
}
1807-
callbr
18081859
}
18091860

18101861
// Emits CFI pointer type membership tests.

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,27 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
177177
fn_abi.apply_attrs_llfn(self, llfn, instance);
178178
}
179179

180-
// todo: check for upgrades, and emit error if not upgradable
180+
if let FunctionSignature::MaybeInvalidIntrinsic(..) = signature {
181+
let mut new_llfn = None;
182+
let can_upgrade =
183+
unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn, false) };
184+
185+
if can_upgrade {
186+
// not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences
187+
if let Some(new_llfn) = new_llfn {
188+
self.tcx.dcx().note(format!(
189+
"Using deprecated intrinsic `{name}`, `{}` can be used instead",
190+
str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap()
191+
));
192+
} else {
193+
self.tcx.dcx().note(format!(
194+
"Using deprecated intrinsic `{name}`, consider using other intrinsics/instructions"
195+
));
196+
}
197+
} else {
198+
self.tcx.dcx().fatal(format!("Invalid LLVM intrinsic: `{name}`"))
199+
}
200+
}
181201

182202
if self.tcx.sess.is_sanitizer_cfi_enabled() {
183203
if let Some(instance) = instance {

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,11 @@ unsafe extern "C" {
12671267
ParamTypes: *const &'a Type,
12681268
ParamCount: size_t,
12691269
) -> &'a Value;
1270+
pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>(
1271+
Fn: &'a Value,
1272+
NewFn: &mut Option<&'a Value>,
1273+
CanUpgradeDebugIntrinsicsToRecords: bool,
1274+
) -> bool;
12701275

12711276
// Operations on parameters
12721277
pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>;
@@ -1783,6 +1788,9 @@ unsafe extern "C" {
17831788
Packed: Bool,
17841789
);
17851790

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

17881796
pub(crate) safe 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
@@ -87,6 +87,16 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
8787
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
8888
unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() }
8989
}
90+
91+
pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
92+
unsafe {
93+
let n_args = llvm::LLVMCountStructElementTypes(ty) as usize;
94+
let mut args = Vec::with_capacity(n_args);
95+
llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr());
96+
args.set_len(n_args);
97+
args
98+
}
99+
}
90100
}
91101
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
92102
pub(crate) fn type_bool(&self) -> &'ll Type {

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/ADT/StringRef.h"
1010
#include "llvm/BinaryFormat/Magic.h"
1111
#include "llvm/Bitcode/BitcodeWriter.h"
12+
#include "llvm/IR/AutoUpgrade.h"
1213
#include "llvm/IR/DIBuilder.h"
1314
#include "llvm/IR/DebugInfoMetadata.h"
1415
#include "llvm/IR/DiagnosticHandler.h"
@@ -1897,6 +1898,17 @@ extern "C" void LLVMRustGetMangledName(LLVMValueRef V, RustStringRef Str) {
18971898
Mangler().getNameWithPrefix(OS, GV, true);
18981899
}
18991900

1901+
extern "C" bool
1902+
LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, LLVMValueRef *NewFn,
1903+
bool canUpgradeDebugIntrinsicsToRecords) {
1904+
Function *F = unwrap<Function>(Fn);
1905+
Function *NewF = nullptr;
1906+
bool CanUpgrade =
1907+
UpgradeIntrinsicFunction(F, NewF, canUpgradeDebugIntrinsicsToRecords);
1908+
*NewFn = wrap(NewF);
1909+
return CanUpgrade;
1910+
}
1911+
19001912
extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
19011913
auto *CB = unwrap<CallBase>(CallSite);
19021914
switch (CB->getIntrinsicID()) {

0 commit comments

Comments
 (0)