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