Skip to content

Be stricter about rejecting LLVM reserved registers in asm! #84658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions compiler/rustc_target/src/asm/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ def_regs! {
x15: reg = ["x15", "w15"],
x16: reg = ["x16", "w16"],
x17: reg = ["x17", "w17"],
x18: reg = ["x18", "w18"],
x19: reg = ["x19", "w19"],
x20: reg = ["x20", "w20"],
x21: reg = ["x21", "w21"],
x22: reg = ["x22", "w22"],
Expand All @@ -96,7 +94,7 @@ def_regs! {
x26: reg = ["x26", "w26"],
x27: reg = ["x27", "w27"],
x28: reg = ["x28", "w28"],
x30: reg = ["x30", "w30", "lr"],
x30: reg = ["x30", "w30", "lr", "wlr"],
v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"],
v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"],
v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"],
Expand Down Expand Up @@ -129,7 +127,11 @@ def_regs! {
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"],
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"],
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"],
#error = ["x29", "fp"] =>
#error = ["x18", "w18"] =>
"x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm",
#error = ["x19", "w19"] =>
"x19 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["x29", "w29", "fp", "wfp"] =>
"the frame pointer cannot be used as an operand for inline asm",
#error = ["sp", "wsp"] =>
"the stack pointer cannot be used as an operand for inline asm",
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/asm/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ def_regs! {
r5: reg, reg_thumb = ["r5", "v2"],
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"],
Expand Down Expand Up @@ -185,6 +184,8 @@ def_regs! {
q15: qreg = ["q15"],
#error = ["r6", "v3"] =>
"r6 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["r9", "v6", "rfp"] =>
"r9 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"] =>
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/asm/hexagon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ def_regs! {
r16: reg = ["r16"],
r17: reg = ["r17"],
r18: reg = ["r18"],
r19: reg = ["r19"],
r20: reg = ["r20"],
r21: reg = ["r21"],
r22: reg = ["r22"],
Expand All @@ -70,6 +69,8 @@ def_regs! {
r26: reg = ["r26"],
r27: reg = ["r27"],
r28: reg = ["r28"],
#error = ["r19"] =>
"r19 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["r29", "sp"] =>
"the stack pointer cannot be used as an operand for inline asm",
#error = ["r30", "fr"] =>
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/asm/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def_regs! {
x5: reg = ["x5", "t0"],
x6: reg = ["x6", "t1"],
x7: reg = ["x7", "t2"],
x9: reg = ["x9", "s1"],
x10: reg = ["x10", "a0"],
x11: reg = ["x11", "a1"],
x12: reg = ["x12", "a2"],
Expand Down Expand Up @@ -121,6 +120,8 @@ def_regs! {
f29: freg = ["f29", "ft9"],
f30: freg = ["f30", "ft10"],
f31: freg = ["f31", "ft11"],
#error = ["x9", "s1"] =>
"s1 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["x8", "s0", "fp"] =>
"the frame pointer cannot be used as an operand for inline asm",
#error = ["x2", "sp"] =>
Expand Down
32 changes: 30 additions & 2 deletions compiler/rustc_target/src/asm/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,41 @@ fn high_byte(
}
}

fn rbx_reserved(
arch: InlineAsmArch,
_has_feature: impl FnMut(&str) -> bool,
_target: &Target,
) -> Result<(), &'static str> {
match arch {
InlineAsmArch::X86 => Ok(()),
InlineAsmArch::X86_64 => {
Err("rbx is used internally by LLVM and cannot be used as an operand for inline asm")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a problematic restriction. Many x86 instructions that you might want to run with asm! take rbx as an operand. Same applies to esi below, really.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the RFC:

The intent is that support for asm! should be independent of the rustc back-end used

Given that, I think backend limitations such as these should be handled by rustc prior to passing the assembly to the backend. It shouldn't be visible to the user. Otherwise, asm! would only be able to support the intersection of all inline assembly implementations across all codegen backends.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling this in rustc is actually non-trivial (see this long Zulip discussion), which is also why LLVM doesn't just do it for us.

}
_ => unreachable!(),
}
}

fn esi_reserved(
arch: InlineAsmArch,
_has_feature: impl FnMut(&str) -> bool,
_target: &Target,
) -> Result<(), &'static str> {
match arch {
InlineAsmArch::X86 => {
Err("esi is used internally by LLVM and cannot be used as an operand for inline asm")
}
InlineAsmArch::X86_64 => Ok(()),
_ => unreachable!(),
}
}

def_regs! {
X86 X86InlineAsmReg X86InlineAsmRegClass {
ax: reg, reg_abcd = ["ax", "eax", "rax"],
bx: reg, reg_abcd = ["bx", "ebx", "rbx"],
bx: reg, reg_abcd = ["bx", "ebx", "rbx"] % rbx_reserved,
cx: reg, reg_abcd = ["cx", "ecx", "rcx"],
dx: reg, reg_abcd = ["dx", "edx", "rdx"],
si: reg = ["si", "esi", "rsi"],
si: reg = ["si", "esi", "rsi"] % esi_reserved,
di: reg = ["di", "edi", "rdi"],
r8: reg = ["r8", "r8w", "r8d"] % x86_64_only,
r9: reg = ["r9", "r9w", "r9d"] % x86_64_only,
Expand Down
10 changes: 8 additions & 2 deletions library/std/src/sys/sgx/ext/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32>
let error;

asm!(
// rbx is reserved by LLVM
"xchg {0}, rbx",
"enclu",
"mov rbx, {0}",
inout(reg) request => _,
inlateout("eax") ENCLU_EGETKEY => error,
in("rbx") request,
in("rcx") out.as_mut_ptr(),
options(nostack),
);
Expand All @@ -60,9 +63,12 @@ pub fn ereport(
let mut report = MaybeUninit::uninit();

asm!(
// rbx is reserved by LLVM
"xchg {0}, rbx",
"enclu",
"mov rbx, {0}",
inout(reg) targetinfo => _,
in("eax") ENCLU_EREPORT,
in("rbx") targetinfo,
in("rcx") reportdata,
in("rdx") report.as_mut_ptr(),
options(preserves_flags, nostack),
Expand Down
24 changes: 12 additions & 12 deletions src/doc/unstable-book/src/library-features/asm.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,20 +535,20 @@ Here is the list of currently supported register classes:

| Architecture | Register class | Registers | LLVM constraint code |
| ------------ | -------------- | --------- | -------------------- |
| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` |
| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | `r` |
| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |
| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` |
| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b` | `q` |
| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | `q` |
| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` |
| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` |
| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |
| x86 | `kreg` | `k[1-7]` | `Yk` |
| AArch64 | `reg` | `x[0-28]`, `x30` | `r` |
| AArch64 | `reg` | `x[0-30]` | `r` |
| AArch64 | `vreg` | `v[0-31]` | `w` |
| AArch64 | `vreg_low16` | `v[0-15]` | `x` |
| ARM | `reg` | `r[0-5]` `r7`\*, `r[8-10]`, `r11`\*, `r12`, `r14` | `r` |
| ARM | `reg` | `r[0-12]`, `r14` | `r` |
| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |
| ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` |
| ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` |
| ARM | `sreg` | `s[0-31]` | `t` |
| ARM | `sreg_low16` | `s[0-15]` | `x` |
| ARM | `dreg` | `d[0-31]` | `w` |
Expand All @@ -573,9 +573,7 @@ Here is the list of currently supported register classes:
>
> 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.
>
> Note #5: WebAssembly doesn't have registers, so named registers are not supported.
> Note #4: WebAssembly doesn't have registers, so named registers are not supported.

Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc).

Expand Down Expand Up @@ -677,13 +675,14 @@ Some registers cannot be used for input or output operands:
| 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. |
| 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. |
| 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. |
| 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). |
| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). |
| AArch64 | `xzr` | This is a constant zero register which can't be modified. |
| ARM | `pc` | This is the program counter, not a real register. |
| ARM | `r9` | This is a reserved register on some ARM targets. |
| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
| MIPS | `$1` or `$at` | Reserved for assembler. |
| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. |
Expand All @@ -693,9 +692,10 @@ Some registers cannot be used for input or output operands:
| 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. |

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 on all architectures.
- `r6` on ARM.
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.

## Template modifiers

Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/asm-multiple-options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#[no_mangle]
pub unsafe fn pure(x: i32) {
let y: i32;
asm!("", out("ax") y, in("bx") x, options(pure), options(nomem));
asm!("", out("ax") y, in("cx") x, options(pure), options(nomem));
}

pub static mut VAR: i32 = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/asm-options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#[no_mangle]
pub unsafe fn pure(x: i32) {
let y: i32;
asm!("", out("ax") y, in("bx") x, options(pure, nomem));
asm!("", out("ax") y, in("cx") x, options(pure, nomem));
}

// CHECK-LABEL: @noreturn
Expand Down
2 changes: 1 addition & 1 deletion src/test/pretty/asm.pp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
asm!("{0}", out(reg) a);
asm!("{0}", inout(reg) b);
asm!("{0} {1}", out(reg) _, inlateout(reg) b => _);
asm!("", out("al") _, lateout("rbx") _);
asm!("", out("al") _, lateout("rcx") _);
asm!("inst1\ninst2");
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b);
asm!("inst2 {1}, 24\ninst1 {0}, 42", in(reg) a, out(reg) b);
Expand Down
2 changes: 1 addition & 1 deletion src/test/pretty/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn main() {
asm!("{0}", out(reg) a);
asm!("{name}", name = inout(reg) b);
asm!("{} {}", out(reg) _, inlateout(reg) b => _);
asm!("", out("al") _, lateout("rbx") _);
asm!("", out("al") _, lateout("rcx") _);
asm!("inst1", "inst2");
asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b);
asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b);
Expand Down