Skip to content

Uniqueness types in ISLE #4745

Open
Open
@elliottt

Description

@elliottt

Feature

When working on lowerings in ISLE, it's convenient to rely on implicit conversions to de-clutter the implementation. However this can hide bugs, as in the following example:

(decl x64_palignr (Xmm XmmMem u8 OperandSize) Xmm)

(convert Value Xmm put_in_xmm)
(convert Value XmmMem put_in_xmm_mem)

(decl swiden_high (Value) Inst)
(extractor
    (swiden_high x)
    (inst_data (InstructionData.Unary (Opcode.SwidenHigh) x))
)

(rule (lower (has_type $I16X8 (swiden_high val @ (value_type $I8X16))))
      (x64_pmovsxbw (x64_palignr val val 8 (OperandSize.Size32))))

In this example, val has type Value, but is used with x64_palignr at two different types: Xmm and XmmMem. Defined elsewhere are two conversions, one from Value -> Xmm and one from Value -> XmmMem that will potentially sink a load into this use if the Value is only used once at the clif level. Because the swiden_high instruction has only one argument, using it twice in its lowering could produce a bug where val is sunk into the XmmMem arg and also used as the Xmm argument, yielding a panic during register allocation.

One approach to solving this problem would be to not permit a Value to be converted into an Xmm or XmmMem more than one time. This could be accomplished by adding an annotation to arguments to ISLE declarations, indicating which arguments must be used uniquely. The clean language uses ! for this, so you could imagine the signature of the swiden_high extractor changing to the following to indicate that Value may only be used once:

(decl swiden_high (!Value) Inst)
(extractor
    (swiden_high x)
    (inst_data (InstructionData.Unary (Opcode.SwidenHigh) x))
)

Another approach would be to allow functions to be marked as explicitly consuming their inputs from the typing environment, using an annotation like in the Mezzo language. In this case we would want to mark both put_in_xmm and put_in_xmm_mem as consuming their arguments, so let's use @ as a straw-man sigil:

(decl put_in_xmm (@Value) Xmm)
(decl put_in_xmm_mem (@Value) XmmMem)

We would get two benefits from this approach: we could call other functions on val before it was used with either of the functions that convert it to an Xmm or XmmReg, and it would be an error to try to call both of those functions on the same value. The initial example of (x64_palignr val val ...) would become a type error, as the two uses of val would lead to independent applications of put_in_xmm and put_in_xmm_mem, both of which consume their arguments.

Benefit

Adding some form of uniqueness typing to ISLE would help us to avoid bugs where the api has a notion of uniqueness that right now must be preserved manually. In the case of the example above, this would help us to enforce that sinking a load into another instruction does not lead to situations where the result of that original load is also expected to be in a register.

Alternatives

We can add assertions to require that we respect the invariants of the underlying apis that the ISLE code interacts with, or refactor those underlying apis to track sinkable loads on the vcode instead of the clif.

Metadata

Metadata

Assignees

No one assigned

    Labels

    isleRelated to the ISLE domain-specific language

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions