Skip to content

Commit 5da2144

Browse files
committed
add -Zforce-intrinsic-fallback flag
1 parent 13f1859 commit 5da2144

5 files changed

Lines changed: 100 additions & 2 deletions

File tree

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
6363
result_place: Option<PlaceValue<Bx::Value>>,
6464
source_info: SourceInfo,
6565
) -> IntrinsicResult<'tcx, Bx::Value> {
66+
// When `-Zforce-intrinsic-fallback` is enabled, always use the fallback body if it exists,
67+
if bx.tcx().sess.opts.unstable_opts.force_intrinsic_fallback
68+
&& let Some(def) = bx.tcx().intrinsic(instance.def_id())
69+
&& !def.must_be_overridden
70+
{
71+
return IntrinsicResult::Fallback(ty::Instance::new_raw(
72+
instance.def_id(),
73+
instance.args,
74+
));
75+
}
76+
6677
let span = source_info.span;
6778

6879
let name = bx.tcx().item_name(instance.def_id());

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -995,11 +995,18 @@ fn visit_instance_use<'tcx>(
995995
output.push(create_fn_mono_item(tcx, panic_instance, source));
996996
}
997997
} else if !intrinsic.must_be_overridden
998-
&& !tcx.sess.replaced_intrinsics.contains(&intrinsic.name)
998+
&& (tcx.sess.opts.unstable_opts.force_intrinsic_fallback
999+
|| !tcx.sess.replaced_intrinsics.contains(&intrinsic.name))
9991000
{
10001001
// Codegen the fallback body of intrinsics with fallback bodies.
10011002
// We have to skip this otherwise as there's no body to codegen.
1002-
// We also skip intrinsics the backend handles, to reduce monomorphizations.
1003+
//
1004+
// We also skip `replaced_intrinsics` which are always replaced by the backend and hence
1005+
// monomorphizing the fallback body would be pointless.
1006+
//
1007+
// However, when -Zforce-intrinsic-fallback is set (e.g. to test the fallback
1008+
// implementations) we ignore the optimization hint and do monomorphize
1009+
// the fallback body.
10031010
let instance = ty::Instance::new_raw(instance.def_id(), instance.args);
10041011
if tcx.should_codegen_locally(instance) {
10051012
output.push(create_fn_mono_item(tcx, instance, source));

compiler/rustc_session/src/options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,9 @@ options! {
24132413
fmt_debug: FmtDebug = (FmtDebug::Full, parse_fmt_debug, [TRACKED],
24142414
"how detailed `#[derive(Debug)]` should be. `full` prints types recursively, \
24152415
`shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"),
2416+
force_intrinsic_fallback: bool = (false, parse_bool, [TRACKED],
2417+
"always use the fallback body of an intrinsic, if it has one, instead of lowering \
2418+
the intrinsic in the codegen backend (default: no)."),
24162419
force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED],
24172420
"force all crates to be `rustc_private` unstable (default: no)"),
24182421
function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED],
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## `force-intrinsic-fallback`
2+
3+
Configures codegen to always use the fallback body of an intrinsic, if it has one,
4+
instead of lowering the intrinsic in the codegen backend.
5+
6+
This is useful for testing the fallback implementation.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//@ add-minicore
2+
//@ assembly-output: emit-asm
3+
//@ compile-flags: --crate-type=lib -Copt-level=3
4+
//
5+
//@ revisions: NORMAL FALLBACK
6+
//@ [FALLBACK] compile-flags: -Zforce-intrinsic-fallback
7+
//
8+
//@ needs-llvm-components: x86
9+
//@ compile-flags: --target=x86_64-unknown-linux-gnu
10+
#![feature(no_core, rustc_attrs, intrinsics)]
11+
#![no_core]
12+
13+
extern crate minicore;
14+
// use minicore::*;
15+
16+
// Check the effect of `-Zforce-intrinsic-fallback`.
17+
//
18+
// When enabled, the backend should insert a libcall. When enabled, for the setup of this test,
19+
// the dedicated instruction should be used instead.
20+
21+
#[no_mangle]
22+
fn call_sqrtf32(x: f32) -> f32 {
23+
// CHECK-LABEL: call_sqrtf32
24+
25+
// Without the flag: dedicated instruction, no libcall.
26+
// NORMAL: tail call float @llvm.sqrt.f32
27+
// NORMAL-NOT: sqrtf
28+
29+
// With the flag: libcall
30+
// FALLBACK: tail call noundef float @sqrtf
31+
sqrtf32(x)
32+
}
33+
34+
#[inline]
35+
#[rustc_intrinsic]
36+
#[rustc_nounwind]
37+
pub fn sqrtf32(x: f32) -> f32 {
38+
sqrtf(x)
39+
}
40+
41+
unsafe extern "C" {
42+
safe fn sqrtf(_: f32) -> f32;
43+
}
44+
45+
// Codegen backends can return a list of `replaced_intrinsics`, for which codegen of the fallback is
46+
// normally skipped. The unchecked_funnel_shl is skipped by the LLVM backend, so we test it here to
47+
// ensure that with the flag enabled the fallback body is actually code generated.
48+
49+
#[no_mangle]
50+
fn call_funnel_shl(a: u32, b: u32, shift: u32) -> u32 {
51+
// CHECK-LABEL: call_funnel_shl
52+
53+
// Without the flag: dedicated instruction, no libcall.
54+
// NORMAL: tail call i32 @llvm.fshl.i32
55+
// NORMAL-NOT: custom_funnel_shl
56+
57+
// With the flag: libcall
58+
// FALLBACK: tail call void @custom_funnel_shl
59+
unsafe { unchecked_funnel_shl(a, b, shift) }
60+
}
61+
62+
#[inline]
63+
#[rustc_intrinsic]
64+
#[rustc_nounwind]
65+
unsafe fn unchecked_funnel_shl<T>(_: T, _: T, shift: u32) -> T {
66+
custom_funnel_shl(shift)
67+
}
68+
69+
unsafe extern "C" {
70+
safe fn custom_funnel_shl(_: u32) -> !;
71+
}

0 commit comments

Comments
 (0)