Skip to main content

cranelift_codegen/ir/
builder.rs

1//! Cranelift instruction builder.
2//!
3//! A `Builder` provides a convenient interface for inserting instructions into a Cranelift
4//! function. Many of its methods are generated from the meta language instruction definitions.
5
6use crate::ir;
7use crate::ir::immediates::Imm64;
8use crate::ir::instructions::InstructionFormat;
9use crate::ir::types;
10use crate::ir::{BlockArg, Inst, Layout, Opcode, Type, Value};
11use crate::ir::{DataFlowGraph, InstructionData};
12
13/// Base trait for instruction builders.
14///
15/// The `InstBuilderBase` trait provides the basic functionality required by the methods of the
16/// generated `InstBuilder` trait. These methods should not normally be used directly. Use the
17/// methods in the `InstBuilder` trait instead.
18///
19/// Any data type that implements `InstBuilderBase` also gets all the methods of the `InstBuilder`
20/// trait.
21pub trait InstBuilderBase<'f>: Sized {
22    /// Get an immutable reference to the data flow graph that will hold the constructed
23    /// instructions.
24    fn data_flow_graph(&self) -> &DataFlowGraph;
25    /// Get a mutable reference to the data flow graph that will hold the constructed
26    /// instructions.
27    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph;
28
29    /// Insert an instruction and return a reference to it, consuming the builder.
30    ///
31    /// The result types may depend on a controlling type variable. For non-polymorphic
32    /// instructions with multiple results, pass `INVALID` for the `ctrl_typevar` argument.
33    fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph);
34
35    /// Build and insert an auxiliary instruction.
36    ///
37    /// This auxiliary instruction must be inserted before the instruction
38    /// this builder will ultimately build.
39    ///
40    /// This method returns the new instruction, without consuming the builder.
41    fn build_aux_inst(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst;
42
43    /// Materialize an immediate integer constant as an `iconst` (and possibly an
44    /// extension), inserting it before the instruction this builder will build,
45    /// and return its result value.
46    ///
47    /// The constant's type is `controlling_type`, or, for vector controlling
48    /// types, its lane type. Because `iconst` only supports `i8` through `i64`,
49    /// an `i128` constant is built as an `iconst.i64` that is then extended:
50    /// sign-extended when `signed` is true, zero-extended otherwise. For narrower
51    /// types the `signed` flag has no effect, as the masked immediate is the same
52    /// either way.
53    fn build_imm_const(&mut self, controlling_type: Type, imm: Imm64, signed: bool) -> Value {
54        if controlling_type == types::I128 {
55            let mut data = InstructionData::UnaryImm {
56                opcode: Opcode::Iconst,
57                imm,
58            };
59            data.mask_immediates(types::I64);
60            let lo = self.build_aux_inst(data, types::I64);
61            let lo = self.data_flow_graph().first_result(lo);
62
63            let opcode = if signed {
64                Opcode::Sextend
65            } else {
66                Opcode::Uextend
67            };
68            let ext_inst =
69                self.build_aux_inst(InstructionData::Unary { opcode, arg: lo }, types::I128);
70            self.data_flow_graph().first_result(ext_inst)
71        } else {
72            let lane_type = controlling_type.lane_type();
73            let mut data = InstructionData::UnaryImm {
74                opcode: Opcode::Iconst,
75                imm,
76            };
77            data.mask_immediates(lane_type);
78            let inst = self.build_aux_inst(data, lane_type);
79            self.data_flow_graph().first_result(inst)
80        }
81    }
82}
83
84// Include trait code generated by `cranelift-codegen/meta/src/gen_inst.rs`.
85//
86// This file defines the `InstBuilder` trait as an extension of `InstBuilderBase` with methods per
87// instruction format and per opcode.
88include!(concat!(env!("OUT_DIR"), "/inst_builder.rs"));
89
90/// Any type implementing `InstBuilderBase` gets all the `InstBuilder` methods for free.
91impl<'f, T: InstBuilderBase<'f>> InstBuilder<'f> for T {}
92
93/// Base trait for instruction inserters.
94///
95/// This is an alternative base trait for an instruction builder to implement.
96///
97/// An instruction inserter can be adapted into an instruction builder by wrapping it in an
98/// `InsertBuilder`. This provides some common functionality for instruction builders that insert
99/// new instructions, as opposed to the `ReplaceBuilder` which overwrites existing instructions.
100pub trait InstInserterBase<'f>: Sized {
101    /// Get an immutable reference to the data flow graph.
102    fn data_flow_graph(&self) -> &DataFlowGraph;
103
104    /// Get a mutable reference to the data flow graph.
105    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph;
106
107    /// Insert a new instruction which belongs to the DFG.
108    fn insert_built_inst(self, inst: Inst) -> &'f mut DataFlowGraph;
109
110    /// Insert `inst` into the layout at the current position without consuming
111    /// the inserter, so that further instructions may be inserted afterward.
112    ///
113    /// This backs [`InstBuilderBase::build_aux_inst`] for the insertion-based
114    /// builders.
115    fn insert_aux_inst(&mut self, inst: Inst);
116}
117
118use core::marker::PhantomData;
119
120/// Builder that inserts an instruction at the current position.
121///
122/// An `InsertBuilder` is a wrapper for an `InstInserterBase` that turns it into an instruction
123/// builder with some additional facilities for creating instructions that reuse existing values as
124/// their results.
125pub struct InsertBuilder<'f, IIB: InstInserterBase<'f>> {
126    inserter: IIB,
127    unused: PhantomData<&'f u32>,
128}
129
130impl<'f, IIB: InstInserterBase<'f>> InsertBuilder<'f, IIB> {
131    /// Create a new builder which inserts instructions at `pos`.
132    /// The `dfg` and `pos.layout` references should be from the same `Function`.
133    pub fn new(inserter: IIB) -> Self {
134        Self {
135            inserter,
136            unused: PhantomData,
137        }
138    }
139
140    /// Reuse result values in `reuse`.
141    ///
142    /// Convert this builder into one that will reuse the provided result values instead of
143    /// allocating new ones. The provided values for reuse must not be attached to anything. Any
144    /// missing result values will be allocated as normal.
145    ///
146    /// The `reuse` argument is expected to be an array of `Option<Value>`.
147    pub fn with_results<Array>(self, reuse: Array) -> InsertReuseBuilder<'f, IIB, Array>
148    where
149        Array: AsRef<[Option<Value>]>,
150    {
151        InsertReuseBuilder {
152            inserter: self.inserter,
153            reuse,
154            unused: PhantomData,
155        }
156    }
157
158    /// Reuse a single result value.
159    ///
160    /// Convert this into a builder that will reuse `v` as the single result value. The reused
161    /// result value `v` must not be attached to anything.
162    ///
163    /// This method should only be used when building an instruction with exactly one result. Use
164    /// `with_results()` for the more general case.
165    pub fn with_result(self, v: Value) -> InsertReuseBuilder<'f, IIB, [Option<Value>; 1]> {
166        // TODO: Specialize this to return a different builder that just attaches `v` instead of
167        // calling `make_inst_results_reusing()`.
168        self.with_results([Some(v)])
169    }
170}
171
172impl<'f, IIB: InstInserterBase<'f>> InstBuilderBase<'f> for InsertBuilder<'f, IIB> {
173    fn data_flow_graph(&self) -> &DataFlowGraph {
174        self.inserter.data_flow_graph()
175    }
176
177    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
178        self.inserter.data_flow_graph_mut()
179    }
180
181    fn build(mut self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
182        let dfg = self.inserter.data_flow_graph_mut();
183        let inst = dfg.make_inst(data);
184        dfg.make_inst_results(inst, ctrl_typevar);
185        (inst, self.inserter.insert_built_inst(inst))
186    }
187
188    fn build_aux_inst(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst {
189        let dfg = self.inserter.data_flow_graph_mut();
190        let inst = dfg.make_inst(data);
191        dfg.make_inst_results(inst, ctrl_typevar);
192        self.inserter.insert_aux_inst(inst);
193        inst
194    }
195}
196
197/// Builder that inserts a new instruction like `InsertBuilder`, but reusing result values.
198pub struct InsertReuseBuilder<'f, IIB, Array>
199where
200    IIB: InstInserterBase<'f>,
201    Array: AsRef<[Option<Value>]>,
202{
203    inserter: IIB,
204    reuse: Array,
205    unused: PhantomData<&'f u32>,
206}
207
208impl<'f, IIB, Array> InstBuilderBase<'f> for InsertReuseBuilder<'f, IIB, Array>
209where
210    IIB: InstInserterBase<'f>,
211    Array: AsRef<[Option<Value>]>,
212{
213    fn data_flow_graph(&self) -> &DataFlowGraph {
214        self.inserter.data_flow_graph()
215    }
216
217    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
218        self.inserter.data_flow_graph_mut()
219    }
220
221    fn build(mut self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
222        let dfg = self.inserter.data_flow_graph_mut();
223        let inst = dfg.make_inst(data);
224        let reuse = self.reuse.as_ref().iter().cloned();
225        dfg.make_inst_results_reusing(inst, ctrl_typevar, reuse);
226        (inst, self.inserter.insert_built_inst(inst))
227    }
228
229    fn build_aux_inst(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst {
230        // Result reuse only applies to the final, builder-consuming instruction;
231        // auxiliary instructions always get freshly allocated results.
232        let inst;
233        {
234            let dfg = self.inserter.data_flow_graph_mut();
235            inst = dfg.make_inst(data);
236            dfg.make_inst_results(inst, ctrl_typevar);
237        }
238        self.inserter.insert_aux_inst(inst);
239        inst
240    }
241}
242
243/// Instruction builder that replaces an existing instruction.
244///
245/// The inserted instruction will have the same `Inst` number as the old one.
246///
247/// If the old instruction still has result values attached, it is assumed that the new instruction
248/// produces the same number and types of results. The old result values are preserved. If the
249/// replacement instruction format does not support multiple results, the builder panics. It is a
250/// bug to leave result values dangling.
251pub struct ReplaceBuilder<'f> {
252    dfg: &'f mut DataFlowGraph,
253    layout: &'f mut Layout,
254    inst: Inst,
255}
256
257impl<'f> ReplaceBuilder<'f> {
258    /// Create a `ReplaceBuilder` that will overwrite `inst`.
259    pub fn new(dfg: &'f mut DataFlowGraph, layout: &'f mut Layout, inst: Inst) -> Self {
260        Self { dfg, layout, inst }
261    }
262}
263
264impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> {
265    fn data_flow_graph(&self) -> &DataFlowGraph {
266        self.dfg
267    }
268
269    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
270        self.dfg
271    }
272
273    fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
274        // Splat the new instruction on top of the old one.
275        self.dfg.insts[self.inst] = data;
276
277        if !self.dfg.has_results(self.inst) {
278            // The old result values were either detached or non-existent.
279            // Construct new ones.
280            self.dfg.make_inst_results(self.inst, ctrl_typevar);
281        }
282
283        (self.inst, self.dfg)
284    }
285
286    fn build_aux_inst(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst {
287        // Auxiliary instructions are inserted into the layout immediately
288        // before the instruction being replaced.
289        let inst = self.dfg.make_inst(data);
290        self.dfg.make_inst_results(inst, ctrl_typevar);
291        self.layout.insert_inst(inst, self.inst);
292        inst
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use crate::cursor::{Cursor, FuncCursor};
299    use crate::ir::condcodes::*;
300    use crate::ir::types::*;
301    use crate::ir::{Function, InstBuilder, Opcode, ValueDef};
302
303    #[test]
304    fn types() {
305        let mut func = Function::new();
306        let block0 = func.dfg.make_block();
307        let arg0 = func.dfg.append_block_param(block0, I32);
308        let mut pos = FuncCursor::new(&mut func);
309        pos.insert_block(block0);
310
311        // Explicit types.
312        let v0 = pos.ins().iconst(I32, 3);
313        assert_eq!(pos.func.dfg.value_type(v0), I32);
314
315        // Inferred from inputs.
316        let v1 = pos.ins().iadd(arg0, v0);
317        assert_eq!(pos.func.dfg.value_type(v1), I32);
318
319        // Formula.
320        let cmp = pos.ins().icmp(IntCC::Equal, arg0, v0);
321        assert_eq!(pos.func.dfg.value_type(cmp), I8);
322    }
323
324    #[test]
325    fn reuse_results() {
326        let mut func = Function::new();
327        let block0 = func.dfg.make_block();
328        let arg0 = func.dfg.append_block_param(block0, I32);
329        let mut pos = FuncCursor::new(&mut func);
330        pos.insert_block(block0);
331
332        let v0 = pos.ins().iadd_imm_s(arg0, 17);
333        assert_eq!(pos.func.dfg.value_type(v0), I32);
334        let iadd = pos.prev_inst().unwrap();
335        assert_eq!(pos.func.dfg.value_def(v0), ValueDef::Result(iadd, 0));
336
337        // Detach v0 and reuse it for a different instruction.
338        pos.func.dfg.clear_results(iadd);
339        let v0b = pos.ins().with_result(v0).iconst(I32, 3);
340        assert_eq!(v0, v0b);
341        assert_eq!(pos.current_inst(), Some(iadd));
342        let iconst = pos.prev_inst().unwrap();
343        assert!(iadd != iconst);
344        assert_eq!(pos.func.dfg.value_def(v0), ValueDef::Result(iconst, 0));
345    }
346
347    #[test]
348    fn replace_with_imm_method() {
349        // Replacing an instruction with a generated `*_imm` convenience method
350        // (here `icmp_imm`) must materialize its `iconst` immediately before the
351        // replaced instruction via `ReplaceBuilder::build_aux_inst`.
352        let mut func = Function::new();
353        let block0 = func.dfg.make_block();
354        let arg0 = func.dfg.append_block_param(block0, I32);
355        {
356            let mut pos = FuncCursor::new(&mut func);
357            pos.insert_block(block0);
358            // A placeholder `icmp` (also `i8`-typed) to be replaced.
359            pos.ins().icmp(IntCC::Equal, arg0, arg0);
360        }
361
362        let inst = func.layout.last_inst(block0).unwrap();
363        assert_eq!(func.dfg.insts[inst].opcode(), Opcode::Icmp);
364
365        let result = func.replace(inst).icmp_imm_s(IntCC::Equal, arg0, 42);
366
367        // The replaced instruction is still an `icmp`, now comparing against a
368        // freshly materialized constant.
369        assert_eq!(func.dfg.insts[inst].opcode(), Opcode::Icmp);
370        assert_eq!(func.dfg.value_type(result), I8);
371
372        // An `iconst.i32 42` was inserted immediately before the replaced inst.
373        let iconst = func.layout.prev_inst(inst).unwrap();
374        assert_eq!(func.dfg.insts[iconst].opcode(), Opcode::Iconst);
375        let iconst_result = func.dfg.first_result(iconst);
376        assert_eq!(func.dfg.value_type(iconst_result), I32);
377        assert_eq!(func.dfg.inst_args(inst), &[arg0, iconst_result]);
378    }
379
380    #[test]
381    fn imm_s_and_imm_u_extend_i128() {
382        // For an `i128` controlling type the immediate is materialized as an
383        // `iconst.i64` and then extended. `_imm_s` sign-extends, `_imm_u`
384        // zero-extends, and the deprecated `_imm` keeps the historical behavior
385        // (signed, for `iadd`).
386        let mut func = Function::new();
387        let block0 = func.dfg.make_block();
388        let arg = func.dfg.append_block_param(block0, I128);
389        let mut pos = FuncCursor::new(&mut func);
390        pos.insert_block(block0);
391
392        // Opcode of the instruction that extends `iadd`'s materialized immediate.
393        fn ext_opcode(pos: &FuncCursor, iadd: crate::ir::Inst) -> Opcode {
394            let imm = pos.func.dfg.inst_args(iadd)[1];
395            let ext = pos.func.dfg.value_def(imm).unwrap_inst();
396            pos.func.dfg.insts[ext].opcode()
397        }
398
399        pos.ins().iadd_imm_s(arg, -1);
400        let iadd = pos.prev_inst().unwrap();
401        assert_eq!(ext_opcode(&pos, iadd), Opcode::Sextend);
402
403        pos.ins().iadd_imm_u(arg, -1);
404        let iadd = pos.prev_inst().unwrap();
405        assert_eq!(ext_opcode(&pos, iadd), Opcode::Uextend);
406
407        #[allow(deprecated, reason = "exercising the deprecated `_imm` shim")]
408        pos.ins().iadd_imm(arg, -1);
409        let iadd = pos.prev_inst().unwrap();
410        assert_eq!(ext_opcode(&pos, iadd), Opcode::Sextend);
411    }
412
413    #[test]
414    #[should_panic]
415    #[cfg(debug_assertions)]
416    fn panics_when_inserting_wrong_opcode() {
417        use crate::ir::{Opcode, TrapCode};
418
419        let mut func = Function::new();
420        let block0 = func.dfg.make_block();
421        let mut pos = FuncCursor::new(&mut func);
422        pos.insert_block(block0);
423
424        // We are trying to create a Opcode::Return with the InstData::Trap, which is obviously wrong
425        pos.ins()
426            .Trap(Opcode::Return, I32, TrapCode::BAD_CONVERSION_TO_INTEGER);
427    }
428}