about summary refs log tree commit diff
path: root/compiler/rustc_codegen_cranelift/src/pointer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_cranelift/src/pointer.rs')
-rw-r--r--compiler/rustc_codegen_cranelift/src/pointer.rs206
1 files changed, 206 insertions, 0 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/pointer.rs b/compiler/rustc_codegen_cranelift/src/pointer.rs
new file mode 100644
index 00000000000..b2036d7bcd4
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/src/pointer.rs
@@ -0,0 +1,206 @@
+//! 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!(),
+        }
+    }
+}