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::instructions::InstructionFormat;
8use crate::ir::types;
9use crate::ir::{DataFlowGraph, InstructionData};
10use crate::ir::{Inst, Opcode, Type, Value};
11
12/// Base trait for instruction builders.
13///
14/// The `InstBuilderBase` trait provides the basic functionality required by the methods of the
15/// generated `InstBuilder` trait. These methods should not normally be used directly. Use the
16/// methods in the `InstBuilder` trait instead.
17///
18/// Any data type that implements `InstBuilderBase` also gets all the methods of the `InstBuilder`
19/// trait.
20pub trait InstBuilderBase<'f>: Sized {
21    /// Get an immutable reference to the data flow graph that will hold the constructed
22    /// instructions.
23    fn data_flow_graph(&self) -> &DataFlowGraph;
24    /// Get a mutable reference to the data flow graph that will hold the constructed
25    /// instructions.
26    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph;
27
28    /// Insert an instruction and return a reference to it, consuming the builder.
29    ///
30    /// The result types may depend on a controlling type variable. For non-polymorphic
31    /// instructions with multiple results, pass `INVALID` for the `ctrl_typevar` argument.
32    fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph);
33}
34
35// Include trait code generated by `cranelift-codegen/meta/src/gen_inst.rs`.
36//
37// This file defines the `InstBuilder` trait as an extension of `InstBuilderBase` with methods per
38// instruction format and per opcode.
39include!(concat!(env!("OUT_DIR"), "/inst_builder.rs"));
40
41/// Any type implementing `InstBuilderBase` gets all the `InstBuilder` methods for free.
42impl<'f, T: InstBuilderBase<'f>> InstBuilder<'f> for T {}
43
44/// Base trait for instruction inserters.
45///
46/// This is an alternative base trait for an instruction builder to implement.
47///
48/// An instruction inserter can be adapted into an instruction builder by wrapping it in an
49/// `InsertBuilder`. This provides some common functionality for instruction builders that insert
50/// new instructions, as opposed to the `ReplaceBuilder` which overwrites existing instructions.
51pub trait InstInserterBase<'f>: Sized {
52    /// Get an immutable reference to the data flow graph.
53    fn data_flow_graph(&self) -> &DataFlowGraph;
54
55    /// Get a mutable reference to the data flow graph.
56    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph;
57
58    /// Insert a new instruction which belongs to the DFG.
59    fn insert_built_inst(self, inst: Inst) -> &'f mut DataFlowGraph;
60}
61
62use core::marker::PhantomData;
63
64/// Builder that inserts an instruction at the current position.
65///
66/// An `InsertBuilder` is a wrapper for an `InstInserterBase` that turns it into an instruction
67/// builder with some additional facilities for creating instructions that reuse existing values as
68/// their results.
69pub struct InsertBuilder<'f, IIB: InstInserterBase<'f>> {
70    inserter: IIB,
71    unused: PhantomData<&'f u32>,
72}
73
74impl<'f, IIB: InstInserterBase<'f>> InsertBuilder<'f, IIB> {
75    /// Create a new builder which inserts instructions at `pos`.
76    /// The `dfg` and `pos.layout` references should be from the same `Function`.
77    pub fn new(inserter: IIB) -> Self {
78        Self {
79            inserter,
80            unused: PhantomData,
81        }
82    }
83
84    /// Reuse result values in `reuse`.
85    ///
86    /// Convert this builder into one that will reuse the provided result values instead of
87    /// allocating new ones. The provided values for reuse must not be attached to anything. Any
88    /// missing result values will be allocated as normal.
89    ///
90    /// The `reuse` argument is expected to be an array of `Option<Value>`.
91    pub fn with_results<Array>(self, reuse: Array) -> InsertReuseBuilder<'f, IIB, Array>
92    where
93        Array: AsRef<[Option<Value>]>,
94    {
95        InsertReuseBuilder {
96            inserter: self.inserter,
97            reuse,
98            unused: PhantomData,
99        }
100    }
101
102    /// Reuse a single result value.
103    ///
104    /// Convert this into a builder that will reuse `v` as the single result value. The reused
105    /// result value `v` must not be attached to anything.
106    ///
107    /// This method should only be used when building an instruction with exactly one result. Use
108    /// `with_results()` for the more general case.
109    pub fn with_result(self, v: Value) -> InsertReuseBuilder<'f, IIB, [Option<Value>; 1]> {
110        // TODO: Specialize this to return a different builder that just attaches `v` instead of
111        // calling `make_inst_results_reusing()`.
112        self.with_results([Some(v)])
113    }
114}
115
116impl<'f, IIB: InstInserterBase<'f>> InstBuilderBase<'f> for InsertBuilder<'f, IIB> {
117    fn data_flow_graph(&self) -> &DataFlowGraph {
118        self.inserter.data_flow_graph()
119    }
120
121    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
122        self.inserter.data_flow_graph_mut()
123    }
124
125    fn build(mut self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
126        let inst;
127        {
128            let dfg = self.inserter.data_flow_graph_mut();
129            inst = dfg.make_inst(data);
130            dfg.make_inst_results(inst, ctrl_typevar);
131        }
132        (inst, self.inserter.insert_built_inst(inst))
133    }
134}
135
136/// Builder that inserts a new instruction like `InsertBuilder`, but reusing result values.
137pub struct InsertReuseBuilder<'f, IIB, Array>
138where
139    IIB: InstInserterBase<'f>,
140    Array: AsRef<[Option<Value>]>,
141{
142    inserter: IIB,
143    reuse: Array,
144    unused: PhantomData<&'f u32>,
145}
146
147impl<'f, IIB, Array> InstBuilderBase<'f> for InsertReuseBuilder<'f, IIB, Array>
148where
149    IIB: InstInserterBase<'f>,
150    Array: AsRef<[Option<Value>]>,
151{
152    fn data_flow_graph(&self) -> &DataFlowGraph {
153        self.inserter.data_flow_graph()
154    }
155
156    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
157        self.inserter.data_flow_graph_mut()
158    }
159
160    fn build(mut self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
161        let inst;
162        {
163            let dfg = self.inserter.data_flow_graph_mut();
164            inst = dfg.make_inst(data);
165            // Make an `Iterator<Item = Option<Value>>`.
166            let ru = self.reuse.as_ref().iter().cloned();
167            dfg.make_inst_results_reusing(inst, ctrl_typevar, ru);
168        }
169        (inst, self.inserter.insert_built_inst(inst))
170    }
171}
172
173/// Instruction builder that replaces an existing instruction.
174///
175/// The inserted instruction will have the same `Inst` number as the old one.
176///
177/// If the old instruction still has result values attached, it is assumed that the new instruction
178/// produces the same number and types of results. The old result values are preserved. If the
179/// replacement instruction format does not support multiple results, the builder panics. It is a
180/// bug to leave result values dangling.
181pub struct ReplaceBuilder<'f> {
182    dfg: &'f mut DataFlowGraph,
183    inst: Inst,
184}
185
186impl<'f> ReplaceBuilder<'f> {
187    /// Create a `ReplaceBuilder` that will overwrite `inst`.
188    pub fn new(dfg: &'f mut DataFlowGraph, inst: Inst) -> Self {
189        Self { dfg, inst }
190    }
191}
192
193impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> {
194    fn data_flow_graph(&self) -> &DataFlowGraph {
195        self.dfg
196    }
197
198    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
199        self.dfg
200    }
201
202    fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
203        // Splat the new instruction on top of the old one.
204        self.dfg.insts[self.inst] = data;
205
206        if !self.dfg.has_results(self.inst) {
207            // The old result values were either detached or non-existent.
208            // Construct new ones.
209            self.dfg.make_inst_results(self.inst, ctrl_typevar);
210        }
211
212        (self.inst, self.dfg)
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use crate::cursor::{Cursor, FuncCursor};
219    use crate::ir::condcodes::*;
220    use crate::ir::types::*;
221    use crate::ir::{Function, InstBuilder, ValueDef};
222
223    #[test]
224    fn types() {
225        let mut func = Function::new();
226        let block0 = func.dfg.make_block();
227        let arg0 = func.dfg.append_block_param(block0, I32);
228        let mut pos = FuncCursor::new(&mut func);
229        pos.insert_block(block0);
230
231        // Explicit types.
232        let v0 = pos.ins().iconst(I32, 3);
233        assert_eq!(pos.func.dfg.value_type(v0), I32);
234
235        // Inferred from inputs.
236        let v1 = pos.ins().iadd(arg0, v0);
237        assert_eq!(pos.func.dfg.value_type(v1), I32);
238
239        // Formula.
240        let cmp = pos.ins().icmp(IntCC::Equal, arg0, v0);
241        assert_eq!(pos.func.dfg.value_type(cmp), I8);
242    }
243
244    #[test]
245    fn reuse_results() {
246        let mut func = Function::new();
247        let block0 = func.dfg.make_block();
248        let arg0 = func.dfg.append_block_param(block0, I32);
249        let mut pos = FuncCursor::new(&mut func);
250        pos.insert_block(block0);
251
252        let v0 = pos.ins().iadd_imm(arg0, 17);
253        assert_eq!(pos.func.dfg.value_type(v0), I32);
254        let iadd = pos.prev_inst().unwrap();
255        assert_eq!(pos.func.dfg.value_def(v0), ValueDef::Result(iadd, 0));
256
257        // Detach v0 and reuse it for a different instruction.
258        pos.func.dfg.clear_results(iadd);
259        let v0b = pos.ins().with_result(v0).iconst(I32, 3);
260        assert_eq!(v0, v0b);
261        assert_eq!(pos.current_inst(), Some(iadd));
262        let iconst = pos.prev_inst().unwrap();
263        assert!(iadd != iconst);
264        assert_eq!(pos.func.dfg.value_def(v0), ValueDef::Result(iconst, 0));
265    }
266
267    #[test]
268    #[should_panic]
269    #[cfg(debug_assertions)]
270    fn panics_when_inserting_wrong_opcode() {
271        use crate::ir::{Opcode, TrapCode};
272
273        let mut func = Function::new();
274        let block0 = func.dfg.make_block();
275        let mut pos = FuncCursor::new(&mut func);
276        pos.insert_block(block0);
277
278        // We are trying to create a Opcode::Return with the InstData::Trap, which is obviously wrong
279        pos.ins()
280            .Trap(Opcode::Return, I32, TrapCode::BAD_CONVERSION_TO_INTEGER);
281    }
282}