Skip to main content

wowlab_engine_domain/rotation/
jit_backend.rs

1// #t(file: rust_unwrap_in_lib) inkwell builder methods return `Result` for IR-construction errors that cannot occur for well-formed input; upstream `compile_inner` runs `module.verify()` after lowering.
2
3//! JIT side of the [`RotationBackend`] lowering trait.
4
5use inkwell::{
6    FloatPredicate, IntPredicate,
7    builder::Builder,
8    context::Context,
9    types::{FloatType, IntType},
10    values::{FloatValue, FunctionValue, IntValue, PointerValue},
11};
12use wowlab_types::numeric::EPSILON;
13
14use super::{
15    backend::RotationBackend,
16    result::{KIND_CAST, KIND_NONE, KIND_POOL, KIND_USE_ITEM, KIND_WAIT},
17};
18
19pub(crate) struct Intrinsics<'ctx> {
20    pub floor: FunctionValue<'ctx>,
21    pub ceil: FunctionValue<'ctx>,
22    pub fabs: FunctionValue<'ctx>,
23    pub minnum: FunctionValue<'ctx>,
24    pub maxnum: FunctionValue<'ctx>,
25}
26
27impl<'ctx> Intrinsics<'ctx> {
28    pub(crate) fn declare(ctx: &'ctx Context, module: &inkwell::module::Module<'ctx>) -> Self {
29        let f64_type = ctx.f64_type();
30        let unary = f64_type.fn_type(&[f64_type.into()], false);
31        let binary = f64_type.fn_type(&[f64_type.into(), f64_type.into()], false);
32
33        Self {
34            floor: module.add_function("llvm.floor.f64", unary, None),
35            ceil: module.add_function("llvm.ceil.f64", unary, None),
36            fabs: module.add_function("llvm.fabs.f64", unary, None),
37            minnum: module.add_function("llvm.minnum.f64", binary, None),
38            maxnum: module.add_function("llvm.maxnum.f64", binary, None),
39        }
40    }
41}
42
43pub(crate) struct JitBackend<'ctx, 'b> {
44    pub(crate) ctx: &'ctx Context,
45    pub(crate) builder: &'b Builder<'ctx>,
46    pub(crate) function: FunctionValue<'ctx>,
47    pub(crate) buf_param: PointerValue<'ctx>,
48    pub(crate) now_param: FloatValue<'ctx>,
49    pub(crate) i8_type: IntType<'ctx>,
50    pub(crate) i32_type: IntType<'ctx>,
51    pub(crate) i64_type: IntType<'ctx>,
52    pub(crate) f64_type: FloatType<'ctx>,
53    pub(crate) bool_type: IntType<'ctx>,
54    pub(crate) intrinsics: Intrinsics<'ctx>,
55}
56
57pub(crate) struct JitEnvironment<'ctx, 'b> {
58    pub(crate) context: &'ctx Context,
59    pub(crate) builder: &'b Builder<'ctx>,
60}
61
62pub(crate) struct JitEntry<'ctx> {
63    pub(crate) function: FunctionValue<'ctx>,
64    pub(crate) buffer: PointerValue<'ctx>,
65    pub(crate) now: FloatValue<'ctx>,
66}
67
68impl<'ctx, 'b> JitBackend<'ctx, 'b> {
69    pub(crate) fn new(
70        environment: &JitEnvironment<'ctx, 'b>,
71        entry: &JitEntry<'ctx>,
72        intrinsics: Intrinsics<'ctx>,
73    ) -> Self {
74        let JitEnvironment {
75            context: ctx,
76            builder,
77        } = environment;
78        let JitEntry {
79            function,
80            buffer: buf_param,
81            now: now_param,
82        } = entry;
83
84        Self {
85            ctx,
86            builder,
87            function: *function,
88            buf_param: *buf_param,
89            now_param: *now_param,
90            i8_type: ctx.i8_type(),
91            i32_type: ctx.i32_type(),
92            i64_type: ctx.i64_type(),
93            f64_type: ctx.f64_type(),
94            bool_type: ctx.bool_type(),
95            intrinsics,
96        }
97    }
98
99    pub(crate) fn pack_result(&self, kind: u8, action_id: u32, payload: u32) -> IntValue<'ctx> {
100        let packed = wowlab_buffer_contract::pack_eval_result(kind, action_id, payload);
101
102        self.i64_type.const_int(packed, false)
103    }
104
105    #[inline]
106    fn gep(&self, offset: usize) -> PointerValue<'ctx> {
107        let offset_val = self.i64_type.const_int(offset as u64, false);
108        // SAFETY: offsets are schema-validated; GEP stays within the buffer.
109
110        unsafe {
111            self.builder
112                .build_in_bounds_gep(self.i8_type, self.buf_param, &[offset_val], "ptr")
113                .unwrap()
114        }
115    }
116
117    fn cond_return(&mut self, cond: IntValue<'ctx>, ret: IntValue<'ctx>, label: &str) {
118        let return_block = self
119            .ctx
120            .append_basic_block(self.function, &format!("ret_{label}"));
121        let cont_block = self
122            .ctx
123            .append_basic_block(self.function, &format!("after_{label}"));
124
125        self.builder
126            .build_conditional_branch(cond, return_block, cont_block)
127            .unwrap();
128        self.builder.position_at_end(return_block);
129        self.builder.build_return(Some(&ret)).unwrap();
130        self.builder.position_at_end(cont_block);
131    }
132}
133
134impl<'ctx> RotationBackend for JitBackend<'ctx, '_> {
135    type Bool = IntValue<'ctx>;
136    type Int = IntValue<'ctx>;
137    type Float = FloatValue<'ctx>;
138
139    fn const_bool(&mut self, v: bool) -> Self::Bool {
140        self.bool_type.const_int(u64::from(v), false)
141    }
142    fn const_i32(&mut self, v: i32) -> Self::Int {
143        self.i64_type
144            .const_int(u64::from_ne_bytes(i64::from(v).to_ne_bytes()), true)
145    }
146    fn const_i64(&mut self, v: i64) -> Self::Int {
147        self.i64_type
148            .const_int(u64::from_ne_bytes(v.to_ne_bytes()), true)
149    }
150    fn const_f64(&mut self, v: f64) -> Self::Float {
151        self.f64_type.const_float(v)
152    }
153
154    fn load_bool(&mut self, offset: usize) -> Self::Bool {
155        let raw = self
156            .builder
157            .build_load(self.i32_type, self.gep(offset), "bool_raw")
158            .unwrap()
159            .into_int_value();
160        let zero = self.i32_type.const_int(0, false);
161
162        self.builder
163            .build_int_compare(IntPredicate::NE, raw, zero, "bool")
164            .unwrap()
165    }
166    fn load_i32(&mut self, offset: usize) -> Self::Int {
167        let raw = self
168            .builder
169            .build_load(self.i32_type, self.gep(offset), "i32_raw")
170            .unwrap()
171            .into_int_value();
172
173        self.builder
174            .build_int_s_extend(raw, self.i64_type, "i32_to_i64")
175            .unwrap()
176    }
177    fn load_i64(&mut self, offset: usize) -> Self::Int {
178        self.builder
179            .build_load(self.i64_type, self.gep(offset), "i64")
180            .unwrap()
181            .into_int_value()
182    }
183    fn load_f64(&mut self, offset: usize) -> Self::Float {
184        self.builder
185            .build_load(self.f64_type, self.gep(offset), "f64")
186            .unwrap()
187            .into_float_value()
188    }
189
190    fn store_bool(&mut self, offset: usize, v: Self::Bool) {
191        let widened = self
192            .builder
193            .build_int_z_extend(v, self.i32_type, "bool_to_i32")
194            .unwrap();
195
196        self.builder.build_store(self.gep(offset), widened).unwrap();
197    }
198    fn store_i32(&mut self, offset: usize, v: Self::Int) {
199        let truncated = self
200            .builder
201            .build_int_truncate(v, self.i32_type, "i64_to_i32")
202            .unwrap();
203
204        self.builder
205            .build_store(self.gep(offset), truncated)
206            .unwrap();
207    }
208    fn store_f64(&mut self, offset: usize, v: Self::Float) {
209        self.builder.build_store(self.gep(offset), v).unwrap();
210    }
211
212    fn now(&mut self) -> Self::Float {
213        self.now_param
214    }
215
216    fn add_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
217        self.builder.build_float_add(a, b, "fadd").unwrap()
218    }
219    fn sub_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
220        self.builder.build_float_sub(a, b, "fsub").unwrap()
221    }
222    fn mul_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
223        self.builder.build_float_mul(a, b, "fmul").unwrap()
224    }
225    fn safe_div_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
226        let zero = self.f64_type.const_float(0.0);
227        let is_zero = self
228            .builder
229            .build_float_compare(FloatPredicate::OEQ, b, zero, "div_zero")
230            .unwrap();
231        let div = self.builder.build_float_div(a, b, "fdiv").unwrap();
232
233        self.builder
234            .build_select(is_zero, zero, div, "safe_div")
235            .unwrap()
236            .into_float_value()
237    }
238    fn true_mod_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
239        let zero = self.f64_type.const_float(0.0);
240        let is_zero = self
241            .builder
242            .build_float_compare(FloatPredicate::OEQ, b, zero, "mod_zero")
243            .unwrap();
244        let div = self.builder.build_float_div(a, b, "mod_div").unwrap();
245        let floored = self.floor_f(div);
246        let prod = self
247            .builder
248            .build_float_mul(b, floored, "mod_prod")
249            .unwrap();
250        let std_mod = self.builder.build_float_sub(a, prod, "mod_std").unwrap();
251        let with_b = self
252            .builder
253            .build_float_add(std_mod, b, "mod_with_b")
254            .unwrap();
255        let div2 = self.builder.build_float_div(with_b, b, "mod_div2").unwrap();
256        let floored2 = self.floor_f(div2);
257        let prod2 = self
258            .builder
259            .build_float_mul(b, floored2, "mod_prod2")
260            .unwrap();
261        let result = self.builder.build_float_sub(with_b, prod2, "mod").unwrap();
262
263        self.builder
264            .build_select(is_zero, zero, result, "true_mod")
265            .unwrap()
266            .into_float_value()
267    }
268    fn min_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
269        self.builder
270            .build_call(self.intrinsics.minnum, &[a.into(), b.into()], "fmin")
271            .unwrap()
272            .try_as_basic_value()
273            .left()
274            .unwrap()
275            .into_float_value()
276    }
277    fn max_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Float {
278        self.builder
279            .build_call(self.intrinsics.maxnum, &[a.into(), b.into()], "fmax")
280            .unwrap()
281            .try_as_basic_value()
282            .left()
283            .unwrap()
284            .into_float_value()
285    }
286    fn floor_f(&mut self, v: Self::Float) -> Self::Float {
287        self.builder
288            .build_call(self.intrinsics.floor, &[v.into()], "floor")
289            .unwrap()
290            .try_as_basic_value()
291            .left()
292            .unwrap()
293            .into_float_value()
294    }
295    fn ceil_f(&mut self, v: Self::Float) -> Self::Float {
296        self.builder
297            .build_call(self.intrinsics.ceil, &[v.into()], "ceil")
298            .unwrap()
299            .try_as_basic_value()
300            .left()
301            .unwrap()
302            .into_float_value()
303    }
304    fn abs_f(&mut self, v: Self::Float) -> Self::Float {
305        self.builder
306            .build_call(self.intrinsics.fabs, &[v.into()], "fabs")
307            .unwrap()
308            .try_as_basic_value()
309            .left()
310            .unwrap()
311            .into_float_value()
312    }
313
314    fn cmp_gt_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Bool {
315        self.builder
316            .build_float_compare(FloatPredicate::OGT, a, b, "fgt")
317            .unwrap()
318    }
319    fn cmp_gte_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Bool {
320        self.builder
321            .build_float_compare(FloatPredicate::OGE, a, b, "fge")
322            .unwrap()
323    }
324    fn cmp_lt_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Bool {
325        self.builder
326            .build_float_compare(FloatPredicate::OLT, a, b, "flt")
327            .unwrap()
328    }
329    fn cmp_lte_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Bool {
330        self.builder
331            .build_float_compare(FloatPredicate::OLE, a, b, "fle")
332            .unwrap()
333    }
334    fn cmp_eq_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Bool {
335        let diff = self.builder.build_float_sub(a, b, "eps_diff").unwrap();
336        let abs = self.abs_f(diff);
337        let eps = self.f64_type.const_float(EPSILON);
338
339        self.builder
340            .build_float_compare(FloatPredicate::OLT, abs, eps, "feq")
341            .unwrap()
342    }
343    fn cmp_ne_f(&mut self, a: Self::Float, b: Self::Float) -> Self::Bool {
344        let diff = self.builder.build_float_sub(a, b, "eps_diff").unwrap();
345        let abs = self.abs_f(diff);
346        let eps = self.f64_type.const_float(EPSILON);
347
348        self.builder
349            .build_float_compare(FloatPredicate::OGE, abs, eps, "fne")
350            .unwrap()
351    }
352    fn cmp_eq_i(&mut self, a: Self::Int, b: Self::Int) -> Self::Bool {
353        self.builder
354            .build_int_compare(IntPredicate::EQ, a, b, "ieq")
355            .unwrap()
356    }
357    fn cmp_ne_i(&mut self, a: Self::Int, b: Self::Int) -> Self::Bool {
358        self.builder
359            .build_int_compare(IntPredicate::NE, a, b, "ine")
360            .unwrap()
361    }
362    fn cmp_gt_i(&mut self, a: Self::Int, b: Self::Int) -> Self::Bool {
363        self.builder
364            .build_int_compare(IntPredicate::SGT, a, b, "igt")
365            .unwrap()
366    }
367    fn cmp_gte_i(&mut self, a: Self::Int, b: Self::Int) -> Self::Bool {
368        self.builder
369            .build_int_compare(IntPredicate::SGE, a, b, "ige")
370            .unwrap()
371    }
372    fn cmp_lt_i(&mut self, a: Self::Int, b: Self::Int) -> Self::Bool {
373        self.builder
374            .build_int_compare(IntPredicate::SLT, a, b, "ilt")
375            .unwrap()
376    }
377    fn cmp_lte_i(&mut self, a: Self::Int, b: Self::Int) -> Self::Bool {
378        self.builder
379            .build_int_compare(IntPredicate::SLE, a, b, "ile")
380            .unwrap()
381    }
382
383    fn and_b(&mut self, a: Self::Bool, b: Self::Bool) -> Self::Bool {
384        self.builder.build_and(a, b, "and").unwrap()
385    }
386    fn or_b(&mut self, a: Self::Bool, b: Self::Bool) -> Self::Bool {
387        self.builder.build_or(a, b, "or").unwrap()
388    }
389    fn not_b(&mut self, a: Self::Bool) -> Self::Bool {
390        let one = self.bool_type.const_int(1, false);
391
392        self.builder.build_xor(a, one, "not").unwrap()
393    }
394
395    fn bool_to_float(&mut self, v: Self::Bool) -> Self::Float {
396        let one = self.f64_type.const_float(1.0);
397        let zero = self.f64_type.const_float(0.0);
398
399        self.builder
400            .build_select(v, one, zero, "b2f")
401            .unwrap()
402            .into_float_value()
403    }
404    fn bool_to_int(&mut self, v: Self::Bool) -> Self::Int {
405        self.builder
406            .build_int_z_extend(v, self.i64_type, "b2i")
407            .unwrap()
408    }
409    fn int_to_float(&mut self, v: Self::Int) -> Self::Float {
410        self.builder
411            .build_signed_int_to_float(v, self.f64_type, "i2f")
412            .unwrap()
413    }
414    fn int_to_bool(&mut self, v: Self::Int) -> Self::Bool {
415        let zero = self.i64_type.const_int(0, false);
416
417        self.builder
418            .build_int_compare(IntPredicate::NE, v, zero, "i2b")
419            .unwrap()
420    }
421    fn float_to_bool(&mut self, v: Self::Float) -> Self::Bool {
422        let zero = self.f64_type.const_float(0.0);
423        // Exact `v != 0.0` (ONE), not epsilon-aware equality.
424
425        self.builder
426            .build_float_compare(FloatPredicate::ONE, v, zero, "f2b")
427            .unwrap()
428    }
429    fn float_to_int(&mut self, v: Self::Float) -> Self::Int {
430        self.builder
431            .build_float_to_signed_int(v, self.i64_type, "f2i")
432            .unwrap()
433    }
434
435    fn select_f(&mut self, cond: Self::Bool, t: Self::Float, f: Self::Float) -> Self::Float {
436        self.builder
437            .build_select(cond, t, f, "sel_f")
438            .unwrap()
439            .into_float_value()
440    }
441    fn select_i(&mut self, cond: Self::Bool, t: Self::Int, f: Self::Int) -> Self::Int {
442        self.builder
443            .build_select(cond, t, f, "sel_i")
444            .unwrap()
445            .into_int_value()
446    }
447    fn select_b(&mut self, cond: Self::Bool, t: Self::Bool, f: Self::Bool) -> Self::Bool {
448        self.builder
449            .build_select(cond, t, f, "sel_b")
450            .unwrap()
451            .into_int_value()
452    }
453
454    fn return_none_if(&mut self, cond: Self::Bool) {
455        let ret = self.pack_result(KIND_NONE, 0, 0);
456
457        self.cond_return(cond, ret, "none");
458    }
459    fn return_cast_if(&mut self, cond: Self::Bool, spell_id: u32, empower_rank: u8) {
460        let ret = self.pack_result(KIND_CAST, spell_id, u32::from(empower_rank));
461
462        self.cond_return(cond, ret, "cast");
463    }
464    fn return_wait_if(&mut self, cond: Self::Bool, seconds: f32) {
465        let ret = self.pack_result(KIND_WAIT, 0, seconds.to_bits());
466
467        self.cond_return(cond, ret, "wait");
468    }
469    fn return_pool_if(&mut self, cond: Self::Bool, target: f32) {
470        let ret = self.pack_result(KIND_POOL, 0, target.to_bits());
471
472        self.cond_return(cond, ret, "pool");
473    }
474    fn return_use_item_if(&mut self, cond: Self::Bool, gear_slot: u8, empower_rank: u8) {
475        let ret = self.pack_result(KIND_USE_ITEM, u32::from(gear_slot), u32::from(empower_rank));
476
477        self.cond_return(cond, ret, "use_item");
478    }
479    fn return_none(&mut self) {
480        let ret = self.pack_result(KIND_NONE, 0, 0);
481
482        self.builder.build_return(Some(&ret)).unwrap();
483    }
484}