about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAndrew Dona-Couch <hi@andrewcou.ch>2021-11-24 23:04:27 -0500
committerAndrew Dona-Couch <hi@andrewcou.ch>2021-12-06 01:02:49 -0500
commitc6e8ae1a6ceeffe343c214859e4eb686b7a71c95 (patch)
tree0c4f61b29d6184bdb3af2cff206afbee4f31deac /src
parent09d8a50ea26df391b58373e22f5891740efdd6cc (diff)
downloadrust-c6e8ae1a6ceeffe343c214859e4eb686b7a71c95.tar.gz
rust-c6e8ae1a6ceeffe343c214859e4eb686b7a71c95.zip
Implement inline asm! for AVR platform
Diffstat (limited to 'src')
-rw-r--r--src/doc/unstable-book/src/library-features/asm.md19
-rw-r--r--src/test/assembly/asm/avr-modifiers.rs60
-rw-r--r--src/test/assembly/asm/avr-types.rs222
3 files changed, 300 insertions, 1 deletions
diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md
index fa238a8b3bc..dffa3a8b80f 100644
--- a/src/doc/unstable-book/src/library-features/asm.md
+++ b/src/doc/unstable-book/src/library-features/asm.md
@@ -32,6 +32,7 @@ Inline assembly is currently supported on the following architectures:
 - wasm32
 - BPF
 - SPIR-V
+- AVR
 
 ## Basic usage
 
@@ -471,6 +472,7 @@ Inline assembly is currently supported on the following architectures:
 - wasm32
 - BPF
 - SPIR-V
+- AVR
 
 Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
 
@@ -593,6 +595,11 @@ Here is the list of currently supported register classes:
 | wasm32 | `local` | None\* | `r` |
 | BPF | `reg` | `r[0-10]` | `r` |
 | BPF | `wreg` | `w[0-10]` | `w` |
+| AVR | `reg` | `r[2-25]`, `XH`, `XL`, `ZH`, `ZL` | `r` |
+| AVR | `reg_upper` | `r[16-25]`, `XH`, `XL`, `ZH`, `ZL` | `d` |
+| AVR | `reg_pair` | `r3r2` .. `r25r24`, `X`, `Z` | `r` |
+| AVR | `reg_iw` | `r25r24`, `X`, `Z` | `w` |
+| AVR | `reg_ptr` | `X`, `Z` | `e` |
 
 > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.
 >
@@ -648,6 +655,8 @@ Each register class has constraints on which value types they can be used with.
 | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` |
 | BPF | `reg` | None | `i8` `i16` `i32` `i64` |
 | BPF | `wreg` | `alu32` | `i8` `i16` `i32` |
+| AVR | `reg`, `reg_upper` | None | `i8` |
+| AVR | `reg_pair`, `reg_iw`, `reg_ptr` | None | `i16` |
 
 > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).
 
@@ -708,13 +717,17 @@ Some registers have multiple names. These are all treated by the compiler as ide
 | Hexagon | `r30` | `fr` |
 | Hexagon | `r31` | `lr` |
 | BPF | `r[0-10]` | `w[0-10]` |
+| AVR | `XH` | `r27` |
+| AVR | `XL` | `r26` |
+| AVR | `ZH` | `r31` |
+| AVR | `ZL` | `r30` |
 
 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), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. |
+| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | 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. |
 | All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `r19` (Hexagon), `x9` (RISC-V) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. |
 | x86 | `k0` | This is a constant zero register which can't be modified. |
@@ -732,11 +745,13 @@ Some registers cannot be used for input or output operands:
 | RISC-V | `x0` | This is a constant zero register which can't be modified. |
 | RISC-V | `gp`, `tp` | These registers are reserved and 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. |
 
 In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are the frame pointer and base pointer
 - The frame pointer and LLVM base pointer on all architectures.
 - `r9` on ARM.
 - `x18` on AArch64.
+- `r0` and `r1` on AVR.
 
 ## Template modifiers
 
@@ -882,6 +897,8 @@ The compiler performs some additional checks on options:
   - RISC-V
     - Floating-point exception flags in `fcsr` (`fflags`).
     - Vector extension state (`vtype`, `vl`, `vcsr`).
+  - AVR
+    - The status register `SREG`.
 - On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit.
   - Behavior is undefined if the direction flag is set on exiting an asm block.
 - The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block.
diff --git a/src/test/assembly/asm/avr-modifiers.rs b/src/test/assembly/asm/avr-modifiers.rs
new file mode 100644
index 00000000000..aba4c982c73
--- /dev/null
+++ b/src/test/assembly/asm/avr-modifiers.rs
@@ -0,0 +1,60 @@
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target avr-unknown-gnu-atmega328
+// needs-llvm-components: avr
+
+#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![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 u64;
+
+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 $hi:literal $lo:literal $reg:tt) => {
+        #[no_mangle]
+        unsafe fn $func() -> i16 {
+            let y;
+            asm!(concat!("mov {0:", $hi, "}, {0:", $lo, "}"), out($reg) y);
+            y
+        }
+    };
+}
+
+// CHECK-LABEL: reg_pair_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_pair_modifiers "h" "l" reg_pair);
+
+// CHECK-LABEL: reg_iw_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_iw_modifiers "h" "l" reg_iw);
+
+// CHECK-LABEL: reg_ptr_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_ptr_modifiers "h" "l" reg_ptr);
diff --git a/src/test/assembly/asm/avr-types.rs b/src/test/assembly/asm/avr-types.rs
new file mode 100644
index 00000000000..53a601e51c8
--- /dev/null
+++ b/src/test/assembly/asm/avr-types.rs
@@ -0,0 +1,222 @@
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target avr-unknown-gnu-atmega328
+// needs-llvm-components: avr
+
+#![feature(no_core, lang_items, rustc_attrs, asm_sym, asm_experimental_arch)]
+#![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 u64;
+
+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! checkw {
+    ($func:ident $ty:ident $class:ident) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!("movw {}, {}", 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_regw {
+    ($func:ident $ty:ident $reg:tt $reg_lit:tt) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!(concat!("movw ", $reg_lit, ", ", $reg_lit), 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: lds r{{[0-9]+}}, extern_static
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_static() -> i8 {
+    let y;
+    asm!("lds {}, {}", lateout(reg) y, sym extern_static);
+    y
+}
+
+// CHECK-LABEL: ld_z:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, Z
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_z(x: i16) -> i8 {
+    let y;
+    asm!("ld {}, Z", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: ldd_z:
+// CHECK: ;APP
+// CHECK: ldd r{{[0-9]+}}, Z+4
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ldd_z(x: i16) -> i8 {
+    let y;
+    asm!("ldd {}, Z+4", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: ld_predecrement:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, -Z
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_predecrement(x: i16) -> i8 {
+    let y;
+    asm!("ld {}, -Z", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: ld_postincrement:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, Z+
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_postincrement(x: i16) -> i8 {
+    let y;
+    asm!("ld {}, Z+", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: muls_clobber:
+// CHECK: ;APP
+// CHECK: muls r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: movw r{{[0-9]+}}, r0
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn muls_clobber(x: i8, y: i8) -> i16 {
+    let z;
+    asm!(
+        "muls {}, {}",
+        "movw {}, r1:r0",
+        out(reg_iw) z,
+        in(reg) x,
+        in(reg) y,
+    );
+    z
+}
+
+// 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_upper_i8:
+// CHECK: ;APP
+// CHECK: mov r{{[1-3][0-9]}}, r{{[1-3][0-9]}}
+// CHECK: ;NO_APP
+check!(reg_upper_i8 i8 reg_upper);
+
+// CHECK-LABEL: reg_pair_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_pair_i16 i16 reg_pair);
+
+// CHECK-LABEL: reg_iw_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_iw_i16 i16 reg_iw);
+
+// CHECK-LABEL: reg_ptr_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_ptr_i16 i16 reg_ptr);
+
+// CHECK-LABEL: r2_i8:
+// CHECK: ;APP
+// CHECK: mov r2, r2
+// CHECK: ;NO_APP
+check_reg!(r2_i8 i8 "r2");
+
+// CHECK-LABEL: xl_i8:
+// CHECK: ;APP
+// CHECK: mov r26, r26
+// CHECK: ;NO_APP
+check_reg!(xl_i8 i8 "XL");
+
+// CHECK-LABEL: xh_i8:
+// CHECK: ;APP
+// CHECK: mov r27, r27
+// CHECK: ;NO_APP
+check_reg!(xh_i8 i8 "XH");
+
+// CHECK-LABEL: x_i16:
+// CHECK: ;APP
+// CHECK: movw r26, r26
+// CHECK: ;NO_APP
+check_regw!(x_i16 i16 "X" "X");
+
+// CHECK-LABEL: r25r24_i16:
+// CHECK: ;APP
+// CHECK: movw r24, r24
+// CHECK: ;NO_APP
+check_regw!(r25r24_i16 i16 "r25r24" "r24");