1use cranelift::codegen::ir::BlockArg;
63use cranelift::prelude::*;
64use cranelift_module::FuncId;
65use qcode::{
66 context::Context,
67 space::MemorySpaceId,
68 value::{
69 BasicBlock, BlockId, ValueId, ValueRef,
70 insn::{
71 Binary, Binop, Carry, InstructionId, IntBinop, Load, Mnemonic, PopCount, Range,
72 SBorrow, SCarry, Sext, Store, Unary, Unop, Zext,
73 },
74 },
75};
76use qcode_vm::{PAGE_PERM_OFFSET, PAGE_SIZE, TLB_ENTRIES, TlbEntry, perm};
77use rustc_hash::{FxHashMap, FxHashSet};
78
79pub const BLOCK_OK: i64 = 0;
81pub const BLOCK_FAULT: i64 = 1;
84
85#[derive(Clone, Copy)]
87enum Access {
88 Load,
89 Store,
90}
91
92impl Access {
93 fn required(self) -> u8 {
100 match self {
101 Self::Load => perm::MAP | perm::READ,
102 Self::Store => perm::MAP | perm::WRITE,
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
112pub enum Unsupported {
113 Mnemonic(&'static str),
115 Width(usize),
117 Access(&'static str),
119 Terminator(&'static str),
121 Operand(&'static str),
123 Escapes(&'static str),
125}
126
127impl std::fmt::Display for Unsupported {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 match self {
130 Self::Mnemonic(what) => write!(f, "unsupported mnemonic `{what}`"),
131 Self::Width(size) => write!(f, "unsupported operand width {size}"),
132 Self::Access(what) => write!(f, "unsupported memory access: {what}"),
133 Self::Terminator(what) => write!(f, "unsupported terminator `{what}`"),
134 Self::Operand(what) => write!(f, "unsupported operand: {what}"),
135 Self::Escapes(what) => write!(f, "value escapes the block: {what}"),
136 }
137 }
138}
139
140#[derive(Clone, Copy, PartialEq, Eq)]
142enum Division {
143 Unsigned,
144 UnsignedRem,
145 Signed,
146 SignedRem,
147}
148
149impl Division {
150 fn is_signed(self) -> bool {
151 matches!(self, Self::Signed | Self::SignedRem)
152 }
153
154 fn helper(self) -> usize {
157 match self {
158 Self::Unsigned => 0,
159 Self::UnsignedRem => 1,
160 Self::Signed => 2,
161 Self::SignedRem => 3,
162 }
163 }
164}
165
166#[derive(Clone, Copy)]
168enum ShiftKind {
169 Logical,
170 Arithmetic,
171}
172
173pub(crate) fn int_type(size: usize) -> Result<Type, Unsupported> {
182 match size {
183 1 => Ok(types::I8),
184 2 => Ok(types::I16),
185 4 => Ok(types::I32),
186 8 => Ok(types::I64),
187 16 => Ok(types::I128),
188 other => Err(Unsupported::Width(other)),
189 }
190}
191
192#[derive(Debug, Default, Clone)]
199pub struct SpaceTable {
200 entries: Vec<(MemorySpaceId, usize)>,
201}
202
203impl SpaceTable {
204 fn slot(&mut self, space: MemorySpaceId, required: usize) -> usize {
207 if let Some(index) = self.entries.iter().position(|(id, _)| *id == space) {
208 self.entries[index].1 = self.entries[index].1.max(required);
209 return index;
210 }
211 self.entries.push((space, required));
212 self.entries.len() - 1
213 }
214
215 pub fn entries(&self) -> &[(MemorySpaceId, usize)] {
216 &self.entries
217 }
218
219 pub fn len(&self) -> usize {
220 self.entries.len()
221 }
222
223 pub fn is_empty(&self) -> bool {
224 self.entries.is_empty()
225 }
226}
227
228pub(crate) struct BlockTranslator<'a, 'ctx> {
230 ctx: &'ctx Context<'ctx>,
231 builder: FunctionBuilder<'a>,
232 spaces_arg: Value,
235 exports_arg: Value,
238 tlb_arg: Value,
240 memory_arg: Value,
242 helpers: HelperRefs,
245 fault_block: Option<cranelift::prelude::Block>,
248 load_slot: Option<codegen::ir::StackSlot>,
251 bases: FxHashMap<usize, Value>,
254 values: FxHashMap<InstructionId, Value>,
256 pub(crate) table: SpaceTable,
257 pub(crate) exports: Vec<Export>,
259}
260
261#[derive(Debug, Clone, Copy)]
264pub struct Helpers {
265 pub load: FuncId,
266 pub store: FuncId,
267 pub divisions: [FuncId; 4],
269}
270
271#[derive(Debug, Clone, Copy)]
273pub(crate) struct HelperRefs {
274 pub(crate) load: codegen::ir::FuncRef,
275 pub(crate) store: codegen::ir::FuncRef,
276 pub(crate) divisions: [codegen::ir::FuncRef; 4],
277}
278
279#[derive(Debug, Clone, Copy)]
281pub struct Export {
282 pub insn: InstructionId,
285 pub size: usize,
287}
288
289impl<'a, 'ctx> BlockTranslator<'a, 'ctx> {
290 pub(crate) fn new(
291 ctx: &'ctx Context<'ctx>,
292 builder: FunctionBuilder<'a>,
293 entry: cranelift::prelude::Block,
294 helpers: HelperRefs,
295 ) -> Self {
296 let spaces_arg = builder.block_params(entry)[0];
297 let exports_arg = builder.block_params(entry)[1];
298 let tlb_arg = builder.block_params(entry)[2];
299 let memory_arg = builder.block_params(entry)[3];
300 Self {
301 ctx,
302 builder,
303 spaces_arg,
304 exports_arg,
305 tlb_arg,
306 memory_arg,
307 helpers,
308 fault_block: None,
309 load_slot: None,
310 bases: FxHashMap::default(),
311 values: FxHashMap::default(),
312 table: SpaceTable::default(),
313 exports: Vec::new(),
314 }
315 }
316
317 fn base(&mut self, slot: usize) -> Value {
319 if let Some(base) = self.bases.get(&slot) {
320 return *base;
321 }
322 let offset = (slot * std::mem::size_of::<*mut u8>()) as i32;
323 let base =
324 self.builder
325 .ins()
326 .load(types::I64, MemFlags::trusted(), self.spaces_arg, offset);
327 self.bases.insert(slot, base);
328 base
329 }
330
331 fn is_flat(&self, space: MemorySpaceId) -> bool {
340 space != MemorySpaceId::Shared(self.ctx.shared.default_space)
341 }
342
343 fn constant_address(&self, ptr: ValueId) -> Option<u64> {
345 match ValueRef::new(ptr, self.ctx) {
346 ValueRef::Literal(literal) => Some(literal.value()),
347 ValueRef::Temp(temp) => Some(temp.address() as u64),
348 ValueRef::Varnode(varnode) => Some(varnode.address() as u64),
349 _ => None,
350 }
351 }
352
353 fn operand(&mut self, id: ValueId, size: usize) -> Result<Value, Unsupported> {
355 let ty = int_type(size)?;
356 match id {
357 ValueId::Literal(_) => {
358 let ValueRef::Literal(literal) = ValueRef::new(id, self.ctx) else {
359 return Err(Unsupported::Operand("literal did not resolve"));
360 };
361 Ok(self.constant(ty, u128::from(literal.value())))
364 }
365 ValueId::Instruction(insn) => self
366 .values
367 .get(&insn)
368 .copied()
369 .ok_or(Unsupported::Operand("value produced outside this block")),
370 _ => Err(Unsupported::Operand("not a literal or in-block value")),
373 }
374 }
375
376 fn width_of(&self, id: ValueId) -> Result<usize, Unsupported> {
378 let ty = self
379 .ctx
380 .stored_type_of(id)
381 .ok_or(Unsupported::Operand("operand has no type"))?;
382 Ok(self.ctx.shared.types.size_of(ty))
383 }
384
385 pub(crate) fn translate_body(&mut self, block: BlockId) -> Result<(), Unsupported> {
388 let insns: Vec<InstructionId> = BasicBlock::from_id(self.ctx, block).instruction_ids();
389 let Some((&terminator, body)) = insns.split_last() else {
390 return Err(Unsupported::Terminator("block is empty"));
391 };
392 let own: FxHashSet<InstructionId> = insns.iter().copied().collect();
393
394 for &insn_id in body {
395 self.translate_one(insn_id)?;
396 self.check_confined(insn_id, &own)?;
397 }
398
399 self.export_terminator_operands(terminator, &own)
400 }
401
402 fn check_confined(
410 &self,
411 insn_id: InstructionId,
412 own: &FxHashSet<InstructionId>,
413 ) -> Result<(), Unsupported> {
414 let value = ValueId::Instruction(insn_id);
415 if self
416 .ctx
417 .users_of(value)
418 .iter()
419 .any(|user| !own.contains(user))
420 {
421 return Err(Unsupported::Escapes("result used from another block"));
422 }
423 Ok(())
424 }
425
426 fn export_terminator_operands(
434 &mut self,
435 terminator: InstructionId,
436 own: &FxHashSet<InstructionId>,
437 ) -> Result<(), Unsupported> {
438 let insn = qcode::value::Instruction::from_id(self.ctx, terminator);
439 let operands: Vec<ValueId> = insn
440 .mnemonic()
441 .args()
442 .into_iter()
443 .map(|arg| arg.qualify(terminator.func))
444 .collect();
445
446 for operand in operands {
447 let ValueId::Instruction(def) = operand else {
448 continue;
449 };
450 if !own.contains(&def) {
451 continue;
452 }
453 if self.exports.iter().any(|export| export.insn == def) {
454 continue;
455 }
456 let value = *self
457 .values
458 .get(&def)
459 .ok_or(Unsupported::Terminator("operand was not compiled"))?;
460 let size = self.width_of(operand)?;
461 if size > std::mem::size_of::<u64>() {
465 return Err(Unsupported::Terminator("operand wider than an export slot"));
466 }
467 let slot = self.exports.len();
468 let widened = self.widen_to_u64(value);
469 self.builder.ins().store(
470 MemFlags::trusted(),
471 widened,
472 self.exports_arg,
473 (slot * std::mem::size_of::<u64>()) as i32,
474 );
475 self.exports.push(Export { insn: def, size });
476 }
477 Ok(())
478 }
479
480 fn widen_to_u64(&mut self, value: Value) -> Value {
482 if self.builder.func.dfg.value_type(value) == types::I64 {
483 value
484 } else {
485 self.builder.ins().uextend(types::I64, value)
486 }
487 }
488
489 fn translate_one(&mut self, insn_id: InstructionId) -> Result<(), Unsupported> {
490 let insn = qcode::value::Instruction::from_id(self.ctx, insn_id);
491 let func = insn_id.func;
492 let result = match insn.mnemonic() {
493 &Mnemonic::Load(Load { space, ptr, size }) => {
494 let space = space.qualify(func);
495 if !self.is_flat(space) {
496 let addr = self.guest_address(ptr.qualify(func))?;
497 let value = self.ram_load(addr, size)?;
498 return self.record(insn_id, Some(value));
499 }
500 let addr = self
501 .constant_address(ptr.qualify(func))
502 .ok_or(Unsupported::Access("non-constant address"))?;
503 let ty = int_type(size)?;
504 let slot = self.table.slot(space, addr as usize + size);
505 let base = self.base(slot);
506 Some(self.builder.ins().load(
507 ty,
508 MemFlags::trusted(),
509 base,
510 i32::try_from(addr).map_err(|_| Unsupported::Access("address too large"))?,
511 ))
512 }
513
514 &Mnemonic::Store(Store {
515 space,
516 ptr,
517 size,
518 src,
519 }) => {
520 let space = space.qualify(func);
521 if !self.is_flat(space) {
522 let addr = self.guest_address(ptr.qualify(func))?;
523 let value = self.operand(src.qualify(func), size)?;
524 self.ram_store(addr, value, size)?;
525 return Ok(());
526 }
527 let addr = self
528 .constant_address(ptr.qualify(func))
529 .ok_or(Unsupported::Access("non-constant address"))?;
530 int_type(size)?;
531 let value = self.operand(src.qualify(func), size)?;
532 let slot = self.table.slot(space, addr as usize + size);
533 let base = self.base(slot);
534 self.builder.ins().store(
535 MemFlags::trusted(),
536 value,
537 base,
538 i32::try_from(addr).map_err(|_| Unsupported::Access("address too large"))?,
539 );
540 None
541 }
542
543 Mnemonic::Binop(Binary { op, lhs, rhs }) => {
544 let lhs_id = lhs.qualify(func);
545 let rhs_id = rhs.qualify(func);
546 let width = self.width_of(lhs_id)?;
547 if self.width_of(rhs_id)? != width {
548 return Err(Unsupported::Operand("mismatched operand widths"));
549 }
550 let a = self.operand(lhs_id, width)?;
551 let b = self.operand(rhs_id, width)?;
552 Some(self.binop(*op, a, b)?)
553 }
554
555 Mnemonic::Unop(Unary { op, src }) => {
556 let src_id = src.qualify(func);
557 let width = self.width_of(src_id)?;
558 let value = self.operand(src_id, width)?;
559 match op {
560 Unop::IntNot => Some(self.builder.ins().bnot(value)),
561 Unop::IntNegate => Some(self.builder.ins().ineg(value)),
562 _ => return Err(Unsupported::Mnemonic("float unop")),
563 }
564 }
565
566 &Mnemonic::Zext(Zext { src, size }) => {
567 let src_id = src.qualify(func);
568 let from = self.width_of(src_id)?;
569 let value = self.operand(src_id, from)?;
570 let ty = int_type(size)?;
571 Some(match from.cmp(&size) {
572 std::cmp::Ordering::Less => self.builder.ins().uextend(ty, value),
573 std::cmp::Ordering::Equal => value,
574 std::cmp::Ordering::Greater => self.builder.ins().ireduce(ty, value),
575 })
576 }
577
578 &Mnemonic::Sext(Sext { src, size }) => {
579 let src_id = src.qualify(func);
580 let from = self.width_of(src_id)?;
581 let value = self.operand(src_id, from)?;
582 let ty = int_type(size)?;
583 Some(match from.cmp(&size) {
584 std::cmp::Ordering::Less => self.builder.ins().sextend(ty, value),
585 std::cmp::Ordering::Equal => value,
586 std::cmp::Ordering::Greater => self.builder.ins().ireduce(ty, value),
587 })
588 }
589
590 &Mnemonic::Range(Range { src, start, size }) => {
591 let src_id = src.qualify(func);
592 let from = self.width_of(src_id)?;
593 let value = self.operand(src_id, from)?;
594 let from_ty = int_type(from)?;
595 let ty = int_type(size)?;
596 let shifted = if start == 0 {
597 value
598 } else {
599 let amount = self.constant(from_ty, (start * 8) as u128);
600 self.builder.ins().ushr(value, amount)
601 };
602 Some(if from == size {
603 shifted
604 } else {
605 self.builder.ins().ireduce(ty, shifted)
606 })
607 }
608
609 &Mnemonic::PopCount(PopCount { src }) => {
613 let src_id = src.qualify(func);
614 let from = self.width_of(src_id)?;
615 if from > std::mem::size_of::<u64>() {
618 return Err(Unsupported::Width(from));
619 }
620 let value = self.operand(src_id, from)?;
621 let counted = self.builder.ins().popcnt(value);
622 let out = self.width_of(ValueId::Instruction(insn_id))?;
623 Some(self.resize(counted, from, out)?)
624 }
625
626 &Mnemonic::Carry(Carry { lhs, rhs }) => {
627 let (a, b) = self.pair(lhs.qualify(func), rhs.qualify(func))?;
628 let sum = self.builder.ins().iadd(a, b);
630 Some(self.builder.ins().icmp(IntCC::UnsignedLessThan, sum, a))
631 }
632
633 &Mnemonic::SCarry(SCarry { lhs, rhs }) => {
634 let (a, b) = self.pair(lhs.qualify(func), rhs.qualify(func))?;
635 let sum = self.builder.ins().iadd(a, b);
638 let a_differs = self.builder.ins().bxor(a, sum);
639 let b_differs = self.builder.ins().bxor(b, sum);
640 let both = self.builder.ins().band(a_differs, b_differs);
641 let ty = self.builder.func.dfg.value_type(both);
642 let zero = self.constant(ty, 0);
643 Some(self.builder.ins().icmp(IntCC::SignedLessThan, both, zero))
644 }
645
646 &Mnemonic::SBorrow(SBorrow { lhs, rhs }) => {
647 let (a, b) = self.pair(lhs.qualify(func), rhs.qualify(func))?;
648 let diff = self.builder.ins().isub(a, b);
651 let operands_differ = self.builder.ins().bxor(a, b);
652 let result_differs = self.builder.ins().bxor(a, diff);
653 let both = self.builder.ins().band(operands_differ, result_differs);
654 let ty = self.builder.func.dfg.value_type(both);
655 let zero = self.constant(ty, 0);
656 Some(self.builder.ins().icmp(IntCC::SignedLessThan, both, zero))
657 }
658
659 Mnemonic::PCodeOp(op) => {
665 let name = &self.ctx.shared.pcode_ops[op.id];
666 match (name.as_ref(), op.args.as_slice()) {
667 ("undef", []) => {
668 let out = self.width_of(ValueId::Instruction(insn_id))?;
669 let ty = int_type(out)?;
670 Some(self.constant(ty, 0))
671 }
672 ("LOCK" | "UNLOCK", []) => None,
673 _ => return Err(Unsupported::Mnemonic("user p-code op")),
674 }
675 }
676
677 other => return Err(Unsupported::Mnemonic(other.opcode())),
678 };
679
680 if let Some(value) = result {
681 self.values.insert(insn_id, value);
682 }
683 Ok(())
684 }
685
686 fn guest_flags() -> MemFlags {
697 MemFlags::new().with_notrap()
700 }
701
702 fn constant(&mut self, ty: Type, value: u128) -> Value {
708 if ty == types::I128 {
709 let low = self.builder.ins().iconst(types::I64, value as u64 as i64);
710 let high = self
711 .builder
712 .ins()
713 .iconst(types::I64, (value >> 64) as u64 as i64);
714 self.builder.ins().iconcat(low, high)
715 } else {
716 self.builder.ins().iconst(ty, value as u64 as i64)
717 }
718 }
719
720 fn splat(byte: u8, ty: Type) -> i64 {
722 let mut bits = [0u8; 8];
723 for slot in bits.iter_mut().take(ty.bytes() as usize) {
724 *slot = byte;
725 }
726 i64::from_le_bytes(bits)
727 }
728
729 fn fault_block(&mut self) -> cranelift::prelude::Block {
736 if let Some(block) = self.fault_block {
737 return block;
738 }
739 let block = self.builder.create_block();
740 self.builder.set_cold_block(block);
741 self.fault_block = Some(block);
742 block
743 }
744
745 fn bail_if(&mut self, cond: Value, target: cranelift::prelude::Block) {
748 let carry_on = self.builder.create_block();
749 self.builder.ins().brif(cond, target, &[], carry_on, &[]);
750 self.builder.switch_to_block(carry_on);
751 }
752
753 fn bail_unless(&mut self, cond: Value, target: cranelift::prelude::Block) {
755 let carry_on = self.builder.create_block();
756 self.builder.ins().brif(cond, carry_on, &[], target, &[]);
757 self.builder.switch_to_block(carry_on);
758 }
759
760 fn guest_address(&mut self, ptr: ValueId) -> Result<Value, Unsupported> {
767 let width = self.width_of(ptr)?;
768 if width > 8 {
769 return Err(Unsupported::Access("address wider than 64 bits"));
770 }
771 let value = self.operand(ptr, width)?;
772 Ok(match self.builder.func.dfg.value_type(value) {
777 types::I64 => value,
778 ty if ty.is_int() => self.builder.ins().uextend(types::I64, value),
779 _ => return Err(Unsupported::Access("address is not an integer")),
780 })
781 }
782
783 fn checked_width(&self, value: Value, size: usize) -> Result<(), Unsupported> {
786 if self.builder.func.dfg.value_type(value) == int_type(size)? {
787 Ok(())
788 } else {
789 Err(Unsupported::Operand("value is not its declared width"))
790 }
791 }
792
793 fn inline_access(
800 &mut self,
801 addr: Value,
802 size: usize,
803 kind: Access,
804 fallback: cranelift::prelude::Block,
805 ) -> Result<(Value, Value), Unsupported> {
806 let ty = int_type(size)?;
807 let page_mask = (PAGE_SIZE - 1) as i64;
808
809 if size > 1 {
812 let offset = self.builder.ins().band_imm(addr, page_mask);
813 let last = self.builder.ins().iadd_imm(offset, size as i64 - 1);
814 let spills = self.builder.ins().band_imm(last, !page_mask);
815 self.bail_if(spills, fallback);
816 }
817
818 let entry_size = std::mem::size_of::<TlbEntry>() as i64;
822 debug_assert!(entry_size.count_ones() == 1);
823 let entry_bits = entry_size.trailing_zeros() as i64;
824 let index_shift = PAGE_SIZE.trailing_zeros() as i64 - entry_bits;
825 let shifted = self.builder.ins().ushr_imm(addr, index_shift);
826 let offset = self
827 .builder
828 .ins()
829 .band_imm(shifted, (TLB_ENTRIES as i64 - 1) << entry_bits);
830 let entry = self.builder.ins().iadd(self.tlb_arg, offset);
831
832 let flags = MemFlags::trusted();
837 let cached = self.builder.ins().load(types::I64, flags, entry, 0);
838 let tag = self.builder.ins().band_imm(addr, !page_mask);
839 let hit = self.builder.ins().icmp(IntCC::Equal, tag, cached);
840 self.bail_unless(hit, fallback);
841
842 let delta = self.builder.ins().load(types::I64, flags, entry, 8);
843 let host = self.builder.ins().iadd(addr, delta);
844
845 let held = self
849 .builder
850 .ins()
851 .load(ty, Self::guest_flags(), host, PAGE_PERM_OFFSET as i32);
852 let required = self
853 .builder
854 .ins()
855 .iconst(ty, Self::splat(kind.required(), ty));
856 let missing = self.builder.ins().band_not(required, held);
857 self.bail_if(missing, fallback);
858
859 Ok((host, held))
860 }
861
862 fn narrow_enough_for_ram(&self, size: usize) -> Result<(), Unsupported> {
869 if size > std::mem::size_of::<u64>() {
870 return Err(Unsupported::Access("guest RAM access wider than 8 bytes"));
871 }
872 Ok(())
873 }
874
875 fn load_slot(&mut self) -> codegen::ir::StackSlot {
877 if let Some(slot) = self.load_slot {
878 return slot;
879 }
880 let slot = self.builder.create_sized_stack_slot(StackSlotData::new(
881 StackSlotKind::ExplicitSlot,
882 8,
883 3,
884 ));
885 self.load_slot = Some(slot);
886 slot
887 }
888
889 fn ram_load(&mut self, addr: Value, size: usize) -> Result<Value, Unsupported> {
891 let ty = int_type(size)?;
892 self.narrow_enough_for_ram(size)?;
893 let done = self.builder.create_block();
894 self.builder.append_block_param(done, ty);
895 let fallback = self.builder.create_block();
896 self.builder.set_cold_block(fallback);
897
898 let (host, _) = self.inline_access(addr, size, Access::Load, fallback)?;
899 let value = self.builder.ins().load(ty, Self::guest_flags(), host, 0);
900 self.builder.ins().jump(done, &[BlockArg::from(value)]);
901
902 self.builder.switch_to_block(fallback);
903 let slot = self.load_slot();
904 let out = self.builder.ins().stack_addr(types::I64, slot, 0);
905 let width = self.builder.ins().iconst(types::I32, size as i64);
906 let call = self
907 .builder
908 .ins()
909 .call(self.helpers.load, &[self.memory_arg, addr, width, out]);
910 let status = self.builder.inst_results(call)[0];
911 let faulted = self.fault_block();
912 self.bail_if(status, faulted);
913 let wide = self.builder.ins().stack_load(types::I64, slot, 0);
914 let narrowed = if ty == types::I64 {
915 wide
916 } else {
917 self.builder.ins().ireduce(ty, wide)
918 };
919 self.builder.ins().jump(done, &[BlockArg::from(narrowed)]);
920
921 self.builder.switch_to_block(done);
922 Ok(self.builder.block_params(done)[0])
923 }
924
925 fn ram_store(&mut self, addr: Value, value: Value, size: usize) -> Result<(), Unsupported> {
927 let ty = int_type(size)?;
928 self.narrow_enough_for_ram(size)?;
929 self.checked_width(value, size)?;
930 let done = self.builder.create_block();
931 let fallback = self.builder.create_block();
932 self.builder.set_cold_block(fallback);
933
934 let (host, held) = self.inline_access(addr, size, Access::Store, fallback)?;
935 self.builder
936 .ins()
937 .store(Self::guest_flags(), value, host, 0);
938 let init = self
943 .builder
944 .ins()
945 .bor_imm(held, Self::splat(perm::INIT, ty));
946 self.builder
947 .ins()
948 .store(Self::guest_flags(), init, host, PAGE_PERM_OFFSET as i32);
949 self.builder.ins().jump(done, &[]);
950
951 self.builder.switch_to_block(fallback);
952 let width = self.builder.ins().iconst(types::I32, size as i64);
953 let wide = self.widen_to_u64(value);
954 let call = self
955 .builder
956 .ins()
957 .call(self.helpers.store, &[self.memory_arg, addr, width, wide]);
958 let status = self.builder.inst_results(call)[0];
959 let faulted = self.fault_block();
960 self.bail_if(status, faulted);
961 self.builder.ins().jump(done, &[]);
962
963 self.builder.switch_to_block(done);
964 Ok(())
965 }
966
967 fn record(&mut self, insn_id: InstructionId, result: Option<Value>) -> Result<(), Unsupported> {
969 if let Some(value) = result {
970 self.values.insert(insn_id, value);
971 }
972 Ok(())
973 }
974
975 fn pair(&mut self, lhs: ValueId, rhs: ValueId) -> Result<(Value, Value), Unsupported> {
977 let width = self.width_of(lhs)?;
978 if self.width_of(rhs)? != width {
979 return Err(Unsupported::Operand("mismatched operand widths"));
980 }
981 Ok((self.operand(lhs, width)?, self.operand(rhs, width)?))
982 }
983
984 fn resize(&mut self, value: Value, from: usize, to: usize) -> Result<Value, Unsupported> {
986 let ty = int_type(to)?;
987 int_type(from)?;
988 Ok(match from.cmp(&to) {
989 std::cmp::Ordering::Less => self.builder.ins().uextend(ty, value),
990 std::cmp::Ordering::Equal => value,
991 std::cmp::Ordering::Greater => self.builder.ins().ireduce(ty, value),
992 })
993 }
994
995 fn guard_shift(&mut self, shifted: Value, a: Value, amount: Value, kind: ShiftKind) -> Value {
997 let ty = self.builder.func.dfg.value_type(a);
998 let bits = u128::from(ty.bits());
999 let width = self.constant(ty, bits);
1002 let in_range = self
1003 .builder
1004 .ins()
1005 .icmp(IntCC::UnsignedLessThan, amount, width);
1006 let saturated = match kind {
1007 ShiftKind::Logical => self.constant(ty, 0),
1008 ShiftKind::Arithmetic => {
1010 let all = self.constant(ty, bits - 1);
1011 self.builder.ins().sshr(a, all)
1012 }
1013 };
1014 self.builder.ins().select(in_range, shifted, saturated)
1015 }
1016
1017 fn divide(&mut self, a: Value, b: Value, kind: Division) -> Result<Value, Unsupported> {
1032 let ty = self.builder.func.dfg.value_type(a);
1033 if ty == types::I128 {
1034 return Ok(self.divide_wide(a, b, kind));
1035 }
1036 let zero = self.constant(ty, 0);
1037 let one = self.constant(ty, 1);
1038 let by_zero = self.builder.ins().icmp(IntCC::Equal, b, zero);
1039
1040 let mut avoid = by_zero;
1041 if kind.is_signed() {
1042 let most_negative = self.constant(ty, 1u128 << (ty.bits() - 1));
1043 let minus_one = self.constant(ty, u128::MAX);
1044 let a_is_min = self.builder.ins().icmp(IntCC::Equal, a, most_negative);
1045 let b_is_minus_one = self.builder.ins().icmp(IntCC::Equal, b, minus_one);
1046 let overflows = self.builder.ins().band(a_is_min, b_is_minus_one);
1047 avoid = self.builder.ins().bor(by_zero, overflows);
1048 }
1049
1050 let divisor = self.builder.ins().select(avoid, one, b);
1051 let result = match kind {
1052 Division::Unsigned => self.builder.ins().udiv(a, divisor),
1053 Division::UnsignedRem => self.builder.ins().urem(a, divisor),
1054 Division::Signed => self.builder.ins().sdiv(a, divisor),
1055 Division::SignedRem => self.builder.ins().srem(a, divisor),
1056 };
1057 Ok(self.builder.ins().select(by_zero, zero, result))
1058 }
1059
1060 fn divide_wide(&mut self, a: Value, b: Value, kind: Division) -> Value {
1067 let slot = self.builder.create_sized_stack_slot(StackSlotData::new(
1068 StackSlotKind::ExplicitSlot,
1069 16,
1070 4,
1071 ));
1072 let out = self.builder.ins().stack_addr(types::I64, slot, 0);
1073 let (a_low, a_high) = self.builder.ins().isplit(a);
1078 let (b_low, b_high) = self.builder.ins().isplit(b);
1079 let helper = self.helpers.divisions[kind.helper()];
1080 self.builder
1081 .ins()
1082 .call(helper, &[a_low, a_high, b_low, b_high, out]);
1083 let low = self.builder.ins().stack_load(types::I64, slot, 0);
1084 let high = self.builder.ins().stack_load(types::I64, slot, 8);
1085 self.builder.ins().iconcat(low, high)
1086 }
1087
1088 fn binop(&mut self, op: Binop, a: Value, b: Value) -> Result<Value, Unsupported> {
1089 let ins = self.builder.ins();
1090 Ok(match op {
1091 Binop::Int(int) => match int {
1092 IntBinop::Add => ins.iadd(a, b),
1093 IntBinop::Sub => ins.isub(a, b),
1094 IntBinop::And => ins.band(a, b),
1095 IntBinop::Or => ins.bor(a, b),
1096 IntBinop::Xor => ins.bxor(a, b),
1097 IntBinop::Mul => ins.imul(a, b),
1098 IntBinop::ShiftLeft => {
1105 let shifted = ins.ishl(a, b);
1106 return Ok(self.guard_shift(shifted, a, b, ShiftKind::Logical));
1107 }
1108 IntBinop::ShiftRight => {
1109 let shifted = ins.ushr(a, b);
1110 return Ok(self.guard_shift(shifted, a, b, ShiftKind::Logical));
1111 }
1112 IntBinop::SShiftRight => {
1113 let shifted = ins.sshr(a, b);
1114 return Ok(self.guard_shift(shifted, a, b, ShiftKind::Arithmetic));
1115 }
1116 IntBinop::Equal => ins.icmp(IntCC::Equal, a, b),
1117 IntBinop::NotEqual => ins.icmp(IntCC::NotEqual, a, b),
1118 IntBinop::Less => ins.icmp(IntCC::UnsignedLessThan, a, b),
1119 IntBinop::LessEqual => ins.icmp(IntCC::UnsignedLessThanOrEqual, a, b),
1120 IntBinop::SLess => ins.icmp(IntCC::SignedLessThan, a, b),
1121 IntBinop::SLessEqual => ins.icmp(IntCC::SignedLessThanOrEqual, a, b),
1122 IntBinop::Div => return self.divide(a, b, Division::Unsigned),
1128 IntBinop::Rem => return self.divide(a, b, Division::UnsignedRem),
1129 IntBinop::Sdiv => return self.divide(a, b, Division::Signed),
1130 IntBinop::Srem => return self.divide(a, b, Division::SignedRem),
1131 _ => return Err(Unsupported::Mnemonic("integer binop")),
1132 },
1133 Binop::Float(_) => return Err(Unsupported::Mnemonic("float binop")),
1134 _ => return Err(Unsupported::Mnemonic("binop")),
1135 })
1136 }
1137
1138 pub(crate) fn finish(mut self) {
1139 let ok = self.builder.ins().iconst(types::I32, BLOCK_OK);
1140 self.builder.ins().return_(&[ok]);
1141 if let Some(block) = self.fault_block {
1144 self.builder.switch_to_block(block);
1145 let status = self.builder.ins().iconst(types::I32, BLOCK_FAULT);
1146 self.builder.ins().return_(&[status]);
1147 }
1148 self.builder.seal_all_blocks();
1152 self.builder.finalize();
1153 }
1154}