1pub 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 fn maybe_dump_ir(ir: &crate::llvm::CompileResult) {
32 if std::env::var("IRVM_DUMP_IR").is_ok() {
33 ir.dump();
34 }
35 }
36
37 #[test]
38 fn test_function_llvm() -> Result<(), Box<dyn std::error::Error>> {
39 let mut module = Module::new("example", Location::unknown());
40 let mut storage = TypeStorage::new();
41 let _bool_ty = storage.add_type(Type::Int(1), Some("bool"));
42 let i32_ty = storage.add_type(Type::Int(32), Some("u32"));
43 let _i64_ty = storage.add_type(Type::Int(64), Some("u64"));
44 let _ptr_ty = storage.add_type(
45 Type::Ptr {
46 pointee: i32_ty,
47 address_space: None,
48 },
49 Some("*i32"),
50 );
51
52 let main_func = module
53 .add_function(
54 "main",
55 &[Parameter::new(i32_ty, Location::Unknown)],
56 Some(i32_ty),
57 Location::Unknown,
58 )
59 .get_id();
60 let test_func = module
61 .add_function(
62 "test",
63 &[Parameter::new(i32_ty, Location::Unknown)],
64 Some(i32_ty),
65 Location::Unknown,
66 )
67 .get_id();
68
69 let test_func_ret_ty = module.get_function(test_func).result_type;
70
71 {
73 let func = module.get_function_mut(main_func);
74 let param = func.param(0)?;
75 let param_dbg = func.create_debug_var_param("argv", i32_ty, 0, &Location::Unknown);
76 let entry_block = func.entry_block;
77
78 let value = func.blocks[entry_block].instr_add(
79 ¶m,
80 &Operand::const_int(4, i32_ty),
81 Location::Unknown,
82 )?;
83
84 func.blocks[entry_block].instr_dbg_value(
85 value.clone(),
86 param_dbg,
87 Location::Unknown,
88 )?;
89
90 let then_block = func.add_block(&[]);
91 let else_block = func.add_block(&[]);
92 let final_block = func.add_block(&[i32_ty]);
93
94 let cond = func.blocks[entry_block].instr_icmp(
95 IcmpCond::Eq,
96 value.clone(),
97 Operand::const_int(6, i32_ty),
98 Location::Unknown,
99 &storage,
100 )?;
101
102 func.blocks[entry_block].instr_cond_jmp(
103 then_block,
104 else_block,
105 &cond,
106 &[],
107 &[],
108 Location::Unknown,
109 );
110
111 {
113 let value = func.blocks[then_block].instr_add(
114 &value,
115 &Operand::const_int(2, i32_ty),
116 Location::Unknown,
117 )?;
118 func.blocks[then_block].instr_jmp(final_block, &[value], Location::Unknown);
119 }
120
121 {
123 let value = func.blocks[else_block].instr_add(
124 &value,
125 &Operand::const_int(6, i32_ty),
126 Location::Unknown,
127 )?;
128 func.blocks[else_block].instr_jmp(final_block, &[value], Location::Unknown);
129 }
130
131 {
133 let param = func.blocks[final_block].arg(0)?;
134 let value = func.blocks[final_block].instr_call(
135 test_func,
136 &[param],
137 test_func_ret_ty.unwrap(),
138 Location::Unknown,
139 )?;
140 func.blocks[final_block].instr_ret(Some(&value), Location::Unknown);
141 }
142 }
143
144 {
146 let func = module.get_function_mut(test_func);
147 let value = func.param(0)?;
148 func.entry_block()
149 .instr_ret(Some(&value), Location::Unknown);
150 }
151
152 let result = test_run_module(
153 &module,
154 &storage,
155 "main",
156 &[JitValue::U32(4)],
157 JitValue::U32(0),
158 )?;
159 assert_eq!(result, JitValue::U32(14));
160
161 Ok(())
162 }
163
164 #[test]
165 fn test_struct() -> Result<(), Box<dyn std::error::Error>> {
166 let mut module = Module::new("example", Location::unknown());
167 let mut storage = TypeStorage::new();
168 let i32_ty = storage.add_type(Type::Int(32), Some("u32"));
169 let ptr_ty = storage.add_type(
170 Type::Ptr {
171 pointee: i32_ty,
172 address_space: None,
173 },
174 Some("*u32"),
175 );
176
177 let strct_type = storage.add_type(
178 Type::Struct(
179 StructType {
180 packed: false,
181 ident: None,
182 fields: vec![ptr_ty, i32_ty],
183 debug_field_names: vec![
184 ("ptr".to_string(), Location::unknown()),
185 ("x".to_string(), Location::unknown()),
186 ],
187 }
188 .into(),
189 ),
190 Some("hello"),
191 );
192
193 let func = module
194 .add_function(
195 "example",
196 &[
197 Parameter::new(i32_ty, Location::Unknown),
198 Parameter::new(strct_type, Location::Unknown),
199 ],
200 None,
201 Location::Unknown,
202 )
203 .get_id();
204
205 let func = module.get_function_mut(func);
206
207 let entry = func.entry_block;
208
209 func.blocks[entry].instr_ret(None, Location::Unknown);
210
211 let ir = lower_module_to_llvmir(&module, &storage)?;
212
213 maybe_dump_ir(&ir);
214
215 Ok(())
216 }
217
218 #[test]
219 fn test_gep() -> Result<(), Box<dyn std::error::Error>> {
220 let mut module = Module::new("gepexample", Location::unknown());
221 let mut storage = TypeStorage::new();
222 let i32_ty = storage.add_type(Type::Int(32), Some("u32"));
223 let ptr_ty = storage.add_type(
224 Type::Ptr {
225 pointee: i32_ty,
226 address_space: None,
227 },
228 Some("*u32"),
229 );
230
231 let func = module
232 .add_function(
233 "example",
234 &[Parameter::new(i32_ty, Location::Unknown)],
235 Some(i32_ty),
236 Location::Unknown,
237 )
238 .get_id();
239
240 let func = module.get_function_mut(func);
241
242 let entry = func.entry_block;
243
244 let param1 = func.param(0)?;
245
246 let ptr_val =
247 func.blocks[entry].instr_alloca(ptr_ty, 4, None, Location::Unknown, &storage)?;
248 let k1 = Operand::const_int(1, i32_ty);
249 func.blocks[entry].instr_store(
250 ptr_val.clone(),
251 k1.clone(),
252 None,
253 Location::Unknown,
254 &storage,
255 )?;
256
257 let ptr_idx1 = func.blocks[entry].instr_gep_ex(
258 ptr_val,
259 &[GepIndex::Const(1)],
260 ptr_ty,
261 Location::Unknown,
262 &storage,
263 )?;
264 func.blocks[entry].instr_store(
265 ptr_idx1.clone(),
266 param1,
267 None,
268 Location::Unknown,
269 &storage,
270 )?;
271
272 let result = func.blocks[entry].instr_load(ptr_idx1, None, Location::Unknown, &storage)?;
273
274 func.blocks[entry].instr_ret(Some(&result), Location::Unknown);
275
276 let result = test_run_module(
277 &module,
278 &storage,
279 "example",
280 &[JitValue::U32(2)],
281 JitValue::U32(0),
282 )?;
283 assert_eq!(result, JitValue::U32(2));
284
285 Ok(())
286 }
287
288 fn test_run_module(
289 module: &Module,
290 storage: &TypeStorage,
291 name: &str,
292 args: &[JitValue],
293 ret_ty: JitValue,
294 ) -> Result<JitValue, crate::llvm::Error> {
295 let result = lower_module_to_llvmir(module, storage)?;
296 maybe_dump_ir(&result);
297 let engine = create_jit_engine(result, 3)?;
298
299 let res = unsafe { engine.execute(name, args, ret_ty)? };
300
301 Ok(res)
302 }
303
304 #[test]
305 fn test_type_casts() -> Result<(), Box<dyn std::error::Error>> {
306 let mut module = Module::new("casts", Location::unknown());
307 let mut storage = TypeStorage::new();
308 let i16_ty = storage.add_type(Type::Int(16), None);
309 let i32_ty = storage.add_type(Type::Int(32), None);
310
311 let func = module
313 .add_function(
314 "test_zext",
315 &[Parameter::new(i32_ty, Location::Unknown)],
316 Some(i32_ty),
317 Location::Unknown,
318 )
319 .get_id();
320 {
321 let func = module.get_function_mut(func);
322 let param = func.param(0)?;
323 let truncated = func
325 .entry_block()
326 .instr_trunc(param, i16_ty, Location::Unknown)?;
327 let extended = func
328 .entry_block()
329 .instr_zext(truncated, i32_ty, Location::Unknown)?;
330 func.entry_block()
331 .instr_ret(Some(&extended), Location::Unknown);
332 }
333
334 let func = module
336 .add_function(
337 "test_sext",
338 &[Parameter::new(i32_ty, Location::Unknown)],
339 Some(i32_ty),
340 Location::Unknown,
341 )
342 .get_id();
343 {
344 let func = module.get_function_mut(func);
345 let param = func.param(0)?;
346 let truncated = func
347 .entry_block()
348 .instr_trunc(param, i16_ty, Location::Unknown)?;
349 let extended = func
350 .entry_block()
351 .instr_sext(truncated, i32_ty, Location::Unknown)?;
352 func.entry_block()
353 .instr_ret(Some(&extended), Location::Unknown);
354 }
355
356 let result = test_run_module(
358 &module,
359 &storage,
360 "test_zext",
361 &[JitValue::U32(50000)],
362 JitValue::U32(0),
363 )?;
364 assert_eq!(result, JitValue::U32(50000));
365
366 let result = test_run_module(
368 &module,
369 &storage,
370 "test_zext",
371 &[JitValue::U32(0x12345)],
372 JitValue::U32(0),
373 )?;
374 assert_eq!(result, JitValue::U32(0x2345));
375
376 let result = test_run_module(
378 &module,
379 &storage,
380 "test_sext",
381 &[JitValue::U32(0xFFFF)],
382 JitValue::U32(0),
383 )?;
384 assert_eq!(result, JitValue::U32(0xFFFFFFFF));
385
386 Ok(())
387 }
388
389 #[test]
390 fn test_select() -> Result<(), Box<dyn std::error::Error>> {
391 let mut module = Module::new("select", Location::unknown());
392 let mut storage = TypeStorage::new();
393 let _i1_ty = storage.get_or_create_i1(); let i32_ty = storage.add_type(Type::Int(32), None);
395
396 let func = module
398 .add_function(
399 "test_select",
400 &[Parameter::new(i32_ty, Location::Unknown)],
401 Some(i32_ty),
402 Location::Unknown,
403 )
404 .get_id();
405 {
406 let func = module.get_function_mut(func);
407 let param = func.param(0)?;
408 let cond = func.entry_block().instr_icmp(
410 IcmpCond::Ne,
411 param,
412 Operand::const_int(0, i32_ty),
413 Location::Unknown,
414 &storage,
415 )?;
416 let true_val = Operand::const_int(42, i32_ty);
417 let false_val = Operand::const_int(100, i32_ty);
418 let result =
419 func.entry_block()
420 .instr_select(cond, true_val, false_val, Location::Unknown)?;
421 func.entry_block()
422 .instr_ret(Some(&result), Location::Unknown);
423 }
424
425 let result = test_run_module(
427 &module,
428 &storage,
429 "test_select",
430 &[JitValue::U32(1)],
431 JitValue::U32(0),
432 )?;
433 assert_eq!(result, JitValue::U32(42));
434
435 let result = test_run_module(
437 &module,
438 &storage,
439 "test_select",
440 &[JitValue::U32(0)],
441 JitValue::U32(0),
442 )?;
443 assert_eq!(result, JitValue::U32(100));
444
445 Ok(())
446 }
447
448 #[test]
449 fn test_switch() -> Result<(), Box<dyn std::error::Error>> {
450 use irvm::block::SwitchCase;
451
452 let mut module = Module::new("switch", Location::unknown());
453 let mut storage = TypeStorage::new();
454 let i32_ty = storage.add_type(Type::Int(32), None);
455
456 let func = module
457 .add_function(
458 "test_switch",
459 &[Parameter::new(i32_ty, Location::Unknown)],
460 Some(i32_ty),
461 Location::Unknown,
462 )
463 .get_id();
464 {
465 let func = module.get_function_mut(func);
466 let param = func.param(0)?;
467
468 let case1_block = func.add_block(&[]);
469 let case2_block = func.add_block(&[]);
470 let default_block = func.add_block(&[]);
471
472 let entry = func.entry_block;
473 func.blocks[entry].instr_switch(
474 param,
475 default_block,
476 &[],
477 vec![
478 SwitchCase {
479 value: 1,
480 block: case1_block,
481 arguments: vec![],
482 },
483 SwitchCase {
484 value: 2,
485 block: case2_block,
486 arguments: vec![],
487 },
488 ],
489 Location::Unknown,
490 );
491
492 func.blocks[case1_block]
494 .instr_ret(Some(&Operand::const_int(10, i32_ty)), Location::Unknown);
495 func.blocks[case2_block]
497 .instr_ret(Some(&Operand::const_int(20, i32_ty)), Location::Unknown);
498 func.blocks[default_block]
500 .instr_ret(Some(&Operand::const_int(0, i32_ty)), Location::Unknown);
501 }
502
503 assert_eq!(
504 test_run_module(
505 &module,
506 &storage,
507 "test_switch",
508 &[JitValue::U32(1)],
509 JitValue::U32(0)
510 )?,
511 JitValue::U32(10)
512 );
513 assert_eq!(
514 test_run_module(
515 &module,
516 &storage,
517 "test_switch",
518 &[JitValue::U32(2)],
519 JitValue::U32(0)
520 )?,
521 JitValue::U32(20)
522 );
523 assert_eq!(
524 test_run_module(
525 &module,
526 &storage,
527 "test_switch",
528 &[JitValue::U32(99)],
529 JitValue::U32(0)
530 )?,
531 JitValue::U32(0)
532 );
533
534 Ok(())
535 }
536
537 #[test]
538 fn test_global_variable() -> Result<(), Box<dyn std::error::Error>> {
539 use irvm::value::ConstValue;
540
541 let mut module = Module::new("globals", Location::unknown());
542 let mut storage = TypeStorage::new();
543 let i32_ty = storage.add_type(Type::Int(32), None);
544 let ptr_ty = storage.add_type(
545 Type::Ptr {
546 pointee: i32_ty,
547 address_space: None,
548 },
549 None,
550 );
551
552 let global_idx = module.add_global(
554 "my_global",
555 i32_ty,
556 Some(ConstValue::Int(42)),
557 false,
558 Location::Unknown,
559 );
560
561 let func = module
562 .add_function("read_global", &[], Some(i32_ty), Location::Unknown)
563 .get_id();
564 {
565 let func = module.get_function_mut(func);
566 let global_ptr = Operand::global(global_idx, ptr_ty);
567 let loaded =
568 func.entry_block()
569 .instr_load(global_ptr, None, Location::Unknown, &storage)?;
570 func.entry_block()
571 .instr_ret(Some(&loaded), Location::Unknown);
572 }
573
574 let result = test_run_module(&module, &storage, "read_global", &[], JitValue::U32(0))?;
575 assert_eq!(result, JitValue::U32(42));
576
577 Ok(())
578 }
579
580 #[test]
581 fn test_atomicrmw() -> Result<(), Box<dyn std::error::Error>> {
582 use irvm::block::{AtomicOrdering, AtomicRMWOp};
583
584 let mut module = Module::new("atomic", Location::unknown());
585 let mut storage = TypeStorage::new();
586 let i32_ty = storage.add_type(Type::Int(32), None);
587 let ptr_ty = storage.add_type(
588 Type::Ptr {
589 pointee: i32_ty,
590 address_space: None,
591 },
592 None,
593 );
594
595 let func = module
596 .add_function(
597 "test_atomic_add",
598 &[Parameter::new(i32_ty, Location::Unknown)],
599 Some(i32_ty),
600 Location::Unknown,
601 )
602 .get_id();
603 {
604 let func = module.get_function_mut(func);
605 let param = func.param(0)?;
606 let entry = func.entry_block;
607
608 let ptr =
610 func.blocks[entry].instr_alloca(ptr_ty, 4, None, Location::Unknown, &storage)?;
611 func.blocks[entry].instr_store(
612 ptr.clone(),
613 Operand::const_int(10, i32_ty),
614 None,
615 Location::Unknown,
616 &storage,
617 )?;
618 let old_val = func.blocks[entry].instr_atomicrmw(
619 AtomicRMWOp::Add,
620 ptr,
621 param,
622 AtomicOrdering::SeqCst,
623 Location::Unknown,
624 )?;
625 func.blocks[entry].instr_ret(Some(&old_val), Location::Unknown);
626 }
627
628 let result = test_run_module(
630 &module,
631 &storage,
632 "test_atomic_add",
633 &[JitValue::U32(5)],
634 JitValue::U32(0),
635 )?;
636 assert_eq!(result, JitValue::U32(10));
637
638 Ok(())
639 }
640
641 #[test]
642 fn test_extractvalue_insertvalue() -> Result<(), Box<dyn std::error::Error>> {
643 use irvm::types::StructType;
644 use irvm::value::ConstValue;
645
646 let mut module = Module::new("aggregate", Location::unknown());
647 let mut storage = TypeStorage::new();
648 let i32_ty = storage.add_type(Type::Int(32), None);
649 let i64_ty = storage.add_type(Type::Int(64), None);
650
651 let struct_ty = storage.add_type(
653 Type::Struct(
654 StructType {
655 packed: false,
656 ident: None,
657 fields: vec![i32_ty, i64_ty],
658 debug_field_names: vec![],
659 }
660 .into(),
661 ),
662 None,
663 );
664
665 let func = module
667 .add_function(
668 "test_aggregate",
669 &[Parameter::new(i32_ty, Location::Unknown)],
670 Some(i32_ty),
671 Location::Unknown,
672 )
673 .get_id();
674 {
675 let func = module.get_function_mut(func);
676 let param = func.param(0)?;
677 let undef_struct = Operand::Constant(ConstValue::Undef, struct_ty);
679 let with_field = func.entry_block().instr_insertvalue(
680 undef_struct,
681 param.clone(),
682 &[0],
683 Location::Unknown,
684 )?;
685 let extracted = func.entry_block().instr_extractvalue(
686 with_field,
687 &[0],
688 i32_ty,
689 Location::Unknown,
690 )?;
691 func.entry_block()
692 .instr_ret(Some(&extracted), Location::Unknown);
693 }
694
695 let result = test_run_module(
697 &module,
698 &storage,
699 "test_aggregate",
700 &[JitValue::U32(123)],
701 JitValue::U32(0),
702 )?;
703 assert_eq!(result, JitValue::U32(123));
704
705 Ok(())
706 }
707
708 #[test]
709 fn test_param_attrs() -> Result<(), Box<dyn std::error::Error>> {
710 use irvm::function::ReturnAttrs;
711
712 let mut module = Module::new("param_attrs", Location::unknown());
713 let mut storage = TypeStorage::new();
714 let i32_ty = storage.add_type(Type::Int(32), None);
715 let ptr_ty = storage.add_type(
716 Type::Ptr {
717 pointee: i32_ty,
718 address_space: None,
719 },
720 None,
721 );
722
723 let mut param1 = Parameter::new(ptr_ty, Location::Unknown);
725 param1.nocapture = true;
726 param1.readonly = true;
727 param1.nonnull = true;
728 param1.dereferenceable = Some(4);
729
730 let mut param2 = Parameter::new(i32_ty, Location::Unknown);
731 param2.noundef = true;
732 param2.zeroext = true;
733
734 let func = module
736 .add_function(
737 "test_attrs",
738 &[param1, param2],
739 Some(ptr_ty),
740 Location::Unknown,
741 )
742 .get_id();
743
744 {
746 let func = module.get_function_mut(func);
747 func.return_attrs = ReturnAttrs {
748 noalias: true,
749 noundef: true,
750 nonnull: true,
751 dereferenceable: Some(4),
752 };
753
754 let param = func.param(0)?;
756 func.entry_block()
757 .instr_ret(Some(¶m), Location::Unknown);
758 }
759
760 let mut param3 = Parameter::new(ptr_ty, Location::Unknown);
763 param3.noalias = true; param3.writeonly = true;
765
766 let func2 = module
767 .add_function("test_attrs2", &[param3], Some(i32_ty), Location::Unknown)
768 .get_id();
769
770 {
771 let func2 = module.get_function_mut(func2);
772 func2.return_attrs = ReturnAttrs {
773 noalias: false, noundef: true,
775 nonnull: false,
776 dereferenceable: None,
777 };
778 let param = func2.param(0)?;
780 let loaded =
781 func2
782 .entry_block()
783 .instr_load(param, None, Location::Unknown, &storage)?;
784 func2
785 .entry_block()
786 .instr_ret(Some(&loaded), Location::Unknown);
787 }
788
789 let ir = lower_module_to_llvmir(&module, &storage)?;
790 maybe_dump_ir(&ir);
791
792 Ok(())
794 }
795
796 #[test]
797 fn test_gc_name() -> Result<(), Box<dyn std::error::Error>> {
798 let mut module = Module::new("gc_test", Location::unknown());
799 let mut storage = TypeStorage::new();
800 let i32_ty = storage.add_type(Type::Int(32), None);
801
802 let func = module
803 .add_function(
804 "gc_func",
805 &[Parameter::new(i32_ty, Location::Unknown)],
806 Some(i32_ty),
807 Location::Unknown,
808 )
809 .get_id();
810
811 {
812 let func = module.get_function_mut(func);
813 func.gc_name = Some("shadow-stack".to_string());
814 let param = func.param(0)?;
815 func.entry_block()
816 .instr_ret(Some(¶m), Location::Unknown);
817 }
818
819 let ir = lower_module_to_llvmir(&module, &storage)?;
820 maybe_dump_ir(&ir);
821
822 Ok(())
824 }
825
826 #[test]
827 fn test_prefix_data() -> Result<(), Box<dyn std::error::Error>> {
828 use irvm::value::ConstValue;
829
830 let mut module = Module::new("prefix_test", Location::unknown());
831 let mut storage = TypeStorage::new();
832 let i32_ty = storage.add_type(Type::Int(32), None);
833
834 let func = module
835 .add_function(
836 "prefix_func",
837 &[Parameter::new(i32_ty, Location::Unknown)],
838 Some(i32_ty),
839 Location::Unknown,
840 )
841 .get_id();
842
843 {
844 let func = module.get_function_mut(func);
845 func.prefix_data = Some((ConstValue::Int(0xDEADBEEF), i32_ty));
847 let param = func.param(0)?;
848 func.entry_block()
849 .instr_ret(Some(¶m), Location::Unknown);
850 }
851
852 let ir = lower_module_to_llvmir(&module, &storage)?;
853 maybe_dump_ir(&ir);
854
855 Ok(())
857 }
858
859 #[test]
860 fn test_prologue_data() -> Result<(), Box<dyn std::error::Error>> {
861 use irvm::value::ConstValue;
862
863 let mut module = Module::new("prologue_test", Location::unknown());
864 let mut storage = TypeStorage::new();
865 let i32_ty = storage.add_type(Type::Int(32), None);
866
867 let func = module
868 .add_function(
869 "prologue_func",
870 &[Parameter::new(i32_ty, Location::Unknown)],
871 Some(i32_ty),
872 Location::Unknown,
873 )
874 .get_id();
875
876 {
877 let func = module.get_function_mut(func);
878 func.prologue_data = Some((ConstValue::Int(0xCAFEBABE), i32_ty));
880 let param = func.param(0)?;
881 func.entry_block()
882 .instr_ret(Some(¶m), Location::Unknown);
883 }
884
885 let ir = lower_module_to_llvmir(&module, &storage)?;
886 maybe_dump_ir(&ir);
887
888 Ok(())
890 }
891
892 #[test]
893 fn test_combined_function_and_param_attrs() -> Result<(), Box<dyn std::error::Error>> {
894 use irvm::function::{FunctionAttrs, ReturnAttrs};
895
896 let mut module = Module::new("combined_attrs", Location::unknown());
897 let mut storage = TypeStorage::new();
898 let i32_ty = storage.add_type(Type::Int(32), None);
899 let ptr_ty = storage.add_type(
900 Type::Ptr {
901 pointee: i32_ty,
902 address_space: None,
903 },
904 None,
905 );
906
907 let mut param = Parameter::new(ptr_ty, Location::Unknown);
909 param.nocapture = true;
910 param.readonly = true;
911
912 let func = module
913 .add_function("combined", &[param], Some(ptr_ty), Location::Unknown)
914 .get_id();
915
916 {
917 let func = module.get_function_mut(func);
918 func.attrs = FunctionAttrs {
920 nounwind: true,
921 willreturn: true,
922 norecurse: true,
923 ..Default::default()
924 };
925 func.return_attrs = ReturnAttrs {
927 noalias: true,
928 nonnull: true,
929 ..Default::default()
930 };
931
932 let param = func.param(0)?;
933 func.entry_block()
934 .instr_ret(Some(¶m), Location::Unknown);
935 }
936
937 let ir = lower_module_to_llvmir(&module, &storage)?;
938 maybe_dump_ir(&ir);
939
940 Ok(())
941 }
942
943 #[test]
944 fn test_param_alignment() -> Result<(), Box<dyn std::error::Error>> {
945 let mut module = Module::new("param_align", Location::unknown());
946 let mut storage = TypeStorage::new();
947 let i32_ty = storage.add_type(Type::Int(32), None);
948 let ptr_ty = storage.add_type(
949 Type::Ptr {
950 pointee: i32_ty,
951 address_space: None,
952 },
953 None,
954 );
955
956 let mut param = Parameter::new(ptr_ty, Location::Unknown);
958 param.align = Some(16); let func = module
961 .add_function("aligned_param", &[param], Some(i32_ty), Location::Unknown)
962 .get_id();
963
964 {
965 let func = module.get_function_mut(func);
966 let param = func.param(0)?;
967 let loaded = func
968 .entry_block()
969 .instr_load(param, None, Location::Unknown, &storage)?;
970 func.entry_block()
971 .instr_ret(Some(&loaded), Location::Unknown);
972 }
973
974 let ir = lower_module_to_llvmir(&module, &storage)?;
975 maybe_dump_ir(&ir);
976
977 Ok(())
978 }
979
980 #[test]
981 fn test_signext_zeroext_param_attrs() -> Result<(), Box<dyn std::error::Error>> {
982 let mut module = Module::new("ext_attrs", Location::unknown());
983 let mut storage = TypeStorage::new();
984 let i8_ty = storage.add_type(Type::Int(8), None);
985 let i32_ty = storage.add_type(Type::Int(32), None);
986
987 let mut param1 = Parameter::new(i8_ty, Location::Unknown);
989 param1.signext = true;
990
991 let mut param2 = Parameter::new(i8_ty, Location::Unknown);
993 param2.zeroext = true;
994
995 let func = module
996 .add_function(
997 "test_ext",
998 &[param1, param2],
999 Some(i32_ty),
1000 Location::Unknown,
1001 )
1002 .get_id();
1003
1004 {
1005 let func = module.get_function_mut(func);
1006 let p1 = func.param(0)?;
1008 let p2 = func.param(1)?;
1009 let p1_ext = func
1010 .entry_block()
1011 .instr_sext(p1, i32_ty, Location::Unknown)?;
1012 let p2_ext = func
1013 .entry_block()
1014 .instr_zext(p2, i32_ty, Location::Unknown)?;
1015 let result = func
1016 .entry_block()
1017 .instr_add(&p1_ext, &p2_ext, Location::Unknown)?;
1018 func.entry_block()
1019 .instr_ret(Some(&result), Location::Unknown);
1020 }
1021
1022 let ir = lower_module_to_llvmir(&module, &storage)?;
1023 maybe_dump_ir(&ir);
1024
1025 Ok(())
1026 }
1027
1028 #[test]
1029 fn test_inreg_returned_nest_attrs() -> Result<(), Box<dyn std::error::Error>> {
1030 let mut module = Module::new("misc_attrs", Location::unknown());
1031 let mut storage = TypeStorage::new();
1032 let i32_ty = storage.add_type(Type::Int(32), None);
1033 let ptr_ty = storage.add_type(
1034 Type::Ptr {
1035 pointee: i32_ty,
1036 address_space: None,
1037 },
1038 None,
1039 );
1040
1041 let mut param1 = Parameter::new(i32_ty, Location::Unknown);
1043 param1.inreg = true;
1044
1045 let mut param2 = Parameter::new(ptr_ty, Location::Unknown);
1047 param2.returned = true;
1048
1049 let mut param3 = Parameter::new(ptr_ty, Location::Unknown);
1051 param3.nest = true;
1052
1053 let func = module
1054 .add_function(
1055 "misc_attrs",
1056 &[param1, param2, param3],
1057 Some(ptr_ty),
1058 Location::Unknown,
1059 )
1060 .get_id();
1061
1062 {
1063 let func = module.get_function_mut(func);
1064 let param = func.param(1)?;
1066 func.entry_block()
1067 .instr_ret(Some(¶m), Location::Unknown);
1068 }
1069
1070 let ir = lower_module_to_llvmir(&module, &storage)?;
1071 maybe_dump_ir(&ir);
1072
1073 Ok(())
1074 }
1075
1076 #[test]
1077 fn test_nofree_param_attr() -> Result<(), Box<dyn std::error::Error>> {
1078 let mut module = Module::new("nofree_test", Location::unknown());
1079 let mut storage = TypeStorage::new();
1080 let i32_ty = storage.add_type(Type::Int(32), None);
1081 let ptr_ty = storage.add_type(
1082 Type::Ptr {
1083 pointee: i32_ty,
1084 address_space: None,
1085 },
1086 None,
1087 );
1088
1089 let mut param = Parameter::new(ptr_ty, Location::Unknown);
1091 param.nofree = true;
1092 param.nocapture = true;
1093
1094 let func = module
1095 .add_function("nofree_func", &[param], Some(i32_ty), Location::Unknown)
1096 .get_id();
1097
1098 {
1099 let func = module.get_function_mut(func);
1100 let param = func.param(0)?;
1101 let loaded = func
1102 .entry_block()
1103 .instr_load(param, None, Location::Unknown, &storage)?;
1104 func.entry_block()
1105 .instr_ret(Some(&loaded), Location::Unknown);
1106 }
1107
1108 let ir = lower_module_to_llvmir(&module, &storage)?;
1109 maybe_dump_ir(&ir);
1110
1111 Ok(())
1112 }
1113
1114 #[test]
1115 fn test_prefix_and_prologue_combined() -> Result<(), Box<dyn std::error::Error>> {
1116 use irvm::value::ConstValue;
1117
1118 let mut module = Module::new("both_data", Location::unknown());
1119 let mut storage = TypeStorage::new();
1120 let i32_ty = storage.add_type(Type::Int(32), None);
1121
1122 let func = module
1123 .add_function(
1124 "both_func",
1125 &[Parameter::new(i32_ty, Location::Unknown)],
1126 Some(i32_ty),
1127 Location::Unknown,
1128 )
1129 .get_id();
1130
1131 {
1132 let func = module.get_function_mut(func);
1133 func.prefix_data = Some((ConstValue::Int(0xDEADBEEF), i32_ty));
1135 func.prologue_data = Some((ConstValue::Int(0xCAFEBABE), i32_ty));
1136 let param = func.param(0)?;
1137 func.entry_block()
1138 .instr_ret(Some(¶m), Location::Unknown);
1139 }
1140
1141 let ir = lower_module_to_llvmir(&module, &storage)?;
1142 maybe_dump_ir(&ir);
1143
1144 Ok(())
1145 }
1146
1147 #[test]
1148 fn test_gc_with_function_attrs() -> Result<(), Box<dyn std::error::Error>> {
1149 use irvm::function::FunctionAttrs;
1150
1151 let mut module = Module::new("gc_attrs", Location::unknown());
1152 let mut storage = TypeStorage::new();
1153 let i32_ty = storage.add_type(Type::Int(32), None);
1154
1155 let func = module
1156 .add_function(
1157 "gc_with_attrs",
1158 &[Parameter::new(i32_ty, Location::Unknown)],
1159 Some(i32_ty),
1160 Location::Unknown,
1161 )
1162 .get_id();
1163
1164 {
1165 let func = module.get_function_mut(func);
1166 func.gc_name = Some("statepoint-example".to_string());
1167 func.attrs = FunctionAttrs {
1168 nounwind: true,
1169 ..Default::default()
1170 };
1171 let param = func.param(0)?;
1172 func.entry_block()
1173 .instr_ret(Some(¶m), Location::Unknown);
1174 }
1175
1176 let ir = lower_module_to_llvmir(&module, &storage)?;
1177 maybe_dump_ir(&ir);
1178
1179 Ok(())
1180 }
1181}