Skip to content

Commit 603a42e

Browse files
committed
Auto merge of rust-lang#84658 - Amanieu:reserved_regs, r=petrochenkov
Be stricter about rejecting LLVM reserved registers in asm! LLVM will silently produce incorrect code if these registers are used as operands. cc `@rust-lang/wg-inline-asm`
2 parents fed59d6 + ea310d9 commit 603a42e

File tree

11 files changed

+66
-27
lines changed

11 files changed

+66
-27
lines changed

compiler/rustc_target/src/asm/aarch64.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ def_regs! {
8585
x15: reg = ["x15", "w15"],
8686
x16: reg = ["x16", "w16"],
8787
x17: reg = ["x17", "w17"],
88-
x18: reg = ["x18", "w18"],
89-
x19: reg = ["x19", "w19"],
9088
x20: reg = ["x20", "w20"],
9189
x21: reg = ["x21", "w21"],
9290
x22: reg = ["x22", "w22"],
@@ -96,7 +94,7 @@ def_regs! {
9694
x26: reg = ["x26", "w26"],
9795
x27: reg = ["x27", "w27"],
9896
x28: reg = ["x28", "w28"],
99-
x30: reg = ["x30", "w30", "lr"],
97+
x30: reg = ["x30", "w30", "lr", "wlr"],
10098
v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"],
10199
v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"],
102100
v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"],
@@ -129,7 +127,11 @@ def_regs! {
129127
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"],
130128
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"],
131129
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"],
132-
#error = ["x29", "fp"] =>
130+
#error = ["x18", "w18"] =>
131+
"x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm",
132+
#error = ["x19", "w19"] =>
133+
"x19 is used internally by LLVM and cannot be used as an operand for inline asm",
134+
#error = ["x29", "w29", "fp", "wfp"] =>
133135
"the frame pointer cannot be used as an operand for inline asm",
134136
#error = ["sp", "wsp"] =>
135137
"the stack pointer cannot be used as an operand for inline asm",

compiler/rustc_target/src/asm/arm.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ def_regs! {
9898
r5: reg, reg_thumb = ["r5", "v2"],
9999
r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7,
100100
r8: reg = ["r8", "v5"],
101-
r9: reg = ["r9", "v6", "rfp"],
102101
r10: reg = ["r10", "sl"],
103102
r11: reg = ["r11", "fp"] % frame_pointer_r11,
104103
r12: reg = ["r12", "ip"],
@@ -185,6 +184,8 @@ def_regs! {
185184
q15: qreg = ["q15"],
186185
#error = ["r6", "v3"] =>
187186
"r6 is used internally by LLVM and cannot be used as an operand for inline asm",
187+
#error = ["r9", "v6", "rfp"] =>
188+
"r9 is used internally by LLVM and cannot be used as an operand for inline asm",
188189
#error = ["r13", "sp"] =>
189190
"the stack pointer cannot be used as an operand for inline asm",
190191
#error = ["r15", "pc"] =>

compiler/rustc_target/src/asm/hexagon.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ def_regs! {
6060
r16: reg = ["r16"],
6161
r17: reg = ["r17"],
6262
r18: reg = ["r18"],
63-
r19: reg = ["r19"],
6463
r20: reg = ["r20"],
6564
r21: reg = ["r21"],
6665
r22: reg = ["r22"],
@@ -70,6 +69,8 @@ def_regs! {
7069
r26: reg = ["r26"],
7170
r27: reg = ["r27"],
7271
r28: reg = ["r28"],
72+
#error = ["r19"] =>
73+
"r19 is used internally by LLVM and cannot be used as an operand for inline asm",
7374
#error = ["r29", "sp"] =>
7475
"the stack pointer cannot be used as an operand for inline asm",
7576
#error = ["r30", "fr"] =>

compiler/rustc_target/src/asm/riscv.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ def_regs! {
6666
x5: reg = ["x5", "t0"],
6767
x6: reg = ["x6", "t1"],
6868
x7: reg = ["x7", "t2"],
69-
x9: reg = ["x9", "s1"],
7069
x10: reg = ["x10", "a0"],
7170
x11: reg = ["x11", "a1"],
7271
x12: reg = ["x12", "a2"],
@@ -121,6 +120,8 @@ def_regs! {
121120
f29: freg = ["f29", "ft9"],
122121
f30: freg = ["f30", "ft10"],
123122
f31: freg = ["f31", "ft11"],
123+
#error = ["x9", "s1"] =>
124+
"s1 is used internally by LLVM and cannot be used as an operand for inline asm",
124125
#error = ["x8", "s0", "fp"] =>
125126
"the frame pointer cannot be used as an operand for inline asm",
126127
#error = ["x2", "sp"] =>

compiler/rustc_target/src/asm/x86.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,41 @@ fn high_byte(
152152
}
153153
}
154154

155+
fn rbx_reserved(
156+
arch: InlineAsmArch,
157+
_has_feature: impl FnMut(&str) -> bool,
158+
_target: &Target,
159+
) -> Result<(), &'static str> {
160+
match arch {
161+
InlineAsmArch::X86 => Ok(()),
162+
InlineAsmArch::X86_64 => {
163+
Err("rbx is used internally by LLVM and cannot be used as an operand for inline asm")
164+
}
165+
_ => unreachable!(),
166+
}
167+
}
168+
169+
fn esi_reserved(
170+
arch: InlineAsmArch,
171+
_has_feature: impl FnMut(&str) -> bool,
172+
_target: &Target,
173+
) -> Result<(), &'static str> {
174+
match arch {
175+
InlineAsmArch::X86 => {
176+
Err("esi is used internally by LLVM and cannot be used as an operand for inline asm")
177+
}
178+
InlineAsmArch::X86_64 => Ok(()),
179+
_ => unreachable!(),
180+
}
181+
}
182+
155183
def_regs! {
156184
X86 X86InlineAsmReg X86InlineAsmRegClass {
157185
ax: reg, reg_abcd = ["ax", "eax", "rax"],
158-
bx: reg, reg_abcd = ["bx", "ebx", "rbx"],
186+
bx: reg, reg_abcd = ["bx", "ebx", "rbx"] % rbx_reserved,
159187
cx: reg, reg_abcd = ["cx", "ecx", "rcx"],
160188
dx: reg, reg_abcd = ["dx", "edx", "rdx"],
161-
si: reg = ["si", "esi", "rsi"],
189+
si: reg = ["si", "esi", "rsi"] % esi_reserved,
162190
di: reg = ["di", "edi", "rdi"],
163191
r8: reg = ["r8", "r8w", "r8d"] % x86_64_only,
164192
r9: reg = ["r9", "r9w", "r9d"] % x86_64_only,

library/std/src/sys/sgx/ext/arch.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@ pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32>
3232
let error;
3333

3434
asm!(
35+
// rbx is reserved by LLVM
36+
"xchg {0}, rbx",
3537
"enclu",
38+
"mov rbx, {0}",
39+
inout(reg) request => _,
3640
inlateout("eax") ENCLU_EGETKEY => error,
37-
in("rbx") request,
3841
in("rcx") out.as_mut_ptr(),
3942
options(nostack),
4043
);
@@ -60,9 +63,12 @@ pub fn ereport(
6063
let mut report = MaybeUninit::uninit();
6164

6265
asm!(
66+
// rbx is reserved by LLVM
67+
"xchg {0}, rbx",
6368
"enclu",
69+
"mov rbx, {0}",
70+
inout(reg) targetinfo => _,
6471
in("eax") ENCLU_EREPORT,
65-
in("rbx") targetinfo,
6672
in("rcx") reportdata,
6773
in("rdx") report.as_mut_ptr(),
6874
options(preserves_flags, nostack),

src/doc/unstable-book/src/library-features/asm.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -535,20 +535,20 @@ Here is the list of currently supported register classes:
535535

536536
| Architecture | Register class | Registers | LLVM constraint code |
537537
| ------------ | -------------- | --------- | -------------------- |
538-
| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` |
538+
| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | `r` |
539539
| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |
540540
| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` |
541-
| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b` | `q` |
541+
| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | `q` |
542542
| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` |
543543
| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` |
544544
| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |
545545
| x86 | `kreg` | `k[1-7]` | `Yk` |
546-
| AArch64 | `reg` | `x[0-28]`, `x30` | `r` |
546+
| AArch64 | `reg` | `x[0-30]` | `r` |
547547
| AArch64 | `vreg` | `v[0-31]` | `w` |
548548
| AArch64 | `vreg_low16` | `v[0-15]` | `x` |
549-
| ARM | `reg` | `r[0-5]` `r7`\*, `r[8-10]`, `r11`\*, `r12`, `r14` | `r` |
549+
| ARM | `reg` | `r[0-12]`, `r14` | `r` |
550550
| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |
551-
| ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` |
551+
| ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` |
552552
| ARM | `sreg` | `s[0-31]` | `t` |
553553
| ARM | `sreg_low16` | `s[0-15]` | `x` |
554554
| ARM | `dreg` | `d[0-31]` | `w` |
@@ -573,9 +573,7 @@ Here is the list of currently supported register classes:
573573
>
574574
> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported.
575575
>
576-
> Note #4: On ARM the frame pointer is either `r7` or `r11` depending on the platform.
577-
>
578-
> Note #5: WebAssembly doesn't have registers, so named registers are not supported.
576+
> Note #4: WebAssembly doesn't have registers, so named registers are not supported.
579577
580578
Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc).
581579

@@ -677,13 +675,14 @@ Some registers cannot be used for input or output operands:
677675
| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |
678676
| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. |
679677
| 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. |
680-
| ARM | `r6` | `r6` is used internally by LLVM as a base pointer and therefore cannot be used as an input or output. |
678+
| 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. |
681679
| x86 | `k0` | This is a constant zero register which can't be modified. |
682680
| x86 | `ip` | This is the program counter, not a real register. |
683681
| x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). |
684682
| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). |
685683
| AArch64 | `xzr` | This is a constant zero register which can't be modified. |
686684
| ARM | `pc` | This is the program counter, not a real register. |
685+
| ARM | `r9` | This is a reserved register on some ARM targets. |
687686
| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
688687
| MIPS | `$1` or `$at` | Reserved for assembler. |
689688
| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. |
@@ -693,9 +692,10 @@ Some registers cannot be used for input or output operands:
693692
| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. |
694693
| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |
695694

696-
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:
697-
- The frame pointer on all architectures.
698-
- `r6` on ARM.
695+
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
696+
- The frame pointer and LLVM base pointer on all architectures.
697+
- `r9` on ARM.
698+
- `x18` on AArch64.
699699

700700
## Template modifiers
701701

src/test/codegen/asm-multiple-options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#[no_mangle]
1111
pub unsafe fn pure(x: i32) {
1212
let y: i32;
13-
asm!("", out("ax") y, in("bx") x, options(pure), options(nomem));
13+
asm!("", out("ax") y, in("cx") x, options(pure), options(nomem));
1414
}
1515

1616
pub static mut VAR: i32 = 0;

src/test/codegen/asm-options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#[no_mangle]
1111
pub unsafe fn pure(x: i32) {
1212
let y: i32;
13-
asm!("", out("ax") y, in("bx") x, options(pure, nomem));
13+
asm!("", out("ax") y, in("cx") x, options(pure, nomem));
1414
}
1515

1616
// CHECK-LABEL: @noreturn

src/test/pretty/asm.pp

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
asm!("{0}", out(reg) a);
2222
asm!("{0}", inout(reg) b);
2323
asm!("{0} {1}", out(reg) _, inlateout(reg) b => _);
24-
asm!("", out("al") _, lateout("rbx") _);
24+
asm!("", out("al") _, lateout("rcx") _);
2525
asm!("inst1\ninst2");
2626
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b);
2727
asm!("inst2 {1}, 24\ninst1 {0}, 42", in(reg) a, out(reg) b);

src/test/pretty/asm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn main() {
1515
asm!("{0}", out(reg) a);
1616
asm!("{name}", name = inout(reg) b);
1717
asm!("{} {}", out(reg) _, inlateout(reg) b => _);
18-
asm!("", out("al") _, lateout("rbx") _);
18+
asm!("", out("al") _, lateout("rcx") _);
1919
asm!("inst1", "inst2");
2020
asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b);
2121
asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b);

0 commit comments

Comments
 (0)