[llvm] [Option] Add Condition for build-conditional library options (PR #230898)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 11 10:10:27 PDT 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/230898
>From 1d9af291a147321b5ff348770757a62ea13c08a5 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 10 Oct 2026 22:15:58 -0700
Subject: [PATCH 1/5] [Option] Add Condition for build-conditional library
options
A field under `let Condition = "!defined(NDEBUG)"` keeps the member
variable with the specified default value, but the option parser code is
guided under `!defined(NDEBUG)`, so the command line rejects it as an
unknown argument in non-assertion builds.
Port -loop-fusion-verbose-debug and -stress-ivchain, the NDEBUG-only
Scalar options. LSR's release-build StressIVChain constant goes away.
Aided by Opus 5.5
---
llvm/include/llvm/Option/LibraryOptions.td | 3 ++
llvm/lib/Option/LibraryOptions.cpp | 3 ++
llvm/lib/Transforms/Scalar/LoopFuse.cpp | 42 ++++++++-----------
.../Transforms/Scalar/LoopStrengthReduce.cpp | 35 ++++++----------
llvm/lib/Transforms/Scalar/ScalarOptions.td | 7 ++++
llvm/unittests/Option/LibraryOptionsTest.cpp | 23 ++++++----
llvm/unittests/Option/LibraryOpts.td | 4 ++
.../TableGen/Basic/OptionParserEmitter.cpp | 9 +++-
8 files changed, 68 insertions(+), 58 deletions(-)
diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td
index 60dcb23166928..0b8b97479048f 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -23,10 +23,13 @@ class OptionsStruct<string namespace = "llvm", string prefix = ""> {
}
// The member an option sets, and the member's type and default value.
+// `let Condition = "!defined(NDEBUG)" in` makes the option unknown where the
+// preprocessor condition is false; the member then keeps its default.
class Field<string name, string type, code default> {
string FieldName = name;
string FieldType = type;
code FieldDefault = default;
+ code Condition = ?;
}
// The value a FlagOrEq row assigns when given without one.
diff --git a/llvm/lib/Option/LibraryOptions.cpp b/llvm/lib/Option/LibraryOptions.cpp
index cfcb82273eb6d..55a317dc57f49 100644
--- a/llvm/lib/Option/LibraryOptions.cpp
+++ b/llvm/lib/Option/LibraryOptions.cpp
@@ -24,6 +24,9 @@ void LibraryOptionsParser::forEachOption(
unsigned Kind = T.getOptionKind(ID);
if (Kind != Option::FlagOrEqClass && Kind != Option::SeparateOrEqClass)
continue;
+ // Skip options a false Condition makes unknown.
+ if (!T.getOption(ID).hasVisibilityFlag(DefaultVis))
+ continue;
StringRef V = T.getOptionMetaVar(ID);
std::string MetaVar;
if (Kind == Option::SeparateOrEqClass)
diff --git a/llvm/lib/Transforms/Scalar/LoopFuse.cpp b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
index 894f14581bd87..d686f1ada5977 100644
--- a/llvm/lib/Transforms/Scalar/LoopFuse.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
@@ -58,7 +58,6 @@
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Verifier.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
@@ -100,13 +99,6 @@ STATISTIC(NumHoistedInsts, "Number of hoisted preheader instructions.");
STATISTIC(NumSunkInsts, "Number of sunk preheader instructions.");
STATISTIC(NumDA, "DA checks passed");
-#ifndef NDEBUG
-static cl::opt<bool>
- VerboseFusionDebugging("loop-fusion-verbose-debug",
- cl::desc("Enable verbose debugging for Loop Fusion"),
- cl::Hidden, cl::init(false));
-#endif
-
namespace {
/// This class is used to represent a candidate for loop fusion. When it is
/// constructed, it checks the conditions for loop fusion to ensure that it
@@ -544,6 +536,8 @@ struct LoopDepthTree {
struct LoopFuser {
private:
+ const ScalarOptions &Opts;
+
// Sets of control flow equivalent fusion candidates for a given nest level.
FusionCandidateCollection FusionCandidates;
@@ -560,19 +554,19 @@ struct LoopFuser {
const TargetTransformInfo &TTI;
public:
- LoopFuser(LoopInfo &LI, DominatorTree &DT, DependenceInfo &DI,
- ScalarEvolution &SE, PostDominatorTree &PDT,
+ LoopFuser(const ScalarOptions &Opts, LoopInfo &LI, DominatorTree &DT,
+ DependenceInfo &DI, ScalarEvolution &SE, PostDominatorTree &PDT,
OptimizationRemarkEmitter &ORE, AssumptionCache &AC,
const TargetTransformInfo &TTI)
- : LDT(LI), DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy), LI(LI),
- DT(DT), DI(DI), SE(SE), PDT(PDT), ORE(ORE), AC(AC), TTI(TTI) {}
+ : Opts(Opts), LDT(LI), DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy),
+ LI(LI), DT(DT), DI(DI), SE(SE), PDT(PDT), ORE(ORE), AC(AC), TTI(TTI) {}
/// This is the main entry point for loop fusion. It will traverse the
/// specified function and collect candidate loops to fuse, starting at the
/// outermost nesting level and working inwards.
bool fuseLoops(Function &F) {
#ifndef NDEBUG
- if (VerboseFusionDebugging) {
+ if (Opts.loop_fusion_verbose_debug) {
LI.print(dbgs());
}
#endif
@@ -593,7 +587,7 @@ struct LoopFuser {
if (LV.size() == 1)
continue;
#ifndef NDEBUG
- if (VerboseFusionDebugging) {
+ if (Opts.loop_fusion_verbose_debug) {
LLVM_DEBUG({
dbgs() << " Visit loop set (#" << LV.size() << "):\n";
printLoopVector(LV);
@@ -652,7 +646,7 @@ struct LoopFuser {
FoundAdjacent = true;
NumFusionCandidates++;
#ifndef NDEBUG
- if (VerboseFusionDebugging)
+ if (Opts.loop_fusion_verbose_debug)
LLVM_DEBUG(dbgs() << "Adding " << CurrCand
<< " to existing candidate list\n");
#endif
@@ -662,7 +656,7 @@ struct LoopFuser {
if (!FoundAdjacent) {
// No list was found. Create a new list and add to FusionCandidates
#ifndef NDEBUG
- if (VerboseFusionDebugging)
+ if (Opts.loop_fusion_verbose_debug)
LLVM_DEBUG(dbgs() << "Adding " << CurrCand << " to new list\n");
#endif
FusionCandidateList NewCandList;
@@ -843,11 +837,9 @@ struct LoopFuser {
// the first loop has a larger trip count. In this case it is possible
// that the first loop is peeled to expose the fusion opportunity.
// Peeling the second loop is not currently supported.
- bool WillPeel =
- FC0.AbleToPeel && TCDifference && *TCDifference > 0 &&
- *TCDifference <=
- static_cast<int64_t>(
- ScalarOptions::Global.loop_fusion_peel_max_count);
+ bool WillPeel = FC0.AbleToPeel && TCDifference && *TCDifference > 0 &&
+ *TCDifference <= static_cast<int64_t>(
+ Opts.loop_fusion_peel_max_count);
if (!WillPeel && (!TCDifference || *TCDifference != 0)) {
LLVM_DEBUG(dbgs() << "Fusion candidates do not have identical trip "
@@ -1187,7 +1179,7 @@ struct LoopFuser {
const FusionCandidate &FC1, Instruction &I0,
Instruction &I1) {
#ifndef NDEBUG
- if (VerboseFusionDebugging) {
+ if (Opts.loop_fusion_verbose_debug) {
LLVM_DEBUG(dbgs() << "Check dep: " << I0 << " vs " << I1 << "\n");
}
#endif
@@ -1203,7 +1195,7 @@ struct LoopFuser {
return true;
}
#ifndef NDEBUG
- if (VerboseFusionDebugging) {
+ if (Opts.loop_fusion_verbose_debug) {
LLVM_DEBUG(dbgs() << "DA res: "; DepResult->dump(dbgs());
dbgs() << " [#l: " << DepResult->getLevels() << "][Ordered: "
<< (DepResult->isOrdered() ? "true" : "false")
@@ -1366,7 +1358,7 @@ struct LoopFuser {
NumHoistedInsts += HoistInsts.size();
NumSunkInsts += SinkInsts.size();
- LLVM_DEBUG(if (VerboseFusionDebugging) {
+ LLVM_DEBUG(if (Opts.loop_fusion_verbose_debug) {
if (!HoistInsts.empty())
dbgs() << "Hoisting: \n";
for (Instruction *I : HoistInsts)
@@ -1934,7 +1926,7 @@ PreservedAnalyses LoopFusePass::run(Function &F, FunctionAnalysisManager &AM) {
if (Changed)
PDT.recalculate(F);
- LoopFuser LF(LI, DT, DI, SE, PDT, ORE, AC, TTI);
+ LoopFuser LF(ScalarOptions::Global, LI, DT, DI, SE, PDT, ORE, AC, TTI);
Changed |= LF.fuseLoops(F);
if (!Changed)
return PreservedAnalyses::all();
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 3f595f95ef064..7252e64a66b18 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -104,7 +104,6 @@
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@@ -144,15 +143,6 @@ static const unsigned MaxIVUsers = 200;
/// the salvaging is not too expensive for the compiler.
static const unsigned MaxSCEVSalvageExpressionSize = 64;
-#ifndef NDEBUG
-// Stress test IV chain generation.
-static cl::opt<bool> StressIVChain(
- "stress-ivchain", cl::Hidden, cl::init(false),
- cl::desc("Stress test LSR IV chains"));
-#else
-static bool StressIVChain = false;
-#endif
-
namespace {
struct MemAccessTy {
@@ -2116,9 +2106,8 @@ struct IVChain {
Instruction *tailUserInst() const { return Incs.back().UserInst; }
// Returns true if IncExpr can be profitably added to this chain.
- bool isProfitableIncrement(const SCEV *OperExpr,
- const SCEV *IncExpr,
- ScalarEvolution&);
+ bool isProfitableIncrement(const ScalarOptions &Opts, const SCEV *OperExpr,
+ const SCEV *IncExpr, ScalarEvolution &);
};
/// Helper for CollectChains to track multiple IV increment uses. Distinguish
@@ -3052,11 +3041,11 @@ static const SCEV *getExprBase(const SCEV *S) {
/// increment will be an offset relative to the same base. We allow such offsets
/// to potentially be used as chain increment as long as it's not obviously
/// expensive to expand using real instructions.
-bool IVChain::isProfitableIncrement(const SCEV *OperExpr,
- const SCEV *IncExpr,
+bool IVChain::isProfitableIncrement(const ScalarOptions &Opts,
+ const SCEV *OperExpr, const SCEV *IncExpr,
ScalarEvolution &SE) {
// Aggressively form chains when -stress-ivchain.
- if (StressIVChain)
+ if (Opts.stress_ivchain)
return true;
// Do not replace a constant offset from IV head with a nonconstant IV
@@ -3081,11 +3070,11 @@ bool IVChain::isProfitableIncrement(const SCEV *OperExpr,
/// increments can be computed in fewer registers when chained.
///
/// TODO: Consider IVInc free if it's already used in another chains.
-static bool isProfitableChain(IVChain &Chain,
+static bool isProfitableChain(const ScalarOptions &Opts, IVChain &Chain,
SmallPtrSetImpl<Instruction *> &Users,
ScalarEvolution &SE,
const TargetTransformInfo &TTI) {
- if (StressIVChain)
+ if (Opts.stress_ivchain)
return true;
if (!Chain.hasIncs())
@@ -3179,7 +3168,7 @@ void LSRInstance::ChainInstruction(Instruction *UserInst, Instruction *IVOper,
// are expressions that operate on the same unscaled SCEVUnknown. This
// "base" will be canceled by the subsequent getMinusSCEV call. Checking
// first avoids creating extra SCEV expressions.
- if (!StressIVChain && Chain.ExprBase != OperExprBase)
+ if (!Opts.stress_ivchain && Chain.ExprBase != OperExprBase)
continue;
Value *PrevIV = getWideOperand(Chain.Incs.back().IVOperand);
@@ -3196,7 +3185,7 @@ void LSRInstance::ChainInstruction(Instruction *UserInst, Instruction *IVOper,
if (isa<SCEVCouldNotCompute>(IncExpr) || !SE.isLoopInvariant(IncExpr, L))
continue;
- if (Chain.isProfitableIncrement(OperExpr, IncExpr, SE)) {
+ if (Chain.isProfitableIncrement(Opts, OperExpr, IncExpr, SE)) {
LastIncExpr = IncExpr;
break;
}
@@ -3206,7 +3195,7 @@ void LSRInstance::ChainInstruction(Instruction *UserInst, Instruction *IVOper,
if (ChainIdx == NChains) {
if (isa<PHINode>(UserInst))
return;
- if (NChains >= MaxChains && !StressIVChain) {
+ if (NChains >= MaxChains && !Opts.stress_ivchain) {
LLVM_DEBUG(dbgs() << "IV Chain Limit\n");
return;
}
@@ -3352,7 +3341,7 @@ void LSRInstance::CollectChains() {
unsigned ChainIdx = 0;
for (unsigned UsersIdx = 0, NChains = IVChainVec.size();
UsersIdx < NChains; ++UsersIdx) {
- if (!isProfitableChain(IVChainVec[UsersIdx],
+ if (!isProfitableChain(Opts, IVChainVec[UsersIdx],
ChainUsersVec[UsersIdx].FarUsers, SE, TTI))
continue;
// Preserve the chain at UsesIdx.
@@ -6330,7 +6319,7 @@ LSRInstance::LSRInstance(const ScalarOptions &Opts, Loop *L, IVUsers &IU,
// registers.
// FIXME: add profitable chain optimization for other kinds major cost, for
// example number of instructions.
- if (TTI.isNumRegsMajorCostOfLSR() || StressIVChain)
+ if (TTI.isNumRegsMajorCostOfLSR() || Opts.stress_ivchain)
CollectChains();
CollectInterestingTypesAndFactors();
CollectFixupsAndInitialFormulae();
diff --git a/llvm/lib/Transforms/Scalar/ScalarOptions.td b/llvm/lib/Transforms/Scalar/ScalarOptions.td
index 1d6275cac7afc..68db4f2c24911 100644
--- a/llvm/lib/Transforms/Scalar/ScalarOptions.td
+++ b/llvm/lib/Transforms/Scalar/ScalarOptions.td
@@ -259,6 +259,9 @@ defm : BoolField<"loop-flatten-widen-iv", "true",
// LoopFuse.cpp
defm : ValueField<"loop-fusion-peel-max-count", "uint32_t", "0",
"Max number of iterations to be peeled from a loop, such that fusion can take place">;
+let Condition = "!defined(NDEBUG)" in
+defm : BoolField<"loop-fusion-verbose-debug", "false",
+ "Enable verbose debugging for Loop Fusion">;
// LoopIdiomRecognize.cpp
defm : EnumField<"loop-idiom-crc-strategy", "CRCStrategyKind", "CRCStrategyKind::Auto",
@@ -360,6 +363,10 @@ defm : OptionalEnumField<"lsr-preferred-addressing-mode", "TargetTransformInfo::
"A flag that overrides the target's preferred addressing mode">;
defm : ValueField<"lsr-setupcost-depth-limit", "unsigned", "7",
"The limit on recursion depth for LSRs setup cost">;
+// Stress test IV chain generation.
+let Condition = "!defined(NDEBUG)" in
+defm : BoolField<"stress-ivchain", "false",
+ "Stress test LSR IV chains">;
// LoopUnrollAndJamPass.cpp
defm : OptionalBoolField<"allow-unroll-and-jam",
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp b/llvm/unittests/Option/LibraryOptionsTest.cpp
index 0c94a172dfc88..dff1d179845b7 100644
--- a/llvm/unittests/Option/LibraryOptionsTest.cpp
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -126,15 +126,16 @@ TEST(LibraryOptionsTest, Parser) {
P.forEachOption([&](StringRef Spelling, StringRef MetaVar, StringRef Help) {
Rows.push_back((Spelling + "|" + MetaVar + "|" + Help).str());
});
- EXPECT_THAT(
- Rows,
- testing::ElementsAre(
- "lib-count|=<value>|An unsigned", "lib-enable||A bool",
- "lib-limit|=<value>|An optional", "lib-list|=<value>|A list",
- "lib-modes|=<a|b>|An enum list", "lib-mode|=<a|b>|An enum",
- "lib-override||An optional bool", "lib-path|=<value>|A string",
- "lib-print|[=<a|b>]|A flag or enum", "lib-ratio|=<value>|A double",
- "lib-tristate|=<Default|Enable|Disable>|A tri-state"));
+ EXPECT_THAT(Rows,
+ testing::ElementsAre(
+ "lib-count|=<value>|An unsigned", "lib-enable||A bool",
+ "lib-in-build||Available", "lib-limit|=<value>|An optional",
+ "lib-list|=<value>|A list", "lib-modes|=<a|b>|An enum list",
+ "lib-mode|=<a|b>|An enum", "lib-override||An optional bool",
+ "lib-path|=<value>|A string",
+ "lib-print|[=<a|b>]|A flag or enum",
+ "lib-ratio|=<value>|A double",
+ "lib-tristate|=<Default|Enable|Disable>|A tri-state"));
BumpPtrAllocator Alloc;
auto Parse = [&](std::initializer_list<const char *> Argv) {
@@ -150,6 +151,10 @@ TEST(LibraryOptionsTest, Parser) {
"1 option '-lib-count' requires an argument");
EXPECT_EQ(Parse({"-lib-other"}), "1 unknown argument '-lib-other'");
EXPECT_EQ(Parse({"-lib-counts=1"}), "1 unknown argument '-lib-counts=1'");
+ EXPECT_EQ(Parse({"-lib-in-build"}), "1 ");
+ EXPECT_TRUE(TestLibraryOptions::Global.in_build);
+ EXPECT_EQ(Parse({"-lib-not-in-build"}),
+ "1 unknown argument '-lib-not-in-build'");
EXPECT_EQ(Parse({"-lib-enable", "0"}), "1 ");
EXPECT_EQ(Parse({"-lib-print", "a"}), "1 ");
EXPECT_EQ(Parse({"-lib-count", "x"}),
diff --git a/llvm/unittests/Option/LibraryOpts.td b/llvm/unittests/Option/LibraryOpts.td
index e39ca46a684e4..2537e3d3a64b7 100644
--- a/llvm/unittests/Option/LibraryOpts.td
+++ b/llvm/unittests/Option/LibraryOpts.td
@@ -11,6 +11,10 @@ include "llvm/Option/LibraryOptions.td"
def TestLibraryOptions : OptionsStruct<"test", prefix = "lib-">;
defm : BoolField<"lib-enable", "false", "A bool">;
+let Condition = "1" in
+defm : BoolField<"lib-in-build", "false", "Available">;
+let Condition = "0" in
+defm : BoolField<"lib-not-in-build", "false", "Unavailable">;
defm : ValueField<"lib-count", "unsigned", "3", "An unsigned">;
defm : OptionalValueField<"lib-limit", "unsigned", "An optional">;
defm : ListField<"lib-list", "unsigned", "A list">;
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index 09baae4d80c6e..be7dce46b3e75 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -647,7 +647,14 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
OS << ", ";
writeStrTableOffset(OS, Table, getHelpText(R));
OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias");
- OS << ", " << ExtraOffset.lookup(&R);
+ // Where a Condition is false, row 0's zero visibility makes the parser
+ // treat the option as unknown.
+ StringRef Condition = getOptionalString(R, "Condition");
+ if (Condition.empty())
+ OS << ", " << ExtraOffset.lookup(&R);
+ else
+ OS << ",\n#if " << Condition << "\n " << ExtraOffset.lookup(&R)
+ << "\n#else\n 0\n#endif\n ";
std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
OS << ", " << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())];
OS << ", llvm::opt::Option::"
>From 08ca432593e4bfddf55426d6313f2d5e23ecaf2e Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 10 Oct 2026 22:43:18 -0700
Subject: [PATCH 2/5] enhance unittest
---
llvm/unittests/Option/LibraryOpts.td | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/unittests/Option/LibraryOpts.td b/llvm/unittests/Option/LibraryOpts.td
index 2537e3d3a64b7..72fc1e7edf179 100644
--- a/llvm/unittests/Option/LibraryOpts.td
+++ b/llvm/unittests/Option/LibraryOpts.td
@@ -11,9 +11,9 @@ include "llvm/Option/LibraryOptions.td"
def TestLibraryOptions : OptionsStruct<"test", prefix = "lib-">;
defm : BoolField<"lib-enable", "false", "A bool">;
-let Condition = "1" in
+let Condition = "__cplusplus" in
defm : BoolField<"lib-in-build", "false", "Available">;
-let Condition = "0" in
+let Condition = "__cplusplus < 201103L" in
defm : BoolField<"lib-not-in-build", "false", "Unavailable">;
defm : ValueField<"lib-count", "unsigned", "3", "An unsigned">;
defm : OptionalValueField<"lib-limit", "unsigned", "An optional">;
>From 6daa87ce61f3ed4e2fa4d6e21a52a50946e04e95 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 11 Oct 2026 09:40:40 -0700
Subject: [PATCH 3/5] remove member when the condition is false
---
llvm/include/llvm/Option/LibraryOptions.td | 4 +-
.../Transforms/Scalar/LoopStrengthReduce.cpp | 39 +++++++++++--------
.../TableGen/Basic/OptionParserEmitter.cpp | 39 ++++++++++++-------
3 files changed, 49 insertions(+), 33 deletions(-)
diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td
index 0b8b97479048f..9ebffa8bb7d15 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -23,8 +23,8 @@ class OptionsStruct<string namespace = "llvm", string prefix = ""> {
}
// The member an option sets, and the member's type and default value.
-// `let Condition = "!defined(NDEBUG)" in` makes the option unknown where the
-// preprocessor condition is false; the member then keeps its default.
+// `let Condition = "!defined(NDEBUG)" in` makes the option unknown, and removes
+// the member, where the preprocessor condition is false.
class Field<string name, string type, code default> {
string FieldName = name;
string FieldType = type;
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 7252e64a66b18..6073a177babaf 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -143,6 +143,14 @@ static const unsigned MaxIVUsers = 200;
/// the salvaging is not too expensive for the compiler.
static const unsigned MaxSCEVSalvageExpressionSize = 64;
+static bool stressIVChain(const ScalarOptions &Opts) {
+#ifndef NDEBUG
+ return Opts.stress_ivchain;
+#else
+ return false;
+#endif
+}
+
namespace {
struct MemAccessTy {
@@ -2106,8 +2114,9 @@ struct IVChain {
Instruction *tailUserInst() const { return Incs.back().UserInst; }
// Returns true if IncExpr can be profitably added to this chain.
- bool isProfitableIncrement(const ScalarOptions &Opts, const SCEV *OperExpr,
- const SCEV *IncExpr, ScalarEvolution &);
+ bool isProfitableIncrement(const SCEV *OperExpr,
+ const SCEV *IncExpr,
+ ScalarEvolution&);
};
/// Helper for CollectChains to track multiple IV increment uses. Distinguish
@@ -3041,13 +3050,9 @@ static const SCEV *getExprBase(const SCEV *S) {
/// increment will be an offset relative to the same base. We allow such offsets
/// to potentially be used as chain increment as long as it's not obviously
/// expensive to expand using real instructions.
-bool IVChain::isProfitableIncrement(const ScalarOptions &Opts,
- const SCEV *OperExpr, const SCEV *IncExpr,
+bool IVChain::isProfitableIncrement(const SCEV *OperExpr,
+ const SCEV *IncExpr,
ScalarEvolution &SE) {
- // Aggressively form chains when -stress-ivchain.
- if (Opts.stress_ivchain)
- return true;
-
// Do not replace a constant offset from IV head with a nonconstant IV
// increment.
if (!isa<SCEVConstant>(IncExpr)) {
@@ -3070,13 +3075,10 @@ bool IVChain::isProfitableIncrement(const ScalarOptions &Opts,
/// increments can be computed in fewer registers when chained.
///
/// TODO: Consider IVInc free if it's already used in another chains.
-static bool isProfitableChain(const ScalarOptions &Opts, IVChain &Chain,
+static bool isProfitableChain(IVChain &Chain,
SmallPtrSetImpl<Instruction *> &Users,
ScalarEvolution &SE,
const TargetTransformInfo &TTI) {
- if (Opts.stress_ivchain)
- return true;
-
if (!Chain.hasIncs())
return false;
@@ -3168,7 +3170,7 @@ void LSRInstance::ChainInstruction(Instruction *UserInst, Instruction *IVOper,
// are expressions that operate on the same unscaled SCEVUnknown. This
// "base" will be canceled by the subsequent getMinusSCEV call. Checking
// first avoids creating extra SCEV expressions.
- if (!Opts.stress_ivchain && Chain.ExprBase != OperExprBase)
+ if (!stressIVChain(Opts) && Chain.ExprBase != OperExprBase)
continue;
Value *PrevIV = getWideOperand(Chain.Incs.back().IVOperand);
@@ -3185,7 +3187,9 @@ void LSRInstance::ChainInstruction(Instruction *UserInst, Instruction *IVOper,
if (isa<SCEVCouldNotCompute>(IncExpr) || !SE.isLoopInvariant(IncExpr, L))
continue;
- if (Chain.isProfitableIncrement(Opts, OperExpr, IncExpr, SE)) {
+ // Aggressively form chains when -stress-ivchain.
+ if (stressIVChain(Opts) ||
+ Chain.isProfitableIncrement(OperExpr, IncExpr, SE)) {
LastIncExpr = IncExpr;
break;
}
@@ -3195,7 +3199,7 @@ void LSRInstance::ChainInstruction(Instruction *UserInst, Instruction *IVOper,
if (ChainIdx == NChains) {
if (isa<PHINode>(UserInst))
return;
- if (NChains >= MaxChains && !Opts.stress_ivchain) {
+ if (NChains >= MaxChains && !stressIVChain(Opts)) {
LLVM_DEBUG(dbgs() << "IV Chain Limit\n");
return;
}
@@ -3341,7 +3345,8 @@ void LSRInstance::CollectChains() {
unsigned ChainIdx = 0;
for (unsigned UsersIdx = 0, NChains = IVChainVec.size();
UsersIdx < NChains; ++UsersIdx) {
- if (!isProfitableChain(Opts, IVChainVec[UsersIdx],
+ if (!stressIVChain(Opts) &&
+ !isProfitableChain(IVChainVec[UsersIdx],
ChainUsersVec[UsersIdx].FarUsers, SE, TTI))
continue;
// Preserve the chain at UsesIdx.
@@ -6319,7 +6324,7 @@ LSRInstance::LSRInstance(const ScalarOptions &Opts, Loop *L, IVUsers &IU,
// registers.
// FIXME: add profitable chain optimization for other kinds major cost, for
// example number of instructions.
- if (TTI.isNumRegsMajorCostOfLSR() || Opts.stress_ivchain)
+ if (TTI.isNumRegsMajorCostOfLSR() || stressIVChain(Opts))
CollectChains();
CollectInterestingTypesAndFactors();
CollectFixupsAndInitialFormulae();
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index be7dce46b3e75..23b6d4eacf581 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -267,7 +267,7 @@ static void emitOptionsStruct(const Record &Struct,
ArrayRef<const Record *> Opts, raw_ostream &OS) {
struct Member {
std::string Name;
- StringRef Type, Default, Spelling;
+ StringRef Type, Default, Spelling, Condition;
};
std::vector<const Record *> Fields;
for (const Record *R : Opts) {
@@ -298,7 +298,8 @@ static void emitOptionsStruct(const Record &Struct,
StringMap<StringRef> MemberSpelling;
for (const Record *R : ByID) {
Member M{getMemberName(*R, Prefix), R->getValueAsString("FieldType"),
- R->getValueAsString("FieldDefault"), R->getValueAsString("Name")};
+ R->getValueAsString("FieldDefault"), R->getValueAsString("Name"),
+ getOptionalString(*R, "Condition")};
auto [It, Inserted] = MemberSpelling.try_emplace(M.Name, M.Spelling);
if (!Inserted)
PrintFatalError(R->getLoc(),
@@ -319,8 +320,13 @@ static void emitOptionsStruct(const Record &Struct,
OS << "struct " << Name << " {\n";
if (Namespace != "llvm")
OS << " using StringRef = llvm::StringRef;\n";
- for (const Member &M : Members)
+ for (const Member &M : Members) {
+ if (!M.Condition.empty())
+ OS << "#if " << M.Condition << '\n';
OS << " " << M.Type << " " << M.Name << "{" << M.Default << "};\n";
+ if (!M.Condition.empty())
+ OS << "#endif\n";
+ }
OS << "\n /// The instance cl::ParseCommandLineOptions sets.\n";
OS << " static " << Name << " Global;\n\n";
OS << " static const llvm::opt::OptTable &optTable();\n";
@@ -347,6 +353,9 @@ static void emitOptionsStruct(const Record &Struct,
<< "::apply(const llvm::opt::Arg &A, llvm::BumpPtrAllocator &Alloc) {\n";
OS << " switch (A.getOption().getID()) {\n";
for (const Record *R : Fields) {
+ StringRef Condition = getOptionalString(*R, "Condition");
+ if (!Condition.empty())
+ OS << "#if " << Condition << '\n';
OS << " case OPT_" << getStructOptionID(*R) << ":\n";
std::string Member = getMemberName(*R, Prefix);
if (R->getValue("BareValue"))
@@ -368,15 +377,17 @@ static void emitOptionsStruct(const Record &Struct,
<< "\") {\n X = " << Enumerator
<< ";\n return true;\n }\n";
OS << " return false;\n });\n";
- continue;
+ } else {
+ // A FlagOrEq without a value means =true.
+ StringRef Value =
+ R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq"
+ ? "A.getNumValues() ? A.getValue() : \"true\""
+ : "A.getValue()";
+ OS << " return llvm::opt::parseArgValue(" << Value << ", " << Member
+ << ", Alloc);\n";
}
- // A FlagOrEq without a value means =true.
- StringRef Value =
- R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq"
- ? "A.getNumValues() ? A.getValue() : \"true\""
- : "A.getValue()";
- OS << " return llvm::opt::parseArgValue(" << Value << ", " << Member
- << ", Alloc);\n";
+ if (!Condition.empty())
+ OS << "#endif\n";
}
OS << " }\n llvm_unreachable(\"option without a member\");\n}\n";
OS << "#endif // OPTIONS_STRUCT_DEFS\n";
@@ -649,12 +660,12 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias");
// Where a Condition is false, row 0's zero visibility makes the parser
// treat the option as unknown.
+ unsigned Extra = ExtraOffset.lookup(&R);
StringRef Condition = getOptionalString(R, "Condition");
if (Condition.empty())
- OS << ", " << ExtraOffset.lookup(&R);
+ OS << ", " << Extra;
else
- OS << ",\n#if " << Condition << "\n " << ExtraOffset.lookup(&R)
- << "\n#else\n 0\n#endif\n ";
+ OS << ",\n#if " << Condition << '\n' << Extra << "\n#else\n0\n#endif\n";
std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
OS << ", " << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())];
OS << ", llvm::opt::Option::"
>From f604569068137daa2bba3c41c5497fd550b75d3e Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 11 Oct 2026 10:01:10 -0700
Subject: [PATCH 4/5] remove member when the condition is false
---
llvm/lib/Option/LibraryOptions.cpp | 3 -
.../TableGen/Basic/OptionParserEmitter.cpp | 104 +++++++++++-------
2 files changed, 65 insertions(+), 42 deletions(-)
diff --git a/llvm/lib/Option/LibraryOptions.cpp b/llvm/lib/Option/LibraryOptions.cpp
index 55a317dc57f49..cfcb82273eb6d 100644
--- a/llvm/lib/Option/LibraryOptions.cpp
+++ b/llvm/lib/Option/LibraryOptions.cpp
@@ -24,9 +24,6 @@ void LibraryOptionsParser::forEachOption(
unsigned Kind = T.getOptionKind(ID);
if (Kind != Option::FlagOrEqClass && Kind != Option::SeparateOrEqClass)
continue;
- // Skip options a false Condition makes unknown.
- if (!T.getOption(ID).hasVisibilityFlag(DefaultVis))
- continue;
StringRef V = T.getOptionMetaVar(ID);
std::string MetaVar;
if (Kind == Option::SeparateOrEqClass)
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index 23b6d4eacf581..5050199799ca2 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -259,6 +259,39 @@ static std::string getStructOptionID(const Record &R) {
return getSpellingIdentifier(R);
}
+namespace {
+// Wraps what is emitted during its lifetime in `#if Condition` where R has one.
+class ConditionScope {
+ raw_ostream &OS;
+ StringRef Condition;
+
+public:
+ ConditionScope(raw_ostream &OS, const Record &R)
+ : OS(OS), Condition(getOptionalString(R, "Condition")) {
+ if (!Condition.empty())
+ OS << "#if " << Condition << '\n';
+ }
+ ~ConditionScope() {
+ if (!Condition.empty())
+ OS << "#endif\n";
+ }
+};
+} // namespace
+
+// Emits the OPT_ IDs of an OptionsStruct, which its table rows name.
+static void emitStructOptionIDs(ArrayRef<const Record *> Groups,
+ ArrayRef<const Record *> Opts,
+ raw_ostream &OS) {
+ OS << "namespace {\nenum ID : unsigned {\n OPT_INVALID = 0,\n";
+ for (const Record *R : Groups)
+ OS << " OPT_" << getOptionName(*R) << ",\n";
+ for (const Record *R : Opts) {
+ ConditionScope CS(OS, *R);
+ OS << " OPT_" << getStructOptionID(*R) << ",\n";
+ }
+ OS << "};\n} // namespace\n\n";
+}
+
// Emits the struct an OptionsStruct def declares: its declaration under
// OPTIONS_STRUCT_DECL, and under OPTIONS_STRUCT_DEFS the global instance, the
// option table, and apply(), which sets the member an argument names.
@@ -267,7 +300,7 @@ static void emitOptionsStruct(const Record &Struct,
ArrayRef<const Record *> Opts, raw_ostream &OS) {
struct Member {
std::string Name;
- StringRef Type, Default, Spelling, Condition;
+ StringRef Type, Default, Spelling;
};
std::vector<const Record *> Fields;
for (const Record *R : Opts) {
@@ -298,8 +331,7 @@ static void emitOptionsStruct(const Record &Struct,
StringMap<StringRef> MemberSpelling;
for (const Record *R : ByID) {
Member M{getMemberName(*R, Prefix), R->getValueAsString("FieldType"),
- R->getValueAsString("FieldDefault"), R->getValueAsString("Name"),
- getOptionalString(*R, "Condition")};
+ R->getValueAsString("FieldDefault"), R->getValueAsString("Name")};
auto [It, Inserted] = MemberSpelling.try_emplace(M.Name, M.Spelling);
if (!Inserted)
PrintFatalError(R->getLoc(),
@@ -320,12 +352,9 @@ static void emitOptionsStruct(const Record &Struct,
OS << "struct " << Name << " {\n";
if (Namespace != "llvm")
OS << " using StringRef = llvm::StringRef;\n";
- for (const Member &M : Members) {
- if (!M.Condition.empty())
- OS << "#if " << M.Condition << '\n';
+ for (auto [R, M] : llvm::zip_equal(ByID, Members)) {
+ ConditionScope CS(OS, *R);
OS << " " << M.Type << " " << M.Name << "{" << M.Default << "};\n";
- if (!M.Condition.empty())
- OS << "#endif\n";
}
OS << "\n /// The instance cl::ParseCommandLineOptions sets.\n";
OS << " static " << Name << " Global;\n\n";
@@ -340,12 +369,6 @@ static void emitOptionsStruct(const Record &Struct,
std::string Qualified = (Namespace + "::" + Name).str();
OS << "\n#ifdef OPTIONS_STRUCT_DEFS\n#undef OPTIONS_STRUCT_DEFS\n";
OS << Qualified << " " << Qualified << "::Global;\n\n";
- OS << "namespace {\nenum ID : unsigned {\n OPT_INVALID = 0,\n";
- for (const Record *R : Groups)
- OS << " OPT_" << getOptionName(*R) << ",\n";
- for (const Record *R : Opts)
- OS << " OPT_" << getStructOptionID(*R) << ",\n";
- OS << "};\n} // namespace\n\n";
OS << "const llvm::opt::OptTable &" << Qualified << "::optTable() {\n";
OS << " static const llvm::opt::LibraryOptTable T(optionTables());\n";
OS << " return T;\n}\n\n";
@@ -353,9 +376,7 @@ static void emitOptionsStruct(const Record &Struct,
<< "::apply(const llvm::opt::Arg &A, llvm::BumpPtrAllocator &Alloc) {\n";
OS << " switch (A.getOption().getID()) {\n";
for (const Record *R : Fields) {
- StringRef Condition = getOptionalString(*R, "Condition");
- if (!Condition.empty())
- OS << "#if " << Condition << '\n';
+ ConditionScope CS(OS, *R);
OS << " case OPT_" << getStructOptionID(*R) << ":\n";
std::string Member = getMemberName(*R, Prefix);
if (R->getValue("BareValue"))
@@ -377,17 +398,15 @@ static void emitOptionsStruct(const Record &Struct,
<< "\") {\n X = " << Enumerator
<< ";\n return true;\n }\n";
OS << " return false;\n });\n";
- } else {
- // A FlagOrEq without a value means =true.
- StringRef Value =
- R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq"
- ? "A.getNumValues() ? A.getValue() : \"true\""
- : "A.getValue()";
- OS << " return llvm::opt::parseArgValue(" << Value << ", " << Member
- << ", Alloc);\n";
+ continue;
}
- if (!Condition.empty())
- OS << "#endif\n";
+ // A FlagOrEq without a value means =true.
+ StringRef Value =
+ R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq"
+ ? "A.getNumValues() ? A.getValue() : \"true\""
+ : "A.getValue()";
+ OS << " return llvm::opt::parseArgValue(" << Value << ", " << Member
+ << ", Alloc);\n";
}
OS << " }\n llvm_unreachable(\"option without a member\");\n}\n";
OS << "#endif // OPTIONS_STRUCT_DEFS\n";
@@ -488,10 +507,16 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
OptionID.try_emplace(&R, OptionID.size() + 1);
for (const Record &R : llvm::make_pointee_range(Opts))
OptionID.try_emplace(&R, OptionID.size() + 1);
- auto GetRefID = [&](const Record &R, StringRef Field) {
- if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit(Field)))
- return OptionID.lookup(DI->getDef());
- return 0u;
+ // An OptionsStruct's rows name the IDs, since a false Condition omits a row
+ // and shifts the IDs after it.
+ auto GetRefID = [&](const Record &R, StringRef Field) -> std::string {
+ const auto *DI = dyn_cast<DefInit>(R.getValueInit(Field));
+ if (!DI)
+ return "0";
+ const Record *Ref = DI->getDef();
+ if (Structs.empty())
+ return utostr(OptionID.lookup(Ref));
+ return "OPT_" + getStructOptionID(*Ref);
};
OS << "/////////\n";
@@ -500,6 +525,8 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
OS << (Structs.empty()
? "#ifdef OPTTABLE_CODE\n"
: "#if defined(OPTTABLE_CODE) || defined(OPTIONS_STRUCT_DEFS)\n");
+ if (!Structs.empty())
+ emitStructOptionIDs(Groups, Opts, OS);
// A function rather than an object: the object needs dynamic relocations.
OS << "static llvm::opt::OptTable::Tables optionTables() {\n";
// An OptionsStruct's .cpp has no using-directive for llvm::opt.
@@ -652,20 +679,18 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
<< ", 0, 0, 0, llvm::opt::Option::GroupClass},\n";
}
for (const Record &R : llvm::make_pointee_range(Opts)) {
+ ConditionScope CS(OS, R);
OS << " {";
writeStrTableOffset(OS, Table, getOptionPrefixedName(R),
/*EmitComment=*/true);
OS << ", ";
writeStrTableOffset(OS, Table, getHelpText(R));
+ if (const auto *DI = dyn_cast<DefInit>(R.getValueInit("Alias"));
+ DI && !getOptionalString(*DI->getDef(), "Condition").empty())
+ PrintFatalError(R.getLoc(), "an alias cannot refer to an option with a "
+ "Condition");
OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias");
- // Where a Condition is false, row 0's zero visibility makes the parser
- // treat the option as unknown.
- unsigned Extra = ExtraOffset.lookup(&R);
- StringRef Condition = getOptionalString(R, "Condition");
- if (Condition.empty())
- OS << ", " << Extra;
- else
- OS << ",\n#if " << Condition << '\n' << Extra << "\n#else\n0\n#endif\n";
+ OS << ", " << ExtraOffset.lookup(&R);
std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
OS << ", " << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())];
OS << ", llvm::opt::Option::"
@@ -832,6 +857,7 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
std::vector<const Record *> OptsWithMarshalling;
for (const Record &R : llvm::make_pointee_range(Opts)) {
+ ConditionScope CS(OS, R);
// Start a single option entry.
OS << "OPTION(";
WriteOptRecordFields(OS, R);
>From e679714a391a0ec853283463296850672ca02ebe Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 11 Oct 2026 10:10:14 -0700
Subject: [PATCH 5/5] enable `let Condition = AssertsOnly in`
---
llvm/include/llvm/Option/LibraryOptions.td | 13 ++++++++++---
llvm/lib/Transforms/Scalar/ScalarOptions.td | 4 ++--
llvm/unittests/Option/LibraryOpts.td | 6 ++++--
llvm/utils/TableGen/Basic/OptionParserEmitter.cpp | 11 +++++++++--
4 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td
index 9ebffa8bb7d15..8aca537f309b8 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -22,14 +22,21 @@ class OptionsStruct<string namespace = "llvm", string prefix = ""> {
string MemberPrefix = prefix;
}
+// The builds an option exists in, as a preprocessor expression.
+class BuildCondition<code expr> {
+ code Expr = expr;
+}
+def AssertsOnly : BuildCondition<"!defined(NDEBUG)">;
+def DumpOnly : BuildCondition<"!defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)">;
+
// The member an option sets, and the member's type and default value.
-// `let Condition = "!defined(NDEBUG)" in` makes the option unknown, and removes
-// the member, where the preprocessor condition is false.
+// `let Condition = AssertsOnly in` makes the option unknown, and removes the
+// member, in other builds.
class Field<string name, string type, code default> {
string FieldName = name;
string FieldType = type;
code FieldDefault = default;
- code Condition = ?;
+ BuildCondition Condition = ?;
}
// The value a FlagOrEq row assigns when given without one.
diff --git a/llvm/lib/Transforms/Scalar/ScalarOptions.td b/llvm/lib/Transforms/Scalar/ScalarOptions.td
index 68db4f2c24911..4aa027c1a512f 100644
--- a/llvm/lib/Transforms/Scalar/ScalarOptions.td
+++ b/llvm/lib/Transforms/Scalar/ScalarOptions.td
@@ -259,7 +259,7 @@ defm : BoolField<"loop-flatten-widen-iv", "true",
// LoopFuse.cpp
defm : ValueField<"loop-fusion-peel-max-count", "uint32_t", "0",
"Max number of iterations to be peeled from a loop, such that fusion can take place">;
-let Condition = "!defined(NDEBUG)" in
+let Condition = AssertsOnly in
defm : BoolField<"loop-fusion-verbose-debug", "false",
"Enable verbose debugging for Loop Fusion">;
@@ -364,7 +364,7 @@ defm : OptionalEnumField<"lsr-preferred-addressing-mode", "TargetTransformInfo::
defm : ValueField<"lsr-setupcost-depth-limit", "unsigned", "7",
"The limit on recursion depth for LSRs setup cost">;
// Stress test IV chain generation.
-let Condition = "!defined(NDEBUG)" in
+let Condition = AssertsOnly in
defm : BoolField<"stress-ivchain", "false",
"Stress test LSR IV chains">;
diff --git a/llvm/unittests/Option/LibraryOpts.td b/llvm/unittests/Option/LibraryOpts.td
index 72fc1e7edf179..6e4d48bc8d37a 100644
--- a/llvm/unittests/Option/LibraryOpts.td
+++ b/llvm/unittests/Option/LibraryOpts.td
@@ -11,9 +11,11 @@ include "llvm/Option/LibraryOptions.td"
def TestLibraryOptions : OptionsStruct<"test", prefix = "lib-">;
defm : BoolField<"lib-enable", "false", "A bool">;
-let Condition = "__cplusplus" in
+def InBuild : BuildCondition<"__cplusplus">;
+def NotInBuild : BuildCondition<"__cplusplus < 201103L">;
+let Condition = InBuild in
defm : BoolField<"lib-in-build", "false", "Available">;
-let Condition = "__cplusplus < 201103L" in
+let Condition = NotInBuild in
defm : BoolField<"lib-not-in-build", "false", "Unavailable">;
defm : ValueField<"lib-count", "unsigned", "3", "An unsigned">;
defm : OptionalValueField<"lib-limit", "unsigned", "An optional">;
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index 5050199799ca2..c4b23c046e718 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -259,6 +259,13 @@ static std::string getStructOptionID(const Record &R) {
return getSpellingIdentifier(R);
}
+// The preprocessor expression of R's Condition, or "" if it has none.
+static StringRef getCondition(const Record &R) {
+ const RecordVal *V = R.getValue("Condition");
+ const auto *DI = V ? dyn_cast<DefInit>(V->getValue()) : nullptr;
+ return DI ? DI->getDef()->getValueAsString("Expr") : "";
+}
+
namespace {
// Wraps what is emitted during its lifetime in `#if Condition` where R has one.
class ConditionScope {
@@ -267,7 +274,7 @@ class ConditionScope {
public:
ConditionScope(raw_ostream &OS, const Record &R)
- : OS(OS), Condition(getOptionalString(R, "Condition")) {
+ : OS(OS), Condition(getCondition(R)) {
if (!Condition.empty())
OS << "#if " << Condition << '\n';
}
@@ -686,7 +693,7 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
OS << ", ";
writeStrTableOffset(OS, Table, getHelpText(R));
if (const auto *DI = dyn_cast<DefInit>(R.getValueInit("Alias"));
- DI && !getOptionalString(*DI->getDef(), "Condition").empty())
+ DI && !getCondition(*DI->getDef()).empty())
PrintFatalError(R.getLoc(), "an alias cannot refer to an option with a "
"Condition");
OS << ", " << GetRefID(R, "Group") << ", " << GetRefID(R, "Alias");
More information about the llvm-commits
mailing list