Skip to content

Commit 7d8eb2f

Browse files
Merge pull request #61551 from nate-chandler/siloptimizer-unittests
[SILOptimizer] Add faux unit testing mechanism.
2 parents 45b8c1e + faa75bd commit 7d8eb2f

26 files changed

+1223
-3
lines changed

docs/SIL.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3924,6 +3924,52 @@ SIL DIExpression can have elements with various types, like AST nodes or strings
39243924

39253925
The ``[trace]`` flag is available for compiler unit testing. It is not produced during normal compilation. It is used combination with internal logging and optimization controls to select specific values to trace or to transform. For example, liveness analysis combines all "traced" values into a single live range with multiple definitions. This exposes corner cases that cannot be represented by passing valid SIL through the pipeline.
39263926

3927+
Testing
3928+
~~~~~~~
3929+
3930+
test_specification
3931+
``````````````````
3932+
::
3933+
3934+
sil-instruction ::= 'test_specification' string-literal
3935+
3936+
test_specification "parsing @trace[3] @function[other].block[2].instruction[1]"
3937+
3938+
Exists only for writing FileCheck tests. Specifies a list of test arguments
3939+
which should be used in order to run a particular test "in the context" of the
3940+
function containing the instruction.
3941+
3942+
Parsing of these test arguments is done via ``parseTestArgumentsFromSpecification``.
3943+
3944+
The following types of test arguments are supported:
3945+
3946+
- boolean: true false
3947+
- unsigned integer: 0...ULONG_MAX
3948+
- string
3949+
- function: @function <-- the current function
3950+
@function[uint] <-- function at index ``uint``
3951+
@function[name] <-- function named ``name``
3952+
- block: @block <-- the first block
3953+
@block[uint] <-- the block at index ``uint``
3954+
@{function}.{block} <-- the indicated block in the indicated function
3955+
Example: @function[foo].block[2]
3956+
- trace: @trace <-- the first ``debug_value [trace]`` in the current function
3957+
@trace[uint] <-- the ``debug_value [trace]`` at index ``uint``
3958+
@{function}.{trace} <-- the indicated trace in the indicated function
3959+
Example: @function[bar].trace
3960+
- instruction: @instruction <-- the first instruction
3961+
@instruction[uint] <-- the instruction at index ``uint``
3962+
@{function}.{instruction} <-- the indicated instruction in the indicated function
3963+
Example: @function[baz].instruction[19]
3964+
@{block}.{instruction} <-- the indicated instruction in the indicated block
3965+
Example: @function[bam].block.instruction
3966+
- operand: @operand <-- the first operand
3967+
@operand[uint] <-- the operand at index ``uint``
3968+
@{instruction}.{operand} <-- the indicated operand of the indicated instruction
3969+
Example: @block[19].instruction[2].operand[3]
3970+
Example: @function[2].instruction.operand
3971+
3972+
39273973
Profiling
39283974
~~~~~~~~~
39293975

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,8 @@ ERROR(expected_sil_profiler_counter_total,none,
539539
"expected profiler counter total", ())
540540
ERROR(expected_sil_profiler_counter_hash,none,
541541
"expected profiler counter hash", ())
542+
ERROR(expected_sil_test_specification_body,none,
543+
"expected a string consisting of the space-separated test arguments", ())
542544

543545
// SIL Values
544546
ERROR(sil_value_redefinition,none,

include/swift/SIL/SILBuilder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,13 @@ class SILBuilder {
988988
return createDebugValue(Loc, src, Var);
989989
}
990990

991+
TestSpecificationInst *
992+
createTestSpecificationInst(SILLocation Loc,
993+
StringRef ArgumentsSpecification) {
994+
return insert(TestSpecificationInst::create(
995+
getSILDebugLocation(Loc), ArgumentsSpecification, getModule()));
996+
}
997+
991998
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
992999
Load##Name##Inst *createLoad##Name(SILLocation Loc, \
9931000
SILValue src, \

include/swift/SIL/SILCloner.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,6 +2631,15 @@ void SILCloner<ImplClass>::visitIncrementProfilerCounterInst(
26312631
Inst->getNumCounters(), Inst->getPGOFuncHash()));
26322632
}
26332633

2634+
template <typename ImplClass>
2635+
void SILCloner<ImplClass>::visitTestSpecificationInst(
2636+
TestSpecificationInst *Inst) {
2637+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
2638+
recordClonedInstruction(Inst, getBuilder().createTestSpecificationInst(
2639+
getOpLocation(Inst->getLoc()),
2640+
Inst->getArgumentsSpecification()));
2641+
}
2642+
26342643
template<typename ImplClass>
26352644
void
26362645
SILCloner<ImplClass>::visitIndexAddrInst(IndexAddrInst *Inst) {

include/swift/SIL/SILInstruction.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4933,6 +4933,32 @@ class DebugValueInst final
49334933
}
49344934
};
49354935

4936+
class TestSpecificationInst final
4937+
: public InstructionBase<SILInstructionKind::TestSpecificationInst,
4938+
NonValueInstruction>,
4939+
private llvm::TrailingObjects<TestSpecificationInst, char> {
4940+
friend TrailingObjects;
4941+
friend SILBuilder;
4942+
4943+
unsigned ArgumentsSpecificationLength;
4944+
4945+
TestSpecificationInst(SILDebugLocation Loc,
4946+
unsigned ArgumentsSpecificationLength)
4947+
: InstructionBase(Loc),
4948+
ArgumentsSpecificationLength(ArgumentsSpecificationLength) {}
4949+
4950+
static TestSpecificationInst *
4951+
create(SILDebugLocation Loc, StringRef argumentsSpecification, SILModule &M);
4952+
4953+
public:
4954+
StringRef getArgumentsSpecification() const {
4955+
return StringRef(getTrailingObjects<char>(), ArgumentsSpecificationLength);
4956+
}
4957+
4958+
ArrayRef<Operand> getAllOperands() const { return {}; }
4959+
MutableArrayRef<Operand> getAllOperands() { return {}; }
4960+
};
4961+
49364962
/// An abstract class representing a load from some kind of reference storage.
49374963
template <SILInstructionKind K>
49384964
class LoadReferenceInstBase

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,8 @@ NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape,
765765
SILInstruction, None, DoesNotRelease)
766766
NON_VALUE_INST(DebugValueInst, debug_value,
767767
SILInstruction, None, DoesNotRelease)
768+
NON_VALUE_INST(TestSpecificationInst, test_specification,
769+
SILInstruction, None, DoesNotRelease)
768770
#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \
769771
NON_VALUE_INST(Store##Name##Inst, store_##name, \
770772
SILInstruction, MayWrite, DoesNotRelease)

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,8 @@ PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-block
445445
"Utility pass. Removes all non-term insts from blocks with unreachable terms")
446446
PASS(SerializeSILPass, "serialize-sil",
447447
"Utility pass. Serializes the current SILModule")
448+
PASS(UnitTestRunner, "unit-test-runner",
449+
"Utility pass. Parses arguments and runs code with them.")
448450
PASS(YieldOnceCheck, "yield-once-check",
449451
"Check correct usage of yields in yield-once coroutines")
450452
PASS(OSLogOptimization, "os-log-optimization", "Optimize os log calls")
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
//===- ParseTestSpecification.h - Parsing for test instructions -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines Test::Argument.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_SIL_PARSETESTSPECIFICATION
18+
#define SWIFT_SIL_PARSETESTSPECIFICATION
19+
20+
#include "swift/Basic/TaggedUnion.h"
21+
#include "swift/SIL/SILValue.h"
22+
#include "llvm/ADT/StringRef.h"
23+
24+
namespace llvm {
25+
template <class T>
26+
class SmallVectorImpl;
27+
}
28+
using llvm::StringRef;
29+
30+
namespace swift {
31+
32+
class SILFunction;
33+
34+
namespace test {
35+
36+
struct Argument {
37+
enum class Kind {
38+
String,
39+
Bool,
40+
UInt,
41+
Value,
42+
Operand,
43+
Instruction,
44+
Block,
45+
Function,
46+
};
47+
using Union = TaggedUnion<StringRef, // StringArgument
48+
bool, // BoolArgument
49+
unsigned long long, // UIntArgument
50+
SILValue, // ValueArgument
51+
Operand *, // OperandArgument
52+
SILInstruction *, // InstructionArgument
53+
SILBasicBlock *, // BlockArgument
54+
SILFunction * // FunctionArgument
55+
>;
56+
57+
private:
58+
Kind kind;
59+
60+
protected:
61+
Argument(Union storage, Kind kind) : kind(kind), storage(storage) {}
62+
Union storage;
63+
64+
public:
65+
Kind getKind() const { return kind; }
66+
};
67+
68+
template <typename Stored, Argument::Kind TheKind>
69+
struct ConcreteArgument : Argument {
70+
using Super = ConcreteArgument<Stored, TheKind>;
71+
ConcreteArgument(Stored stored) : Argument(Union{stored}, TheKind) {}
72+
Stored getValue() { return storage.get<Stored>(); }
73+
static bool classof(const Argument *argument) {
74+
return argument->getKind() == TheKind;
75+
}
76+
};
77+
78+
struct ValueArgument : ConcreteArgument<SILValue, Argument::Kind::Value> {
79+
ValueArgument(SILValue stored) : Super(stored) {}
80+
};
81+
82+
struct OperandArgument : ConcreteArgument<Operand *, Argument::Kind::Operand> {
83+
OperandArgument(Operand *stored) : Super(stored) {}
84+
};
85+
86+
struct InstructionArgument
87+
: ConcreteArgument<SILInstruction *, Argument::Kind::Instruction> {
88+
InstructionArgument(SILInstruction *stored) : Super(stored) {}
89+
};
90+
91+
struct BlockArgument
92+
: ConcreteArgument<SILBasicBlock *, Argument::Kind::Block> {
93+
BlockArgument(SILBasicBlock *stored) : Super(stored) {}
94+
};
95+
96+
struct FunctionArgument
97+
: ConcreteArgument<SILFunction *, Argument::Kind::Function> {
98+
FunctionArgument(SILFunction *stored) : Super(stored) {}
99+
};
100+
101+
struct BoolArgument : ConcreteArgument<bool, Argument::Kind::Bool> {
102+
BoolArgument(bool stored) : Super(stored) {}
103+
};
104+
105+
struct UIntArgument
106+
: ConcreteArgument<unsigned long long, Argument::Kind::UInt> {
107+
UIntArgument(unsigned long long stored) : Super(stored) {}
108+
};
109+
110+
struct StringArgument : ConcreteArgument<StringRef, Argument::Kind::String> {
111+
StringArgument(StringRef stored) : Super(stored) {}
112+
};
113+
114+
struct Arguments {
115+
llvm::SmallVector<Argument, 8> storage;
116+
unsigned untakenIndex = 0;
117+
118+
void assertUsed() {
119+
assert(untakenIndex == storage.size() && "arguments remain!");
120+
}
121+
122+
void clear() {
123+
assertUsed();
124+
storage.clear();
125+
untakenIndex = 0;
126+
}
127+
128+
Arguments() {}
129+
Arguments(Arguments const &) = delete;
130+
Arguments &operator=(Arguments const &) = delete;
131+
~Arguments() { assertUsed(); }
132+
Argument &takeArgument() { return storage[untakenIndex++]; }
133+
StringRef takeString() {
134+
return cast<StringArgument>(takeArgument()).getValue();
135+
}
136+
bool takeBool() { return cast<BoolArgument>(takeArgument()).getValue(); }
137+
unsigned long long takeUInt() {
138+
return cast<UIntArgument>(takeArgument()).getValue();
139+
}
140+
SILValue takeValue() {
141+
return cast<ValueArgument>(takeArgument()).getValue();
142+
}
143+
Operand *takeOperand() {
144+
return cast<OperandArgument>(takeArgument()).getValue();
145+
}
146+
SILInstruction *takeInstruction() {
147+
return cast<InstructionArgument>(takeArgument()).getValue();
148+
}
149+
SILBasicBlock *takeBlock() {
150+
return cast<BlockArgument>(takeArgument()).getValue();
151+
}
152+
SILFunction *takeFunction() {
153+
return cast<FunctionArgument>(takeArgument()).getValue();
154+
}
155+
};
156+
157+
/// Finds and deletes each test_specification instruction in \p function and
158+
/// appends its string payload to the provided vector.
159+
void getTestSpecifications(SILFunction *function,
160+
SmallVectorImpl<std::string> &specifications);
161+
162+
void parseTestArgumentsFromSpecification(
163+
SILFunction *, StringRef specification, Arguments &arguments,
164+
SmallVectorImpl<StringRef> &argumentStrings);
165+
166+
} // namespace test
167+
} // namespace swift
168+
169+
#endif

lib/IRGen/IRGenSIL.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,9 @@ class IRGenSILFunction :
13841384
void visitLinearFunctionExtractInst(LinearFunctionExtractInst *i);
13851385
void visitDifferentiabilityWitnessFunctionInst(
13861386
DifferentiabilityWitnessFunctionInst *i);
1387+
void visitTestSpecificationInst(TestSpecificationInst *i) {
1388+
llvm_unreachable("test-only instruction in Lowered SIL?!");
1389+
}
13871390

13881391
#define LOADABLE_REF_STORAGE_HELPER(Name) \
13891392
void visitRefTo##Name##Inst(RefTo##Name##Inst *i); \

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ SHOULD_NEVER_VISIT_INST(ReleaseValueAddr)
120120
SHOULD_NEVER_VISIT_INST(StrongRelease)
121121
SHOULD_NEVER_VISIT_INST(GetAsyncContinuation)
122122
SHOULD_NEVER_VISIT_INST(IncrementProfilerCounter)
123+
SHOULD_NEVER_VISIT_INST(TestSpecification)
123124

124125
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
125126
SHOULD_NEVER_VISIT_INST(StrongRetain##Name) \

0 commit comments

Comments
 (0)