Add accessors to prelude.isle to access data fields of
`func_addr` and `symbol_value` instructions.
These are based on similar versions I had added to the s390x
back-end, but are a bit more straightforward to use.
- func_ref_data: Extract SigRef, ExternalName, and RelocDistance
fields given a FuncRef.
- symbol_value_data: Extract ExternalName, RelocDistance, and
offset fields given a GlobalValue representing a Symbol.
- reloc_distance_near: Test for RelocDistance::Near.
The s390x back-end is changed to use these common versions.
Note that this exposed a bug in common isle code: This extractor:
(extractor (load_sym inst)
(and inst
(load _ (def_inst (symbol_value
(symbol_value_data _
(reloc_distance_near) offset)))
(i64_from_offset
(memarg_symbol_offset_sum <offset _)))))
would raise an assertion in sema.rs due to a supposed cycle in
extractor definitions. But there was no actual cycle, it was
simply that the extractor tree refers twice to the `insn_data`
extractor (once via the `load` and once via the `symbol_value`
extractor). Fixed by checking for pre-existing definitions only
along one path in the tree, not across the whole tree.
403 lines
14 KiB
Common Lisp
403 lines
14 KiB
Common Lisp
;; This is a prelude of standard definitions for ISLE, the instruction-selector
|
|
;; DSL, as we use it bound to our interfaces.
|
|
;;
|
|
;; Note that all `extern` functions here are typically defined in the
|
|
;; `isle_prelude_methods` macro defined in `src/isa/isle.rs`
|
|
|
|
;;;; Primitive and External Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; `()`
|
|
(type Unit (primitive Unit))
|
|
|
|
;; `bool` is declared in `clif.isle`.
|
|
(extern const $true bool)
|
|
(extern const $false bool)
|
|
|
|
(type u8 (primitive u8))
|
|
(type u16 (primitive u16))
|
|
(type u32 (primitive u32))
|
|
(type u64 (primitive u64))
|
|
(type u128 (primitive u128))
|
|
(type usize (primitive usize))
|
|
|
|
(type i8 (primitive i8))
|
|
(type i16 (primitive i16))
|
|
(type i32 (primitive i32))
|
|
(type i64 (primitive i64))
|
|
(type i128 (primitive i128))
|
|
(type isize (primitive isize))
|
|
|
|
;; `cranelift-entity`-based identifiers.
|
|
(type Inst (primitive Inst))
|
|
(type Type (primitive Type))
|
|
(type Value (primitive Value))
|
|
|
|
;; ISLE representation of `&[Value]`.
|
|
(type ValueSlice (primitive ValueSlice))
|
|
|
|
(type ValueList (primitive ValueList))
|
|
(type ValueRegs (primitive ValueRegs))
|
|
|
|
(decl u32_add (u32 u32) u32)
|
|
(extern constructor u32_add u32_add)
|
|
|
|
(decl u8_and (u8 u8) u8)
|
|
(extern constructor u8_and u8_and)
|
|
|
|
;;;; Registers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(type Reg (primitive Reg))
|
|
(type WritableReg (primitive WritableReg))
|
|
(type OptionWritableReg (primitive OptionWritableReg))
|
|
(type VecReg extern (enum))
|
|
(type VecWritableReg extern (enum))
|
|
|
|
;; Construct a `ValueRegs` of one register.
|
|
(decl value_reg (Reg) ValueRegs)
|
|
(extern constructor value_reg value_reg)
|
|
|
|
;; Construct a `ValueRegs` of two registers.
|
|
(decl value_regs (Reg Reg) ValueRegs)
|
|
(extern constructor value_regs value_regs)
|
|
|
|
;; Construct an empty `ValueRegs` containing only invalid register sentinels.
|
|
(decl value_regs_invalid () ValueRegs)
|
|
(extern constructor value_regs_invalid value_regs_invalid)
|
|
|
|
;; Get a temporary register for writing.
|
|
(decl temp_writable_reg (Type) WritableReg)
|
|
(extern constructor temp_writable_reg temp_writable_reg)
|
|
|
|
;; Get a temporary register for reading.
|
|
(decl temp_reg (Type) Reg)
|
|
(rule (temp_reg ty)
|
|
(writable_reg_to_reg (temp_writable_reg ty)))
|
|
|
|
;; Get the invalid register.
|
|
(decl invalid_reg () Reg)
|
|
(extern constructor invalid_reg invalid_reg)
|
|
|
|
;; Put the given value into a register.
|
|
;;
|
|
;; Asserts that the value fits into a single register, and doesn't require
|
|
;; multiple registers for its representation (like `i128` on x64 for example).
|
|
;;
|
|
;; As a side effect, this marks the value as used.
|
|
(decl put_in_reg (Value) Reg)
|
|
(extern constructor put_in_reg put_in_reg)
|
|
|
|
;; Put the given value into one or more registers.
|
|
;;
|
|
;; As a side effect, this marks the value as used.
|
|
(decl put_in_regs (Value) ValueRegs)
|
|
(extern constructor put_in_regs put_in_regs)
|
|
|
|
;; Get the `n`th register inside a `ValueRegs`.
|
|
(decl value_regs_get (ValueRegs usize) Reg)
|
|
(extern constructor value_regs_get value_regs_get)
|
|
|
|
;; Put the value into one or more registers and return the first register.
|
|
;;
|
|
;; Unlike `put_in_reg`, this does not assert that the value fits in a single
|
|
;; register. This is useful for things like a `i128` shift amount, where we mask
|
|
;; the shift amount to the bit width of the value being shifted, and so the high
|
|
;; half of the `i128` won't ever be used.
|
|
;;
|
|
;; As a side efect, this marks that value as used.
|
|
(decl lo_reg (Value) Reg)
|
|
(rule (lo_reg val)
|
|
(let ((regs ValueRegs (put_in_regs val)))
|
|
(value_regs_get regs 0)))
|
|
|
|
;;;; Common Mach Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(type MachLabel (primitive MachLabel))
|
|
(type VecMachLabel extern (enum))
|
|
(type ValueLabel (primitive ValueLabel))
|
|
(type UnwindInst (primitive UnwindInst))
|
|
(type ExternalName (primitive ExternalName))
|
|
(type BoxExternalName (primitive BoxExternalName))
|
|
(type RelocDistance (primitive RelocDistance))
|
|
|
|
;;;; Primitive Type Conversions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(decl u8_as_u64 (u8) u64)
|
|
(extern constructor u8_as_u64 u8_as_u64)
|
|
|
|
(decl u16_as_u64 (u16) u64)
|
|
(extern constructor u16_as_u64 u16_as_u64)
|
|
|
|
(decl u32_as_u64 (u32) u64)
|
|
(extern constructor u32_as_u64 u32_as_u64)
|
|
|
|
;;;; `cranelift_codegen::ir::Type` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(extern const $B1 Type)
|
|
(extern const $B8 Type)
|
|
(extern const $B16 Type)
|
|
(extern const $B32 Type)
|
|
(extern const $B64 Type)
|
|
(extern const $B128 Type)
|
|
|
|
(extern const $I8 Type)
|
|
(extern const $I16 Type)
|
|
(extern const $I32 Type)
|
|
(extern const $I64 Type)
|
|
(extern const $I128 Type)
|
|
|
|
(extern const $R32 Type)
|
|
(extern const $R64 Type)
|
|
|
|
(extern const $F32 Type)
|
|
(extern const $F64 Type)
|
|
|
|
(extern const $B8X16 Type)
|
|
(extern const $B16X8 Type)
|
|
(extern const $B32X4 Type)
|
|
(extern const $B64X2 Type)
|
|
|
|
(extern const $I8X16 Type)
|
|
(extern const $I16X8 Type)
|
|
(extern const $I32X4 Type)
|
|
(extern const $I64X2 Type)
|
|
|
|
(extern const $F32X4 Type)
|
|
(extern const $F64X2 Type)
|
|
|
|
;; Get the bit width of a given type.
|
|
(decl ty_bits (Type) u8)
|
|
(extern constructor ty_bits ty_bits)
|
|
|
|
;; Get the bit width of a given type.
|
|
(decl ty_bits_u16 (Type) u16)
|
|
(extern constructor ty_bits_u16 ty_bits_u16)
|
|
|
|
;; Get the byte width of a given type.
|
|
(decl ty_bytes (Type) u16)
|
|
(extern constructor ty_bytes ty_bytes)
|
|
|
|
;; Get the type of each lane in the given type.
|
|
(decl lane_type (Type) Type)
|
|
(extern constructor lane_type lane_type)
|
|
|
|
;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; An extractor that only matches types that can fit in 16 bits.
|
|
(decl fits_in_16 (Type) Type)
|
|
(extern extractor fits_in_16 fits_in_16)
|
|
|
|
;; An extractor that only matches types that can fit in 32 bits.
|
|
(decl fits_in_32 (Type) Type)
|
|
(extern extractor fits_in_32 fits_in_32)
|
|
|
|
;; An extractor that only matches types that can fit in 64 bits.
|
|
(decl fits_in_64 (Type) Type)
|
|
(extern extractor fits_in_64 fits_in_64)
|
|
|
|
;; An extractor that maches 32- and 64-bit types only.
|
|
(decl ty_32_or_64 (Type) Type)
|
|
(extern extractor ty_32_or_64 ty_32_or_64)
|
|
|
|
;; An extractor that maches 8- and 16-bit types only.
|
|
(decl ty_8_or_16 (Type) Type)
|
|
(extern extractor ty_8_or_16 ty_8_or_16)
|
|
|
|
;; An extractor that only matches 128-bit vector types.
|
|
(decl vec128 (Type) Type)
|
|
(extern extractor vec128 vec128)
|
|
|
|
;; An extractor that matches everything except i64x2
|
|
(decl not_i64x2 () Type)
|
|
(extern extractor not_i64x2 not_i64x2)
|
|
|
|
;; Extractor to get a `ValueSlice` out of a `ValueList`.
|
|
(decl value_list_slice (ValueSlice) ValueList)
|
|
(extern extractor infallible value_list_slice value_list_slice)
|
|
|
|
;; Extractor to get the first element from a value list, along with its tail as
|
|
;; a `ValueSlice`.
|
|
(decl unwrap_head_value_list_1 (Value ValueSlice) ValueList)
|
|
(extern extractor infallible unwrap_head_value_list_1 unwrap_head_value_list_1)
|
|
|
|
;; Extractor to get the first two elements from a value list, along with its
|
|
;; tail as a `ValueSlice`.
|
|
(decl unwrap_head_value_list_2 (Value Value ValueSlice) ValueList)
|
|
(extern extractor infallible unwrap_head_value_list_2 unwrap_head_value_list_2)
|
|
|
|
;; Turn a `Writable<Reg>` into a `Reg` via `Writable::to_reg`.
|
|
(decl writable_reg_to_reg (WritableReg) Reg)
|
|
(extern constructor writable_reg_to_reg writable_reg_to_reg)
|
|
|
|
;; Extract a `u8` from an `Uimm8`.
|
|
(decl u8_from_uimm8 (u8) Uimm8)
|
|
(extern extractor infallible u8_from_uimm8 u8_from_uimm8)
|
|
|
|
;; Extract a `u64` from an `Imm64`.
|
|
(decl u64_from_imm64 (u64) Imm64)
|
|
(extern extractor infallible u64_from_imm64 u64_from_imm64)
|
|
|
|
;; Extract a `u64` from an `Imm64` which is not zero.
|
|
(decl nonzero_u64_from_imm64 (u64) Imm64)
|
|
(extern extractor nonzero_u64_from_imm64 nonzero_u64_from_imm64)
|
|
|
|
;; Extract a `u64` from an `Ieee32`.
|
|
(decl u64_from_ieee32 (u64) Ieee32)
|
|
(extern extractor infallible u64_from_ieee32 u64_from_ieee32)
|
|
|
|
;; Extract a `u64` from an `Ieee64`.
|
|
(decl u64_from_ieee64 (u64) Ieee64)
|
|
(extern extractor infallible u64_from_ieee64 u64_from_ieee64)
|
|
|
|
;; Extract the result values for the given instruction.
|
|
(decl inst_results (ValueSlice) Inst)
|
|
(extern extractor infallible inst_results inst_results)
|
|
|
|
;; Extract the first result value of the given instruction.
|
|
(decl first_result (Value) Inst)
|
|
(extern extractor first_result first_result)
|
|
|
|
;; Extract the `InstructionData` for an `Inst`.
|
|
(decl inst_data (InstructionData) Inst)
|
|
(extern extractor infallible inst_data inst_data)
|
|
|
|
;; Extract the type of a `Value`.
|
|
(decl value_type (Type) Value)
|
|
(extern extractor infallible value_type value_type)
|
|
|
|
;; Extract the type of the instruction's first result.
|
|
(decl result_type (Type) Inst)
|
|
(extractor (result_type ty)
|
|
(first_result (value_type ty)))
|
|
|
|
;; Extract the type of the instruction's first result and pass along the
|
|
;; instruction as well.
|
|
(decl has_type (Type Inst) Inst)
|
|
(extractor (has_type ty inst)
|
|
(and (result_type ty)
|
|
inst))
|
|
|
|
;; Match a multi-lane type, extracting (# bits per lane, # lanes) from the given
|
|
;; type. Will only match when there is more than one lane.
|
|
(decl multi_lane (u8 u16) Type)
|
|
(extern extractor multi_lane multi_lane)
|
|
|
|
;; Match the instruction that defines the given value, if any.
|
|
(decl def_inst (Inst) Value)
|
|
(extern extractor def_inst def_inst)
|
|
|
|
;; Extract a constant `u64` from a value defined by an `iconst`.
|
|
(decl u64_from_iconst (u64) Value)
|
|
(extractor (u64_from_iconst x)
|
|
(def_inst (iconst (u64_from_imm64 x))))
|
|
|
|
;; Instruction creation helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Emit an instruction.
|
|
;;
|
|
;; This is low-level and side-effectful; it should only be used as an
|
|
;; implementation detail by helpers that preserve the SSA facade themselves.
|
|
|
|
(decl emit (MInst) Unit)
|
|
(extern constructor emit emit)
|
|
|
|
(decl emit_safepoint (MInst) Unit)
|
|
(extern constructor emit_safepoint emit_safepoint)
|
|
|
|
;;;; Helpers for Side-Effectful Instructions Without Results ;;;;;;;;;;;;;;;;;;;
|
|
|
|
(type SideEffectNoResult (enum (Inst (inst MInst))))
|
|
|
|
;; Create an empty `ValueRegs`, but do emit the given side-effectful
|
|
;; instruction.
|
|
(decl value_regs_none (SideEffectNoResult) ValueRegs)
|
|
(rule (value_regs_none (SideEffectNoResult.Inst inst))
|
|
(let ((_ Unit (emit inst)))
|
|
(value_regs_invalid)))
|
|
|
|
;; Similarly, but emit the side-effectful instruction as a safepoint.
|
|
(decl safepoint (SideEffectNoResult) ValueRegs)
|
|
(rule (safepoint (SideEffectNoResult.Inst inst))
|
|
(let ((_ Unit (emit_safepoint inst)))
|
|
(value_regs_invalid)))
|
|
|
|
;;;; Helpers for Working with Flags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Newtype wrapper around `MInst` for instructions that are used for their
|
|
;; effect on flags.
|
|
(type ProducesFlags (enum (ProducesFlags (inst MInst) (result Reg))))
|
|
|
|
;; Newtype wrapper around `MInst` for instructions that consume flags.
|
|
(type ConsumesFlags (enum (ConsumesFlags (inst MInst) (result Reg))))
|
|
|
|
;; Combine flags-producing and -consuming instructions together, ensuring that
|
|
;; they are emitted back-to-back and no other instructions can be emitted
|
|
;; between them and potentially clobber the flags.
|
|
;;
|
|
;; Returns a `ValueRegs` where the first register is the result of the
|
|
;; `ProducesFlags` instruction and the second is the result of the
|
|
;; `ConsumesFlags` instruction.
|
|
(decl with_flags (ProducesFlags ConsumesFlags) ValueRegs)
|
|
(rule (with_flags (ProducesFlags.ProducesFlags producer_inst producer_result)
|
|
(ConsumesFlags.ConsumesFlags consumer_inst consumer_result))
|
|
(let ((_x Unit (emit producer_inst))
|
|
(_y Unit (emit consumer_inst)))
|
|
(value_regs producer_result consumer_result)))
|
|
|
|
;; Like `with_flags` but returns only the result of the consumer operation.
|
|
(decl with_flags_1 (ProducesFlags ConsumesFlags) Reg)
|
|
(rule (with_flags_1 (ProducesFlags.ProducesFlags producer_inst _producer_result)
|
|
(ConsumesFlags.ConsumesFlags consumer_inst consumer_result))
|
|
(let ((_x Unit (emit producer_inst))
|
|
(_y Unit (emit consumer_inst)))
|
|
consumer_result))
|
|
|
|
;; Like `with_flags` but allows two consumers of the same flags. The result is a
|
|
;; `ValueRegs` containing the first consumer's result and then the second
|
|
;; consumer's result.
|
|
(decl with_flags_2 (ProducesFlags ConsumesFlags ConsumesFlags) ValueRegs)
|
|
(rule (with_flags_2 (ProducesFlags.ProducesFlags producer_inst _producer_result)
|
|
(ConsumesFlags.ConsumesFlags consumer_inst_1 consumer_result_1)
|
|
(ConsumesFlags.ConsumesFlags consumer_inst_2 consumer_result_2))
|
|
(let ((_x Unit (emit producer_inst))
|
|
;; Note that the order of emission here is swapped, as this seems
|
|
;; to generate better register allocation for now with fewer
|
|
;; `mov` instructions.
|
|
(_y Unit (emit consumer_inst_2))
|
|
(_z Unit (emit consumer_inst_1)))
|
|
(value_regs consumer_result_1 consumer_result_2)))
|
|
|
|
;;;; Helpers for Working with TrapCode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(decl trap_code_division_by_zero () TrapCode)
|
|
(extern constructor trap_code_division_by_zero trap_code_division_by_zero)
|
|
|
|
(decl trap_code_integer_overflow () TrapCode)
|
|
(extern constructor trap_code_integer_overflow trap_code_integer_overflow)
|
|
|
|
(decl trap_code_bad_conversion_to_integer () TrapCode)
|
|
(extern constructor trap_code_bad_conversion_to_integer trap_code_bad_conversion_to_integer)
|
|
|
|
;;;; Helpers for accessing compilation flags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(decl avoid_div_traps () Type)
|
|
(extern extractor avoid_div_traps avoid_div_traps)
|
|
|
|
;;;; Helpers for accessing instruction data ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Accessor for `FuncRef`.
|
|
|
|
(decl func_ref_data (SigRef ExternalName RelocDistance) FuncRef)
|
|
(extern extractor infallible func_ref_data func_ref_data)
|
|
|
|
;; Accessor for `GobalValue`.
|
|
|
|
(decl symbol_value_data (ExternalName RelocDistance i64) GlobalValue)
|
|
(extern extractor symbol_value_data symbol_value_data)
|
|
|
|
;; Accessor for `RelocDistance`.
|
|
|
|
(decl reloc_distance_near () RelocDistance)
|
|
(extern extractor reloc_distance_near reloc_distance_near)
|
|
|
|
|