1use lift_core::context::Context;
2use lift_core::types::{CoreType, TypeData};
3use std::fmt::Write;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum LlvmExportError {
8 #[error("Unsupported operation for LLVM export: {0}")]
9 UnsupportedOp(String),
10 #[error("Export error: {0}")]
11 General(String),
12}
13
14#[derive(Debug)]
15pub struct LlvmExporter;
16
17impl LlvmExporter {
18 pub fn new() -> Self {
19 Self
20 }
21
22 fn llvm_type_for_value(&self, ctx: &Context, val_key: lift_core::values::ValueKey) -> String {
23 if let Some(val) = ctx.get_value(val_key) {
24 match ctx.resolve_type(val.ty) {
25 CoreType::Opaque {
26 data: TypeData::Tensor(info),
27 ..
28 } => {
29 let mut elems = 1usize;
30 for dim in &info.shape {
31 if let Some(s) = dim.static_value() {
32 elems = elems.saturating_mul(s);
33 }
34 }
35 let dt = match info.dtype.byte_size() {
36 2 => "half",
37 4 => "float",
38 8 => "double",
39 _ => "float",
40 };
41 format!("<{} x {}>", elems, dt)
42 }
43 CoreType::Float { bits: 32 } => "float".to_string(),
44 CoreType::Float { bits: 64 } => "double".to_string(),
45 CoreType::Integer { bits, .. } => format!("i{}", bits),
46 CoreType::Boolean => "i1".to_string(),
47 _ => "ptr".to_string(),
48 }
49 } else {
50 "ptr".to_string()
51 }
52 }
53
54 fn runtime_call_for_op(&self, op_name: &str) -> &str {
55 match op_name {
56 "tensor.matmul" => "lift_rt_matmul",
57 "tensor.add" => "lift_rt_add",
58 "tensor.sub" => "lift_rt_sub",
59 "tensor.mul" => "lift_rt_mul",
60 "tensor.div" => "lift_rt_div",
61 "tensor.relu" => "lift_rt_relu",
62 "tensor.gelu" => "lift_rt_gelu",
63 "tensor.silu" => "lift_rt_silu",
64 "tensor.softmax" => "lift_rt_softmax",
65 "tensor.sigmoid" => "lift_rt_sigmoid",
66 "tensor.tanh" => "lift_rt_tanh",
67 "tensor.layernorm" => "lift_rt_layernorm",
68 "tensor.rmsnorm" => "lift_rt_rmsnorm",
69 "tensor.batchnorm" => "lift_rt_batchnorm",
70 "tensor.conv2d" => "lift_rt_conv2d",
71 "tensor.conv1d" => "lift_rt_conv1d",
72 "tensor.maxpool2d" => "lift_rt_maxpool2d",
73 "tensor.avgpool2d" => "lift_rt_avgpool2d",
74 "tensor.global_avgpool" => "lift_rt_global_avgpool",
75 "tensor.attention" => "lift_rt_attention",
76 "tensor.multi_head_attention" => "lift_rt_multi_head_attention",
77 "tensor.grouped_query_attention" => "lift_rt_grouped_query_attention",
78 "tensor.flash_attention" => "lift_rt_flash_attention",
79 "tensor.cross_attention" => "lift_rt_cross_attention",
80 "tensor.sliding_window_attention" => "lift_rt_sliding_window_attention",
81 "tensor.paged_attention" => "lift_rt_paged_attention",
82 "tensor.embedding" => "lift_rt_embedding",
83 "tensor.linear" => "lift_rt_linear",
84 "tensor.reshape" => "lift_rt_reshape",
85 "tensor.transpose" => "lift_rt_transpose",
86 "tensor.concat" => "lift_rt_concat",
87 "tensor.moe_dispatch" => "lift_rt_moe_dispatch",
88 "tensor.moe_combine" => "lift_rt_moe_combine",
89 "tensor.fused_matmul_bias_relu" => "lift_rt_fused_matmul_bias_relu",
90 "tensor.fused_matmul_bias" => "lift_rt_fused_matmul_bias",
91 "tensor.fused_linear_gelu" => "lift_rt_fused_linear_gelu",
92 "tensor.fused_linear_silu" => "lift_rt_fused_linear_silu",
93 "tensor.quantize" => "lift_rt_quantize",
94 "tensor.dequantize" => "lift_rt_dequantize",
95 _ => "lift_rt_generic_op",
96 }
97 }
98
99 pub fn export(&self, ctx: &Context) -> Result<String, LlvmExportError> {
100 let mut output = String::new();
101
102 let _ = writeln!(output, "; LIFT IR -> LLVM IR export");
103 let _ = writeln!(output, "; Generated by LIFT compiler framework");
104 let _ = writeln!(output, "; Target: GPU runtime (cuBLAS/cuDNN backend)");
105 let _ = writeln!(output);
106
107 let _ = writeln!(output, "; === Runtime function declarations ===");
109 let rt_funcs = [
110 "lift_rt_matmul",
111 "lift_rt_add",
112 "lift_rt_sub",
113 "lift_rt_mul",
114 "lift_rt_div",
115 "lift_rt_relu",
116 "lift_rt_gelu",
117 "lift_rt_silu",
118 "lift_rt_softmax",
119 "lift_rt_sigmoid",
120 "lift_rt_tanh",
121 "lift_rt_layernorm",
122 "lift_rt_rmsnorm",
123 "lift_rt_batchnorm",
124 "lift_rt_conv2d",
125 "lift_rt_conv1d",
126 "lift_rt_maxpool2d",
127 "lift_rt_avgpool2d",
128 "lift_rt_global_avgpool",
129 "lift_rt_attention",
130 "lift_rt_multi_head_attention",
131 "lift_rt_grouped_query_attention",
132 "lift_rt_flash_attention",
133 "lift_rt_cross_attention",
134 "lift_rt_sliding_window_attention",
135 "lift_rt_paged_attention",
136 "lift_rt_embedding",
137 "lift_rt_linear",
138 "lift_rt_reshape",
139 "lift_rt_transpose",
140 "lift_rt_concat",
141 "lift_rt_moe_dispatch",
142 "lift_rt_moe_combine",
143 "lift_rt_fused_matmul_bias_relu",
144 "lift_rt_fused_matmul_bias",
145 "lift_rt_fused_linear_gelu",
146 "lift_rt_fused_linear_silu",
147 "lift_rt_quantize",
148 "lift_rt_dequantize",
149 "lift_rt_generic_op",
150 ];
151 for f in &rt_funcs {
152 let _ = writeln!(output, "declare ptr @{}(ptr, ptr, ptr, i64, i64)", f);
153 }
154 let _ = writeln!(output);
155
156 for module in &ctx.modules {
157 let name = ctx.strings.resolve(module.name);
158 let _ = writeln!(output, "; === Module: {} ===", name);
159 let _ = writeln!(output);
160
161 for func in &module.functions {
162 let fname = ctx.strings.resolve(func.name);
163 let _ = write!(output, "define ptr @{}(", fname);
164
165 for (i, _param) in func.params.iter().enumerate() {
166 if i > 0 {
167 let _ = write!(output, ", ");
168 }
169 let _ = write!(output, "ptr %arg{}", i);
170 }
171
172 let _ = writeln!(output, ") {{");
173 let _ = writeln!(output, "entry:");
174
175 let mut result_counter = 0usize;
176
177 if let Some(region_key) = func.body {
178 if let Some(region) = ctx.get_region(region_key) {
179 for &block_key in ®ion.blocks {
180 if let Some(block) = ctx.get_block(block_key) {
181 for &op_key in &block.ops {
182 if let Some(op) = ctx.get_op(op_key) {
183 let op_name = ctx.strings.resolve(op.name).to_string();
184 let rt_func = self.runtime_call_for_op(&op_name);
185
186 let in0 = if !op.inputs.is_empty() {
188 format!("%arg{}", 0)
189 } else {
190 "null".to_string()
191 };
192 let in1 = if op.inputs.len() > 1 {
193 format!("%arg{}", 1)
194 } else {
195 "null".to_string()
196 };
197
198 let num_in = op.inputs.len() as i64;
199 let num_out = op.results.len() as i64;
200
201 let _ = writeln!(
202 output,
203 " ; {} ({} inputs -> {} outputs)",
204 op_name, num_in, num_out
205 );
206 if let Some(&first_in) = op.inputs.first() {
210 let ty = self.llvm_type_for_value(ctx, first_in);
211 let _ = writeln!(output, " ; input type: {}", ty);
212 }
213 let _ = writeln!(output,
214 " %r{} = call ptr @{}(ptr {}, ptr {}, ptr null, i64 {}, i64 {})",
215 result_counter, rt_func, in0, in1, num_in, num_out);
216 result_counter += 1;
217 }
218 }
219 }
220 }
221 }
222 }
223
224 if result_counter > 0 {
225 let _ = writeln!(output, " ret ptr %r{}", result_counter - 1);
226 } else {
227 let _ = writeln!(output, " ret ptr null");
228 }
229 let _ = writeln!(output, "}}");
230 let _ = writeln!(output);
231 }
232 }
233
234 Ok(output)
235 }
236}
237
238impl Default for LlvmExporter {
239 fn default() -> Self {
240 Self::new()
241 }
242}