Add support for implicit type conversions to ISLE. This feature allows the DSL user to register to the compiler that a particular term (used as a constructor or extractor) converts from one type to another. The compiler will then *automatically* insert this term whenever a type mismatch involving that specific pair of types occurs. This significantly cleans up many uses of the ISLE DSL. For example, when defining the compiler backends, we often have newtypes like `Gpr` around `Reg` (signifying a particular type of register); we can define a conversion from Gpr to Reg automatically. Conversions can also have side-effects, as long as these side-effects are idempotent. For example, `put_value_in_reg` in a compiler backend has the effect of marking the value as used, causing codegen to produce it, and assigns a register to the value; but multiple invocations of this will return the same register for the same value. Thus it is safe to use it as an implicit conversion that may be invoked multiple times. This is documented in the ISLE-Cranelift integration document. This PR also adds some testing infrastructure to the ISLE compiler, checking that "pass" tests pass through the DSL compiler, "fail" tests do not, and "link" tests are able to generate code and link that code with corresponding Rust code.
66 lines
1.8 KiB
Common Lisp
66 lines
1.8 KiB
Common Lisp
(type Opcode extern (enum
|
|
Iadd
|
|
Isub
|
|
Load
|
|
Store))
|
|
|
|
(type Inst (primitive Inst))
|
|
(type InstInput (primitive InstInput))
|
|
(type Reg (primitive Reg))
|
|
(type u32 (primitive u32))
|
|
|
|
(decl Op (Opcode) Inst)
|
|
(extern extractor infallible Op get_opcode)
|
|
|
|
(decl InstInput (InstInput u32) Inst)
|
|
(extern extractor infallible InstInput get_inst_input (out in))
|
|
|
|
(decl Producer (Inst) InstInput)
|
|
(extern extractor Producer get_input_producer)
|
|
|
|
(decl UseInput (InstInput) Reg)
|
|
(extern constructor UseInput put_input_in_reg)
|
|
|
|
(type MachInst (enum
|
|
(Add (a Reg) (b Reg))
|
|
(Add3 (a Reg) (b Reg) (c Reg))
|
|
(Sub (a Reg) (b Reg))))
|
|
|
|
(decl Lower (Inst) MachInst)
|
|
|
|
;; Extractors that give syntax sugar for (Iadd ra rb), etc.
|
|
;;
|
|
;; Note that this is somewhat simplistic: it directly connects inputs to
|
|
;; MachInst regs; really we'd want to return a VReg or InstInput that we can use
|
|
;; another extractor to connect to another (producer) inst.
|
|
;;
|
|
;; Also, note that while it looks a little indirect, a verification effort could
|
|
;; define equivalences across the `rule` LHS/RHS pairs, and the types ensure that
|
|
;; we are dealing (at the semantic level) with pure value equivalences of
|
|
;; "terms", not arbitrary side-effecting calls.
|
|
|
|
(decl Iadd (InstInput InstInput) Inst)
|
|
(decl Isub (InstInput InstInput) Inst)
|
|
(extractor
|
|
(Iadd a b)
|
|
(and
|
|
(Op (Opcode.Iadd))
|
|
(InstInput a <0)
|
|
(InstInput b <1)))
|
|
(extractor
|
|
(Isub a b)
|
|
(and
|
|
(Op (Opcode.Isub))
|
|
(InstInput a <0)
|
|
(InstInput b <1)))
|
|
|
|
;; Now the nice syntax-sugar that "end-user" backend authors can write:
|
|
(rule
|
|
(Lower (Iadd ra rb))
|
|
(MachInst.Add (UseInput ra) (UseInput rb)))
|
|
(rule
|
|
(Lower (Iadd (Producer (Iadd ra rb)) rc))
|
|
(MachInst.Add3 (UseInput ra) (UseInput rb) (UseInput rc)))
|
|
(rule
|
|
(Lower (Isub ra rb))
|
|
(MachInst.Sub (UseInput ra) (UseInput rb))) |