1;;;; Instruction definition ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 3(type OperandSize extern (enum Size32 Size64)) 4 5(decl ty_to_operand_size (Type) OperandSize) 6(rule (ty_to_operand_size $I32) (OperandSize.Size32)) 7(rule (ty_to_operand_size $I64) (OperandSize.Size64)) 8 9;; Note: in the instructions below, we order destination registers first and 10;; then source registers afterwards. 11(type MInst 12 (enum 13 ;;;; Pseudo-Instructions ;;;; 14 15 ;; A pseudo-instruction that captures register arguments in vregs. 16 (Args (args VecArgPair)) 17 18 ;; A pseudo-instruction that moves vregs to return registers. 19 (Rets (rets VecRetPair)) 20 21 (DummyUse (reg Reg)) 22 23 ;; Implementation of `br_table`, uses `idx` to jump to one of `targets` or 24 ;; jumps to `default` if it's out-of-bounds. 25 (BrTable 26 (idx XReg) 27 (default MachLabel) 28 (targets BoxVecMachLabel)) 29 30 ;;;; Actual Instructions ;;;; 31 32 ;; Trap if `cond` is true. 33 (TrapIf (cond Cond) (code TrapCode)) 34 35 ;; Nothing. 36 (Nop) 37 38 ;; Move a special register (e.g. sp, fp, lr, etc) in to a general-purpose 39 ;; register. 40 (GetSpecial 41 (dst WritableXReg) 42 (reg XReg)) 43 44 ;; Load an external symbol's address into a register. 45 (LoadExtName (dst WritableXReg) 46 (name BoxExternalName) 47 (offset i64)) 48 49 ;; A direct call to a known callee. 50 (Call (info BoxCallInfo)) 51 52 ;; An indirect call to an unknown callee. 53 (IndirectCall (info BoxCallIndInfo)) 54 55 ;; A direct return-call macro instruction. 56 (ReturnCall (info BoxReturnCallInfo)) 57 58 ;; An indirect return-call macro instruction. 59 (ReturnIndirectCall (info BoxReturnCallIndInfo)) 60 61 ;; An indirect call out to a host-defined function. The host function 62 ;; pointer is the first "argument" of this function call. 63 (IndirectCallHost (info BoxCallIndirectHostInfo)) 64 65 ;; Unconditional jumps. 66 (Jump (label MachLabel)) 67 68 ;; Jump to `then` if `c` is true, otherwise to `else`. 69 (BrIf (cond Cond) (taken MachLabel) (not_taken MachLabel)) 70 71 ;; Load the memory address referenced by `mem` into `dst`. 72 (LoadAddr (dst WritableXReg) (mem Amode)) 73 74 ;; Load `ty` bytes from memory pointed to by `mem` and store in `dst`. 75 ;; 76 ;; How much is written to the register is defined by `ExtKind`. The `flags` 77 ;; control behavior such as endianness. 78 (XLoad (dst WritableXReg) (mem Amode) (ty Type) (flags MemFlags)) 79 (FLoad (dst WritableFReg) (mem Amode) (ty Type) (flags MemFlags)) 80 (VLoad (dst WritableVReg) (mem Amode) (ty Type) (flags MemFlags)) 81 82 ;; Stores. 83 (XStore (mem Amode) (src XReg) (ty Type) (flags MemFlags)) 84 (FStore (mem Amode) (src FReg) (ty Type) (flags MemFlags)) 85 (VStore (mem Amode) (src VReg) (ty Type) (flags MemFlags)) 86 87 ;; A raw pulley instruction generated at compile-time via Pulley's 88 ;; `for_each_op!` macro. This variant has `pulley_*` constructors to 89 ;; emit this instruction and auto-generated methods for other various 90 ;; bits and pieces of boilerplate in the backend. 91 (Raw (raw RawInst)) 92 93 ;; Island generation prior to variable-length instructions. 94 (EmitIsland (space_needed u32)) 95 ) 96) 97 98;; Helper type on conditional branches and traps to represent what the 99;; condition that is being performed is. 100;; 101;; Used in `BrIf` and `TrapIf` above for example. 102(type Cond 103 (enum 104 ;; True if `reg` contains a nonzero value in the low 32-bits. 105 (If32 (reg XReg)) 106 ;; True if `reg` contains a zero in the low 32-bits. 107 (IfNot32 (reg XReg)) 108 109 ;; Conditionals for comparing the low 32-bits of two registers. 110 (IfXeq32 (src1 XReg) (src2 XReg)) 111 (IfXneq32 (src1 XReg) (src2 XReg)) 112 (IfXslt32 (src1 XReg) (src2 XReg)) 113 (IfXslteq32 (src1 XReg) (src2 XReg)) 114 (IfXult32 (src1 XReg) (src2 XReg)) 115 (IfXulteq32 (src1 XReg) (src2 XReg)) 116 117 (IfXeq32I32 (src1 XReg) (src2 i32)) 118 (IfXneq32I32 (src1 XReg) (src2 i32)) 119 (IfXslt32I32 (src1 XReg) (src2 i32)) 120 (IfXslteq32I32 (src1 XReg) (src2 i32)) 121 (IfXult32I32 (src1 XReg) (src2 u32)) 122 (IfXulteq32I32 (src1 XReg) (src2 u32)) 123 (IfXsgt32I32 (src1 XReg) (src2 i32)) 124 (IfXsgteq32I32 (src1 XReg) (src2 i32)) 125 (IfXugt32I32 (src1 XReg) (src2 u32)) 126 (IfXugteq32I32 (src1 XReg) (src2 u32)) 127 128 ;; Conditionals for comparing two 64-bit registers. 129 (IfXeq64 (src1 XReg) (src2 XReg)) 130 (IfXneq64 (src1 XReg) (src2 XReg)) 131 (IfXslt64 (src1 XReg) (src2 XReg)) 132 (IfXslteq64 (src1 XReg) (src2 XReg)) 133 (IfXult64 (src1 XReg) (src2 XReg)) 134 (IfXulteq64 (src1 XReg) (src2 XReg)) 135 136 (IfXeq64I32 (src1 XReg) (src2 i32)) 137 (IfXneq64I32 (src1 XReg) (src2 i32)) 138 (IfXslt64I32 (src1 XReg) (src2 i32)) 139 (IfXslteq64I32 (src1 XReg) (src2 i32)) 140 (IfXult64I32 (src1 XReg) (src2 u32)) 141 (IfXulteq64I32 (src1 XReg) (src2 u32)) 142 (IfXsgt64I32 (src1 XReg) (src2 i32)) 143 (IfXsgteq64I32 (src1 XReg) (src2 i32)) 144 (IfXugt64I32 (src1 XReg) (src2 u32)) 145 (IfXugteq64I32 (src1 XReg) (src2 u32)) 146 ) 147) 148 149(decl cond_invert (Cond) Cond) 150(extern constructor cond_invert cond_invert) 151 152(decl raw_inst_to_inst (RawInst) MInst) 153(rule (raw_inst_to_inst inst) (MInst.Raw inst)) 154(convert RawInst MInst raw_inst_to_inst) 155 156(type U6 (primitive U6)) 157(type BoxCallInfo (primitive BoxCallInfo)) 158(type BoxCallIndInfo (primitive BoxCallIndInfo)) 159(type BoxReturnCallInfo (primitive BoxReturnCallInfo)) 160(type BoxReturnCallIndInfo (primitive BoxReturnCallIndInfo)) 161(type UpperXRegSet (primitive UpperXRegSet)) 162(type BoxCallIndirectHostInfo (primitive BoxCallIndirectHostInfo)) 163 164;;;; Address Modes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 165 166(type ExtKind (enum None Sign32 Sign64 Zero32 Zero64)) 167(type VExtKind (enum None S8x8 U8x8 S16x4 U16x4 S32x2 U32x2)) 168 169;; Helper to convert a `(Value Offset32)` to `(Value i32)` while peeling off 170;; constant addition within the first `Value` into the static offset, if 171;; possible. 172;; 173;; Note that ideally this wouldn't be necessary and we could rely on the egraph 174;; pass to do this but that's not implemented at this time. 175(type ValueOffset (enum (Both (value Value) (offset i32)))) 176(decl pure amode_base (Value Offset32) ValueOffset) 177(rule (amode_base addr (offset32 offset)) (ValueOffset.Both addr offset)) 178(rule 1 (amode_base (iadd addr (i32_from_iconst b)) (offset32 offset)) 179 (if-let new_offset (i32_checked_add b offset)) 180 (ValueOffset.Both addr new_offset)) 181 182(type StackAMode extern (enum)) 183 184(type Amode 185 (enum 186 (SpOffset (offset i32)) 187 (RegOffset (base XReg) (offset i32)) 188 (Stack (amode StackAMode)) 189 ) 190) 191 192(decl amode (Value Offset32) Amode) 193(rule (amode addr offset) 194 (if-let (ValueOffset.Both a o) (amode_base addr offset)) 195 (Amode.RegOffset a o)) 196 197;;; ISLE representation of the `AddrO32` ("*_o32") addressing mode in Pulley. 198(type AddrO32 199 (enum 200 (Base 201 (addr XReg) 202 (offset i32)))) 203 204;; Constructor for the `AddrO32` type used in `*_o32` loads/stores 205(decl addro32 (Value Offset32) AddrO32) 206(rule (addro32 addr offset) 207 (if-let (ValueOffset.Both reg off32) (amode_base addr offset)) 208 (AddrO32.Base reg off32)) 209 210;;; ISLE representation of the `AddrZ` ("*_z") addressing mode in Pulley. 211(type AddrZ 212 (enum 213 (Base 214 (addr XReg) 215 (offset i32)))) 216 217;; Constructor for the `AddrZ` type used in `*_z` loads/stores 218(decl addrz (Value Offset32) AddrZ) 219(rule (addrz addr offset) 220 (if-let (ValueOffset.Both reg off32) (amode_base addr offset)) 221 (AddrZ.Base reg off32)) 222 223;;; ISLE representation of the `AddrG32` ("*_g32") addressing mode in Pulley. 224(type AddrG32 225 (enum 226 (RegisterBound 227 (host_heap_base XReg) 228 (host_heap_bound XReg) 229 (wasm_addr XReg) 230 (offset u16)))) 231 232;;; ISLE representation of the `AddrG32Bne` ("*_g32bne") addressing mode in Pulley. 233(type AddrG32Bne 234 (enum 235 (BoundNe 236 (host_heap_base XReg) 237 (host_heap_bound_addr XReg) 238 (host_heap_bound_offset u8) 239 (wasm_addr XReg) 240 (offset u8)))) 241 242;; Helper to determine the endianness of `MemFlags` taking the current target 243;; into account. 244(decl pure endianness (MemFlags) Endianness) 245(extern constructor endianness endianness) 246(type Endianness extern (enum Little Big)) 247 248(decl pure is_native_endianness (Endianness) bool) 249(extern constructor is_native_endianness is_native_endianness) 250 251;; Partial constructor and type representing a "sinkable load" which can be 252;; moved into another instruction. Note that `SinkableLoad` should not be used 253;; as-is and should instead be converted to a `SunkLoad`. 254;; 255;; To be a sinkable load the load must pass: 256;; 257;; * The `is_sinkable_inst` shared amongst backends test must be `true` 258;; * The load must be in "native endianness" 259;; * The static offset must fit in an unsigned 8-bit integer. 260;; 261;; If the last two requirements here are too restrictive then multiple helpers 262;; might be needed in the future. 263(type SinkableLoad (enum (Load (inst Inst) (ty Type) (addr Value) (offset u8)))) 264(decl pure partial sinkable_load (Value) SinkableLoad) 265(rule (sinkable_load value @ (value_type ty)) 266 (if-let inst @ (load flags addr (offset32 offset)) (is_sinkable_inst value)) 267 (if-let true (is_native_endianness (endianness flags))) 268 (if-let true (memflags_nontrapping flags)) 269 (if-let offset8 (i32_try_into_u8 offset)) 270 (SinkableLoad.Load inst ty addr offset8)) 271 272;; Representation of a "sunk load" where once this is created it must be used. 273;; 274;; This is paired with `sinkable_load` above where that's used in an `if-let` 275;; and then once the rule is selected this is used to commit to using the load. 276;; Callers will likely match on `SunkLoad` itself to extract the 277;; type/value/offset that the load matches. 278(type SunkLoad (enum (Load (ty Type) (addr Value) (offset u8)))) 279(decl sink_load (SinkableLoad) SunkLoad) 280(rule (sink_load (SinkableLoad.Load inst ty addr offset)) 281 (let ((_ Unit (sink_inst inst))) 282 (SunkLoad.Load ty addr offset))) 283 284(convert SinkableLoad SunkLoad sink_load) 285 286;; Helper for determining what the pointer width of the host is. 287(type PointerWidth extern (enum PointerWidth32 PointerWidth64)) 288(decl pure pointer_width () PointerWidth) 289(extern constructor pointer_width pointer_width) 290 291(decl pure memflags_nontrapping (MemFlags) bool) 292(extern constructor memflags_nontrapping memflags_nontrapping) 293 294(decl pure memflags_is_wasm (MemFlags) bool) 295(extern constructor memflags_is_wasm memflags_is_wasm) 296 297;; Helper type to represent a "pending" `AddrG32` value. 298(type G32 (enum (All (heap_base Value) (heap_bound Value) (wasm_addr Value) (offset u16)))) 299 300;; Auto-conversion from `G32` to `AddrG32`. 301(decl gen_addrg32 (G32) AddrG32) 302(rule (gen_addrg32 (G32.All base bound wasm offset)) 303 (AddrG32.RegisterBound base bound wasm offset)) 304(convert G32 AddrG32 gen_addrg32) 305 306;; Helper type to represent a "pending" `AddrG32Bne` value. 307(type G32Bne (enum (All (heap_base Value) (heap_bound SinkableLoad) (wasm_addr Value) (offset u8)))) 308 309;; Auto-conversion from `G32Bne` to `AddrG32Bne`. 310(decl gen_addrg32bne (G32Bne) AddrG32Bne) 311(rule (gen_addrg32bne (G32Bne.All base heap_bound_load wasm offset)) 312 (gen_addrg32bne_for_real base heap_bound_load wasm offset)) 313(convert G32Bne AddrG32Bne gen_addrg32bne) 314 315;; Small workaround to pattern-match `SunkLoad` here. 316(decl gen_addrg32bne_for_real (XReg SunkLoad XReg u8) AddrG32Bne) 317(rule (gen_addrg32bne_for_real base (SunkLoad.Load _ bound_addr bound_offset) wasm_addr offset) 318 (AddrG32Bne.BoundNe base bound_addr bound_offset wasm_addr offset)) 319 320;; Helper to extract `G32Bne` from `G32` if applicable. 321;; 322;; This is possible when the heap bound is itself a sinkable load which can get 323;; folded into `G32Bne`. 324(decl pure partial addrg32bne (G32) G32Bne) 325(rule (addrg32bne (G32.All heap_base heap_bound wasm_addr offset)) 326 (if-let heap_bound_load (sinkable_load heap_bound)) 327 (if-let offset8 (u16_try_into_u8 offset)) 328 (G32Bne.All heap_base heap_bound_load wasm_addr offset8)) 329 330;; Main entrypoint for matching a `G32` address which can be used in `*_g32` 331;; pulley instructions for loads/stores. 332;; 333;; Pulley loads/stores are emitted as fallible loads/stores where the only 334;; defined trapping address is null. That's modeled as a load-from-`select` 335;; where an out-of-bounds condition yields null. 336;; 337;; Here the top-layer of this rule matches the `(select ...)` node where 0 is 338;; used if the oob condition is true. Otherwise the raw address is used for the 339;; load/store. 340(decl pure partial wasm_g32 (Value Offset32 MemFlags Type) G32) 341(rule (wasm_g32 (select oob (u64_from_iconst 0) raw_addr) (offset32 0) flags ty) 342 ;; This must be a wasm load/store according to `MemFlags`, namely that it's 343 ;; got a "HEAP_OUT_OF_BOUNDS" trap code and it's little-endian. 344 (if-let true (memflags_is_wasm flags)) 345 ;; Peel off the static wasm offset from `raw_addr`, if one is present. If 346 ;; one isn't present then `heap_offset` will be zero. This handles the `N` 347 ;; in wasm instructions `i32.load offset=N`. 348 (if-let (HostOffset.All host_addr heap_offset) (host_offset raw_addr)) 349 ;; Next see if the `oob` and `raw_addr` combination match. This will attempt 350 ;; extract a full bounds check from these values. If everything succeeds the 351 ;; final step is then to extract an 8-bit offset of the load/store operation, 352 ;; if appplicable, assuming that the constants used in various places all line 353 ;; up just right. 354 (if-let (OobSelect.All base bound wasm_addr access_size_plus_offset) 355 (wasm_oob_select oob host_addr)) 356 (if-let offset16 (g32_offset heap_offset ty access_size_plus_offset)) 357 (G32.All base bound wasm_addr offset16)) 358 359;; Host helper to do the math to extract the offset here. 360;; 361;; Ensures that the first argument, the heap offset on the load, plus the size 362;; of the type equals the third argument, the bounds-checked static offset. If 363;; the offset then fits within `u16` it's returned. 364(decl pure partial g32_offset (i32 Type u64) u16) 365(extern constructor g32_offset g32_offset) 366 367;; Helper used in `wasm_g32` above to extract `(iadd addr N)` where `N` is a 368;; constant. If there is no constant then the constant 0 is returned instead. 369;; 370;; Note that this also requires `addr` itself to be an `iadd` internally to 371;; represent the host address plus the guest offset. If `addr` isn't internally 372;; an `iadd` then the `N` here is instead probably the static address in the 373;; guest. 374(decl pure host_offset (Value) HostOffset) 375(type HostOffset (enum (All (a Value) (b i32)))) 376(rule 0 (host_offset val) (HostOffset.All val 0)) 377(rule 1 (host_offset (iadd val @ (iadd _ _) (i32_from_iconst n))) (HostOffset.All val n)) 378(rule 2 (host_offset (iadd (i32_from_iconst n) val @ (iadd _ _))) (HostOffset.All val n)) 379 380;; Helper to test whether the first argument `oob` contains a condition for 381;; matching whether second argument `addr` is out-of-bounds. Searches for a 382;; variety of structures that optimizations or the frontend may produce. 383(decl pure partial wasm_oob_select (Value Value) OobSelect) 384(type OobSelect (enum (All (a Value) (b Value) (c Value) (d u64)))) 385 386;; 32-bit hosts: search either side of the `iadd` for the base address within 387;; `oob` to see if it's a matching condition. 388(rule 0 (wasm_oob_select oob (iadd base wasm_addr @ (value_type $I32))) 389 (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr oob)) 390 (if-let (PointerWidth.PointerWidth32) (pointer_width)) 391 (OobSelect.All base bound wasm_addr n)) 392(rule 1 (wasm_oob_select oob (iadd wasm_addr @ (value_type $I32) base)) 393 (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr oob)) 394 (if-let (PointerWidth.PointerWidth32) (pointer_width)) 395 (OobSelect.All base bound wasm_addr n)) 396 397;; 64-bit hosts: also search either side, but the wasm address must also be 398;; a `uextend` from a 32-bit value. 399(rule 0 (wasm_oob_select oob (iadd base wasm_addr_ext @ (value_type $I64))) 400 (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr_ext oob)) 401 (if-let wasm_addr (wasm32_addr_for_host64 wasm_addr_ext)) 402 (if-let (PointerWidth.PointerWidth64) (pointer_width)) 403 (OobSelect.All base bound wasm_addr n)) 404(rule 1 (wasm_oob_select oob (iadd wasm_addr_ext @ (value_type $I64) base)) 405 (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr_ext oob)) 406 (if-let wasm_addr (wasm32_addr_for_host64 wasm_addr_ext)) 407 (if-let (PointerWidth.PointerWidth64) (pointer_width)) 408 (OobSelect.All base bound wasm_addr n)) 409 410;; Helper to extract a 32-bit `Value` from a 64-bit input. The returned value, 411;; if one is returned, can losslessly represent the original value when zero 412;; extended from 32-bits. That means that this only matches `(uextend ...)` or 413;; an `iconst` which already fits in 32 bits. 414(decl pure partial wasm32_addr_for_host64 (Value) Value) 415(rule (wasm32_addr_for_host64 (uextend addr @ (value_type $I32))) addr) 416(rule (wasm32_addr_for_host64 val @ (u32_from_iconst _)) val) 417 418;; Helper to search for the first argument, `wasm_addr`, within the oob 419;; condition second argument, `oob`. It should appear as an integer comparison 420;; of the address against a particular bound. 421(decl pure partial wasm_oob_cond (Value Value) OobCond) 422(type OobCond (enum (All (a Value) (b u64)))) 423(rule (wasm_oob_cond wasm_addr (ugt wasm_addr (isub bound (u64_from_iconst n)))) 424 (OobCond.All bound n)) 425(rule (wasm_oob_cond wasm_addr (ult (isub bound (u64_from_iconst n)) wasm_addr)) 426 (OobCond.All bound n)) 427 428;;;; Newtypes for Different Register Classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 429 430(type XReg (primitive XReg)) 431(type WritableXReg (primitive WritableXReg)) 432 433(type FReg (primitive FReg)) 434(type WritableFReg (primitive WritableFReg)) 435 436(type VReg (primitive VReg)) 437(type WritableVReg (primitive WritableVReg)) 438 439;; Construct a new `XReg` from a `Reg`. 440;; 441;; Asserts that the register has an integer RegClass. 442(decl xreg_new (Reg) XReg) 443(extern constructor xreg_new xreg_new) 444(convert Reg XReg xreg_new) 445 446;; Construct a new `WritableXReg` from a `WritableReg`. 447;; 448;; Asserts that the register has an integer RegClass. 449(decl writable_xreg_new (WritableReg) WritableXReg) 450(extern constructor writable_xreg_new writable_xreg_new) 451(convert WritableReg WritableXReg writable_xreg_new) 452 453;; Put a value into a XReg. 454;; 455;; Asserts that the value goes into a XReg. 456(decl put_in_xreg (Value) XReg) 457(rule (put_in_xreg val) (xreg_new (put_in_reg val))) 458(convert Value XReg put_in_xreg) 459 460;; Construct an `InstOutput` out of a single XReg register. 461(decl output_xreg (XReg) InstOutput) 462(rule (output_xreg x) (output_reg x)) 463(convert XReg InstOutput output_xreg) 464 465;; Convert a `WritableXReg` to an `XReg`. 466(decl pure writable_xreg_to_xreg (WritableXReg) XReg) 467(extern constructor writable_xreg_to_xreg writable_xreg_to_xreg) 468(convert WritableXReg XReg writable_xreg_to_xreg) 469 470;; Convert a `WritableXReg` to an `WritableReg`. 471(decl pure writable_xreg_to_writable_reg (WritableXReg) WritableReg) 472(extern constructor writable_xreg_to_writable_reg writable_xreg_to_writable_reg) 473(convert WritableXReg WritableReg writable_xreg_to_writable_reg) 474 475;; Convert a `WritableXReg` to an `Reg`. 476(decl pure writable_xreg_to_reg (WritableXReg) Reg) 477(rule (writable_xreg_to_reg x) (writable_xreg_to_writable_reg x)) 478(convert WritableXReg Reg writable_xreg_to_reg) 479 480;; Convert an `XReg` to a `Reg`. 481(decl pure xreg_to_reg (XReg) Reg) 482(extern constructor xreg_to_reg xreg_to_reg) 483(convert XReg Reg xreg_to_reg) 484 485;; Convert a `XReg` to a `ValueRegs`. 486(decl xreg_to_value_regs (XReg) ValueRegs) 487(rule (xreg_to_value_regs x) (value_reg x)) 488(convert XReg ValueRegs xreg_to_reg) 489 490;; Convert a `WritableXReg` to a `ValueRegs`. 491(decl writable_xreg_to_value_regs (WritableXReg) ValueRegs) 492(rule (writable_xreg_to_value_regs x) (value_reg x)) 493(convert WritableXReg ValueRegs writable_xreg_to_value_regs) 494 495;; Allocates a new `WritableXReg`. 496(decl temp_writable_xreg () WritableXReg) 497(rule (temp_writable_xreg) (temp_writable_reg $I64)) 498 499;; Construct a new `FReg` from a `Reg`. 500;; 501;; Asserts that the register has a Float RegClass. 502(decl freg_new (Reg) FReg) 503(extern constructor freg_new freg_new) 504(convert Reg FReg freg_new) 505 506;; Construct a new `WritableFReg` from a `WritableReg`. 507;; 508;; Asserts that the register has a Float RegClass. 509(decl writable_freg_new (WritableReg) WritableFReg) 510(extern constructor writable_freg_new writable_freg_new) 511(convert WritableReg WritableFReg writable_freg_new) 512 513;; Put a value into a FReg. 514;; 515;; Asserts that the value goes into a FReg. 516(decl put_in_freg (Value) FReg) 517(rule (put_in_freg val) (freg_new (put_in_reg val))) 518(convert Value FReg put_in_freg) 519 520;; Construct an `InstOutput` out of a single FReg register. 521(decl output_freg (FReg) InstOutput) 522(rule (output_freg x) (output_reg x)) 523(convert FReg InstOutput output_freg) 524 525;; Convert a `WritableFReg` to an `FReg`. 526(decl pure writable_freg_to_freg (WritableFReg) FReg) 527(extern constructor writable_freg_to_freg writable_freg_to_freg) 528(convert WritableFReg FReg writable_freg_to_freg) 529 530;; Convert a `WritableFReg` to an `WritableReg`. 531(decl pure writable_freg_to_writable_reg (WritableFReg) WritableReg) 532(extern constructor writable_freg_to_writable_reg writable_freg_to_writable_reg) 533(convert WritableFReg WritableReg writable_freg_to_writable_reg) 534 535;; Convert a `WritableFReg` to an `Reg`. 536(decl pure writable_freg_to_reg (WritableFReg) Reg) 537(rule (writable_freg_to_reg x) (writable_freg_to_writable_reg x)) 538(convert WritableFReg Reg writable_freg_to_reg) 539 540;; Convert an `FReg` to a `Reg`. 541(decl pure freg_to_reg (FReg) Reg) 542(extern constructor freg_to_reg freg_to_reg) 543(convert FReg Reg freg_to_reg) 544 545;; Convert a `FReg` to a `ValueRegs`. 546(decl freg_to_value_regs (FReg) ValueRegs) 547(rule (freg_to_value_regs x) (value_reg x)) 548(convert FReg ValueRegs xreg_to_reg) 549 550;; Convert a `WritableFReg` to a `ValueRegs`. 551(decl writable_freg_to_value_regs (WritableFReg) ValueRegs) 552(rule (writable_freg_to_value_regs x) (value_reg x)) 553(convert WritableFReg ValueRegs writable_freg_to_value_regs) 554 555;; Allocates a new `WritableFReg`. 556(decl temp_writable_freg () WritableFReg) 557(rule (temp_writable_freg) (temp_writable_reg $F64)) 558 559;; Construct a new `VReg` from a `Reg`. 560;; 561;; Asserts that the register has a Vector RegClass. 562(decl vreg_new (Reg) VReg) 563(extern constructor vreg_new vreg_new) 564(convert Reg VReg vreg_new) 565 566;; Construct a new `WritableVReg` from a `WritableReg`. 567;; 568;; Asserts that the register has a Vector RegClass. 569(decl writable_vreg_new (WritableReg) WritableVReg) 570(extern constructor writable_vreg_new writable_vreg_new) 571(convert WritableReg WritableVReg writable_vreg_new) 572 573;; Put a value into a VReg. 574;; 575;; Asserts that the value goes into a VReg. 576(decl put_in_vreg (Value) VReg) 577(rule (put_in_vreg val) (vreg_new (put_in_reg val))) 578(convert Value VReg put_in_vreg) 579 580;; Construct an `InstOutput` out of a single VReg register. 581(decl output_vreg (VReg) InstOutput) 582(rule (output_vreg x) (output_reg x)) 583(convert VReg InstOutput output_vreg) 584 585;; Convert a `WritableVReg` to an `VReg`. 586(decl pure writable_vreg_to_vreg (WritableVReg) VReg) 587(extern constructor writable_vreg_to_vreg writable_vreg_to_vreg) 588(convert WritableVReg VReg writable_vreg_to_vreg) 589 590;; Convert a `WritableVReg` to an `WritableReg`. 591(decl pure writable_vreg_to_writable_reg (WritableVReg) WritableReg) 592(extern constructor writable_vreg_to_writable_reg writable_vreg_to_writable_reg) 593(convert WritableVReg WritableReg writable_vreg_to_writable_reg) 594 595;; Convert a `WritableVReg` to an `Reg`. 596(decl pure writable_vreg_to_reg (WritableVReg) Reg) 597(rule (writable_vreg_to_reg x) (writable_vreg_to_writable_reg x)) 598(convert WritableVReg Reg writable_vreg_to_reg) 599 600;; Convert an `VReg` to a `Reg`. 601(decl pure vreg_to_reg (VReg) Reg) 602(extern constructor vreg_to_reg vreg_to_reg) 603(convert VReg Reg vreg_to_reg) 604 605;; Convert a `VReg` to a `ValueRegs`. 606(decl vreg_to_value_regs (VReg) ValueRegs) 607(rule (vreg_to_value_regs x) (value_reg x)) 608(convert VReg ValueRegs xreg_to_reg) 609 610;; Convert a `WritableVReg` to a `ValueRegs`. 611(decl writable_vreg_to_value_regs (WritableVReg) ValueRegs) 612(rule (writable_vreg_to_value_regs x) (value_reg x)) 613(convert WritableVReg ValueRegs writable_vreg_to_value_regs) 614 615;; Allocates a new `WritableVReg`. 616(decl temp_writable_vreg () WritableVReg) 617(rule (temp_writable_vreg) (temp_writable_reg $I8X16)) 618 619;;;; Materializing Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 620 621;; Lower a constant into a register. 622(decl imm (i64) Reg) 623 624;; Special case some instructions with no immediates 625(rule 6 (imm 0) (pulley_xzero)) 626(rule 5 (imm 1) (pulley_xone)) 627 628;; For integer constants use the smallest instructions we can that fits the 629;; desired value 630(rule 4 (imm (i8_from_i64 imm)) (pulley_xconst8 imm)) 631(rule 3 (imm (i16_from_i64 imm)) (pulley_xconst16 imm)) 632(rule 2 (imm (i32_from_i64 imm)) (pulley_xconst32 imm)) 633(rule 1 (imm imm) (pulley_xconst64 imm)) 634 635;;;; Instruction Constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 636 637(decl pulley_trap_if (Cond TrapCode) SideEffectNoResult) 638(rule (pulley_trap_if cond code) 639 (SideEffectNoResult.Inst (MInst.TrapIf cond code))) 640 641(decl sp_reg () XReg) 642(extern constructor sp_reg sp_reg) 643 644(decl pulley_get_special (XReg) XReg) 645(rule (pulley_get_special reg) 646 (let ((dst WritableXReg (temp_writable_xreg)) 647 (_ Unit (emit (MInst.GetSpecial dst reg)))) 648 dst)) 649 650(decl pulley_jump (MachLabel) SideEffectNoResult) 651(rule (pulley_jump label) 652 (SideEffectNoResult.Inst (MInst.Jump label))) 653 654(decl pulley_br_if (Cond MachLabel MachLabel) SideEffectNoResult) 655(rule (pulley_br_if cond taken not_taken) 656 (SideEffectNoResult.Inst (MInst.BrIf cond taken not_taken))) 657 658(decl pulley_xload (Amode Type MemFlags) XReg) 659(rule (pulley_xload amode ty flags) 660 (let ((dst WritableXReg (temp_writable_xreg)) 661 (_ Unit (emit (MInst.XLoad dst amode ty flags)))) 662 dst)) 663 664(decl pulley_xstore (Amode XReg Type MemFlags) SideEffectNoResult) 665(rule (pulley_xstore amode src ty flags) 666 (SideEffectNoResult.Inst (MInst.XStore amode src ty flags))) 667 668(decl pulley_fload (Amode Type MemFlags) FReg) 669(rule (pulley_fload amode ty flags) 670 (let ((dst WritableFReg (temp_writable_freg)) 671 (_ Unit (emit (MInst.FLoad dst amode ty flags)))) 672 dst)) 673 674(decl pulley_fstore (Amode FReg Type MemFlags) SideEffectNoResult) 675(rule (pulley_fstore amode src ty flags) 676 (SideEffectNoResult.Inst (MInst.FStore amode src ty flags))) 677 678(decl pulley_vload (Amode Type MemFlags) VReg) 679(rule (pulley_vload amode ty flags) 680 (let ((dst WritableVReg (temp_writable_vreg)) 681 (_ Unit (emit (MInst.VLoad dst amode ty flags)))) 682 dst)) 683 684(decl pulley_vstore (Amode VReg Type MemFlags) SideEffectNoResult) 685(rule (pulley_vstore amode src ty flags) 686 (SideEffectNoResult.Inst (MInst.VStore amode src ty flags))) 687 688(decl gen_br_table (XReg MachLabel BoxVecMachLabel) Unit) 689(rule (gen_br_table idx default labels) 690 (emit (MInst.BrTable idx default labels))) 691 692;;;; Helpers for Emitting Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 693 694(decl gen_call_info (Sig ExternalName CallArgList CallRetList OptionTryCallInfo) BoxCallInfo) 695(extern constructor gen_call_info gen_call_info) 696 697(decl gen_call_ind_info (Sig Reg CallArgList CallRetList OptionTryCallInfo) BoxCallIndInfo) 698(extern constructor gen_call_ind_info gen_call_ind_info) 699 700(decl gen_call_host_info (Sig ExternalName CallArgList CallRetList OptionTryCallInfo) BoxCallIndirectHostInfo) 701(extern constructor gen_call_host_info gen_call_host_info) 702 703(decl gen_return_call_info (Sig ExternalName CallArgList) BoxReturnCallInfo) 704(extern constructor gen_return_call_info gen_return_call_info) 705 706(decl gen_return_call_ind_info (Sig Reg CallArgList) BoxReturnCallIndInfo) 707(extern constructor gen_return_call_ind_info gen_return_call_ind_info) 708 709;; Helper for creating `MInst.Call` instructions. 710(decl call_impl (BoxCallInfo) SideEffectNoResult) 711(rule (call_impl info) 712 (SideEffectNoResult.Inst (MInst.Call info))) 713 714;; Helper for creating `MInst.IndirectCall` instructions. 715(decl indirect_call_impl (BoxCallIndInfo) SideEffectNoResult) 716(rule (indirect_call_impl info) 717 (SideEffectNoResult.Inst (MInst.IndirectCall info))) 718 719;; Helper for creating `MInst.IndirectCallHost` instructions. 720(decl indirect_call_host_impl (BoxCallIndirectHostInfo) SideEffectNoResult) 721(rule (indirect_call_host_impl info) 722 (SideEffectNoResult.Inst (MInst.IndirectCallHost info))) 723 724;; Helper for creating `MInst.ReturnCall` instructions. 725(decl return_call_impl (BoxReturnCallInfo) SideEffectNoResult) 726(rule (return_call_impl info) 727 (SideEffectNoResult.Inst (MInst.ReturnCall info))) 728 729;; Helper for creating `MInst.ReturnIndirectCall` instructions. 730(decl return_indirect_call_impl (BoxReturnCallIndInfo) SideEffectNoResult) 731(rule (return_indirect_call_impl info) 732 (SideEffectNoResult.Inst (MInst.ReturnIndirectCall info))) 733 734;;;; Helpers for Sign extension ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 735 736;; Sign extend a `Value` to at least 32-bit 737(decl zext32 (Value) XReg) 738(rule (zext32 val @ (value_type $I8)) (pulley_zext8 val)) 739(rule (zext32 val @ (value_type $I16)) (pulley_zext16 val)) 740(rule (zext32 val @ (value_type $I32)) val) 741(rule (zext32 val @ (value_type $I64)) val) 742 743;; Same as `zext32` but for sign-extension 744(decl sext32 (Value) XReg) 745(rule (sext32 val @ (value_type $I8)) (pulley_sext8 val)) 746(rule (sext32 val @ (value_type $I16)) (pulley_sext16 val)) 747(rule (sext32 val @ (value_type $I32)) val) 748(rule (sext32 val @ (value_type $I64)) val) 749 750;; Sign extend a `Value` to at least 64-bit 751(decl zext64 (Value) XReg) 752(rule (zext64 val @ (value_type $I8)) (pulley_zext8 val)) 753(rule (zext64 val @ (value_type $I16)) (pulley_zext16 val)) 754(rule (zext64 val @ (value_type $I32)) (pulley_zext32 val)) 755(rule (zext64 val @ (value_type $I64)) val) 756 757;; Same as `zext64` but for sign-extension 758(decl sext64 (Value) XReg) 759(rule (sext64 val @ (value_type $I8)) (pulley_sext8 val)) 760(rule (sext64 val @ (value_type $I16)) (pulley_sext16 val)) 761(rule (sext64 val @ (value_type $I32)) (pulley_sext32 val)) 762(rule (sext64 val @ (value_type $I64)) val) 763