[ubsan][pgo] Pass to remove ubsan checks based on profile data (#83471)

UBSAN checks can be too expensive to be used
in release binaries. However not all code affect
performace in the same way. Removing small
number of checks in hot code we can performance
loss, preserving most of the checks.
This commit is contained in:
Vitaly Buka
2024-03-07 10:52:43 -08:00
committed by GitHub
parent 41572177d1
commit a6a6fca791
6 changed files with 536 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
//===- RemoveTrapsPass.h ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file provides the interface for the pass responsible for removing
/// expensive ubsan checks.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_UBSANOPTIMIZATIONPASS_H
#define LLVM_TRANSFORMS_INSTRUMENTATION_UBSANOPTIMIZATIONPASS_H
#include "llvm/IR/Function.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace llvm {
// This pass is responsible for removing optional traps, like llvm.ubsantrap
// from the hot code.
class RemoveTrapsPass : public PassInfoMixin<RemoveTrapsPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};
} // namespace llvm
#endif

View File

@@ -177,6 +177,7 @@
#include "llvm/Transforms/Instrumentation/PGOForceFunctionAttrs.h"
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
#include "llvm/Transforms/Instrumentation/PoisonChecking.h"
#include "llvm/Transforms/Instrumentation/RemoveTrapsPass.h"
#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"

View File

@@ -422,6 +422,7 @@ FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(dbgs()))
FUNCTION_PASS("reassociate", ReassociatePass())
FUNCTION_PASS("redundant-dbg-inst-elim", RedundantDbgInstEliminationPass())
FUNCTION_PASS("reg2mem", RegToMemPass())
FUNCTION_PASS("remove-traps", RemoveTrapsPass())
FUNCTION_PASS("safe-stack", SafeStackPass(TM))
FUNCTION_PASS("scalarize-masked-mem-intrin", ScalarizeMaskedMemIntrinPass())
FUNCTION_PASS("scalarizer", ScalarizerPass())

View File

@@ -17,6 +17,7 @@ add_llvm_component_library(LLVMInstrumentation
PGOInstrumentation.cpp
PGOMemOPSizeOpt.cpp
PoisonChecking.cpp
RemoveTrapsPass.cpp
SanitizerCoverage.cpp
SanitizerBinaryMetadata.cpp
ValueProfileCollector.cpp

View File

@@ -0,0 +1,104 @@
//===- RemoveTrapsPass.cpp --------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Instrumentation/RemoveTrapsPass.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/RandomNumberGenerator.h"
#include <memory>
#include <random>
using namespace llvm;
#define DEBUG_TYPE "remove-traps"
static cl::opt<int> HotPercentileCutoff(
"remove-traps-percentile-cutoff-hot", cl::init(0),
cl::desc("Alternative hot percentile cuttoff. By default "
"`-profile-summary-cutoff-hot` is used."));
static cl::opt<float>
RandomRate("remove-traps-random-rate", cl::init(0.0),
cl::desc("Probability value in the range [0.0, 1.0] of "
"unconditional pseudo-random checks removal."));
STATISTIC(NumChecksTotal, "Number of checks");
STATISTIC(NumChecksRemoved, "Number of removed checks");
static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI,
const ProfileSummaryInfo *PSI) {
SmallVector<IntrinsicInst *, 16> Remove;
std::unique_ptr<RandomNumberGenerator> Rng;
auto ShouldRemove = [&](bool IsHot) {
if (!RandomRate.getNumOccurrences())
return IsHot;
if (!Rng)
Rng = F.getParent()->createRNG(F.getName());
std::bernoulli_distribution D(RandomRate);
return D(*Rng);
};
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
if (!II)
continue;
auto ID = II->getIntrinsicID();
switch (ID) {
case Intrinsic::ubsantrap: {
++NumChecksTotal;
bool IsHot = false;
if (PSI) {
uint64_t Count = 0;
for (const auto *PR : predecessors(&BB))
Count += BFI.getBlockProfileCount(PR).value_or(0);
IsHot =
HotPercentileCutoff.getNumOccurrences()
? (HotPercentileCutoff > 0 &&
PSI->isHotCountNthPercentile(HotPercentileCutoff, Count))
: PSI->isHotCount(Count);
}
if (ShouldRemove(IsHot)) {
Remove.push_back(II);
++NumChecksRemoved;
}
break;
}
default:
break;
}
}
}
for (IntrinsicInst *I : Remove)
I->eraseFromParent();
return !Remove.empty();
}
PreservedAnalyses RemoveTrapsPass::run(Function &F,
FunctionAnalysisManager &AM) {
if (F.isDeclaration())
return PreservedAnalyses::all();
auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
ProfileSummaryInfo *PSI =
MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
BlockFrequencyInfo &BFI = AM.getResult<BlockFrequencyAnalysis>(F);
return removeUbsanTraps(F, BFI, PSI) ? PreservedAnalyses::none()
: PreservedAnalyses::all();
}

View File

@@ -0,0 +1,397 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt < %s -passes='function(remove-traps)' -S | FileCheck %s --check-prefixes=NOPROFILE
; RUN: opt < %s -passes='function(remove-traps)' -remove-traps-random-rate=1 -S | FileCheck %s --check-prefixes=ALL
; RUN: opt < %s -passes='require<profile-summary>,function(remove-traps)' -S | FileCheck %s --check-prefixes=HOT
; RUN: opt < %s -passes='require<profile-summary>,function(remove-traps)' -remove-traps-percentile-cutoff-hot=700000 -S | FileCheck %s --check-prefixes=HOT70
target triple = "x86_64-pc-linux-gnu"
declare void @llvm.ubsantrap(i8 immarg)
define dso_local noundef i32 @simple(ptr noundef readonly %0) {
; NOPROFILE-LABEL: define dso_local noundef i32 @simple(
; NOPROFILE-SAME: ptr noundef readonly [[TMP0:%.*]]) {
; NOPROFILE-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; NOPROFILE-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; NOPROFILE: 3:
; NOPROFILE-NEXT: tail call void @llvm.ubsantrap(i8 22)
; NOPROFILE-NEXT: unreachable
; NOPROFILE: 4:
; NOPROFILE-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; NOPROFILE-NEXT: ret i32 [[TMP5]]
;
; ALL-LABEL: define dso_local noundef i32 @simple(
; ALL-SAME: ptr noundef readonly [[TMP0:%.*]]) {
; ALL-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; ALL-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; ALL: 3:
; ALL-NEXT: unreachable
; ALL: 4:
; ALL-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; ALL-NEXT: ret i32 [[TMP5]]
;
; HOT-LABEL: define dso_local noundef i32 @simple(
; HOT-SAME: ptr noundef readonly [[TMP0:%.*]]) {
; HOT-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; HOT-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; HOT: 3:
; HOT-NEXT: tail call void @llvm.ubsantrap(i8 22)
; HOT-NEXT: unreachable
; HOT: 4:
; HOT-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; HOT-NEXT: ret i32 [[TMP5]]
;
; HOT70-LABEL: define dso_local noundef i32 @simple(
; HOT70-SAME: ptr noundef readonly [[TMP0:%.*]]) {
; HOT70-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; HOT70-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; HOT70: 3:
; HOT70-NEXT: tail call void @llvm.ubsantrap(i8 22)
; HOT70-NEXT: unreachable
; HOT70: 4:
; HOT70-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; HOT70-NEXT: ret i32 [[TMP5]]
;
%2 = icmp eq ptr %0, null
br i1 %2, label %3, label %4
3:
tail call void @llvm.ubsantrap(i8 22)
unreachable
4:
%5 = load i32, ptr %0, align 4
ret i32 %5
}
define dso_local noundef i32 @hot(ptr noundef readonly %0) !prof !36 {
; NOPROFILE-LABEL: define dso_local noundef i32 @hot(
; NOPROFILE-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF16:![0-9]+]] {
; NOPROFILE-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; NOPROFILE-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; NOPROFILE: 3:
; NOPROFILE-NEXT: tail call void @llvm.ubsantrap(i8 22)
; NOPROFILE-NEXT: unreachable
; NOPROFILE: 4:
; NOPROFILE-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; NOPROFILE-NEXT: ret i32 [[TMP5]]
;
; ALL-LABEL: define dso_local noundef i32 @hot(
; ALL-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF16:![0-9]+]] {
; ALL-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; ALL-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; ALL: 3:
; ALL-NEXT: unreachable
; ALL: 4:
; ALL-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; ALL-NEXT: ret i32 [[TMP5]]
;
; HOT-LABEL: define dso_local noundef i32 @hot(
; HOT-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF16:![0-9]+]] {
; HOT-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; HOT-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; HOT: 3:
; HOT-NEXT: unreachable
; HOT: 4:
; HOT-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; HOT-NEXT: ret i32 [[TMP5]]
;
; HOT70-LABEL: define dso_local noundef i32 @hot(
; HOT70-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF16:![0-9]+]] {
; HOT70-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; HOT70-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; HOT70: 3:
; HOT70-NEXT: tail call void @llvm.ubsantrap(i8 22)
; HOT70-NEXT: unreachable
; HOT70: 4:
; HOT70-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; HOT70-NEXT: ret i32 [[TMP5]]
;
%2 = icmp eq ptr %0, null
br i1 %2, label %3, label %4
3:
tail call void @llvm.ubsantrap(i8 22)
unreachable
4:
%5 = load i32, ptr %0, align 4
ret i32 %5
}
define dso_local noundef i32 @veryHot(ptr noundef readonly %0) !prof !39 {
; NOPROFILE-LABEL: define dso_local noundef i32 @veryHot(
; NOPROFILE-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF17:![0-9]+]] {
; NOPROFILE-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; NOPROFILE-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; NOPROFILE: 3:
; NOPROFILE-NEXT: tail call void @llvm.ubsantrap(i8 22)
; NOPROFILE-NEXT: unreachable
; NOPROFILE: 4:
; NOPROFILE-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; NOPROFILE-NEXT: ret i32 [[TMP5]]
;
; ALL-LABEL: define dso_local noundef i32 @veryHot(
; ALL-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF17:![0-9]+]] {
; ALL-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; ALL-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; ALL: 3:
; ALL-NEXT: unreachable
; ALL: 4:
; ALL-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; ALL-NEXT: ret i32 [[TMP5]]
;
; HOT-LABEL: define dso_local noundef i32 @veryHot(
; HOT-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF17:![0-9]+]] {
; HOT-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; HOT-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; HOT: 3:
; HOT-NEXT: unreachable
; HOT: 4:
; HOT-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; HOT-NEXT: ret i32 [[TMP5]]
;
; HOT70-LABEL: define dso_local noundef i32 @veryHot(
; HOT70-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF17:![0-9]+]] {
; HOT70-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; HOT70-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]]
; HOT70: 3:
; HOT70-NEXT: unreachable
; HOT70: 4:
; HOT70-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
; HOT70-NEXT: ret i32 [[TMP5]]
;
%2 = icmp eq ptr %0, null
br i1 %2, label %3, label %4
3:
tail call void @llvm.ubsantrap(i8 22)
unreachable
4:
%5 = load i32, ptr %0, align 4
ret i32 %5
}
define dso_local noundef i32 @branchColdFnHot(i32 noundef %0, ptr noundef readonly %1) !prof !39 {
; NOPROFILE-LABEL: define dso_local noundef i32 @branchColdFnHot(
; NOPROFILE-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF17]] {
; NOPROFILE-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; NOPROFILE-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF18:![0-9]+]]
; NOPROFILE: 4:
; NOPROFILE-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; NOPROFILE-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; NOPROFILE: 6:
; NOPROFILE-NEXT: tail call void @llvm.ubsantrap(i8 22)
; NOPROFILE-NEXT: unreachable
; NOPROFILE: 7:
; NOPROFILE-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; NOPROFILE-NEXT: br label [[TMP9]]
; NOPROFILE: 9:
; NOPROFILE-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; NOPROFILE-NEXT: ret i32 [[TMP10]]
;
; ALL-LABEL: define dso_local noundef i32 @branchColdFnHot(
; ALL-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF17]] {
; ALL-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; ALL-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF18:![0-9]+]]
; ALL: 4:
; ALL-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; ALL-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; ALL: 6:
; ALL-NEXT: unreachable
; ALL: 7:
; ALL-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; ALL-NEXT: br label [[TMP9]]
; ALL: 9:
; ALL-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; ALL-NEXT: ret i32 [[TMP10]]
;
; HOT-LABEL: define dso_local noundef i32 @branchColdFnHot(
; HOT-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF17]] {
; HOT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; HOT-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF18:![0-9]+]]
; HOT: 4:
; HOT-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; HOT-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; HOT: 6:
; HOT-NEXT: tail call void @llvm.ubsantrap(i8 22)
; HOT-NEXT: unreachable
; HOT: 7:
; HOT-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; HOT-NEXT: br label [[TMP9]]
; HOT: 9:
; HOT-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; HOT-NEXT: ret i32 [[TMP10]]
;
; HOT70-LABEL: define dso_local noundef i32 @branchColdFnHot(
; HOT70-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF17]] {
; HOT70-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; HOT70-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF18:![0-9]+]]
; HOT70: 4:
; HOT70-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; HOT70-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; HOT70: 6:
; HOT70-NEXT: tail call void @llvm.ubsantrap(i8 22)
; HOT70-NEXT: unreachable
; HOT70: 7:
; HOT70-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; HOT70-NEXT: br label [[TMP9]]
; HOT70: 9:
; HOT70-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; HOT70-NEXT: ret i32 [[TMP10]]
;
%3 = icmp eq i32 %0, 0
br i1 %3, label %9, label %4, !prof !38
4:
%5 = icmp eq ptr %1, null
br i1 %5, label %6, label %7
6:
tail call void @llvm.ubsantrap(i8 22) #2
unreachable
7:
%8 = load i32, ptr %1, align 4
br label %9
9:
%10 = phi i32 [ %8, %7 ], [ 0, %2 ]
ret i32 %10
}
define dso_local noundef i32 @branchHotFnCold(i32 noundef %0, ptr noundef readonly %1) !prof !36 {
; NOPROFILE-LABEL: define dso_local noundef i32 @branchHotFnCold(
; NOPROFILE-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF16]] {
; NOPROFILE-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; NOPROFILE-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF19:![0-9]+]]
; NOPROFILE: 4:
; NOPROFILE-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; NOPROFILE-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; NOPROFILE: 6:
; NOPROFILE-NEXT: tail call void @llvm.ubsantrap(i8 22)
; NOPROFILE-NEXT: unreachable
; NOPROFILE: 7:
; NOPROFILE-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; NOPROFILE-NEXT: br label [[TMP9]]
; NOPROFILE: 9:
; NOPROFILE-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; NOPROFILE-NEXT: ret i32 [[TMP10]]
;
; ALL-LABEL: define dso_local noundef i32 @branchHotFnCold(
; ALL-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF16]] {
; ALL-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; ALL-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF19:![0-9]+]]
; ALL: 4:
; ALL-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; ALL-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; ALL: 6:
; ALL-NEXT: unreachable
; ALL: 7:
; ALL-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; ALL-NEXT: br label [[TMP9]]
; ALL: 9:
; ALL-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; ALL-NEXT: ret i32 [[TMP10]]
;
; HOT-LABEL: define dso_local noundef i32 @branchHotFnCold(
; HOT-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF16]] {
; HOT-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; HOT-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF19:![0-9]+]]
; HOT: 4:
; HOT-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; HOT-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; HOT: 6:
; HOT-NEXT: unreachable
; HOT: 7:
; HOT-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; HOT-NEXT: br label [[TMP9]]
; HOT: 9:
; HOT-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; HOT-NEXT: ret i32 [[TMP10]]
;
; HOT70-LABEL: define dso_local noundef i32 @branchHotFnCold(
; HOT70-SAME: i32 noundef [[TMP0:%.*]], ptr noundef readonly [[TMP1:%.*]]) !prof [[PROF16]] {
; HOT70-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 0
; HOT70-NEXT: br i1 [[TMP3]], label [[TMP9:%.*]], label [[TMP4:%.*]], !prof [[PROF19:![0-9]+]]
; HOT70: 4:
; HOT70-NEXT: [[TMP5:%.*]] = icmp eq ptr [[TMP1]], null
; HOT70-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP7:%.*]]
; HOT70: 6:
; HOT70-NEXT: tail call void @llvm.ubsantrap(i8 22)
; HOT70-NEXT: unreachable
; HOT70: 7:
; HOT70-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4
; HOT70-NEXT: br label [[TMP9]]
; HOT70: 9:
; HOT70-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP8]], [[TMP7]] ], [ 0, [[TMP2:%.*]] ]
; HOT70-NEXT: ret i32 [[TMP10]]
;
%3 = icmp eq i32 %0, 0
br i1 %3, label %9, label %4, !prof !37
4:
%5 = icmp eq ptr %1, null
br i1 %5, label %6, label %7
6:
tail call void @llvm.ubsantrap(i8 22) #2
unreachable
7:
%8 = load i32, ptr %1, align 4
br label %9
9:
%10 = phi i32 [ %8, %7 ], [ 0, %2 ]
ret i32 %10
}
!llvm.module.flags = !{!6}
!6 = !{i32 1, !"ProfileSummary", !7}
!7 = !{!8, !9, !10, !11, !12, !13, !14, !17}
!8 = !{!"ProfileFormat", !"InstrProf"}
!9 = !{!"TotalCount", i64 30000}
!10 = !{!"MaxCount", i64 10000}
!11 = !{!"MaxInternalCount", i64 10000}
!12 = !{!"MaxFunctionCount", i64 10000}
!13 = !{!"NumCounts", i64 3}
!14 = !{!"NumFunctions", i64 5}
!17 = !{!"DetailedSummary", !18}
!18 = !{!19, !29, !30, !32, !34}
!19 = !{i32 10000, i64 10000, i32 3}
!29 = !{i32 950000, i64 5000, i32 3}
!30 = !{i32 990000, i64 500, i32 4}
!32 = !{i32 999900, i64 250, i32 4}
!34 = !{i32 999999, i64 1, i32 6}
!36 = !{!"function_entry_count", i64 1000}
!39 = !{!"function_entry_count", i64 7000}
!37 = !{!"branch_weights", i32 1, i32 1000}
!38 = !{!"branch_weights", i32 1000, i32 1}
;.
; NOPROFILE: [[PROF16]] = !{!"function_entry_count", i64 1000}
; NOPROFILE: [[PROF17]] = !{!"function_entry_count", i64 7000}
; NOPROFILE: [[PROF18]] = !{!"branch_weights", i32 1000, i32 1}
; NOPROFILE: [[PROF19]] = !{!"branch_weights", i32 1, i32 1000}
;.
; ALL: [[PROF16]] = !{!"function_entry_count", i64 1000}
; ALL: [[PROF17]] = !{!"function_entry_count", i64 7000}
; ALL: [[PROF18]] = !{!"branch_weights", i32 1000, i32 1}
; ALL: [[PROF19]] = !{!"branch_weights", i32 1, i32 1000}
;.
; HOT: [[PROF16]] = !{!"function_entry_count", i64 1000}
; HOT: [[PROF17]] = !{!"function_entry_count", i64 7000}
; HOT: [[PROF18]] = !{!"branch_weights", i32 1000, i32 1}
; HOT: [[PROF19]] = !{!"branch_weights", i32 1, i32 1000}
;.
; HOT70: [[PROF16]] = !{!"function_entry_count", i64 1000}
; HOT70: [[PROF17]] = !{!"function_entry_count", i64 7000}
; HOT70: [[PROF18]] = !{!"branch_weights", i32 1000, i32 1}
; HOT70: [[PROF19]] = !{!"branch_weights", i32 1, i32 1000}
;.