Skip to content

Commit d3cdf0f

Browse files
authored
[LLVM][Option] Refactor option name comparison (#108219)
Move common functions shared by TableGen Option Emitter and Options library to Support: - Move `StrCmpOptionName` and base it on the existing version in OptTable.cpp, with an additional mode to control fall back to case insensitive comparison. - Add `StrCmpOptionPrefixes` to compare prefixes and use zip() to iterate through lists of prefixes. - Rename `CompareOptionRecords` to less ambiguous name `IsOptionRecordLess`. - Merge 2 back-to-back ifs with same condition in `IsOptionRecordLess`. Fixes #107723
1 parent 0e34dbb commit d3cdf0f

File tree

9 files changed

+118
-91
lines changed

9 files changed

+118
-91
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===- OptionStrCmp.h - Option String Comparison ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_SUPPORT_OPTIONSTRCMP_H
10+
#define LLVM_SUPPORT_OPTIONSTRCMP_H
11+
12+
#include "llvm/ADT/ArrayRef.h"
13+
#include "llvm/ADT/StringRef.h"
14+
15+
namespace llvm {
16+
17+
// Comparison function for Option strings (option names & prefixes).
18+
// The ordering is *almost* case-insensitive lexicographic, with an exception.
19+
// '\0' comes at the end of the alphabet instead of the beginning (thus options
20+
// precede any other options which prefix them). Additionally, if two options
21+
// are identical ignoring case, they are ordered according to case sensitive
22+
// ordering if `FallbackCaseSensitive` is true.
23+
int StrCmpOptionName(StringRef A, StringRef B,
24+
bool FallbackCaseSensitive = true);
25+
26+
// Comparison function for Option prefixes.
27+
int StrCmpOptionPrefixes(ArrayRef<StringRef> APrefixes,
28+
ArrayRef<StringRef> BPrefixes);
29+
30+
} // namespace llvm
31+
32+
#endif // LLVM_SUPPORT_OPTIONSTRCMP_H

llvm/lib/Option/OptTable.cpp

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/Support/CommandLine.h" // for expandResponseFiles
1717
#include "llvm/Support/Compiler.h"
1818
#include "llvm/Support/ErrorHandling.h"
19+
#include "llvm/Support/OptionStrCmp.h"
1920
#include "llvm/Support/raw_ostream.h"
2021
#include <algorithm>
2122
#include <cassert>
@@ -30,42 +31,26 @@
3031
using namespace llvm;
3132
using namespace llvm::opt;
3233

33-
namespace llvm {
34-
namespace opt {
35-
36-
// Ordering on Info. The ordering is *almost* case-insensitive lexicographic,
37-
// with an exception. '\0' comes at the end of the alphabet instead of the
38-
// beginning (thus options precede any other options which prefix them).
39-
static int StrCmpOptionNameIgnoreCase(StringRef A, StringRef B) {
40-
size_t MinSize = std::min(A.size(), B.size());
41-
if (int Res = A.substr(0, MinSize).compare_insensitive(B.substr(0, MinSize)))
42-
return Res;
43-
44-
if (A.size() == B.size())
45-
return 0;
46-
47-
return (A.size() == MinSize) ? 1 /* A is a prefix of B. */
48-
: -1 /* B is a prefix of A */;
49-
}
50-
34+
namespace llvm::opt {
5135
#ifndef NDEBUG
52-
static int StrCmpOptionName(StringRef A, StringRef B) {
53-
if (int N = StrCmpOptionNameIgnoreCase(A, B))
54-
return N;
55-
return A.compare(B);
56-
}
57-
5836
static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) {
5937
if (&A == &B)
6038
return false;
6139

62-
if (int N = StrCmpOptionName(A.getName(), B.getName()))
63-
return N < 0;
40+
if (int Cmp = StrCmpOptionName(A.getName(), B.getName()))
41+
return Cmp < 0;
6442

65-
for (size_t I = 0, K = std::min(A.Prefixes.size(), B.Prefixes.size()); I != K;
66-
++I)
67-
if (int N = StrCmpOptionName(A.Prefixes[I], B.Prefixes[I]))
68-
return N < 0;
43+
// Note: we are converting ArrayRef<StringLiteral> to ArrayRef<StringRef>.
44+
// In general, ArrayRef<SubClass> cannot be safely viewed as ArrayRef<Base>
45+
// since sizeof(SubClass) may not be same as sizeof(Base). However in this
46+
// case, sizeof(StringLiteral) is same as sizeof(StringRef), so this
47+
// conversion is safe.
48+
static_assert(sizeof(StringRef) == sizeof(StringLiteral));
49+
ArrayRef<StringRef> APrefixes(A.Prefixes.data(), A.Prefixes.size());
50+
ArrayRef<StringRef> BPrefixes(B.Prefixes.data(), B.Prefixes.size());
51+
52+
if (int Cmp = StrCmpOptionPrefixes(APrefixes, BPrefixes))
53+
return Cmp < 0;
6954

7055
// Names are the same, check that classes are in order; exactly one
7156
// should be joined, and it should succeed the other.
@@ -77,11 +62,10 @@ static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) {
7762

7863
// Support lower_bound between info and an option name.
7964
static inline bool operator<(const OptTable::Info &I, StringRef Name) {
80-
return StrCmpOptionNameIgnoreCase(I.getName(), Name) < 0;
65+
// Do not fallback to case sensitive comparison.
66+
return StrCmpOptionName(I.getName(), Name, false) < 0;
8167
}
82-
83-
} // end namespace opt
84-
} // end namespace llvm
68+
} // namespace llvm::opt
8569

8670
OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {}
8771

llvm/lib/Option/Option.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "llvm/Option/Option.h"
910
#include "llvm/ADT/StringRef.h"
1011
#include "llvm/ADT/Twine.h"
1112
#include "llvm/Config/llvm-config.h"
1213
#include "llvm/Option/Arg.h"
1314
#include "llvm/Option/ArgList.h"
14-
#include "llvm/Option/Option.h"
1515
#include "llvm/Option/OptTable.h"
1616
#include "llvm/Support/Compiler.h"
1717
#include "llvm/Support/Debug.h"
1818
#include "llvm/Support/ErrorHandling.h"
1919
#include "llvm/Support/raw_ostream.h"
2020
#include <cassert>
21-
#include <cstring>
2221

2322
using namespace llvm;
2423
using namespace llvm::opt;

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ add_llvm_component_library(LLVMSupport
212212
NativeFormatting.cpp
213213
OptimizedStructLayout.cpp
214214
Optional.cpp
215+
OptionStrCmp.cpp
215216
PGOOptions.cpp
216217
Parallel.cpp
217218
PluginLoader.cpp

llvm/lib/Support/OptionStrCmp.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===- OptionStrCmp.cpp - Option String Comparison --------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Support/OptionStrCmp.h"
10+
#include "llvm/ADT/STLExtras.h"
11+
12+
using namespace llvm;
13+
14+
// Comparison function for Option strings (option names & prefixes).
15+
// The ordering is *almost* case-insensitive lexicographic, with an exception.
16+
// '\0' comes at the end of the alphabet instead of the beginning (thus options
17+
// precede any other options which prefix them). Additionally, if two options
18+
// are identical ignoring case, they are ordered according to case sensitive
19+
// ordering if `FallbackCaseSensitive` is true.
20+
int llvm::StrCmpOptionName(StringRef A, StringRef B,
21+
bool FallbackCaseSensitive) {
22+
size_t MinSize = std::min(A.size(), B.size());
23+
if (int Res = A.substr(0, MinSize).compare_insensitive(B.substr(0, MinSize)))
24+
return Res;
25+
26+
// If they are identical ignoring case, use case sensitive ordering.
27+
if (A.size() == B.size())
28+
return FallbackCaseSensitive ? A.compare(B) : 0;
29+
30+
return (A.size() == MinSize) ? 1 /* A is a prefix of B. */
31+
: -1 /* B is a prefix of A */;
32+
}
33+
34+
// Comparison function for Option prefixes.
35+
int llvm::StrCmpOptionPrefixes(ArrayRef<StringRef> APrefixes,
36+
ArrayRef<StringRef> BPrefixes) {
37+
for (const auto &[APre, BPre] : zip(APrefixes, BPrefixes)) {
38+
if (int Cmp = StrCmpOptionName(APre, BPre))
39+
return Cmp;
40+
}
41+
// Both prefixes are identical.
42+
return 0;
43+
}

llvm/utils/TableGen/Common/OptEmitter.cpp

Lines changed: 19 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,77 +8,44 @@
88

99
#include "OptEmitter.h"
1010
#include "llvm/ADT/Twine.h"
11+
#include "llvm/Support/OptionStrCmp.h"
1112
#include "llvm/TableGen/Error.h"
1213
#include "llvm/TableGen/Record.h"
13-
#include <cctype>
14-
#include <cstring>
15-
16-
namespace llvm {
17-
18-
// Ordering on Info. The logic should match with the consumer-side function in
19-
// llvm/Option/OptTable.h.
20-
// FIXME: Make this take StringRefs instead of null terminated strings to
21-
// simplify callers.
22-
static int StrCmpOptionName(const char *A, const char *B) {
23-
const char *X = A, *Y = B;
24-
char a = tolower(*A), b = tolower(*B);
25-
while (a == b) {
26-
if (a == '\0')
27-
return strcmp(A, B);
28-
29-
a = tolower(*++X);
30-
b = tolower(*++Y);
31-
}
32-
33-
if (a == '\0') // A is a prefix of B.
34-
return 1;
35-
if (b == '\0') // B is a prefix of A.
36-
return -1;
37-
38-
// Otherwise lexicographic.
39-
return (a < b) ? -1 : 1;
40-
}
4114

4215
// Returns true if A is ordered before B.
43-
bool CompareOptionRecords(const Record *A, const Record *B) {
16+
bool llvm::IsOptionRecordsLess(const Record *A, const Record *B) {
4417
if (A == B)
4518
return false;
19+
4620
// Sentinel options precede all others and are only ordered by precedence.
47-
bool ASent = A->getValueAsDef("Kind")->getValueAsBit("Sentinel");
48-
bool BSent = B->getValueAsDef("Kind")->getValueAsBit("Sentinel");
21+
const Record *AKind = A->getValueAsDef("Kind");
22+
const Record *BKind = B->getValueAsDef("Kind");
23+
24+
bool ASent = AKind->getValueAsBit("Sentinel");
25+
bool BSent = BKind->getValueAsBit("Sentinel");
4926
if (ASent != BSent)
5027
return ASent;
5128

52-
// Compare options by name, unless they are sentinels.
53-
if (!ASent)
54-
if (int Cmp = StrCmpOptionName(A->getValueAsString("Name").str().c_str(),
55-
B->getValueAsString("Name").str().c_str()))
56-
return Cmp < 0;
29+
std::vector<StringRef> APrefixes = A->getValueAsListOfStrings("Prefixes");
30+
std::vector<StringRef> BPrefixes = B->getValueAsListOfStrings("Prefixes");
5731

32+
// Compare options by name, unless they are sentinels.
5833
if (!ASent) {
59-
std::vector<StringRef> APrefixes = A->getValueAsListOfStrings("Prefixes");
60-
std::vector<StringRef> BPrefixes = B->getValueAsListOfStrings("Prefixes");
34+
if (int Cmp = StrCmpOptionName(A->getValueAsString("Name"),
35+
B->getValueAsString("Name")))
36+
return Cmp < 0;
6137

62-
for (std::vector<StringRef>::const_iterator APre = APrefixes.begin(),
63-
AEPre = APrefixes.end(),
64-
BPre = BPrefixes.begin(),
65-
BEPre = BPrefixes.end();
66-
APre != AEPre && BPre != BEPre; ++APre, ++BPre) {
67-
if (int Cmp = StrCmpOptionName(APre->str().c_str(), BPre->str().c_str()))
68-
return Cmp < 0;
69-
}
38+
if (int Cmp = StrCmpOptionPrefixes(APrefixes, BPrefixes))
39+
return Cmp < 0;
7040
}
7141

7242
// Then by the kind precedence;
73-
int APrec = A->getValueAsDef("Kind")->getValueAsInt("Precedence");
74-
int BPrec = B->getValueAsDef("Kind")->getValueAsInt("Precedence");
75-
if (APrec == BPrec && A->getValueAsListOfStrings("Prefixes") ==
76-
B->getValueAsListOfStrings("Prefixes")) {
43+
int APrec = AKind->getValueAsInt("Precedence");
44+
int BPrec = BKind->getValueAsInt("Precedence");
45+
if (APrec == BPrec && APrefixes == BPrefixes) {
7746
PrintError(A->getLoc(), Twine("Option is equivalent to"));
7847
PrintError(B->getLoc(), Twine("Other defined here"));
7948
PrintFatalError("Equivalent Options found.");
8049
}
8150
return APrec < BPrec;
8251
}
83-
84-
} // namespace llvm

llvm/utils/TableGen/Common/OptEmitter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
namespace llvm {
1313
class Record;
14-
bool CompareOptionRecords(const Record *A, const Record *B);
14+
/// Return true of Option record \p A is ordered before \p B.
15+
bool IsOptionRecordsLess(const Record *A, const Record *B);
1516
} // namespace llvm
1617

1718
#endif // LLVM_UTILS_TABLEGEN_COMMON_OPTEMITTER_H

llvm/utils/TableGen/OptParserEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,10 @@ static void EmitOptParser(const RecordKeeper &Records, raw_ostream &OS) {
255255
ArrayRef<const Record *> Groups =
256256
Records.getAllDerivedDefinitions("OptionGroup");
257257
std::vector<const Record *> Opts = Records.getAllDerivedDefinitions("Option");
258+
llvm::sort(Opts, IsOptionRecordsLess);
258259

259260
emitSourceFileHeader("Option Parsing Definitions", OS);
260261

261-
llvm::sort(Opts, CompareOptionRecords);
262262
// Generate prefix groups.
263263
typedef SmallVector<SmallString<2>, 2> PrefixKeyT;
264264
typedef std::map<PrefixKeyT, std::string> PrefixesT;

llvm/utils/TableGen/OptRSTEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ static void EmitOptRST(const RecordKeeper &Records, raw_ostream &OS) {
2222

2323
// Get the options.
2424
std::vector<const Record *> Opts = Records.getAllDerivedDefinitions("Option");
25-
llvm::sort(Opts, CompareOptionRecords);
25+
llvm::sort(Opts, IsOptionRecordsLess);
2626

2727
// Get the option groups.
2828
for (const Record *R : Records.getAllDerivedDefinitions("OptionGroup"))

0 commit comments

Comments
 (0)