1
Fork 0

Be stricter about rejecting LLVM reserved registers in asm!

This commit is contained in:
Amanieu d'Antras 2021-04-28 16:28:59 +01:00
parent 154858c430
commit e6a731eb90
10 changed files with 59 additions and 25 deletions

View file

@ -83,10 +83,8 @@ def_regs! {
x13: reg = ["x13", "w13"], x13: reg = ["x13", "w13"],
x14: reg = ["x14", "w14"], x14: reg = ["x14", "w14"],
x15: reg = ["x15", "w15"], x15: reg = ["x15", "w15"],
x16: reg = ["x16", "w16"],
x17: reg = ["x17", "w17"], x17: reg = ["x17", "w17"],
x18: reg = ["x18", "w18"], x18: reg = ["x18", "w18"],
x19: reg = ["x19", "w19"],
x20: reg = ["x20", "w20"], x20: reg = ["x20", "w20"],
x21: reg = ["x21", "w21"], x21: reg = ["x21", "w21"],
x22: reg = ["x22", "w22"], x22: reg = ["x22", "w22"],
@ -96,7 +94,7 @@ def_regs! {
x26: reg = ["x26", "w26"], x26: reg = ["x26", "w26"],
x27: reg = ["x27", "w27"], x27: reg = ["x27", "w27"],
x28: reg = ["x28", "w28"], 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"], v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"],
v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"], v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"],
v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"], v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"],
@ -129,7 +127,11 @@ def_regs! {
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"], v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"],
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"], v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"],
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"], v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"],
#error = ["x29", "fp"] => #error = ["x16", "w16"] =>
"x16 is used internally by LLVM 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", "the frame pointer cannot be used as an operand for inline asm",
#error = ["sp", "wsp"] => #error = ["sp", "wsp"] =>
"the stack pointer cannot be used as an operand for inline asm", "the stack pointer cannot be used as an operand for inline asm",

View file

@ -98,7 +98,6 @@ def_regs! {
r5: reg, reg_thumb = ["r5", "v2"], r5: reg, reg_thumb = ["r5", "v2"],
r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7, r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7,
r8: reg = ["r8", "v5"], r8: reg = ["r8", "v5"],
r9: reg = ["r9", "v6", "rfp"],
r10: reg = ["r10", "sl"], r10: reg = ["r10", "sl"],
r11: reg = ["r11", "fp"] % frame_pointer_r11, r11: reg = ["r11", "fp"] % frame_pointer_r11,
r12: reg = ["r12", "ip"], r12: reg = ["r12", "ip"],
@ -185,6 +184,8 @@ def_regs! {
q15: qreg = ["q15"], q15: qreg = ["q15"],
#error = ["r6", "v3"] => #error = ["r6", "v3"] =>
"r6 is used internally by LLVM and cannot be used as an operand for inline asm", "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"] => #error = ["r13", "sp"] =>
"the stack pointer cannot be used as an operand for inline asm", "the stack pointer cannot be used as an operand for inline asm",
#error = ["r15", "pc"] => #error = ["r15", "pc"] =>

View file

@ -60,7 +60,6 @@ def_regs! {
r16: reg = ["r16"], r16: reg = ["r16"],
r17: reg = ["r17"], r17: reg = ["r17"],
r18: reg = ["r18"], r18: reg = ["r18"],
r19: reg = ["r19"],
r20: reg = ["r20"], r20: reg = ["r20"],
r21: reg = ["r21"], r21: reg = ["r21"],
r22: reg = ["r22"], r22: reg = ["r22"],
@ -70,6 +69,8 @@ def_regs! {
r26: reg = ["r26"], r26: reg = ["r26"],
r27: reg = ["r27"], r27: reg = ["r27"],
r28: reg = ["r28"], r28: reg = ["r28"],
#error = ["r19"] =>
"r19 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["r29", "sp"] => #error = ["r29", "sp"] =>
"the stack pointer cannot be used as an operand for inline asm", "the stack pointer cannot be used as an operand for inline asm",
#error = ["r30", "fr"] => #error = ["r30", "fr"] =>

View file

@ -66,7 +66,6 @@ def_regs! {
x5: reg = ["x5", "t0"], x5: reg = ["x5", "t0"],
x6: reg = ["x6", "t1"], x6: reg = ["x6", "t1"],
x7: reg = ["x7", "t2"], x7: reg = ["x7", "t2"],
x9: reg = ["x9", "s1"],
x10: reg = ["x10", "a0"], x10: reg = ["x10", "a0"],
x11: reg = ["x11", "a1"], x11: reg = ["x11", "a1"],
x12: reg = ["x12", "a2"], x12: reg = ["x12", "a2"],
@ -121,6 +120,8 @@ def_regs! {
f29: freg = ["f29", "ft9"], f29: freg = ["f29", "ft9"],
f30: freg = ["f30", "ft10"], f30: freg = ["f30", "ft10"],
f31: freg = ["f31", "ft11"], 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"] => #error = ["x8", "s0", "fp"] =>
"the frame pointer cannot be used as an operand for inline asm", "the frame pointer cannot be used as an operand for inline asm",
#error = ["x2", "sp"] => #error = ["x2", "sp"] =>

View file

@ -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")
}
_ => 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! { def_regs! {
X86 X86InlineAsmReg X86InlineAsmRegClass { X86 X86InlineAsmReg X86InlineAsmRegClass {
ax: reg, reg_abcd = ["ax", "eax", "rax"], 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"], cx: reg, reg_abcd = ["cx", "ecx", "rcx"],
dx: reg, reg_abcd = ["dx", "edx", "rdx"], dx: reg, reg_abcd = ["dx", "edx", "rdx"],
si: reg = ["si", "esi", "rsi"], si: reg = ["si", "esi", "rsi"] % esi_reserved,
di: reg = ["di", "edi", "rdi"], di: reg = ["di", "edi", "rdi"],
r8: reg = ["r8", "r8w", "r8d"] % x86_64_only, r8: reg = ["r8", "r8w", "r8d"] % x86_64_only,
r9: reg = ["r9", "r9w", "r9d"] % x86_64_only, r9: reg = ["r9", "r9w", "r9d"] % x86_64_only,

View file

@ -535,20 +535,20 @@ Here is the list of currently supported register classes:
| Architecture | Register class | Registers | LLVM constraint code | | 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 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |
| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `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 | `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 | `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 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |
| x86 | `kreg` | `k[1-7]` | `Yk` | | 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` | `v[0-31]` | `w` |
| AArch64 | `vreg_low16` | `v[0-15]` | `x` | | 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 (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` | `s[0-31]` | `t` |
| ARM | `sreg_low16` | `s[0-15]` | `x` | | ARM | `sreg_low16` | `s[0-15]` | `x` |
| ARM | `dreg` | `d[0-31]` | `w` | | ARM | `dreg` | `d[0-31]` | `w` |
@ -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 #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 #4: WebAssembly doesn't have registers, so named registers are not supported.
>
> Note #5: 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). Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc).
@ -677,13 +675,15 @@ 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 | `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) | 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 | `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 | `k0` | This is a constant zero register which can't be modified. |
| x86 | `ip` | This is the program counter, not a real register. | | 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 | `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). | | 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. | | AArch64 | `xzr` | This is a constant zero register which can't be modified. |
| AArch64 | `x16` | This is used internally by LLVM for speculative load hardening. |
| ARM | `pc` | This is the program counter, not a real register. | | 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 | `$0` or `$zero` | This is a constant zero register which can't be modified. |
| MIPS | `$1` or `$at` | Reserved for assembler. | | MIPS | `$1` or `$at` | Reserved for assembler. |
| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. |
@ -693,9 +693,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. | | 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. | | 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: 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 on all architectures. - The frame pointer and LLVM base pointer on all architectures.
- `r6` on ARM. - `x16` on AArch64.
- `r6` and `r9` on ARM.
## Template modifiers ## Template modifiers

View file

@ -10,7 +10,7 @@
#[no_mangle] #[no_mangle]
pub unsafe fn pure(x: i32) { pub unsafe fn pure(x: i32) {
let y: 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; pub static mut VAR: i32 = 0;

View file

@ -10,7 +10,7 @@
#[no_mangle] #[no_mangle]
pub unsafe fn pure(x: i32) { pub unsafe fn pure(x: i32) {
let y: 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 // CHECK-LABEL: @noreturn

View file

@ -21,7 +21,7 @@ pub fn main() {
asm!("{0}", out(reg) a); asm!("{0}", out(reg) a);
asm!("{0}", inout(reg) b); asm!("{0}", inout(reg) b);
asm!("{0} {1}", out(reg) _, inlateout(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\ninst2");
asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b); 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); asm!("inst2 {1}, 24\ninst1 {0}, 42", in(reg) a, out(reg) b);

View file

@ -15,7 +15,7 @@ pub fn main() {
asm!("{0}", out(reg) a); asm!("{0}", out(reg) a);
asm!("{name}", name = inout(reg) b); asm!("{name}", name = inout(reg) b);
asm!("{} {}", out(reg) _, inlateout(reg) b => _); asm!("{} {}", out(reg) _, inlateout(reg) b => _);
asm!("", out("al") _, lateout("rbx") _); asm!("", out("al") _, lateout("rcx") _);
asm!("inst1", "inst2"); asm!("inst1", "inst2");
asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b); asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b);
asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b); asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b);