1use std::collections::BTreeMap;
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7 #[error("Target is not WASM")]
8 InvalidTarget,
9
10 #[error("Func not found: {0}")]
11 FuncNotFound(String),
12
13 #[error("Llama: {0}")]
14 Llama(#[from] llama::Error),
15
16 #[error("Lightbeam")]
17 Lightbeam(String),
18}
19
20pub use lightbeam::ExecutableModule as Exec;
22
23pub struct Wasm<'a> {
25 exec: Exec,
26 func_map: BTreeMap<String, usize>,
27 _module: &'a llama::Module<'a>,
28 _codegen: llama::Codegen,
29}
30
31impl<'a> Wasm<'a> {
32 pub fn new<'b>(
34 module: &'a llama::Module,
35 exports: impl AsRef<[&'b str]>,
36 ) -> Result<Wasm<'a>, Error> {
37 if !module.target()?.to_ascii_lowercase().starts_with("wasm") {
38 return Err(Error::InvalidTarget);
39 }
40
41 let mut func_map = BTreeMap::new();
42
43 let exports = exports.as_ref();
44
45 let codegen = llama::Codegen::new(&module, exports, true)?;
46
47 let mut index = 0;
48 let symbols = codegen.symbols();
49 for sym in symbols {
50 if exports.contains(&sym.as_str()) {
51 func_map.insert(sym.clone(), index);
52 }
53 index += 1;
54 }
55
56 let exec = lightbeam::translate(codegen.as_ref())
57 .map_err(|x| Error::Lightbeam(format!("{:?}", x)))?;
58 Ok(Wasm {
59 _module: module,
60 _codegen: codegen,
61 exec,
62 func_map,
63 })
64 }
65
66 pub fn index(&self, name: impl AsRef<str>) -> Option<u32> {
68 self.func_map.get(name.as_ref()).map(|x| *x as u32)
69 }
70
71 pub fn exec(&self) -> &Exec {
73 &self.exec
74 }
75}
76
77#[macro_export]
80macro_rules! call {
81 ($wasm:ident.$name:ident($($arg:expr),*$(,)?)) => {
82 $wasm.exec().execute_func($wasm.index(stringify!($name)).expect("Invalid function"), ($($arg,)*)).map_err(|x| $crate::Error::Lightbeam(format!("{:?}", x)))
83 };
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::*;
89
90 #[test]
91 fn codegen() -> Result<(), Error> {
92 let context = llama::Context::new()?;
93 let mut module = llama::Module::new(&context, "test")?;
94 module.set_wasm32();
95
96 let builder = llama::Builder::new(&context)?;
97
98 let i32 = llama::Type::int(&context, 32)?;
99
100 let ft = llama::FuncType::new(i32, &[i32, i32])?;
101 module.declare_function(&builder, "testing_sub", ft, |f| {
102 let params = f.params();
103 let a = builder.sub(¶ms[0], ¶ms[1], "a")?;
104 builder.ret(&a)
105 })?;
106
107 let ft = llama::FuncType::new(i32, &[i32, i32])?;
108 module.declare_function(&builder, "testing", ft, |f| {
109 let params = f.params();
110 let a = builder.add(¶ms[0], ¶ms[1], "a")?;
111 builder.ret(&a)
112 })?;
113
114 println!("{}", module);
115
116 let wasm = Wasm::new(&module, &["testing"])?;
117 println!("{:?}", wasm.func_map);
118
119 let x: i32 = call!(wasm.testing(1i32, 2i32))?;
120 assert_eq!(x, 3);
121 Ok(())
122 }
123
124 #[test]
125 fn test_for_loop() -> Result<(), Error> {
126 use llama::*;
127
128 let ctx = Context::new()?;
129 let mut module = Module::new(&ctx, "test_for_loop")?;
130 module.set_wasm32();
131 let builder = Builder::new(&ctx)?;
132
133 let i64 = Type::int(&ctx, 64)?;
134 let ft = FuncType::new(i64, &[i64])?;
135 module.declare_function(&builder, "testing", ft, |f| {
136 let params = f.params();
137 let one = Const::int_sext(i64, 1)?;
138 let f = builder.for_loop(
139 Const::int_sext(i64, 0)?,
140 |x| builder.icmp(Icmp::LLVMIntSLE, x, ¶ms[0], "cond"),
141 |x| builder.add(x, one, "add"),
142 |x| Ok(*x),
143 )?;
144 builder.ret(f)
145 })?;
146
147 println!("{}", module);
148
149 let wasm = Wasm::new(&module, &["testing"])?;
150 println!("{:?}", wasm.func_map);
151
152 let x: i64 = call!(wasm.testing(9i64))?;
153 assert_eq!(x, 9);
154 Ok(())
155 }
156}