Skip to content

Commit

Permalink
[nfc][BoundsChecking] Refactor BoundsCheckingOptions (#122346)
Browse files Browse the repository at this point in the history
Remove ReportingMode and ReportingOpts.
  • Loading branch information
vitalybuka authored Jan 10, 2025
1 parent 211bcf6 commit 9c2de99
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 103 deletions.
35 changes: 15 additions & 20 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1025,26 +1025,21 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
// Register callbacks to schedule sanitizer passes at the appropriate part
// of the pipeline.
if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds))
PB.registerScalarOptimizerLateEPCallback(
[this](FunctionPassManager &FPM, OptimizationLevel Level) {
BoundsCheckingPass::ReportingMode Mode;
bool Merge = CodeGenOpts.SanitizeMergeHandlers.has(
SanitizerKind::LocalBounds);

if (CodeGenOpts.SanitizeTrap.has(SanitizerKind::LocalBounds)) {
Mode = BoundsCheckingPass::ReportingMode::Trap;
} else if (CodeGenOpts.SanitizeMinimalRuntime) {
Mode = CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds)
? BoundsCheckingPass::ReportingMode::MinRuntime
: BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
} else {
Mode = CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds)
? BoundsCheckingPass::ReportingMode::FullRuntime
: BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
}
BoundsCheckingPass::BoundsCheckingOptions Options(Mode, Merge);
FPM.addPass(BoundsCheckingPass(Options));
});
PB.registerScalarOptimizerLateEPCallback([this](FunctionPassManager &FPM,
OptimizationLevel Level) {
BoundsCheckingPass::BoundsCheckingOptions Options;
Options.Merge =
CodeGenOpts.SanitizeMergeHandlers.has(SanitizerKind::LocalBounds);
if (!CodeGenOpts.SanitizeTrap.has(SanitizerKind::LocalBounds)) {
Options.Rt = {
/*MinRuntime=*/static_cast<bool>(
CodeGenOpts.SanitizeMinimalRuntime),
/*MayReturn=*/
CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds),
};
}
FPM.addPass(BoundsCheckingPass(Options));
});

// Don't add sanitizers if we are here from ThinLTO PostLink. That already
// done on PreLink stage.
Expand Down
21 changes: 9 additions & 12 deletions llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_TRANSFORMS_INSTRUMENTATION_BOUNDSCHECKING_H

#include "llvm/IR/PassManager.h"
#include <optional>

namespace llvm {
class Function;
Expand All @@ -19,19 +20,15 @@ class Function;
class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {

public:
enum class ReportingMode {
Trap,
MinRuntime,
MinRuntimeAbort,
FullRuntime,
FullRuntimeAbort,
};

struct BoundsCheckingOptions {
BoundsCheckingOptions(ReportingMode Mode, bool Merge);

ReportingMode Mode;
bool Merge;
struct Runtime {
Runtime(bool MinRuntime, bool MayReturn)
: MinRuntime(MinRuntime), MayReturn(MayReturn) {}
bool MinRuntime;
bool MayReturn;
};
std::optional<Runtime> Rt; // Trap if empty.
bool Merge = false;
};

BoundsCheckingPass(BoundsCheckingOptions Options) : Options(Options) {}
Expand Down
25 changes: 18 additions & 7 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1286,21 +1286,32 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) {

Expected<BoundsCheckingPass::BoundsCheckingOptions>
parseBoundsCheckingOptions(StringRef Params) {
BoundsCheckingPass::BoundsCheckingOptions Options(
BoundsCheckingPass::ReportingMode::Trap, false);
BoundsCheckingPass::BoundsCheckingOptions Options;
while (!Params.empty()) {
StringRef ParamName;
std::tie(ParamName, Params) = Params.split(';');
if (ParamName == "trap") {
Options.Mode = BoundsCheckingPass::ReportingMode::Trap;
Options.Rt = std::nullopt;
} else if (ParamName == "rt") {
Options.Mode = BoundsCheckingPass::ReportingMode::FullRuntime;
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/true,
};
} else if (ParamName == "rt-abort") {
Options.Mode = BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/false,
};
} else if (ParamName == "min-rt") {
Options.Mode = BoundsCheckingPass::ReportingMode::MinRuntime;
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/true,
};
} else if (ParamName == "min-rt-abort") {
Options.Mode = BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/false,
};
} else if (ParamName == "merge") {
Options.Merge = true;
} else {
Expand Down
99 changes: 35 additions & 64 deletions llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ STATISTIC(ChecksUnable, "Bounds checks unable to add");

using BuilderTy = IRBuilder<TargetFolder>;

BoundsCheckingPass::BoundsCheckingOptions::BoundsCheckingOptions(
ReportingMode Mode, bool Merge)
: Mode(Mode), Merge(Merge) {}

/// Gets the conditions under which memory accessing instructions will overflow.
///
/// \p Ptr is the pointer that will be read/written, and \p InstVal is either
Expand Down Expand Up @@ -166,42 +162,19 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
BranchInst::Create(TrapBB, Cont, Or, OldBB);
}

struct ReportingOpts {
bool MayReturn = false;
bool UseTrap = false;
bool MinRuntime = false;
bool MayMerge = true;
StringRef Name;

ReportingOpts(BoundsCheckingPass::ReportingMode Mode, bool Merge) {
switch (Mode) {
case BoundsCheckingPass::ReportingMode::Trap:
UseTrap = true;
break;
case BoundsCheckingPass::ReportingMode::MinRuntime:
Name = "__ubsan_handle_local_out_of_bounds_minimal";
MinRuntime = true;
MayReturn = true;
break;
case BoundsCheckingPass::ReportingMode::MinRuntimeAbort:
Name = "__ubsan_handle_local_out_of_bounds_minimal_abort";
MinRuntime = true;
break;
case BoundsCheckingPass::ReportingMode::FullRuntime:
Name = "__ubsan_handle_local_out_of_bounds";
MayReturn = true;
break;
case BoundsCheckingPass::ReportingMode::FullRuntimeAbort:
Name = "__ubsan_handle_local_out_of_bounds_abort";
break;
}

MayMerge = Merge;
}
};
static std::string getRuntimeCallName(
const BoundsCheckingPass::BoundsCheckingOptions::Runtime &Opts) {
std::string Name = "__ubsan_handle_local_out_of_bounds";
if (Opts.MinRuntime)
Name += "_minimal";
if (!Opts.MayReturn)
Name += "_abort";
return Name;
}

static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
ScalarEvolution &SE, const ReportingOpts &Opts) {
static bool
addBoundsChecking(Function &F, TargetLibraryInfo &TLI, ScalarEvolution &SE,
const BoundsCheckingPass::BoundsCheckingOptions &Opts) {
if (F.hasFnAttribute(Attribute::NoSanitizeBounds))
return false;

Expand Down Expand Up @@ -239,11 +212,16 @@ static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
TrapInfo.push_back(std::make_pair(&I, Or));
}

std::string Name;
if (Opts.Rt)
Name = getRuntimeCallName(*Opts.Rt);

// Create a trapping basic block on demand using a callback. Depending on
// flags, this will either create a single block for the entire function or
// will create a fresh block every time it is called.
BasicBlock *ReuseTrapBB = nullptr;
auto GetTrapBB = [&ReuseTrapBB, &Opts](BuilderTy &IRB, BasicBlock *Cont) {
auto GetTrapBB = [&ReuseTrapBB, &Opts, &Name](BuilderTy &IRB,
BasicBlock *Cont) {
Function *Fn = IRB.GetInsertBlock()->getParent();
auto DebugLoc = IRB.getCurrentDebugLocation();
IRBuilder<>::InsertPointGuard Guard(IRB);
Expand All @@ -257,23 +235,24 @@ static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
BasicBlock *TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
IRB.SetInsertPoint(TrapBB);

bool DebugTrapBB = !Opts.MayMerge;
CallInst *TrapCall = Opts.UseTrap
? InsertTrap(IRB, DebugTrapBB)
: InsertCall(IRB, Opts.MayReturn, Opts.Name);
bool DebugTrapBB = !Opts.Merge;
CallInst *TrapCall = Opts.Rt ? InsertCall(IRB, Opts.Rt->MayReturn, Name)
: InsertTrap(IRB, DebugTrapBB);
if (DebugTrapBB)
TrapCall->addFnAttr(llvm::Attribute::NoMerge);

TrapCall->setDoesNotThrow();
TrapCall->setDebugLoc(DebugLoc);
if (Opts.MayReturn) {

bool MayReturn = Opts.Rt && Opts.Rt->MayReturn;
if (MayReturn) {
IRB.CreateBr(Cont);
} else {
TrapCall->setDoesNotReturn();
IRB.CreateUnreachable();
}

if (!Opts.MayReturn && SingleTrapBB && !DebugTrapBB)
if (!MayReturn && SingleTrapBB && !DebugTrapBB)
ReuseTrapBB = TrapBB;

return TrapBB;
Expand All @@ -292,8 +271,7 @@ PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);

if (!addBoundsChecking(F, TLI, SE,
ReportingOpts(Options.Mode, Options.Merge)))
if (!addBoundsChecking(F, TLI, SE, Options))
return PreservedAnalyses::all();

return PreservedAnalyses::none();
Expand All @@ -303,22 +281,15 @@ void BoundsCheckingPass::printPipeline(
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
static_cast<PassInfoMixin<BoundsCheckingPass> *>(this)->printPipeline(
OS, MapClassName2PassName);
switch (Options.Mode) {
case ReportingMode::Trap:
OS << "<trap";
break;
case ReportingMode::MinRuntime:
OS << "<min-rt";
break;
case ReportingMode::MinRuntimeAbort:
OS << "<min-rt-abort";
break;
case ReportingMode::FullRuntime:
OS << "<rt";
break;
case ReportingMode::FullRuntimeAbort:
OS << "<rt-abort";
break;
OS << "<";
if (Options.Rt) {
if (Options.Rt->MinRuntime)
OS << "min-";
OS << "rt";
if (!Options.Rt->MayReturn)
OS << "-abort";
} else {
OS << "trap";
}
if (Options.Merge)
OS << ";merge";
Expand Down

0 comments on commit 9c2de99

Please sign in to comment.