Browse Source
This PR fixes a bug in the ISLE compiler related to rule priorities. An important note first: the bug did not affect the correctness of the Cranelift backends, either in theory (because the rules should be correct applied in any order, even contrary to the stated priorities) or in practice (because the generated code actually does not change at all with the DSL compiler fix, only with a separate minimized bug example). The issue was a simple swap of `min` for `max` (see first commit). This is the minimal fix, I think, to get a correct priority-trie with the minimized bug example in this commit. However, while debugging this, I started to convince myself that the complexity of merging multiple priority ranges using the sort of hybrid interval tree / string-matching trie data structure was unneeded. The original design was built with the assumption we might have a bunch of different priority levels, and would need the efficiency of merging where possible. But in practice we haven't used priorities this way: the vast majority of lowering rules exist at the default (priority 0), and just a few overrides are explicitly at prio 1, 2 or (rarely) 3. So, it turns out to be a lot simpler to label trie edges with (prio, symbol) rather than (prio-range, symbol), and delete the whole mess of interval-splitting logic on insertion. It's easier (IMHO) to convince oneself that the resulting insertion algorithm is correct. I was worried that this might impact the size of the generated Rust code or its runtime, but In fact, to my initial surprise (but it makes sense given the above "rarely used" factor), the generated code with this compiler fix is *exactly the same*. I rebuilt with `--features rebuild-isle,all-arch` but... there were no diffs to commit! This is to me the simplest evidence that we didn't really need that complexity.pull/4094/head
Chris Fallin
3 years ago
committed by
GitHub
3 changed files with 156 additions and 274 deletions
@ -0,0 +1,108 @@ |
|||||
|
;; Minimized bug reproducer for earlier priority-range-merging trie |
||||
|
;; implementation in ISLE compiler. This example, when compiled with |
||||
|
;; old versions of islec, would result in the bottom-most rule (at |
||||
|
;; priority 0) being applied before the rule involving `iconst` at |
||||
|
;; priority 1 below. |
||||
|
|
||||
|
(type Unit (primitive Unit)) |
||||
|
(type u8 (primitive u8)) |
||||
|
(type u32 (primitive u32)) |
||||
|
(type Reg (primitive Reg)) |
||||
|
(type MemFlags (primitive MemFlags)) |
||||
|
(type MachLabel (primitive MachLabel)) |
||||
|
(type Value (primitive Value)) |
||||
|
|
||||
|
(decl iadd (Value Value) Value) |
||||
|
(extern extractor iadd iadd) |
||||
|
(decl ishl (Value Value) Value) |
||||
|
(extern extractor ishl ishl) |
||||
|
(decl uextend (Value) Value) |
||||
|
(extern extractor uextend uextend) |
||||
|
(decl sextend (Value) Value) |
||||
|
(extern extractor sextend sextend) |
||||
|
(decl iconst (u32) Value) |
||||
|
(extern extractor iconst iconst) |
||||
|
|
||||
|
(decl put_in_reg (Value) Reg) |
||||
|
(convert Value Reg put_in_reg) |
||||
|
(extern constructor put_in_reg put_in_reg) |
||||
|
|
||||
|
(decl invalid_reg () Reg) |
||||
|
(extern extractor invalid_reg invalid_reg) |
||||
|
(decl valid_reg () Reg) |
||||
|
(extern extractor valid_reg valid_reg) |
||||
|
|
||||
|
(decl pure u32_lteq (u32 u32) Unit) |
||||
|
(extern constructor u32_lteq u32_lteq) |
||||
|
|
||||
|
(decl pure s32_add_fallible (u32 u32) u32) |
||||
|
(extern constructor s32_add_fallible s32_add_fallible) |
||||
|
|
||||
|
(decl x64_add (Reg Reg) Reg) |
||||
|
(extern constructor x64_add x64_add) |
||||
|
|
||||
|
;; An `Amode` represents a possible addressing mode that can be used |
||||
|
;; in instructions. These denote a 64-bit value only. |
||||
|
(type Amode (enum |
||||
|
;; Immediate sign-extended and a register |
||||
|
(ImmReg (simm32 u32) |
||||
|
(base Reg) |
||||
|
(flags MemFlags)) |
||||
|
|
||||
|
;; Sign-extend-32-to-64(simm32) + base + (index << shift) |
||||
|
(ImmRegRegShift (simm32 u32) |
||||
|
(base Reg) |
||||
|
(index Reg) |
||||
|
(shift u32) |
||||
|
(flags MemFlags)) |
||||
|
|
||||
|
;; Sign-extend-32-to-64(immediate) + RIP (instruction |
||||
|
;; pointer). The appropriate relocation is emitted so |
||||
|
;; that the resulting immediate makes this Amode refer to |
||||
|
;; the given MachLabel. |
||||
|
(RipRelative (target MachLabel)))) |
||||
|
|
||||
|
;; One step in amode processing: take an existing amode and add |
||||
|
;; another value to it. |
||||
|
(decl amode_add (Amode Value) Amode) |
||||
|
|
||||
|
;; -- Top-level driver: pull apart the addends. |
||||
|
;; |
||||
|
;; Any amode can absorb an `iadd` by absorbing first the LHS of the |
||||
|
;; add, then the RHS. |
||||
|
;; |
||||
|
;; Priority 2 to take this above fallbacks and ensure we traverse the |
||||
|
;; `iadd` tree fully. |
||||
|
(rule 2 (amode_add amode (iadd x y)) |
||||
|
(let ((amode1 Amode (amode_add amode x)) |
||||
|
(amode2 Amode (amode_add amode1 y))) |
||||
|
amode2)) |
||||
|
|
||||
|
;; -- Case 1 (adding a register to the initial Amode with invalid_reg). |
||||
|
;; |
||||
|
;; An Amode.ImmReg with invalid_reg (initial state) can absorb a |
||||
|
;; register as the base register. |
||||
|
(rule (amode_add (Amode.ImmReg off (invalid_reg) flags) value) |
||||
|
(Amode.ImmReg off value flags)) |
||||
|
|
||||
|
;; -- Case 4 (absorbing constant offsets). |
||||
|
;; |
||||
|
;; An Amode can absorb a constant (i64, or extended i32) as long as |
||||
|
;; the sum still fits in the signed-32-bit offset. |
||||
|
;; |
||||
|
;; Priority 3 in order to take this option above the fallback |
||||
|
;; (immediate in register). Two rules, for imm+reg and |
||||
|
;; imm+reg+scale*reg cases. |
||||
|
(rule 1 (amode_add (Amode.ImmRegRegShift off base index shift flags) |
||||
|
(iconst c)) |
||||
|
(if-let sum (s32_add_fallible off c)) |
||||
|
(Amode.ImmRegRegShift sum base index shift flags)) |
||||
|
|
||||
|
;; -- Case 5 (fallback to add a new value to an imm+reg+scale*reg). |
||||
|
;; |
||||
|
;; An Amode.ImmRegRegShift can absorb any other value by creating a |
||||
|
;; new add instruction and replacing the base with |
||||
|
;; (base+value). |
||||
|
(rule (amode_add (Amode.ImmRegRegShift off base index shift flags) value) |
||||
|
(let ((sum Reg (x64_add base value))) |
||||
|
(Amode.ImmRegRegShift off sum index shift flags))) |
Loading…
Reference in new issue