1use alloc::vec::Vec;
7
8use pliron::{
9 builtin::{
10 op_interfaces::{OneRegionInterface, SymbolOpInterface},
11 ops::{ConstantOp as BuiltinConstantOp, FuncOp as BuiltinFuncOp, ModuleOp},
12 type_interfaces::FunctionTypeInterface,
13 types::{FunctionType as BuiltinFunctionType, UnitType},
14 },
15 common_traits::Verify,
16 context::{Context, Ptr},
17 derive::{op_interface_impl, type_interface_impl},
18 input_err_noloc, input_error_noloc,
19 irbuild::{
20 dialect_conversion::{self, DialectConversion, DialectConversionRewriter, OperandsInfo},
21 inserter::Inserter,
22 rewriter::Rewriter,
23 },
24 op::{Op, op_impls},
25 operation::Operation,
26 pass::{GuardedPass, OpGuard, OpPass, Pass, PassResult},
27 region::Region,
28 result::{Error, ErrorKind, Result},
29 r#type::{TypeHandle, TypedHandle, type_cast},
30};
31
32use crate::{
33 ToLLVMDialect, ToLLVMType,
34 ops::{ConstantOp as LLVMConstantOp, FuncOp as LLVMFuncOp},
35 types::{FuncType as LLVMFuncType, VoidType},
36};
37
38#[derive(thiserror::Error, Debug)]
39pub enum BuiltinToLLVMConversionError {
40 #[error("Invalid function type, cannot be converted to LLVM function type")]
41 InvalidFunctionType,
42}
43
44#[type_interface_impl]
45impl ToLLVMType for BuiltinFunctionType {
46 fn convert(&self, ctx: &Context) -> Result<TypeHandle> {
47 let arg_types = self.arg_types();
48 let res_types = self.res_types();
49
50 let convert_type_to_llvm = |ty: TypeHandle| {
51 type_cast::<dyn ToLLVMType>(&*ty.deref(ctx))
52 .map(|ty| ty.convert(ctx))
53 .unwrap_or(Ok(ty))
54 };
55
56 let arg_types = arg_types
57 .into_iter()
58 .map(convert_type_to_llvm)
59 .collect::<Result<Vec<_>>>()?;
60 let res_types = res_types
61 .into_iter()
62 .map(convert_type_to_llvm)
63 .collect::<Result<Vec<_>>>()?;
64
65 if res_types.is_empty() || res_types.len() > 1 {
66 return input_err_noloc!(BuiltinToLLVMConversionError::InvalidFunctionType);
67 }
68 let result_type = res_types[0];
69
70 let llvm_func_type = LLVMFuncType::get(ctx, result_type, arg_types, false);
71 Ok(llvm_func_type.into())
72 }
73}
74
75#[type_interface_impl]
76impl ToLLVMType for UnitType {
77 fn convert(&self, ctx: &Context) -> Result<TypeHandle> {
78 Ok(VoidType::get(ctx).to_handle())
79 }
80}
81
82#[op_interface_impl]
84impl ToLLVMDialect for BuiltinConstantOp {
85 fn rewrite(
86 &self,
87 ctx: &mut Context,
88 rewriter: &mut DialectConversionRewriter,
89 _operands_info: &OperandsInfo,
90 ) -> Result<()> {
91 let const_value = pliron::dyn_clone::clone_box(&*self.get_value(ctx));
92
93 let llvm_const = LLVMConstantOp::new(ctx, const_value);
95
96 if let Err(e @ Error { .. }) = llvm_const.verify(ctx) {
97 return Err(Error {
98 kind: ErrorKind::InvalidInput,
99 backtrace: pliron::std_deps::backtrace::Backtrace::capture(),
101 ..e
102 });
103 }
104
105 rewriter.insert_operation(ctx, llvm_const.get_operation());
107
108 let old_op = self.get_operation();
110 rewriter.replace_operation(ctx, old_op, llvm_const.get_operation());
111
112 Ok(())
113 }
114}
115
116#[op_interface_impl]
118impl ToLLVMDialect for BuiltinFuncOp {
119 fn rewrite(
120 &self,
121 ctx: &mut Context,
122 rewriter: &mut DialectConversionRewriter,
123 _operands_info: &OperandsInfo,
124 ) -> Result<()> {
125 let func_name = self.get_symbol_name(ctx);
127
128 let builtin_func_type = self.get_type(ctx);
130 let llvm_func_type = type_cast::<dyn ToLLVMType>(&*builtin_func_type.deref(ctx))
131 .ok_or_else(|| {
132 input_error_noloc!("builtin.func type does not implement ToLLVMType interface")
133 })?
134 .convert(ctx)?;
135 let llvm_func_type = TypedHandle::from_handle(llvm_func_type, ctx)?;
136
137 let llvm_func = LLVMFuncOp::new(ctx, func_name, llvm_func_type);
139
140 Region::move_to_op(self.get_region(ctx), llvm_func.get_operation(), ctx);
142
143 let old_op = self.get_operation();
145
146 rewriter.insert_operation(ctx, llvm_func.get_operation());
148
149 rewriter.replace_operation(ctx, old_op, llvm_func.get_operation());
151
152 Ok(())
153 }
154}
155
156#[derive(Default)]
158pub struct BuiltinToLLVMConversion;
159
160impl DialectConversion for BuiltinToLLVMConversion {
161 fn can_convert_op(&self, ctx: &Context, op: Ptr<Operation>) -> bool {
162 let op_dyn = Operation::get_op_dyn(op, ctx);
163 let op_ref = op_dyn.op_ref();
164
165 op_impls::<dyn ToLLVMDialect>(op_ref)
167 }
168
169 fn rewrite(
170 &mut self,
171 ctx: &mut Context,
172 rewriter: &mut DialectConversionRewriter,
173 op: Ptr<Operation>,
174 operands_info: &OperandsInfo,
175 ) -> Result<()> {
176 let op_dyn = Operation::get_op_dyn(op, ctx);
177 let op_ref = op_dyn.op_ref();
178
179 if let Some(to_llvm) = pliron::op::op_cast::<dyn ToLLVMDialect>(op_ref) {
181 to_llvm.rewrite(ctx, rewriter, operands_info)?;
182 }
183
184 Ok(())
185 }
186}
187
188pub fn convert_builtin_to_llvm(ctx: &mut Context, module: ModuleOp) -> Result<PassResult> {
190 builtin_to_llvm_pass().run(
191 module.get_operation(),
192 ctx,
193 &mut pliron::pass::AnalysisManager::default(),
194 )
195}
196
197pub fn builtin_to_llvm_pass()
200-> OpPass<ModuleOp, dialect_conversion::PassWrapper<BuiltinToLLVMConversion>> {
201 let pass = dialect_conversion::PassWrapper::<BuiltinToLLVMConversion>::new(
202 "builtin_to_llvm",
203 BuiltinToLLVMConversion,
204 );
205 GuardedPass::new(OpGuard::<ModuleOp>::default(), pass)
206}