;; aarch64 instruction selection and CLIF-to-MachInst lowering. ;; The main lowering constructor term: takes a clif `Inst` and returns the ;; register(s) within which the lowered instruction's result values live. (decl lower (Inst) ValueRegs) ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type ty (iconst (u64_from_imm64 n)))) (value_reg (imm ty n))) ;;;; Rules for `bconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type ty (bconst $false))) (value_reg (imm ty 0))) (rule (lower (has_type ty (bconst $true))) (value_reg (imm ty 1))) ;;;; Rules for `null` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type ty (null))) (value_reg (imm ty 0))) ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller ;; Base case, simply adding things in registers. (rule (lower (has_type (fits_in_64 ty) (iadd x y))) (value_reg (alu_rrr (iadd_op ty) (put_in_reg x) (put_in_reg y)))) ;; Special cases for when one operand is an immediate that fits in 12 bits. (rule (lower (has_type (fits_in_64 ty) (iadd x (imm12_from_value y)))) (value_reg (alu_rr_imm12 (iadd_op ty) (put_in_reg x) y))) (rule (lower (has_type (fits_in_64 ty) (iadd (imm12_from_value x) y))) (value_reg (alu_rr_imm12 (iadd_op ty) (put_in_reg y) x))) ;; Same as the previous special cases, except we can switch the addition to a ;; subtraction if the negated immediate fits in 12 bits. (rule (lower (has_type (fits_in_64 ty) (iadd x (imm12_from_negated_value y)))) (value_reg (alu_rr_imm12 (isub_op ty) (put_in_reg x) y))) (rule (lower (has_type (fits_in_64 ty) (iadd (imm12_from_negated_value x) y))) (value_reg (alu_rr_imm12 (isub_op ty) (put_in_reg y) x))) ;; Special cases for when we're adding an extended register where the extending ;; operation can get folded into the add itself. (rule (lower (has_type (fits_in_64 ty) (iadd x (extended_value_from_value y)))) (value_reg (alu_rr_extend_reg (iadd_op ty) (put_in_reg x) y))) (rule (lower (has_type (fits_in_64 ty) (iadd (extended_value_from_value x) y))) (value_reg (alu_rr_extend_reg (iadd_op ty) (put_in_reg y) x))) ;; Special cases for when we're adding the shift of a different ;; register by a constant amount and the shift can get folded into the add. (rule (lower (has_type (fits_in_64 ty) (iadd x (def_inst (ishl y (def_inst (iconst (lshl_from_imm64 ORR_NOT rd, zero, rm (rule (lower (has_type (fits_in_64 ty) (bnot x))) (value_reg (alu_rrr (orr_not_op ty) (zero_reg) (put_in_reg x)))) ;; Special case to use `AluRRRShift` if it's a `bnot` of a const-left-shifted ;; value. (rule (lower (has_type (fits_in_64 ty) (bnot (def_inst (ishl x (def_inst (iconst (lshl_from_imm64 ;; ;; and masked_amt, amt, ;; sub tmp_sub, masked_amt, ;; sub neg_amt, zero, tmp_sub ; neg ;; lsr val_rshift, val, masked_amt ;; lsl val_lshift, val, neg_amt ;; orr rd, val_lshift val_rshift (decl small_rotr (Type Reg Reg) Reg) (rule (small_rotr ty val amt) (let ( (masked_amt Reg (alu_rr_imm_logic (ALUOp.And32) amt (rotr_mask ty))) (tmp_sub Reg (alu_rr_imm12 (ALUOp.Sub32) masked_amt (u8_into_imm12 (ty_bits ty)))) (neg_amt Reg (alu_rrr (ALUOp.Sub32) (zero_reg) tmp_sub)) (val_rshift Reg (alu_rrr (ALUOp.Lsr32) val masked_amt)) (val_lshift Reg (alu_rrr (ALUOp.Lsl32) val neg_amt)) ) (alu_rrr (ALUOp.Orr32) val_lshift val_rshift))) (decl rotr_mask (Type) ImmLogic) (extern constructor rotr_mask rotr_mask) ;; For a constant amount, we can instead do: ;; ;; rotr rd, val, #amt ;; ;; => ;; ;; lsr val_rshift, val, # ;; lsl val_lshift, val, ;; orr rd, val_lshift, val_rshift (decl small_rotr_imm (Type Reg ImmShift) Reg) (rule (small_rotr_imm ty val amt) (let ( (val_rshift Reg (alu_rr_imm_shift (ALUOp.Lsr32) val amt)) (val_lshift Reg (alu_rr_imm_shift (ALUOp.Lsl32) val (rotr_opposite_amount ty amt))) ) (alu_rrr (ALUOp.Orr32) val_lshift val_rshift))) (decl rotr_opposite_amount (Type ImmShift) ImmShift) (extern constructor rotr_opposite_amount rotr_opposite_amount) ;; General 128-bit case. ;; ;; TODO: much better codegen is possible with a constant amount. (rule (lower (has_type $I128 (rotr x y))) (let ( (val ValueRegs (put_in_regs x)) (amt Reg (value_regs_get (put_in_regs y) 0)) (neg_amt Reg (alu_rrr (ALUOp.Sub64) (imm $I64 128) amt)) (rshift ValueRegs (lower_ushr128 val amt)) (lshift ValueRegs (lower_shl128 val neg_amt)) (hi Reg (alu_rrr (ALUOp.Orr64) (value_regs_get rshift 1) (value_regs_get lshift 1))) (lo Reg (alu_rrr (ALUOp.Orr64) (value_regs_get rshift 0) (value_regs_get lshift 0))) ) (value_regs lo hi)))