Skip to content

Commit b9a8334

Browse files
committed
[Test] Contextualized bare test_spec refs.
Made bare @Instruction and @block more useful. Rather than referring to the first instruction and block in the current function, instead, they now refer to the instruction after the test_specification instruction (which must always exist) and the block containing the test_specification instruction.
1 parent b970e95 commit b9a8334

File tree

5 files changed

+132
-37
lines changed

5 files changed

+132
-37
lines changed

docs/SIL.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3949,15 +3949,15 @@ The following types of test arguments are supported:
39493949
- function: @function <-- the current function
39503950
@function[uint] <-- function at index ``uint``
39513951
@function[name] <-- function named ``name``
3952-
- block: @block <-- the first block
3952+
- block: @block <-- the block containing the test_specification instruction
39533953
@block[uint] <-- the block at index ``uint``
39543954
@{function}.{block} <-- the indicated block in the indicated function
39553955
Example: @function[foo].block[2]
39563956
- trace: @trace <-- the first ``debug_value [trace]`` in the current function
39573957
@trace[uint] <-- the ``debug_value [trace]`` at index ``uint``
39583958
@{function}.{trace} <-- the indicated trace in the indicated function
39593959
Example: @function[bar].trace
3960-
- instruction: @instruction <-- the first instruction
3960+
- instruction: @instruction <-- the instruction after* the test_specification instruction
39613961
@instruction[uint] <-- the instruction at index ``uint``
39623962
@{function}.{instruction} <-- the indicated instruction in the indicated function
39633963
Example: @function[baz].instruction[19]
@@ -3969,6 +3969,11 @@ The following types of test arguments are supported:
39693969
Example: @block[19].instruction[2].operand[3]
39703970
Example: @function[2].instruction.operand
39713971

3972+
* The first instruction that isn't deleted when processing functions for tests.
3973+
The following instructions currently are deleted:
3974+
test_specification
3975+
debug_value [trace]
3976+
39723977

39733978
Profiling
39743979
~~~~~~~~~

include/swift/SILOptimizer/Utils/ParseTestSpecification.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,17 +163,32 @@ struct Arguments {
163163
}
164164
};
165165

166+
/// The specification for a test which has not yet been parsed.
167+
struct UnparsedSpecification {
168+
/// The string which specifies the test.
169+
///
170+
/// Not a StringRef because the TestSpecificationInst whose payload is of
171+
/// interest gets deleted.
172+
std::string string;
173+
/// The next non-debug instruction.
174+
///
175+
/// Provides an "anchor" for the specification. Contextual arguments
176+
/// (@instruction, @block, @function) can be parsed in terms of this anchor.
177+
SILInstruction *context;
178+
};
179+
166180
/// Finds and deletes each test_specification instruction in \p function and
167181
/// appends its string payload to the provided vector.
168-
void getTestSpecifications(SILFunction *function,
169-
SmallVectorImpl<std::string> &specifications);
182+
void getTestSpecifications(
183+
SILFunction *function,
184+
SmallVectorImpl<UnparsedSpecification> &specifications);
170185

171186
/// Given the string \p specification operand of a test_specification
172187
/// instruction from \p function, parse the arguments which it refers to into
173188
/// \p arguments and the component strings into \p argumentStrings.
174189
void parseTestArgumentsFromSpecification(
175-
SILFunction *function, StringRef specification, Arguments &arguments,
176-
SmallVectorImpl<StringRef> &argumentStrings);
190+
SILFunction *function, UnparsedSpecification const &specification,
191+
Arguments &arguments, SmallVectorImpl<StringRef> &argumentStrings);
177192

178193
} // namespace test
179194
} // namespace swift

lib/SILOptimizer/UtilityPasses/ParseTestSpecification.cpp

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@ void findAndDeleteTraceValues(SILFunction *function,
3939
}
4040
}
4141

42+
bool isDeleteableTestInstruction(SILInstruction const *instruction) {
43+
if (auto *dvi = dyn_cast<DebugValueInst>(instruction))
44+
return dvi->hasTrace();
45+
if (isa<TestSpecificationInst>(instruction))
46+
return true;
47+
return false;
48+
}
49+
50+
SILInstruction *findAnchorInstructionAfter(TestSpecificationInst *tsi) {
51+
for (auto *instruction = tsi->getNextInstruction(); instruction;
52+
instruction = instruction->getNextInstruction()) {
53+
if (!isDeleteableTestInstruction(instruction))
54+
return instruction;
55+
}
56+
// This can't happen because a TestSpecificationInst isn't a terminator itself
57+
// nor are any deleteable instructions.
58+
llvm_unreachable("found no anchor after TestSpecificationInst!?");
59+
}
60+
4261
// Helpers: Looking up subobjects by index
4362

4463
SILInstruction *getInstruction(SILFunction *function, unsigned long index) {
@@ -89,14 +108,14 @@ class ParseTestSpecification;
89108
class ParseArgumentSpecification {
90109
ParseTestSpecification &outer;
91110
StringRef specification;
111+
SILInstruction *context;
92112

93-
SILFunction *getFunction();
94113
SILValue getTraceValue(unsigned index, SILFunction *function);
95114

96115
public:
97116
ParseArgumentSpecification(ParseTestSpecification &outer,
98-
StringRef specification)
99-
: outer(outer), specification(specification) {}
117+
StringRef specification, SILInstruction *context)
118+
: outer(outer), specification(specification), context(context) {}
100119

101120
Argument parse() {
102121
auto argument = parseArgument();
@@ -183,6 +202,10 @@ class ParseArgumentSpecification {
183202
SILValue parseTraceComponent(SILFunction *within) {
184203
if (!consumePrefix("trace"))
185204
return SILValue();
205+
// TODO: Use a bare @trace (i.e. within == nullptr) to refer to the value
206+
// which appears in the debug_value [trace] instruction.
207+
if (!within)
208+
within = context->getFunction();
186209
if (empty() || peekPrefix("."))
187210
return getTraceValue(0, within);
188211
if (auto subscript = parseSubscript()) {
@@ -228,11 +251,13 @@ class ParseArgumentSpecification {
228251
return OperandArgument{operand};
229252
}
230253

231-
SILInstruction *parseInstructionComponent(
232-
TaggedUnion<SILFunction *, SILBasicBlock *> within) {
254+
using InstructionContext = TaggedUnion<SILFunction *, SILBasicBlock *>;
255+
256+
SILInstruction *
257+
parseInstructionComponent(Optional<InstructionContext> within) {
233258
if (!consumePrefix("instruction"))
234259
return nullptr;
235-
auto getInstructionAtIndex = [within](unsigned index) {
260+
auto getInstructionAtIndex = [](unsigned index, InstructionContext within) {
236261
if (within.isa<SILFunction *>()) {
237262
auto *function = within.get<SILFunction *>();
238263
return getInstruction(function, index);
@@ -241,17 +266,23 @@ class ParseArgumentSpecification {
241266
return getInstruction(block, index);
242267
};
243268
if (empty() || peekPrefix(".")) {
244-
return getInstructionAtIndex(0);
269+
if (!within) {
270+
// If this is a bare @instruction reference, it refers to to the
271+
// context of the test_specification.
272+
return context;
273+
}
274+
return getInstructionAtIndex(0, *within);
245275
}
246276
if (auto subscript = parseSubscript()) {
247277
auto index = subscript->get<unsigned long long>();
248-
return getInstructionAtIndex(index);
278+
return getInstructionAtIndex(index,
279+
within.getValueOr(context->getFunction()));
249280
}
250281
llvm_unreachable("bad suffix after 'instruction'!?");
251282
}
252283

253-
Optional<Argument> parseInstructionReference(
254-
TaggedUnion<SILFunction *, SILBasicBlock *> within) {
284+
Optional<Argument>
285+
parseInstructionReference(Optional<InstructionContext> within) {
255286
auto *instruction = parseInstructionComponent(within);
256287
if (!instruction)
257288
return llvm::None;
@@ -266,10 +297,18 @@ class ParseArgumentSpecification {
266297
if (!consumePrefix("block"))
267298
return nullptr;
268299
if (empty() || peekPrefix(".")) {
300+
// If this is a bare @block reference, it refers to the block containing
301+
// the test_specification instruction.
302+
if (!within) {
303+
return context->getParent();
304+
}
269305
auto *block = getBlock(within, 0);
270306
return block;
271307
}
272308
if (auto subscript = parseSubscript()) {
309+
if (!within) {
310+
within = context->getFunction();
311+
}
273312
auto index = subscript->get<unsigned long long>();
274313
auto *block = getBlock(within, index);
275314
return block;
@@ -283,7 +322,7 @@ class ParseArgumentSpecification {
283322
return llvm::None;
284323
if (!consumePrefix("."))
285324
return BlockArgument{block};
286-
if (auto arg = parseInstructionReference(block))
325+
if (auto arg = parseInstructionReference({block}))
287326
return *arg;
288327
llvm_unreachable("unhandled suffix after 'block'!?");
289328
}
@@ -292,16 +331,24 @@ class ParseArgumentSpecification {
292331
if (!consumePrefix("function"))
293332
return nullptr;
294333
if (empty() || peekPrefix(".")) {
295-
return getFunction();
334+
// If this is a bare @function reference, it refers to the function
335+
// containing the test_specification instruction.
336+
if (!within) {
337+
return context->getFunction();
338+
}
339+
return ::getFunction(within, 0);
296340
}
297341
if (auto subscript = parseSubscript()) {
342+
if (!within) {
343+
within = &context->getFunction()->getModule();
344+
}
298345
if (subscript->isa<unsigned long long>()) {
299346
auto index = subscript->get<unsigned long long>();
300347
auto *fn = ::getFunction(within, index);
301348
return fn;
302349
} else {
303350
auto name = subscript->get<StringRef>();
304-
auto *specified = getFunction()->getModule().lookUpFunction(name);
351+
auto *specified = within->lookUpFunction(name);
305352
if (!specified)
306353
llvm_unreachable("unknown function name!?");
307354
return specified;
@@ -316,7 +363,7 @@ class ParseArgumentSpecification {
316363
return llvm::None;
317364
if (!consumePrefix("."))
318365
return FunctionArgument{function};
319-
if (auto arg = parseInstructionReference(function))
366+
if (auto arg = parseInstructionReference({function}))
320367
return *arg;
321368
if (auto arg = parseTraceReference(function))
322369
return *arg;
@@ -328,15 +375,16 @@ class ParseArgumentSpecification {
328375
Optional<Argument> parseReference() {
329376
if (!consumePrefix("@"))
330377
return llvm::None;
331-
if (auto arg = parseTraceReference(getFunction()))
378+
if (auto arg = parseTraceReference(nullptr))
332379
return *arg;
333-
if (auto arg = parseOperandReference(getInstruction(getFunction(), 0)))
380+
if (auto arg =
381+
parseOperandReference(getInstruction(context->getFunction(), 0)))
334382
return *arg;
335-
if (auto arg = parseInstructionReference(getFunction()))
383+
if (auto arg = parseInstructionReference(llvm::None))
336384
return *arg;
337-
if (auto arg = parseBlockReference(getFunction()))
385+
if (auto arg = parseBlockReference(nullptr))
338386
return *arg;
339-
if (auto arg = parseFunctionReference(&getFunction()->getModule()))
387+
if (auto arg = parseFunctionReference(nullptr))
340388
return *arg;
341389
return llvm::None;
342390
}
@@ -384,21 +432,20 @@ class ParseTestSpecification {
384432
SmallVectorImpl<StringRef> &components)
385433
: function(function), components(components) {}
386434

387-
void parse(StringRef specification, Arguments &arguments) {
388-
specification.split(components, " ");
435+
void parse(UnparsedSpecification const &specification, Arguments &arguments) {
436+
StringRef specificationString = specification.string;
437+
specificationString.split(components, " ");
389438
for (unsigned long index = 0, size = components.size(); index < size;
390439
++index) {
391-
auto component = components[index];
392-
ParseArgumentSpecification parser(*this, component);
440+
auto componentString = components[index];
441+
ParseArgumentSpecification parser(*this, componentString,
442+
specification.context);
393443
auto argument = parser.parse();
394444
arguments.storage.push_back(argument);
395445
}
396446
}
397447
};
398448

399-
SILFunction *ParseArgumentSpecification::getFunction() {
400-
return outer.function;
401-
}
402449
SILValue ParseArgumentSpecification::getTraceValue(unsigned index,
403450
SILFunction *function) {
404451
return outer.getTraceValue(index, function);
@@ -409,22 +456,24 @@ SILValue ParseArgumentSpecification::getTraceValue(unsigned index,
409456
// API
410457

411458
void swift::test::getTestSpecifications(
412-
SILFunction *function, SmallVectorImpl<std::string> &specifications) {
459+
SILFunction *function,
460+
SmallVectorImpl<UnparsedSpecification> &specifications) {
413461
InstructionDeleter deleter;
414462
for (auto &block : *function) {
415463
for (SILInstruction *inst : deleter.updatingRange(&block)) {
416464
if (auto *tsi = dyn_cast<TestSpecificationInst>(inst)) {
417465
auto ref = tsi->getArgumentsSpecification();
418-
specifications.push_back(std::string(ref.begin(), ref.end()));
466+
auto *anchor = findAnchorInstructionAfter(tsi);
467+
specifications.push_back({std::string(ref.begin(), ref.end()), anchor});
419468
deleter.forceDelete(tsi);
420469
}
421470
}
422471
}
423472
}
424473

425474
void swift::test::parseTestArgumentsFromSpecification(
426-
SILFunction *function, StringRef specification, Arguments &arguments,
427-
SmallVectorImpl<StringRef> &argumentStrings) {
475+
SILFunction *function, UnparsedSpecification const &specification,
476+
Arguments &arguments, SmallVectorImpl<StringRef> &argumentStrings) {
428477
ParseTestSpecification parser(function, argumentStrings);
429478
parser.parse(specification, arguments);
430479
}

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ class UnitTestRunner : public SILFunctionTransform {
325325
}
326326

327327
void run() override {
328-
llvm::SmallVector<std::string, 2> testSpecifications;
328+
llvm::SmallVector<UnparsedSpecification, 2> testSpecifications;
329329
getTestSpecifications(getFunction(), testSpecifications);
330330
Arguments arguments;
331331
SmallVector<StringRef, 4> components;

test/SILOptimizer/unit_test.sil

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,32 @@ bb1:
8989
return %zero : $()
9090
}
9191

92+
// CHECK-LABEL: begin running test 1 of {{[^,]+}} on test_contextual_arg_parsing: test-specification-parsing
93+
// CHECK: block:
94+
// CHECK: bb1
95+
// CHECK-LABEL: end running test 1 of {{[^,]+}} on test_contextual_arg_parsing: test-specification-parsing
96+
// CHECK-LABEL: begin running test 2 of {{[^,]+}} on test_contextual_arg_parsing: test-specification-parsing
97+
// CHECK: instruction:
98+
// CHECK: tuple
99+
// CHECK-LABEL: end running test 2 of {{[^,]+}} on test_contextual_arg_parsing: test-specification-parsing
100+
// CHECK-LABEL: begin running test 3 of {{[^,]+}} on test_contextual_arg_parsing: test-specification-parsing
101+
// CHECK: function: test_contextual_arg_parsing
102+
// CHECK-LABEL: end running test 3 of {{[^,]+}} on test_contextual_arg_parsing: test-specification-parsing
103+
sil [ossa] @test_contextual_arg_parsing : $() -> () {
104+
entry:
105+
br one
106+
one:
107+
test_specification "test-specification-parsing B @block"
108+
br two
109+
two:
110+
test_specification "test-specification-parsing I @instruction"
111+
%retval = tuple ()
112+
br exit
113+
exit:
114+
test_specification "test-specification-parsing F @function"
115+
return %retval : $()
116+
}
117+
92118
class C {}
93119

94120
sil [ossa] @getC : $@convention(thin) () -> @owned C

0 commit comments

Comments
 (0)