Skip to content

Commit ed74919

Browse files
committed
Check for AutoUpgraded intrinsics, and emit a hard error for unknown intrinsics
1 parent 6684561 commit ed74919

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;
@@ -355,6 +356,36 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
355356
);
356357
}
357358

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

445476
iter::once((rust_return_ty, llvm_return_ty))
446477
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
447-
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
478+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
448479
}
449480

450481
fn llvm_type(

compiler/rustc_codegen_llvm/src/builder.rs

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

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

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

453454
fn unreachable(&mut self) {
@@ -1395,7 +1396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13951396
) -> &'ll Value {
13961397
debug!("call {:?} with args ({:?})", llfn, args);
13971398

1398-
let args = self.check_call("call", llty, llfn, args);
1399+
let args = self.cast_arguments("call", llty, llfn, args, fn_abi.is_some());
13991400
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
14001401
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
14011402
if let Some(funclet_bundle) = funclet_bundle {
@@ -1448,8 +1449,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14481449

14491450
if let Some(fn_abi) = fn_abi {
14501451
fn_abi.apply_attrs_callsite(self, call, llfn);
1452+
self.cast_return(fn_abi, llfn, call)
1453+
} else {
1454+
call
14511455
}
1452-
call
14531456
}
14541457

14551458
fn tail_call(
@@ -1630,47 +1633,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16301633
ret.expect("LLVM does not have support for catchret")
16311634
}
16321635

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

1758-
let args = self.check_call("callbr", llty, llfn, args);
1807+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
17591808
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
17601809
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
17611810
if let Some(funclet_bundle) = funclet_bundle {
@@ -1788,8 +1837,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17881837
};
17891838
if let Some(fn_abi) = fn_abi {
17901839
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1840+
self.cast_return(fn_abi, llfn, callbr)
1841+
} else {
1842+
callbr
17911843
}
1792-
callbr
17931844
}
17941845

17951846
// 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
@@ -1135,6 +1135,11 @@ unsafe extern "C" {
11351135
ParamTypes: *const &'a Type,
11361136
ParamCount: size_t,
11371137
) -> &'a Value;
1138+
pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>(
1139+
Fn: &'a Value,
1140+
NewFn: &mut Option<&'a Value>,
1141+
CanUpgradeDebugIntrinsicsToRecords: bool,
1142+
) -> bool;
11381143

11391144
// Operations on parameters
11401145
pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>;
@@ -1651,6 +1656,9 @@ unsafe extern "C" {
16511656
Packed: Bool,
16521657
);
16531658

1659+
pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint;
1660+
pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type);
1661+
16541662
pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
16551663

16561664
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"
@@ -1765,6 +1766,17 @@ extern "C" void LLVMRustGetMangledName(LLVMValueRef V, RustStringRef Str) {
17651766
Mangler().getNameWithPrefix(OS, GV, true);
17661767
}
17671768

1769+
extern "C" bool
1770+
LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, LLVMValueRef *NewFn,
1771+
bool canUpgradeDebugIntrinsicsToRecords) {
1772+
Function *F = unwrap<Function>(Fn);
1773+
Function *NewF = nullptr;
1774+
bool CanUpgrade =
1775+
UpgradeIntrinsicFunction(F, NewF, canUpgradeDebugIntrinsicsToRecords);
1776+
*NewFn = wrap(NewF);
1777+
return CanUpgrade;
1778+
}
1779+
17681780
extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
17691781
auto *CB = unwrap<CallBase>(CallSite);
17701782
switch (CB->getIntrinsicID()) {

0 commit comments

Comments
 (0)