irvm_lower/
lib.rs

1//! ## irmv-lower
2//!
3//! Lower irvm IR to other IRs.
4//!
5//! This crates currently only allows you to lower irmv IR to LLVM IR.
6//!
7//! See the [`llvm`] submodule for more information.
8//!
9//! ```bash
10//! cargo add irvm-lower
11//! ```
12//!
13
14pub mod llvm;
15
16#[cfg(test)]
17mod test {
18
19    use irvm::{
20        block::{GepIndex, IcmpCond},
21        common::Location,
22        function::Parameter,
23        module::Module,
24        types::{StructType, Type, TypeStorage},
25        value::Operand,
26    };
27
28    use crate::llvm::{JitValue, create_jit_engine, lower_module_to_llvmir};
29
30    #[test]
31    fn test_function_llvm() -> Result<(), Box<dyn std::error::Error>> {
32        let mut module = Module::new("example", Location::unknown());
33        let mut storage = TypeStorage::new();
34        let _bool_ty = storage.add_type(Type::Int(1), Some("bool"));
35        let i32_ty = storage.add_type(Type::Int(32), Some("u32"));
36        let _i64_ty = storage.add_type(Type::Int(64), Some("u64"));
37        let _ptr_ty = storage.add_type(
38            Type::Ptr {
39                pointee: i32_ty,
40                address_space: None,
41            },
42            Some("*i32"),
43        );
44
45        let main_func = module
46            .add_function(
47                "main",
48                &[Parameter::new(i32_ty, Location::Unknown)],
49                Some(i32_ty),
50                Location::Unknown,
51            )
52            .get_id();
53        let test_func = module
54            .add_function(
55                "test",
56                &[Parameter::new(i32_ty, Location::Unknown)],
57                Some(i32_ty),
58                Location::Unknown,
59            )
60            .get_id();
61
62        let test_func_ret_ty = module.get_function(test_func).result_type;
63
64        // main function
65        {
66            let func = module.get_function_mut(main_func);
67            let param = func.param(0)?;
68            let param_dbg = func.create_debug_var_param("argv", i32_ty, 0, &Location::Unknown);
69            let entry_block = func.entry_block;
70
71            let value = func.blocks[entry_block].instr_add(
72                &param,
73                &Operand::const_int(4, i32_ty),
74                Location::Unknown,
75            )?;
76
77            func.blocks[entry_block].instr_dbg_value(
78                value.clone(),
79                param_dbg,
80                Location::Unknown,
81            )?;
82
83            let then_block = func.add_block(&[]);
84            let else_block = func.add_block(&[]);
85            let final_block = func.add_block(&[i32_ty]);
86
87            let cond = func.blocks[entry_block].instr_icmp(
88                IcmpCond::Eq,
89                value.clone(),
90                Operand::const_int(6, i32_ty),
91                Location::Unknown,
92                &storage,
93            )?;
94
95            func.blocks[entry_block].instr_cond_jmp(
96                then_block,
97                else_block,
98                &cond,
99                &[],
100                &[],
101                Location::Unknown,
102            );
103
104            // then block
105            {
106                let value = func.blocks[then_block].instr_add(
107                    &value,
108                    &Operand::const_int(2, i32_ty),
109                    Location::Unknown,
110                )?;
111                func.blocks[then_block].instr_jmp(final_block, &[value], Location::Unknown);
112            }
113
114            // else block
115            {
116                let value = func.blocks[else_block].instr_add(
117                    &value,
118                    &Operand::const_int(6, i32_ty),
119                    Location::Unknown,
120                )?;
121                func.blocks[else_block].instr_jmp(final_block, &[value], Location::Unknown);
122            }
123
124            // final block
125            {
126                let param = func.blocks[final_block].arg(0)?;
127                let value = func.blocks[final_block].instr_call(
128                    test_func,
129                    &[param],
130                    test_func_ret_ty.unwrap(),
131                    Location::Unknown,
132                )?;
133                func.blocks[final_block].instr_ret(Some(&value), Location::Unknown);
134            }
135        }
136
137        // test function
138        {
139            let func = module.get_function_mut(test_func);
140            let value = func.param(0)?;
141            func.entry_block()
142                .instr_ret(Some(&value), Location::Unknown);
143        }
144
145        let result = test_run_module(
146            &module,
147            &storage,
148            "main",
149            &[JitValue::U32(4)],
150            JitValue::U32(0),
151        )?;
152        assert_eq!(result, JitValue::U32(14));
153
154        Ok(())
155    }
156
157    #[test]
158    fn test_struct() -> Result<(), Box<dyn std::error::Error>> {
159        let mut module = Module::new("example", Location::unknown());
160        let mut storage = TypeStorage::new();
161        let i32_ty = storage.add_type(Type::Int(32), Some("u32"));
162        let ptr_ty = storage.add_type(
163            Type::Ptr {
164                pointee: i32_ty,
165                address_space: None,
166            },
167            Some("*u32"),
168        );
169
170        let strct_type = storage.add_type(
171            Type::Struct(
172                StructType {
173                    packed: false,
174                    ident: None,
175                    fields: vec![ptr_ty, i32_ty],
176                    debug_field_names: vec![
177                        ("ptr".to_string(), Location::unknown()),
178                        ("x".to_string(), Location::unknown()),
179                    ],
180                }
181                .into(),
182            ),
183            Some("hello"),
184        );
185
186        let func = module
187            .add_function(
188                "example",
189                &[
190                    Parameter::new(i32_ty, Location::Unknown),
191                    Parameter::new(strct_type, Location::Unknown),
192                ],
193                None,
194                Location::Unknown,
195            )
196            .get_id();
197
198        let func = module.get_function_mut(func);
199
200        let entry = func.entry_block;
201
202        func.blocks[entry].instr_ret(None, Location::Unknown);
203
204        let ir = lower_module_to_llvmir(&module, &storage)?;
205
206        ir.dump();
207
208        Ok(())
209    }
210
211    #[test]
212    fn test_gep() -> Result<(), Box<dyn std::error::Error>> {
213        let mut module = Module::new("gepexample", Location::unknown());
214        let mut storage = TypeStorage::new();
215        let i32_ty = storage.add_type(Type::Int(32), Some("u32"));
216        let ptr_ty = storage.add_type(
217            Type::Ptr {
218                pointee: i32_ty,
219                address_space: None,
220            },
221            Some("*u32"),
222        );
223
224        let func = module
225            .add_function(
226                "example",
227                &[Parameter::new(i32_ty, Location::Unknown)],
228                Some(i32_ty),
229                Location::Unknown,
230            )
231            .get_id();
232
233        let func = module.get_function_mut(func);
234
235        let entry = func.entry_block;
236
237        let param1 = func.param(0)?;
238
239        let ptr_val =
240            func.blocks[entry].instr_alloca(ptr_ty, 4, None, Location::Unknown, &storage)?;
241        let k1 = Operand::const_int(1, i32_ty);
242        func.blocks[entry].instr_store(
243            ptr_val.clone(),
244            k1.clone(),
245            None,
246            Location::Unknown,
247            &storage,
248        )?;
249
250        let ptr_idx1 = func.blocks[entry].instr_gep(
251            ptr_val,
252            &[GepIndex::Const(1)],
253            ptr_ty,
254            Location::Unknown,
255            &storage,
256        )?;
257        func.blocks[entry].instr_store(
258            ptr_idx1.clone(),
259            param1,
260            None,
261            Location::Unknown,
262            &storage,
263        )?;
264
265        let result = func.blocks[entry].instr_load(ptr_idx1, None, Location::Unknown, &storage)?;
266
267        func.blocks[entry].instr_ret(Some(&result), Location::Unknown);
268
269        let result = test_run_module(
270            &module,
271            &storage,
272            "example",
273            &[JitValue::U32(2)],
274            JitValue::U32(0),
275        )?;
276        assert_eq!(result, JitValue::U32(2));
277
278        Ok(())
279    }
280
281    fn test_run_module(
282        module: &Module,
283        storage: &TypeStorage,
284        name: &str,
285        args: &[JitValue],
286        ret_ty: JitValue,
287    ) -> Result<JitValue, crate::llvm::Error> {
288        let result = lower_module_to_llvmir(module, storage)?;
289        let engine = create_jit_engine(result, 3)?;
290
291        let res = unsafe { engine.execute(name, args, ret_ty)? };
292
293        Ok(res)
294    }
295}