1 //! Lowering rules for AArch64.
2 //!
3 //! TODO: opportunities for better code generation:
4 //!
5 //! - Smarter use of addressing modes. Recognize a+SCALE*b patterns. Recognize
6 //!   pre/post-index opportunities.
7 //!
8 //! - Floating-point immediates (FIMM instruction).
9 
10 use crate::ir::Inst as IRInst;
11 use crate::ir::condcodes::{FloatCC, IntCC};
12 use crate::ir::{Opcode, Value};
13 use crate::isa::aarch64::AArch64Backend;
14 use crate::isa::aarch64::inst::*;
15 use crate::machinst::lower::*;
16 use crate::machinst::*;
17 
18 pub mod isle;
19 
20 //============================================================================
21 // Lowering: convert instruction inputs to forms that we can use.
22 
get_as_extended_value(ctx: &mut Lower<Inst>, val: Value) -> Option<(Value, ExtendOp)>23 fn get_as_extended_value(ctx: &mut Lower<Inst>, val: Value) -> Option<(Value, ExtendOp)> {
24     let inputs = ctx.get_value_as_source_or_const(val);
25     let (insn, n) = inputs.inst.as_inst()?;
26     if n != 0 {
27         return None;
28     }
29     let op = ctx.data(insn).opcode();
30     let out_ty = ctx.output_ty(insn, 0);
31     let out_bits = ty_bits(out_ty);
32 
33     // Is this a zero-extend or sign-extend and can we handle that with a register-mode operator?
34     if op == Opcode::Uextend || op == Opcode::Sextend {
35         let sign_extend = op == Opcode::Sextend;
36         let inner_ty = ctx.input_ty(insn, 0);
37         let inner_bits = ty_bits(inner_ty);
38         assert!(inner_bits < out_bits);
39         let extendop = match (sign_extend, inner_bits) {
40             (true, 8) => ExtendOp::SXTB,
41             (false, 8) => ExtendOp::UXTB,
42             (true, 16) => ExtendOp::SXTH,
43             (false, 16) => ExtendOp::UXTH,
44             (true, 32) => ExtendOp::SXTW,
45             (false, 32) => ExtendOp::UXTW,
46             _ => unreachable!(),
47         };
48         return Some((ctx.input_as_value(insn, 0), extendop));
49     }
50 
51     None
52 }
53 
lower_condcode(cc: IntCC) -> Cond54 pub(crate) fn lower_condcode(cc: IntCC) -> Cond {
55     match cc {
56         IntCC::Equal => Cond::Eq,
57         IntCC::NotEqual => Cond::Ne,
58         IntCC::SignedGreaterThanOrEqual => Cond::Ge,
59         IntCC::SignedGreaterThan => Cond::Gt,
60         IntCC::SignedLessThanOrEqual => Cond::Le,
61         IntCC::SignedLessThan => Cond::Lt,
62         IntCC::UnsignedGreaterThanOrEqual => Cond::Hs,
63         IntCC::UnsignedGreaterThan => Cond::Hi,
64         IntCC::UnsignedLessThanOrEqual => Cond::Ls,
65         IntCC::UnsignedLessThan => Cond::Lo,
66     }
67 }
68 
lower_fp_condcode(cc: FloatCC) -> Cond69 pub(crate) fn lower_fp_condcode(cc: FloatCC) -> Cond {
70     // Refer to `codegen/shared/src/condcodes.rs` and to the `FCMP` AArch64 docs.
71     // The FCMP instruction sets:
72     //               NZCV
73     // - PCSR.NZCV = 0011 on UN (unordered),
74     //               0110 on EQ,
75     //               1000 on LT,
76     //               0010 on GT.
77     match cc {
78         // EQ | LT | GT. Vc => V clear.
79         FloatCC::Ordered => Cond::Vc,
80         // UN. Vs => V set.
81         FloatCC::Unordered => Cond::Vs,
82         // EQ. Eq => Z set.
83         FloatCC::Equal => Cond::Eq,
84         // UN | LT | GT. Ne => Z clear.
85         FloatCC::NotEqual => Cond::Ne,
86         // LT | GT.
87         FloatCC::OrderedNotEqual => unimplemented!(),
88         //  UN | EQ
89         FloatCC::UnorderedOrEqual => unimplemented!(),
90         // LT. Mi => N set.
91         FloatCC::LessThan => Cond::Mi,
92         // LT | EQ. Ls => C clear or Z set.
93         FloatCC::LessThanOrEqual => Cond::Ls,
94         // GT. Gt => Z clear, N = V.
95         FloatCC::GreaterThan => Cond::Gt,
96         // GT | EQ. Ge => N = V.
97         FloatCC::GreaterThanOrEqual => Cond::Ge,
98         // UN | LT. Lt => N != V.
99         FloatCC::UnorderedOrLessThan => Cond::Lt,
100         // UN | LT | EQ. Le => not (Z clear, N = V).
101         FloatCC::UnorderedOrLessThanOrEqual => Cond::Le,
102         // UN | GT. Hi => C set, Z clear.
103         FloatCC::UnorderedOrGreaterThan => Cond::Hi,
104         // UN | GT | EQ. Pl => N clear.
105         FloatCC::UnorderedOrGreaterThanOrEqual => Cond::Pl,
106     }
107 }
108 
109 //=============================================================================
110 // Lowering-backend trait implementation.
111 
112 impl LowerBackend for AArch64Backend {
113     type MInst = Inst;
114 
lower(&self, ctx: &mut Lower<Inst>, ir_inst: IRInst) -> Option<InstOutput>115     fn lower(&self, ctx: &mut Lower<Inst>, ir_inst: IRInst) -> Option<InstOutput> {
116         isle::lower(ctx, self, ir_inst)
117     }
118 
lower_branch( &self, ctx: &mut Lower<Inst>, ir_inst: IRInst, targets: &[MachLabel], ) -> Option<()>119     fn lower_branch(
120         &self,
121         ctx: &mut Lower<Inst>,
122         ir_inst: IRInst,
123         targets: &[MachLabel],
124     ) -> Option<()> {
125         isle::lower_branch(ctx, self, ir_inst, targets)
126     }
127 
maybe_pinned_reg(&self) -> Option<Reg>128     fn maybe_pinned_reg(&self) -> Option<Reg> {
129         Some(regs::pinned_reg())
130     }
131 }
132