1pub mod error;
8pub mod reader;
10pub mod writer;
12
13pub use error::BitcodeError;
15pub use reader::read_bitcode;
17pub use writer::write_bitcode;
19
20#[cfg(test)]
23mod tests {
24 use super::*;
25 use llvm_ir::{Builder, Context, Linkage, Module};
26
27 fn make_empty_module() -> (Context, Module) {
28 let ctx = Context::new();
29 let module = Module::new("empty");
30 (ctx, module)
31 }
32
33 fn make_add_fn() -> (Context, Module) {
34 let mut ctx = Context::new();
35 let mut module = Module::new("test");
36 let mut b = Builder::new(&mut ctx, &mut module);
37 b.add_function(
38 "add",
39 b.ctx.i64_ty,
40 vec![b.ctx.i64_ty, b.ctx.i64_ty],
41 vec!["a".into(), "b".into()],
42 false,
43 Linkage::External,
44 );
45 let entry = b.add_block("entry");
46 b.position_at_end(entry);
47 let a = b.get_arg(0);
48 let bv = b.get_arg(1);
49 let sum = b.build_add("sum", a, bv);
50 b.build_ret(sum);
51 (ctx, module)
52 }
53
54 #[test]
55 fn write_then_read_empty_module() {
56 let (ctx, module) = make_empty_module();
57 let bytes = write_bitcode(&ctx, &module);
58 let (ctx2, module2) = read_bitcode(&bytes).expect("round-trip must succeed");
59 assert_eq!(module2.name, "empty");
60 assert_eq!(module2.functions.len(), 0);
61 assert!(ctx2.num_types() > 0);
63 }
64
65 #[test]
66 fn write_then_read_simple_function() {
67 let (ctx, module) = make_add_fn();
68 let bytes = write_bitcode(&ctx, &module);
69 let (_, module2) = read_bitcode(&bytes).expect("round-trip must succeed");
70 assert_eq!(module2.functions.len(), 1);
71 let func = &module2.functions[0];
72 assert!(!func.blocks.is_empty());
74 assert!(!func.instructions.is_empty());
75 }
76
77 #[test]
78 fn write_then_read_preserves_freeze_instruction() {
79 let mut ctx = Context::new();
80 let mut module = Module::new("freeze");
81 let mut b = Builder::new(&mut ctx, &mut module);
82 b.add_function(
83 "freeze_id",
84 b.ctx.i32_ty,
85 vec![b.ctx.i32_ty],
86 vec!["x".into()],
87 false,
88 Linkage::External,
89 );
90 let entry = b.add_block("entry");
91 b.position_at_end(entry);
92 let x = b.get_arg(0);
93 let y = b.build_freeze("y", x);
94 b.build_ret(y);
95
96 let bytes = write_bitcode(&ctx, &module);
97 let (_, module2) = read_bitcode(&bytes).expect("round-trip must succeed");
98 let func = &module2.functions[0];
99 let iid = func.blocks[0].body[0];
100 assert_eq!(func.instr(iid).kind.opcode(), "freeze");
101 }
102
103 #[test]
104 fn write_then_read_preserves_function_names() {
105 let (ctx, module) = make_add_fn();
106 let bytes = write_bitcode(&ctx, &module);
107 let (_, module2) = read_bitcode(&bytes).expect("round-trip must succeed");
108 assert_eq!(module2.functions[0].name, "add");
109 }
110
111 #[test]
112 fn write_then_read_multiple_functions() {
113 let mut ctx = Context::new();
114 let mut module = Module::new("multi");
115
116 let mut b = Builder::new(&mut ctx, &mut module);
118 b.add_function(
119 "add",
120 b.ctx.i64_ty,
121 vec![b.ctx.i64_ty, b.ctx.i64_ty],
122 vec!["x".into(), "y".into()],
123 false,
124 Linkage::External,
125 );
126 let entry1 = b.add_block("entry");
127 b.position_at_end(entry1);
128 let x = b.get_arg(0);
129 let y = b.get_arg(1);
130 let sum = b.build_add("sum", x, y);
131 b.build_ret(sum);
132
133 b.add_function(
135 "sub",
136 b.ctx.i64_ty,
137 vec![b.ctx.i64_ty, b.ctx.i64_ty],
138 vec!["a".into(), "b".into()],
139 false,
140 Linkage::External,
141 );
142 let entry2 = b.add_block("entry");
143 b.position_at_end(entry2);
144 let a = b.get_arg(0);
145 let bv = b.get_arg(1);
146 let diff = b.build_sub("diff", a, bv);
147 b.build_ret(diff);
148
149 let bytes = write_bitcode(&ctx, &module);
150 let (_, module2) = read_bitcode(&bytes).expect("round-trip must succeed");
151
152 assert_eq!(module2.functions.len(), 2);
153 assert_eq!(module2.functions[0].name, "add");
154 assert_eq!(module2.functions[1].name, "sub");
155 }
156
157 #[test]
158 fn metadata_type_round_trips_as_metadata_not_label() {
159 use llvm_ir::TypeData;
162 let mut ctx = Context::new();
163 let meta_ty = ctx.mk_metadata();
164 let module = Module::new("meta_test");
165 let bytes = write_bitcode(&ctx, &module);
166 let (ctx2, _) = read_bitcode(&bytes).expect("round-trip must succeed");
167 let td = ctx2.get_type(meta_ty);
169 assert_eq!(
170 td,
171 &TypeData::Metadata,
172 "Metadata type must round-trip as TypeData::Metadata, not Label"
173 );
174 }
175
176 #[test]
177 fn invalid_magic_returns_error() {
178 let bad = b"BAAD\x01\x00\x00\x00\x00\x00\x00\x00";
179 let result = read_bitcode(bad);
180 assert!(result.is_err(), "invalid magic must return an error");
181 if let Err(BitcodeError::InvalidMagic) = result { } else {
183 panic!("expected InvalidMagic error");
184 }
185 }
186}