Skip to content

ISLE: add an implicit type conversion mechanism #3753

@fitzgen

Description

@fitzgen

There is a lot of (RegMem.Reg my_reg) expressions in the x64 backend that convert a Reg into a RegMem when passing a Reg as an operand to an instruction that can also take a memory operand. Even more of this kind of boring conversion will be littered about with the introduction of newtypes for GPRs and XMMs in #3752 where we have to do (xmm_to_xmm_mem my_xmm).

If we were writing these APIs in Rust, we would take an reg_mem: impl Into<RegMem> param for the first example and "hide" the type conversion inside the API so that the noise is hidden from callers and the API feels nicer and more flexible. The second example would have xmm_mem: impl Into<XmmMem>. We don't have that mechanism in ISLE and the result is that certain bits of code are painfully noisy to the point where it is distracting from what instructions we are actually lowering into.

This issue is proposing that we add something similar to impl Into<T> to ISLE, to fulfill those same API ergonomics/niceties, but much simpler and without introducing a whole traits system.

First off, a big constraint: we want to maintain that there is only one expected type for every expression. This keeps type checking a single, simple pass. We don't need to do any kind of type constraint collection/solving/inference or anything like that.

Straw person proposal (feel free to bikeshed on syntax):

;; A plain decl+constructor that happens to take a `Reg` and return a `RegMem`.
(decl reg_to_reg_mem (Reg) RegMem)
(rule (reg_to_reg_mem r)
      (RegMem.Reg r))

;; Declare that we can convert a `Reg` into a `RegMem` with the
;; `reg_to_reg_mem` constructor.
;;
;; Note that this is unidirectional and does *not* imply you can
;; convert a `RegMem` into a `Reg`!
(converts Reg RegMem reg_to_reg_mem)

;; The x64 add instruction takes two operands: a register operand and
;; a register-or-memory operand.
(decl x64_add (Reg RegMem) Reg)
(rule (x64_add src1 src2) ...)

;; We can pass a `Reg` as the second register-or-memory operand to
;; `x64_add`, without explicitly writing any type conversions, and
;; the ISLE compiler will automatically insert a call to the
;; `reg_to_reg_mem` constructor for us.
(let ((a Reg ...)
      (b Reg ...))
  (x64_add a b))

;; That is, the above is equivalent to this:
(let ((a Reg ...)
      (b Reg ...))
  (x64_add a (reg_to_reg_mem b)))

How do we implement this? Well when you try to compile the above example with ISLE today, it gives you a type error saying that it expected a RegMem but got a Reg. It already knows the expected and actual types, so it can just look up and see if an implicit type conversion between the two is already registered. If so, then insert the conversion call. If not, then report the error.

Thoughts?

(cc @cfallin @abrown @uweigand)

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