From 11250b8661f2f37e96827d6d5434833221b355d2 Mon Sep 17 00:00:00 2001
From: Amanieu d'Antras <amanieu@gmail.com>
Date: Thu, 10 Feb 2022 20:50:16 +0000
Subject: [PATCH] asm: Allow the use of r8-r14 as clobbers on Thumb1

Previously these were entirely disallowed, except for r11 which was
allowed by accident.
---
 compiler/rustc_ast_lowering/src/asm.rs   |  11 +--
 compiler/rustc_target/src/asm/aarch64.rs |   1 +
 compiler/rustc_target/src/asm/arm.rs     |  17 +++-
 compiler/rustc_target/src/asm/bpf.rs     |   1 +
 compiler/rustc_target/src/asm/mod.rs     | 108 +++++++++++++++--------
 compiler/rustc_target/src/asm/riscv.rs   |   1 +
 compiler/rustc_target/src/asm/x86.rs     |   4 +
 7 files changed, 97 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 5a85356d96daa..18fcc99ffbaa0 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -129,13 +129,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             .operands
             .iter()
             .map(|(op, op_sp)| {
-                let lower_reg = |reg| match reg {
+                let lower_reg = |reg, is_clobber| match reg {
                     InlineAsmRegOrRegClass::Reg(s) => {
                         asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
                             asm::InlineAsmReg::parse(
                                 asm_arch,
                                 &sess.target_features,
                                 &sess.target,
+                                is_clobber,
                                 s,
                             )
                             .unwrap_or_else(|e| {
@@ -162,24 +163,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                 let op = match *op {
                     InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
-                        reg: lower_reg(reg),
+                        reg: lower_reg(reg, false),
                         expr: self.lower_expr_mut(expr),
                     },
                     InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
-                        reg: lower_reg(reg),
+                        reg: lower_reg(reg, expr.is_none()),
                         late,
                         expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
                     },
                     InlineAsmOperand::InOut { reg, late, ref expr } => {
                         hir::InlineAsmOperand::InOut {
-                            reg: lower_reg(reg),
+                            reg: lower_reg(reg, false),
                             late,
                             expr: self.lower_expr_mut(expr),
                         }
                     }
                     InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
                         hir::InlineAsmOperand::SplitInOut {
-                            reg: lower_reg(reg),
+                            reg: lower_reg(reg, false),
                             late,
                             in_expr: self.lower_expr_mut(in_expr),
                             out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs
index da875508676d4..d184ad4e78ae5 100644
--- a/compiler/rustc_target/src/asm/aarch64.rs
+++ b/compiler/rustc_target/src/asm/aarch64.rs
@@ -77,6 +77,7 @@ pub fn reserved_x18(
     _arch: InlineAsmArch,
     _target_features: &FxHashSet<Symbol>,
     target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     if target.os == "android"
         || target.is_like_fuchsia
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
index e3615b43c70eb..b2d5bb3736afd 100644
--- a/compiler/rustc_target/src/asm/arm.rs
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -66,10 +66,13 @@ fn frame_pointer_is_r7(target_features: &FxHashSet<Symbol>, target: &Target) ->
 }
 
 fn frame_pointer_r11(
-    _arch: InlineAsmArch,
+    arch: InlineAsmArch,
     target_features: &FxHashSet<Symbol>,
     target: &Target,
+    is_clobber: bool,
 ) -> Result<(), &'static str> {
+    not_thumb1(arch, target_features, target, is_clobber)?;
+
     if !frame_pointer_is_r7(target_features, target) {
         Err("the frame pointer (r11) cannot be used as an operand for inline asm")
     } else {
@@ -81,6 +84,7 @@ fn frame_pointer_r7(
     _arch: InlineAsmArch,
     target_features: &FxHashSet<Symbol>,
     target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     if frame_pointer_is_r7(target_features, target) {
         Err("the frame pointer (r7) cannot be used as an operand for inline asm")
@@ -93,9 +97,13 @@ fn not_thumb1(
     _arch: InlineAsmArch,
     target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    is_clobber: bool,
 ) -> Result<(), &'static str> {
-    if target_features.contains(&sym::thumb_mode) && !target_features.contains(&sym::thumb2) {
-        Err("high registers (r8+) cannot be used in Thumb-1 code")
+    if !is_clobber
+        && target_features.contains(&sym::thumb_mode)
+        && !target_features.contains(&sym::thumb2)
+    {
+        Err("high registers (r8+) can only be used as clobbers in Thumb-1 code")
     } else {
         Ok(())
     }
@@ -105,8 +113,9 @@ fn reserved_r9(
     arch: InlineAsmArch,
     target_features: &FxHashSet<Symbol>,
     target: &Target,
+    is_clobber: bool,
 ) -> Result<(), &'static str> {
-    not_thumb1(arch, target_features, target)?;
+    not_thumb1(arch, target_features, target, is_clobber)?;
 
     // We detect this using the reserved-r9 feature instead of using the target
     // because the relocation model can be changed with compiler options.
diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs
index d94fcb53e24c9..b4d982f3836be 100644
--- a/compiler/rustc_target/src/asm/bpf.rs
+++ b/compiler/rustc_target/src/asm/bpf.rs
@@ -47,6 +47,7 @@ fn only_alu32(
     _arch: InlineAsmArch,
     target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     if !target_features.contains(&sym::alu32) {
         Err("register can't be used without the `alu32` target feature")
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index a84410d0f3c46..fd95b0338a6e1 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -83,12 +83,13 @@ macro_rules! def_regs {
                 _arch: super::InlineAsmArch,
                 _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>,
                 _target: &crate::spec::Target,
+                _is_clobber: bool,
                 name: &str,
             ) -> Result<Self, &'static str> {
                 match name {
                     $(
                         $($alias)|* | $reg_name => {
-                            $($filter(_arch, _target_features, _target)?;)?
+                            $($filter(_arch, _target_features, _target, _is_clobber)?;)?
                             Ok(Self::$reg)
                         }
                     )*
@@ -112,7 +113,7 @@ macro_rules! def_regs {
             #[allow(unused_imports)]
             use super::{InlineAsmReg, InlineAsmRegClass};
             $(
-                if $($filter(_arch, _target_features, _target).is_ok() &&)? true {
+                if $($filter(_arch, _target_features, _target, false).is_ok() &&)? true {
                     if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) {
                         set.insert(InlineAsmReg::$arch($arch_reg::$reg));
                     }
@@ -298,6 +299,7 @@ impl InlineAsmReg {
         arch: InlineAsmArch,
         target_features: &FxHashSet<Symbol>,
         target: &Target,
+        is_clobber: bool,
         name: Symbol,
     ) -> Result<Self, &'static str> {
         // FIXME: use direct symbol comparison for register names
@@ -305,47 +307,79 @@ impl InlineAsmReg {
         let name = name.as_str();
         Ok(match arch {
             InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
-                Self::X86(X86InlineAsmReg::parse(arch, target_features, target, name)?)
+                Self::X86(X86InlineAsmReg::parse(arch, target_features, target, is_clobber, name)?)
             }
             InlineAsmArch::Arm => {
-                Self::Arm(ArmInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::AArch64 => {
-                Self::AArch64(AArch64InlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
-                Self::RiscV(RiscVInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::Nvptx64 => {
-                Self::Nvptx(NvptxInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {
-                Self::PowerPC(PowerPCInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::Hexagon => {
-                Self::Hexagon(HexagonInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::Mips | InlineAsmArch::Mips64 => {
-                Self::Mips(MipsInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::S390x => {
-                Self::S390x(S390xInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::SpirV => {
-                Self::SpirV(SpirVInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {
-                Self::Wasm(WasmInlineAsmReg::parse(arch, target_features, target, name)?)
+                Self::Arm(ArmInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?)
             }
+            InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
+            InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => Self::RiscV(
+                RiscVInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?,
+            ),
+            InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
+            InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => Self::PowerPC(
+                PowerPCInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?,
+            ),
+            InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
+            InlineAsmArch::Mips | InlineAsmArch::Mips64 => Self::Mips(MipsInlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
+            InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
+            InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
+            InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => Self::Wasm(WasmInlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
             InlineAsmArch::Bpf => {
-                Self::Bpf(BpfInlineAsmReg::parse(arch, target_features, target, name)?)
+                Self::Bpf(BpfInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?)
             }
             InlineAsmArch::Avr => {
-                Self::Avr(AvrInlineAsmReg::parse(arch, target_features, target, name)?)
-            }
-            InlineAsmArch::Msp430 => {
-                Self::Msp430(Msp430InlineAsmReg::parse(arch, target_features, target, name)?)
+                Self::Avr(AvrInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?)
             }
+            InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse(
+                arch,
+                target_features,
+                target,
+                is_clobber,
+                name,
+            )?),
         })
     }
 
@@ -844,7 +878,7 @@ impl InlineAsmClobberAbi {
             },
             InlineAsmArch::AArch64 => match name {
                 "C" | "system" | "efiapi" => {
-                    Ok(if aarch64::reserved_x18(arch, target_features, target).is_err() {
+                    Ok(if aarch64::reserved_x18(arch, target_features, target, true).is_err() {
                         InlineAsmClobberAbi::AArch64NoX18
                     } else {
                         InlineAsmClobberAbi::AArch64
diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs
index 39644d232badb..e145ba8a16e64 100644
--- a/compiler/rustc_target/src/asm/riscv.rs
+++ b/compiler/rustc_target/src/asm/riscv.rs
@@ -56,6 +56,7 @@ fn not_e(
     _arch: InlineAsmArch,
     target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     if target_features.contains(&sym::e) {
         Err("register can't be used with the `e` target feature")
diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs
index 01d32570f78a2..a8ee80ec4ea27 100644
--- a/compiler/rustc_target/src/asm/x86.rs
+++ b/compiler/rustc_target/src/asm/x86.rs
@@ -141,6 +141,7 @@ fn x86_64_only(
     arch: InlineAsmArch,
     _target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     match arch {
         InlineAsmArch::X86 => Err("register is only available on x86_64"),
@@ -153,6 +154,7 @@ fn high_byte(
     arch: InlineAsmArch,
     _target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     match arch {
         InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"),
@@ -164,6 +166,7 @@ fn rbx_reserved(
     arch: InlineAsmArch,
     _target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     match arch {
         InlineAsmArch::X86 => Ok(()),
@@ -178,6 +181,7 @@ fn esi_reserved(
     arch: InlineAsmArch,
     _target_features: &FxHashSet<Symbol>,
     _target: &Target,
+    _is_clobber: bool,
 ) -> Result<(), &'static str> {
     match arch {
         InlineAsmArch::X86 => {