about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-01-23 20:13:04 +0100
committerGitHub <noreply@github.com>2022-01-23 20:13:04 +0100
commit552b564df09f453191ef1839aeac83a915e8b503 (patch)
tree94c4424edb054801afc1a8f6a29f92124a63525a
parent0f2ff4b2700a37c70f59717ee665567946ac1cad (diff)
parent19809ed76d97104fced81b22f51d34475228b305 (diff)
downloadrust-552b564df09f453191ef1839aeac83a915e8b503.tar.gz
rust-552b564df09f453191ef1839aeac83a915e8b503.zip
Rollup merge of #93219 - cr1901:msp430-asm-squashed, r=Amanieu
Add preliminary support for inline assembly for msp430.

The `llvm_asm` macro was removed recently, and the MSP430 backend relies on inline assembly to build useful embedded apps. I conveniently "found" time to implement basic support for the new inline `asm` macro syntax with the help of `@Amanieu` :D.

In addition to tests in the compiler, I have tested this locally against deployed MSP430 code and have not found any noticeable differences in firmware operation or `objdump` disassemblies between the old `llvm_asm` and the new `asm` syntax.
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs6
-rw-r--r--compiler/rustc_target/src/asm/mod.rs25
-rw-r--r--compiler/rustc_target/src/asm/msp430.rs81
-rw-r--r--src/doc/unstable-book/src/language-features/asm-experimental-arch.md17
-rw-r--r--src/test/assembly/asm/msp430-types.rs158
6 files changed, 289 insertions, 1 deletions
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index b4213da6e05..8a74c4c07e0 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -560,6 +560,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
             InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(),
             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(),
             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(),
+            InlineAsmRegClass::Msp430(_) => unimplemented!(),
             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(),
             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(),
             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(),
@@ -622,6 +623,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
         InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(),
+        InlineAsmRegClass::Msp430(_) => unimplemented!(),
         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(),
         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(),
         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(),
@@ -729,6 +731,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option
         InlineAsmRegClass::Bpf(_) => unimplemented!(),
         InlineAsmRegClass::Hexagon(_) => unimplemented!(),
         InlineAsmRegClass::Mips(_) => unimplemented!(),
+        InlineAsmRegClass::Msp430(_) => unimplemented!(),
         InlineAsmRegClass::Nvptx(_) => unimplemented!(),
         InlineAsmRegClass::PowerPC(_) => unimplemented!(),
         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index 8b696dc6fba..e22bec24951 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -232,6 +232,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 InlineAsmArch::SpirV => {}
                 InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {}
                 InlineAsmArch::Bpf => {}
+                InlineAsmArch::Msp430 => {
+                    constraints.push("~{sr}".to_string());
+                }
             }
         }
         if !options.contains(InlineAsmOptions::NOMEM) {
@@ -580,6 +583,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
+            InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r",
             InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
                 bug!("LLVM backend does not support SPIR-V")
             }
@@ -666,6 +670,7 @@ fn modifier_to_llvm(
         },
         InlineAsmRegClass::Avr(_) => None,
         InlineAsmRegClass::S390x(_) => None,
+        InlineAsmRegClass::Msp430(_) => None,
         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
             bug!("LLVM backend does not support SPIR-V")
         }
@@ -734,6 +739,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
         InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
+        InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
             bug!("LLVM backend does not support SPIR-V")
         }
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index 6b82bb337e6..8046267a59d 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -152,6 +152,7 @@ mod avr;
 mod bpf;
 mod hexagon;
 mod mips;
+mod msp430;
 mod nvptx;
 mod powerpc;
 mod riscv;
@@ -166,6 +167,7 @@ pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass};
 pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass};
 pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass};
 pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass};
+pub use msp430::{Msp430InlineAsmReg, Msp430InlineAsmRegClass};
 pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass};
 pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass};
 pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass};
@@ -194,6 +196,7 @@ pub enum InlineAsmArch {
     Wasm64,
     Bpf,
     Avr,
+    Msp430,
 }
 
 impl FromStr for InlineAsmArch {
@@ -219,6 +222,7 @@ impl FromStr for InlineAsmArch {
             "wasm64" => Ok(Self::Wasm64),
             "bpf" => Ok(Self::Bpf),
             "avr" => Ok(Self::Avr),
+            "msp430" => Ok(Self::Msp430),
             _ => Err(()),
         }
     }
@@ -250,6 +254,7 @@ pub enum InlineAsmReg {
     Wasm(WasmInlineAsmReg),
     Bpf(BpfInlineAsmReg),
     Avr(AvrInlineAsmReg),
+    Msp430(Msp430InlineAsmReg),
     // Placeholder for invalid register constraints for the current target
     Err,
 }
@@ -267,6 +272,7 @@ impl InlineAsmReg {
             Self::S390x(r) => r.name(),
             Self::Bpf(r) => r.name(),
             Self::Avr(r) => r.name(),
+            Self::Msp430(r) => r.name(),
             Self::Err => "<reg>",
         }
     }
@@ -283,6 +289,7 @@ impl InlineAsmReg {
             Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()),
             Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()),
             Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()),
+            Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()),
             Self::Err => InlineAsmRegClass::Err,
         }
     }
@@ -336,6 +343,9 @@ impl InlineAsmReg {
             InlineAsmArch::Avr => {
                 Self::Avr(AvrInlineAsmReg::parse(arch, target_features, target, name)?)
             }
+            InlineAsmArch::Msp430 => {
+                Self::Msp430(Msp430InlineAsmReg::parse(arch, target_features, target, name)?)
+            }
         })
     }
 
@@ -358,6 +368,7 @@ impl InlineAsmReg {
             Self::S390x(r) => r.emit(out, arch, modifier),
             Self::Bpf(r) => r.emit(out, arch, modifier),
             Self::Avr(r) => r.emit(out, arch, modifier),
+            Self::Msp430(r) => r.emit(out, arch, modifier),
             Self::Err => unreachable!("Use of InlineAsmReg::Err"),
         }
     }
@@ -374,6 +385,7 @@ impl InlineAsmReg {
             Self::S390x(_) => cb(self),
             Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))),
             Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))),
+            Self::Msp430(_) => cb(self),
             Self::Err => unreachable!("Use of InlineAsmReg::Err"),
         }
     }
@@ -405,6 +417,7 @@ pub enum InlineAsmRegClass {
     Wasm(WasmInlineAsmRegClass),
     Bpf(BpfInlineAsmRegClass),
     Avr(AvrInlineAsmRegClass),
+    Msp430(Msp430InlineAsmRegClass),
     // Placeholder for invalid register constraints for the current target
     Err,
 }
@@ -425,6 +438,7 @@ impl InlineAsmRegClass {
             Self::Wasm(r) => r.name(),
             Self::Bpf(r) => r.name(),
             Self::Avr(r) => r.name(),
+            Self::Msp430(r) => r.name(),
             Self::Err => rustc_span::symbol::sym::reg,
         }
     }
@@ -447,6 +461,7 @@ impl InlineAsmRegClass {
             Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm),
             Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf),
             Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr),
+            Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -476,6 +491,7 @@ impl InlineAsmRegClass {
             Self::Wasm(r) => r.suggest_modifier(arch, ty),
             Self::Bpf(r) => r.suggest_modifier(arch, ty),
             Self::Avr(r) => r.suggest_modifier(arch, ty),
+            Self::Msp430(r) => r.suggest_modifier(arch, ty),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -501,6 +517,7 @@ impl InlineAsmRegClass {
             Self::Wasm(r) => r.default_modifier(arch),
             Self::Bpf(r) => r.default_modifier(arch),
             Self::Avr(r) => r.default_modifier(arch),
+            Self::Msp430(r) => r.default_modifier(arch),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -525,6 +542,7 @@ impl InlineAsmRegClass {
             Self::Wasm(r) => r.supported_types(arch),
             Self::Bpf(r) => r.supported_types(arch),
             Self::Avr(r) => r.supported_types(arch),
+            Self::Msp430(r) => r.supported_types(arch),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -554,6 +572,7 @@ impl InlineAsmRegClass {
             }
             InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?),
             InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(arch, name)?),
+            InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(arch, name)?),
         })
     }
 
@@ -574,6 +593,7 @@ impl InlineAsmRegClass {
             Self::Wasm(r) => r.valid_modifiers(arch),
             Self::Bpf(r) => r.valid_modifiers(arch),
             Self::Avr(r) => r.valid_modifiers(arch),
+            Self::Msp430(r) => r.valid_modifiers(arch),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -764,6 +784,11 @@ pub fn allocatable_registers(
             avr::fill_reg_map(arch, target_features, target, &mut map);
             map
         }
+        InlineAsmArch::Msp430 => {
+            let mut map = msp430::regclass_map();
+            msp430::fill_reg_map(arch, target_features, target, &mut map);
+            map
+        }
     }
 }
 
diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs
new file mode 100644
index 00000000000..a27d6390a72
--- /dev/null
+++ b/compiler/rustc_target/src/asm/msp430.rs
@@ -0,0 +1,81 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use rustc_span::Symbol;
+use std::fmt;
+
+def_reg_class! {
+    Msp430 Msp430InlineAsmRegClass {
+        reg,
+    }
+}
+
+impl Msp430InlineAsmRegClass {
+    pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+        &[]
+    }
+
+    pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+        None
+    }
+
+    pub fn suggest_modifier(
+        self,
+        _arch: InlineAsmArch,
+        _ty: InlineAsmType,
+    ) -> Option<(char, &'static str)> {
+        None
+    }
+
+    pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+        None
+    }
+
+    pub fn supported_types(
+        self,
+        arch: InlineAsmArch,
+    ) -> &'static [(InlineAsmType, Option<Symbol>)] {
+        match (self, arch) {
+            (Self::reg, _) => types! { _: I8, I16; },
+        }
+    }
+}
+
+// The reserved registers are taken from:
+// https://github.com/llvm/llvm-project/blob/36cb29cbbe1b22dcd298ad65e1fabe899b7d7249/llvm/lib/Target/MSP430/MSP430RegisterInfo.cpp#L73.
+def_regs! {
+    Msp430 Msp430InlineAsmReg Msp430InlineAsmRegClass {
+        r5: reg = ["r5"],
+        r6: reg = ["r6"],
+        r7: reg = ["r7"],
+        r8: reg = ["r8"],
+        r9: reg = ["r9"],
+        r10: reg = ["r10"],
+        r11: reg = ["r11"],
+        r12: reg = ["r12"],
+        r13: reg = ["r13"],
+        r14: reg = ["r14"],
+        r15: reg = ["r15"],
+
+        #error = ["r0", "pc"] =>
+            "the program counter cannot be used as an operand for inline asm",
+        #error = ["r1", "sp"] =>
+            "the stack pointer cannot be used as an operand for inline asm",
+        #error = ["r2", "sr"] =>
+            "the status register cannot be used as an operand for inline asm",
+        #error = ["r3", "cg"] =>
+            "the constant generator cannot be used as an operand for inline asm",
+        #error = ["r4", "fp"] =>
+            "the frame pointer cannot be used as an operand for inline asm",
+    }
+}
+
+impl Msp430InlineAsmReg {
+    pub fn emit(
+        self,
+        out: &mut dyn fmt::Write,
+        _arch: InlineAsmArch,
+        _modifier: Option<char>,
+    ) -> fmt::Result {
+        out.write_str(self.name())
+    }
+}
diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
index ec97eaa8b2b..37fd67447c1 100644
--- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
+++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
@@ -15,6 +15,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 - BPF
 - SPIR-V
 - AVR
+- MSP430
 
 ## Register classes
 
@@ -39,6 +40,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | AVR          | `reg_pair`     | `r3r2` .. `r25r24`, `X`, `Z`       | `r`                  |
 | AVR          | `reg_iw`       | `r25r24`, `X`, `Z`                 | `w`                  |
 | AVR          | `reg_ptr`      | `X`, `Z`                           | `e`                  |
+| MSP430       | `reg`          | `r[0-15]`                          | `r`                  |
 
 > **Notes**:
 > - NVPTX doesn't have a fixed register set, so named registers are not supported.
@@ -67,6 +69,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | BPF          | `wreg`                          | `alu32`        | `i8` `i16` `i32`                        |
 | AVR          | `reg`, `reg_upper`              | None           | `i8`                                    |
 | AVR          | `reg_pair`, `reg_iw`, `reg_ptr` | None           | `i16`                                   |
+| MSP430       | `reg`                           | None           | `i8`, `i16`                             |
 
 ## Register aliases
 
@@ -80,13 +83,22 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | AVR          | `XL`          | `r26`     |
 | AVR          | `ZH`          | `r31`     |
 | AVR          | `ZL`          | `r30`     |
+| MSP430       | `r0`          | `pc`      |
+| MSP430       | `r1`          | `sp`      |
+| MSP430       | `r2`          | `sr`      |
+| MSP430       | `r3`          | `cg`      |
+| MSP430       | `r4`          | `fp`      |
+
+> **Notes**:
+> - TI does not mandate a frame pointer for MSP430, but toolchains are allowed
+    to use one; LLVM uses `r4`.
 
 ## Unsupported registers
 
 | 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          | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | The frame pointer cannot be used as an input or output.                                                                                                                             |
+| All          | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430) | The frame pointer cannot be used as an input or output.                                                                                                                             |
 | All          | `r19` (Hexagon)                         | This is used internally by LLVM as a "base pointer" for functions with complex stack frames.                                                                                        |
 | MIPS         | `$0` or `$zero`                         | This is a constant zero register which can't be modified.                                                                                                                           |
 | MIPS         | `$1` or `$at`                           | Reserved for assembler.                                                                                                                                                             |
@@ -95,6 +107,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | MIPS         | `$ra`                                   | Return address cannot be used as inputs or outputs.                                                                                                                                 |
 | Hexagon      | `lr`                                    | This is the link register which cannot be used as an input or output.                                                                                                               |
 | AVR          | `r0`, `r1`, `r1r0`                      | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs.  If modified, they must be restored to their original values before the end of the block. |
+|MSP430        | `r0`, `r2`, `r3`                        | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to.                          |
 
 ## Template modifiers
 
@@ -115,3 +128,5 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set:
 - AVR
   - The status register `SREG`.
+- MSP430
+  - The status register `r2`.
diff --git a/src/test/assembly/asm/msp430-types.rs b/src/test/assembly/asm/msp430-types.rs
new file mode 100644
index 00000000000..6cfb86e276e
--- /dev/null
+++ b/src/test/assembly/asm/msp430-types.rs
@@ -0,0 +1,158 @@
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target msp430-none-elf
+// needs-llvm-components: msp430
+
+#![feature(no_core, lang_items, rustc_attrs, asm_sym, asm_experimental_arch, asm_const)]
+#![crate_type = "rlib"]
+#![no_core]
+#![allow(non_camel_case_types)]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+    () => {};
+}
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+type ptr = *const i16;
+
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for i64 {}
+impl Copy for ptr {}
+
+macro_rules! check {
+    ($func:ident $ty:ident $class:ident) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!("mov {}, {}", lateout($class) y, in($class) x);
+            y
+        }
+    };
+}
+
+macro_rules! checkb {
+    ($func:ident $ty:ident $class:ident) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!("mov.b {}, {}", lateout($class) y, in($class) x);
+            y
+        }
+    };
+}
+
+macro_rules! check_reg {
+    ($func:ident $ty:ident $reg:tt) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!(concat!("mov ", $reg, ", ", $reg), lateout($reg) y, in($reg) x);
+            y
+        }
+    };
+}
+
+macro_rules! check_regb {
+    ($func:ident $ty:ident $reg:tt) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!(concat!("mov.b ", $reg, ", ", $reg), lateout($reg) y, in($reg) x);
+            y
+        }
+    };
+}
+
+extern "C" {
+    fn extern_func();
+    static extern_static: i8;
+}
+
+// CHECK-LABEL: sym_fn
+// CHECK: ;APP
+// CHECK: call extern_func
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_fn() {
+    asm!("call {}", sym extern_func);
+}
+
+// CHECK-LABEL: sym_static
+// CHECK: ;APP
+// CHECK: mov.b extern_static, r{{[0-9]+}}
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_static() -> i8 {
+    let y;
+    asm!("mov.b {1}, {0}", lateout(reg) y, sym extern_static);
+    y
+}
+
+// CHECK-LABEL: add_const:
+// CHECK: ;APP
+// CHECK: add.b #5, r{{[0-9]+}}
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn add_const() -> i8 {
+    let y;
+    asm!("add.b #{number}, {}", out(reg) y, number = const 5);
+    y
+}
+
+// CHECK-LABEL: mov_postincrement:
+// CHECK: ;APP
+// CHECK: mov @r5+, r{{[0-9]+}}
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn mov_postincrement(mut x: *const i16) -> (i16, *const i16) {
+    let y;
+    asm!("mov @r5+, {0}", out(reg) y, inlateout("r5") x);
+    (y, x)
+}
+
+// CHECK-LABEL: reg_i8:
+// CHECK: ;APP
+// CHECK: mov r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+check!(reg_i8 i8 reg);
+
+// CHECK-LABEL: reg_i16:
+// CHECK: ;APP
+// CHECK: mov r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+check!(reg_i16 i16 reg);
+
+// CHECK-LABEL: reg_i8b:
+// CHECK: ;APP
+// CHECK: mov.b r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkb!(reg_i8b i8 reg);
+
+// CHECK-LABEL: r5_i8:
+// CHECK: ;APP
+// CHECK: mov r5, r5
+// CHECK: ;NO_APP
+check_reg!(r5_i8 i8 "r5");
+
+// CHECK-LABEL: r5_i16:
+// CHECK: ;APP
+// CHECK: mov r5, r5
+// CHECK: ;NO_APP
+check_reg!(r5_i16 i16 "r5");
+
+// CHECK-LABEL: r5_i8b:
+// CHECK: ;APP
+// CHECK: mov.b r5, r5
+// CHECK: ;NO_APP
+check_regb!(r5_i8b i8 "r5");