This is the first version of the testgen - a tool, currently implemented as an
llc MIR-pass, that generates regression lit-tests for GlobalISel's Instruction
Selector. The generation is done on rule-by-rule basis and currently covers
selection rules automatically imported by TableGen from SelectionDAGISel.
This is a prerequisite for the tests generated for
- AArch64: https://reviews.llvm.org/D43976
- ARM: https://reviews.llvm.org/D43979
- updated in https://reviews.llvm.org/D43982 (this also demonstrates how the tests could be updated when GlobalISel changes)
- X86: https://reviews.llvm.org/D43994 (this also demonstrates the usefulness of ABI speculation done by Testgen)
What this tool is and isn't:
- This is not a fuzzer for Instruction Selector, it isn't trying to come up with a malicious input for it and break it, nor it's trying to discover bugs in it.
- This is a regression testing tool, it's main goal is to capture the current state of the GlobalISel's InstructionSelect pass providing the best test coverage for it with small and highly targeted tests that pass, and catch any regressions later due to changes in:
- *.td-definitions of the instructions and selection patterns;
- GlobalISel's emitter (the TableGen backend), including the ones that intend to change rules' priorities and the ones that don't;
- manually written parts of the Instruction Selector.
- Potentially this is also an analysis tool that may make it easier to see and control the actual effects of changes like listed above on the Selector, detect dead rules, etc.
- It may be extended in the future to generate tests for other passes of the GlobalISel's pipeline, and / or have a fuzzer mode of operation, but currently these aren't the goals.
- It also may be turned into a benchmark-gen for Instruction Selector relatively easily. That way we could benchmark the Selector on large inputs created in-memory, avoiding huge (~90%) overhead of parsing MIR off disk, and having the input with any desirable probabilities of any pattern supported, as well as having only the patterns supported, avoiding any input that is not selectable, thus getting more stable, targeted, and precise performance data.
Potential user stories:
- New backend development.
- Porting an existing backend from SelectionDAG ISel to GlobalISel.
While the first one is promising, it appears that the second one is more
prominent right now and therefore the main target of this tool.
Design goals:
- As we mostly care about providing regression testing of InstructionSelect pass of GlobalISel's implementations early in the development for pre-existing targets, we can not rely on any other GlobalISel passes being well-developed and fully functional, in particular, we expect InstructionSelect pass to be well ahead everything else due to the semi-automatic porting mechanism.
See https://reviews.llvm.org/rL326396 as an example of breaking ties with the Legalizer, selectUnconstrainedRegBanks of this patch as an example of the same w.r.t. RegBankSelect.
- We want the testgen to be relatively robust and able to handle gracefully non-functional changes, for instance, changes in the typical order of the MatchTable opcodes for rules, or even presense of specific opcodes, like the number of operands check, or changes in concrete serialization format for MIR.
- We want the testgen to be as target-independent and generic as possible and impose as less maintainance burden on backend writers as possible.
- If it's not jeopardizing other goals and not too difficult to do, we want testgen to generate naturally-looking tests that are likely to come out the same if written by a human.
Design decisions made:
- Current implementation of testgen uses TableGen'erated MatchTable's to generate the tests. We could've branched off input data-wise earlier, but that would mean re-implementing too much of the GlobalISel's emitter.
- We're using only matching parts of the MatchTable to generate MIR and relying on the selector itself to generate FileCheck's for the expected output for a few major reasons:
- it simplifies the implementation;
- it reduces the number of tests failing as of time of their generation due to the MIR being selected not by a rule intended, which is desirable as we aren't fuzzing the selector, but trying to generate passing tests;
Usage:
llc -mtriple aarch64-- -run-pass instruction-select-testgen -simplify-mir input.mir -o output.mir
will add a number of Machine Function's, one per every imported *.td-defined
selection rule, into intput.mir and write the result as output.mir.
Command line options:
- -testgen-from-rule=N -testgen-until-rule=M - generate tests for a subrange of rules only;
- -testgen-exclude-rules=N{,N} - skip specific rules;
- -testgen-include-only=N{,N} - generate tests for explicitly listed rules only;
- -testgen-set-all-features - speculatively satisfy all target / module / and function features requirements to cover feature-specific rules;
- -testgen-no-abi - don't speculate on ABI boundaries tring to make the test look natural and test COPY's selection, but just IMPLICIT_DEF undefined vregs instead.
Note:
-testgen-no-abi=false tried to emit real RET opcodes at some point by using
CallLowering::lowerReturn and deriving IR Types from LLTs, but it proved
to be unreliable for most targets and created an extra dependency.
This patch also provides utils/update_instruction_select_testgen_tests.sh tool
that would generate a couple of lit-tests:
usage: ./utils/update_instruction_select_testgen_tests.sh <testgen'd file> <llc binary> <target triple> [extra llc args]
for instance, executing
../../utils/update_instruction_select_testgen_tests.sh ../../test/CodeGen/AArch64/GlobalISel/arm64-instruction-select-testgen-testgend.mir ./bin/llc aarch64--
from a build/obj directory would create 2 files:
../../test/CodeGen/AArch64/GlobalISel/arm64-instruction-select-testgen-testgend.mir
and
../../test/CodeGen/AArch64/GlobalISel/arm64-instruction-select-testgen-selected.mir
testing that the testgen outputs the same MIR and the selector selects that MIR
the same way respectively.
Coverage:
Target | Rules | Fail to | Tests | Selected by the | Imported | Select | Generated | Rule Intended --------+----------+---------+-----------+---------------- AArch64 | 1654 | 0.0% | 1449 | 85% ARM | 1055 | 0.2% | 991 | 78% x86 | 887 | 13.8% | 765 | 68%
"Fail to Select" stands for "a generated test crashed / asserted the selector",
this is something to -testgen-exclude-rules in practice. The major reason
for this right now is a limited support of COPY_TO_REGCLASS in *.td-defined
patterns by the GlobalISel importing mechanism.
"Selected by the Rule Intended" basically means the target coverage provided by
the tool. A test could be selected by a rule different from the rule that was
used to generate it for a variety of reasons, approximately in order from most
prominent ones to the rarest ones:
- The test generated isn't specific enough due to:
- lack of support of complex patterns by the testgen;
- too basic support of immediate predicates by the testgen;
- rules genuinely intersecting with each other and local approach of the testgen not considering rules partially hiding each other.
- A rule is genuinely dead and
- it was rendered dead by GlobalISel;
- it was dead in SelectionDAG ISel to beging with;
- it is rendered dead by manually written parts of the selector executing before trying TableGen'erated selectImpl.
Known deficiencies:
- Testgen could not be currently easily extended by a target to support complex patterns, which should greatly improve coverage.
- Testgen's way of dealing with features is very sketchy at the moment and needs to improved.
- Testgen should probably be a separate from llc binary tool
approximately from the most important to fix soon to the least important.
A couple of dependencies for this patch as well as the tests generated
are coming soon in separate patches.
See also test/CodeGen/AArch64/GlobalISel/select-with-no-legality-check.mir
currently committed for an example output of the testgen.
Is this comment accurate? GIR_AddRegister is listed as having 2 operands and I only see 2 in my build