1use 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
19const I64_EXCLUSIVE_UPPER_BOUND_AS_F64: f64 = 9_223_372_036_854_775_808.0;
20
21pub(crate) struct Intrinsics<'ctx> {
22 pub floor: FunctionValue<'ctx>,
23 pub ceil: FunctionValue<'ctx>,
24 pub fabs: FunctionValue<'ctx>,
25 pub minnum: FunctionValue<'ctx>,
26 pub maxnum: FunctionValue<'ctx>,
27}
28
29impl<'ctx> Intrinsics<'ctx> {
30 pub(crate) fn declare(ctx: &'ctx Context, module: &inkwell::module::Module<'ctx>) -> Self {
31 let f64_type = ctx.f64_type();
32 let unary = f64_type.fn_type(&[f64_type.into()], false);
33 let binary = f64_type.fn_type(&[f64_type.into(), f64_type.into()], false);
34
35 Self {
36 floor: module.add_function("llvm.floor.f64", unary, None),
37 ceil: module.add_function("llvm.ceil.f64", unary, None),
38 fabs: module.add_function("llvm.fabs.f64", unary, None),
39 minnum: module.add_function("llvm.minnum.f64", binary, None),
40 maxnum: module.add_function("llvm.maxnum.f64", binary, None),
41 }
42 }
43}
44
45pub(crate) struct JitBackend<'ctx, 'b> {
46 pub(crate) ctx: &'ctx Context,
47 pub(crate) builder: &'b Builder<'ctx>,
48 pub(crate) function: FunctionValue<'ctx>,
49 pub(crate) buf_param: PointerValue<'ctx>,
50 pub(crate) now_param: FloatValue<'ctx>,
51 pub(crate) i8_type: IntType<'ctx>,
52 pub(crate) i32_type: IntType<'ctx>,
53 pub(crate) i64_type: IntType<'ctx>,
54 pub(crate) f64_type: FloatType<'ctx>,
55 pub(crate) bool_type: IntType<'ctx>,
56 pub(crate) intrinsics: Intrinsics<'ctx>,
57}
58
59pub(crate) struct JitEnvironment<'ctx, 'b> {
60 pub(crate) context: &'ctx Context,
61 pub(crate) builder: &'b Builder<'ctx>,
62}
63
64pub(crate) struct JitEntry<'ctx> {
65 pub(crate) function: FunctionValue<'ctx>,
66 pub(crate) buffer: PointerValue<'ctx>,
67 pub(crate) now: FloatValue<'ctx>,
68}
69
70impl<'ctx, 'b> JitBackend<'ctx, 'b> {
71 pub(crate) fn new(
72 environment: &JitEnvironment<'ctx, 'b>,
73 entry: &JitEntry<'ctx>,
74 intrinsics: Intrinsics<'ctx>,
75 ) -> Self {
76 let JitEnvironment {
77 context: ctx,
78 builder,
79 } = environment;
80 let JitEntry {
81 function,
82 buffer: buf_param,
83 now: now_param,
84 } = entry;
85
86 Self {
87 ctx,
88 builder,
89 function: *function,
90 buf_param: *buf_param,
91 now_param: *now_param,
92 i8_type: ctx.i8_type(),
93 i32_type: ctx.i32_type(),
94 i64_type: ctx.i64_type(),
95 f64_type: ctx.f64_type(),
96 bool_type: ctx.bool_type(),
97 intrinsics,
98 }
99 }
100
101 pub(crate) fn pack_result(&self, kind: u8, action_id: u32, payload: u32) -> IntValue<'ctx> {
102 let packed = wowlab_buffer_contract::pack_eval_result(kind, action_id, payload);
103
104 self.i64_type.const_int(packed, false)
105 }
106
107 #[inline]
108 fn gep(&self, offset: usize) -> PointerValue<'ctx> {
109 let offset_val = self.i64_type.const_int(offset as u64, false);
110 unsafe {
113 self.builder
114 .build_in_bounds_gep(self.i8_type, self.buf_param, &[offset_val], "ptr")
115 .unwrap()
116 }
117 }
118
119 fn cond_return(&mut self, cond: IntValue<'ctx>, ret: IntValue<'ctx>, label: &str) {
120 let return_block = self
121 .ctx
122 .append_basic_block(self.function, &format!("ret_{label}"));
123 let cont_block = self
124 .ctx
125 .append_basic_block(self.function, &format!("after_{label}"));
126
127 self.builder
128 .build_conditional_branch(cond, return_block, cont_block)
129 .unwrap();
130 self.builder.position_at_end(return_block);
131 self.builder.build_return(Some(&ret)).unwrap();
132 self.builder.position_at_end(cont_block);
133 }
134}
135
136impl<'ctx> RotationBackend for JitBackend<'ctx, '_> {
137 type Bool = IntValue<'ctx>;
138 type Int = IntValue<'ctx>;
139 type Float = FloatValue<'ctx>;
140 type AndChain = IntValue<'ctx>;
141 type OrChain = IntValue<'ctx>;
142 #[cfg(feature = "rotation-optimizations")]
143 type CastGuard = IntValue<'ctx>;
144
145 fn const_bool(&mut self, v: bool) -> Self::Bool {
146 self.bool_type.const_int(u64::from(v), false)
147 }
148 fn const_i32(&mut self, v: i32) -> Self::Int {
149 self.i64_type
150 .const_int(u64::from_ne_bytes(i64::from(v).to_ne_bytes()), true)
151 }
152 fn const_i64(&mut self, v: i64) -> Self::Int {
153 self.i64_type
154 .const_int(u64::from_ne_bytes(v.to_ne_bytes()), true)
155 }
156 fn const_f64(&mut self, v: f64) -> Self::Float {
157 self.f64_type.const_float(v)
158 }
159
160 fn load_bool(&mut self, offset: usize) -> Self::Bool {
161 let raw = self
162 .builder
163 .build_load(self.i32_type, self.gep(offset), "bool_raw")
164 .unwrap()
165 .into_int_value();
166 let zero = self.i32_type.const_int(0, false);
167
168 self.builder
169 .build_int_compare(IntPredicate::NE, raw, zero, "bool")
170 .unwrap()
171 }
172 fn load_i32(&mut self, offset: usize) -> Self::Int {
173 let raw = self
174 .builder
175 .build_load(self.i32_type, self.gep(offset), "i32_raw")
176 .unwrap()
177 .into_int_value();
178
179 self.builder
180 .build_int_s_extend(raw, self.i64_type, "i32_to_i64")
181 .unwrap()
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 begin_and(&mut self, first: Self::Bool) -> Self::AndChain {
396 first
397 }
398
399 fn push_and(&mut self, chain: &mut Self::AndChain, value: Self::Bool) {
400 *chain = self.and_b(*chain, value);
401 }
402
403 fn finish_and(&mut self, chain: Self::AndChain) -> Self::Bool {
404 chain
405 }
406
407 fn begin_or(&mut self, first: Self::Bool) -> Self::OrChain {
408 first
409 }
410
411 fn push_or(&mut self, chain: &mut Self::OrChain, value: Self::Bool) {
412 *chain = self.or_b(*chain, value);
413 }
414
415 fn finish_or(&mut self, chain: Self::OrChain) -> Self::Bool {
416 chain
417 }
418
419 #[cfg(feature = "rotation-optimizations")]
420 fn begin_cast_guard(&mut self, first: Self::Bool) -> Self::CastGuard {
421 first
422 }
423
424 #[cfg(feature = "rotation-optimizations")]
425 fn push_cast_guard(&mut self, guard: &mut Self::CastGuard, value: Self::Bool) {
426 *guard = self.and_b(*guard, value);
427 }
428
429 #[cfg(feature = "rotation-optimizations")]
430 fn finish_cast_guard(
431 &mut self,
432 guard: Self::CastGuard,
433 spell_id: u32,
434 empower_rank: u8,
435 ) -> Self::Bool {
436 self.return_cast_if(guard, spell_id, empower_rank);
437
438 guard
439 }
440
441 fn bool_to_float(&mut self, v: Self::Bool) -> Self::Float {
442 let one = self.f64_type.const_float(1.0);
443 let zero = self.f64_type.const_float(0.0);
444
445 self.builder
446 .build_select(v, one, zero, "b2f")
447 .unwrap()
448 .into_float_value()
449 }
450 fn bool_to_int(&mut self, v: Self::Bool) -> Self::Int {
451 self.builder
452 .build_int_z_extend(v, self.i64_type, "b2i")
453 .unwrap()
454 }
455 fn int_to_float(&mut self, v: Self::Int) -> Self::Float {
456 self.builder
457 .build_signed_int_to_float(v, self.f64_type, "i2f")
458 .unwrap()
459 }
460 fn int_to_bool(&mut self, v: Self::Int) -> Self::Bool {
461 let zero = self.i64_type.const_int(0, false);
462
463 self.builder
464 .build_int_compare(IntPredicate::NE, v, zero, "i2b")
465 .unwrap()
466 }
467 fn float_to_bool(&mut self, v: Self::Float) -> Self::Bool {
468 let zero = self.f64_type.const_float(0.0);
469 self.builder
472 .build_float_compare(FloatPredicate::ONE, v, zero, "f2b")
473 .unwrap()
474 }
475 fn float_to_int(&mut self, v: Self::Float) -> Self::Int {
476 let zero_f = self.f64_type.const_zero();
477 let zero_i = self.i64_type.const_zero();
478 let upper_bound = self.f64_type.const_float(I64_EXCLUSIVE_UPPER_BOUND_AS_F64);
479 let lower_bound = self.f64_type.const_float(-I64_EXCLUSIVE_UPPER_BOUND_AS_F64);
480 let is_nan = self
481 .builder
482 .build_float_compare(FloatPredicate::UNO, v, v, "f2i_nan")
483 .unwrap();
484 let overflows_high = self
485 .builder
486 .build_float_compare(FloatPredicate::OGE, v, upper_bound, "f2i_high")
487 .unwrap();
488 let overflows_low = self
489 .builder
490 .build_float_compare(FloatPredicate::OLT, v, lower_bound, "f2i_low")
491 .unwrap();
492 let invalid = self
493 .builder
494 .build_or(is_nan, overflows_high, "f2i_nan_or_high")
495 .unwrap();
496 let invalid = self
497 .builder
498 .build_or(invalid, overflows_low, "f2i_invalid")
499 .unwrap();
500 let safe_input = self
501 .builder
502 .build_select(invalid, zero_f, v, "f2i_safe_input")
503 .unwrap()
504 .into_float_value();
505 let converted = self
506 .builder
507 .build_float_to_signed_int(safe_input, self.i64_type, "f2i")
508 .unwrap();
509 let with_low = self
510 .builder
511 .build_select(
512 overflows_low,
513 self.i64_type
514 .const_int(u64::from_ne_bytes(i64::MIN.to_ne_bytes()), false),
515 converted,
516 "f2i_with_low",
517 )
518 .unwrap()
519 .into_int_value();
520 let with_high = self
521 .builder
522 .build_select(
523 overflows_high,
524 self.i64_type
525 .const_int(u64::from_ne_bytes(i64::MAX.to_ne_bytes()), false),
526 with_low,
527 "f2i_with_high",
528 )
529 .unwrap()
530 .into_int_value();
531
532 self.builder
533 .build_select(is_nan, zero_i, with_high, "f2i_saturated")
534 .unwrap()
535 .into_int_value()
536 }
537
538 fn select_f(&mut self, cond: Self::Bool, t: Self::Float, f: Self::Float) -> Self::Float {
539 self.builder
540 .build_select(cond, t, f, "sel_f")
541 .unwrap()
542 .into_float_value()
543 }
544 fn select_i(&mut self, cond: Self::Bool, t: Self::Int, f: Self::Int) -> Self::Int {
545 self.builder
546 .build_select(cond, t, f, "sel_i")
547 .unwrap()
548 .into_int_value()
549 }
550 fn select_b(&mut self, cond: Self::Bool, t: Self::Bool, f: Self::Bool) -> Self::Bool {
551 self.builder
552 .build_select(cond, t, f, "sel_b")
553 .unwrap()
554 .into_int_value()
555 }
556
557 fn return_cast_if(&mut self, cond: Self::Bool, spell_id: u32, empower_rank: u8) {
558 let ret = self.pack_result(KIND_CAST, spell_id, u32::from(empower_rank));
559
560 self.cond_return(cond, ret, "cast");
561 }
562 fn return_wait_if(&mut self, cond: Self::Bool, seconds: f32) {
563 let ret = self.pack_result(KIND_WAIT, 0, seconds.to_bits());
564
565 self.cond_return(cond, ret, "wait");
566 }
567 fn return_pool_if(&mut self, cond: Self::Bool, target: f32) {
568 let ret = self.pack_result(KIND_POOL, 0, target.to_bits());
569
570 self.cond_return(cond, ret, "pool");
571 }
572 fn return_use_item_if(&mut self, cond: Self::Bool, gear_slot: u8, empower_rank: u8) {
573 let ret = self.pack_result(KIND_USE_ITEM, u32::from(gear_slot), u32::from(empower_rank));
574
575 self.cond_return(cond, ret, "use_item");
576 }
577 fn return_none(&mut self) {
578 let ret = self.pack_result(KIND_NONE, 0, 0);
579
580 self.builder.build_return(Some(&ret)).unwrap();
581 }
582}