| //! Defines [`Pointer`] which is used to improve the quality of the generated clif ir for pointer |
| //! operations. |
| |
| use crate::prelude::*; |
| |
| use rustc_target::abi::Align; |
| |
| use cranelift_codegen::ir::immediates::Offset32; |
| |
| /// A pointer pointing either to a certain address, a certain stack slot or nothing. |
| #[derive(Copy, Clone, Debug)] |
| pub(crate) struct Pointer { |
| base: PointerBase, |
| offset: Offset32, |
| } |
| |
| #[derive(Copy, Clone, Debug)] |
| pub(crate) enum PointerBase { |
| Addr(Value), |
| Stack(StackSlot), |
| Dangling(Align), |
| } |
| |
| impl Pointer { |
| pub(crate) fn new(addr: Value) -> Self { |
| Pointer { |
| base: PointerBase::Addr(addr), |
| offset: Offset32::new(0), |
| } |
| } |
| |
| pub(crate) fn stack_slot(stack_slot: StackSlot) -> Self { |
| Pointer { |
| base: PointerBase::Stack(stack_slot), |
| offset: Offset32::new(0), |
| } |
| } |
| |
| pub(crate) fn const_addr<'a, 'tcx>( |
| fx: &mut FunctionCx<'a, 'tcx, impl Module>, |
| addr: i64, |
| ) -> Self { |
| let addr = fx.bcx.ins().iconst(fx.pointer_type, addr); |
| Pointer { |
| base: PointerBase::Addr(addr), |
| offset: Offset32::new(0), |
| } |
| } |
| |
| pub(crate) fn dangling(align: Align) -> Self { |
| Pointer { |
| base: PointerBase::Dangling(align), |
| offset: Offset32::new(0), |
| } |
| } |
| |
| #[cfg(debug_assertions)] |
| pub(crate) fn base_and_offset(self) -> (PointerBase, Offset32) { |
| (self.base, self.offset) |
| } |
| |
| pub(crate) fn get_addr<'a, 'tcx>(self, fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> Value { |
| match self.base { |
| PointerBase::Addr(base_addr) => { |
| let offset: i64 = self.offset.into(); |
| if offset == 0 { |
| base_addr |
| } else { |
| fx.bcx.ins().iadd_imm(base_addr, offset) |
| } |
| } |
| PointerBase::Stack(stack_slot) => { |
| fx.bcx |
| .ins() |
| .stack_addr(fx.pointer_type, stack_slot, self.offset) |
| } |
| PointerBase::Dangling(align) => fx |
| .bcx |
| .ins() |
| .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()), |
| } |
| } |
| |
| pub(crate) fn offset<'a, 'tcx>( |
| self, |
| fx: &mut FunctionCx<'a, 'tcx, impl Module>, |
| extra_offset: Offset32, |
| ) -> Self { |
| self.offset_i64(fx, extra_offset.into()) |
| } |
| |
| pub(crate) fn offset_i64<'a, 'tcx>( |
| self, |
| fx: &mut FunctionCx<'a, 'tcx, impl Module>, |
| extra_offset: i64, |
| ) -> Self { |
| if let Some(new_offset) = self.offset.try_add_i64(extra_offset) { |
| Pointer { |
| base: self.base, |
| offset: new_offset, |
| } |
| } else { |
| let base_offset: i64 = self.offset.into(); |
| if let Some(new_offset) = base_offset.checked_add(extra_offset) { |
| let base_addr = match self.base { |
| PointerBase::Addr(addr) => addr, |
| PointerBase::Stack(stack_slot) => { |
| fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0) |
| } |
| PointerBase::Dangling(align) => fx |
| .bcx |
| .ins() |
| .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()), |
| }; |
| let addr = fx.bcx.ins().iadd_imm(base_addr, new_offset); |
| Pointer { |
| base: PointerBase::Addr(addr), |
| offset: Offset32::new(0), |
| } |
| } else { |
| panic!( |
| "self.offset ({}) + extra_offset ({}) not representable in i64", |
| base_offset, extra_offset |
| ); |
| } |
| } |
| } |
| |
| pub(crate) fn offset_value<'a, 'tcx>( |
| self, |
| fx: &mut FunctionCx<'a, 'tcx, impl Module>, |
| extra_offset: Value, |
| ) -> Self { |
| match self.base { |
| PointerBase::Addr(addr) => Pointer { |
| base: PointerBase::Addr(fx.bcx.ins().iadd(addr, extra_offset)), |
| offset: self.offset, |
| }, |
| PointerBase::Stack(stack_slot) => { |
| let base_addr = fx |
| .bcx |
| .ins() |
| .stack_addr(fx.pointer_type, stack_slot, self.offset); |
| Pointer { |
| base: PointerBase::Addr(fx.bcx.ins().iadd(base_addr, extra_offset)), |
| offset: Offset32::new(0), |
| } |
| } |
| PointerBase::Dangling(align) => { |
| let addr = fx |
| .bcx |
| .ins() |
| .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()); |
| Pointer { |
| base: PointerBase::Addr(fx.bcx.ins().iadd(addr, extra_offset)), |
| offset: self.offset, |
| } |
| } |
| } |
| } |
| |
| pub(crate) fn load<'a, 'tcx>( |
| self, |
| fx: &mut FunctionCx<'a, 'tcx, impl Module>, |
| ty: Type, |
| flags: MemFlags, |
| ) -> Value { |
| match self.base { |
| PointerBase::Addr(base_addr) => fx.bcx.ins().load(ty, flags, base_addr, self.offset), |
| PointerBase::Stack(stack_slot) => { |
| if ty == types::I128 || ty.is_vector() { |
| // WORKAROUND for stack_load.i128 and stack_load.iXxY not being implemented |
| let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); |
| fx.bcx.ins().load(ty, flags, base_addr, self.offset) |
| } else { |
| fx.bcx.ins().stack_load(ty, stack_slot, self.offset) |
| } |
| } |
| PointerBase::Dangling(_align) => unreachable!(), |
| } |
| } |
| |
| pub(crate) fn store<'a, 'tcx>( |
| self, |
| fx: &mut FunctionCx<'a, 'tcx, impl Module>, |
| value: Value, |
| flags: MemFlags, |
| ) { |
| match self.base { |
| PointerBase::Addr(base_addr) => { |
| fx.bcx.ins().store(flags, value, base_addr, self.offset); |
| } |
| PointerBase::Stack(stack_slot) => { |
| let val_ty = fx.bcx.func.dfg.value_type(value); |
| if val_ty == types::I128 || val_ty.is_vector() { |
| // WORKAROUND for stack_store.i128 and stack_store.iXxY not being implemented |
| let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); |
| fx.bcx.ins().store(flags, value, base_addr, self.offset); |
| } else { |
| fx.bcx.ins().stack_store(value, stack_slot, self.offset); |
| } |
| } |
| PointerBase::Dangling(_align) => unreachable!(), |
| } |
| } |
| } |