Skip to content

Commit 4b42195

Browse files
committed
Check for AutoUpgraded intrinsics, and emit a hard error for unknown intrinsics
1 parent b247a7b commit 4b42195

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, Type, Value};
2728
use crate::llvm_util;
2829
use crate::type_of::LayoutLlvmExt;
@@ -353,6 +354,36 @@ 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+
// Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust
364+
// due to auto field-alignment in non-packed structs (packed structs are represented in LLVM
365+
// as, well, packed structs, so they won't match with those either)
366+
if self.type_kind(llvm_ty) == TypeKind::Struct
367+
&& self.type_kind(rust_ty) == TypeKind::Struct
368+
{
369+
let rust_element_tys = self.struct_element_types(rust_ty);
370+
let llvm_element_tys = self.struct_element_types(llvm_ty);
371+
372+
if rust_element_tys.len() != llvm_element_tys.len() {
373+
return false;
374+
}
375+
376+
iter::zip(rust_element_tys, llvm_element_tys).all(
377+
|(rust_element_ty, llvm_element_ty)| {
378+
self.equate_ty(rust_element_ty, llvm_element_ty)
379+
},
380+
)
381+
} else {
382+
false
383+
}
384+
}
385+
}
386+
356387
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
357388
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
358389
match &self.ret.mode {
@@ -442,7 +473,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
442473

443474
iter::once((rust_return_ty, llvm_return_ty))
444475
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
445-
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
476+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
446477
}
447478

448479
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 {
@@ -412,7 +411,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
412411
) -> &'ll Value {
413412
debug!("invoke {:?} with args ({:?})", llfn, args);
414413

415-
let args = self.check_call("invoke", llty, llfn, args);
414+
let args = self.cast_arguments("invoke", llty, llfn, args, fn_abi.is_some());
416415
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
417416
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
418417
if let Some(funclet_bundle) = funclet_bundle {
@@ -444,8 +443,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
444443
};
445444
if let Some(fn_abi) = fn_abi {
446445
fn_abi.apply_attrs_callsite(self, invoke, llfn);
446+
self.cast_return(fn_abi, llfn, invoke)
447+
} else {
448+
invoke
447449
}
448-
invoke
449450
}
450451

451452
fn unreachable(&mut self) {
@@ -1393,7 +1394,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13931394
) -> &'ll Value {
13941395
debug!("call {:?} with args ({:?})", llfn, args);
13951396

1396-
let args = self.check_call("call", llty, llfn, args);
1397+
let args = self.cast_arguments("call", llty, llfn, args, fn_abi.is_some());
13971398
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
13981399
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
13991400
if let Some(funclet_bundle) = funclet_bundle {
@@ -1446,8 +1447,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14461447

14471448
if let Some(fn_abi) = fn_abi {
14481449
fn_abi.apply_attrs_callsite(self, call, llfn);
1450+
self.cast_return(fn_abi, llfn, call)
1451+
} else {
1452+
call
14491453
}
1450-
call
14511454
}
14521455

14531456
fn tail_call(
@@ -1628,47 +1631,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16281631
ret.expect("LLVM does not have support for catchret")
16291632
}
16301633

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

1756-
let args = self.check_call("callbr", llty, llfn, args);
1805+
let args = self.cast_arguments("callbr", llty, llfn, args, fn_abi.is_some());
17571806
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
17581807
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
17591808
if let Some(funclet_bundle) = funclet_bundle {
@@ -1786,8 +1835,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17861835
};
17871836
if let Some(fn_abi) = fn_abi {
17881837
fn_abi.apply_attrs_callsite(self, callbr, llfn);
1838+
self.cast_return(fn_abi, llfn, callbr)
1839+
} else {
1840+
callbr
17891841
}
1790-
callbr
17911842
}
17921843

17931844
// 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
@@ -175,7 +175,27 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
175175
fn_abi.apply_attrs_llfn(self, llfn, instance);
176176
}
177177

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

180200
if self.tcx.sess.is_sanitizer_cfi_enabled() {
181201
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
@@ -1134,6 +1134,11 @@ unsafe extern "C" {
11341134
ParamTypes: *const &'a Type,
11351135
ParamCount: size_t,
11361136
) -> &'a Value;
1137+
pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>(
1138+
Fn: &'a Value,
1139+
NewFn: &mut Option<&'a Value>,
1140+
CanUpgradeDebugIntrinsicsToRecords: bool,
1141+
) -> bool;
11371142

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

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

16551663
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
@@ -85,6 +85,16 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
8585
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
8686
unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() }
8787
}
88+
89+
pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
90+
unsafe {
91+
let n_args = llvm::LLVMCountStructElementTypes(ty) as usize;
92+
let mut args = Vec::with_capacity(n_args);
93+
llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr());
94+
args.set_len(n_args);
95+
args
96+
}
97+
}
8898
}
8999
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
90100
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)