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