Auto merge of #84107 - Amanieu:global_asm2, r=nagisa

Add support for const operands and options to global_asm!

On x86, the default syntax is also switched to Intel to match asm!.

Currently `global_asm!` only supports `const` operands and the `att_syntax` option. In the future, `sym` operands will also be supported. However there is no plan to support any of the other operand types or options since they don't make sense in the context of `global_asm!`.

r? `@nagisa`
This commit is contained in:
bors 2021-05-13 22:17:43 +00:00
commit 17f30e5451
56 changed files with 1424 additions and 842 deletions

View File

@ -655,9 +655,9 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.39"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
checksum = "65af2dcae4779003dfa91aedc6ade7bdc7ba685944e50a8b4f9380df376a4466"
dependencies = [
"cc",
"rustc-std-workspace-core",

View File

@ -2279,14 +2279,6 @@ pub struct ForeignMod {
pub items: Vec<P<ForeignItem>>,
}
/// Global inline assembly.
///
/// Also known as "module-level assembly" or "file-scoped assembly".
#[derive(Clone, Encodable, Decodable, Debug, Copy)]
pub struct GlobalAsm {
pub asm: Symbol,
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct EnumDef {
pub variants: Vec<Variant>,
@ -2669,7 +2661,7 @@ pub enum ItemKind {
/// E.g., `extern {}` or `extern "C" {}`.
ForeignMod(ForeignMod),
/// Module-level inline assembly (from `global_asm!()`).
GlobalAsm(GlobalAsm),
GlobalAsm(InlineAsm),
/// A type alias (`type`).
///
/// E.g., `type Foo = Bar<u8>;`.

View File

@ -965,7 +965,7 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
ModKind::Unloaded => {}
},
ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
ItemKind::GlobalAsm(_ga) => {}
ItemKind::GlobalAsm(asm) => noop_visit_inline_asm(asm, vis),
ItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => {
vis.visit_generics(generics);
visit_bounds(bounds, vis);
@ -1170,6 +1170,28 @@ pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonCo
vis.visit_expr(value);
}
fn noop_visit_inline_asm<T: MutVisitor>(asm: &mut InlineAsm, vis: &mut T) {
for (op, _) in &mut asm.operands {
match op {
InlineAsmOperand::In { expr, .. }
| InlineAsmOperand::InOut { expr, .. }
| InlineAsmOperand::Sym { expr, .. } => vis.visit_expr(expr),
InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
vis.visit_expr(expr);
}
}
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
vis.visit_expr(in_expr);
if let Some(out_expr) = out_expr {
vis.visit_expr(out_expr);
}
}
InlineAsmOperand::Const { anon_const, .. } => vis.visit_anon_const(anon_const),
}
}
}
pub fn noop_visit_expr<T: MutVisitor>(
Expr { kind, id, span, attrs, tokens }: &mut Expr,
vis: &mut T,
@ -1288,27 +1310,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
ExprKind::Ret(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::InlineAsm(asm) => {
for (op, _) in &mut asm.operands {
match op {
InlineAsmOperand::In { expr, .. }
| InlineAsmOperand::InOut { expr, .. }
| InlineAsmOperand::Sym { expr, .. } => vis.visit_expr(expr),
InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
vis.visit_expr(expr);
}
}
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
vis.visit_expr(in_expr);
if let Some(out_expr) = out_expr {
vis.visit_expr(out_expr);
}
}
InlineAsmOperand::Const { anon_const, .. } => vis.visit_anon_const(anon_const),
}
}
}
ExprKind::InlineAsm(asm) => noop_visit_inline_asm(asm, vis),
ExprKind::LlvmInlineAsm(asm) => {
let LlvmInlineAsm {
asm: _,

View File

@ -90,9 +90,6 @@ pub trait Visitor<'ast>: Sized {
fn visit_foreign_item(&mut self, i: &'ast ForeignItem) {
walk_foreign_item(self, i)
}
fn visit_global_asm(&mut self, ga: &'ast GlobalAsm) {
walk_global_asm(self, ga)
}
fn visit_item(&mut self, i: &'ast Item) {
walk_item(self, i)
}
@ -299,7 +296,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
ItemKind::ForeignMod(ref foreign_module) => {
walk_list!(visitor, visit_foreign_item, &foreign_module.items);
}
ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga),
ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm),
ItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty)) => {
visitor.visit_generics(generics);
walk_list!(visitor, visit_param_bound, bounds);
@ -557,10 +554,6 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI
}
}
pub fn walk_global_asm<'a, V: Visitor<'a>>(_: &mut V, _: &'a GlobalAsm) {
// Empty!
}
pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) {
match *bound {
GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier),
@ -708,6 +701,28 @@ pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonCo
visitor.visit_expr(&constant.value);
}
fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) {
for (op, _) in &asm.operands {
match op {
InlineAsmOperand::In { expr, .. }
| InlineAsmOperand::InOut { expr, .. }
| InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr),
InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
visitor.visit_expr(expr);
}
}
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
visitor.visit_expr(in_expr);
if let Some(out_expr) = out_expr {
visitor.visit_expr(out_expr);
}
}
InlineAsmOperand::Const { anon_const, .. } => visitor.visit_anon_const(anon_const),
}
}
}
pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
walk_list!(visitor, visit_attribute, expression.attrs.iter());
@ -830,29 +845,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
}
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(ref ia) => {
for (op, _) in &ia.operands {
match op {
InlineAsmOperand::In { expr, .. }
| InlineAsmOperand::InOut { expr, .. }
| InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr),
InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
visitor.visit_expr(expr);
}
}
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
visitor.visit_expr(in_expr);
if let Some(out_expr) = out_expr {
visitor.visit_expr(out_expr);
}
}
InlineAsmOperand::Const { anon_const, .. } => {
visitor.visit_anon_const(anon_const)
}
}
}
}
ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
ExprKind::LlvmInlineAsm(ref ia) => {
for &(_, ref input) in &ia.inputs {
visitor.visit_expr(input)

View File

@ -0,0 +1,329 @@
use super::LoweringContext;
use rustc_ast::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_span::{Span, Symbol};
use rustc_target::asm;
use std::collections::hash_map::Entry;
use std::fmt::Write;
impl<'a, 'hir> LoweringContext<'a, 'hir> {
crate fn lower_inline_asm(&mut self, sp: Span, asm: &InlineAsm) -> &'hir hir::InlineAsm<'hir> {
// Rustdoc needs to support asm! from foriegn architectures: don't try
// lowering the register contraints in this case.
let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch };
if asm_arch.is_none() && !self.sess.opts.actually_rustdoc {
struct_span_err!(self.sess, sp, E0472, "inline assembly is unsupported on this target")
.emit();
}
if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
&& !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
&& !self.sess.opts.actually_rustdoc
{
self.sess
.struct_span_err(sp, "the `att_syntax` option is only supported on x86")
.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
.operands
.iter()
.map(|(op, op_sp)| {
let lower_reg = |reg| match reg {
InlineAsmRegOrRegClass::Reg(s) => {
asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
asm::InlineAsmReg::parse(
asm_arch,
|feature| sess.target_features.contains(&Symbol::intern(feature)),
&sess.target,
s,
)
.unwrap_or_else(|e| {
let msg = format!("invalid register `{}`: {}", s.as_str(), e);
sess.struct_span_err(*op_sp, &msg).emit();
asm::InlineAsmReg::Err
})
} else {
asm::InlineAsmReg::Err
})
}
InlineAsmRegOrRegClass::RegClass(s) => {
asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
sess.struct_span_err(*op_sp, &msg).emit();
asm::InlineAsmRegClass::Err
})
} else {
asm::InlineAsmRegClass::Err
})
}
};
let op = match *op {
InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
reg: lower_reg(reg),
expr: self.lower_expr_mut(expr),
},
InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
reg: lower_reg(reg),
late,
expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
},
InlineAsmOperand::InOut { reg, late, ref expr } => {
hir::InlineAsmOperand::InOut {
reg: lower_reg(reg),
late,
expr: self.lower_expr_mut(expr),
}
}
InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
hir::InlineAsmOperand::SplitInOut {
reg: lower_reg(reg),
late,
in_expr: self.lower_expr_mut(in_expr),
out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
}
}
InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const {
anon_const: self.lower_anon_const(anon_const),
},
InlineAsmOperand::Sym { ref expr } => {
hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
}
};
(op, *op_sp)
})
.collect();
// Validate template modifiers against the register classes for the operands
for p in &asm.template {
if let InlineAsmTemplatePiece::Placeholder {
operand_idx,
modifier: Some(modifier),
span: placeholder_span,
} = *p
{
let op_sp = asm.operands[operand_idx].1;
match &operands[operand_idx].0 {
hir::InlineAsmOperand::In { reg, .. }
| hir::InlineAsmOperand::Out { reg, .. }
| hir::InlineAsmOperand::InOut { reg, .. }
| hir::InlineAsmOperand::SplitInOut { reg, .. } => {
let class = reg.reg_class();
if class == asm::InlineAsmRegClass::Err {
continue;
}
let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
if !valid_modifiers.contains(&modifier) {
let mut err = sess.struct_span_err(
placeholder_span,
"invalid asm template modifier for this register class",
);
err.span_label(placeholder_span, "template modifier");
err.span_label(op_sp, "argument");
if !valid_modifiers.is_empty() {
let mut mods = format!("`{}`", valid_modifiers[0]);
for m in &valid_modifiers[1..] {
let _ = write!(mods, ", `{}`", m);
}
err.note(&format!(
"the `{}` register class supports \
the following template modifiers: {}",
class.name(),
mods
));
} else {
err.note(&format!(
"the `{}` register class does not support template modifiers",
class.name()
));
}
err.emit();
}
}
hir::InlineAsmOperand::Const { .. } => {
let mut err = sess.struct_span_err(
placeholder_span,
"asm template modifiers are not allowed for `const` arguments",
);
err.span_label(placeholder_span, "template modifier");
err.span_label(op_sp, "argument");
err.emit();
}
hir::InlineAsmOperand::Sym { .. } => {
let mut err = sess.struct_span_err(
placeholder_span,
"asm template modifiers are not allowed for `sym` arguments",
);
err.span_label(placeholder_span, "template modifier");
err.span_label(op_sp, "argument");
err.emit();
}
}
}
}
let mut used_input_regs = FxHashMap::default();
let mut used_output_regs = FxHashMap::default();
let mut required_features: Vec<&str> = vec![];
for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
if let Some(reg) = op.reg() {
// Make sure we don't accidentally carry features from the
// previous iteration.
required_features.clear();
let reg_class = reg.reg_class();
if reg_class == asm::InlineAsmRegClass::Err {
continue;
}
// We ignore target feature requirements for clobbers: if the
// feature is disabled then the compiler doesn't care what we
// do with the registers.
//
// Note that this is only possible for explicit register
// operands, which cannot be used in the asm string.
let is_clobber = matches!(
op,
hir::InlineAsmOperand::Out {
reg: asm::InlineAsmRegOrRegClass::Reg(_),
late: _,
expr: None
}
);
if !is_clobber {
// Validate register classes against currently enabled target
// features. We check that at least one type is available for
// the current target.
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
if let Some(feature) = feature {
if self.sess.target_features.contains(&Symbol::intern(feature)) {
required_features.clear();
break;
} else {
required_features.push(feature);
}
} else {
required_features.clear();
break;
}
}
// We are sorting primitive strs here and can use unstable sort here
required_features.sort_unstable();
required_features.dedup();
match &required_features[..] {
[] => {}
[feature] => {
let msg = format!(
"register class `{}` requires the `{}` target feature",
reg_class.name(),
feature
);
sess.struct_span_err(op_sp, &msg).emit();
}
features => {
let msg = format!(
"register class `{}` requires at least one target feature: {}",
reg_class.name(),
features.join(", ")
);
sess.struct_span_err(op_sp, &msg).emit();
}
}
}
// Check for conflicts between explicit register operands.
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
let (input, output) = match op {
hir::InlineAsmOperand::In { .. } => (true, false),
// Late output do not conflict with inputs, but normal outputs do
hir::InlineAsmOperand::Out { late, .. } => (!late, true),
hir::InlineAsmOperand::InOut { .. }
| hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {
unreachable!()
}
};
// Flag to output the error only once per operand
let mut skip = false;
reg.overlapping_regs(|r| {
let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
input| {
match used_regs.entry(r) {
Entry::Occupied(o) => {
if skip {
return;
}
skip = true;
let idx2 = *o.get();
let &(ref op2, op_sp2) = &operands[idx2];
let reg2 = match op2.reg() {
Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
_ => unreachable!(),
};
let msg = format!(
"register `{}` conflicts with register `{}`",
reg.name(),
reg2.name()
);
let mut err = sess.struct_span_err(op_sp, &msg);
err.span_label(op_sp, &format!("register `{}`", reg.name()));
err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
match (op, op2) {
(
hir::InlineAsmOperand::In { .. },
hir::InlineAsmOperand::Out { late, .. },
)
| (
hir::InlineAsmOperand::Out { late, .. },
hir::InlineAsmOperand::In { .. },
) => {
assert!(!*late);
let out_op_sp = if input { op_sp2 } else { op_sp };
let msg = "use `lateout` instead of \
`out` to avoid conflict";
err.span_help(out_op_sp, msg);
}
_ => {}
}
err.emit();
}
Entry::Vacant(v) => {
v.insert(idx);
}
}
};
if input {
check(&mut used_input_regs, true);
}
if output {
check(&mut used_output_regs, false);
}
});
}
}
}
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[..]);
let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans };
self.arena.alloc(hir_asm)
}
}

View File

@ -3,7 +3,6 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericAr
use rustc_ast::attr;
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::struct_span_err;
@ -15,9 +14,6 @@ use rustc_span::hygiene::ExpnId;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
use rustc_target::asm;
use std::collections::hash_map::Entry;
use std::fmt::Write;
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
@ -222,7 +218,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let e = e.as_ref().map(|x| self.lower_expr(x));
hir::ExprKind::Ret(e)
}
ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm),
ExprKind::InlineAsm(ref asm) => {
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
}
ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
ExprKind::Struct(ref se) => {
let rest = match &se.rest {
@ -1329,319 +1327,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
result
}
fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> {
// Rustdoc needs to support asm! from foriegn architectures: don't try
// lowering the register contraints in this case.
let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch };
if asm_arch.is_none() && !self.sess.opts.actually_rustdoc {
struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit();
}
if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
&& !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
&& !self.sess.opts.actually_rustdoc
{
self.sess
.struct_span_err(sp, "the `att_syntax` option is only supported on x86")
.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
.operands
.iter()
.map(|(op, op_sp)| {
let lower_reg = |reg| match reg {
InlineAsmRegOrRegClass::Reg(s) => {
asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
asm::InlineAsmReg::parse(
asm_arch,
|feature| sess.target_features.contains(&Symbol::intern(feature)),
&sess.target,
s,
)
.unwrap_or_else(|e| {
let msg = format!("invalid register `{}`: {}", s.as_str(), e);
sess.struct_span_err(*op_sp, &msg).emit();
asm::InlineAsmReg::Err
})
} else {
asm::InlineAsmReg::Err
})
}
InlineAsmRegOrRegClass::RegClass(s) => {
asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
sess.struct_span_err(*op_sp, &msg).emit();
asm::InlineAsmRegClass::Err
})
} else {
asm::InlineAsmRegClass::Err
})
}
};
let op = match *op {
InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
reg: lower_reg(reg),
expr: self.lower_expr_mut(expr),
},
InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
reg: lower_reg(reg),
late,
expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
},
InlineAsmOperand::InOut { reg, late, ref expr } => {
hir::InlineAsmOperand::InOut {
reg: lower_reg(reg),
late,
expr: self.lower_expr_mut(expr),
}
}
InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
hir::InlineAsmOperand::SplitInOut {
reg: lower_reg(reg),
late,
in_expr: self.lower_expr_mut(in_expr),
out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
}
}
InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const {
anon_const: self.lower_anon_const(anon_const),
},
InlineAsmOperand::Sym { ref expr } => {
hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
}
};
(op, *op_sp)
})
.collect();
// Validate template modifiers against the register classes for the operands
for p in &asm.template {
if let InlineAsmTemplatePiece::Placeholder {
operand_idx,
modifier: Some(modifier),
span: placeholder_span,
} = *p
{
let op_sp = asm.operands[operand_idx].1;
match &operands[operand_idx].0 {
hir::InlineAsmOperand::In { reg, .. }
| hir::InlineAsmOperand::Out { reg, .. }
| hir::InlineAsmOperand::InOut { reg, .. }
| hir::InlineAsmOperand::SplitInOut { reg, .. } => {
let class = reg.reg_class();
if class == asm::InlineAsmRegClass::Err {
continue;
}
let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
if !valid_modifiers.contains(&modifier) {
let mut err = sess.struct_span_err(
placeholder_span,
"invalid asm template modifier for this register class",
);
err.span_label(placeholder_span, "template modifier");
err.span_label(op_sp, "argument");
if !valid_modifiers.is_empty() {
let mut mods = format!("`{}`", valid_modifiers[0]);
for m in &valid_modifiers[1..] {
let _ = write!(mods, ", `{}`", m);
}
err.note(&format!(
"the `{}` register class supports \
the following template modifiers: {}",
class.name(),
mods
));
} else {
err.note(&format!(
"the `{}` register class does not support template modifiers",
class.name()
));
}
err.emit();
}
}
hir::InlineAsmOperand::Const { .. } => {
let mut err = sess.struct_span_err(
placeholder_span,
"asm template modifiers are not allowed for `const` arguments",
);
err.span_label(placeholder_span, "template modifier");
err.span_label(op_sp, "argument");
err.emit();
}
hir::InlineAsmOperand::Sym { .. } => {
let mut err = sess.struct_span_err(
placeholder_span,
"asm template modifiers are not allowed for `sym` arguments",
);
err.span_label(placeholder_span, "template modifier");
err.span_label(op_sp, "argument");
err.emit();
}
}
}
}
let mut used_input_regs = FxHashMap::default();
let mut used_output_regs = FxHashMap::default();
let mut required_features: Vec<&str> = vec![];
for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
if let Some(reg) = op.reg() {
// Make sure we don't accidentally carry features from the
// previous iteration.
required_features.clear();
let reg_class = reg.reg_class();
if reg_class == asm::InlineAsmRegClass::Err {
continue;
}
// We ignore target feature requirements for clobbers: if the
// feature is disabled then the compiler doesn't care what we
// do with the registers.
//
// Note that this is only possible for explicit register
// operands, which cannot be used in the asm string.
let is_clobber = matches!(
op,
hir::InlineAsmOperand::Out {
reg: asm::InlineAsmRegOrRegClass::Reg(_),
late: _,
expr: None
}
);
if !is_clobber {
// Validate register classes against currently enabled target
// features. We check that at least one type is available for
// the current target.
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
if let Some(feature) = feature {
if self.sess.target_features.contains(&Symbol::intern(feature)) {
required_features.clear();
break;
} else {
required_features.push(feature);
}
} else {
required_features.clear();
break;
}
}
// We are sorting primitive strs here and can use unstable sort here
required_features.sort_unstable();
required_features.dedup();
match &required_features[..] {
[] => {}
[feature] => {
let msg = format!(
"register class `{}` requires the `{}` target feature",
reg_class.name(),
feature
);
sess.struct_span_err(op_sp, &msg).emit();
}
features => {
let msg = format!(
"register class `{}` requires at least one target feature: {}",
reg_class.name(),
features.join(", ")
);
sess.struct_span_err(op_sp, &msg).emit();
}
}
}
// Check for conflicts between explicit register operands.
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
let (input, output) = match op {
hir::InlineAsmOperand::In { .. } => (true, false),
// Late output do not conflict with inputs, but normal outputs do
hir::InlineAsmOperand::Out { late, .. } => (!late, true),
hir::InlineAsmOperand::InOut { .. }
| hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {
unreachable!()
}
};
// Flag to output the error only once per operand
let mut skip = false;
reg.overlapping_regs(|r| {
let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
input| {
match used_regs.entry(r) {
Entry::Occupied(o) => {
if skip {
return;
}
skip = true;
let idx2 = *o.get();
let &(ref op2, op_sp2) = &operands[idx2];
let reg2 = match op2.reg() {
Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
_ => unreachable!(),
};
let msg = format!(
"register `{}` conflicts with register `{}`",
reg.name(),
reg2.name()
);
let mut err = sess.struct_span_err(op_sp, &msg);
err.span_label(op_sp, &format!("register `{}`", reg.name()));
err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
match (op, op2) {
(
hir::InlineAsmOperand::In { .. },
hir::InlineAsmOperand::Out { late, .. },
)
| (
hir::InlineAsmOperand::Out { late, .. },
hir::InlineAsmOperand::In { .. },
) => {
assert!(!*late);
let out_op_sp = if input { op_sp2 } else { op_sp };
let msg = "use `lateout` instead of \
`out` to avoid conflict";
err.span_help(out_op_sp, msg);
}
_ => {}
}
err.emit();
}
Entry::Vacant(v) => {
v.insert(idx);
}
}
};
if input {
check(&mut used_input_regs, true);
}
if output {
check(&mut used_output_regs, false);
}
});
}
}
}
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[..]);
let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans };
hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm))
}
fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> {
let inner = hir::LlvmInlineAsmInner {
inputs: asm.inputs.iter().map(|&(c, _)| c).collect(),

View File

@ -329,7 +329,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
}
}
ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)),
ItemKind::GlobalAsm(ref asm) => {
hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm))
}
ItemKind::TyAlias(box TyAliasKind(_, ref gen, _, Some(ref ty))) => {
// We lower
//
@ -746,10 +748,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_global_asm(&mut self, ga: &GlobalAsm) -> &'hir hir::GlobalAsm {
self.arena.alloc(hir::GlobalAsm { asm: ga.asm })
}
fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
let id = self.lower_node_id(v.id);
self.lower_attrs(id, &v.attrs);

View File

@ -77,6 +77,7 @@ macro_rules! arena_vec {
});
}
mod asm;
mod expr;
mod item;
mod pat;

View File

@ -1164,9 +1164,9 @@ impl<'a> State<'a> {
self.print_foreign_mod(nmod, &item.attrs);
self.bclose(item.span);
}
ast::ItemKind::GlobalAsm(ref ga) => {
ast::ItemKind::GlobalAsm(ref asm) => {
self.head(visibility_qualified(&item.vis, "global_asm!"));
self.s.word(ga.asm.to_string());
self.print_inline_asm(asm);
self.end();
}
ast::ItemKind::TyAlias(box ast::TyAliasKind(def, ref generics, ref bounds, ref ty)) => {
@ -2066,117 +2066,8 @@ impl<'a> State<'a> {
}
}
ast::ExprKind::InlineAsm(ref a) => {
enum AsmArg<'a> {
Template(String),
Operand(&'a InlineAsmOperand),
Options(InlineAsmOptions),
}
let mut args = vec![];
args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&a.template)));
args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
if !a.options.is_empty() {
args.push(AsmArg::Options(a.options));
}
self.word("asm!");
self.popen();
self.commasep(Consistent, &args, |s, arg| match arg {
AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
AsmArg::Operand(op) => {
let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r
{
InlineAsmRegOrRegClass::Reg(r) => {
s.print_symbol(*r, ast::StrStyle::Cooked)
}
InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
};
match op {
InlineAsmOperand::In { reg, expr } => {
s.word("in");
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
s.print_expr(expr);
}
InlineAsmOperand::Out { reg, late, expr } => {
s.word(if *late { "lateout" } else { "out" });
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
match expr {
Some(expr) => s.print_expr(expr),
None => s.word("_"),
}
}
InlineAsmOperand::InOut { reg, late, expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
s.print_expr(expr);
}
InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
s.print_expr(in_expr);
s.space();
s.word_space("=>");
match out_expr {
Some(out_expr) => s.print_expr(out_expr),
None => s.word("_"),
}
}
InlineAsmOperand::Const { anon_const } => {
s.word("const");
s.space();
s.print_expr(&anon_const.value);
}
InlineAsmOperand::Sym { expr } => {
s.word("sym");
s.space();
s.print_expr(expr);
}
}
}
AsmArg::Options(opts) => {
s.word("options");
s.popen();
let mut options = vec![];
if opts.contains(InlineAsmOptions::PURE) {
options.push("pure");
}
if opts.contains(InlineAsmOptions::NOMEM) {
options.push("nomem");
}
if opts.contains(InlineAsmOptions::READONLY) {
options.push("readonly");
}
if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
options.push("preserves_flags");
}
if opts.contains(InlineAsmOptions::NORETURN) {
options.push("noreturn");
}
if opts.contains(InlineAsmOptions::NOSTACK) {
options.push("nostack");
}
if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
options.push("att_syntax");
}
s.commasep(Inconsistent, &options, |s, &opt| {
s.word(opt);
});
s.pclose();
}
});
self.pclose();
self.print_inline_asm(a);
}
ast::ExprKind::LlvmInlineAsm(ref a) => {
self.s.word("llvm_asm!");
@ -2267,6 +2158,116 @@ impl<'a> State<'a> {
self.end();
}
fn print_inline_asm(&mut self, asm: &ast::InlineAsm) {
enum AsmArg<'a> {
Template(String),
Operand(&'a InlineAsmOperand),
Options(InlineAsmOptions),
}
let mut args = vec![];
args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template)));
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
if !asm.options.is_empty() {
args.push(AsmArg::Options(asm.options));
}
self.popen();
self.commasep(Consistent, &args, |s, arg| match arg {
AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
AsmArg::Operand(op) => {
let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
};
match op {
InlineAsmOperand::In { reg, expr } => {
s.word("in");
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
s.print_expr(expr);
}
InlineAsmOperand::Out { reg, late, expr } => {
s.word(if *late { "lateout" } else { "out" });
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
match expr {
Some(expr) => s.print_expr(expr),
None => s.word("_"),
}
}
InlineAsmOperand::InOut { reg, late, expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
s.print_expr(expr);
}
InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
print_reg_or_class(s, reg);
s.pclose();
s.space();
s.print_expr(in_expr);
s.space();
s.word_space("=>");
match out_expr {
Some(out_expr) => s.print_expr(out_expr),
None => s.word("_"),
}
}
InlineAsmOperand::Const { anon_const } => {
s.word("const");
s.space();
s.print_expr(&anon_const.value);
}
InlineAsmOperand::Sym { expr } => {
s.word("sym");
s.space();
s.print_expr(expr);
}
}
}
AsmArg::Options(opts) => {
s.word("options");
s.popen();
let mut options = vec![];
if opts.contains(InlineAsmOptions::PURE) {
options.push("pure");
}
if opts.contains(InlineAsmOptions::NOMEM) {
options.push("nomem");
}
if opts.contains(InlineAsmOptions::READONLY) {
options.push("readonly");
}
if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
options.push("preserves_flags");
}
if opts.contains(InlineAsmOptions::NORETURN) {
options.push("noreturn");
}
if opts.contains(InlineAsmOptions::NOSTACK) {
options.push("nostack");
}
if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
options.push("att_syntax");
}
s.commasep(Inconsistent, &options, |s, &opt| {
s.word(opt);
});
s.pclose();
}
});
self.pclose();
}
crate fn print_local_decl(&mut self, loc: &ast::Local) {
self.print_pat(&loc.pat);
if let Some(ref ty) = loc.ty {

View File

@ -8,9 +8,11 @@ use rustc_expand::base::{self, *};
use rustc_parse::parser::Parser;
use rustc_parse_format as parse;
use rustc_session::lint;
use rustc_span::symbol::Ident;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{InnerSpan, Span};
use rustc_target::asm::InlineAsmArch;
use smallvec::smallvec;
struct AsmArgs {
templates: Vec<P<ast::Expr>>,
@ -25,6 +27,7 @@ fn parse_args<'a>(
ecx: &mut ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
is_global_asm: bool,
) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
let mut p = ecx.new_parser_from_tts(tts);
@ -33,7 +36,7 @@ fn parse_args<'a>(
}
// Detect use of the legacy llvm_asm! syntax (which used to be called asm!)
if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
if !is_global_asm && p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
let mut err =
ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported");
err.note("consider migrating to the new asm! syntax specified in RFC 2873");
@ -84,7 +87,7 @@ fn parse_args<'a>(
// Parse options
if p.eat_keyword(sym::options) {
parse_options(&mut p, &mut args)?;
parse_options(&mut p, &mut args, is_global_asm)?;
allow_templates = false;
continue;
}
@ -103,19 +106,19 @@ fn parse_args<'a>(
};
let mut explicit_reg = false;
let op = if p.eat_keyword(kw::In) {
let op = if !is_global_asm && p.eat_keyword(kw::In) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if p.eat_keyword(sym::out) {
} else if !is_global_asm && p.eat_keyword(sym::out) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if p.eat_keyword(sym::lateout) {
} else if !is_global_asm && p.eat_keyword(sym::lateout) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if p.eat_keyword(sym::inout) {
} else if !is_global_asm && p.eat_keyword(sym::inout) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?;
if p.eat(&token::FatArrow) {
@ -125,7 +128,7 @@ fn parse_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if p.eat_keyword(sym::inlateout) {
} else if !is_global_asm && p.eat_keyword(sym::inlateout) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?;
if p.eat(&token::FatArrow) {
@ -138,7 +141,7 @@ fn parse_args<'a>(
} else if p.eat_keyword(kw::Const) {
let anon_const = p.parse_anon_const_expr()?;
ast::InlineAsmOperand::Const { anon_const }
} else if p.eat_keyword(sym::sym) {
} else if !is_global_asm && p.eat_keyword(sym::sym) {
let expr = p.parse_expr()?;
match expr.kind {
ast::ExprKind::Path(..) => {}
@ -329,23 +332,27 @@ fn try_set_option<'a>(
}
}
fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), DiagnosticBuilder<'a>> {
fn parse_options<'a>(
p: &mut Parser<'a>,
args: &mut AsmArgs,
is_global_asm: bool,
) -> Result<(), DiagnosticBuilder<'a>> {
let span_start = p.prev_token.span;
p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
if p.eat_keyword(sym::pure) {
if !is_global_asm && p.eat_keyword(sym::pure) {
try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
} else if p.eat_keyword(sym::nomem) {
} else if !is_global_asm && p.eat_keyword(sym::nomem) {
try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM);
} else if p.eat_keyword(sym::readonly) {
} else if !is_global_asm && p.eat_keyword(sym::readonly) {
try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY);
} else if p.eat_keyword(sym::preserves_flags) {
} else if !is_global_asm && p.eat_keyword(sym::preserves_flags) {
try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS);
} else if p.eat_keyword(sym::noreturn) {
} else if !is_global_asm && p.eat_keyword(sym::noreturn) {
try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN);
} else if p.eat_keyword(sym::nostack) {
} else if !is_global_asm && p.eat_keyword(sym::nostack) {
try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
} else if p.eat_keyword(sym::att_syntax) {
try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
@ -388,7 +395,7 @@ fn parse_reg<'a>(
Ok(result)
}
fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast::Expr> {
fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> {
let mut template = vec![];
// Register operands are implicitly used since they are not allowed to be
// referenced in the template string.
@ -415,7 +422,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
if let Some(mut err) = err {
err.emit();
}
return DummyResult::raw_expr(sp, true);
return None;
}
};
@ -492,7 +499,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
e.span_label(err_sp, label);
}
e.emit();
return DummyResult::raw_expr(sp, true);
return None;
}
curarg = parser.curarg;
@ -643,15 +650,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
}
}
let inline_asm =
ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans };
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
span: sp,
attrs: ast::AttrVec::new(),
tokens: None,
})
Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
}
pub fn expand_asm<'cx>(
@ -659,8 +658,53 @@ pub fn expand_asm<'cx>(
sp: Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
match parse_args(ecx, sp, tts) {
Ok(args) => MacEager::expr(expand_preparsed_asm(ecx, sp, args)),
match parse_args(ecx, sp, tts, false) {
Ok(args) => {
let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
span: sp,
attrs: ast::AttrVec::new(),
tokens: None,
})
} else {
DummyResult::raw_expr(sp, true)
};
MacEager::expr(expr)
}
Err(mut err) => {
err.emit();
DummyResult::any(sp)
}
}
}
pub fn expand_global_asm<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
match parse_args(ecx, sp, tts, true) {
Ok(args) => {
if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
MacEager::items(smallvec![P(ast::Item {
ident: Ident::invalid(),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
kind: ast::ItemKind::GlobalAsm(inline_asm),
vis: ast::Visibility {
span: sp.shrink_to_lo(),
kind: ast::VisibilityKind::Inherited,
tokens: None,
},
span: ecx.with_def_site_ctxt(sp),
tokens: None,
})])
} else {
DummyResult::any(sp)
}
}
Err(mut err) => {
err.emit();
DummyResult::any(sp)

View File

@ -1,68 +0,0 @@
//! Module-level assembly support.
//!
//! The macro defined here allows you to specify "top-level",
//! "file-scoped", or "module-level" assembly. These synonyms
//! all correspond to LLVM's module-level inline assembly instruction.
//!
//! For example, `global_asm!("some assembly here")` codegens to
//! LLVM's `module asm "some assembly here"`. All of LLVM's caveats
//! therefore apply.
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::DiagnosticBuilder;
use rustc_expand::base::{self, *};
use rustc_span::symbol::Ident;
use rustc_span::Span;
use smallvec::smallvec;
pub fn expand_global_asm<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
match parse_global_asm(cx, sp, tts) {
Ok(Some(global_asm)) => MacEager::items(smallvec![P(ast::Item {
ident: Ident::invalid(),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
kind: ast::ItemKind::GlobalAsm(global_asm),
vis: ast::Visibility {
span: sp.shrink_to_lo(),
kind: ast::VisibilityKind::Inherited,
tokens: None,
},
span: cx.with_def_site_ctxt(sp),
tokens: None,
})]),
Ok(None) => DummyResult::any(sp),
Err(mut err) => {
err.emit();
DummyResult::any(sp)
}
}
}
fn parse_global_asm<'a>(
cx: &mut ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
) -> Result<Option<ast::GlobalAsm>, DiagnosticBuilder<'a>> {
let mut p = cx.new_parser_from_tts(tts);
if p.token == token::Eof {
let mut err = cx.struct_span_err(sp, "macro requires a string literal as an argument");
err.span_label(sp, "string literal required");
return Err(err);
}
let expr = p.parse_expr()?;
let (asm, _) = match expr_to_string(cx, expr, "inline assembly must be a string literal") {
Some((s, st)) => (s, st),
None => return Ok(None),
};
Ok(Some(ast::GlobalAsm { asm }))
}

View File

@ -37,7 +37,6 @@ mod env;
mod format;
mod format_foreign;
mod global_allocator;
mod global_asm;
mod llvm_asm;
mod log_syntax;
mod panic;
@ -75,7 +74,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
file: source_util::expand_file,
format_args_nl: format::expand_format_args_nl,
format_args: format::expand_format_args,
global_asm: global_asm::expand_global_asm,
global_asm: asm::expand_global_asm,
include_bytes: source_util::expand_include_bytes,
include_str: source_util::expand_include_str,
include: source_util::expand_include,

View File

@ -3,6 +3,7 @@
use std::path::PathBuf;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -125,9 +126,19 @@ fn module_codegen(
MonoItem::Static(def_id) => crate::constant::codegen_static(tcx, &mut module, def_id),
MonoItem::GlobalAsm(item_id) => {
let item = cx.tcx.hir().item(item_id);
if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind {
cx.global_asm.push_str(&*asm.as_str());
cx.global_asm.push_str("\n\n");
if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind {
if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
cx.global_asm.push_str("\n.intel_syntax noprefix\n");
} else {
cx.global_asm.push_str("\n.att_syntax\n");
}
for piece in asm.template {
match *piece {
InlineAsmTemplatePiece::String(ref s) => cx.global_asm.push_str(s),
InlineAsmTemplatePiece::Placeholder { .. } => todo!(),
}
}
cx.global_asm.push_str("\n.att_syntax\n\n");
} else {
bug!("Expected GlobalAsm found {:?}", item);
}

View File

@ -356,10 +356,49 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
}
impl AsmMethods for CodegenCx<'ll, 'tcx> {
fn codegen_global_asm(&self, ga: &hir::GlobalAsm) {
let asm = ga.asm.as_str();
fn codegen_global_asm(
&self,
template: &[InlineAsmTemplatePiece],
operands: &[GlobalAsmOperandRef],
options: InlineAsmOptions,
_line_spans: &[Span],
) {
let asm_arch = self.tcx.sess.asm_arch.unwrap();
// Default to Intel syntax on x86
let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
&& !options.contains(InlineAsmOptions::ATT_SYNTAX);
// Build the template string
let mut template_str = String::new();
if intel_syntax {
template_str.push_str(".intel_syntax\n");
}
for piece in template {
match *piece {
InlineAsmTemplatePiece::String(ref s) => template_str.push_str(s),
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => {
match operands[operand_idx] {
GlobalAsmOperandRef::Const { ref string } => {
// Const operands get injected directly into the
// template. Note that we don't need to escape $
// here unlike normal inline assembly.
template_str.push_str(string);
}
}
}
}
}
if intel_syntax {
template_str.push_str("\n.att_syntax\n");
}
unsafe {
llvm::LLVMRustAppendModuleInlineAsm(self.llmod, asm.as_ptr().cast(), asm.len());
llvm::LLVMRustAppendModuleInlineAsm(
self.llmod,
template_str.as_ptr().cast(),
template_str.len(),
);
}
}
}

View File

@ -4,7 +4,8 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::LangItem;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
use rustc_session::Session;
use rustc_span::Span;
@ -194,3 +195,32 @@ pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
pub fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) {
struct_span_err!(a, b, E0511, "{}", c).emit();
}
pub fn asm_const_to_str<'tcx>(
tcx: TyCtxt<'tcx>,
sp: Span,
const_value: ConstValue<'tcx>,
ty_and_layout: TyAndLayout<'tcx>,
) -> String {
let scalar = match const_value {
ConstValue::Scalar(s) => s,
_ => {
span_bug!(sp, "expected Scalar for promoted asm const, but got {:#?}", const_value)
}
};
let value = scalar.assert_bits(ty_and_layout.size);
match ty_and_layout.ty.kind() {
ty::Uint(_) => value.to_string(),
ty::Int(int_ty) => match int_ty.normalize(tcx.sess.target.pointer_width) {
ty::IntTy::I8 => (value as i8).to_string(),
ty::IntTy::I16 => (value as i16).to_string(),
ty::IntTy::I32 => (value as i32).to_string(),
ty::IntTy::I64 => (value as i64).to_string(),
ty::IntTy::I128 => (value as i128).to_string(),
ty::IntTy::Isize => unreachable!(),
},
ty::Float(ty::FloatTy::F32) => f32::from_bits(value as u32).to_string(),
ty::Float(ty::FloatTy::F64) => f64::from_bits(value as u64).to_string(),
_ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty),
}
}

View File

@ -12,7 +12,6 @@ use crate::MemFlags;
use rustc_ast as ast;
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::AssertKind;
use rustc_middle::mir::{self, SwitchTargets};
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
@ -825,33 +824,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let const_value = self
.eval_mir_constant(value)
.unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved"));
let ty = value.ty();
let size = bx.layout_of(ty).size;
let scalar = match const_value {
ConstValue::Scalar(s) => s,
_ => span_bug!(
span,
"expected Scalar for promoted asm const, but got {:#?}",
const_value
),
};
let value = scalar.assert_bits(size);
let string = match ty.kind() {
ty::Uint(_) => value.to_string(),
ty::Int(int_ty) => {
match int_ty.normalize(bx.tcx().sess.target.pointer_width) {
ty::IntTy::I8 => (value as i8).to_string(),
ty::IntTy::I16 => (value as i16).to_string(),
ty::IntTy::I32 => (value as i32).to_string(),
ty::IntTy::I64 => (value as i64).to_string(),
ty::IntTy::I128 => (value as i128).to_string(),
ty::IntTy::Isize => unreachable!(),
}
}
ty::Float(ty::FloatTy::F32) => f32::from_bits(value as u32).to_string(),
ty::Float(ty::FloatTy::F64) => f64::from_bits(value as u64).to_string(),
_ => span_bug!(span, "asm const has bad type {}", ty),
};
let string = common::asm_const_to_str(
bx.tcx(),
span,
const_value,
bx.layout_of(value.ty()),
);
InlineAsmOperandRef::Const { string }
}
mir::InlineAsmOperand::SymFn { ref value } => {

View File

@ -1,10 +1,11 @@
use crate::base;
use crate::common;
use crate::traits::*;
use rustc_hir as hir;
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::mir::mono::MonoItem;
use rustc_target::abi::LayoutOf;
pub trait MonoItemExt<'a, 'tcx> {
fn define<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx);
@ -32,8 +33,35 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
}
MonoItem::GlobalAsm(item_id) => {
let item = cx.tcx().hir().item(item_id);
if let hir::ItemKind::GlobalAsm(ref ga) = item.kind {
cx.codegen_global_asm(ga);
if let hir::ItemKind::GlobalAsm(ref asm) = item.kind {
let operands: Vec<_> = asm
.operands
.iter()
.map(|(op, op_sp)| match *op {
hir::InlineAsmOperand::Const { ref anon_const } => {
let anon_const_def_id =
cx.tcx().hir().local_def_id(anon_const.hir_id).to_def_id();
let const_value =
cx.tcx().const_eval_poly(anon_const_def_id).unwrap_or_else(
|_| span_bug!(*op_sp, "asm const cannot be resolved"),
);
let ty = cx
.tcx()
.typeck_body(anon_const.body)
.node_type(anon_const.hir_id);
let string = common::asm_const_to_str(
cx.tcx(),
*op_sp,
const_value,
cx.layout_of(ty),
);
GlobalAsmOperandRef::Const { string }
}
_ => span_bug!(*op_sp, "invalid operand type for global_asm!"),
})
.collect();
cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans);
} else {
span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
}

View File

@ -3,7 +3,7 @@ use crate::mir::operand::OperandRef;
use crate::mir::place::PlaceRef;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::def_id::DefId;
use rustc_hir::{GlobalAsm, LlvmInlineAsmInner};
use rustc_hir::LlvmInlineAsmInner;
use rustc_middle::ty::Instance;
use rustc_span::Span;
use rustc_target::asm::InlineAsmRegOrRegClass;
@ -36,6 +36,11 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> {
},
}
#[derive(Debug)]
pub enum GlobalAsmOperandRef {
Const { string: String },
}
pub trait AsmBuilderMethods<'tcx>: BackendTypes {
/// Take an inline assembly expression and splat it out via LLVM
fn codegen_llvm_inline_asm(
@ -57,5 +62,11 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes {
}
pub trait AsmMethods {
fn codegen_global_asm(&self, ga: &GlobalAsm);
fn codegen_global_asm(
&self,
template: &[InlineAsmTemplatePiece],
operands: &[GlobalAsmOperandRef],
options: InlineAsmOptions,
line_spans: &[Span],
);
}

View File

@ -29,7 +29,7 @@ mod type_;
mod write;
pub use self::abi::AbiBuilderMethods;
pub use self::asm::{AsmBuilderMethods, AsmMethods, InlineAsmOperandRef};
pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
pub use self::builder::{BuilderMethods, OverflowOp};
pub use self::consts::ConstMethods;

View File

@ -19,7 +19,6 @@ macro_rules! arena_types {
[] attribute: rustc_ast::Attribute,
[] block: rustc_hir::Block<$tcx>,
[] bare_fn_ty: rustc_hir::BareFnTy<$tcx>,
[few] global_asm: rustc_hir::GlobalAsm,
[] generic_arg: rustc_hir::GenericArg<$tcx>,
[] generic_args: rustc_hir::GenericArgs<$tcx>,
[] generic_bound: rustc_hir::GenericBound<$tcx>,

View File

@ -2502,11 +2502,6 @@ pub struct Mod<'hir> {
pub item_ids: &'hir [ItemId],
}
#[derive(Encodable, Debug, HashStable_Generic)]
pub struct GlobalAsm {
pub asm: Symbol,
}
#[derive(Debug, HashStable_Generic)]
pub struct EnumDef<'hir> {
pub variants: &'hir [Variant<'hir>],
@ -2766,7 +2761,7 @@ pub enum ItemKind<'hir> {
/// An external module, e.g. `extern { .. }`.
ForeignMod { abi: Abi, items: &'hir [ForeignItemRef<'hir>] },
/// Module-level inline assembly (from `global_asm!`).
GlobalAsm(&'hir GlobalAsm),
GlobalAsm(&'hir InlineAsm<'hir>),
/// A type alias, e.g., `type Foo = Bar<u8>`.
TyAlias(&'hir Ty<'hir>, Generics<'hir>),
/// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`.

View File

@ -589,8 +589,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
visitor.visit_id(item.hir_id());
walk_list!(visitor, visit_foreign_item_ref, items);
}
ItemKind::GlobalAsm(_) => {
ItemKind::GlobalAsm(asm) => {
visitor.visit_id(item.hir_id());
walk_inline_asm(visitor, asm);
}
ItemKind::TyAlias(ref ty, ref generics) => {
visitor.visit_id(item.hir_id());
@ -650,6 +651,28 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
}
}
fn walk_inline_asm<'v, V: Visitor<'v>>(visitor: &mut V, asm: &'v InlineAsm<'v>) {
for (op, _op_sp) in asm.operands {
match op {
InlineAsmOperand::In { expr, .. }
| InlineAsmOperand::InOut { expr, .. }
| InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr),
InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
visitor.visit_expr(expr);
}
}
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
visitor.visit_expr(in_expr);
if let Some(out_expr) = out_expr {
visitor.visit_expr(out_expr);
}
}
InlineAsmOperand::Const { anon_const } => visitor.visit_anon_const(anon_const),
}
}
}
pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) {
visitor.visit_id(hir_id);
visitor.visit_path(path, hir_id);
@ -1185,27 +1208,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
walk_list!(visitor, visit_expr, optional_expression);
}
ExprKind::InlineAsm(ref asm) => {
for (op, _op_sp) in asm.operands {
match op {
InlineAsmOperand::In { expr, .. }
| InlineAsmOperand::InOut { expr, .. }
| InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr),
InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
visitor.visit_expr(expr);
}
}
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
visitor.visit_expr(in_expr);
if let Some(out_expr) = out_expr {
visitor.visit_expr(out_expr);
}
}
InlineAsmOperand::Const { anon_const, .. } => {
visitor.visit_anon_const(anon_const)
}
}
}
walk_inline_asm(visitor, asm);
}
ExprKind::LlvmInlineAsm(ref asm) => {
walk_list!(visitor, visit_expr, asm.outputs_exprs);

View File

@ -660,9 +660,9 @@ impl<'a> State<'a> {
}
self.bclose(item.span);
}
hir::ItemKind::GlobalAsm(ref ga) => {
self.head(visibility_qualified(&item.vis, "global asm"));
self.s.word(ga.asm.to_string());
hir::ItemKind::GlobalAsm(ref asm) => {
self.head(visibility_qualified(&item.vis, "global_asm!"));
self.print_inline_asm(asm);
self.end()
}
hir::ItemKind::TyAlias(ref ty, ref generics) => {
@ -1352,6 +1352,110 @@ impl<'a> State<'a> {
self.word(lit.node.to_lit_token().to_string())
}
fn print_inline_asm(&mut self, asm: &hir::InlineAsm<'_>) {
enum AsmArg<'a> {
Template(String),
Operand(&'a hir::InlineAsmOperand<'a>),
Options(ast::InlineAsmOptions),
}
let mut args = vec![];
args.push(AsmArg::Template(ast::InlineAsmTemplatePiece::to_string(&asm.template)));
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
if !asm.options.is_empty() {
args.push(AsmArg::Options(asm.options));
}
self.popen();
self.commasep(Consistent, &args, |s, arg| match arg {
AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
AsmArg::Operand(op) => match op {
hir::InlineAsmOperand::In { reg, expr } => {
s.word("in");
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
s.print_expr(expr);
}
hir::InlineAsmOperand::Out { reg, late, expr } => {
s.word(if *late { "lateout" } else { "out" });
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
match expr {
Some(expr) => s.print_expr(expr),
None => s.word("_"),
}
}
hir::InlineAsmOperand::InOut { reg, late, expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
s.print_expr(expr);
}
hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
s.print_expr(in_expr);
s.space();
s.word_space("=>");
match out_expr {
Some(out_expr) => s.print_expr(out_expr),
None => s.word("_"),
}
}
hir::InlineAsmOperand::Const { anon_const } => {
s.word("const");
s.space();
s.print_anon_const(anon_const);
}
hir::InlineAsmOperand::Sym { expr } => {
s.word("sym");
s.space();
s.print_expr(expr);
}
},
AsmArg::Options(opts) => {
s.word("options");
s.popen();
let mut options = vec![];
if opts.contains(ast::InlineAsmOptions::PURE) {
options.push("pure");
}
if opts.contains(ast::InlineAsmOptions::NOMEM) {
options.push("nomem");
}
if opts.contains(ast::InlineAsmOptions::READONLY) {
options.push("readonly");
}
if opts.contains(ast::InlineAsmOptions::PRESERVES_FLAGS) {
options.push("preserves_flags");
}
if opts.contains(ast::InlineAsmOptions::NORETURN) {
options.push("noreturn");
}
if opts.contains(ast::InlineAsmOptions::NOSTACK) {
options.push("nostack");
}
if opts.contains(ast::InlineAsmOptions::ATT_SYNTAX) {
options.push("att_syntax");
}
s.commasep(Inconsistent, &options, |s, &opt| {
s.word(opt);
});
s.pclose();
}
});
self.pclose();
}
pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
self.maybe_print_comment(expr.span.lo());
self.print_outer_attributes(self.attrs(expr.hir_id));
@ -1530,109 +1634,9 @@ impl<'a> State<'a> {
self.print_expr_maybe_paren(&expr, parser::PREC_JUMP);
}
}
hir::ExprKind::InlineAsm(ref a) => {
enum AsmArg<'a> {
Template(String),
Operand(&'a hir::InlineAsmOperand<'a>),
Options(ast::InlineAsmOptions),
}
let mut args = vec![];
args.push(AsmArg::Template(ast::InlineAsmTemplatePiece::to_string(&a.template)));
args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
if !a.options.is_empty() {
args.push(AsmArg::Options(a.options));
}
hir::ExprKind::InlineAsm(ref asm) => {
self.word("asm!");
self.popen();
self.commasep(Consistent, &args, |s, arg| match arg {
AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
AsmArg::Operand(op) => match op {
hir::InlineAsmOperand::In { reg, expr } => {
s.word("in");
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
s.print_expr(expr);
}
hir::InlineAsmOperand::Out { reg, late, expr } => {
s.word(if *late { "lateout" } else { "out" });
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
match expr {
Some(expr) => s.print_expr(expr),
None => s.word("_"),
}
}
hir::InlineAsmOperand::InOut { reg, late, expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
s.print_expr(expr);
}
hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
s.word(if *late { "inlateout" } else { "inout" });
s.popen();
s.word(format!("{}", reg));
s.pclose();
s.space();
s.print_expr(in_expr);
s.space();
s.word_space("=>");
match out_expr {
Some(out_expr) => s.print_expr(out_expr),
None => s.word("_"),
}
}
hir::InlineAsmOperand::Const { anon_const } => {
s.word("const");
s.space();
s.print_anon_const(anon_const);
}
hir::InlineAsmOperand::Sym { expr } => {
s.word("sym");
s.space();
s.print_expr(expr);
}
},
AsmArg::Options(opts) => {
s.word("options");
s.popen();
let mut options = vec![];
if opts.contains(ast::InlineAsmOptions::PURE) {
options.push("pure");
}
if opts.contains(ast::InlineAsmOptions::NOMEM) {
options.push("nomem");
}
if opts.contains(ast::InlineAsmOptions::READONLY) {
options.push("readonly");
}
if opts.contains(ast::InlineAsmOptions::PRESERVES_FLAGS) {
options.push("preserves_flags");
}
if opts.contains(ast::InlineAsmOptions::NORETURN) {
options.push("noreturn");
}
if opts.contains(ast::InlineAsmOptions::NOSTACK) {
options.push("nostack");
}
if opts.contains(ast::InlineAsmOptions::ATT_SYNTAX) {
options.push("att_syntax");
}
s.commasep(Inconsistent, &options, |s, &opt| {
s.word(opt);
});
s.pclose();
}
});
self.pclose();
self.print_inline_asm(asm);
}
hir::ExprKind::LlvmInlineAsm(ref a) => {
let i = &a.inner;

View File

@ -390,8 +390,24 @@ fn collect_items_rec<'tcx>(
collect_neighbours(tcx, instance, &mut neighbors);
});
}
MonoItem::GlobalAsm(..) => {
MonoItem::GlobalAsm(item_id) => {
recursion_depth_reset = None;
let item = tcx.hir().item(item_id);
if let hir::ItemKind::GlobalAsm(asm) = item.kind {
for (op, op_sp) in asm.operands {
match op {
hir::InlineAsmOperand::Const { .. } => {
// Only constants which resolve to a plain integer
// are supported. Therefore the value should not
// depend on any other items.
}
_ => span_bug!(*op_sp, "invalid operand type for global_asm!"),
}
}
} else {
span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
}
}
}

View File

@ -405,6 +405,33 @@ impl Visitor<'tcx> for ItemVisitor<'tcx> {
ExprVisitor { tcx: self.tcx, param_env, typeck_results }.visit_body(body);
self.visit_body(body);
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
if let hir::ItemKind::GlobalAsm(asm) = item.kind {
for (op, op_sp) in asm.operands {
match *op {
hir::InlineAsmOperand::Const { ref anon_const } => {
let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id);
match value.ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
_ => {
let msg = "asm `const` arguments must be integer or floating-point values";
self.tcx.sess.span_err(*op_sp, msg);
}
}
}
hir::InlineAsmOperand::In { .. }
| hir::InlineAsmOperand::Out { .. }
| hir::InlineAsmOperand::InOut { .. }
| hir::InlineAsmOperand::SplitInOut { .. }
| hir::InlineAsmOperand::Sym { .. } => unreachable!(),
}
}
}
intravisit::walk_item(self, item);
}
}
impl Visitor<'tcx> for ExprVisitor<'tcx> {

View File

@ -1066,10 +1066,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.future_proof_import(use_tree);
}
ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) => {
ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) => {
// do nothing, these are just around to be encoded
}
ItemKind::GlobalAsm(_) => {
visit::walk_item(self, item);
}
ItemKind::MacCall(_) => panic!("unexpanded macro in resolve!"),
}
}

View File

@ -545,8 +545,9 @@ fn typeck_with_fallback<'tcx>(
kind: TypeVariableOriginKind::TypeInference,
span,
}),
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(ia), .. })
if ia.operands.iter().any(|(op, _op_sp)| match op {
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
| Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } => {
anon_const.hir_id == id
}

View File

@ -450,8 +450,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
tcx.typeck(def_id).node_type(anon_const.hir_id)
}
Node::Expr(&Expr { kind: ExprKind::InlineAsm(ia), .. })
if ia.operands.iter().any(|(op, _op_sp)| match op {
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
| Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } => anon_const.hir_id == hir_id,
_ => false,
}) =>

View File

@ -11,7 +11,7 @@ edition = "2018"
[dependencies]
core = { path = "../core" }
compiler_builtins = { version = "0.1.39", features = ['rustc-dep-of-std'] }
compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] }
[dev-dependencies]
rand = "0.7"

View File

@ -1358,7 +1358,10 @@ pub(crate) mod builtin {
#[rustc_builtin_macro]
#[macro_export]
macro_rules! global_asm {
("assembly") => {
("assembly template",
$(operands,)*
$(options($(option),*))?
) => {
/* compiler built-in */
};
}

View File

@ -17,7 +17,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
libc = { version = "0.2.93", default-features = false, features = ['rustc-dep-of-std'] }
compiler_builtins = { version = "0.1.39" }
compiler_builtins = { version = "0.1.43" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] }

View File

@ -15,7 +15,7 @@ pub mod tls;
pub mod usercalls;
#[cfg(not(test))]
global_asm!(include_str!("entry.S"));
global_asm!(include_str!("entry.S"), options(att_syntax));
#[repr(C)]
struct EntryReturn(u64, u64);

View File

@ -8,12 +8,9 @@ The tracking issue for this feature is: [#35119]
The `global_asm!` macro allows the programmer to write arbitrary
assembly outside the scope of a function body, passing it through
`rustc` and `llvm` to the assembler. The macro is a no-frills
interface to LLVM's concept of [module-level inline assembly]. That is,
all caveats applicable to LLVM's module-level inline assembly apply
to `global_asm!`.
[module-level inline assembly]: http://llvm.org/docs/LangRef.html#module-level-inline-assembly
`rustc` and `llvm` to the assembler. That is to say, `global_asm!` is
equivalent to assembling the asm with an external assembler and then
linking the resulting object file with the current crate.
`global_asm!` fills a role not currently satisfied by either `asm!`
or `#[naked]` functions. The programmer has _all_ features of the
@ -38,11 +35,11 @@ And a more complicated usage looks like this:
# mod x86 {
pub mod sally {
global_asm!(r#"
.global foo
foo:
jmp baz
"#);
global_asm!(
".global foo",
"foo:",
"jmp baz",
);
#[no_mangle]
pub unsafe extern "C" fn baz() {}
@ -56,11 +53,11 @@ extern "C" {
}
pub mod harry {
global_asm!(r#"
.global bar
bar:
jmp quux
"#);
global_asm!(
".global bar",
"bar:",
"jmp quux",
);
#[no_mangle]
pub unsafe extern "C" fn quux() {}
@ -69,8 +66,44 @@ pub mod harry {
```
You may use `global_asm!` multiple times, anywhere in your crate, in
whatever way suits you. The effect is as if you concatenated all
usages and placed the larger, single usage in the crate root.
whatever way suits you. However, you should not rely on assembler state
(e.g. assembler macros) defined in one `global_asm!` to be available in
another one. It is implementation-defined whether the multiple usages
are concatenated into one or assembled separately.
`global_asm!` also supports `const` operands like `asm!`, which allows
constants defined in Rust to be used in assembly code:
```rust,no_run
#![feature(global_asm)]
# #[cfg(any(target_arch="x86", target_arch="x86_64"))]
# mod x86 {
const C: i32 = 1234;
global_asm!(
".global bar",
"bar: .word {c}",
c = const C,
);
# }
```
The syntax for passing operands is the same as `asm!` except that only
`const` operands are allowed. Refer to the [asm](asm.md) documentation
for more details.
On x86, the assembly code will use intel syntax by default. You can
override this by adding `options(att_syntax)` at the end of the macro
arguments list:
```rust,no_run
#![feature(global_asm)]
# #[cfg(any(target_arch="x86", target_arch="x86_64"))]
# mod x86 {
global_asm!("movl ${}, %ecx", const 5, options(att_syntax));
// is equivalent to
global_asm!("mov ecx, {}", const 5);
# }
```
------------------------

View File

@ -0,0 +1,14 @@
// min-llvm-version: 10.0.1
// only-x86_64
// assembly-output: emit-asm
// compile-flags: -C llvm-args=--x86-asm-syntax=intel
#![feature(asm, global_asm)]
#![crate_type = "rlib"]
// CHECK: mov eax, eax
global_asm!("mov eax, eax");
// CHECK: mov ebx, 5
global_asm!("mov ebx, {}", const 5);
// CHECK: mov ecx, 5
global_asm!("movl ${}, %ecx", const 5, options(att_syntax));

View File

@ -8,7 +8,7 @@ rust_plus_one_global_asm:
movl (%rdi), %eax
inc %eax
retq
"# );
"#, options(att_syntax));
extern {
fn cc_plus_one_c(arg : &u32) -> u32;

View File

@ -8,12 +8,19 @@
macro_rules! asm {
() => {};
}
#[rustc_builtin_macro]
macro_rules! global_asm {
() => {};
}
#[lang = "sized"]
trait Sized {}
fn main() {
unsafe {
asm!("");
//~^ ERROR asm! is unsupported on this target
//~^ ERROR inline assembly is unsupported on this target
}
}
global_asm!("");
//~^ ERROR inline assembly is unsupported on this target

View File

@ -1,8 +1,16 @@
error[E0472]: asm! is unsupported on this target
--> $DIR/bad-arch.rs:16:9
error[E0472]: inline assembly is unsupported on this target
--> $DIR/bad-arch.rs:20:9
|
LL | asm!("");
| ^^^^^^^^^
error: aborting due to previous error
error[E0472]: inline assembly is unsupported on this target
--> $DIR/bad-arch.rs:25:1
|
LL | global_asm!("");
| ^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `global_asm` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors

View File

@ -1,6 +1,6 @@
// only-x86_64
#![feature(asm)]
#![feature(asm, global_asm)]
fn main() {
let mut foo = 0;
@ -16,3 +16,16 @@ fn main() {
//~^ ERROR asm outputs are not allowed with the `noreturn` option
}
}
global_asm!("", options(nomem));
//~^ ERROR expected one of
global_asm!("", options(readonly));
//~^ ERROR expected one of
global_asm!("", options(noreturn));
//~^ ERROR expected one of
global_asm!("", options(pure));
//~^ ERROR expected one of
global_asm!("", options(nostack));
//~^ ERROR expected one of
global_asm!("", options(preserves_flags));
//~^ ERROR expected one of

View File

@ -28,5 +28,41 @@ error: asm outputs are not allowed with the `noreturn` option
LL | asm!("{}", out(reg) foo, options(noreturn));
| ^^^^^^^^^^^^
error: aborting due to 5 previous errors
error: expected one of `)` or `att_syntax`, found `nomem`
--> $DIR/bad-options.rs:20:25
|
LL | global_asm!("", options(nomem));
| ^^^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `readonly`
--> $DIR/bad-options.rs:22:25
|
LL | global_asm!("", options(readonly));
| ^^^^^^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `noreturn`
--> $DIR/bad-options.rs:24:25
|
LL | global_asm!("", options(noreturn));
| ^^^^^^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `pure`
--> $DIR/bad-options.rs:26:25
|
LL | global_asm!("", options(pure));
| ^^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `nostack`
--> $DIR/bad-options.rs:28:25
|
LL | global_asm!("", options(nostack));
| ^^^^^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `preserves_flags`
--> $DIR/bad-options.rs:30:25
|
LL | global_asm!("", options(preserves_flags));
| ^^^^^^^^^^^^^^^ expected one of `)` or `att_syntax`
error: aborting due to 11 previous errors

View File

@ -1,6 +1,6 @@
// only-x86_64
#![feature(asm)]
#![feature(asm, global_asm)]
fn main() {
let mut foo = 0;
@ -26,3 +26,22 @@ fn main() {
//~^ ERROR multiple unused asm arguments
}
}
const FOO: i32 = 1;
global_asm!("{}");
//~^ ERROR invalid reference to argument at index 0
global_asm!("{1}", const FOO);
//~^ ERROR invalid reference to argument at index 1
//~^^ ERROR argument never used
global_asm!("{a}");
//~^ ERROR there is no argument named `a`
global_asm!("{}", a = const FOO);
//~^ ERROR invalid reference to argument at index 0
//~^^ ERROR argument never used
global_asm!("{1}", a = const FOO);
//~^ ERROR invalid reference to argument at index 1
//~^^ ERROR named argument never used
global_asm!("{:foo}", const FOO);
//~^ ERROR asm template modifier must be a single character
global_asm!("", const FOO, const FOO);
//~^ ERROR multiple unused asm arguments

View File

@ -98,5 +98,90 @@ LL | asm!("", in(reg) 0, in(reg) 1);
|
= help: if these arguments are intentionally unused, consider using them in an asm comment: `"/* {0} {1} */"`
error: aborting due to 11 previous errors
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:31:14
|
LL | global_asm!("{}");
| ^^ from here
|
= note: no arguments were given
error: invalid reference to argument at index 1
--> $DIR/bad-template.rs:33:14
|
LL | global_asm!("{1}", const FOO);
| ^^^ from here
|
= note: there is 1 argument
error: argument never used
--> $DIR/bad-template.rs:33:20
|
LL | global_asm!("{1}", const FOO);
| ^^^^^^^^^ argument never used
|
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
--> $DIR/bad-template.rs:36:14
|
LL | global_asm!("{a}");
| ^^^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:14
|
LL | global_asm!("{}", a = const FOO);
| ^^ ------------- named argument
| |
| from here
|
= note: no positional arguments were given
note: named arguments cannot be referenced by position
--> $DIR/bad-template.rs:38:19
|
LL | global_asm!("{}", a = const FOO);
| ^^^^^^^^^^^^^
error: named argument never used
--> $DIR/bad-template.rs:38:19
|
LL | global_asm!("{}", a = const FOO);
| ^^^^^^^^^^^^^ named argument never used
|
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {a} */"`
error: invalid reference to argument at index 1
--> $DIR/bad-template.rs:41:14
|
LL | global_asm!("{1}", a = const FOO);
| ^^^ from here
|
= note: no positional arguments were given
error: named argument never used
--> $DIR/bad-template.rs:41:20
|
LL | global_asm!("{1}", a = const FOO);
| ^^^^^^^^^^^^^ named argument never used
|
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {a} */"`
error: asm template modifier must be a single character
--> $DIR/bad-template.rs:44:16
|
LL | global_asm!("{:foo}", const FOO);
| ^^^
error: multiple unused asm arguments
--> $DIR/bad-template.rs:46:17
|
LL | global_asm!("", const FOO, const FOO);
| ^^^^^^^^^ ^^^^^^^^^ argument never used
| |
| argument never used
|
= help: if these arguments are intentionally unused, consider using them in an asm comment: `"/* {0} {1} */"`
error: aborting due to 21 previous errors

View File

@ -2,7 +2,7 @@
// only-x86_64
// run-pass
#![feature(asm)]
#![feature(asm, global_asm)]
fn const_generic<const X: usize>() -> usize {
unsafe {
@ -34,3 +34,7 @@ fn main() {
let d = const_generic::<5>();
assert_eq!(d, 5);
}
global_asm!("mov eax, {}", const 5);
global_asm!("mov eax, {}", const constfn(5));
global_asm!("mov eax, {}", const constfn(5) + constfn(5));

View File

@ -1,7 +1,7 @@
// only-x86_64
// run-rustfix
#![feature(asm)]
#![feature(asm, global_asm)]
fn main() {
unsafe {
@ -24,3 +24,6 @@ fn main() {
);
}
}
global_asm!("", options(att_syntax, ));
//~^ ERROR the `att_syntax` option was already provided

View File

@ -1,7 +1,7 @@
// only-x86_64
// run-rustfix
#![feature(asm)]
#![feature(asm, global_asm)]
fn main() {
unsafe {
@ -24,3 +24,6 @@ fn main() {
);
}
}
global_asm!("", options(att_syntax, att_syntax));
//~^ ERROR the `att_syntax` option was already provided

View File

@ -52,5 +52,11 @@ error: the `noreturn` option was already provided
LL | options(noreturn),
| ^^^^^^^^ this option was already provided
error: aborting due to 9 previous errors
error: the `att_syntax` option was already provided
--> $DIR/duplicate-options.rs:28:37
|
LL | global_asm!("", options(att_syntax, att_syntax));
| ^^^^^^^^^^ this option was already provided
error: aborting due to 10 previous errors

View File

@ -1,5 +1,19 @@
error: unknown directive
--> $DIR/inline-syntax.rs:25:15
.intel_syntax noprefix
^
error: unknown directive
.intel_syntax noprefix
^
error: unknown directive
|
note: instantiated into assembly here
--> <inline asm>:1:1
|
LL | .intel_syntax noprefix
| ^
error: unknown directive
--> $DIR/inline-syntax.rs:29:15
|
LL | asm!(".intel_syntax noprefix", "nop");
| ^
@ -11,7 +25,7 @@ LL | .intel_syntax noprefix
| ^
error: unknown directive
--> $DIR/inline-syntax.rs:28:15
--> $DIR/inline-syntax.rs:32:15
|
LL | asm!(".intel_syntax aaa noprefix", "nop");
| ^
@ -23,7 +37,7 @@ LL | .intel_syntax aaa noprefix
| ^
error: unknown directive
--> $DIR/inline-syntax.rs:31:15
--> $DIR/inline-syntax.rs:35:15
|
LL | asm!(".att_syntax noprefix", "nop");
| ^
@ -35,7 +49,7 @@ LL | .att_syntax noprefix
| ^
error: unknown directive
--> $DIR/inline-syntax.rs:34:15
--> $DIR/inline-syntax.rs:38:15
|
LL | asm!(".att_syntax bbb noprefix", "nop");
| ^
@ -47,7 +61,7 @@ LL | .att_syntax bbb noprefix
| ^
error: unknown directive
--> $DIR/inline-syntax.rs:37:15
--> $DIR/inline-syntax.rs:41:15
|
LL | asm!(".intel_syntax noprefix; nop");
| ^
@ -59,7 +73,7 @@ LL | .intel_syntax noprefix; nop
| ^
error: unknown directive
--> $DIR/inline-syntax.rs:43:13
--> $DIR/inline-syntax.rs:47:13
|
LL | .intel_syntax noprefix
| ^
@ -70,5 +84,5 @@ note: instantiated into assembly here
LL | .intel_syntax noprefix
| ^
error: aborting due to 6 previous errors
error: aborting due to 7 previous errors

View File

@ -16,6 +16,10 @@
macro_rules! asm {
() => {};
}
#[rustc_builtin_macro]
macro_rules! global_asm {
() => {};
}
#[lang = "sized"]
trait Sized {}
@ -47,3 +51,7 @@ pub fn main() {
//[arm]~^^^^ ERROR unknown directive
}
}
global_asm!(".intel_syntax noprefix", "nop");
//[x86_64]~^ WARN avoid using `.intel_syntax`
// Assembler errors don't have line numbers, so no error on ARM

View File

@ -1,40 +1,46 @@
warning: avoid using `.intel_syntax`, Intel syntax is the default
--> $DIR/inline-syntax.rs:25:15
--> $DIR/inline-syntax.rs:55:14
|
LL | asm!(".intel_syntax noprefix", "nop");
| ^^^^^^^^^^^^^^^^^^^^^^
LL | global_asm!(".intel_syntax noprefix", "nop");
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(bad_asm_style)]` on by default
warning: avoid using `.intel_syntax`, Intel syntax is the default
--> $DIR/inline-syntax.rs:28:15
--> $DIR/inline-syntax.rs:29:15
|
LL | asm!(".intel_syntax noprefix", "nop");
| ^^^^^^^^^^^^^^^^^^^^^^
warning: avoid using `.intel_syntax`, Intel syntax is the default
--> $DIR/inline-syntax.rs:32:15
|
LL | asm!(".intel_syntax aaa noprefix", "nop");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead
--> $DIR/inline-syntax.rs:31:15
--> $DIR/inline-syntax.rs:35:15
|
LL | asm!(".att_syntax noprefix", "nop");
| ^^^^^^^^^^^^^^^^^^^^
warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead
--> $DIR/inline-syntax.rs:34:15
--> $DIR/inline-syntax.rs:38:15
|
LL | asm!(".att_syntax bbb noprefix", "nop");
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: avoid using `.intel_syntax`, Intel syntax is the default
--> $DIR/inline-syntax.rs:37:15
--> $DIR/inline-syntax.rs:41:15
|
LL | asm!(".intel_syntax noprefix; nop");
| ^^^^^^^^^^^^^^^^^^^^^^
warning: avoid using `.intel_syntax`, Intel syntax is the default
--> $DIR/inline-syntax.rs:43:13
--> $DIR/inline-syntax.rs:47:13
|
LL | .intel_syntax noprefix
| ^^^^^^^^^^^^^^^^^^^^^^
warning: 6 warnings emitted
warning: 7 warnings emitted

View File

@ -1,6 +1,6 @@
// only-x86_64
#![feature(asm)]
#![feature(asm, global_asm)]
fn main() {
let mut foo = 0;
@ -63,3 +63,37 @@ fn main() {
//~^ ERROR asm template must be a string literal
}
}
const FOO: i32 = 1;
const BAR: i32 = 2;
global_asm!();
//~^ ERROR requires at least a template string argument
global_asm!(FOO);
//~^ ERROR asm template must be a string literal
global_asm!("{}" FOO);
//~^ ERROR expected token: `,`
global_asm!("{}", FOO);
//~^ ERROR expected operand, options, or additional template string
global_asm!("{}", const);
//~^ ERROR expected expression, found end of macro arguments
global_asm!("{}", const(reg) FOO);
//~^ ERROR expected one of
global_asm!("", options(FOO));
//~^ ERROR expected one of
global_asm!("", options(nomem FOO));
//~^ ERROR expected one of
global_asm!("", options(nomem, FOO));
//~^ ERROR expected one of
global_asm!("{}", options(), const FOO);
//~^ ERROR arguments are not allowed after options
global_asm!("{a}", a = const FOO, a = const BAR);
//~^ ERROR duplicate argument named `a`
//~^^ ERROR argument never used
global_asm!("", options(), "");
//~^ ERROR expected one of
global_asm!("{}", const FOO, "{}", const FOO);
//~^ ERROR expected one of
global_asm!(format!("{{{}}}", 0), const FOO);
//~^ ERROR asm template must be a string literal
global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
//~^ ERROR asm template must be a string literal

View File

@ -164,6 +164,112 @@ 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
|
LL | global_asm!();
| ^^^^^^^^^^^^^^
error: asm template must be a string literal
--> $DIR/parse-error.rs:71:13
|
LL | global_asm!(FOO);
| ^^^
error: expected token: `,`
--> $DIR/parse-error.rs:73:18
|
LL | global_asm!("{}" FOO);
| ^^^ expected `,`
error: expected operand, options, or additional template string
--> $DIR/parse-error.rs:75: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
|
LL | global_asm!("{}", const);
| ^ expected expression
error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
--> $DIR/parse-error.rs:79:30
|
LL | global_asm!("{}", const(reg) FOO);
| ^^^ expected one of `,`, `.`, `?`, or an operator
error: expected one of `)` or `att_syntax`, found `FOO`
--> $DIR/parse-error.rs:81:25
|
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `nomem`
--> $DIR/parse-error.rs:83:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ expected one of `)` or `att_syntax`
error: expected one of `)` or `att_syntax`, found `nomem`
--> $DIR/parse-error.rs:85:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ expected one of `)` or `att_syntax`
error: arguments are not allowed after options
--> $DIR/parse-error.rs:87:30
|
LL | global_asm!("{}", options(), const FOO);
| --------- ^^^^^^^^^ argument
| |
| previous options
error: duplicate argument named `a`
--> $DIR/parse-error.rs:89:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ------------- ^^^^^^^^^^^^^ duplicate argument
| |
| previously here
error: argument never used
--> $DIR/parse-error.rs:89: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
|
LL | global_asm!("", options(), "");
| ^^ expected one of `const` or `options`
error: expected one of `const` or `options`, found `"{}"`
--> $DIR/parse-error.rs:94:30
|
LL | global_asm!("{}", const FOO, "{}", const FOO);
| ^^^^ expected one of `const` or `options`
error: asm template must be a string literal
--> $DIR/parse-error.rs:96:13
|
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
|
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:37:37
|
@ -218,6 +324,6 @@ LL | let mut bar = 0;
LL | asm!("{1}", in("eax") foo, const bar);
| ^^^ non-constant value
error: aborting due to 31 previous errors
error: aborting due to 47 previous errors
For more information about this error, try `rustc --explain E0435`.

View File

@ -1,6 +1,6 @@
// only-x86_64
#![feature(asm, repr_simd, never_type)]
#![feature(asm, global_asm, repr_simd, never_type)]
#[repr(simd)]
struct SimdNonCopy(f32, f32, f32, f32);
@ -90,3 +90,11 @@ fn main() {
asm!("{}", in(reg) u);
}
}
// Const operands must be integer or floats, and must be constants.
global_asm!("{}", const 0);
global_asm!("{}", const 0i32);
global_asm!("{}", const 0f32);
global_asm!("{}", const 0 as *mut u8);
//~^ ERROR asm `const` arguments must be integer or floating-point values

View File

@ -61,6 +61,12 @@ LL | asm!("{}", inout(reg) r);
|
= note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
error: asm `const` arguments must be integer or floating-point values
--> $DIR/type-check-2.rs:99:19
|
LL | global_asm!("{}", const 0 as *mut u8);
| ^^^^^^^^^^^^^^^^^^
error: asm `sym` operand must point to a fn or static
--> $DIR/type-check-2.rs:47:24
|
@ -103,7 +109,7 @@ LL | let v: Vec<u64> = vec![0, 1, 2];
LL | asm!("{}", inout(reg) v[0]);
| ^ cannot borrow as mutable
error: aborting due to 14 previous errors
error: aborting due to 15 previous errors
Some errors have detailed explanations: E0381, E0596.
For more information about an error, try `rustc --explain E0381`.

View File

@ -1,7 +1,7 @@
// only-x86_64
// compile-flags: -C target-feature=+avx512f
#![feature(asm)]
#![feature(asm, global_asm)]
use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps};
@ -69,3 +69,21 @@ fn main() {
asm!("{:r}", inout(reg) main => val_u64);
}
}
// Constants must be... constant
static S: i32 = 1;
const fn const_foo(x: i32) -> i32 {
x
}
const fn const_bar<T>(x: T) -> T {
x
}
global_asm!("{}", const S);
//~^ ERROR constants cannot refer to statics
global_asm!("{}", const const_foo(0));
global_asm!("{}", const const_foo(S));
//~^ ERROR constants cannot refer to statics
global_asm!("{}", const const_bar(0));
global_asm!("{}", const const_bar(S));
//~^ ERROR constants cannot refer to statics

View File

@ -114,5 +114,30 @@ LL | asm!("{:r}", inout(reg) main => val_u32);
|
= note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size
error: aborting due to 9 previous errors; 4 warnings emitted
error[E0013]: constants cannot refer to statics
--> $DIR/type-check-3.rs:82:25
|
LL | global_asm!("{}", const S);
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
error[E0013]: constants cannot refer to statics
--> $DIR/type-check-3.rs:85:35
|
LL | global_asm!("{}", const const_foo(S));
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
error[E0013]: constants cannot refer to statics
--> $DIR/type-check-3.rs:88:35
|
LL | global_asm!("{}", const const_bar(S));
| ^
|
= help: consider extracting the value of the `static` to a `const`, and referring to that
error: aborting due to 12 previous errors; 4 warnings emitted
For more information about this error, try `rustc --explain E0013`.

View File

@ -1,7 +1,7 @@
#![feature(global_asm)]
fn main() {
global_asm!(); //~ ERROR requires a string literal as an argument
global_asm!(); //~ ERROR requires at least a template string argument
global_asm!(struct); //~ ERROR expected expression
global_asm!(123); //~ ERROR inline assembly must be a string literal
global_asm!(123); //~ ERROR asm template must be a string literal
}

View File

@ -1,8 +1,8 @@
error: macro requires a string literal as an argument
error: requires at least a template string argument
--> $DIR/global-asm.rs:4:5
|
LL | global_asm!();
| ^^^^^^^^^^^^^^ string literal required
| ^^^^^^^^^^^^^^
error: expected expression, found keyword `struct`
--> $DIR/global-asm.rs:5:17
@ -10,7 +10,7 @@ error: expected expression, found keyword `struct`
LL | global_asm!(struct);
| ^^^^^^ expected expression
error: inline assembly must be a string literal
error: asm template must be a string literal
--> $DIR/global-asm.rs:6:17
|
LL | global_asm!(123);