1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! A utility for inserting [Operation]s from a specified insertion point.
//! Similar in spirit to LLVM's IRBuilder, but does not build operations.
use crate::{
basic_block::BasicBlock,
common_traits::Named,
context::{Context, Ptr},
op::Op,
operation::Operation,
printable::{self, Printable},
};
/// Insertion point specification for inserting [Operation]s using [OpInserter].
#[derive(Clone, Copy)]
pub enum OpInsertionPoint {
AtBlockStart(Ptr<BasicBlock>),
AtBlockEnd(Ptr<BasicBlock>),
AfterOperation(Ptr<Operation>),
BeforeOperation(Ptr<Operation>),
}
impl Printable for OpInsertionPoint {
fn fmt(
&self,
ctx: &Context,
_state: &printable::State,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
match self {
OpInsertionPoint::AtBlockStart(block) => {
write!(
f,
"At start of BasicBlock {}",
block.deref(ctx).unique_name(ctx)
)
}
OpInsertionPoint::AtBlockEnd(block) => {
write!(
f,
"At end of BasicBlock {}",
block.deref(ctx).unique_name(ctx)
)
}
OpInsertionPoint::AfterOperation(op) => {
write!(f, "After Operation {}", op.disp(ctx))
}
OpInsertionPoint::BeforeOperation(op) => {
write!(f, "Before Operation {}", op.disp(ctx))
}
}
}
}
/// A utility for inserting [Operation]s from a specified insertion point.
pub struct OpInserter {
insertion_point: OpInsertionPoint,
}
impl OpInserter {
/// Creates a new [OpInserter] that inserts the next operation
/// at the start of the given [BasicBlock].
pub fn new_at_block_start(block: Ptr<BasicBlock>) -> Self {
Self {
insertion_point: OpInsertionPoint::AtBlockStart(block),
}
}
/// Creates a new [OpInserter] that inserts the next operation
/// at the end of the given [BasicBlock].
pub fn new_at_block_end(block: Ptr<BasicBlock>) -> Self {
Self {
insertion_point: OpInsertionPoint::AtBlockEnd(block),
}
}
/// Creates a new [OpInserter] that inserts the next operation
/// after the given [Operation].
pub fn new_after_operation(op: Ptr<Operation>) -> Self {
Self {
insertion_point: OpInsertionPoint::AfterOperation(op),
}
}
/// Creates a new [OpInserter] that inserts the next operation
/// before the given [Operation].
pub fn new_before_operation(op: Ptr<Operation>) -> Self {
Self {
insertion_point: OpInsertionPoint::BeforeOperation(op),
}
}
/// Appends an [Operation] at the current insertion point.
/// The insertion point is updated to be after this newly inserted [Operation].
pub fn append_operation(&mut self, ctx: &Context, operation: Ptr<Operation>) {
// Insert the operation at the current insertion point
self.insert_operation(ctx, operation);
// Update the insertion point to be after the newly inserted operation
self.insertion_point = OpInsertionPoint::AfterOperation(operation);
}
/// Appends an [Op] at the current insertion point.
/// The insertion point is updated to be after this newly inserted [Op].
pub fn append_op(&mut self, ctx: &Context, op: impl Op) {
let operation = op.get_operation();
self.append_operation(ctx, operation);
}
/// Inserts an [Operation] at the current insertion point.
/// To insert a sequence in-order, use [append_operation](Self::append_operation).
pub fn insert_operation(&self, ctx: &Context, operation: Ptr<Operation>) {
match self.insertion_point {
OpInsertionPoint::AtBlockStart(block) => {
// Insert operation at the start of the block
operation.insert_at_front(block, ctx);
}
OpInsertionPoint::AtBlockEnd(block) => {
// Insert operation at the end of the block
operation.insert_at_back(block, ctx);
}
OpInsertionPoint::AfterOperation(op) => {
// Insert operation after the specified operation
operation.insert_after(ctx, op);
}
OpInsertionPoint::BeforeOperation(op) => {
// Insert operation before the specified operation
operation.insert_before(ctx, op);
}
}
}
/// Inserts an [Op] at the current insertion point.
/// To insert a sequence in-order, use [append_op](Self::append_op).
pub fn insert_op(&self, ctx: &Context, op: impl Op) {
let operation = op.get_operation();
self.insert_operation(ctx, operation);
}
/// Gets the current insertion point.
pub fn insertion_point(&self) -> OpInsertionPoint {
self.insertion_point
}
/// Get the current insertion block.
pub fn get_insertion_block(&self, ctx: &Context) -> Ptr<BasicBlock> {
match self.insertion_point {
OpInsertionPoint::AtBlockStart(block) => block,
OpInsertionPoint::AtBlockEnd(block) => block,
OpInsertionPoint::AfterOperation(op) => op
.deref(ctx)
.get_parent_block()
.expect("Insertion point Operation must have parent block"),
OpInsertionPoint::BeforeOperation(op) => op
.deref(ctx)
.get_parent_block()
.expect("Insertion point Operation must have parent block"),
}
}
}