cranelift_codegen/legalizer/
mod.rs

1//! Legalize instructions.
2//!
3//! A legal instruction is one that can be mapped directly to a machine code instruction for the
4//! target ISA. The `legalize_function()` function takes as input any function and transforms it
5//! into an equivalent function using only legal instructions.
6//!
7//! The characteristics of legal instructions depend on the target ISA, so any given instruction
8//! can be legal for one ISA and illegal for another.
9//!
10//! Besides transforming instructions, the legalizer also fills out the `function.encodings` map
11//! which provides a legal encoding recipe for every instruction.
12//!
13//! The legalizer does not deal with register allocation constraints. These constraints are derived
14//! from the encoding recipes, and solved later by the register allocator.
15
16use crate::cursor::{Cursor, FuncCursor};
17use crate::ir::immediates::Imm64;
18use crate::ir::types::{self, I128, I64};
19use crate::ir::{self, InstBuilder, InstructionData, MemFlags, Value};
20use crate::isa::TargetIsa;
21use crate::trace;
22
23mod globalvalue;
24
25use self::globalvalue::expand_global_value;
26
27fn imm_const(pos: &mut FuncCursor, arg: Value, imm: Imm64, is_signed: bool) -> Value {
28    let ty = pos.func.dfg.value_type(arg);
29    match (ty, is_signed) {
30        (I128, true) => {
31            let imm = pos.ins().iconst(I64, imm);
32            pos.ins().sextend(I128, imm)
33        }
34        (I128, false) => {
35            let imm = pos.ins().iconst(I64, imm);
36            pos.ins().uextend(I128, imm)
37        }
38        _ => {
39            let bits = imm.bits();
40            let unsigned = match ty.lane_type() {
41                types::I8 => bits as u8 as i64,
42                types::I16 => bits as u16 as i64,
43                types::I32 => bits as u32 as i64,
44                types::I64 => bits,
45                _ => unreachable!(),
46            };
47            pos.ins().iconst(ty.lane_type(), unsigned)
48        }
49    }
50}
51
52/// Perform a simple legalization by expansion of the function, without
53/// platform-specific transforms.
54pub fn simple_legalize(func: &mut ir::Function, isa: &dyn TargetIsa) {
55    trace!("Pre-legalization function:\n{}", func.display());
56
57    let mut pos = FuncCursor::new(func);
58    let func_begin = pos.position();
59    pos.set_position(func_begin);
60    while let Some(_block) = pos.next_block() {
61        let mut prev_pos = pos.position();
62        while let Some(inst) = pos.next_inst() {
63            match pos.func.dfg.insts[inst] {
64                // memory and constants
65                InstructionData::UnaryGlobalValue {
66                    opcode: ir::Opcode::GlobalValue,
67                    global_value,
68                } => expand_global_value(inst, &mut pos.func, isa, global_value),
69                InstructionData::StackLoad {
70                    opcode: ir::Opcode::StackLoad,
71                    stack_slot,
72                    offset,
73                } => {
74                    let ty = pos.func.dfg.value_type(pos.func.dfg.first_result(inst));
75                    let addr_ty = isa.pointer_type();
76
77                    let mut pos = FuncCursor::new(pos.func).at_inst(inst);
78                    pos.use_srcloc(inst);
79
80                    let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
81
82                    // Stack slots are required to be accessible.
83                    // We can't currently ensure that they are aligned.
84                    let mut mflags = MemFlags::new();
85                    mflags.set_notrap();
86                    pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
87                }
88                InstructionData::StackStore {
89                    opcode: ir::Opcode::StackStore,
90                    arg,
91                    stack_slot,
92                    offset,
93                } => {
94                    let addr_ty = isa.pointer_type();
95
96                    let mut pos = FuncCursor::new(pos.func).at_inst(inst);
97                    pos.use_srcloc(inst);
98
99                    let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
100
101                    // Stack slots are required to be accessible.
102                    // We can't currently ensure that they are aligned.
103                    let mut mflags = MemFlags::new();
104                    mflags.set_notrap();
105                    pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);
106                }
107                InstructionData::DynamicStackLoad {
108                    opcode: ir::Opcode::DynamicStackLoad,
109                    dynamic_stack_slot,
110                } => {
111                    let ty = pos.func.dfg.value_type(pos.func.dfg.first_result(inst));
112                    assert!(ty.is_dynamic_vector());
113                    let addr_ty = isa.pointer_type();
114
115                    let mut pos = FuncCursor::new(pos.func).at_inst(inst);
116                    pos.use_srcloc(inst);
117
118                    let addr = pos.ins().dynamic_stack_addr(addr_ty, dynamic_stack_slot);
119
120                    // Stack slots are required to be accessible and aligned.
121                    let mflags = MemFlags::trusted();
122                    pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
123                }
124                InstructionData::DynamicStackStore {
125                    opcode: ir::Opcode::DynamicStackStore,
126                    arg,
127                    dynamic_stack_slot,
128                } => {
129                    pos.use_srcloc(inst);
130                    let addr_ty = isa.pointer_type();
131                    let vector_ty = pos.func.dfg.value_type(arg);
132                    assert!(vector_ty.is_dynamic_vector());
133
134                    let addr = pos.ins().dynamic_stack_addr(addr_ty, dynamic_stack_slot);
135
136                    let mut mflags = MemFlags::new();
137                    // Stack slots are required to be accessible and aligned.
138                    mflags.set_notrap();
139                    mflags.set_aligned();
140                    pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);
141                }
142
143                InstructionData::BinaryImm64 { opcode, arg, imm } => {
144                    let is_signed = match opcode {
145                        ir::Opcode::IaddImm
146                        | ir::Opcode::IrsubImm
147                        | ir::Opcode::ImulImm
148                        | ir::Opcode::SdivImm
149                        | ir::Opcode::SremImm => true,
150                        _ => false,
151                    };
152
153                    let imm = imm_const(&mut pos, arg, imm, is_signed);
154                    let replace = pos.func.dfg.replace(inst);
155                    match opcode {
156                        // bitops
157                        ir::Opcode::BandImm => {
158                            replace.band(arg, imm);
159                        }
160                        ir::Opcode::BorImm => {
161                            replace.bor(arg, imm);
162                        }
163                        ir::Opcode::BxorImm => {
164                            replace.bxor(arg, imm);
165                        }
166                        // bitshifting
167                        ir::Opcode::IshlImm => {
168                            replace.ishl(arg, imm);
169                        }
170                        ir::Opcode::RotlImm => {
171                            replace.rotl(arg, imm);
172                        }
173                        ir::Opcode::RotrImm => {
174                            replace.rotr(arg, imm);
175                        }
176                        ir::Opcode::SshrImm => {
177                            replace.sshr(arg, imm);
178                        }
179                        ir::Opcode::UshrImm => {
180                            replace.ushr(arg, imm);
181                        }
182                        // math
183                        ir::Opcode::IaddImm => {
184                            replace.iadd(arg, imm);
185                        }
186                        ir::Opcode::IrsubImm => {
187                            // note: arg order reversed
188                            replace.isub(imm, arg);
189                        }
190                        ir::Opcode::ImulImm => {
191                            replace.imul(arg, imm);
192                        }
193                        ir::Opcode::SdivImm => {
194                            replace.sdiv(arg, imm);
195                        }
196                        ir::Opcode::SremImm => {
197                            replace.srem(arg, imm);
198                        }
199                        ir::Opcode::UdivImm => {
200                            replace.udiv(arg, imm);
201                        }
202                        ir::Opcode::UremImm => {
203                            replace.urem(arg, imm);
204                        }
205                        _ => prev_pos = pos.position(),
206                    };
207                }
208
209                // comparisons
210                InstructionData::IntCompareImm {
211                    opcode: ir::Opcode::IcmpImm,
212                    cond,
213                    arg,
214                    imm,
215                } => {
216                    let imm = imm_const(&mut pos, arg, imm, true);
217                    pos.func.dfg.replace(inst).icmp(cond, arg, imm);
218                }
219
220                // Legalize the fused bitwise-plus-not instructions into simpler
221                // instructions to assist with optimizations. Lowering will
222                // pattern match this sequence regardless when architectures
223                // support the instruction natively.
224                InstructionData::Binary { opcode, args } => {
225                    match opcode {
226                        ir::Opcode::BandNot => {
227                            let neg = pos.ins().bnot(args[1]);
228                            pos.func.dfg.replace(inst).band(args[0], neg);
229                        }
230                        ir::Opcode::BorNot => {
231                            let neg = pos.ins().bnot(args[1]);
232                            pos.func.dfg.replace(inst).bor(args[0], neg);
233                        }
234                        ir::Opcode::BxorNot => {
235                            let neg = pos.ins().bnot(args[1]);
236                            pos.func.dfg.replace(inst).bxor(args[0], neg);
237                        }
238                        _ => prev_pos = pos.position(),
239                    };
240                }
241
242                _ => {
243                    prev_pos = pos.position();
244                    continue;
245                }
246            }
247
248            // Legalization implementations require fixpoint loop here.
249            // TODO: fix this.
250            pos.set_position(prev_pos);
251        }
252    }
253
254    trace!("Post-legalization function:\n{}", func.display());
255}