Skip to main content

lift_export/
llvm.rs

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        // Declare runtime functions
108        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 &region.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                                        // Emit input argument gathering
187                                        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                                        // Annotate the LLVM type of the first
207                                        // input/result (informational; the
208                                        // runtime calls take pointers).
209                                        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}