Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/builder.rs
1693 views
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
6
use crate::ir;
7
use crate::ir::instructions::InstructionFormat;
8
use crate::ir::types;
9
use crate::ir::{BlockArg, Inst, Opcode, Type, Value};
10
use crate::ir::{DataFlowGraph, InstructionData};
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.
20
pub 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.
39
include!(concat!(env!("OUT_DIR"), "/inst_builder.rs"));
40
41
/// Any type implementing `InstBuilderBase` gets all the `InstBuilder` methods for free.
42
impl<'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.
51
pub 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
62
use 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.
69
pub struct InsertBuilder<'f, IIB: InstInserterBase<'f>> {
70
inserter: IIB,
71
unused: PhantomData<&'f u32>,
72
}
73
74
impl<'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
116
impl<'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.
137
pub struct InsertReuseBuilder<'f, IIB, Array>
138
where
139
IIB: InstInserterBase<'f>,
140
Array: AsRef<[Option<Value>]>,
141
{
142
inserter: IIB,
143
reuse: Array,
144
unused: PhantomData<&'f u32>,
145
}
146
147
impl<'f, IIB, Array> InstBuilderBase<'f> for InsertReuseBuilder<'f, IIB, Array>
148
where
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.
181
pub struct ReplaceBuilder<'f> {
182
dfg: &'f mut DataFlowGraph,
183
inst: Inst,
184
}
185
186
impl<'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
193
impl<'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)]
217
mod 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
}
283
284