Skip to content

Commit 08e4ee1

Browse files
author
git apple-llvm automerger
committed
Merge commit '1a70420ff3b9' from llvm.org/main into next
2 parents e93c7f2 + 1a70420 commit 08e4ee1

File tree

3 files changed

+77
-29
lines changed

3 files changed

+77
-29
lines changed

mlir/include/mlir/Pass/PassOptions.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ class PassOptions : protected llvm::cl::SubCommand {
253253
assert(!(this->getMiscFlags() & llvm::cl::MiscFlags::CommaSeparated) &&
254254
"ListOption is implicitly comma separated, specifying "
255255
"CommaSeparated is extraneous");
256+
257+
// Make the default explicitly "empty" if no default was given.
258+
if (!this->isDefaultAssigned())
259+
this->setInitialValues({});
260+
256261
parent.options.push_back(this);
257262
elementParser.initialize();
258263
}
@@ -296,11 +301,21 @@ class PassOptions : protected llvm::cl::SubCommand {
296301
const llvm::cl::Option *getOption() const final { return this; }
297302

298303
/// Print the name and value of this option to the given stream.
304+
/// Note that there is currently a limitation with regards to
305+
/// `ListOption<string>`: parsing 'option=""` will result in `option` being
306+
/// set to the empty list, not to a size-1 list containing an empty string.
299307
void print(raw_ostream &os) final {
300-
// Don't print the list if empty. An empty option value can be treated as
301-
// an element of the list in certain cases (e.g. ListOption<std::string>).
302-
if ((**this).empty())
303-
return;
308+
// Don't print the list if the value is the default value.
309+
if (this->isDefaultAssigned() &&
310+
this->getDefault().size() == (**this).size()) {
311+
unsigned i = 0;
312+
for (unsigned e = (**this).size(); i < e; i++) {
313+
if (!this->getDefault()[i].compare((**this)[i]))
314+
break;
315+
}
316+
if (i == (**this).size())
317+
return;
318+
}
304319

305320
os << this->ArgStr << "={";
306321
auto printElementFn = [&](const DataType &value) {

mlir/lib/Pass/PassRegistry.cpp

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,30 @@ const PassPipelineInfo *mlir::PassPipelineInfo::lookup(StringRef pipelineArg) {
186186
// PassOptions
187187
//===----------------------------------------------------------------------===//
188188

189+
/// Attempt to find the next occurance of character 'c' in the string starting
190+
/// from the `index`-th position , omitting any occurances that appear within
191+
/// intervening ranges or literals.
192+
static size_t findChar(StringRef str, size_t index, char c) {
193+
for (size_t i = index, e = str.size(); i < e; ++i) {
194+
if (str[i] == c)
195+
return i;
196+
// Check for various range characters.
197+
if (str[i] == '{')
198+
i = findChar(str, i + 1, '}');
199+
else if (str[i] == '(')
200+
i = findChar(str, i + 1, ')');
201+
else if (str[i] == '[')
202+
i = findChar(str, i + 1, ']');
203+
else if (str[i] == '\"')
204+
i = str.find_first_of('\"', i + 1);
205+
else if (str[i] == '\'')
206+
i = str.find_first_of('\'', i + 1);
207+
if (i == StringRef::npos)
208+
return StringRef::npos;
209+
}
210+
return StringRef::npos;
211+
}
212+
189213
/// Extract an argument from 'options' and update it to point after the arg.
190214
/// Returns the cleaned argument string.
191215
static StringRef extractArgAndUpdateOptions(StringRef &options,
@@ -194,47 +218,37 @@ static StringRef extractArgAndUpdateOptions(StringRef &options,
194218
options = options.drop_front(argSize).ltrim();
195219

196220
// Early exit if there's no escape sequence.
197-
if (str.size() <= 2)
221+
if (str.size() <= 1)
198222
return str;
199223

200224
const auto escapePairs = {std::make_pair('\'', '\''),
201-
std::make_pair('"', '"'), std::make_pair('{', '}')};
225+
std::make_pair('"', '"')};
202226
for (const auto &escape : escapePairs) {
203227
if (str.front() == escape.first && str.back() == escape.second) {
204228
// Drop the escape characters and trim.
205-
str = str.drop_front().drop_back().trim();
206229
// Don't process additional escape sequences.
207-
break;
230+
return str.drop_front().drop_back().trim();
208231
}
209232
}
210233

234+
// Arguments may be wrapped in `{...}`. Unlike the quotation markers that
235+
// denote literals, we respect scoping here. The outer `{...}` should not
236+
// be stripped in cases such as "arg={...},{...}", which can be used to denote
237+
// lists of nested option structs.
238+
if (str.front() == '{') {
239+
unsigned match = findChar(str, 1, '}');
240+
if (match == str.size() - 1)
241+
str = str.drop_front().drop_back().trim();
242+
}
243+
211244
return str;
212245
}
213246

214247
LogicalResult detail::pass_options::parseCommaSeparatedList(
215248
llvm::cl::Option &opt, StringRef argName, StringRef optionStr,
216249
function_ref<LogicalResult(StringRef)> elementParseFn) {
217-
// Functor used for finding a character in a string, and skipping over
218-
// various "range" characters.
219-
llvm::unique_function<size_t(StringRef, size_t, char)> findChar =
220-
[&](StringRef str, size_t index, char c) -> size_t {
221-
for (size_t i = index, e = str.size(); i < e; ++i) {
222-
if (str[i] == c)
223-
return i;
224-
// Check for various range characters.
225-
if (str[i] == '{')
226-
i = findChar(str, i + 1, '}');
227-
else if (str[i] == '(')
228-
i = findChar(str, i + 1, ')');
229-
else if (str[i] == '[')
230-
i = findChar(str, i + 1, ']');
231-
else if (str[i] == '\"')
232-
i = str.find_first_of('\"', i + 1);
233-
else if (str[i] == '\'')
234-
i = str.find_first_of('\'', i + 1);
235-
}
236-
return StringRef::npos;
237-
};
250+
if (optionStr.empty())
251+
return success();
238252

239253
size_t nextElePos = findChar(optionStr, 0, ',');
240254
while (nextElePos != StringRef::npos) {

mlir/test/Pass/pipeline-options-parsing.mlir

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@
1414
// RUN: mlir-opt %s -verify-each=false '-test-options-super-pass-pipeline=super-list={{enum=zero list=1 string=foo},{enum=one list=2 string="bar"},{enum=two list=3 string={baz}}}' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_7 %s
1515
// RUN: mlir-opt %s -verify-each=false -pass-pipeline='builtin.module(func.func(test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one list={2} string=bar },{enum=two list={3} string=baz }}}))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_7 %s
1616

17+
18+
// This test checks that lists-of-nested-options like 'option1={...},{....}' can be parsed
19+
// just like how 'option=1,2,3' is also allowed:
20+
21+
// RUN: mlir-opt %s -verify-each=false -pass-pipeline='builtin.module(func.func(test-options-super-pass{list={enum=zero list={1} string=foo },{enum=one list={2} string=bar },{enum=two list={3} string=baz }}))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_7 %s
22+
23+
// This test checks that it is legal to specify an empty list using '{}'.
24+
// RUN: mlir-opt %s -verify-each=false '--test-options-super-pass=list={enum=zero list={1} string=foo},{enum=one list={} string=bar}' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_8 %s
25+
26+
// It is not possible to specify a size-1 list of empty string.
27+
// It is possible to specify a size > 1 list of empty strings.
28+
// RUN: mlir-opt %s -verify-each=false '--pass-pipeline=builtin.module(func.func(test-options-pass{string-list={""}}))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_9 %s
29+
// RUN: mlir-opt %s -verify-each=false '--pass-pipeline=builtin.module(func.func(test-options-pass{string-list={,}}))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_10 %s
30+
// RUN: mlir-opt %s -verify-each=false '--pass-pipeline=builtin.module(func.func(test-options-pass{string-list={"",}}))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_10 %s
31+
32+
1733
// CHECK_ERROR_1: missing closing '}' while processing pass options
1834
// CHECK_ERROR_2: no such option test-option
1935
// CHECK_ERROR_3: no such option invalid-option
@@ -27,3 +43,6 @@
2743
// CHECK_5: builtin.module(builtin.module(func.func(test-options-pass{enum=zero list={3} string= }),func.func(test-options-pass{enum=one list={1,2,3,4} string={foo bar baz} })))
2844
// CHECK_6: builtin.module(builtin.module(func.func(test-options-pass{enum=zero list={3} string= }),func.func(test-options-pass{enum=one list={1,2,3,4} string=foo"bar"baz })))
2945
// CHECK_7{LITERAL}: builtin.module(func.func(test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one list={2} string=bar },{enum=two list={3} string=baz }}}))
46+
// CHECK_8{LITERAL}: builtin.module(func.func(test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one string=bar }}}))
47+
// CHECK_9: builtin.module(func.func(test-options-pass{enum=zero string= string-list={}}))
48+
// CHECK_10: builtin.module(func.func(test-options-pass{enum=zero string= string-list={,}}))

0 commit comments

Comments
 (0)