From 090d19bd24883efdd7515e2cd66bec835ccbba27 Mon Sep 17 00:00:00 2001 From: Anniesld Date: Wed, 1 Jul 2026 19:59:23 -0500 Subject: [PATCH 1/7] changes --- cranelift/docs/add-a-clif-instruction.md | 122 +++++++++++++++++++++++ cranelift/docs/compare-llvm.md | 14 +-- cranelift/docs/isle-integration.md | 4 +- cranelift/docs/testing.md | 99 +++++------------- 4 files changed, 157 insertions(+), 82 deletions(-) create mode 100644 cranelift/docs/add-a-clif-instruction.md diff --git a/cranelift/docs/add-a-clif-instruction.md b/cranelift/docs/add-a-clif-instruction.md new file mode 100644 index 000000000000..3ad41e84ae15 --- /dev/null +++ b/cranelift/docs/add-a-clif-instruction.md @@ -0,0 +1,122 @@ +# How to Add a New Instruction to CLIF + +This is a walkthrough of what's involved in adding a brand new opcode to +Cranelift's target-independent IR. It doesn't cover adding a new instruction +to a specific machine backend (the ISA-specific `MachInst`s); see +[Adding a New Instruction to a Machine Backend](add-a-backend-instruction.md) +for that. + +Before adding a new instruction, it's worth checking whether the semantics you +want can already be expressed as a combination of existing instructions. CLIF +tries to keep its instruction set fairly small; new opcodes are for cases +where composing existing ones would be either impossible or noticeably worse +for codegen (e.g. because the mid-end or backends couldn't optimize the +combination as well as a dedicated instruction). + +## 1. Declare the instruction + +Instructions are declared in the meta-language, not directly in Rust. The +shared (ISA-independent) instructions live in +`cranelift/codegen/meta/src/shared/instructions.rs`. A declaration looks like +this (this is the real definition of `iadd`): + +```rust +ig.push( + Inst::new( + "iadd", + r#" + Wrapping integer addition: `a := x + y \pmod{2^B}`. + + This instruction does not depend on the signed/unsigned interpretation + of the operands. + "#, + &formats.binary, + ) + .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)]) + .operands_out(vec![Operand::new("a", Int)]) + .inst_builder_imm_method(true), +); +``` + +The third argument to `Inst::new` is an *instruction format*, chosen from +`cranelift/codegen/meta/src/shared/formats.rs` (`unary`, `binary`, `ternary`, +`call`, `load`, `store`, `atomic_rmw`, and so on). The format determines what +shape of operands the generated `InstructionData` variant has; reuse an +existing format if your instruction fits one, rather than adding a new one. + +If your instruction has effects beyond producing a result — it can trap, +branch, read or write memory, or otherwise isn't safe to reorder or delete — +say so on the `Inst` builder with methods like `.can_trap()`, `.can_load()`, +`.can_store()`, `.other_side_effects()`, `.branches()`, or `.call()` (see +`cranelift/codegen/meta/src/cdsl/instructions.rs` for the full list). This +isn't just documentation: the mid-end and alias analysis read these flags to +decide what's safe to reorder, hoist, or eliminate. + +## 2. Let the build regenerate the Rust and ISLE glue + +`cranelift/codegen/build.rs` runs the meta crate as part of the normal +`cargo build`. From your instruction declaration it generates (into +`OUT_DIR`, under `target/`): + +- A variant of the `Opcode` enum and of `InstructionData`. +- Support code for the format you used (encoding, decoding, `Display`). +- A method on the `InstBuilder` trait, so frontends can write + `builder.ins().your_new_instr(...)`. +- `extern` declarations for the instruction in the auto-generated + `clif_lower.isle` and `clif_opt.isle` files, so it's immediately usable + as an ISLE pattern from lowering and mid-end rules. (See + [How ISLE is Integrated with Cranelift](isle-integration.md) for more on + those generated files.) + +You don't write any of this by hand — just run `cargo check` (or `build`) and +the compiler will tell you where you still need to plug the new opcode in. + +## 3. Fill in the remaining match arms + +Because `Opcode` gained a new variant, several exhaustive `match`es elsewhere +in the codebase will stop compiling until you handle it. In practice this +means the compiler walks you through most of the checklist, but the usual +places are: + +- **The interpreter**, `cranelift/interpreter/src/step.rs`, which matches + over `inst.opcode()` to give every instruction an execution semantics. + Without this, `test interpret` and `test run` can't exercise the new + instruction. +- **The verifier**, `cranelift/codegen/src/verifier/mod.rs`, if the new + instruction has type or operand constraints beyond what the format and + type variables already check. +- **Alias analysis and the egraph mid-end** + (`cranelift/codegen/src/alias_analysis.rs`, + `cranelift/codegen/src/egraph/`) if the instruction touches memory or + needs special handling to be optimized correctly — this is really an + extension of the side-effect flags from step 1. + +Simple, pure instructions (no side effects, standard type variables) often +need nothing beyond steps 1 and 2 plus a lowering rule in each backend that +should support them. + +## 4. Add a lowering rule per backend + +A CLIF instruction with no backend that can lower it will hit ISLE's "no rule +matched" panic the moment it's used. At minimum, add a `(rule (lower +(your_instr ...)) ...)` to each `cranelift/codegen/src/isa//lower.isle` +you care about (or an explicit "unsupported" path if it's intentionally not +supported everywhere yet). See +[Adding a New Instruction to a Machine Backend](add-a-backend-instruction.md) +for what that involves in more detail. + +## 5. Test it + +Add file tests under `cranelift/filetests/filetests/`. `test interpret` and +`test run` exercise the semantics you added in step 3, `test optimize` +exercises any mid-end rewrites, and `test compile` (per architecture, under +`filetests/isa//`) exercises the lowering from step 4. See +[Testing Cranelift](testing.md) for how these test commands work. + +## Worked example + +[#12101](https://github.com/bytecodealliance/wasmtime/pull/12101), which +added the `patchable_call` instruction, is a reasonably self-contained recent +example that touches most of the above: the instruction declaration in +`instructions.rs`, and lowering rules in both the x64 and aarch64 +`lower.isle` files. diff --git a/cranelift/docs/compare-llvm.md b/cranelift/docs/compare-llvm.md index d1fe5272e410..77c293671518 100644 --- a/cranelift/docs/compare-llvm.md +++ b/cranelift/docs/compare-llvm.md @@ -94,12 +94,14 @@ lower level of abstraction, to allow it to be used all the way through the codegen process. This design tradeoff does mean that Cranelift IR is less friendly for mid-level -optimizations. Cranelift doesn't currently perform mid-level optimizations, -however if it should grow to where this becomes important, the vision is that -Cranelift would add a separate IR layer, or possibly an separate IR, to support -this. Instead of frontends producing optimizer IR which is then translated to -codegen IR, Cranelift would have frontends producing codegen IR, which can be -translated to optimizer IR and back. +optimizations. Cranelift performs its mid-level optimizations (GVN, LICM, and +other rewrites) through an egraph-based pass that works directly on the same +IR, rather than through a separate optimizer IR. If more aggressive mid-level +optimization becomes important down the road, the vision is still that +Cranelift could add a separate IR layer to support it. Instead of frontends +producing optimizer IR which is then translated to codegen IR, Cranelift would +have frontends producing codegen IR, which can be translated to optimizer IR +and back. This biases the overall system towards fast compilation when mid-level optimization is not needed, such as when emitting unoptimized code for or when diff --git a/cranelift/docs/isle-integration.md b/cranelift/docs/isle-integration.md index 10c3cd8a6219..9e0783609aa2 100644 --- a/cranelift/docs/isle-integration.md +++ b/cranelift/docs/isle-integration.md @@ -134,9 +134,7 @@ aarch64, we implement the handful of move mitosis special cases at the remain pure. Finally, note that these moves are generally cleaned up by the register -allocator's move coalescing, and move mitosis will eventually go away completely -once we switch over to `regalloc2`, which takes instructions in SSA form -directly as input. +allocator's move coalescing. Instructions that implicitly operate on specific registers, or which require that certain operands be in certain registers, are handled similarly: the diff --git a/cranelift/docs/testing.md b/cranelift/docs/testing.md index b1c110b2c2b5..612321e49974 100644 --- a/cranelift/docs/testing.md +++ b/cranelift/docs/testing.md @@ -22,14 +22,14 @@ easier to provide substantial input functions for the compiler tests. File tests are `*.clif` files in the `filetests/` directory hierarchy. Each file has a header describing what to test followed by a number -of input functions in the :doc:`Cranelift textual intermediate representation -`: +of input functions in the [Cranelift textual intermediate representation](ir.md): -.. productionlist:: - test_file : test_header `function_list` - test_header : test_commands (`isa_specs` | `settings`) - test_commands : test_command { test_command } - test_command : "test" test_name { option } "\n" +``` +test_file : test_header function_list +test_header : test_commands (isa_specs | settings) +test_commands : test_command { test_command } +test_command : "test" test_name { option } "\n" +``` The available test commands are described below. @@ -37,27 +37,29 @@ Many test commands only make sense in the context of a target instruction set architecture. These tests require one or more ISA specifications in the test header: -.. productionlist:: - isa_specs : { [`settings`] isa_spec } - isa_spec : "isa" isa_name { `option` } "\n" +``` +isa_specs : { [settings] isa_spec } +isa_spec : "isa" isa_name { option } "\n" +``` The options given on the `isa` line modify the ISA-specific settings defined in -`cranelift-codegen/meta-python/isa/*/settings.py`. +`cranelift/codegen/src/isa//settings.rs`. All types of tests allow shared Cranelift settings to be modified: -.. productionlist:: - settings : { setting } - setting : "set" { option } "\n" - option : flag | setting "=" value +``` +settings : { setting } +setting : "set" { option } "\n" +option : flag | setting "=" value +``` The shared settings available for all target ISAs are defined in -`cranelift-codegen/meta-python/base/settings.py`. +`cranelift/codegen/meta/src/shared/settings.rs`. The `set` lines apply settings cumulatively: ``` - test legalizer + test optimize set opt_level=best set is_pic=1 target riscv64 @@ -67,7 +69,7 @@ The `set` lines apply settings cumulatively: function %foo() {} ``` -This example will run the legalizer test twice. Both runs will have +This example will run the optimize test twice. Both runs will have `opt_level=best`, but they will have different `is_pic` settings. The 32-bit run will also have the RISC-V specific flag `supports_m` disabled. @@ -87,7 +89,7 @@ $ CRANELIFT_FILETESTS_THREADS=1 clif-util test path/to/file.clif Many of the test commands described below use *filecheck* to verify their output. Filecheck is a Rust implementation of the LLVM tool of the same name. -See the `documentation `_ for details of its syntax. +See the [documentation](https://docs.rs/filecheck/) for details of its syntax. Comments in `.clif` files are associated with the entity they follow. This typically means an instruction or the whole function. Those tests that @@ -212,61 +214,12 @@ both correct and complete. This test also sends the computed CFG post-order through filecheck. -### `test legalizer` - -Legalize each function for the specified target ISA and run the resulting -function through filecheck. This test command can be used to validate the -encodings selected for legal instructions as well as the instruction -transformations performed by the legalizer. - -### `test regalloc` - -Test the register allocator. - -First, each function is legalized for the specified target ISA. This is -required for register allocation since the instruction encodings provide -register class constraints to the register allocator. - -Second, the register allocator is run on the function, inserting spill code and -assigning registers and stack slots to all values. - -The resulting function is then run through filecheck. - - -### `test simple-gvn` - -Test the simple GVN pass. - -The simple GVN pass is run on each function, and then results are run -through filecheck. - -### `test licm` - -Test the LICM pass. - -The LICM pass is run on each function, and then results are run -through filecheck. - -### `test dce` - -Test the DCE pass. - -The DCE pass is run on each function, and then results are run -through filecheck. - -### `test shrink` - -Test the instruction shrinking pass. - -The shrink pass is run on each function, and then results are run -through filecheck. - -### `test simple_preopt` - -Test the preopt pass. +### `test optimize` -The preopt pass is run on each function, and then results are run -through filecheck. +Run each function through the optimization passes (egraph-based rewriting, +GVN, LICM, etc.), but not lowering or register allocation, and run the +resulting function through filecheck. This requires a target ISA, since some +of the rewrites are ISA-specific. ### `test compile` From de76f0fa12f743c8b3734cc63be93573f6308766 Mon Sep 17 00:00:00 2001 From: Anniesld Date: Thu, 2 Jul 2026 16:54:04 -0500 Subject: [PATCH 2/7] test modification --- cranelift/docs/add-a-clif-instruction.md | 12 ++++-------- cranelift/docs/compare-llvm.md | 14 ++++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/cranelift/docs/add-a-clif-instruction.md b/cranelift/docs/add-a-clif-instruction.md index 3ad41e84ae15..1e3bd7231578 100644 --- a/cranelift/docs/add-a-clif-instruction.md +++ b/cranelift/docs/add-a-clif-instruction.md @@ -10,8 +10,7 @@ Before adding a new instruction, it's worth checking whether the semantics you want can already be expressed as a combination of existing instructions. CLIF tries to keep its instruction set fairly small; new opcodes are for cases where composing existing ones would be either impossible or noticeably worse -for codegen (e.g. because the mid-end or backends couldn't optimize the -combination as well as a dedicated instruction). +for codegen. ## 1. Declare the instruction @@ -41,16 +40,13 @@ ig.push( The third argument to `Inst::new` is an *instruction format*, chosen from `cranelift/codegen/meta/src/shared/formats.rs` (`unary`, `binary`, `ternary`, `call`, `load`, `store`, `atomic_rmw`, and so on). The format determines what -shape of operands the generated `InstructionData` variant has; reuse an +shape of operands the generated `InstructionData` variant has. That is to say,reuse an existing format if your instruction fits one, rather than adding a new one. -If your instruction has effects beyond producing a result — it can trap, -branch, read or write memory, or otherwise isn't safe to reorder or delete — +If your instruction has effects beyond producing a result say so on the `Inst` builder with methods like `.can_trap()`, `.can_load()`, `.can_store()`, `.other_side_effects()`, `.branches()`, or `.call()` (see -`cranelift/codegen/meta/src/cdsl/instructions.rs` for the full list). This -isn't just documentation: the mid-end and alias analysis read these flags to -decide what's safe to reorder, hoist, or eliminate. +`cranelift/codegen/meta/src/cdsl/instructions.rs` for the full list). ## 2. Let the build regenerate the Rust and ISLE glue diff --git a/cranelift/docs/compare-llvm.md b/cranelift/docs/compare-llvm.md index 77c293671518..d1fe5272e410 100644 --- a/cranelift/docs/compare-llvm.md +++ b/cranelift/docs/compare-llvm.md @@ -94,14 +94,12 @@ lower level of abstraction, to allow it to be used all the way through the codegen process. This design tradeoff does mean that Cranelift IR is less friendly for mid-level -optimizations. Cranelift performs its mid-level optimizations (GVN, LICM, and -other rewrites) through an egraph-based pass that works directly on the same -IR, rather than through a separate optimizer IR. If more aggressive mid-level -optimization becomes important down the road, the vision is still that -Cranelift could add a separate IR layer to support it. Instead of frontends -producing optimizer IR which is then translated to codegen IR, Cranelift would -have frontends producing codegen IR, which can be translated to optimizer IR -and back. +optimizations. Cranelift doesn't currently perform mid-level optimizations, +however if it should grow to where this becomes important, the vision is that +Cranelift would add a separate IR layer, or possibly an separate IR, to support +this. Instead of frontends producing optimizer IR which is then translated to +codegen IR, Cranelift would have frontends producing codegen IR, which can be +translated to optimizer IR and back. This biases the overall system towards fast compilation when mid-level optimization is not needed, such as when emitting unoptimized code for or when From 3c598c58ff8eb5b62753227508fdfe2617e97a07 Mon Sep 17 00:00:00 2001 From: Anniesld Date: Thu, 2 Jul 2026 17:27:21 -0500 Subject: [PATCH 3/7] add-clif-instr improved --- cranelift/docs/add-a-clif-instruction.md | 49 ++++++++---------------- cranelift/docs/testing.md | 12 ++---- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/cranelift/docs/add-a-clif-instruction.md b/cranelift/docs/add-a-clif-instruction.md index 1e3bd7231578..18804c65ece3 100644 --- a/cranelift/docs/add-a-clif-instruction.md +++ b/cranelift/docs/add-a-clif-instruction.md @@ -2,22 +2,16 @@ This is a walkthrough of what's involved in adding a brand new opcode to Cranelift's target-independent IR. It doesn't cover adding a new instruction -to a specific machine backend (the ISA-specific `MachInst`s); see -[Adding a New Instruction to a Machine Backend](add-a-backend-instruction.md) -for that. +to a specific machine backend (the ISA-specific `MachInst`s); -Before adding a new instruction, it's worth checking whether the semantics you -want can already be expressed as a combination of existing instructions. CLIF -tries to keep its instruction set fairly small; new opcodes are for cases -where composing existing ones would be either impossible or noticeably worse -for codegen. +Before adding a new instruction, it's worth checking whether the semantics you want can already be expressed as a combination of existing instructions. CLIF tries to keep its instruction set fairly small; new opcodes are for cases where composing existing ones would be either impossible or noticeably worse for codegen. ## 1. Declare the instruction Instructions are declared in the meta-language, not directly in Rust. The -shared (ISA-independent) instructions live in +shared (ISA-independent) instructions are in `cranelift/codegen/meta/src/shared/instructions.rs`. A declaration looks like -this (this is the real definition of `iadd`): +this: ```rust ig.push( @@ -37,7 +31,7 @@ ig.push( ); ``` -The third argument to `Inst::new` is an *instruction format*, chosen from +The third argument to `Inst::new` is an instruction format, chosen from `cranelift/codegen/meta/src/shared/formats.rs` (`unary`, `binary`, `ternary`, `call`, `load`, `store`, `atomic_rmw`, and so on). The format determines what shape of operands the generated `InstructionData` variant has. That is to say,reuse an @@ -64,8 +58,8 @@ say so on the `Inst` builder with methods like `.can_trap()`, `.can_load()`, [How ISLE is Integrated with Cranelift](isle-integration.md) for more on those generated files.) -You don't write any of this by hand — just run `cargo check` (or `build`) and -the compiler will tell you where you still need to plug the new opcode in. +You don't write any of this by hand, by running `cargo check` (or `build`) +the compiler will tell you where you still need to put the new opcode in. ## 3. Fill in the remaining match arms @@ -75,38 +69,29 @@ means the compiler walks you through most of the checklist, but the usual places are: - **The interpreter**, `cranelift/interpreter/src/step.rs`, which matches - over `inst.opcode()` to give every instruction an execution semantics. - Without this, `test interpret` and `test run` can't exercise the new + on `inst.opcode()` to give every instruction an execution semantics. + Without this, `test interpret` and `test run` won't be able to execute the instruction. -- **The verifier**, `cranelift/codegen/src/verifier/mod.rs`, if the new - instruction has type or operand constraints beyond what the format and - type variables already check. +- **The verifier**, `cranelift/codegen/src/verifier/mod.rs`, if the new instruction introduces type rules or operand constraints that aren't already enforced by its format or type variables. - **Alias analysis and the egraph mid-end** (`cranelift/codegen/src/alias_analysis.rs`, `cranelift/codegen/src/egraph/`) if the instruction touches memory or - needs special handling to be optimized correctly — this is really an - extension of the side-effect flags from step 1. + needs special handling to be optimized correctly. In practice, this is often an extension of the side-effect annotations defined in the first step. -Simple, pure instructions (no side effects, standard type variables) often +Pure instructions (no side effects, standard type variables) often need nothing beyond steps 1 and 2 plus a lowering rule in each backend that should support them. ## 4. Add a lowering rule per backend A CLIF instruction with no backend that can lower it will hit ISLE's "no rule -matched" panic the moment it's used. At minimum, add a `(rule (lower -(your_instr ...)) ...)` to each `cranelift/codegen/src/isa//lower.isle` -you care about (or an explicit "unsupported" path if it's intentionally not -supported everywhere yet). See -[Adding a New Instruction to a Machine Backend](add-a-backend-instruction.md) -for what that involves in more detail. - +matched" panic the moment it's used. At a minimum, you should add a (rule (lower (your_instr ...)) ...) definition to each cranelift/codegen/src/isa//lower.isle backend you intend to support. If support for an architecture is not yet implemented, consider adding an explicit "unsupported" case instead. ## 5. Test it -Add file tests under `cranelift/filetests/filetests/`. `test interpret` and -`test run` exercise the semantics you added in step 3, `test optimize` -exercises any mid-end rewrites, and `test compile` (per architecture, under -`filetests/isa//`) exercises the lowering from step 4. See +Add file tests under `cranelift/filetests/filetests/`. Use `test interpret` and +`test run` to verify the execution semantics implemented in stop 3, `test optimize` +to validate any mid-end rewrites, and `test compile` (placed under +`filetests/isa//` for each target architecture)to test the backend lowering implemented in step 4. See [Testing Cranelift](testing.md) for how these test commands work. ## Worked example diff --git a/cranelift/docs/testing.md b/cranelift/docs/testing.md index 612321e49974..0022e0e4dc04 100644 --- a/cranelift/docs/testing.md +++ b/cranelift/docs/testing.md @@ -216,20 +216,14 @@ This test also sends the computed CFG post-order through filecheck. ### `test optimize` -Run each function through the optimization passes (egraph-based rewriting, -GVN, LICM, etc.), but not lowering or register allocation, and run the +Runs each function through the optimization passes (egraph-based rewriting, +GVN, LICM, etc.), but not lowering or register allocation, and runs the resulting function through filecheck. This requires a target ISA, since some of the rewrites are ISA-specific. ### `test compile` -Test the whole code generation pipeline. - -Each function is passed through the full `Context::compile()` function -which is normally used to compile code. This type of test often depends -on assertions or verifier errors, but it is also possible to use -filecheck directives which will be matched against the final form of the -Cranelift IR right before binary machine code emission. +Tests the whole code generation pipeline. Each function is passed through the full `Context::compile()` function which is normally used to compile code. This type of test often depends on assertions or verifier errors, but it is also possible to use filecheck directives which will be matched against the final form of the Cranelift IR right before binary machine code emission. ### `test run` From 1f442a1fea532fdfd5531f1811ef3dfe327c5095 Mon Sep 17 00:00:00 2001 From: Anniesld Date: Thu, 2 Jul 2026 17:32:21 -0500 Subject: [PATCH 4/7] add-clif-instr improved --- cranelift/docs/add-a-clif-instruction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cranelift/docs/add-a-clif-instruction.md b/cranelift/docs/add-a-clif-instruction.md index 18804c65ece3..917a95936e71 100644 --- a/cranelift/docs/add-a-clif-instruction.md +++ b/cranelift/docs/add-a-clif-instruction.md @@ -89,7 +89,7 @@ matched" panic the moment it's used. At a minimum, you should add a (rule (lower ## 5. Test it Add file tests under `cranelift/filetests/filetests/`. Use `test interpret` and -`test run` to verify the execution semantics implemented in stop 3, `test optimize` +`test run` to verify the execution semantics implemented in step 3, `test optimize` to validate any mid-end rewrites, and `test compile` (placed under `filetests/isa//` for each target architecture)to test the backend lowering implemented in step 4. See [Testing Cranelift](testing.md) for how these test commands work. From 03b4d1b78f1195bdff789a61445a60097618a676 Mon Sep 17 00:00:00 2001 From: AnnieSld Date: Wed, 8 Jul 2026 23:52:46 -0500 Subject: [PATCH 5/7] Update cranelift/docs/testing.md Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- cranelift/docs/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cranelift/docs/testing.md b/cranelift/docs/testing.md index 0022e0e4dc04..7c7da05bc6fa 100644 --- a/cranelift/docs/testing.md +++ b/cranelift/docs/testing.md @@ -43,7 +43,7 @@ isa_spec : "isa" isa_name { option } "\n" ``` The options given on the `isa` line modify the ISA-specific settings defined in -`cranelift/codegen/src/isa//settings.rs`. +`cranelift/codegen/meta/src/isa/.rs`. All types of tests allow shared Cranelift settings to be modified: From cde4e818bc664d5dba58c96b2c906211e978a279 Mon Sep 17 00:00:00 2001 From: AnnieSld Date: Wed, 8 Jul 2026 23:55:38 -0500 Subject: [PATCH 6/7] Update documentation for adding CLIF instructions Clarified instructions for adding a lowering rule per backend and emphasized the need for tests. --- cranelift/docs/add-a-clif-instruction.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cranelift/docs/add-a-clif-instruction.md b/cranelift/docs/add-a-clif-instruction.md index 917a95936e71..e0eb7c582a69 100644 --- a/cranelift/docs/add-a-clif-instruction.md +++ b/cranelift/docs/add-a-clif-instruction.md @@ -85,7 +85,8 @@ should support them. ## 4. Add a lowering rule per backend A CLIF instruction with no backend that can lower it will hit ISLE's "no rule -matched" panic the moment it's used. At a minimum, you should add a (rule (lower (your_instr ...)) ...) definition to each cranelift/codegen/src/isa//lower.isle backend you intend to support. If support for an architecture is not yet implemented, consider adding an explicit "unsupported" case instead. +matched" panic the moment it's used. At a minimum, you should add a (rule (lower (your_instr ...)) ...) definition to each cranelift/codegen/src/isa//lower.isle backend you intend to support. + ## 5. Test it Add file tests under `cranelift/filetests/filetests/`. Use `test interpret` and From 772bbf270b01119239741803e74c2b954a6eb58b Mon Sep 17 00:00:00 2001 From: AnnieSld Date: Thu, 9 Jul 2026 00:01:05 -0500 Subject: [PATCH 7/7] Clarify handling of modified registers in ISAs Removed references to 'move mitosis' and clarified the explanation of handling modified registers in ISAs. --- cranelift/docs/isle-integration.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cranelift/docs/isle-integration.md b/cranelift/docs/isle-integration.md index 9e0783609aa2..b2cd4a6451b6 100644 --- a/cranelift/docs/isle-integration.md +++ b/cranelift/docs/isle-integration.md @@ -108,8 +108,7 @@ Instead, these things should be handled by some combination of `cranelift/codegen/src/isa//lower/isle.rs` or elsewhere). When an instruction modifies a register, both reading from it and writing to it, -we should build an SSA view of that instruction that gets legalized via "move -mitosis" by splitting a move out from the register. +we should build an SSA view of that instruction that gets legalized by splitting a move out from the register. For example, on x86 the `add` instruction reads and writes its first operand: @@ -125,11 +124,11 @@ Then, as an implementation detail of the facade, we emit moves as necessary: add a, b, c ==> mov a, b; add b, c -We call the process of emitting these moves "move mitosis". For ISAs with +For ISAs with ubiquitous use of modified registers and instructions in two-operand form, like -x86, we implement move mitosis with methods on the ISA's `MachInst`. For other +x86, we implement this with methods methods on the ISA's `MachInst`. For other ISAs that are RISCier and where modified registers are pretty rare, such as -aarch64, we implement the handful of move mitosis special cases at the +aarch64, we handle the handful of required cases at the `inst.isle` layer. Either way, the important thing is that the lowering rules remain pure.