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