Skip to content

cold_path does not propagate correctly #155263

@DaniPopes

Description

@DaniPopes

I tried this code (godbolt):

#![feature(core_intrinsics)]
#![allow(internal_features)]

use std::intrinsics::cold_path;

#[inline(never)]
#[unsafe(no_mangle)]
pub fn dispatch_dec(x: &mut u64, y: &mut u64) -> Result<(), u8> {
    dispatch(dec, x, y)
}

pub fn dispatch<F: FnOnce(&mut u64) -> Result<(), u8>>(
    f: F,
    x: &mut u64,
    y: &mut u64,
) -> Result<(), u8> {
    // `dec` directly works.
    if let Err(e) = dec(x) {
        cold_path();
        return Err(e);
    }

    // `dec` through `FnOnce` doesn't propagate `cold_path` after optimizations.
    if let Err(e) = f(y) {
        cold_path();
        return Err(e);
    }

    Ok(())
}

fn dec(x: &mut u64) -> Result<(), u8> {
    if *x == 0 {
        return Err(1);
    }
    *x -= 1;
    Ok(())
}

I expected to see this happen: both if let Err { ... } have a branch_weights metadata.

Instead, this happened: only the direct call, does. The f parameter, which goes thru FnOnce::call indirection does not have a branch_weights metadata.

The LLVM IR output has been roughly the same since 1.85 when the cold_path intrinsic was introduced, so it's not a regression, it never worked.

It seems that the llvm.expect intrinsic / branch_weights is not propagated that well in this specific case.

Full optimized LLVM IR

source_filename = "example.baafcdf745886294-cgu.0"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define { i1, i8 } @dispatch_dec(ptr noalias noundef align 8 captures(none) dereferenceable(8) %x, ptr noalias noundef align 8 captures(none) dereferenceable(8) %y) unnamed_addr {
start:
  tail call void @llvm.experimental.noalias.scope.decl(metadata !40)
  tail call void @llvm.experimental.noalias.scope.decl(metadata !44)
  %_12.i = load i64, ptr %x, align 8, !alias.scope !40, !noalias !44, !noundef !32
  %0 = icmp eq i64 %_12.i, 0
  br i1 %0, label %_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_.exit, label %bb8.i, !prof !71

bb8.i:
  %1 = add i64 %_12.i, -1
  store i64 %1, ptr %x, align 8, !alias.scope !40, !noalias !44
  %_2.i.i.i = load i64, ptr %y, align 8, !alias.scope !98, !noalias !40, !noundef !32
  %2 = icmp eq i64 %_2.i.i.i, 0
  br i1 %2, label %_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_.exit, label %bb2.i.i.i

bb2.i.i.i:
  %3 = add i64 %_2.i.i.i, -1
  store i64 %3, ptr %y, align 8, !alias.scope !98, !noalias !40
  br label %_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_.exit

_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_.exit:
  %_0.sroa.0.0.i = phi i1 [ true, %start ], [ true, %bb8.i ], [ false, %bb2.i.i.i ]
  %4 = insertvalue { i1, i8 } poison, i1 %_0.sroa.0.0.i, 0
  %5 = insertvalue { i1, i8 } %4, i8 1, 1
  ret { i1, i8 } %5
}

declare void @llvm.experimental.noalias.scope.decl(metadata) #1

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}
!llvm.dbg.cu = !{!5}

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 2, !"RtLibUseGOT", i32 1}
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{!"rustc version 1.97.0-nightly (14196dbfa 2026-04-12)"}
!5 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !6, producer: "clang LLVM (rustc version 1.97.0-nightly (14196dbfa 2026-04-12))", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!6 = !DIFile(filename: "/app/example.rs/@/example.baafcdf745886294-cgu.0", directory: "/app")
!7 = distinct !DISubprogram(name: "dispatch_dec", scope: !9, file: !8, line: 8, type: !10, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !5, templateParams: !32, retainedNodes: !36)
!8 = !DIFile(filename: "example.rs", directory: "/app", checksumkind: CSK_MD5, checksum: "ad080e4c2f34b48b1f106ac17181a8ae")
!9 = !DINamespace(name: "example", scope: null)
!10 = !DISubroutineType(types: !11)
!11 = !{!12, !34, !34}
!12 = !DICompositeType(tag: DW_TAG_structure_type, name: "Result<(), u8>", scope: !14, file: !13, size: 16, align: 8, flags: DIFlagPublic, elements: !16, templateParams: !32, identifier: "a11d02b8ee57d93c79548c735b5efadf")
!13 = !DIFile(filename: "<unknown>", directory: "")
!14 = !DINamespace(name: "result", scope: !15)
!15 = !DINamespace(name: "core", scope: null)
!16 = !{!17}
!17 = !DICompositeType(tag: DW_TAG_variant_part, scope: !12, file: !13, size: 16, align: 8, elements: !18, templateParams: !32, identifier: "99e3eabef0ee30d4f479ede165685c0", discriminator: !33)
!18 = !{!19, !28}
!19 = !DIDerivedType(tag: DW_TAG_member, name: "Ok", scope: !17, file: !13, baseType: !20, size: 16, align: 8, extraData: i8 0)
!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Ok", scope: !12, file: !13, size: 16, align: 8, flags: DIFlagPublic, elements: !21, templateParams: !24, identifier: "878683938168ebd49beca9c09630e546")
!21 = !{!22}
!22 = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: !20, file: !13, baseType: !23, align: 8, offset: 8, flags: DIFlagPublic)
!23 = !DIBasicType(name: "()", encoding: DW_ATE_unsigned)
!24 = !{!25, !26}
!25 = !DITemplateTypeParameter(name: "T", type: !23)
!26 = !DITemplateTypeParameter(name: "E", type: !27)
!27 = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned)
!28 = !DIDerivedType(tag: DW_TAG_member, name: "Err", scope: !17, file: !13, baseType: !29, size: 16, align: 8, extraData: i8 1)
!29 = !DICompositeType(tag: DW_TAG_structure_type, name: "Err", scope: !12, file: !13, size: 16, align: 8, flags: DIFlagPublic, elements: !30, templateParams: !24, identifier: "b3b90f90c850520ee8601e7b8f3d537f")
!30 = !{!31}
!31 = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: !29, file: !13, baseType: !27, size: 8, align: 8, offset: 8, flags: DIFlagPublic)
!32 = !{}
!33 = !DIDerivedType(tag: DW_TAG_member, scope: !12, file: !13, baseType: !27, size: 8, align: 8, flags: DIFlagArtificial)
!34 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&mut u64", baseType: !35, size: 64, align: 64, dwarfAddressSpace: 0)
!35 = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned)
!36 = !{!37, !38}
!37 = !DILocalVariable(name: "x", arg: 1, scope: !7, file: !8, line: 8, type: !34)
!38 = !DILocalVariable(name: "y", arg: 2, scope: !7, file: !8, line: 8, type: !34)
!39 = !DILocation(line: 0, scope: !7)
!40 = !{!41}
!41 = distinct !{!41, !42, !"_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_: %x"}
!42 = distinct !{!42, !"_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_"}
!43 = !DILocation(line: 9, column: 5, scope: !7)
!44 = !{!45}
!45 = distinct !{!45, !42, !"_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_: %y"}
!46 = !DILocalVariable(name: "f", arg: 1, scope: !47, file: !8, line: 13, type: !50)
!47 = distinct !DISubprogram(name: "dispatch<fn(&mut u64) -> core::result::Result<(), u8>>", linkageName: "_RINvCsg1JcJewIdhw_7example8dispatchNvB2_3decEB2_", scope: !9, file: !8, line: 12, type: !48, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !5, templateParams: !60, retainedNodes: !53)
!48 = !DISubroutineType(types: !49)
!49 = !{!12, !50, !34, !34}
!50 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "fn(&mut u64) -> core::result::Result<(), u8>", baseType: !51, align: 8, dwarfAddressSpace: 0)
!51 = !DISubroutineType(types: !52)
!52 = !{!12, !34}
!53 = !{!46, !54, !55, !56, !58}
!54 = !DILocalVariable(name: "x", arg: 2, scope: !47, file: !8, line: 14, type: !34)
!55 = !DILocalVariable(name: "y", arg: 3, scope: !47, file: !8, line: 15, type: !34)
!56 = !DILocalVariable(name: "e", scope: !57, file: !8, line: 18, type: !27, align: 8)
!57 = distinct !DILexicalBlock(scope: !47, file: !8, line: 18, column: 28)
!58 = !DILocalVariable(name: "e", scope: !59, file: !8, line: 24, type: !27, align: 8)
!59 = distinct !DILexicalBlock(scope: !47, file: !8, line: 24, column: 26)
!60 = !{!61}
!61 = !DITemplateTypeParameter(name: "F", type: !50)
!62 = !DILocation(line: 13, column: 5, scope: !47, inlinedAt: !63)
!63 = distinct !DILocation(line: 9, column: 5, scope: !7)
!64 = !DILocation(line: 0, scope: !47, inlinedAt: !63)
!65 = !DILocalVariable(name: "x", arg: 1, scope: !66, file: !8, line: 32, type: !34)
!66 = distinct !DISubprogram(name: "dec", linkageName: "_RNvCsg1JcJewIdhw_7example3dec", scope: !9, file: !8, line: 32, type: !51, scopeLine: 32, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !5, templateParams: !32, retainedNodes: !67)
!67 = !{!65}
!68 = !DILocation(line: 0, scope: !66, inlinedAt: !69)
!69 = distinct !DILocation(line: 18, column: 21, scope: !57, inlinedAt: !63)
!70 = !DILocation(line: 33, column: 8, scope: !66, inlinedAt: !69)
!71 = !{!"branch_weights", !"expected", i32 1, i32 2000}
!72 = !DILocation(line: 36, column: 5, scope: !66, inlinedAt: !69)
!73 = !DILocalVariable(arg: 2, scope: !74, file: !75, line: 250, type: !86)
!74 = distinct !DISubprogram(name: "call_once<fn(&mut u64) -> core::result::Result<(), u8>, (&mut u64)>", linkageName: "_RNvYNvCsg1JcJewIdhw_7example3decINtNtNtCs8M5hEbwQKR0_4core3ops8function6FnOnceTQyEE9call_onceB4_", scope: !76, file: !75, line: 250, type: !79, scopeLine: 250, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !5, templateParams: !83, retainedNodes: !81)
!75 = !DIFile(filename: "library/core/src/ops/function.rs", directory: "/rustc/14196dbfa3eb7c30195251eac092b1b86c8a2d84", checksumkind: CSK_MD5, checksum: "7165aec212fc528edf645f7f5c1c91bb")
!76 = !DINamespace(name: "FnOnce", scope: !77)
!77 = !DINamespace(name: "function", scope: !78)
!78 = !DINamespace(name: "ops", scope: !15)
!79 = !DISubroutineType(types: !80)
!80 = !{!12, !50, !34}
!81 = !{!82, !73}
!82 = !DILocalVariable(arg: 1, scope: !74, file: !75, line: 250, type: !50)
!83 = !{!84, !85}
!84 = !DITemplateTypeParameter(name: "Self", type: !50)
!85 = !DITemplateTypeParameter(name: "Args", type: !86)
!86 = !DICompositeType(tag: DW_TAG_structure_type, name: "(&mut u64)", file: !13, size: 64, align: 64, elements: !87, templateParams: !32, identifier: "841a4431c5bd953064ec65cc27ab64f4")
!87 = !{!88}
!88 = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: !86, file: !13, baseType: !34, size: 64, align: 64)
!89 = !DILocation(line: 0, scope: !74, inlinedAt: !90)
!90 = distinct !DILocation(line: 24, column: 21, scope: !59, inlinedAt: !63)
!91 = !DILocation(line: 250, column: 5, scope: !74, inlinedAt: !90)
!92 = !DILocalVariable(name: "x", arg: 1, scope: !93, file: !8, line: 32, type: !34)
!93 = distinct !DISubprogram(name: "dec", linkageName: "_RNvCsg1JcJewIdhw_7example3dec", scope: !9, file: !8, line: 32, type: !51, scopeLine: 32, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !5, templateParams: !32, retainedNodes: !94)
!94 = !{!92}
!95 = !DILocation(line: 0, scope: !93, inlinedAt: !96)
!96 = distinct !DILocation(line: 250, column: 5, scope: !74, inlinedAt: !90)
!97 = !DILocation(line: 33, column: 8, scope: !93, inlinedAt: !96)
!98 = !{!99, !101, !45}
!99 = distinct !{!99, !100, !"_RNvCsg1JcJewIdhw_7example3dec: %x"}
!100 = distinct !{!100, !"_RNvCsg1JcJewIdhw_7example3dec"}
!101 = distinct !{!101, !102, !"_RNvYNvCsg1JcJewIdhw_7example3decINtNtNtCs8M5hEbwQKR0_4core3ops8function6FnOnceTQyEE9call_onceB4_: argument 0"}
!102 = distinct !{!102, !"_RNvYNvCsg1JcJewIdhw_7example3decINtNtNtCs8M5hEbwQKR0_4core3ops8function6FnOnceTQyEE9call_onceB4_"}
!103 = !DILocation(line: 36, column: 5, scope: !93, inlinedAt: !96)
!104 = !DILocation(line: 38, column: 2, scope: !93, inlinedAt: !96)
!105 = !DILocation(line: 30, column: 2, scope: !47, inlinedAt: !63)
!106 = !DILocation(line: 10, column: 2, scope: !7)

Meta

rustc --version --verbose:

rustc 1.97.0-nightly (14196dbfa 2026-04-12)
binary: rustc
commit-hash: 14196dbfa3eb7c30195251eac092b1b86c8a2d84
commit-date: 2026-04-12
host: x86_64-unknown-linux-gnu
release: 1.97.0-nightly
LLVM version: 22.1.2

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.F-core_intrinsicsIssue in the "core intrinsics" for internal usage only.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-internal-featuresThis issue requires the use of internal features.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions