1use crate::{
4 ir::{
5 attribute::{FlatSymbolRefAttribute, StringAttribute, TypeAttribute},
6 operation::OperationBuilder,
7 r#type::FunctionType,
8 Attribute, Identifier, Location, Operation, Region, Type, Value,
9 },
10 Context,
11};
12
13pub fn call<'c>(
15 context: &'c Context,
16 function: FlatSymbolRefAttribute<'c>,
17 arguments: &[Value<'c, '_>],
18 result_types: &[Type<'c>],
19 location: Location<'c>,
20) -> Operation<'c> {
21 OperationBuilder::new("func.call", location)
22 .add_attributes(&[(Identifier::new(context, "callee"), function.into())])
23 .add_operands(arguments)
24 .add_results(result_types)
25 .build()
26 .expect("valid operation")
27}
28
29pub fn call_indirect<'c>(
31 function: Value<'c, '_>,
32 arguments: &[Value<'c, '_>],
33 result_types: &[Type<'c>],
34 location: Location<'c>,
35) -> Operation<'c> {
36 OperationBuilder::new("func.call_indirect", location)
37 .add_operands(&[function])
38 .add_operands(arguments)
39 .add_results(result_types)
40 .build()
41 .expect("valid operation")
42}
43
44pub fn constant<'c>(
46 context: &'c Context,
47 function: FlatSymbolRefAttribute<'c>,
48 r#type: FunctionType<'c>,
49 location: Location<'c>,
50) -> Operation<'c> {
51 OperationBuilder::new("func.constant", location)
52 .add_attributes(&[(Identifier::new(context, "value"), function.into())])
53 .add_results(&[r#type.into()])
54 .build()
55 .expect("valid operation")
56}
57
58pub fn func<'c>(
60 context: &'c Context,
61 name: StringAttribute<'c>,
62 r#type: TypeAttribute<'c>,
63 region: Region<'c>,
64 attributes: &[(Identifier<'c>, Attribute<'c>)],
65 location: Location<'c>,
66) -> Operation<'c> {
67 OperationBuilder::new("func.func", location)
68 .add_attributes(&[
69 (Identifier::new(context, "sym_name"), name.into()),
70 (Identifier::new(context, "function_type"), r#type.into()),
71 ])
72 .add_attributes(attributes)
73 .add_regions([region])
74 .build()
75 .expect("valid operation")
76}
77
78pub fn r#return<'c>(operands: &[Value<'c, '_>], location: Location<'c>) -> Operation<'c> {
80 OperationBuilder::new("func.return", location)
81 .add_operands(operands)
82 .build()
83 .expect("valid operation")
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::{
90 ir::{Block, Module, Type},
91 test::create_test_context,
92 };
93
94 #[test]
95 fn compile_call() {
96 let context = create_test_context();
97
98 let location = Location::unknown(&context);
99 let module = Module::new(location);
100 let index_type = Type::index(&context);
101 let function_type = FunctionType::new(&context, &[index_type], &[index_type]);
102
103 let function = func(
104 &context,
105 StringAttribute::new(&context, "foo"),
106 TypeAttribute::new(function_type.into()),
107 {
108 let block = Block::new(&[(index_type, location)]);
109
110 let value = block
111 .append_operation(call(
112 &context,
113 FlatSymbolRefAttribute::new(&context, "foo"),
114 &[block.argument(0).unwrap().into()],
115 &[index_type],
116 location,
117 ))
118 .result(0)
119 .unwrap()
120 .into();
121 block.append_operation(r#return(&[value], location));
122
123 let region = Region::new();
124 region.append_block(block);
125 region
126 },
127 &[],
128 Location::unknown(&context),
129 );
130
131 module.body().append_operation(function);
132
133 assert!(module.as_operation().verify());
134 insta::assert_snapshot!(module.as_operation());
135 }
136
137 #[test]
138 fn compile_call_indirect() {
139 let context = create_test_context();
140
141 let location = Location::unknown(&context);
142 let module = Module::new(location);
143 let index_type = Type::index(&context);
144 let function_type = FunctionType::new(&context, &[index_type], &[index_type]);
145
146 let function = func(
147 &context,
148 StringAttribute::new(&context, "foo"),
149 TypeAttribute::new(function_type.into()),
150 {
151 let block = Block::new(&[(index_type, location)]);
152
153 let function = block.append_operation(constant(
154 &context,
155 FlatSymbolRefAttribute::new(&context, "foo"),
156 function_type,
157 location,
158 ));
159 let value = block
160 .append_operation(call_indirect(
161 function.result(0).unwrap().into(),
162 &[block.argument(0).unwrap().into()],
163 &[index_type],
164 location,
165 ))
166 .result(0)
167 .unwrap()
168 .into();
169 block.append_operation(r#return(&[value], location));
170
171 let region = Region::new();
172 region.append_block(block);
173 region
174 },
175 &[],
176 Location::unknown(&context),
177 );
178
179 module.body().append_operation(function);
180
181 assert!(module.as_operation().verify());
182 insta::assert_snapshot!(module.as_operation());
183 }
184
185 #[test]
186 fn compile_function() {
187 let context = create_test_context();
188
189 let location = Location::unknown(&context);
190 let module = Module::new(location);
191
192 let integer_type = Type::index(&context);
193
194 let function = {
195 let block = Block::new(&[(integer_type, location)]);
196
197 block.append_operation(r#return(&[block.argument(0).unwrap().into()], location));
198
199 let region = Region::new();
200 region.append_block(block);
201
202 func(
203 &context,
204 StringAttribute::new(&context, "foo"),
205 TypeAttribute::new(
206 FunctionType::new(&context, &[integer_type], &[integer_type]).into(),
207 ),
208 region,
209 &[],
210 Location::unknown(&context),
211 )
212 };
213
214 module.body().append_operation(function);
215
216 assert!(module.as_operation().verify());
217 insta::assert_snapshot!(module.as_operation());
218 }
219
220 #[test]
221 fn compile_external_function() {
222 let context = create_test_context();
223
224 let location = Location::unknown(&context);
225 let module = Module::new(location);
226
227 let integer_type = Type::index(&context);
228
229 let function = func(
230 &context,
231 StringAttribute::new(&context, "foo"),
232 TypeAttribute::new(
233 FunctionType::new(&context, &[integer_type], &[integer_type]).into(),
234 ),
235 Region::new(),
236 &[(
237 Identifier::new(&context, "sym_visibility"),
238 StringAttribute::new(&context, "private").into(),
239 )],
240 Location::unknown(&context),
241 );
242
243 module.body().append_operation(function);
244
245 assert!(module.as_operation().verify());
246 insta::assert_snapshot!(module.as_operation());
247 }
248}