Skip to content

Commit a7aceda

Browse files
Rollup merge of rust-lang#158377 - folkertdev:force-intrinsic-fallback, r=saethlin
add `-Zforce-intrinsic-fallback` flag Add a flag that forces the use of the fallback body (if one exists), so that we can test that these fallback implementations actually work. cc rust-lang#150946 cc [#t-compiler > testing intrinsic fallback bodies](https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/testing.20intrinsic.20fallback.20bodies/with/606299558) cc [#t-infra > CI for -Zforce-intrinsic-fallback](https://rust-lang.zulipchat.com/#narrow/channel/242791-t-infra/topic/CI.20for.20-Zforce-intrinsic-fallback/with/608129693)
2 parents 7148b31 + 6810f13 commit a7aceda

5 files changed

Lines changed: 67 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
@@ -2417,6 +2417,9 @@ options! {
24172417
fmt_debug: FmtDebug = (FmtDebug::Full, parse_fmt_debug, [TRACKED],
24182418
"how detailed `#[derive(Debug)]` should be. `full` prints types recursively, \
24192419
`shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"),
2420+
force_intrinsic_fallback: bool = (false, parse_bool, [TRACKED],
2421+
"always use the fallback body of an intrinsic, if it has one, instead of lowering \
2422+
the intrinsic in the codegen backend (default: no)."),
24202423
force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED],
24212424
"force all crates to be `rustc_private` unstable (default: no)"),
24222425
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//@ compile-flags: --crate-type=lib -C no-prepopulate-passes -Copt-level=3
2+
//
3+
//@ revisions: NORMAL FALLBACK
4+
//@ [FALLBACK] compile-flags: -Zforce-intrinsic-fallback
5+
#![feature(core_intrinsics, funnel_shifts)]
6+
7+
// Check the effect of `-Zforce-intrinsic-fallback`.
8+
//
9+
// Without the flag, the dedicated backend lowering of the intrinsic is used.
10+
// With the flag, the fallback body is called instead.
11+
12+
#[no_mangle]
13+
pub fn call_minimumf32(x: f32, y: f32) -> f32 {
14+
// CHECK-LABEL: @call_minimumf32
15+
16+
// NORMAL: call float @llvm.minimum.f32
17+
// NORMAL-NOT: minimumf32
18+
19+
// FALLBACK-NOT: @llvm.minimum
20+
// FALLBACK: call {{.*}}minimumf32
21+
core::intrinsics::minimumf32(x, y)
22+
}
23+
24+
// Codegen backends can return a list of `replaced_intrinsics`, for which codegen of the fallback is
25+
// normally skipped. `unchecked_funnel_shl` is in that list for the LLVM backend, so we test it here
26+
// to ensure that with the flag enabled the fallback body is actually code generated and called.
27+
28+
#[no_mangle]
29+
pub fn call_funnel_shl(a: u32, b: u32, shift: u32) -> u32 {
30+
// CHECK-LABEL: @call_funnel_shl
31+
32+
// NORMAL: call i32 @llvm.fshl.i32
33+
// NORMAL-NOT: funnel_shl
34+
35+
// FALLBACK-NOT: @llvm.fshl
36+
// FALLBACK: call {{.*}}funnel_shl
37+
unsafe { core::intrinsics::unchecked_funnel_shl(a, b, shift) }
38+
}

0 commit comments

Comments
 (0)