about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/unstable-book/src/library-features/asm.md8
-rw-r--r--src/librustc_ast_lowering/expr.rs1
-rw-r--r--src/librustc_codegen_llvm/llvm_util.rs4
-rw-r--r--src/librustc_target/asm/arm.rs40
-rw-r--r--src/librustc_target/asm/mod.rs35
-rw-r--r--src/librustc_target/asm/riscv.rs2
-rw-r--r--src/librustc_target/asm/x86.rs3
7 files changed, 73 insertions, 20 deletions
diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md
index a941bc9348f..c4c985dd134 100644
--- a/src/doc/unstable-book/src/library-features/asm.md
+++ b/src/doc/unstable-book/src/library-features/asm.md
@@ -474,7 +474,7 @@ Here is the list of currently supported register classes:
 | AArch64 | `reg` | `x[0-28]`, `x30` | `r` |
 | AArch64 | `vreg` | `v[0-31]` | `w` |
 | AArch64 | `vreg_low16` | `v[0-15]` | `x` |
-| ARM | `reg` | `r[0-r10]`, `r12`, `r14` | `r` |
+| ARM | `reg` | `r[0-5]` `r7`\*, `r[8-10]`, `r11`\*, `r12`, `r14` | `r` |
 | ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |
 | ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` |
 | ARM | `sreg` | `s[0-31]` | `t` |
@@ -497,6 +497,8 @@ Here is the list of currently supported register classes:
 > Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register.
 >
 > Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported.
+>
+> Note #4: On ARM the frame pointer is either `r7` or `r11` depending on the platform.
 
 Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc).
 
@@ -591,7 +593,9 @@ Some registers cannot be used for input or output operands:
 | Architecture | Unsupported register | Reason |
 | ------------ | -------------------- | ------ |
 | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |
-| All | `bp` (x86), `r11` (ARM), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon) | The frame pointer cannot be used as an input or output. |
+| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon) | The frame pointer cannot be used as an input or output. |
+| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |
+| ARM | `r6` | `r6` is used internally by LLVM as a base pointer and therefore cannot be used as an input or output. |
 | x86 | `k0` | This is a constant zero register which can't be modified. |
 | x86 | `ip` | This is the program counter, not a real register. |
 | x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). |
diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs
index b7894eb145b..d2c4478ccfe 100644
--- a/src/librustc_ast_lowering/expr.rs
+++ b/src/librustc_ast_lowering/expr.rs
@@ -1001,6 +1001,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             asm::InlineAsmReg::parse(
                                 sess.asm_arch?,
                                 |feature| sess.target_features.contains(&Symbol::intern(feature)),
+                                &sess.target.target,
                                 s,
                             )
                             .map_err(|e| {
diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs
index 67a2251e859..80278bb9f53 100644
--- a/src/librustc_codegen_llvm/llvm_util.rs
+++ b/src/librustc_codegen_llvm/llvm_util.rs
@@ -156,6 +156,10 @@ const ARM_WHITELIST: &[(&str, Option<Symbol>)] = &[
     ("vfp2", Some(sym::arm_target_feature)),
     ("vfp3", Some(sym::arm_target_feature)),
     ("vfp4", Some(sym::arm_target_feature)),
+    // This is needed for inline assembly, but shouldn't be stabilized as-is
+    // since it should be enabled per-function using #[instruction_set], not
+    // #[target_feature].
+    ("thumb-mode", Some(sym::arm_target_feature)),
 ];
 
 const AARCH64_WHITELIST: &[(&str, Option<Symbol>)] = &[
diff --git a/src/librustc_target/asm/arm.rs b/src/librustc_target/asm/arm.rs
index 1798b2a0949..85a136b94aa 100644
--- a/src/librustc_target/asm/arm.rs
+++ b/src/librustc_target/asm/arm.rs
@@ -1,4 +1,5 @@
 use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::Target;
 use rustc_macros::HashStable_Generic;
 use std::fmt;
 
@@ -58,6 +59,37 @@ impl ArmInlineAsmRegClass {
     }
 }
 
+// This uses the same logic as useR7AsFramePointer in LLVM
+fn frame_pointer_is_r7(mut has_feature: impl FnMut(&str) -> bool, target: &Target) -> bool {
+    target.options.is_like_osx || (!target.options.is_like_windows && has_feature("thumb-mode"))
+}
+
+fn frame_pointer_r11(
+    _arch: InlineAsmArch,
+    has_feature: impl FnMut(&str) -> bool,
+    target: &Target,
+    _allocating: bool,
+) -> Result<(), &'static str> {
+    if !frame_pointer_is_r7(has_feature, target) {
+        Err("the frame pointer (r11) cannot be used as an operand for inline asm")
+    } else {
+        Ok(())
+    }
+}
+
+fn frame_pointer_r7(
+    _arch: InlineAsmArch,
+    has_feature: impl FnMut(&str) -> bool,
+    target: &Target,
+    _allocating: bool,
+) -> Result<(), &'static str> {
+    if frame_pointer_is_r7(has_feature, target) {
+        Err("the frame pointer (r7) cannot be used as an operand for inline asm")
+    } else {
+        Ok(())
+    }
+}
+
 def_regs! {
     Arm ArmInlineAsmReg ArmInlineAsmRegClass {
         r0: reg, reg_thumb = ["r0", "a1"],
@@ -66,11 +98,11 @@ def_regs! {
         r3: reg, reg_thumb = ["r3", "a4"],
         r4: reg, reg_thumb = ["r4", "v1"],
         r5: reg, reg_thumb = ["r5", "v2"],
-        r6: reg, reg_thumb = ["r6", "v3"],
-        r7: reg, reg_thumb = ["r7", "v4"],
+        r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7,
         r8: reg = ["r8", "v5"],
         r9: reg = ["r9", "v6", "rfp"],
         r10: reg = ["r10", "sl"],
+        r11: reg = ["r11", "fp"] % frame_pointer_r11,
         r12: reg = ["r12", "ip"],
         r14: reg = ["r14", "lr"],
         s0: sreg, sreg_low16 = ["s0"],
@@ -153,8 +185,8 @@ def_regs! {
         q13: qreg = ["q13"],
         q14: qreg = ["q14"],
         q15: qreg = ["q15"],
-        #error = ["r11", "fp"] =>
-            "the frame pointer cannot be used as an operand for inline asm",
+        #error = ["r6", "v3"] =>
+            "r6 is used internally by LLVM and cannot be used as an operand for inline asm",
         #error = ["r13", "sp"] =>
             "the stack pointer cannot be used as an operand for inline asm",
         #error = ["r15", "pc"] =>
diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs
index 834d7c6d381..ccec17817d3 100644
--- a/src/librustc_target/asm/mod.rs
+++ b/src/librustc_target/asm/mod.rs
@@ -1,4 +1,5 @@
 use crate::abi::Size;
+use crate::spec::Target;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
@@ -83,12 +84,13 @@ macro_rules! def_regs {
             pub fn parse(
                 _arch: super::InlineAsmArch,
                 mut _has_feature: impl FnMut(&str) -> bool,
+                _target: &crate::spec::Target,
                 name: &str,
             ) -> Result<Self, &'static str> {
                 match name {
                     $(
                         $($alias)|* | $reg_name => {
-                            $($filter(_arch, &mut _has_feature, false)?;)?
+                            $($filter(_arch, &mut _has_feature, _target, false)?;)?
                             Ok(Self::$reg)
                         }
                     )*
@@ -103,6 +105,7 @@ macro_rules! def_regs {
         pub(super) fn fill_reg_map(
             _arch: super::InlineAsmArch,
             mut _has_feature: impl FnMut(&str) -> bool,
+            _target: &crate::spec::Target,
             _map: &mut rustc_data_structures::fx::FxHashMap<
                 super::InlineAsmRegClass,
                 rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
@@ -111,7 +114,7 @@ macro_rules! def_regs {
             #[allow(unused_imports)]
             use super::{InlineAsmReg, InlineAsmRegClass};
             $(
-                if $($filter(_arch, &mut _has_feature, true).is_ok() &&)? true {
+                if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true {
                     if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) {
                         set.insert(InlineAsmReg::$arch($arch_reg::$reg));
                     }
@@ -234,6 +237,7 @@ impl InlineAsmReg {
     pub fn parse(
         arch: InlineAsmArch,
         has_feature: impl FnMut(&str) -> bool,
+        target: &Target,
         name: Symbol,
     ) -> Result<Self, &'static str> {
         // FIXME: use direct symbol comparison for register names
@@ -241,20 +245,22 @@ impl InlineAsmReg {
         let name = name.as_str();
         Ok(match arch {
             InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
-                Self::X86(X86InlineAsmReg::parse(arch, has_feature, &name)?)
+                Self::X86(X86InlineAsmReg::parse(arch, has_feature, target, &name)?)
+            }
+            InlineAsmArch::Arm => {
+                Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, target, &name)?)
             }
-            InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, &name)?),
             InlineAsmArch::AArch64 => {
-                Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, &name)?)
+                Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, target, &name)?)
             }
             InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
-                Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, &name)?)
+                Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, target, &name)?)
             }
             InlineAsmArch::Nvptx64 => {
-                Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, &name)?)
+                Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, target, &name)?)
             }
             InlineAsmArch::Hexagon => {
-                Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, &name)?)
+                Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?)
             }
         })
     }
@@ -536,36 +542,37 @@ impl fmt::Display for InlineAsmType {
 pub fn allocatable_registers(
     arch: InlineAsmArch,
     has_feature: impl FnMut(&str) -> bool,
+    target: &crate::spec::Target,
 ) -> FxHashMap<InlineAsmRegClass, FxHashSet<InlineAsmReg>> {
     match arch {
         InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
             let mut map = x86::regclass_map();
-            x86::fill_reg_map(arch, has_feature, &mut map);
+            x86::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
         InlineAsmArch::Arm => {
             let mut map = arm::regclass_map();
-            arm::fill_reg_map(arch, has_feature, &mut map);
+            arm::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
         InlineAsmArch::AArch64 => {
             let mut map = aarch64::regclass_map();
-            aarch64::fill_reg_map(arch, has_feature, &mut map);
+            aarch64::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
         InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
             let mut map = riscv::regclass_map();
-            riscv::fill_reg_map(arch, has_feature, &mut map);
+            riscv::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
         InlineAsmArch::Nvptx64 => {
             let mut map = nvptx::regclass_map();
-            nvptx::fill_reg_map(arch, has_feature, &mut map);
+            nvptx::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
         InlineAsmArch::Hexagon => {
             let mut map = hexagon::regclass_map();
-            hexagon::fill_reg_map(arch, has_feature, &mut map);
+            hexagon::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
     }
diff --git a/src/librustc_target/asm/riscv.rs b/src/librustc_target/asm/riscv.rs
index 3ff542247ff..ced7483b005 100644
--- a/src/librustc_target/asm/riscv.rs
+++ b/src/librustc_target/asm/riscv.rs
@@ -1,4 +1,5 @@
 use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::Target;
 use rustc_macros::HashStable_Generic;
 use std::fmt;
 
@@ -50,6 +51,7 @@ impl RiscVInlineAsmRegClass {
 fn not_e(
     _arch: InlineAsmArch,
     mut has_feature: impl FnMut(&str) -> bool,
+    _target: &Target,
     _allocating: bool,
 ) -> Result<(), &'static str> {
     if has_feature("e") {
diff --git a/src/librustc_target/asm/x86.rs b/src/librustc_target/asm/x86.rs
index ed51b526414..0f62c19e1a3 100644
--- a/src/librustc_target/asm/x86.rs
+++ b/src/librustc_target/asm/x86.rs
@@ -1,4 +1,5 @@
 use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::Target;
 use rustc_macros::HashStable_Generic;
 use std::fmt;
 
@@ -131,6 +132,7 @@ impl X86InlineAsmRegClass {
 fn x86_64_only(
     arch: InlineAsmArch,
     _has_feature: impl FnMut(&str) -> bool,
+    _target: &Target,
     _allocating: bool,
 ) -> Result<(), &'static str> {
     match arch {
@@ -143,6 +145,7 @@ fn x86_64_only(
 fn high_byte(
     arch: InlineAsmArch,
     _has_feature: impl FnMut(&str) -> bool,
+    _target: &Target,
     allocating: bool,
 ) -> Result<(), &'static str> {
     match arch {