Skip to content

Commit

Permalink
Feature gate fp, and other registers. Add Xtensa to asm! documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
MabezDev committed Aug 11, 2021
1 parent 4e1bfd7 commit ab82ad9
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 12 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_target/src/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ impl fmt::Display for InlineAsmRegOrRegClass {
/// Set of types which can be used with a particular register class.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InlineAsmType {
I1,
I8,
I16,
I32,
Expand All @@ -616,6 +617,7 @@ impl InlineAsmType {

pub fn size(self) -> Size {
Size::from_bytes(match self {
Self::I1 => return Size::from_bits(1),
Self::I8 => 1,
Self::I16 => 2,
Self::I32 => 4,
Expand All @@ -637,6 +639,7 @@ impl InlineAsmType {
impl fmt::Display for InlineAsmType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::I1 => f.write_str("i1"),
Self::I8 => f.write_str("i8"),
Self::I16 => f.write_str("i16"),
Self::I32 => f.write_str("i32"),
Expand Down
54 changes: 43 additions & 11 deletions compiler/rustc_target/src/asm/xtensa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ impl XtensaInlineAsmRegClass {
_arch: InlineAsmArch,
) -> &'static [(InlineAsmType, Option<&'static str>)] {
match self {
Self::reg | Self::breg => types! { _: I8, I16, I32; },
Self::freg => types! { "fp":F32; }, // TODO how does the dfpaccel feature interact F64 types? // _:F64;
Self::reg => types! { _: I8, I16, I32; },
Self::breg => types! { "bool": I1; },
Self::freg => types! { "fp":F32; "dfpaccel":F64; },
}
}
}
Expand Down Expand Up @@ -101,31 +102,57 @@ fn has_gpio_out(
}
}

fn frame_pointer_is_a7(
_arch: InlineAsmArch,
mut has_feature: impl FnMut(&str) -> bool,
_target: &Target,
) -> bool {
has_feature("windowed")
}

fn frame_pointer_a7(
arch: InlineAsmArch,
has_feature: impl FnMut(&str) -> bool,
target: &Target,
) -> Result<(), &'static str> {
if frame_pointer_is_a7(arch, has_feature, target) {
Err("the frame pointer (a7) cannot be used as an operand for inline asm")
} else {
Ok(())
}
}

fn frame_pointer_a15(
arch: InlineAsmArch,
has_feature: impl FnMut(&str) -> bool,
target: &Target,
) -> Result<(), &'static str> {
if !frame_pointer_is_a7(arch, has_feature, target) {
Err("the frame pointer (a15) cannot be used as an operand for inline asm")
} else {
Ok(())
}
}

def_regs! {
Xtensa XtensaInlineAsmReg XtensaInlineAsmRegClass {
a0: reg = ["a0"],
sp: reg = ["sp", "a1"],
a2: reg = ["a2"],
a3: reg = ["a3"],
a4: reg = ["a4"],
a5: reg = ["a5"],
a6: reg = ["a6"],
a7: reg = ["a7"],
a7: reg = ["a7"] % frame_pointer_a7,
a8: reg = ["a8"],
a9: reg = ["a9"],
a10: reg = ["a10"],
a11: reg = ["a11"],
a12: reg = ["a12"],
a13: reg = ["a13"],
a14: reg = ["a14"],
a15: reg = ["a15"],
sar: reg = ["sar"], // TODO what feature enables this, if any?
ddr: reg = ["ddr"], // TODO what feature enables this, if any?
ps: reg = ["ps"], // TODO what feature enables this, if any?
configid0: reg = ["configid0"], // TODO what feature enables this, if any?
configid1: reg = ["configid1"], // TODO what feature enables this, if any?
a15: reg = ["a15"] % frame_pointer_a15,
sar: reg = ["sar"],
configid0: reg = ["configid0"],
configid1: reg = ["configid1"],
lbeg: reg = ["lbeg"] % has_loop,
lend: reg = ["lend"] % has_loop,
lcount: reg = ["lcount"] % has_loop,
Expand All @@ -139,6 +166,7 @@ def_regs! {
m3: reg = ["m3"] % has_mac16,
windowbase: reg = ["windowbase"] % has_windowed,
windowstart: reg = ["windowstart"] % has_windowed,
ddr: reg = ["ddr"] % has_debug,
ibreakenable: reg = ["ibreakenable"] % has_debug,
ibreaka0: reg = ["ibreaka0"] % has_debug,
ibreaka1: reg = ["ibreaka1"] % has_debug,
Expand All @@ -151,6 +179,7 @@ def_regs! {
debugcause: reg = ["debugcause"] % has_debug,
memctl: reg = ["memctl"] % has_memctl,
atomctl: reg = ["atomctl"] % has_atomctl,
ps: reg = ["ps"] % has_exception,
epc1: reg = ["epc1"] % has_exception,
epc2: reg = ["epc2"] % has_exception,
epc3: reg = ["epc3"] % has_exception,
Expand Down Expand Up @@ -229,6 +258,9 @@ def_regs! {
b13: breg = ["b13"] % has_bool,
b14: breg = ["b14"] % has_bool,
b15: breg = ["b15"] % has_bool,

#error = ["a0"] => "a0 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["sp", "a1"] => "sp is used internally by LLVM and cannot be used as an operand for inline asm",
}
}

Expand Down
28 changes: 27 additions & 1 deletion src/doc/unstable-book/src/library-features/asm.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Inline assembly is currently supported on the following architectures:
- Hexagon
- MIPS32r2 and MIPS64r2
- wasm32
- Xtensa
- BPF

## Basic usage
Expand Down Expand Up @@ -461,7 +462,7 @@ options := "options(" option *["," option] [","] ")"
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
```

The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, Xtensa, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.

[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax

Expand Down Expand Up @@ -571,8 +572,25 @@ Here is the list of currently supported register classes:
| PowerPC | `reg_nonzero` | | `r[1-31]` | `b` |
| PowerPC | `freg` | `f[0-31]` | `f` |
| wasm32 | `local` | None\* | `r` |
| Xtensa | `reg` | `a[0-15]` | `r` |
| Xtensa | `freg` | `f[0-15]` | `f` |
| Xtensa | `breg` | `b[0-15]` | `b` |
| BPF | `reg` | `r[0-10]` | `r` |
| BPF | `wreg` | `w[0-10]` | `w` |
| Xtensa | `reg` | `a[0-15]`, `lbeg`, `lend`, `lcount` | `r` |
| Xtensa | `reg` | `sar`, `br`, `litbase`, `scompare1` | `r` |
| Xtensa | `reg` | `acclo`, `acchi`, `m0`, `m1`, `m2`, `m3` | `r` |
| Xtensa | `reg` | `windowbase`, `windowstart`, `ibreakenable`, `memctl`, `atomctl`, `ddr` | `r` |
| Xtensa | `reg` | `ibreaka0`, `ibreaka1`, `dbreaka0`, `dbreaka1`, `dbreakc0`, `dbreakc1`, `configid[0-1]` | `r` |
| Xtensa | `reg` | `epc[1-7]`, `depc`, `eps[2-7]` | `r` |
| Xtensa | `reg` | `excsave[1-7]`, `cpenable`, `interrupt`, `intclear`, `intenable` | `r` |
| Xtensa | `reg` | `ps`, `vecbase`, `exccause`, `debugcause`, `ccount` | `r` |
| Xtensa | `reg` | `prid`, `icount`, `icountlevel`, `excvaddr`, `ccompare[0-2]` | `r` |
| Xtensa | `reg` | `excsave[1-7]`, `cpenable`, `interrupt`, `intclear`, `intenable` | `r` |
| Xtensa | `reg` | `misc[0-3]`, `gpio_out`, `expstate`, `threadptr` | `r` |
| Xtensa | `reg` | `fcr`, `fsr`, `f64r_lo`, `f64r_hi`, `f64s` | `r` |
| Xtensa | `breg` | `b[0-15]` | `b` |
| Xtensa | `freg` | `f[0-15]` | `f` |

> **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.
>
Expand Down Expand Up @@ -620,6 +638,9 @@ 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` |
| Xtensa | `reg` | None | `i8`, `i16`, `i32` |
| Xtensa | `breg` | None | None |
| Xtensa | `freg` | None | `f32` |

> **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).
Expand Down Expand Up @@ -680,6 +701,7 @@ 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]` |
| Xtensa | `a1` | `sp` |

Some registers cannot be used for input or output operands:

Expand All @@ -703,6 +725,7 @@ Some registers cannot be used for input or output operands:
| MIPS | `$ra` | Return address cannot be used as inputs or outputs. |
| 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. |
| Xtensa | `a7` or `a15` | On Xtensa the frame pointer can be either `a7` or `a15` depending on whether the target supports the windowed ABI. The frame pointer cannot be used as an input or output. |
| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |

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
Expand Down Expand Up @@ -760,6 +783,9 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen
| PowerPC | `reg` | None | `0` | None |
| PowerPC | `reg_nonzero` | None | `3` | `b` |
| PowerPC | `freg` | None | `0` | None |
| Xtensa | `reg` | None | `a0` | None |
| Xtensa | `breg` | None | `b0` | None |
| Xtensa | `freg` | None | `f0` | None |

> Notes:
> - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register.
Expand Down

0 comments on commit ab82ad9

Please sign in to comment.