mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #87581 - Amanieu:asm_clobber_abi, r=nagisa
Add support for clobber_abi to asm! This PR adds the `clobber_abi` feature that was proposed in #81092. Fixes #81092 cc `@rust-lang/wg-inline-asm` r? `@nagisa`
This commit is contained in:
commit
85109e257a
@ -2027,6 +2027,7 @@ pub enum InlineAsmOperand {
|
||||
pub struct InlineAsm {
|
||||
pub template: Vec<InlineAsmTemplatePiece>,
|
||||
pub operands: Vec<(InlineAsmOperand, Span)>,
|
||||
pub clobber_abi: Option<(Symbol, Span)>,
|
||||
pub options: InlineAsmOptions,
|
||||
pub line_spans: Vec<Span>,
|
||||
}
|
||||
|
@ -27,11 +27,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
.emit();
|
||||
}
|
||||
|
||||
let mut clobber_abi = None;
|
||||
if let Some(asm_arch) = asm_arch {
|
||||
if let Some((abi_name, abi_span)) = asm.clobber_abi {
|
||||
match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, abi_name) {
|
||||
Ok(abi) => clobber_abi = Some((abi, abi_span)),
|
||||
Err(&[]) => {
|
||||
self.sess
|
||||
.struct_span_err(
|
||||
abi_span,
|
||||
"`clobber_abi` is not supported on this target",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
Err(supported_abis) => {
|
||||
let mut err =
|
||||
self.sess.struct_span_err(abi_span, "invalid ABI for `clobber_abi`");
|
||||
let mut abis = format!("`{}`", supported_abis[0]);
|
||||
for m in &supported_abis[1..] {
|
||||
let _ = write!(abis, ", `{}`", m);
|
||||
}
|
||||
err.note(&format!(
|
||||
"the following ABIs are supported on this target: {}",
|
||||
abis
|
||||
));
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lower operands to HIR. We use dummy register classes if an error
|
||||
// occurs during lowering because we still need to be able to produce a
|
||||
// valid HIR.
|
||||
let sess = self.sess;
|
||||
let operands: Vec<_> = asm
|
||||
let mut operands: Vec<_> = asm
|
||||
.operands
|
||||
.iter()
|
||||
.map(|(op, op_sp)| {
|
||||
@ -336,6 +366,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
// If a clobber_abi is specified, add the necessary clobbers to the
|
||||
// operands list.
|
||||
if let Some((abi, abi_span)) = clobber_abi {
|
||||
for &clobber in abi.clobbered_regs() {
|
||||
let mut output_used = false;
|
||||
clobber.overlapping_regs(|reg| {
|
||||
if used_output_regs.contains_key(®) {
|
||||
output_used = true;
|
||||
}
|
||||
});
|
||||
|
||||
if !output_used {
|
||||
operands.push((
|
||||
hir::InlineAsmOperand::Out {
|
||||
reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
|
||||
late: true,
|
||||
expr: None,
|
||||
},
|
||||
abi_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let operands = self.arena.alloc_from_iter(operands);
|
||||
let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
|
||||
let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
|
||||
|
@ -2186,11 +2186,15 @@ impl<'a> State<'a> {
|
||||
enum AsmArg<'a> {
|
||||
Template(String),
|
||||
Operand(&'a InlineAsmOperand),
|
||||
ClobberAbi(Symbol),
|
||||
Options(InlineAsmOptions),
|
||||
}
|
||||
|
||||
let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
|
||||
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
|
||||
if let Some((abi, _)) = asm.clobber_abi {
|
||||
args.push(AsmArg::ClobberAbi(abi));
|
||||
}
|
||||
if !asm.options.is_empty() {
|
||||
args.push(AsmArg::Options(asm.options));
|
||||
}
|
||||
@ -2257,6 +2261,12 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
AsmArg::ClobberAbi(abi) => {
|
||||
s.word("clobber_abi");
|
||||
s.popen();
|
||||
s.print_symbol(*abi, ast::StrStyle::Cooked);
|
||||
s.pclose();
|
||||
}
|
||||
AsmArg::Options(opts) => {
|
||||
s.word("options");
|
||||
s.popen();
|
||||
|
@ -19,6 +19,7 @@ struct AsmArgs {
|
||||
operands: Vec<(ast::InlineAsmOperand, Span)>,
|
||||
named_args: FxHashMap<Symbol, usize>,
|
||||
reg_args: FxHashSet<usize>,
|
||||
clobber_abi: Option<(Symbol, Span)>,
|
||||
options: ast::InlineAsmOptions,
|
||||
options_spans: Vec<Span>,
|
||||
}
|
||||
@ -63,6 +64,7 @@ fn parse_args<'a>(
|
||||
operands: vec![],
|
||||
named_args: FxHashMap::default(),
|
||||
reg_args: FxHashSet::default(),
|
||||
clobber_abi: None,
|
||||
options: ast::InlineAsmOptions::empty(),
|
||||
options_spans: vec![],
|
||||
};
|
||||
@ -85,6 +87,13 @@ fn parse_args<'a>(
|
||||
break;
|
||||
} // accept trailing commas
|
||||
|
||||
// Parse clobber_abi
|
||||
if p.eat_keyword(sym::clobber_abi) {
|
||||
parse_clobber_abi(&mut p, &mut args)?;
|
||||
allow_templates = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse options
|
||||
if p.eat_keyword(sym::options) {
|
||||
parse_options(&mut p, &mut args, is_global_asm)?;
|
||||
@ -160,7 +169,11 @@ fn parse_args<'a>(
|
||||
ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {}
|
||||
ast::ExprKind::MacCall(..) => {}
|
||||
_ => {
|
||||
let errstr = "expected operand, options, or additional template string";
|
||||
let errstr = if is_global_asm {
|
||||
"expected operand, options, or additional template string"
|
||||
} else {
|
||||
"expected operand, clobber_abi, options, or additional template string"
|
||||
};
|
||||
let mut err = ecx.struct_span_err(template.span, errstr);
|
||||
err.span_label(template.span, errstr);
|
||||
return Err(err);
|
||||
@ -177,13 +190,19 @@ fn parse_args<'a>(
|
||||
let slot = args.operands.len();
|
||||
args.operands.push((op, span));
|
||||
|
||||
// Validate the order of named, positional & explicit register operands and options. We do
|
||||
// this at the end once we have the full span of the argument available.
|
||||
// Validate the order of named, positional & explicit register operands and
|
||||
// clobber_abi/options. We do this at the end once we have the full span
|
||||
// of the argument available.
|
||||
if !args.options_spans.is_empty() {
|
||||
ecx.struct_span_err(span, "arguments are not allowed after options")
|
||||
.span_labels(args.options_spans.clone(), "previous options")
|
||||
.span_label(span, "argument")
|
||||
.emit();
|
||||
} else if let Some((_, abi_span)) = args.clobber_abi {
|
||||
ecx.struct_span_err(span, "arguments are not allowed after clobber_abi")
|
||||
.span_label(abi_span, "clobber_abi")
|
||||
.span_label(span, "argument")
|
||||
.emit();
|
||||
}
|
||||
if explicit_reg {
|
||||
if name.is_some() {
|
||||
@ -256,16 +275,23 @@ fn parse_args<'a>(
|
||||
|
||||
let mut have_real_output = false;
|
||||
let mut outputs_sp = vec![];
|
||||
let mut regclass_outputs = vec![];
|
||||
for (op, op_sp) in &args.operands {
|
||||
match op {
|
||||
ast::InlineAsmOperand::Out { expr, .. }
|
||||
| ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => {
|
||||
ast::InlineAsmOperand::Out { reg, expr, .. }
|
||||
| ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
|
||||
outputs_sp.push(*op_sp);
|
||||
have_real_output |= expr.is_some();
|
||||
if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
|
||||
regclass_outputs.push(*op_sp);
|
||||
}
|
||||
}
|
||||
ast::InlineAsmOperand::InOut { .. } => {
|
||||
ast::InlineAsmOperand::InOut { reg, .. } => {
|
||||
outputs_sp.push(*op_sp);
|
||||
have_real_output = true;
|
||||
if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
|
||||
regclass_outputs.push(*op_sp);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -273,7 +299,7 @@ fn parse_args<'a>(
|
||||
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
|
||||
ecx.struct_span_err(
|
||||
args.options_spans.clone(),
|
||||
"asm with `pure` option must have at least one output",
|
||||
"asm with the `pure` option must have at least one output",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
@ -284,6 +310,24 @@ fn parse_args<'a>(
|
||||
// Bail out now since this is likely to confuse MIR
|
||||
return Err(err);
|
||||
}
|
||||
if let Some((_, abi_span)) = args.clobber_abi {
|
||||
if is_global_asm {
|
||||
let err =
|
||||
ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`");
|
||||
|
||||
// Bail out now since this is likely to confuse later stages
|
||||
return Err(err);
|
||||
}
|
||||
if !regclass_outputs.is_empty() {
|
||||
ecx.struct_span_err(
|
||||
regclass_outputs.clone(),
|
||||
"asm with `clobber_abi` must specify explicit registers for outputs",
|
||||
)
|
||||
.span_label(abi_span, "clobber_abi")
|
||||
.span_labels(regclass_outputs, "generic outputs")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
@ -375,6 +419,49 @@ fn parse_options<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_clobber_abi<'a>(
|
||||
p: &mut Parser<'a>,
|
||||
args: &mut AsmArgs,
|
||||
) -> Result<(), DiagnosticBuilder<'a>> {
|
||||
let span_start = p.prev_token.span;
|
||||
|
||||
p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
|
||||
|
||||
let clobber_abi = match p.parse_str_lit() {
|
||||
Ok(str_lit) => str_lit.symbol_unescaped,
|
||||
Err(opt_lit) => {
|
||||
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
|
||||
let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
|
||||
err.span_label(span, "not a string literal");
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
|
||||
|
||||
let new_span = span_start.to(p.prev_token.span);
|
||||
|
||||
if let Some((_, prev_span)) = args.clobber_abi {
|
||||
let mut err = p
|
||||
.sess
|
||||
.span_diagnostic
|
||||
.struct_span_err(new_span, "clobber_abi specified multiple times");
|
||||
err.span_label(prev_span, "clobber_abi previously specified here");
|
||||
return Err(err);
|
||||
} else if !args.options_spans.is_empty() {
|
||||
let mut err = p
|
||||
.sess
|
||||
.span_diagnostic
|
||||
.struct_span_err(new_span, "clobber_abi is not allowed after options");
|
||||
err.span_labels(args.options_spans.clone(), "options");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
args.clobber_abi = Some((clobber_abi, new_span));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_reg<'a>(
|
||||
p: &mut Parser<'a>,
|
||||
explicit_reg: &mut bool,
|
||||
@ -730,7 +817,13 @@ fn expand_preparsed_asm(
|
||||
}
|
||||
}
|
||||
|
||||
Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
|
||||
Some(ast::InlineAsm {
|
||||
template,
|
||||
operands: args.operands,
|
||||
clobber_abi: args.clobber_abi,
|
||||
options: args.options,
|
||||
line_spans,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expand_asm<'cx>(
|
||||
|
@ -302,7 +302,14 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
"~{flags}".to_string(),
|
||||
]);
|
||||
}
|
||||
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {}
|
||||
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
|
||||
constraints.extend_from_slice(&[
|
||||
"~{vtype}".to_string(),
|
||||
"~{vl}".to_string(),
|
||||
"~{vxsat}".to_string(),
|
||||
"~{vxrm}".to_string(),
|
||||
]);
|
||||
}
|
||||
InlineAsmArch::Nvptx64 => {}
|
||||
InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {}
|
||||
InlineAsmArch::Hexagon => {}
|
||||
|
@ -378,6 +378,7 @@ symbols! {
|
||||
char,
|
||||
client,
|
||||
clippy,
|
||||
clobber_abi,
|
||||
clone,
|
||||
clone_closures,
|
||||
clone_from,
|
||||
|
@ -712,3 +712,185 @@ pub fn allocatable_registers(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Encodable,
|
||||
Decodable,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Hash,
|
||||
HashStable_Generic
|
||||
)]
|
||||
pub enum InlineAsmClobberAbi {
|
||||
X86,
|
||||
X86_64Win,
|
||||
X86_64SysV,
|
||||
Arm,
|
||||
AArch64,
|
||||
RiscV,
|
||||
}
|
||||
|
||||
impl InlineAsmClobberAbi {
|
||||
/// Parses a clobber ABI for the given target, or returns a list of supported
|
||||
/// clobber ABIs for the target.
|
||||
pub fn parse(
|
||||
arch: InlineAsmArch,
|
||||
target: &Target,
|
||||
name: Symbol,
|
||||
) -> Result<Self, &'static [&'static str]> {
|
||||
let name = &*name.as_str();
|
||||
match arch {
|
||||
InlineAsmArch::X86 => match name {
|
||||
"C" | "system" | "efiapi" | "cdecl" | "stdcall" | "fastcall" => {
|
||||
Ok(InlineAsmClobberAbi::X86)
|
||||
}
|
||||
_ => Err(&["C", "system", "efiapi", "cdecl", "stdcall", "fastcall"]),
|
||||
},
|
||||
InlineAsmArch::X86_64 => match name {
|
||||
"C" | "system" if !target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64SysV),
|
||||
"C" | "system" if target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64Win),
|
||||
"win64" | "efiapi" => Ok(InlineAsmClobberAbi::X86_64Win),
|
||||
"sysv64" => Ok(InlineAsmClobberAbi::X86_64SysV),
|
||||
_ => Err(&["C", "system", "efiapi", "win64", "sysv64"]),
|
||||
},
|
||||
InlineAsmArch::Arm => match name {
|
||||
"C" | "system" | "efiapi" | "aapcs" => Ok(InlineAsmClobberAbi::Arm),
|
||||
_ => Err(&["C", "system", "efiapi", "aapcs"]),
|
||||
},
|
||||
InlineAsmArch::AArch64 => match name {
|
||||
"C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::AArch64),
|
||||
_ => Err(&["C", "system", "efiapi"]),
|
||||
},
|
||||
InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name {
|
||||
"C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV),
|
||||
_ => Err(&["C", "system", "efiapi"]),
|
||||
},
|
||||
_ => Err(&[]),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the set of registers which are clobbered by this ABI.
|
||||
pub fn clobbered_regs(self) -> &'static [InlineAsmReg] {
|
||||
macro_rules! clobbered_regs {
|
||||
($arch:ident $arch_reg:ident {
|
||||
$(
|
||||
$reg:ident,
|
||||
)*
|
||||
}) => {
|
||||
&[
|
||||
$(InlineAsmReg::$arch($arch_reg::$reg),)*
|
||||
]
|
||||
};
|
||||
}
|
||||
match self {
|
||||
InlineAsmClobberAbi::X86 => clobbered_regs! {
|
||||
X86 X86InlineAsmReg {
|
||||
ax, cx, dx,
|
||||
|
||||
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
|
||||
|
||||
k1, k2, k3, k4, k5, k6, k7,
|
||||
|
||||
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
|
||||
st0, st1, st2, st3, st4, st5, st6, st7,
|
||||
}
|
||||
},
|
||||
InlineAsmClobberAbi::X86_64SysV => clobbered_regs! {
|
||||
X86 X86InlineAsmReg {
|
||||
ax, cx, dx, si, di, r8, r9, r10, r11,
|
||||
|
||||
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
|
||||
xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
|
||||
zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
|
||||
zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
|
||||
|
||||
k1, k2, k3, k4, k5, k6, k7,
|
||||
|
||||
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
|
||||
st0, st1, st2, st3, st4, st5, st6, st7,
|
||||
}
|
||||
},
|
||||
InlineAsmClobberAbi::X86_64Win => clobbered_regs! {
|
||||
X86 X86InlineAsmReg {
|
||||
// rdi and rsi are callee-saved on windows
|
||||
ax, cx, dx, r8, r9, r10, r11,
|
||||
|
||||
// xmm6-xmm15 are callee-saved on windows, but we need to
|
||||
// mark them as clobbered anyways because the upper portions
|
||||
// of ymm6-ymm15 are volatile.
|
||||
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
|
||||
xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
|
||||
zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
|
||||
zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
|
||||
|
||||
k1, k2, k3, k4, k5, k6, k7,
|
||||
|
||||
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
|
||||
st0, st1, st2, st3, st4, st5, st6, st7,
|
||||
}
|
||||
},
|
||||
InlineAsmClobberAbi::AArch64 => clobbered_regs! {
|
||||
AArch64 AArch64InlineAsmReg {
|
||||
x0, x1, x2, x3, x4, x5, x6, x7,
|
||||
x8, x9, x10, x11, x12, x13, x14, x15,
|
||||
// x18 is platform-reserved or temporary, but we exclude it
|
||||
// here since it is a reserved register.
|
||||
x16, x17, x30,
|
||||
|
||||
// Technically the low 64 bits of v8-v15 are preserved, but
|
||||
// we have no way of expressing this using clobbers.
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10, v11, v12, v13, v14, v15,
|
||||
v16, v17, v18, v19, v20, v21, v22, v23,
|
||||
v24, v25, v26, v27, v28, v29, v30, v31,
|
||||
|
||||
p0, p1, p2, p3, p4, p5, p6, p7,
|
||||
p8, p9, p10, p11, p12, p13, p14, p15,
|
||||
ffr,
|
||||
|
||||
}
|
||||
},
|
||||
InlineAsmClobberAbi::Arm => clobbered_regs! {
|
||||
Arm ArmInlineAsmReg {
|
||||
// r9 is platform-reserved and is treated as callee-saved.
|
||||
r0, r1, r2, r3, r12, r14,
|
||||
|
||||
// The finest-grained register variant is used here so that
|
||||
// partial uses of larger registers are properly handled.
|
||||
s0, s1, s2, s3, s4, s5, s6, s7,
|
||||
s8, s9, s10, s11, s12, s13, s14, s15,
|
||||
// s16-s31 are callee-saved
|
||||
d16, d17, d18, d19, d20, d21, d22, d23,
|
||||
d24, d25, d26, d27, d28, d29, d30, d31,
|
||||
}
|
||||
},
|
||||
InlineAsmClobberAbi::RiscV => clobbered_regs! {
|
||||
RiscV RiscVInlineAsmReg {
|
||||
// ra
|
||||
x1,
|
||||
// t0-t2
|
||||
x5, x6, x7,
|
||||
// a0-a7
|
||||
x10, x11, x12, x13, x14, x15, x16, x17,
|
||||
// t3-t6
|
||||
x28, x29, x30, x31,
|
||||
// ft0-ft7
|
||||
f0, f1, f2, f3, f4, f5, f6, f7,
|
||||
// fa0-fa7
|
||||
f10, f11, f12, f13, f14, f15, f16, f17,
|
||||
// ft8-ft11
|
||||
f28, f29, f30, f31,
|
||||
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10, v11, v12, v13, v14, v15,
|
||||
v16, v17, v18, v19, v20, v21, v22, v23,
|
||||
v24, v25, v26, v27, v28, v29, v30, v31,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -290,38 +290,33 @@ unsafe {
|
||||
assert_eq!(x, 4 * 6);
|
||||
```
|
||||
|
||||
## Symbol operands
|
||||
## Symbol operands and ABI clobbers
|
||||
|
||||
A special operand type, `sym`, allows you to use the symbol name of a `fn` or `static` in inline assembly code.
|
||||
This allows you to call a function or access a global variable without needing to keep its address in a register.
|
||||
|
||||
```rust,allow_fail
|
||||
#![feature(asm)]
|
||||
extern "C" fn foo(arg: i32) {
|
||||
extern "C" fn foo(arg: i32) -> i32 {
|
||||
println!("arg = {}", arg);
|
||||
arg * 2
|
||||
}
|
||||
|
||||
fn call_foo(arg: i32) {
|
||||
fn call_foo(arg: i32) -> i32 {
|
||||
unsafe {
|
||||
let result;
|
||||
asm!(
|
||||
"call {}",
|
||||
sym foo,
|
||||
// 1st argument in rdi, which is caller-saved
|
||||
inout("rdi") arg => _,
|
||||
// All caller-saved registers must be marked as clobbered
|
||||
out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
|
||||
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
|
||||
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
|
||||
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
|
||||
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
|
||||
out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
|
||||
// Also mark AVX-512 registers as clobbered. This is accepted by the
|
||||
// compiler even if AVX-512 is not enabled on the current target.
|
||||
out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
|
||||
out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _,
|
||||
out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
|
||||
out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
|
||||
)
|
||||
// 1st argument in rdi
|
||||
in("rdi") arg,
|
||||
// Return value in rax
|
||||
out("rax") result,
|
||||
// Mark all registers which are not preserved by the "C" calling
|
||||
// convention as clobbered.
|
||||
clobber_abi("C"),
|
||||
);
|
||||
result
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -329,6 +324,8 @@ fn call_foo(arg: i32) {
|
||||
Note that the `fn` or `static` item does not need to be public or `#[no_mangle]`:
|
||||
the compiler will automatically insert the appropriate mangled symbol name into the assembly code.
|
||||
|
||||
By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`](#abi-clobbers) argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered.
|
||||
|
||||
## Register template modifiers
|
||||
|
||||
In some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a "view" over a subset of the register (e.g. the low 32 bits of a 64-bit register).
|
||||
@ -456,12 +453,25 @@ reg_spec := <register class> / "<explicit register>"
|
||||
operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
|
||||
reg_operand := dir_spec "(" reg_spec ")" operand_expr
|
||||
operand := reg_operand / "const" const_expr / "sym" path
|
||||
clobber_abi := "clobber_abi(" <abi> ")"
|
||||
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
|
||||
options := "options(" option *["," option] [","] ")"
|
||||
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
|
||||
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," clobber_abi] ["," 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.
|
||||
Inline assembly is currently supported on the following architectures:
|
||||
- x86 and x86-64
|
||||
- ARM
|
||||
- AArch64
|
||||
- RISC-V
|
||||
- NVPTX
|
||||
- PowerPC
|
||||
- Hexagon
|
||||
- MIPS32r2 and MIPS64r2
|
||||
- wasm32
|
||||
- BPF
|
||||
|
||||
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
|
||||
|
||||
@ -780,6 +790,24 @@ As stated in the previous section, passing an input value smaller than the regis
|
||||
|
||||
[llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers
|
||||
|
||||
## ABI clobbers
|
||||
|
||||
The `clobber_abi` keyword can be used to apply a default set of clobbers to an `asm` block. This will automatically insert the necessary clobber constraints as needed for calling a function with a particular calling convention: if the calling convention does not fully preserve the value of a register across a call then a `lateout("reg") _` is implicitly added to the operands list.
|
||||
|
||||
Generic register class outputs are disallowed by the compiler when `clobber_abi` is used: all outputs must specify an explicit register. Explicit register outputs have precedence over the implicit clobbers inserted by `clobber_abi`: a clobber will only be inserted for a register if that register is not used as an output.
|
||||
The following ABIs can be used with `clobber_abi`:
|
||||
|
||||
| Architecture | ABI name | Clobbered registers |
|
||||
| ------------ | -------- | ------------------- |
|
||||
| x86-32 | `"C"`, `"system"`, `"efiapi"`, `"cdecl"`, `"stdcall"`, `"fastcall"` | `ax`, `cx`, `dx`, `xmm[0-7]`, `mm[0-7]`, `st([0-7])` |
|
||||
| x86-64 | `"C"`, `"system"` (on Windows), `"efiapi"`, `"win64"` | `ax`, `cx`, `dx`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `st([0-7])` |
|
||||
| x86-64 | `"C"`, `"system"` (on non-Windows), `"sysv64"` | `ax`, `cx`, `dx`, `si`, `di`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `st([0-7])` |
|
||||
| AArch64 | `"C"`, `"system"`, `"efiapi"` | `x[0-17]`, `x30`, `v[0-31]`, `p[0-15]`, `ffr` |
|
||||
| ARM | `"C"`, `"system"`, `"efiapi"`, `"aapcs"` | `r[0-3]`, `r12`, `r14`, `s[0-15]`, `d[0-7]`, `d[16-31]` |
|
||||
| RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` |
|
||||
|
||||
The list of clobbered registers for each ABI is updated in rustc as architectures gain new registers: this ensures that `asm` clobbers will continue to be correct when LLVM starts using these new registers in its generated code.
|
||||
|
||||
## Options
|
||||
|
||||
Flags are used to further influence the behavior of the inline assembly block.
|
||||
@ -842,6 +870,7 @@ The compiler performs some additional checks on options:
|
||||
- Floating-point status (`FPSR` register).
|
||||
- RISC-V
|
||||
- Floating-point exception flags in `fcsr` (`fflags`).
|
||||
- Vector extension state (`vtype`, `vl`, `vcsr`).
|
||||
- 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.
|
||||
|
35
src/test/codegen/asm-clobber_abi.rs
Normal file
35
src/test/codegen/asm-clobber_abi.rs
Normal file
@ -0,0 +1,35 @@
|
||||
// compile-flags: -O
|
||||
// only-x86_64
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
#![feature(asm)]
|
||||
|
||||
// CHECK-LABEL: @clobber_sysv64
|
||||
// CHECK: ={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
|
||||
#[no_mangle]
|
||||
pub unsafe fn clobber_sysv64() {
|
||||
asm!("", clobber_abi("sysv64"));
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @clobber_win64
|
||||
// CHECK: ={ax},={cx},={dx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
|
||||
#[no_mangle]
|
||||
pub unsafe fn clobber_win64() {
|
||||
asm!("", clobber_abi("win64"));
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @clobber_sysv64
|
||||
// CHECK: =&{dx},={ax},={cx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
|
||||
#[no_mangle]
|
||||
pub unsafe fn clobber_sysv64_edx() {
|
||||
let foo: i32;
|
||||
asm!("", out("edx") foo, clobber_abi("sysv64"));
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @clobber_win64
|
||||
// CHECK: =&{dx},={ax},={cx},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{dirflag},~{fpsr},~{flags},~{memory}
|
||||
#[no_mangle]
|
||||
pub unsafe fn clobber_win64_edx() {
|
||||
let foo: i32;
|
||||
asm!("", out("edx") foo, clobber_abi("win64"));
|
||||
}
|
@ -9,12 +9,20 @@ fn main() {
|
||||
//~^ ERROR the `nomem` and `readonly` options are mutually exclusive
|
||||
asm!("", options(pure, nomem, noreturn));
|
||||
//~^ ERROR the `pure` and `noreturn` options are mutually exclusive
|
||||
//~^^ ERROR asm with `pure` option must have at least one output
|
||||
//~^^ ERROR asm with the `pure` option must have at least one output
|
||||
asm!("{}", in(reg) foo, options(pure, nomem));
|
||||
//~^ ERROR asm with `pure` option must have at least one output
|
||||
//~^ ERROR asm with the `pure` option must have at least one output
|
||||
asm!("{}", out(reg) foo, options(noreturn));
|
||||
//~^ ERROR asm outputs are not allowed with the `noreturn` option
|
||||
}
|
||||
|
||||
unsafe {
|
||||
asm!("", clobber_abi("foo"));
|
||||
//~^ ERROR invalid ABI for `clobber_abi`
|
||||
asm!("{}", out(reg) foo, clobber_abi("C"));
|
||||
//~^ ERROR asm with `clobber_abi` must specify explicit registers for outputs
|
||||
asm!("", out("eax") foo, clobber_abi("C"));
|
||||
}
|
||||
}
|
||||
|
||||
global_asm!("", options(nomem));
|
||||
|
@ -10,13 +10,13 @@ error: the `pure` and `noreturn` options are mutually exclusive
|
||||
LL | asm!("", options(pure, nomem, noreturn));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: asm with `pure` option must have at least one output
|
||||
error: asm with the `pure` option must have at least one output
|
||||
--> $DIR/bad-options.rs:10:18
|
||||
|
|
||||
LL | asm!("", options(pure, nomem, noreturn));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: asm with `pure` option must have at least one output
|
||||
error: asm with the `pure` option must have at least one output
|
||||
--> $DIR/bad-options.rs:13:33
|
||||
|
|
||||
LL | asm!("{}", in(reg) foo, options(pure, nomem));
|
||||
@ -28,41 +28,57 @@ error: asm outputs are not allowed with the `noreturn` option
|
||||
LL | asm!("{}", out(reg) foo, options(noreturn));
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: asm with `clobber_abi` must specify explicit registers for outputs
|
||||
--> $DIR/bad-options.rs:22:20
|
||||
|
|
||||
LL | asm!("{}", out(reg) foo, clobber_abi("C"));
|
||||
| ^^^^^^^^^^^^ ---------------- clobber_abi
|
||||
| |
|
||||
| generic outputs
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
|
||||
--> $DIR/bad-options.rs:20:25
|
||||
--> $DIR/bad-options.rs:28:25
|
||||
|
|
||||
LL | global_asm!("", options(nomem));
|
||||
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
|
||||
--> $DIR/bad-options.rs:22:25
|
||||
--> $DIR/bad-options.rs:30:25
|
||||
|
|
||||
LL | global_asm!("", options(readonly));
|
||||
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
|
||||
--> $DIR/bad-options.rs:24:25
|
||||
--> $DIR/bad-options.rs:32:25
|
||||
|
|
||||
LL | global_asm!("", options(noreturn));
|
||||
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
|
||||
--> $DIR/bad-options.rs:26:25
|
||||
--> $DIR/bad-options.rs:34:25
|
||||
|
|
||||
LL | global_asm!("", options(pure));
|
||||
| ^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
|
||||
--> $DIR/bad-options.rs:28:25
|
||||
--> $DIR/bad-options.rs:36:25
|
||||
|
|
||||
LL | global_asm!("", options(nostack));
|
||||
| ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
|
||||
--> $DIR/bad-options.rs:30:25
|
||||
--> $DIR/bad-options.rs:38:25
|
||||
|
|
||||
LL | global_asm!("", options(preserves_flags));
|
||||
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: invalid ABI for `clobber_abi`
|
||||
--> $DIR/bad-options.rs:20:18
|
||||
|
|
||||
LL | asm!("", clobber_abi("foo"));
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -124,7 +124,7 @@ unsafe extern "C" fn invalid_options() {
|
||||
#[naked]
|
||||
unsafe extern "C" fn invalid_options_continued() {
|
||||
asm!("", options(readonly, nostack), options(pure));
|
||||
//~^ ERROR asm with `pure` option must have at least one output
|
||||
//~^ ERROR asm with the `pure` option must have at least one output
|
||||
//~| WARN asm options unsupported in naked functions: `nostack`, `pure`, `readonly`
|
||||
//~| WARN this was previously accepted
|
||||
//~| WARN asm in naked functions must use `noreturn` option
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: asm with `pure` option must have at least one output
|
||||
error: asm with the `pure` option must have at least one output
|
||||
--> $DIR/naked-functions.rs:126:14
|
||||
|
|
||||
LL | asm!("", options(readonly, nostack), options(pure));
|
||||
|
@ -13,7 +13,7 @@ fn main() {
|
||||
asm!("{}" foo);
|
||||
//~^ ERROR expected token: `,`
|
||||
asm!("{}", foo);
|
||||
//~^ ERROR expected operand, options, or additional template string
|
||||
//~^ ERROR expected operand, clobber_abi, options, or additional template string
|
||||
asm!("{}", in foo);
|
||||
//~^ ERROR expected `(`, found `foo`
|
||||
asm!("{}", in(reg foo));
|
||||
@ -37,6 +37,21 @@ fn main() {
|
||||
asm!("{}", options(), const foo);
|
||||
//~^ ERROR arguments are not allowed after options
|
||||
//~^^ ERROR attempt to use a non-constant value in a constant
|
||||
asm!("", clobber_abi(foo));
|
||||
//~^ ERROR expected string literal
|
||||
asm!("", clobber_abi("C" foo));
|
||||
//~^ ERROR expected `)`, found `foo`
|
||||
asm!("", clobber_abi("C", foo));
|
||||
//~^ ERROR expected `)`, found `,`
|
||||
asm!("{}", clobber_abi("C"), const foo);
|
||||
//~^ ERROR arguments are not allowed after clobber_abi
|
||||
//~^^ ERROR attempt to use a non-constant value in a constant
|
||||
asm!("", options(), clobber_abi("C"));
|
||||
//~^ ERROR clobber_abi is not allowed after options
|
||||
asm!("{}", options(), clobber_abi("C"), const foo);
|
||||
//~^ ERROR clobber_abi is not allowed after options
|
||||
asm!("", clobber_abi("C"), clobber_abi("C"));
|
||||
//~^ ERROR clobber_abi specified multiple times
|
||||
asm!("{a}", a = const foo, a = const bar);
|
||||
//~^ ERROR duplicate argument named `a`
|
||||
//~^^ ERROR argument never used
|
||||
@ -86,6 +101,21 @@ global_asm!("", options(nomem, FOO));
|
||||
//~^ ERROR expected one of
|
||||
global_asm!("{}", options(), const FOO);
|
||||
//~^ ERROR arguments are not allowed after options
|
||||
global_asm!("", clobber_abi(FOO));
|
||||
//~^ ERROR expected string literal
|
||||
global_asm!("", clobber_abi("C" FOO));
|
||||
//~^ ERROR expected `)`, found `FOO`
|
||||
global_asm!("", clobber_abi("C", FOO));
|
||||
//~^ ERROR expected `)`, found `,`
|
||||
global_asm!("{}", clobber_abi("C"), const FOO);
|
||||
//~^ ERROR arguments are not allowed after clobber_abi
|
||||
//~^^ ERROR `clobber_abi` cannot be used with `global_asm!`
|
||||
global_asm!("", options(), clobber_abi("C"));
|
||||
//~^ ERROR clobber_abi is not allowed after options
|
||||
global_asm!("{}", options(), clobber_abi("C"), const FOO);
|
||||
//~^ ERROR clobber_abi is not allowed after options
|
||||
global_asm!("", clobber_abi("C"), clobber_abi("C"));
|
||||
//~^ ERROR clobber_abi specified multiple times
|
||||
global_asm!("{a}", a = const FOO, a = const BAR);
|
||||
//~^ ERROR duplicate argument named `a`
|
||||
//~^^ ERROR argument never used
|
||||
|
@ -16,11 +16,11 @@ error: expected token: `,`
|
||||
LL | asm!("{}" foo);
|
||||
| ^^^ expected `,`
|
||||
|
||||
error: expected operand, options, or additional template string
|
||||
error: expected operand, clobber_abi, options, or additional template string
|
||||
--> $DIR/parse-error.rs:15:20
|
||||
|
|
||||
LL | asm!("{}", foo);
|
||||
| ^^^ expected operand, options, or additional template string
|
||||
| ^^^ expected operand, clobber_abi, options, or additional template string
|
||||
|
||||
error: expected `(`, found `foo`
|
||||
--> $DIR/parse-error.rs:17:23
|
||||
@ -90,8 +90,58 @@ LL | asm!("{}", options(), const foo);
|
||||
| |
|
||||
| previous options
|
||||
|
||||
error: expected string literal
|
||||
--> $DIR/parse-error.rs:40:30
|
||||
|
|
||||
LL | asm!("", clobber_abi(foo));
|
||||
| ^^^ not a string literal
|
||||
|
||||
error: expected `)`, found `foo`
|
||||
--> $DIR/parse-error.rs:42:34
|
||||
|
|
||||
LL | asm!("", clobber_abi("C" foo));
|
||||
| ^^^ expected `)`
|
||||
|
||||
error: expected `)`, found `,`
|
||||
--> $DIR/parse-error.rs:44:33
|
||||
|
|
||||
LL | asm!("", clobber_abi("C", foo));
|
||||
| ^ expected `)`
|
||||
|
||||
error: arguments are not allowed after clobber_abi
|
||||
--> $DIR/parse-error.rs:46:38
|
||||
|
|
||||
LL | asm!("{}", clobber_abi("C"), const foo);
|
||||
| ---------------- ^^^^^^^^^ argument
|
||||
| |
|
||||
| clobber_abi
|
||||
|
||||
error: clobber_abi is not allowed after options
|
||||
--> $DIR/parse-error.rs:49:29
|
||||
|
|
||||
LL | asm!("", options(), clobber_abi("C"));
|
||||
| --------- ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| options
|
||||
|
||||
error: clobber_abi is not allowed after options
|
||||
--> $DIR/parse-error.rs:51:31
|
||||
|
|
||||
LL | asm!("{}", options(), clobber_abi("C"), const foo);
|
||||
| --------- ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| options
|
||||
|
||||
error: clobber_abi specified multiple times
|
||||
--> $DIR/parse-error.rs:53:36
|
||||
|
|
||||
LL | asm!("", clobber_abi("C"), clobber_abi("C"));
|
||||
| ---------------- ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| clobber_abi previously specified here
|
||||
|
||||
error: duplicate argument named `a`
|
||||
--> $DIR/parse-error.rs:40:36
|
||||
--> $DIR/parse-error.rs:55:36
|
||||
|
|
||||
LL | asm!("{a}", a = const foo, a = const bar);
|
||||
| ------------- ^^^^^^^^^^^^^ duplicate argument
|
||||
@ -99,7 +149,7 @@ LL | asm!("{a}", a = const foo, a = const bar);
|
||||
| previously here
|
||||
|
||||
error: argument never used
|
||||
--> $DIR/parse-error.rs:40:36
|
||||
--> $DIR/parse-error.rs:55:36
|
||||
|
|
||||
LL | asm!("{a}", a = const foo, a = const bar);
|
||||
| ^^^^^^^^^^^^^ argument never used
|
||||
@ -107,13 +157,13 @@ LL | asm!("{a}", a = const foo, a = const bar);
|
||||
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
|
||||
|
||||
error: explicit register arguments cannot have names
|
||||
--> $DIR/parse-error.rs:45:18
|
||||
--> $DIR/parse-error.rs:60:18
|
||||
|
|
||||
LL | asm!("", a = in("eax") foo);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: named arguments cannot follow explicit register arguments
|
||||
--> $DIR/parse-error.rs:47:36
|
||||
--> $DIR/parse-error.rs:62:36
|
||||
|
|
||||
LL | asm!("{a}", in("eax") foo, a = const bar);
|
||||
| ------------- ^^^^^^^^^^^^^ named argument
|
||||
@ -121,7 +171,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar);
|
||||
| explicit register argument
|
||||
|
||||
error: named arguments cannot follow explicit register arguments
|
||||
--> $DIR/parse-error.rs:50:36
|
||||
--> $DIR/parse-error.rs:65:36
|
||||
|
|
||||
LL | asm!("{a}", in("eax") foo, a = const bar);
|
||||
| ------------- ^^^^^^^^^^^^^ named argument
|
||||
@ -129,27 +179,27 @@ LL | asm!("{a}", in("eax") foo, a = const bar);
|
||||
| explicit register argument
|
||||
|
||||
error: positional arguments cannot follow named arguments or explicit register arguments
|
||||
--> $DIR/parse-error.rs:53:36
|
||||
--> $DIR/parse-error.rs:68:36
|
||||
|
|
||||
LL | asm!("{1}", in("eax") foo, const bar);
|
||||
| ------------- ^^^^^^^^^ positional argument
|
||||
| |
|
||||
| explicit register argument
|
||||
|
||||
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
|
||||
--> $DIR/parse-error.rs:56:29
|
||||
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
|
||||
--> $DIR/parse-error.rs:71:29
|
||||
|
|
||||
LL | asm!("", options(), "");
|
||||
| ^^ expected one of 8 possible tokens
|
||||
| ^^ expected one of 9 possible tokens
|
||||
|
||||
error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
|
||||
--> $DIR/parse-error.rs:58:33
|
||||
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
|
||||
--> $DIR/parse-error.rs:73:33
|
||||
|
|
||||
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
|
||||
| ^^^^ expected one of 8 possible tokens
|
||||
| ^^^^ expected one of 9 possible tokens
|
||||
|
||||
error: asm template must be a string literal
|
||||
--> $DIR/parse-error.rs:60:14
|
||||
--> $DIR/parse-error.rs:75:14
|
||||
|
|
||||
LL | asm!(format!("{{{}}}", 0), in(reg) foo);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@ -157,7 +207,7 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo);
|
||||
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: asm template must be a string literal
|
||||
--> $DIR/parse-error.rs:62:21
|
||||
--> $DIR/parse-error.rs:77:21
|
||||
|
|
||||
LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@ -165,69 +215,125 @@ LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
|
||||
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: requires at least a template string argument
|
||||
--> $DIR/parse-error.rs:69:1
|
||||
--> $DIR/parse-error.rs:84:1
|
||||
|
|
||||
LL | global_asm!();
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: asm template must be a string literal
|
||||
--> $DIR/parse-error.rs:71:13
|
||||
--> $DIR/parse-error.rs:86:13
|
||||
|
|
||||
LL | global_asm!(FOO);
|
||||
| ^^^
|
||||
|
||||
error: expected token: `,`
|
||||
--> $DIR/parse-error.rs:73:18
|
||||
--> $DIR/parse-error.rs:88:18
|
||||
|
|
||||
LL | global_asm!("{}" FOO);
|
||||
| ^^^ expected `,`
|
||||
|
||||
error: expected operand, options, or additional template string
|
||||
--> $DIR/parse-error.rs:75:19
|
||||
--> $DIR/parse-error.rs:90:19
|
||||
|
|
||||
LL | global_asm!("{}", FOO);
|
||||
| ^^^ expected operand, options, or additional template string
|
||||
|
||||
error: expected expression, found end of macro arguments
|
||||
--> $DIR/parse-error.rs:77:24
|
||||
--> $DIR/parse-error.rs:92:24
|
||||
|
|
||||
LL | global_asm!("{}", const);
|
||||
| ^ expected expression
|
||||
|
||||
error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
|
||||
--> $DIR/parse-error.rs:79:30
|
||||
--> $DIR/parse-error.rs:94:30
|
||||
|
|
||||
LL | global_asm!("{}", const(reg) FOO);
|
||||
| ^^^ expected one of `,`, `.`, `?`, or an operator
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
|
||||
--> $DIR/parse-error.rs:81:25
|
||||
--> $DIR/parse-error.rs:96:25
|
||||
|
|
||||
LL | global_asm!("", options(FOO));
|
||||
| ^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
|
||||
--> $DIR/parse-error.rs:83:25
|
||||
--> $DIR/parse-error.rs:98:25
|
||||
|
|
||||
LL | global_asm!("", options(nomem FOO));
|
||||
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
|
||||
--> $DIR/parse-error.rs:85:25
|
||||
--> $DIR/parse-error.rs:100:25
|
||||
|
|
||||
LL | global_asm!("", options(nomem, FOO));
|
||||
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
||||
|
||||
error: arguments are not allowed after options
|
||||
--> $DIR/parse-error.rs:87:30
|
||||
--> $DIR/parse-error.rs:102:30
|
||||
|
|
||||
LL | global_asm!("{}", options(), const FOO);
|
||||
| --------- ^^^^^^^^^ argument
|
||||
| |
|
||||
| previous options
|
||||
|
||||
error: expected string literal
|
||||
--> $DIR/parse-error.rs:104:29
|
||||
|
|
||||
LL | global_asm!("", clobber_abi(FOO));
|
||||
| ^^^ not a string literal
|
||||
|
||||
error: expected `)`, found `FOO`
|
||||
--> $DIR/parse-error.rs:106:33
|
||||
|
|
||||
LL | global_asm!("", clobber_abi("C" FOO));
|
||||
| ^^^ expected `)`
|
||||
|
||||
error: expected `)`, found `,`
|
||||
--> $DIR/parse-error.rs:108:32
|
||||
|
|
||||
LL | global_asm!("", clobber_abi("C", FOO));
|
||||
| ^ expected `)`
|
||||
|
||||
error: arguments are not allowed after clobber_abi
|
||||
--> $DIR/parse-error.rs:110:37
|
||||
|
|
||||
LL | global_asm!("{}", clobber_abi("C"), const FOO);
|
||||
| ---------------- ^^^^^^^^^ argument
|
||||
| |
|
||||
| clobber_abi
|
||||
|
||||
error: `clobber_abi` cannot be used with `global_asm!`
|
||||
--> $DIR/parse-error.rs:110:19
|
||||
|
|
||||
LL | global_asm!("{}", clobber_abi("C"), const FOO);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: clobber_abi is not allowed after options
|
||||
--> $DIR/parse-error.rs:113:28
|
||||
|
|
||||
LL | global_asm!("", options(), clobber_abi("C"));
|
||||
| --------- ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| options
|
||||
|
||||
error: clobber_abi is not allowed after options
|
||||
--> $DIR/parse-error.rs:115:30
|
||||
|
|
||||
LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
|
||||
| --------- ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| options
|
||||
|
||||
error: clobber_abi specified multiple times
|
||||
--> $DIR/parse-error.rs:117:35
|
||||
|
|
||||
LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
|
||||
| ---------------- ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| clobber_abi previously specified here
|
||||
|
||||
error: duplicate argument named `a`
|
||||
--> $DIR/parse-error.rs:89:35
|
||||
--> $DIR/parse-error.rs:119:35
|
||||
|
|
||||
LL | global_asm!("{a}", a = const FOO, a = const BAR);
|
||||
| ------------- ^^^^^^^^^^^^^ duplicate argument
|
||||
@ -235,27 +341,27 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
|
||||
| previously here
|
||||
|
||||
error: argument never used
|
||||
--> $DIR/parse-error.rs:89:35
|
||||
--> $DIR/parse-error.rs:119:35
|
||||
|
|
||||
LL | global_asm!("{a}", a = const FOO, a = const BAR);
|
||||
| ^^^^^^^^^^^^^ argument never used
|
||||
|
|
||||
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
|
||||
|
||||
error: expected one of `const` or `options`, found `""`
|
||||
--> $DIR/parse-error.rs:92:28
|
||||
error: expected one of `clobber_abi`, `const`, or `options`, found `""`
|
||||
--> $DIR/parse-error.rs:122:28
|
||||
|
|
||||
LL | global_asm!("", options(), "");
|
||||
| ^^ expected one of `const` or `options`
|
||||
| ^^ expected one of `clobber_abi`, `const`, or `options`
|
||||
|
||||
error: expected one of `const` or `options`, found `"{}"`
|
||||
--> $DIR/parse-error.rs:94:30
|
||||
error: expected one of `clobber_abi`, `const`, or `options`, found `"{}"`
|
||||
--> $DIR/parse-error.rs:124:30
|
||||
|
|
||||
LL | global_asm!("{}", const FOO, "{}", const FOO);
|
||||
| ^^^^ expected one of `const` or `options`
|
||||
| ^^^^ expected one of `clobber_abi`, `const`, or `options`
|
||||
|
||||
error: asm template must be a string literal
|
||||
--> $DIR/parse-error.rs:96:13
|
||||
--> $DIR/parse-error.rs:126:13
|
||||
|
|
||||
LL | global_asm!(format!("{{{}}}", 0), const FOO);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@ -263,7 +369,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
|
||||
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: asm template must be a string literal
|
||||
--> $DIR/parse-error.rs:98:20
|
||||
--> $DIR/parse-error.rs:128:20
|
||||
|
|
||||
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@ -280,7 +386,16 @@ LL | asm!("{}", options(), const foo);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error[E0435]: attempt to use a non-constant value in a constant
|
||||
--> $DIR/parse-error.rs:40:31
|
||||
--> $DIR/parse-error.rs:46:44
|
||||
|
|
||||
LL | let mut foo = 0;
|
||||
| ---------- help: consider using `const` instead of `let`: `const foo`
|
||||
...
|
||||
LL | asm!("{}", clobber_abi("C"), const foo);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error[E0435]: attempt to use a non-constant value in a constant
|
||||
--> $DIR/parse-error.rs:55:31
|
||||
|
|
||||
LL | let mut foo = 0;
|
||||
| ---------- help: consider using `const` instead of `let`: `const foo`
|
||||
@ -289,7 +404,7 @@ LL | asm!("{a}", a = const foo, a = const bar);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error[E0435]: attempt to use a non-constant value in a constant
|
||||
--> $DIR/parse-error.rs:40:46
|
||||
--> $DIR/parse-error.rs:55:46
|
||||
|
|
||||
LL | let mut bar = 0;
|
||||
| ---------- help: consider using `const` instead of `let`: `const bar`
|
||||
@ -298,7 +413,7 @@ LL | asm!("{a}", a = const foo, a = const bar);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error[E0435]: attempt to use a non-constant value in a constant
|
||||
--> $DIR/parse-error.rs:47:46
|
||||
--> $DIR/parse-error.rs:62:46
|
||||
|
|
||||
LL | let mut bar = 0;
|
||||
| ---------- help: consider using `const` instead of `let`: `const bar`
|
||||
@ -307,7 +422,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error[E0435]: attempt to use a non-constant value in a constant
|
||||
--> $DIR/parse-error.rs:50:46
|
||||
--> $DIR/parse-error.rs:65:46
|
||||
|
|
||||
LL | let mut bar = 0;
|
||||
| ---------- help: consider using `const` instead of `let`: `const bar`
|
||||
@ -316,7 +431,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error[E0435]: attempt to use a non-constant value in a constant
|
||||
--> $DIR/parse-error.rs:53:42
|
||||
--> $DIR/parse-error.rs:68:42
|
||||
|
|
||||
LL | let mut bar = 0;
|
||||
| ---------- help: consider using `const` instead of `let`: `const bar`
|
||||
@ -324,6 +439,6 @@ LL | let mut bar = 0;
|
||||
LL | asm!("{1}", in("eax") foo, const bar);
|
||||
| ^^^ non-constant value
|
||||
|
||||
error: aborting due to 47 previous errors
|
||||
error: aborting due to 63 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0435`.
|
||||
|
Loading…
Reference in New Issue
Block a user