1use super::*;
2use crate::alloc::emit_alloc_fast_path;
3use crate::emit::{EmitError, SsaVal};
4use crate::pipeline::CodegenPipeline;
5use cranelift_codegen::ir::{
6 self, condcodes::FloatCC, condcodes::IntCC, types, AbiParam, BlockArg, InstBuilder, MemFlags,
7 Signature, Value,
8};
9use cranelift_frontend::FunctionBuilder;
10use cranelift_module::Linkage;
11use cranelift_module::Module;
12use tidepool_heap::layout;
13use tidepool_repr::PrimOpKind;
14
15fn emit_div_zero_check(builder: &mut FunctionBuilder, divisor: Value) {
17 let zero = builder.ins().iconst(types::I64, 0);
18 let is_zero = builder.ins().icmp(IntCC::Equal, divisor, zero);
19 let ok_block = builder.create_block();
20 let trap_block = builder.create_block();
21 builder.ins().brif(is_zero, trap_block, &[], ok_block, &[]);
22
23 builder.switch_to_block(trap_block);
24 builder.seal_block(trap_block);
25 builder
26 .ins()
27 .trap(cranelift_codegen::ir::TrapCode::unwrap_user(3));
28
29 builder.switch_to_block(ok_block);
30 builder.seal_block(ok_block);
31}
32
33pub fn emit_primop(
35 sess: &mut EmitSession,
36 builder: &mut FunctionBuilder,
37 op: &PrimOpKind,
38 args: &[SsaVal],
39) -> Result<SsaVal, EmitError> {
40 match op {
41 PrimOpKind::IntAdd => {
43 check_arity(op, 2, args.len())?;
44 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
45 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
46 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_INT))
47 }
48 PrimOpKind::IntSub => {
49 check_arity(op, 2, args.len())?;
50 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
51 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
52 Ok(SsaVal::Raw(builder.ins().isub(a, b), LIT_TAG_INT))
53 }
54 PrimOpKind::IntMul => {
55 check_arity(op, 2, args.len())?;
56 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
57 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
58 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_INT))
59 }
60 PrimOpKind::IntNegate => {
61 check_arity(op, 1, args.len())?;
62 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
63 Ok(SsaVal::Raw(builder.ins().ineg(a), LIT_TAG_INT))
64 }
65 PrimOpKind::IntQuot => {
66 check_arity(op, 2, args.len())?;
67 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
68 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
69 emit_div_zero_check(builder, b);
70 Ok(SsaVal::Raw(builder.ins().sdiv(a, b), LIT_TAG_INT))
71 }
72 PrimOpKind::IntRem => {
73 check_arity(op, 2, args.len())?;
74 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
75 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
76 emit_div_zero_check(builder, b);
77 Ok(SsaVal::Raw(builder.ins().srem(a, b), LIT_TAG_INT))
78 }
79
80 PrimOpKind::IntAnd => {
82 check_arity(op, 2, args.len())?;
83 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
84 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
85 Ok(SsaVal::Raw(builder.ins().band(a, b), LIT_TAG_INT))
86 }
87 PrimOpKind::IntOr => {
88 check_arity(op, 2, args.len())?;
89 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
90 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
91 Ok(SsaVal::Raw(builder.ins().bor(a, b), LIT_TAG_INT))
92 }
93 PrimOpKind::IntXor => {
94 check_arity(op, 2, args.len())?;
95 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
96 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
97 Ok(SsaVal::Raw(builder.ins().bxor(a, b), LIT_TAG_INT))
98 }
99 PrimOpKind::IntNot => {
100 check_arity(op, 1, args.len())?;
101 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
102 Ok(SsaVal::Raw(builder.ins().bnot(a), LIT_TAG_INT))
103 }
104
105 PrimOpKind::IntShl => {
107 check_arity(op, 2, args.len())?;
108 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
109 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
110 Ok(SsaVal::Raw(builder.ins().ishl(a, b), LIT_TAG_INT))
111 }
112 PrimOpKind::IntShra => {
113 check_arity(op, 2, args.len())?;
114 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
115 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
116 Ok(SsaVal::Raw(builder.ins().sshr(a, b), LIT_TAG_INT))
117 }
118 PrimOpKind::IntShrl => {
119 check_arity(op, 2, args.len())?;
120 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
121 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
122 Ok(SsaVal::Raw(builder.ins().ushr(a, b), LIT_TAG_INT))
123 }
124
125 PrimOpKind::IntEq => emit_int_compare(
127 sess.pipeline,
128 builder,
129 sess.vmctx,
130 op,
131 IntCC::Equal,
132 args,
133 LIT_TAG_INT,
134 ),
135 PrimOpKind::IntNe => emit_int_compare(
136 sess.pipeline,
137 builder,
138 sess.vmctx,
139 op,
140 IntCC::NotEqual,
141 args,
142 LIT_TAG_INT,
143 ),
144 PrimOpKind::IntLt => emit_int_compare(
145 sess.pipeline,
146 builder,
147 sess.vmctx,
148 op,
149 IntCC::SignedLessThan,
150 args,
151 LIT_TAG_INT,
152 ),
153 PrimOpKind::IntLe => emit_int_compare(
154 sess.pipeline,
155 builder,
156 sess.vmctx,
157 op,
158 IntCC::SignedLessThanOrEqual,
159 args,
160 LIT_TAG_INT,
161 ),
162 PrimOpKind::IntGt => emit_int_compare(
163 sess.pipeline,
164 builder,
165 sess.vmctx,
166 op,
167 IntCC::SignedGreaterThan,
168 args,
169 LIT_TAG_INT,
170 ),
171 PrimOpKind::IntGe => emit_int_compare(
172 sess.pipeline,
173 builder,
174 sess.vmctx,
175 op,
176 IntCC::SignedGreaterThanOrEqual,
177 args,
178 LIT_TAG_INT,
179 ),
180
181 PrimOpKind::WordAdd => {
183 check_arity(op, 2, args.len())?;
184 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
185 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
186 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_WORD))
187 }
188 PrimOpKind::WordSub => {
189 check_arity(op, 2, args.len())?;
190 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
191 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
192 Ok(SsaVal::Raw(builder.ins().isub(a, b), LIT_TAG_WORD))
193 }
194 PrimOpKind::WordMul => {
195 check_arity(op, 2, args.len())?;
196 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
197 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
198 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_WORD))
199 }
200
201 PrimOpKind::WordQuot => {
202 check_arity(op, 2, args.len())?;
203 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
204 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
205 emit_div_zero_check(builder, b);
206 Ok(SsaVal::Raw(builder.ins().udiv(a, b), LIT_TAG_WORD))
207 }
208 PrimOpKind::WordRem => {
209 check_arity(op, 2, args.len())?;
210 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
211 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
212 emit_div_zero_check(builder, b);
213 Ok(SsaVal::Raw(builder.ins().urem(a, b), LIT_TAG_WORD))
214 }
215
216 PrimOpKind::WordAnd => {
218 check_arity(op, 2, args.len())?;
219 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
220 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
221 Ok(SsaVal::Raw(builder.ins().band(a, b), LIT_TAG_WORD))
222 }
223 PrimOpKind::WordOr => {
224 check_arity(op, 2, args.len())?;
225 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
226 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
227 Ok(SsaVal::Raw(builder.ins().bor(a, b), LIT_TAG_WORD))
228 }
229 PrimOpKind::WordXor => {
230 check_arity(op, 2, args.len())?;
231 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
232 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
233 Ok(SsaVal::Raw(builder.ins().bxor(a, b), LIT_TAG_WORD))
234 }
235 PrimOpKind::WordNot => {
236 check_arity(op, 1, args.len())?;
237 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
238 Ok(SsaVal::Raw(builder.ins().bnot(a), LIT_TAG_WORD))
239 }
240
241 PrimOpKind::WordShl => {
243 check_arity(op, 2, args.len())?;
244 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
245 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
246 Ok(SsaVal::Raw(builder.ins().ishl(a, b), LIT_TAG_WORD))
247 }
248 PrimOpKind::WordShrl => {
249 check_arity(op, 2, args.len())?;
250 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
251 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
252 Ok(SsaVal::Raw(builder.ins().ushr(a, b), LIT_TAG_WORD))
253 }
254
255 PrimOpKind::WordEq => emit_int_compare(
257 sess.pipeline,
258 builder,
259 sess.vmctx,
260 op,
261 IntCC::Equal,
262 args,
263 LIT_TAG_INT,
264 ),
265 PrimOpKind::WordNe => emit_int_compare(
266 sess.pipeline,
267 builder,
268 sess.vmctx,
269 op,
270 IntCC::NotEqual,
271 args,
272 LIT_TAG_INT,
273 ),
274 PrimOpKind::WordLt => emit_int_compare(
275 sess.pipeline,
276 builder,
277 sess.vmctx,
278 op,
279 IntCC::UnsignedLessThan,
280 args,
281 LIT_TAG_INT,
282 ),
283 PrimOpKind::WordLe => emit_int_compare(
284 sess.pipeline,
285 builder,
286 sess.vmctx,
287 op,
288 IntCC::UnsignedLessThanOrEqual,
289 args,
290 LIT_TAG_INT,
291 ),
292 PrimOpKind::WordGt => emit_int_compare(
293 sess.pipeline,
294 builder,
295 sess.vmctx,
296 op,
297 IntCC::UnsignedGreaterThan,
298 args,
299 LIT_TAG_INT,
300 ),
301 PrimOpKind::WordGe => emit_int_compare(
302 sess.pipeline,
303 builder,
304 sess.vmctx,
305 op,
306 IntCC::UnsignedGreaterThanOrEqual,
307 args,
308 LIT_TAG_INT,
309 ),
310
311 PrimOpKind::DoubleAdd => {
313 check_arity(op, 2, args.len())?;
314 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
315 let b = unbox_double(sess.pipeline, builder, sess.vmctx, args[1]);
316 Ok(SsaVal::Raw(builder.ins().fadd(a, b), LIT_TAG_DOUBLE))
317 }
318 PrimOpKind::DoubleSub => {
319 check_arity(op, 2, args.len())?;
320 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
321 let b = unbox_double(sess.pipeline, builder, sess.vmctx, args[1]);
322 Ok(SsaVal::Raw(builder.ins().fsub(a, b), LIT_TAG_DOUBLE))
323 }
324 PrimOpKind::DoubleMul => {
325 check_arity(op, 2, args.len())?;
326 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
327 let b = unbox_double(sess.pipeline, builder, sess.vmctx, args[1]);
328 Ok(SsaVal::Raw(builder.ins().fmul(a, b), LIT_TAG_DOUBLE))
329 }
330 PrimOpKind::DoubleDiv => {
331 check_arity(op, 2, args.len())?;
332 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
333 let b = unbox_double(sess.pipeline, builder, sess.vmctx, args[1]);
334 Ok(SsaVal::Raw(builder.ins().fdiv(a, b), LIT_TAG_DOUBLE))
335 }
336
337 PrimOpKind::DoubleEq => emit_double_compare(
339 sess.pipeline,
340 builder,
341 sess.vmctx,
342 op,
343 FloatCC::Equal,
344 args,
345 LIT_TAG_INT,
346 ),
347 PrimOpKind::DoubleNe => emit_double_compare(
348 sess.pipeline,
349 builder,
350 sess.vmctx,
351 op,
352 FloatCC::NotEqual,
353 args,
354 LIT_TAG_INT,
355 ),
356 PrimOpKind::DoubleLt => emit_double_compare(
357 sess.pipeline,
358 builder,
359 sess.vmctx,
360 op,
361 FloatCC::LessThan,
362 args,
363 LIT_TAG_INT,
364 ),
365 PrimOpKind::DoubleLe => emit_double_compare(
366 sess.pipeline,
367 builder,
368 sess.vmctx,
369 op,
370 FloatCC::LessThanOrEqual,
371 args,
372 LIT_TAG_INT,
373 ),
374 PrimOpKind::DoubleGt => emit_double_compare(
375 sess.pipeline,
376 builder,
377 sess.vmctx,
378 op,
379 FloatCC::GreaterThan,
380 args,
381 LIT_TAG_INT,
382 ),
383 PrimOpKind::DoubleGe => emit_double_compare(
384 sess.pipeline,
385 builder,
386 sess.vmctx,
387 op,
388 FloatCC::GreaterThanOrEqual,
389 args,
390 LIT_TAG_INT,
391 ),
392
393 PrimOpKind::CharEq => emit_int_compare(
395 sess.pipeline,
396 builder,
397 sess.vmctx,
398 op,
399 IntCC::Equal,
400 args,
401 LIT_TAG_INT,
402 ),
403 PrimOpKind::CharNe => emit_int_compare(
404 sess.pipeline,
405 builder,
406 sess.vmctx,
407 op,
408 IntCC::NotEqual,
409 args,
410 LIT_TAG_INT,
411 ),
412 PrimOpKind::CharLt => emit_int_compare(
413 sess.pipeline,
414 builder,
415 sess.vmctx,
416 op,
417 IntCC::UnsignedLessThan,
418 args,
419 LIT_TAG_INT,
420 ),
421 PrimOpKind::CharLe => emit_int_compare(
422 sess.pipeline,
423 builder,
424 sess.vmctx,
425 op,
426 IntCC::UnsignedLessThanOrEqual,
427 args,
428 LIT_TAG_INT,
429 ),
430 PrimOpKind::CharGt => emit_int_compare(
431 sess.pipeline,
432 builder,
433 sess.vmctx,
434 op,
435 IntCC::UnsignedGreaterThan,
436 args,
437 LIT_TAG_INT,
438 ),
439 PrimOpKind::CharGe => emit_int_compare(
440 sess.pipeline,
441 builder,
442 sess.vmctx,
443 op,
444 IntCC::UnsignedGreaterThanOrEqual,
445 args,
446 LIT_TAG_INT,
447 ),
448
449 PrimOpKind::Chr => {
451 check_arity(op, 1, args.len())?;
452 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
453
454 let zero = builder.ins().iconst(types::I64, 0);
458 let max_valid = builder.ins().iconst(types::I64, 0x10FFFF);
459 let is_negative = builder.ins().icmp(IntCC::SignedLessThan, v, zero);
460 let is_too_large = builder.ins().icmp(IntCC::SignedGreaterThan, v, max_valid);
461 let surrogate_lo = builder.ins().iconst(types::I64, 0xD800);
462 let surrogate_hi = builder.ins().iconst(types::I64, 0xDFFF);
463 let is_surr_lo = builder
464 .ins()
465 .icmp(IntCC::SignedGreaterThanOrEqual, v, surrogate_lo);
466 let is_surr_hi = builder
467 .ins()
468 .icmp(IntCC::SignedLessThanOrEqual, v, surrogate_hi);
469 let is_surrogate = builder.ins().band(is_surr_lo, is_surr_hi);
470 let out_of_range = builder.ins().bor(is_negative, is_too_large);
471 let is_invalid = builder.ins().bor(out_of_range, is_surrogate);
472 builder
473 .ins()
474 .trapnz(is_invalid, cranelift_codegen::ir::TrapCode::unwrap_user(1));
475
476 Ok(SsaVal::Raw(v, LIT_TAG_CHAR))
477 }
478 PrimOpKind::Ord => {
479 check_arity(op, 1, args.len())?;
480 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
481 Ok(SsaVal::Raw(v, LIT_TAG_INT))
482 }
483 PrimOpKind::Int2Word | PrimOpKind::Word2Int => {
484 check_arity(op, 1, args.len())?;
485 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
486 let tag = if matches!(op, PrimOpKind::Int2Word) {
487 LIT_TAG_WORD
488 } else {
489 LIT_TAG_INT
490 };
491 Ok(SsaVal::Raw(v, tag))
492 }
493 PrimOpKind::Int2Double => {
494 check_arity(op, 1, args.len())?;
495 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
496 Ok(SsaVal::Raw(
497 builder.ins().fcvt_from_sint(types::F64, v),
498 LIT_TAG_DOUBLE,
499 ))
500 }
501 PrimOpKind::Double2Int => {
502 check_arity(op, 1, args.len())?;
503 let v = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
504 Ok(SsaVal::Raw(
505 builder.ins().fcvt_to_sint_sat(types::I64, v),
506 LIT_TAG_INT,
507 ))
508 }
509 PrimOpKind::DecodeDoubleMantissa => {
510 check_arity(op, 1, args.len())?;
511 let d = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
512 let bits = builder.ins().bitcast(types::I64, MemFlags::new(), d);
513 let result = emit_runtime_call(
514 sess.pipeline,
515 builder,
516 "runtime_decode_double_mantissa",
517 &[AbiParam::new(types::I64)],
518 &[AbiParam::new(types::I64)],
519 &[bits],
520 )?;
521 Ok(SsaVal::Raw(result, LIT_TAG_INT))
522 }
523 PrimOpKind::DecodeDoubleExponent => {
524 check_arity(op, 1, args.len())?;
525 let d = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
526 let bits = builder.ins().bitcast(types::I64, MemFlags::new(), d);
527 let result = emit_runtime_call(
528 sess.pipeline,
529 builder,
530 "runtime_decode_double_exponent",
531 &[AbiParam::new(types::I64)],
532 &[AbiParam::new(types::I64)],
533 &[bits],
534 )?;
535 Ok(SsaVal::Raw(result, LIT_TAG_INT))
536 }
537 PrimOpKind::ShowDoubleAddr => {
538 check_arity(op, 1, args.len())?;
539 let d = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
540 let bits = builder.ins().bitcast(types::I64, MemFlags::new(), d);
541 let result = emit_runtime_call(
542 sess.pipeline,
543 builder,
544 "runtime_show_double_addr",
545 &[AbiParam::new(types::I64)],
546 &[AbiParam::new(types::I64)],
547 &[bits],
548 )?;
549 Ok(SsaVal::Raw(result, LIT_TAG_ADDR))
550 }
551 PrimOpKind::Int2Float => {
552 check_arity(op, 1, args.len())?;
553 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
554 Ok(SsaVal::Raw(
555 builder.ins().fcvt_from_sint(types::F32, v),
556 LIT_TAG_FLOAT,
557 ))
558 }
559 PrimOpKind::Float2Int => {
560 check_arity(op, 1, args.len())?;
561 let v = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
562 Ok(SsaVal::Raw(
563 builder.ins().fcvt_to_sint_sat(types::I64, v),
564 LIT_TAG_INT,
565 ))
566 }
567 PrimOpKind::Double2Float => {
568 check_arity(op, 1, args.len())?;
569 let v = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
570 Ok(SsaVal::Raw(
571 builder.ins().fdemote(types::F32, v),
572 LIT_TAG_FLOAT,
573 ))
574 }
575 PrimOpKind::Float2Double => {
576 check_arity(op, 1, args.len())?;
577 let v = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
578 Ok(SsaVal::Raw(
579 builder.ins().fpromote(types::F64, v),
580 LIT_TAG_DOUBLE,
581 ))
582 }
583
584 PrimOpKind::Narrow8Int => {
586 check_arity(op, 1, args.len())?;
587 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
588 let narrow = builder.ins().ireduce(types::I8, v);
589 Ok(SsaVal::Raw(
590 builder.ins().sextend(types::I64, narrow),
591 LIT_TAG_INT,
592 ))
593 }
594 PrimOpKind::Narrow16Int => {
595 check_arity(op, 1, args.len())?;
596 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
597 let narrow = builder.ins().ireduce(types::I16, v);
598 Ok(SsaVal::Raw(
599 builder.ins().sextend(types::I64, narrow),
600 LIT_TAG_INT,
601 ))
602 }
603 PrimOpKind::Narrow32Int => {
604 check_arity(op, 1, args.len())?;
605 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
606 let narrow = builder.ins().ireduce(types::I32, v);
607 Ok(SsaVal::Raw(
608 builder.ins().sextend(types::I64, narrow),
609 LIT_TAG_INT,
610 ))
611 }
612 PrimOpKind::Narrow8Word => {
613 check_arity(op, 1, args.len())?;
614 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
615 let narrow = builder.ins().ireduce(types::I8, v);
616 Ok(SsaVal::Raw(
617 builder.ins().uextend(types::I64, narrow),
618 LIT_TAG_WORD,
619 ))
620 }
621 PrimOpKind::Narrow16Word => {
622 check_arity(op, 1, args.len())?;
623 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
624 let narrow = builder.ins().ireduce(types::I16, v);
625 Ok(SsaVal::Raw(
626 builder.ins().uextend(types::I64, narrow),
627 LIT_TAG_WORD,
628 ))
629 }
630 PrimOpKind::Narrow32Word => {
631 check_arity(op, 1, args.len())?;
632 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
633 let narrow = builder.ins().ireduce(types::I32, v);
634 Ok(SsaVal::Raw(
635 builder.ins().uextend(types::I64, narrow),
636 LIT_TAG_WORD,
637 ))
638 }
639
640 PrimOpKind::DataToTag => {
642 check_arity(op, 1, args.len())?;
643 let obj = args[0].value();
644 let tag = builder
645 .ins()
646 .load(types::I64, MemFlags::trusted(), obj, CON_TAG_OFFSET);
647 Ok(SsaVal::Raw(tag, LIT_TAG_INT))
648 }
649 PrimOpKind::DoubleNegate => {
650 check_arity(op, 1, args.len())?;
651 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
652 Ok(SsaVal::Raw(builder.ins().fneg(a), LIT_TAG_DOUBLE))
653 }
654 PrimOpKind::DoubleFabs => {
655 check_arity(op, 1, args.len())?;
656 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
657 Ok(SsaVal::Raw(builder.ins().fabs(a), LIT_TAG_DOUBLE))
658 }
659 PrimOpKind::DoubleSqrt => {
661 check_arity(op, 1, args.len())?;
662 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
663 Ok(SsaVal::Raw(builder.ins().sqrt(a), LIT_TAG_DOUBLE))
664 }
665 PrimOpKind::DoubleExp
666 | PrimOpKind::DoubleExpM1
667 | PrimOpKind::DoubleLog
668 | PrimOpKind::DoubleLog1P
669 | PrimOpKind::DoubleSin
670 | PrimOpKind::DoubleCos
671 | PrimOpKind::DoubleTan
672 | PrimOpKind::DoubleAsin
673 | PrimOpKind::DoubleAcos
674 | PrimOpKind::DoubleAtan
675 | PrimOpKind::DoubleSinh
676 | PrimOpKind::DoubleCosh
677 | PrimOpKind::DoubleTanh
678 | PrimOpKind::DoubleAsinh
679 | PrimOpKind::DoubleAcosh
680 | PrimOpKind::DoubleAtanh => {
681 check_arity(op, 1, args.len())?;
682 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
683 let fn_name = match op {
684 PrimOpKind::DoubleExp => "runtime_double_exp",
685 PrimOpKind::DoubleExpM1 => "runtime_double_expm1",
686 PrimOpKind::DoubleLog => "runtime_double_log",
687 PrimOpKind::DoubleLog1P => "runtime_double_log1p",
688 PrimOpKind::DoubleSin => "runtime_double_sin",
689 PrimOpKind::DoubleCos => "runtime_double_cos",
690 PrimOpKind::DoubleTan => "runtime_double_tan",
691 PrimOpKind::DoubleAsin => "runtime_double_asin",
692 PrimOpKind::DoubleAcos => "runtime_double_acos",
693 PrimOpKind::DoubleAtan => "runtime_double_atan",
694 PrimOpKind::DoubleSinh => "runtime_double_sinh",
695 PrimOpKind::DoubleCosh => "runtime_double_cosh",
696 PrimOpKind::DoubleTanh => "runtime_double_tanh",
697 PrimOpKind::DoubleAsinh => "runtime_double_asinh",
698 PrimOpKind::DoubleAcosh => "runtime_double_acosh",
699 PrimOpKind::DoubleAtanh => "runtime_double_atanh",
700 _ => {
701 return Err(EmitError::InternalError(format!(
702 "unexpected double primop variant: {:?}",
703 op
704 )))
705 }
706 };
707 let bits = builder.ins().bitcast(types::I64, MemFlags::new(), a);
708 let result = emit_runtime_call(
709 sess.pipeline,
710 builder,
711 fn_name,
712 &[AbiParam::new(types::I64)],
713 &[AbiParam::new(types::I64)],
714 &[bits],
715 )?;
716 let d = builder.ins().bitcast(types::F64, MemFlags::new(), result);
717 Ok(SsaVal::Raw(d, LIT_TAG_DOUBLE))
718 }
719 PrimOpKind::DoublePower => {
720 check_arity(op, 2, args.len())?;
721 let a = unbox_double(sess.pipeline, builder, sess.vmctx, args[0]);
722 let b = unbox_double(sess.pipeline, builder, sess.vmctx, args[1]);
723 let bits_a = builder.ins().bitcast(types::I64, MemFlags::new(), a);
724 let bits_b = builder.ins().bitcast(types::I64, MemFlags::new(), b);
725 let result = emit_runtime_call(
726 sess.pipeline,
727 builder,
728 "runtime_double_power",
729 &[AbiParam::new(types::I64), AbiParam::new(types::I64)],
730 &[AbiParam::new(types::I64)],
731 &[bits_a, bits_b],
732 )?;
733 let d = builder.ins().bitcast(types::F64, MemFlags::new(), result);
734 Ok(SsaVal::Raw(d, LIT_TAG_DOUBLE))
735 }
736 PrimOpKind::FloatNegate => {
737 check_arity(op, 1, args.len())?;
738 let a = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
739 Ok(SsaVal::Raw(builder.ins().fneg(a), LIT_TAG_FLOAT))
740 }
741
742 PrimOpKind::ReallyUnsafePtrEquality => {
743 check_arity(op, 2, args.len())?;
744 Ok(SsaVal::Raw(
745 builder.ins().iconst(types::I64, 0),
746 LIT_TAG_INT,
747 ))
748 }
749
750 PrimOpKind::IndexCharOffAddr => {
751 check_arity(op, 2, args.len())?;
752 let addr = unbox_addr(sess.pipeline, builder, args[0]);
753 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
754 let effective = builder.ins().iadd(addr, idx);
755 let byte_val = builder.ins().load(types::I8, MemFlags::new(), effective, 0);
756 let char_val = builder.ins().uextend(types::I64, byte_val);
757 Ok(SsaVal::Raw(char_val, LIT_TAG_CHAR))
758 }
759
760 PrimOpKind::PlusAddr => {
761 check_arity(op, 2, args.len())?;
762 let addr = unbox_addr(sess.pipeline, builder, args[0]);
763 let offset = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
764 Ok(SsaVal::Raw(builder.ins().iadd(addr, offset), LIT_TAG_ADDR))
765 }
766
767 PrimOpKind::Int64Add => {
774 check_arity(op, 2, args.len())?;
775 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
776 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
777 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_INT))
778 }
779 PrimOpKind::Int64Sub => {
780 check_arity(op, 2, args.len())?;
781 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
782 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
783 Ok(SsaVal::Raw(builder.ins().isub(a, b), LIT_TAG_INT))
784 }
785 PrimOpKind::Int64Mul => {
786 check_arity(op, 2, args.len())?;
787 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
788 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
789 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_INT))
790 }
791 PrimOpKind::Int64Negate => {
792 check_arity(op, 1, args.len())?;
793 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
794 Ok(SsaVal::Raw(builder.ins().ineg(a), LIT_TAG_INT))
795 }
796 PrimOpKind::Int64Shl => {
797 check_arity(op, 2, args.len())?;
798 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
799 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
800 Ok(SsaVal::Raw(builder.ins().ishl(a, b), LIT_TAG_INT))
801 }
802 PrimOpKind::Int64Shra => {
803 check_arity(op, 2, args.len())?;
804 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
805 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
806 Ok(SsaVal::Raw(builder.ins().sshr(a, b), LIT_TAG_INT))
807 }
808
809 PrimOpKind::Int64Lt => emit_int_compare(
811 sess.pipeline,
812 builder,
813 sess.vmctx,
814 op,
815 IntCC::SignedLessThan,
816 args,
817 LIT_TAG_INT,
818 ),
819 PrimOpKind::Int64Le => emit_int_compare(
820 sess.pipeline,
821 builder,
822 sess.vmctx,
823 op,
824 IntCC::SignedLessThanOrEqual,
825 args,
826 LIT_TAG_INT,
827 ),
828 PrimOpKind::Int64Gt => emit_int_compare(
829 sess.pipeline,
830 builder,
831 sess.vmctx,
832 op,
833 IntCC::SignedGreaterThan,
834 args,
835 LIT_TAG_INT,
836 ),
837 PrimOpKind::Int64Ge => emit_int_compare(
838 sess.pipeline,
839 builder,
840 sess.vmctx,
841 op,
842 IntCC::SignedGreaterThanOrEqual,
843 args,
844 LIT_TAG_INT,
845 ),
846
847 PrimOpKind::Word64And => {
849 check_arity(op, 2, args.len())?;
850 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
851 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
852 Ok(SsaVal::Raw(builder.ins().band(a, b), LIT_TAG_WORD))
853 }
854 PrimOpKind::Word64Shl => {
855 check_arity(op, 2, args.len())?;
856 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
857 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
858 Ok(SsaVal::Raw(builder.ins().ishl(a, b), LIT_TAG_WORD))
859 }
860 PrimOpKind::Word64Or => {
861 check_arity(op, 2, args.len())?;
862 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
863 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
864 Ok(SsaVal::Raw(builder.ins().bor(a, b), LIT_TAG_WORD))
865 }
866
867 PrimOpKind::Word64ToInt64 | PrimOpKind::Int64ToInt | PrimOpKind::Int64ToWord64 => {
869 check_arity(op, 1, args.len())?;
870 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
871 Ok(SsaVal::Raw(v, LIT_TAG_INT))
872 }
873 PrimOpKind::IntToInt64 => {
874 check_arity(op, 1, args.len())?;
875 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
876 Ok(SsaVal::Raw(v, LIT_TAG_INT))
877 }
878 PrimOpKind::Word8ToWord => {
879 check_arity(op, 1, args.len())?;
880 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
881 Ok(SsaVal::Raw(v, LIT_TAG_WORD))
882 }
883 PrimOpKind::WordToWord8 => {
884 check_arity(op, 1, args.len())?;
887 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
888 let mask = builder.ins().iconst(types::I64, 0xFF);
889 let narrow = builder.ins().band(v, mask);
890 Ok(SsaVal::Raw(narrow, LIT_TAG_WORD))
891 }
892
893 PrimOpKind::Word8Add => {
895 check_arity(op, 2, args.len())?;
896 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
897 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
898 let sum = builder.ins().iadd(a, b);
899 Ok(SsaVal::Raw(builder.ins().band_imm(sum, 0xFF), LIT_TAG_WORD))
900 }
901 PrimOpKind::Word8Sub => {
902 check_arity(op, 2, args.len())?;
903 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
904 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
905 let diff = builder.ins().isub(a, b);
906 Ok(SsaVal::Raw(
907 builder.ins().band_imm(diff, 0xFF),
908 LIT_TAG_WORD,
909 ))
910 }
911 PrimOpKind::Word8Lt => emit_int_compare(
912 sess.pipeline,
913 builder,
914 sess.vmctx,
915 op,
916 IntCC::UnsignedLessThan,
917 args,
918 LIT_TAG_INT,
919 ),
920 PrimOpKind::Word8Le => emit_int_compare(
921 sess.pipeline,
922 builder,
923 sess.vmctx,
924 op,
925 IntCC::UnsignedLessThanOrEqual,
926 args,
927 LIT_TAG_INT,
928 ),
929 PrimOpKind::Word8Ge => emit_int_compare(
930 sess.pipeline,
931 builder,
932 sess.vmctx,
933 op,
934 IntCC::UnsignedGreaterThanOrEqual,
935 args,
936 LIT_TAG_INT,
937 ),
938
939 PrimOpKind::AddIntCVal => {
946 check_arity(op, 2, args.len())?;
947 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
948 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
949 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_INT))
950 }
951 PrimOpKind::AddIntCCarry => {
952 check_arity(op, 2, args.len())?;
953 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
954 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
955 let sum = builder.ins().iadd(a, b);
956 let xor_ab = builder.ins().bxor(a, b);
959 let xor_as = builder.ins().bxor(a, sum);
960 let not_xor_ab = builder.ins().bnot(xor_ab);
962 let overflow_bits = builder.ins().band(not_xor_ab, xor_as);
963 let shifted = builder.ins().ushr_imm(overflow_bits, 63);
965 Ok(SsaVal::Raw(shifted, LIT_TAG_INT))
966 }
967
968 PrimOpKind::SubWordCVal => {
970 check_arity(op, 2, args.len())?;
971 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
972 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
973 Ok(SsaVal::Raw(builder.ins().isub(a, b), LIT_TAG_WORD))
974 }
975 PrimOpKind::SubWordCCarry => {
976 check_arity(op, 2, args.len())?;
977 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
978 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
979 let borrow = builder.ins().icmp(IntCC::UnsignedLessThan, a, b);
981 Ok(SsaVal::Raw(
982 builder.ins().uextend(types::I64, borrow),
983 LIT_TAG_INT,
984 ))
985 }
986
987 PrimOpKind::AddWordCVal => {
989 check_arity(op, 2, args.len())?;
990 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
991 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
992 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_WORD))
993 }
994 PrimOpKind::AddWordCCarry => {
995 check_arity(op, 2, args.len())?;
996 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
997 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
998 let sum = builder.ins().iadd(a, b);
999 let carry = builder.ins().icmp(IntCC::UnsignedLessThan, sum, a);
1001 Ok(SsaVal::Raw(
1002 builder.ins().uextend(types::I64, carry),
1003 LIT_TAG_INT,
1004 ))
1005 }
1006
1007 PrimOpKind::TimesInt2Hi => {
1010 check_arity(op, 2, args.len())?;
1011 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1012 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1013 Ok(SsaVal::Raw(builder.ins().smulhi(a, b), LIT_TAG_INT))
1014 }
1015 PrimOpKind::TimesInt2Lo => {
1016 check_arity(op, 2, args.len())?;
1017 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1018 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1019 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_INT))
1020 }
1021 PrimOpKind::TimesInt2Overflow => {
1022 check_arity(op, 2, args.len())?;
1023 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1024 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1025 let hi = builder.ins().smulhi(a, b);
1028 let lo = builder.ins().imul(a, b);
1029 let lo_sign = builder.ins().sshr_imm(lo, 63);
1030 let overflow = builder.ins().icmp(IntCC::NotEqual, hi, lo_sign);
1031 Ok(SsaVal::Raw(
1032 builder.ins().uextend(types::I64, overflow),
1033 LIT_TAG_INT,
1034 ))
1035 }
1036
1037 PrimOpKind::TimesWord2Hi => {
1040 check_arity(op, 2, args.len())?;
1041 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1042 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1043 Ok(SsaVal::Raw(builder.ins().umulhi(a, b), LIT_TAG_WORD))
1044 }
1045 PrimOpKind::TimesWord2Lo => {
1046 check_arity(op, 2, args.len())?;
1047 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1048 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1049 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_WORD))
1050 }
1051
1052 PrimOpKind::QuotRemWordVal => {
1054 check_arity(op, 2, args.len())?;
1055 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1056 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1057 emit_div_zero_check(builder, b);
1058 Ok(SsaVal::Raw(builder.ins().udiv(a, b), LIT_TAG_WORD))
1059 }
1060 PrimOpKind::QuotRemWordRem => {
1061 check_arity(op, 2, args.len())?;
1062 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1063 let b = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1064 emit_div_zero_check(builder, b);
1065 Ok(SsaVal::Raw(builder.ins().urem(a, b), LIT_TAG_WORD))
1066 }
1067
1068 PrimOpKind::NewByteArray => {
1074 let size = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1077 let ba_ptr = emit_runtime_call(
1078 sess.pipeline,
1079 builder,
1080 "runtime_new_byte_array",
1081 &[AbiParam::new(types::I64)],
1082 &[AbiParam::new(types::I64)],
1083 &[size],
1084 )?;
1085 Ok(emit_lit_bytearray(
1087 builder,
1088 sess.vmctx,
1089 sess.gc_sig,
1090 sess.oom_func,
1091 ba_ptr,
1092 ))
1093 }
1094
1095 PrimOpKind::UnsafeFreezeByteArray => {
1096 Ok(args[0])
1099 }
1100
1101 PrimOpKind::SizeofByteArray | PrimOpKind::SizeofMutableByteArray => {
1102 let ba_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1104 let len = builder.ins().load(types::I64, MemFlags::new(), ba_ptr, 0);
1106 Ok(SsaVal::Raw(len, LIT_TAG_INT))
1107 }
1108
1109 PrimOpKind::ReadWord8Array | PrimOpKind::IndexWord8Array => {
1110 let ba_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1112 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1113 let base = builder.ins().iadd_imm(ba_ptr, 8);
1115 let effective = builder.ins().iadd(base, idx);
1116 let byte = builder.ins().load(types::I8, MemFlags::new(), effective, 0);
1117 let val = builder.ins().uextend(types::I64, byte);
1118 Ok(SsaVal::Raw(val, LIT_TAG_WORD))
1119 }
1120
1121 PrimOpKind::WriteWord8Array => {
1122 let ba_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1124 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1125 let val = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1126 let base = builder.ins().iadd_imm(ba_ptr, 8);
1127 let effective = builder.ins().iadd(base, idx);
1128 let byte = builder.ins().ireduce(types::I8, val);
1129 builder.ins().store(MemFlags::new(), byte, effective, 0);
1130 Ok(SsaVal::Raw(
1132 builder.ins().iconst(types::I64, 0),
1133 LIT_TAG_INT,
1134 ))
1135 }
1136
1137 PrimOpKind::IndexWordArray | PrimOpKind::ReadWordArray => {
1138 let ba_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1140 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1141 let base = builder.ins().iadd_imm(ba_ptr, 8);
1142 let byte_offset = builder.ins().imul_imm(idx, 8);
1144 let effective = builder.ins().iadd(base, byte_offset);
1145 let word = builder
1146 .ins()
1147 .load(types::I64, MemFlags::new(), effective, 0);
1148 Ok(SsaVal::Raw(word, LIT_TAG_WORD))
1149 }
1150
1151 PrimOpKind::WriteWordArray => {
1152 let ba_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1154 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1155 let val = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1156 let base = builder.ins().iadd_imm(ba_ptr, 8);
1157 let byte_offset = builder.ins().imul_imm(idx, 8);
1158 let effective = builder.ins().iadd(base, byte_offset);
1159 builder.ins().store(MemFlags::new(), val, effective, 0);
1160 Ok(SsaVal::Raw(
1161 builder.ins().iconst(types::I64, 0),
1162 LIT_TAG_INT,
1163 ))
1164 }
1165
1166 PrimOpKind::CopyAddrToByteArray => {
1167 let src = unbox_addr(sess.pipeline, builder, args[0]);
1169 let dest_ba = unbox_bytearray(sess.pipeline, builder, args[1]);
1170 let dest_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1171 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1172 let _ = emit_runtime_call(
1173 sess.pipeline,
1174 builder,
1175 "runtime_copy_addr_to_byte_array",
1176 &[
1177 AbiParam::new(types::I64),
1178 AbiParam::new(types::I64),
1179 AbiParam::new(types::I64),
1180 AbiParam::new(types::I64),
1181 ],
1182 &[],
1183 &[src, dest_ba, dest_off, len],
1184 )?;
1185 Ok(SsaVal::Raw(
1186 builder.ins().iconst(types::I64, 0),
1187 LIT_TAG_INT,
1188 ))
1189 }
1190
1191 PrimOpKind::SetByteArray => {
1192 let ba = unbox_bytearray(sess.pipeline, builder, args[0]);
1194 let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1195 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1196 let val = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1197 let _ = emit_runtime_call(
1198 sess.pipeline,
1199 builder,
1200 "runtime_set_byte_array",
1201 &[
1202 AbiParam::new(types::I64),
1203 AbiParam::new(types::I64),
1204 AbiParam::new(types::I64),
1205 AbiParam::new(types::I64),
1206 ],
1207 &[],
1208 &[ba, off, len, val],
1209 )?;
1210 Ok(SsaVal::Raw(
1211 builder.ins().iconst(types::I64, 0),
1212 LIT_TAG_INT,
1213 ))
1214 }
1215
1216 PrimOpKind::ShrinkMutableByteArray => {
1217 let ba = unbox_bytearray(sess.pipeline, builder, args[0]);
1219 let new_size = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1220 let _ = emit_runtime_call(
1221 sess.pipeline,
1222 builder,
1223 "runtime_shrink_byte_array",
1224 &[AbiParam::new(types::I64), AbiParam::new(types::I64)],
1225 &[],
1226 &[ba, new_size],
1227 )?;
1228 Ok(SsaVal::Raw(
1229 builder.ins().iconst(types::I64, 0),
1230 LIT_TAG_INT,
1231 ))
1232 }
1233 PrimOpKind::ResizeMutableByteArray => {
1234 let ba = unbox_bytearray(sess.pipeline, builder, args[0]);
1238 let new_size = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1239 let result = emit_runtime_call(
1240 sess.pipeline,
1241 builder,
1242 "runtime_resize_byte_array",
1243 &[AbiParam::new(types::I64), AbiParam::new(types::I64)],
1244 &[AbiParam::new(types::I64)],
1245 &[ba, new_size],
1246 )?;
1247 Ok(SsaVal::Raw(result, LIT_TAG_BYTEARRAY))
1248 }
1249
1250 PrimOpKind::CopyByteArray => {
1251 let src = unbox_bytearray(sess.pipeline, builder, args[0]);
1254 let src_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1255 let dest = unbox_bytearray(sess.pipeline, builder, args[2]);
1256 let dest_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1257 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[4]);
1258 let _ = emit_runtime_call(
1259 sess.pipeline,
1260 builder,
1261 "runtime_copy_byte_array",
1262 &[
1263 AbiParam::new(types::I64),
1264 AbiParam::new(types::I64),
1265 AbiParam::new(types::I64),
1266 AbiParam::new(types::I64),
1267 AbiParam::new(types::I64),
1268 ],
1269 &[],
1270 &[src, src_off, dest, dest_off, len],
1271 )?;
1272 Ok(SsaVal::Raw(
1273 builder.ins().iconst(types::I64, 0),
1274 LIT_TAG_INT,
1275 ))
1276 }
1277 PrimOpKind::CopyMutableByteArray => {
1278 let src = unbox_bytearray(sess.pipeline, builder, args[0]);
1280 let src_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1281 let dest = unbox_bytearray(sess.pipeline, builder, args[2]);
1282 let dest_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1283 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[4]);
1284 let _ = emit_runtime_call(
1285 sess.pipeline,
1286 builder,
1287 "runtime_copy_byte_array",
1288 &[
1289 AbiParam::new(types::I64),
1290 AbiParam::new(types::I64),
1291 AbiParam::new(types::I64),
1292 AbiParam::new(types::I64),
1293 AbiParam::new(types::I64),
1294 ],
1295 &[],
1296 &[src, src_off, dest, dest_off, len],
1297 )?;
1298 Ok(SsaVal::Raw(
1299 builder.ins().iconst(types::I64, 0),
1300 LIT_TAG_INT,
1301 ))
1302 }
1303 PrimOpKind::CompareByteArrays => {
1304 if args.len() != 5 {
1306 return Err(EmitError::InvalidArity(
1307 PrimOpKind::CompareByteArrays,
1308 5,
1309 args.len(),
1310 ));
1311 }
1312 let a = unbox_bytearray(sess.pipeline, builder, args[0]);
1313 let a_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1314 let b = unbox_bytearray(sess.pipeline, builder, args[2]);
1315 let b_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1316 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[4]);
1317 let result = emit_runtime_call(
1318 sess.pipeline,
1319 builder,
1320 "runtime_compare_byte_arrays",
1321 &[
1322 AbiParam::new(types::I64),
1323 AbiParam::new(types::I64),
1324 AbiParam::new(types::I64),
1325 AbiParam::new(types::I64),
1326 AbiParam::new(types::I64),
1327 ],
1328 &[AbiParam::new(types::I64)],
1329 &[a, a_off, b, b_off, len],
1330 )?;
1331 Ok(SsaVal::Raw(result, LIT_TAG_INT))
1332 }
1333 PrimOpKind::IndexWord8OffAddr => {
1334 let addr = unbox_addr(sess.pipeline, builder, args[0]);
1336 let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1337 let ptr = builder.ins().iadd(addr, off);
1338 let byte = builder.ins().load(types::I8, MemFlags::trusted(), ptr, 0);
1339 let word = builder.ins().uextend(types::I64, byte);
1340 Ok(SsaVal::Raw(word, LIT_TAG_WORD))
1341 }
1342 PrimOpKind::Clz8 => {
1343 check_arity(op, 1, args.len())?;
1345 let v = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1346 let narrow = builder.ins().ireduce(types::I8, v);
1347 let clz8 = builder.ins().clz(narrow);
1348 let result = builder.ins().uextend(types::I64, clz8);
1349 Ok(SsaVal::Raw(result, LIT_TAG_WORD))
1350 }
1351 PrimOpKind::Raise => {
1352 let kind = builder.ins().iconst(types::I64, 2); let _ = emit_runtime_call(
1355 sess.pipeline,
1356 builder,
1357 "runtime_error",
1358 &[AbiParam::new(types::I64)],
1359 &[AbiParam::new(types::I64)],
1360 &[kind],
1361 )?;
1362 Ok(SsaVal::Raw(
1363 builder.ins().iconst(types::I64, 0),
1364 LIT_TAG_INT,
1365 ))
1366 }
1367 PrimOpKind::FfiStrlen => {
1368 let addr = unbox_addr(sess.pipeline, builder, args[0]);
1370 let result = emit_runtime_call(
1371 sess.pipeline,
1372 builder,
1373 "runtime_strlen",
1374 &[AbiParam::new(types::I64)],
1375 &[AbiParam::new(types::I64)],
1376 &[addr],
1377 )?;
1378 Ok(SsaVal::Raw(result, LIT_TAG_INT))
1379 }
1380 PrimOpKind::FfiTextMeasureOff => {
1381 let ba = unbox_bytearray(sess.pipeline, builder, args[0]);
1383 let data_ptr = builder.ins().iadd_imm(ba, 8); let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1385 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1386 let cnt = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1387 let result = emit_runtime_call(
1388 sess.pipeline,
1389 builder,
1390 "runtime_text_measure_off",
1391 &[
1392 AbiParam::new(types::I64),
1393 AbiParam::new(types::I64),
1394 AbiParam::new(types::I64),
1395 AbiParam::new(types::I64),
1396 ],
1397 &[AbiParam::new(types::I64)],
1398 &[data_ptr, off, len, cnt],
1399 )?;
1400 Ok(SsaVal::Raw(result, LIT_TAG_INT))
1401 }
1402 PrimOpKind::FfiTextMemchr => {
1403 let ba = unbox_bytearray(sess.pipeline, builder, args[0]);
1405 let data_ptr = builder.ins().iadd_imm(ba, 8); let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1407 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1408 let needle = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1409 let result = emit_runtime_call(
1410 sess.pipeline,
1411 builder,
1412 "runtime_text_memchr",
1413 &[
1414 AbiParam::new(types::I64),
1415 AbiParam::new(types::I64),
1416 AbiParam::new(types::I64),
1417 AbiParam::new(types::I64),
1418 ],
1419 &[AbiParam::new(types::I64)],
1420 &[data_ptr, off, len, needle],
1421 )?;
1422 Ok(SsaVal::Raw(result, LIT_TAG_INT))
1423 }
1424 PrimOpKind::FfiTextReverse => {
1425 let dest_ba = unbox_bytearray(sess.pipeline, builder, args[0]);
1427 let dest_ptr = builder.ins().iadd_imm(dest_ba, 8); let src_ba = unbox_bytearray(sess.pipeline, builder, args[1]);
1429 let src_ptr = builder.ins().iadd_imm(src_ba, 8); let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1431 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1432 let _ = emit_runtime_call(
1433 sess.pipeline,
1434 builder,
1435 "runtime_text_reverse",
1436 &[
1437 AbiParam::new(types::I64),
1438 AbiParam::new(types::I64),
1439 AbiParam::new(types::I64),
1440 AbiParam::new(types::I64),
1441 ],
1442 &[],
1443 &[dest_ptr, src_ptr, off, len],
1444 )?;
1445 Ok(SsaVal::Raw(
1446 builder.ins().iconst(types::I64, 0),
1447 LIT_TAG_INT,
1448 ))
1449 }
1450
1451 PrimOpKind::FloatAdd => {
1453 check_arity(op, 2, args.len())?;
1454 let a = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
1455 let b = unbox_float(sess.pipeline, builder, sess.vmctx, args[1]);
1456 Ok(SsaVal::Raw(builder.ins().fadd(a, b), LIT_TAG_FLOAT))
1457 }
1458 PrimOpKind::FloatSub => {
1459 check_arity(op, 2, args.len())?;
1460 let a = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
1461 let b = unbox_float(sess.pipeline, builder, sess.vmctx, args[1]);
1462 Ok(SsaVal::Raw(builder.ins().fsub(a, b), LIT_TAG_FLOAT))
1463 }
1464 PrimOpKind::FloatMul => {
1465 check_arity(op, 2, args.len())?;
1466 let a = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
1467 let b = unbox_float(sess.pipeline, builder, sess.vmctx, args[1]);
1468 Ok(SsaVal::Raw(builder.ins().fmul(a, b), LIT_TAG_FLOAT))
1469 }
1470 PrimOpKind::FloatDiv => {
1471 check_arity(op, 2, args.len())?;
1472 let a = unbox_float(sess.pipeline, builder, sess.vmctx, args[0]);
1473 let b = unbox_float(sess.pipeline, builder, sess.vmctx, args[1]);
1474 Ok(SsaVal::Raw(builder.ins().fdiv(a, b), LIT_TAG_FLOAT))
1475 }
1476 PrimOpKind::FloatEq => emit_f32_compare(
1477 sess.pipeline,
1478 builder,
1479 sess.vmctx,
1480 op,
1481 FloatCC::Equal,
1482 args,
1483 LIT_TAG_INT,
1484 ),
1485 PrimOpKind::FloatNe => emit_f32_compare(
1486 sess.pipeline,
1487 builder,
1488 sess.vmctx,
1489 op,
1490 FloatCC::NotEqual,
1491 args,
1492 LIT_TAG_INT,
1493 ),
1494 PrimOpKind::FloatLt => emit_f32_compare(
1495 sess.pipeline,
1496 builder,
1497 sess.vmctx,
1498 op,
1499 FloatCC::LessThan,
1500 args,
1501 LIT_TAG_INT,
1502 ),
1503 PrimOpKind::FloatLe => emit_f32_compare(
1504 sess.pipeline,
1505 builder,
1506 sess.vmctx,
1507 op,
1508 FloatCC::LessThanOrEqual,
1509 args,
1510 LIT_TAG_INT,
1511 ),
1512 PrimOpKind::FloatGt => emit_f32_compare(
1513 sess.pipeline,
1514 builder,
1515 sess.vmctx,
1516 op,
1517 FloatCC::GreaterThan,
1518 args,
1519 LIT_TAG_INT,
1520 ),
1521 PrimOpKind::FloatGe => emit_f32_compare(
1522 sess.pipeline,
1523 builder,
1524 sess.vmctx,
1525 op,
1526 FloatCC::GreaterThanOrEqual,
1527 args,
1528 LIT_TAG_INT,
1529 ),
1530
1531 PrimOpKind::TagToEnum | PrimOpKind::SeqOp => {
1532 Err(EmitError::NotYetImplemented(format!("{:?}", op)))
1533 }
1534
1535 PrimOpKind::NewSmallArray | PrimOpKind::NewArray => {
1541 let size = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1543 let init_ptr = args[1].value();
1544 let arr_ptr = emit_runtime_call(
1545 sess.pipeline,
1546 builder,
1547 "runtime_new_boxed_array",
1548 &[AbiParam::new(types::I64), AbiParam::new(types::I64)],
1549 &[AbiParam::new(types::I64)],
1550 &[size, init_ptr],
1551 )?;
1552 let lit_tag = if matches!(op, PrimOpKind::NewSmallArray) {
1553 LIT_TAG_SMALLARRAY
1554 } else {
1555 LIT_TAG_ARRAY
1556 };
1557 Ok(emit_lit_boxed_array(
1558 builder,
1559 sess.vmctx,
1560 sess.gc_sig,
1561 sess.oom_func,
1562 arr_ptr,
1563 lit_tag,
1564 ))
1565 }
1566
1567 PrimOpKind::ReadSmallArray
1568 | PrimOpKind::IndexSmallArray
1569 | PrimOpKind::ReadArray
1570 | PrimOpKind::IndexArray => {
1571 if args.len() < 2 {
1574 return Err(EmitError::NotYetImplemented(format!(
1575 "{:?}: expected >=2 args, got {} (args: {:?})",
1576 op,
1577 args.len(),
1578 args
1579 )));
1580 }
1581 let arr_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1582 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1583 let base = builder.ins().iadd_imm(arr_ptr, 8);
1584 let byte_offset = builder.ins().imul_imm(idx, 8);
1585 let effective = builder.ins().iadd(base, byte_offset);
1586 let loaded = builder
1587 .ins()
1588 .load(types::I64, MemFlags::new(), effective, 0);
1589 builder.declare_value_needs_stack_map(loaded);
1590 Ok(SsaVal::HeapPtr(loaded))
1591 }
1592
1593 PrimOpKind::WriteSmallArray | PrimOpKind::WriteArray => {
1594 let arr_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1596 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1597 let val = args[2].value();
1598 let base = builder.ins().iadd_imm(arr_ptr, 8);
1599 let byte_offset = builder.ins().imul_imm(idx, 8);
1600 let effective = builder.ins().iadd(base, byte_offset);
1601 builder.ins().store(MemFlags::new(), val, effective, 0);
1602 Ok(SsaVal::Raw(
1603 builder.ins().iconst(types::I64, 0),
1604 LIT_TAG_INT,
1605 ))
1606 }
1607
1608 PrimOpKind::SizeofSmallArray
1609 | PrimOpKind::SizeofSmallMutableArray
1610 | PrimOpKind::SizeofArray
1611 | PrimOpKind::SizeofMutableArray => {
1612 let arr_ptr = unbox_bytearray(sess.pipeline, builder, args[0]);
1614 let len = builder.ins().load(types::I64, MemFlags::new(), arr_ptr, 0);
1615 Ok(SsaVal::Raw(len, LIT_TAG_INT))
1616 }
1617
1618 PrimOpKind::UnsafeFreezeSmallArray
1619 | PrimOpKind::UnsafeThawSmallArray
1620 | PrimOpKind::UnsafeFreezeArray
1621 | PrimOpKind::UnsafeThawArray => {
1622 Ok(args[0])
1624 }
1625
1626 PrimOpKind::CopySmallArray
1627 | PrimOpKind::CopySmallMutableArray
1628 | PrimOpKind::CopyArray
1629 | PrimOpKind::CopyMutableArray => {
1630 let src = unbox_bytearray(sess.pipeline, builder, args[0]);
1632 let src_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1633 let dest = unbox_bytearray(sess.pipeline, builder, args[2]);
1634 let dest_off = unbox_int(sess.pipeline, builder, sess.vmctx, args[3]);
1635 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[4]);
1636 let _ = emit_runtime_call(
1637 sess.pipeline,
1638 builder,
1639 "runtime_copy_boxed_array",
1640 &[
1641 AbiParam::new(types::I64),
1642 AbiParam::new(types::I64),
1643 AbiParam::new(types::I64),
1644 AbiParam::new(types::I64),
1645 AbiParam::new(types::I64),
1646 ],
1647 &[],
1648 &[src, src_off, dest, dest_off, len],
1649 )?;
1650 Ok(SsaVal::Raw(
1651 builder.ins().iconst(types::I64, 0),
1652 LIT_TAG_INT,
1653 ))
1654 }
1655
1656 PrimOpKind::CloneSmallArray | PrimOpKind::CloneSmallMutableArray => {
1657 let arr = unbox_bytearray(sess.pipeline, builder, args[0]);
1659 let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1660 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1661 let result = emit_runtime_call(
1662 sess.pipeline,
1663 builder,
1664 "runtime_clone_boxed_array",
1665 &[
1666 AbiParam::new(types::I64),
1667 AbiParam::new(types::I64),
1668 AbiParam::new(types::I64),
1669 ],
1670 &[AbiParam::new(types::I64)],
1671 &[arr, off, len],
1672 )?;
1673 Ok(emit_lit_boxed_array(
1674 builder,
1675 sess.vmctx,
1676 sess.gc_sig,
1677 sess.oom_func,
1678 result,
1679 LIT_TAG_SMALLARRAY,
1680 ))
1681 }
1682
1683 PrimOpKind::CloneArray | PrimOpKind::CloneMutableArray => {
1684 let arr = unbox_bytearray(sess.pipeline, builder, args[0]);
1685 let off = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1686 let len = unbox_int(sess.pipeline, builder, sess.vmctx, args[2]);
1687 let result = emit_runtime_call(
1688 sess.pipeline,
1689 builder,
1690 "runtime_clone_boxed_array",
1691 &[
1692 AbiParam::new(types::I64),
1693 AbiParam::new(types::I64),
1694 AbiParam::new(types::I64),
1695 ],
1696 &[AbiParam::new(types::I64)],
1697 &[arr, off, len],
1698 )?;
1699 Ok(emit_lit_boxed_array(
1700 builder,
1701 sess.vmctx,
1702 sess.gc_sig,
1703 sess.oom_func,
1704 result,
1705 LIT_TAG_ARRAY,
1706 ))
1707 }
1708
1709 PrimOpKind::ShrinkSmallMutableArray => {
1710 let arr = unbox_bytearray(sess.pipeline, builder, args[0]);
1711 let new_len = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1712 let _ = emit_runtime_call(
1713 sess.pipeline,
1714 builder,
1715 "runtime_shrink_boxed_array",
1716 &[AbiParam::new(types::I64), AbiParam::new(types::I64)],
1717 &[],
1718 &[arr, new_len],
1719 )?;
1720 Ok(SsaVal::Raw(
1721 builder.ins().iconst(types::I64, 0),
1722 LIT_TAG_INT,
1723 ))
1724 }
1725
1726 PrimOpKind::CasSmallArray => {
1727 let arr = unbox_bytearray(sess.pipeline, builder, args[0]);
1732 let idx = unbox_int(sess.pipeline, builder, sess.vmctx, args[1]);
1733 let expected = args[2].value();
1734 let new_val = args[3].value();
1735 let old = emit_runtime_call(
1736 sess.pipeline,
1737 builder,
1738 "runtime_cas_boxed_array",
1739 &[
1740 AbiParam::new(types::I64),
1741 AbiParam::new(types::I64),
1742 AbiParam::new(types::I64),
1743 AbiParam::new(types::I64),
1744 ],
1745 &[AbiParam::new(types::I64)],
1746 &[arr, idx, expected, new_val],
1747 )?;
1748 builder.declare_value_needs_stack_map(old);
1750 Ok(SsaVal::HeapPtr(old))
1751 }
1752
1753 PrimOpKind::PopCnt | PrimOpKind::PopCnt64 => {
1757 check_arity(op, 1, args.len())?;
1758 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1759 Ok(SsaVal::Raw(builder.ins().popcnt(a), LIT_TAG_WORD))
1760 }
1761 PrimOpKind::PopCnt8 => {
1762 check_arity(op, 1, args.len())?;
1763 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1764 let masked = builder.ins().band_imm(a, 0xFF);
1765 Ok(SsaVal::Raw(builder.ins().popcnt(masked), LIT_TAG_WORD))
1766 }
1767 PrimOpKind::PopCnt16 => {
1768 check_arity(op, 1, args.len())?;
1769 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1770 let masked = builder.ins().band_imm(a, 0xFFFF);
1771 Ok(SsaVal::Raw(builder.ins().popcnt(masked), LIT_TAG_WORD))
1772 }
1773 PrimOpKind::PopCnt32 => {
1774 check_arity(op, 1, args.len())?;
1775 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1776 let masked = builder.ins().band_imm(a, 0xFFFF_FFFF);
1777 Ok(SsaVal::Raw(builder.ins().popcnt(masked), LIT_TAG_WORD))
1778 }
1779 PrimOpKind::Ctz | PrimOpKind::Ctz64 => {
1780 check_arity(op, 1, args.len())?;
1781 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1782 Ok(SsaVal::Raw(builder.ins().ctz(a), LIT_TAG_WORD))
1783 }
1784 PrimOpKind::Ctz8 => {
1785 check_arity(op, 1, args.len())?;
1786 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1787 let with_sentinel = builder.ins().bor_imm(a, 0x100);
1789 Ok(SsaVal::Raw(builder.ins().ctz(with_sentinel), LIT_TAG_WORD))
1790 }
1791 PrimOpKind::Ctz16 => {
1792 check_arity(op, 1, args.len())?;
1793 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1794 let with_sentinel = builder.ins().bor_imm(a, 0x10000);
1795 Ok(SsaVal::Raw(builder.ins().ctz(with_sentinel), LIT_TAG_WORD))
1796 }
1797 PrimOpKind::Ctz32 => {
1798 check_arity(op, 1, args.len())?;
1799 let a = unbox_int(sess.pipeline, builder, sess.vmctx, args[0]);
1800 let with_sentinel = builder.ins().bor_imm(a, 0x1_0000_0000);
1801 Ok(SsaVal::Raw(builder.ins().ctz(with_sentinel), LIT_TAG_WORD))
1802 }
1803 }
1804}
1805
1806fn check_arity(op: &PrimOpKind, expected: usize, got: usize) -> Result<(), EmitError> {
1807 if expected != got {
1808 Err(EmitError::InvalidArity(*op, expected, got))
1809 } else {
1810 Ok(())
1811 }
1812}
1813
1814fn emit_int_compare(
1815 pipeline: &mut CodegenPipeline,
1816 builder: &mut FunctionBuilder,
1817 vmctx: Value,
1818 op: &PrimOpKind,
1819 cc: IntCC,
1820 args: &[SsaVal],
1821 tag: i64,
1822) -> Result<SsaVal, EmitError> {
1823 check_arity(op, 2, args.len())?;
1824 let a = unbox_int(pipeline, builder, vmctx, args[0]);
1825 let b = unbox_int(pipeline, builder, vmctx, args[1]);
1826 let cmp = builder.ins().icmp(cc, a, b);
1827 Ok(SsaVal::Raw(builder.ins().uextend(types::I64, cmp), tag))
1828}
1829
1830fn emit_double_compare(
1831 pipeline: &mut CodegenPipeline,
1832 builder: &mut FunctionBuilder,
1833 vmctx: Value,
1834 op: &PrimOpKind,
1835 cc: FloatCC,
1836 args: &[SsaVal],
1837 tag: i64,
1838) -> Result<SsaVal, EmitError> {
1839 check_arity(op, 2, args.len())?;
1840 let a = unbox_double(pipeline, builder, vmctx, args[0]);
1841 let b = unbox_double(pipeline, builder, vmctx, args[1]);
1842 let cmp = builder.ins().fcmp(cc, a, b);
1843 Ok(SsaVal::Raw(builder.ins().uextend(types::I64, cmp), tag))
1844}
1845
1846fn emit_f32_compare(
1847 pipeline: &mut CodegenPipeline,
1848 builder: &mut FunctionBuilder,
1849 vmctx: Value,
1850 op: &PrimOpKind,
1851 cc: FloatCC,
1852 args: &[SsaVal],
1853 tag: i64,
1854) -> Result<SsaVal, EmitError> {
1855 check_arity(op, 2, args.len())?;
1856 let a = unbox_float(pipeline, builder, vmctx, args[0]);
1857 let b = unbox_float(pipeline, builder, vmctx, args[1]);
1858 let cmp = builder.ins().fcmp(cc, a, b);
1859 Ok(SsaVal::Raw(builder.ins().uextend(types::I64, cmp), tag))
1860}
1861
1862fn unbox_addr(
1864 _pipeline: &mut CodegenPipeline,
1865 builder: &mut FunctionBuilder,
1866 val: SsaVal,
1867) -> Value {
1868 match val {
1869 SsaVal::Raw(v, _) => v,
1870 SsaVal::HeapPtr(v) => {
1871 let start_block = builder.create_block();
1872 let next_block = builder.create_block();
1873 builder.append_block_param(start_block, types::I64);
1874 builder.append_block_param(next_block, types::I64);
1875
1876 builder.ins().jump(start_block, &[BlockArg::Value(v)]);
1877
1878 builder.switch_to_block(start_block);
1879 let curr_v = builder.block_params(start_block)[0];
1880 let tag = builder
1881 .ins()
1882 .load(types::I8, MemFlags::trusted(), curr_v, 0);
1883 let is_con = builder
1884 .ins()
1885 .icmp_imm(IntCC::Equal, tag, layout::TAG_CON as i64);
1886
1887 let con_block = builder.create_block();
1888 builder.ins().brif(
1889 is_con,
1890 con_block,
1891 &[],
1892 next_block,
1893 &[BlockArg::Value(curr_v)],
1894 );
1895
1896 builder.switch_to_block(con_block);
1897 builder.seal_block(con_block);
1898 let field0 = builder.ins().load(
1899 types::I64,
1900 MemFlags::trusted(),
1901 curr_v,
1902 layout::CON_FIELDS_OFFSET as i32,
1903 );
1904 builder.ins().jump(start_block, &[BlockArg::Value(field0)]);
1905
1906 builder.switch_to_block(next_block);
1907 builder.seal_block(start_block);
1908 builder.seal_block(next_block);
1909 let v_final = builder.block_params(next_block)[0];
1910
1911 let raw_val =
1912 builder
1913 .ins()
1914 .load(types::I64, MemFlags::trusted(), v_final, LIT_VALUE_OFFSET);
1915 let lit_tag =
1916 builder
1917 .ins()
1918 .load(types::I8, MemFlags::trusted(), v_final, LIT_TAG_OFFSET);
1919 let lit_tag_ext = builder.ins().uextend(types::I64, lit_tag);
1920
1921 let is_string = builder
1922 .ins()
1923 .icmp_imm(IntCC::Equal, lit_tag_ext, LIT_TAG_STRING);
1924 let is_ba = builder
1925 .ins()
1926 .icmp_imm(IntCC::Equal, lit_tag_ext, LIT_TAG_BYTEARRAY);
1927 let needs_adj = builder.ins().bor(is_string, is_ba);
1928 let adjusted = builder.ins().iadd_imm(raw_val, 8);
1929 builder.ins().select(needs_adj, adjusted, raw_val)
1930 }
1931 }
1932}
1933
1934fn unbox_bytearray(
1936 _pipeline: &mut CodegenPipeline,
1937 builder: &mut FunctionBuilder,
1938 val: SsaVal,
1939) -> Value {
1940 match val {
1941 SsaVal::Raw(v, _) => v,
1942 SsaVal::HeapPtr(v) => {
1943 let start_block = builder.create_block();
1944 let next_block = builder.create_block();
1945 builder.append_block_param(start_block, types::I64);
1946 builder.append_block_param(next_block, types::I64);
1947
1948 builder.ins().jump(start_block, &[BlockArg::Value(v)]);
1949
1950 builder.switch_to_block(start_block);
1951 let curr_v = builder.block_params(start_block)[0];
1952 let tag = builder
1953 .ins()
1954 .load(types::I8, MemFlags::trusted(), curr_v, 0);
1955 let is_con = builder
1956 .ins()
1957 .icmp_imm(IntCC::Equal, tag, layout::TAG_CON as i64);
1958
1959 let con_block = builder.create_block();
1960 builder.ins().brif(
1961 is_con,
1962 con_block,
1963 &[],
1964 next_block,
1965 &[BlockArg::Value(curr_v)],
1966 );
1967
1968 builder.switch_to_block(con_block);
1969 builder.seal_block(con_block);
1970 let field0 = builder.ins().load(
1971 types::I64,
1972 MemFlags::trusted(),
1973 curr_v,
1974 layout::CON_FIELDS_OFFSET as i32,
1975 );
1976 builder.ins().jump(start_block, &[BlockArg::Value(field0)]);
1977
1978 builder.switch_to_block(next_block);
1979 builder.seal_block(start_block);
1980 builder.seal_block(next_block);
1981 let v_final = builder.block_params(next_block)[0];
1982
1983 let raw_val =
1984 builder
1985 .ins()
1986 .load(types::I64, MemFlags::trusted(), v_final, LIT_VALUE_OFFSET);
1987 let lit_tag =
1988 builder
1989 .ins()
1990 .load(types::I8, MemFlags::trusted(), v_final, LIT_TAG_OFFSET);
1991 let lit_tag_ext = builder.ins().uextend(types::I64, lit_tag);
1992
1993 let is_string = builder
1995 .ins()
1996 .icmp_imm(IntCC::Equal, lit_tag_ext, LIT_TAG_STRING);
1997 let adjusted = builder.ins().iadd_imm(raw_val, 8);
1998 builder.ins().select(is_string, adjusted, raw_val)
1999 }
2000 }
2001}
2002
2003fn unbox_numeric(
2006 pipeline: &mut CodegenPipeline,
2007 builder: &mut FunctionBuilder,
2008 vmctx: Value,
2009 val: SsaVal,
2010 load_type: types::Type,
2011) -> Value {
2012 match val {
2013 SsaVal::Raw(v, _) => v,
2014 SsaVal::HeapPtr(v) => {
2015 let start_block = builder.create_block();
2016 let next_block = builder.create_block();
2017 builder.append_block_param(start_block, types::I64);
2018 builder.append_block_param(next_block, types::I64);
2019
2020 builder.ins().jump(start_block, &[BlockArg::Value(v)]);
2021
2022 builder.switch_to_block(start_block);
2023 let curr_v = builder.block_params(start_block)[0];
2024 let tag = builder
2025 .ins()
2026 .load(types::I8, MemFlags::trusted(), curr_v, 0);
2027 let is_con = builder
2028 .ins()
2029 .icmp_imm(IntCC::Equal, tag, layout::TAG_CON as i64);
2030
2031 let con_block = builder.create_block();
2032 let check_thunk_block = builder.create_block();
2033 builder
2034 .ins()
2035 .brif(is_con, con_block, &[], check_thunk_block, &[]);
2036
2037 builder.switch_to_block(con_block);
2038 builder.seal_block(con_block);
2039 let field0 = builder.ins().load(
2040 types::I64,
2041 MemFlags::trusted(),
2042 curr_v,
2043 layout::CON_FIELDS_OFFSET as i32,
2044 );
2045 builder.ins().jump(start_block, &[BlockArg::Value(field0)]);
2046
2047 builder.switch_to_block(check_thunk_block);
2049 builder.seal_block(check_thunk_block);
2050 let is_thunk = builder
2051 .ins()
2052 .icmp_imm(IntCC::Equal, tag, layout::TAG_THUNK as i64);
2053 let thunk_force_block = builder.create_block();
2054 builder.ins().brif(
2055 is_thunk,
2056 thunk_force_block,
2057 &[],
2058 next_block,
2059 &[BlockArg::Value(curr_v)],
2060 );
2061 builder.switch_to_block(thunk_force_block);
2062 builder.seal_block(thunk_force_block);
2063 let force_fn = pipeline
2064 .module
2065 .declare_function("heap_force", Linkage::Import, &{
2066 let mut sig = Signature::new(pipeline.isa.default_call_conv());
2067 sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); sig
2071 })
2072 .expect("declare heap_force");
2073 let force_ref = pipeline.module.declare_func_in_func(force_fn, builder.func);
2074 let inst = builder.ins().call(force_ref, &[vmctx, curr_v]);
2075 let forced = builder.inst_results(inst)[0];
2076 builder.ins().jump(start_block, &[BlockArg::Value(forced)]);
2077
2078 builder.switch_to_block(next_block);
2079 builder.seal_block(start_block);
2080 builder.seal_block(next_block);
2081 let v_final = builder.block_params(next_block)[0];
2082
2083 builder
2084 .ins()
2085 .load(load_type, MemFlags::trusted(), v_final, LIT_VALUE_OFFSET)
2086 }
2087 }
2088}
2089
2090pub fn unbox_int(
2091 pipeline: &mut CodegenPipeline,
2092 builder: &mut FunctionBuilder,
2093 vmctx: Value,
2094 val: SsaVal,
2095) -> Value {
2096 unbox_numeric(pipeline, builder, vmctx, val, types::I64)
2097}
2098
2099pub fn unbox_double(
2100 pipeline: &mut CodegenPipeline,
2101 builder: &mut FunctionBuilder,
2102 vmctx: Value,
2103 val: SsaVal,
2104) -> Value {
2105 unbox_numeric(pipeline, builder, vmctx, val, types::F64)
2106}
2107
2108pub fn unbox_float(
2109 pipeline: &mut CodegenPipeline,
2110 builder: &mut FunctionBuilder,
2111 vmctx: Value,
2112 val: SsaVal,
2113) -> Value {
2114 unbox_numeric(pipeline, builder, vmctx, val, types::F32)
2115}
2116
2117fn emit_lit_bytearray(
2119 builder: &mut FunctionBuilder,
2120 vmctx: Value,
2121 gc_sig: ir::SigRef,
2122 oom_func: ir::FuncRef,
2123 ba_ptr: Value,
2124) -> SsaVal {
2125 let ptr = emit_alloc_fast_path(builder, vmctx, LIT_TOTAL_SIZE, gc_sig, oom_func);
2126 let tag = builder.ins().iconst(types::I8, layout::TAG_LIT as i64);
2127 builder.ins().store(MemFlags::trusted(), tag, ptr, 0);
2128 let size = builder.ins().iconst(types::I16, LIT_TOTAL_SIZE as i64);
2129 builder.ins().store(MemFlags::trusted(), size, ptr, 1);
2130 let lit_tag = builder.ins().iconst(types::I8, LIT_TAG_BYTEARRAY);
2131 builder
2132 .ins()
2133 .store(MemFlags::trusted(), lit_tag, ptr, LIT_TAG_OFFSET);
2134 builder
2135 .ins()
2136 .store(MemFlags::trusted(), ba_ptr, ptr, LIT_VALUE_OFFSET);
2137 builder.declare_value_needs_stack_map(ptr);
2138 SsaVal::HeapPtr(ptr)
2139}
2140
2141fn emit_lit_boxed_array(
2143 builder: &mut FunctionBuilder,
2144 vmctx: Value,
2145 gc_sig: ir::SigRef,
2146 oom_func: ir::FuncRef,
2147 arr_ptr: Value,
2148 lit_tag: i64,
2149) -> SsaVal {
2150 let ptr = emit_alloc_fast_path(builder, vmctx, LIT_TOTAL_SIZE, gc_sig, oom_func);
2151 let tag = builder.ins().iconst(types::I8, layout::TAG_LIT as i64);
2152 builder.ins().store(MemFlags::trusted(), tag, ptr, 0);
2153 let size = builder.ins().iconst(types::I16, LIT_TOTAL_SIZE as i64);
2154 builder.ins().store(MemFlags::trusted(), size, ptr, 1);
2155 let lt = builder.ins().iconst(types::I8, lit_tag);
2156 builder
2157 .ins()
2158 .store(MemFlags::trusted(), lt, ptr, LIT_TAG_OFFSET);
2159 builder
2160 .ins()
2161 .store(MemFlags::trusted(), arr_ptr, ptr, LIT_VALUE_OFFSET);
2162 builder.declare_value_needs_stack_map(ptr);
2163 SsaVal::HeapPtr(ptr)
2164}
2165
2166fn emit_runtime_call(
2168 pipeline: &mut CodegenPipeline,
2169 builder: &mut FunctionBuilder,
2170 name: &str,
2171 params: &[AbiParam],
2172 returns: &[AbiParam],
2173 arg_vals: &[Value],
2174) -> Result<Value, EmitError> {
2175 let mut sig = Signature::new(pipeline.isa.default_call_conv());
2176 for p in params {
2177 sig.params.push(*p);
2178 }
2179 for r in returns {
2180 sig.returns.push(*r);
2181 }
2182 let func_id = pipeline
2183 .module
2184 .declare_function(name, Linkage::Import, &sig)
2185 .map_err(|e| EmitError::CraneliftError(e.to_string()))?;
2186 let func_ref = pipeline.module.declare_func_in_func(func_id, builder.func);
2187 let inst = builder.ins().call(func_ref, arg_vals);
2188 if returns.is_empty() {
2189 Ok(builder.ins().iconst(types::I64, 0))
2190 } else {
2191 Ok(builder.inst_results(inst)[0])
2192 }
2193}