xref: /wasmtime-44.0.1/cranelift/docs/testing.md (revision c17a3d89)
1# Testing Cranelift
2
3Cranelift is tested at multiple levels of abstraction and integration. When
4possible, Rust unit tests are used to verify single functions and types. When
5testing the interaction between compiler passes, file-level tests are
6appropriate.
7
8## Rust tests
9
10Rust and Cargo have good support for testing. Cranelift uses unit tests, doc
11tests, and integration tests where appropriate. The
12[Rust By Example page on Testing] is a great illustration on how to write
13each of these forms of test.
14
15[Rust By Example page on Testing]: https://doc.rust-lang.org/rust-by-example/testing.html
16
17## File tests
18
19Compilers work with large data structures representing programs, and it quickly
20gets unwieldy to generate test data programmatically. File-level tests make it
21easier to provide substantial input functions for the compiler tests.
22
23File tests are `*.clif` files in the `filetests/` directory
24hierarchy. Each file has a header describing what to test followed by a number
25of input functions in the :doc:`Cranelift textual intermediate representation
26<ir>`:
27
28.. productionlist::
29    test_file     : test_header `function_list`
30    test_header   : test_commands (`isa_specs` | `settings`)
31    test_commands : test_command { test_command }
32    test_command  : "test" test_name { option } "\n"
33
34The available test commands are described below.
35
36Many test commands only make sense in the context of a target instruction set
37architecture. These tests require one or more ISA specifications in the test
38header:
39
40.. productionlist::
41    isa_specs     : { [`settings`] isa_spec }
42    isa_spec      : "isa" isa_name { `option` } "\n"
43
44The options given on the `isa` line modify the ISA-specific settings defined in
45`cranelift-codegen/meta-python/isa/*/settings.py`.
46
47All types of tests allow shared Cranelift settings to be modified:
48
49.. productionlist::
50    settings      : { setting }
51    setting       : "set" { option } "\n"
52    option        : flag | setting "=" value
53
54The shared settings available for all target ISAs are defined in
55`cranelift-codegen/meta-python/base/settings.py`.
56
57The `set` lines apply settings cumulatively:
58
59```
60    test legalizer
61    set opt_level=best
62    set is_pic=1
63    target riscv64
64    set is_pic=0
65    target riscv32 supports_m=false
66
67    function %foo() {}
68```
69
70This example will run the legalizer test twice. Both runs will have
71`opt_level=best`, but they will have different `is_pic` settings. The 32-bit
72run will also have the RISC-V specific flag `supports_m` disabled.
73
74The filetests are run automatically as part of `cargo test`, and they can
75also be run manually with the `clif-util test` command.
76
77By default, the test runner will spawn a thread pool with as many threads as
78there are logical CPUs. You can explicitly control how many threads are spawned
79via the `CRANELIFT_FILETESTS_THREADS` environment variable. For example, to
80limit the test runner to a single thread, use:
81
82```
83$ CRANELIFT_FILETESTS_THREADS=1 clif-util test path/to/file.clif
84```
85
86### Filecheck
87
88Many of the test commands described below use *filecheck* to verify their
89output. Filecheck is a Rust implementation of the LLVM tool of the same name.
90See the `documentation <https://docs.rs/filecheck/>`_ for details of its syntax.
91
92Comments in `.clif` files are associated with the entity they follow.
93This typically means an instruction or the whole function. Those tests that
94use filecheck will extract comments associated with each function (or its
95entities) and scan them for filecheck directives. The test output for each
96function is then matched against the filecheck directives for that function.
97
98Comments appearing before the first function in a file apply to every function.
99This is useful for defining common regular expression variables with the
100`regex:` directive, for example.
101
102Note that LLVM's file tests don't separate filecheck directives by their
103associated function. It verifies the concatenated output against all filecheck
104directives in the test file. LLVM's :command:`FileCheck` command has a
105`CHECK-LABEL:` directive to help separate the output from different functions.
106Cranelift's tests don't need this.
107
108### `test cat`
109
110This is one of the simplest file tests, used for testing the conversion to and
111from textual IR. The `test cat` command simply parses each function and
112converts it back to text again. The text of each function is then matched
113against the associated filecheck directives.
114
115Example:
116
117```
118    function %r1() -> i32, f32 {
119    block1:
120        v10 = iconst.i32 3
121        v20 = f32const 0.0
122        return v10, v20
123    }
124    ; sameln: function %r1() -> i32, f32 {
125    ; nextln: block0:
126    ; nextln:     v10 = iconst.i32 3
127    ; nextln:     v20 = f32const 0.0
128    ; nextln:     return v10, v20
129    ; nextln: }
130```
131
132### `test verifier`
133
134Run each function through the IR verifier and check that it produces the
135expected error messages.
136
137Expected error messages are indicated with an `error:` directive *on the
138instruction that produces the verifier error*. Both the error message and
139reported location of the error is verified:
140
141```
142    test verifier
143
144    function %test(i32) {
145        block0(v0: i32):
146            jump block1       ; error: terminator
147            return
148    }
149```
150
151This example test passes if the verifier fails with an error message containing
152the sub-string `"terminator"` *and* the error is reported for the `jump`
153instruction.
154
155If a function contains no `error:` annotations, the test passes if the
156function verifies correctly.
157
158### `test print-cfg`
159
160Print the control flow graph of each function as a Graphviz graph, and run
161filecheck over the result. See also the :command:`clif-util print-cfg`
162command:
163
164```
165    ; For testing cfg generation. This code is nonsense.
166    test print-cfg
167    test verifier
168
169    function %nonsense(i32, i32) -> f32 {
170    ; check: digraph %nonsense {
171    ; regex: I=\binst\d+\b
172    ; check: label="{block0 | <$(BRIF=$I)>brif v1, block1(v2), block2 }"]
173
174    block0(v0: i32, v1: i32):
175        v2 = iconst.i32 0
176        brif v1, block1(v2), block2  ; unordered: block0:$BRIF -> block1
177                                     ; unordered: block0:$BRIF -> block2
178
179    block1(v5: i32):
180        return v0
181
182    block2:
183        v100 = f32const 0.0
184        return v100
185    }
186```
187
188### `test domtree`
189
190Compute the dominator tree of each function and validate it against the
191`dominates:` annotations::
192
193```
194    test domtree
195
196    function %test(i32) {
197        block0(v0: i32):
198            jump block1              ; dominates: block1
199        block1:
200            brif v0, block2, block3  ; dominates: block2, block3
201        block2:
202            jump block3
203        block3:
204            return
205    }
206```
207
208Every reachable basic block except for the entry block has an
209*immediate dominator* which is a jump or branch instruction. This test passes
210if the `dominates:` annotations on the immediate dominator instructions are
211both correct and complete.
212
213This test also sends the computed CFG post-order through filecheck.
214
215### `test legalizer`
216
217Legalize each function for the specified target ISA and run the resulting
218function through filecheck. This test command can be used to validate the
219encodings selected for legal instructions as well as the instruction
220transformations performed by the legalizer.
221
222### `test regalloc`
223
224Test the register allocator.
225
226First, each function is legalized for the specified target ISA. This is
227required for register allocation since the instruction encodings provide
228register class constraints to the register allocator.
229
230Second, the register allocator is run on the function, inserting spill code and
231assigning registers and stack slots to all values.
232
233The resulting function is then run through filecheck.
234
235
236### `test simple-gvn`
237
238Test the simple GVN pass.
239
240The simple GVN pass is run on each function, and then results are run
241through filecheck.
242
243### `test licm`
244
245Test the LICM pass.
246
247The LICM pass is run on each function, and then results are run
248through filecheck.
249
250### `test dce`
251
252Test the DCE pass.
253
254The DCE pass is run on each function, and then results are run
255through filecheck.
256
257### `test shrink`
258
259Test the instruction shrinking pass.
260
261The shrink pass is run on each function, and then results are run
262through filecheck.
263
264### `test simple_preopt`
265
266Test the preopt pass.
267
268The preopt pass is run on each function, and then results are run
269through filecheck.
270
271### `test compile`
272
273Test the whole code generation pipeline.
274
275Each function is passed through the full `Context::compile()` function
276which is normally used to compile code. This type of test often depends
277on assertions or verifier errors, but it is also possible to use
278filecheck directives which will be matched against the final form of the
279Cranelift IR right before binary machine code emission.
280
281### `test run`
282
283Compile and execute a function.
284
285This test command allows several directives:
286 - to print the result of running a function to stdout, add a `print`
287 directive and call the preceding function with arguments (see `%foo` in
288 the example below); remember to enable `--nocapture` if running these
289 tests through Cargo
290 - to check the result of a function, add a `run` directive and call the
291 preceding function with a comparison (`==` or `!=`) (see `%bar` below)
292 - for backwards compatibility, to check the result of a function with a
293 `() -> i*` signature, only the `run` directive is required, with no
294 invocation or comparison (see `%baz` below);  a non zero value is
295 interpreted as a successful test execution, whereas a zero value is
296 interpreted as a failed test.
297
298Currently a `target` is required but is only used to indicate whether the host
299platform can run the test and currently only the architecture is filtered. The
300host platform's native target will be used to actually compile the test.
301
302Example:
303
304```
305    test run
306    target x86_64
307
308    ; how to print the results of a function
309    function %foo() -> i32 {
310    block0:
311        v0 = iconst.i32 42
312        return v0
313    }
314    ; print: %foo()
315
316    ; how to check the results of a function
317    function %bar(i32) -> i32 {
318    block0(v0:i32):
319        v1 = iadd_imm v0, 1
320        return v1
321    }
322    ; run: %bar(1) == 2
323
324    ; legacy method of checking the results of a function
325    function %baz() -> i8 {
326    block0:
327        v0 = iconst.i8 1
328        return v0
329    }
330    ; run
331```
332