diff --git a/Cargo.lock b/Cargo.lock
index f9a607c56db..d237997843c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -655,9 +655,9 @@ dependencies = [
[[package]]
name = "compiler_builtins"
-version = "0.1.39"
+version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
+checksum = "288a0d48b8155926ebb4552bdde3fa32744ce424c5de0a26ddbc68369aeb7172"
dependencies = [
"cc",
"rustc-std-workspace-core",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index ac69fa20202..fb012d9802f 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2279,14 +2279,6 @@ pub struct ForeignMod {
pub items: Vec
>,
}
-/// 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,
@@ -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;`.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 05f57f978c7..374a6ec972f 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -965,7 +965,7 @@ pub fn noop_visit_item_kind(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(AnonConst { id, value }: &mut AnonCo
vis.visit_expr(value);
}
+fn noop_visit_inline_asm(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(
Expr { kind, id, span, attrs, tokens }: &mut Expr,
vis: &mut T,
@@ -1288,27 +1310,7 @@ pub fn noop_visit_expr(
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: _,
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 3f35919ae6a..c50b334d3e9 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -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)
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
new file mode 100644
index 00000000000..6acdfa1b5f8
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -0,0 +1,328 @@
+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, "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,
+ 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)
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index ea0770daf0e..483135ed3a3 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -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]) -> &'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,
- 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(),
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 5fd8f7eb33a..aa236a690ec 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -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);
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index fe4459ccdc0..0439de0ee7b 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -77,6 +77,7 @@ macro_rules! arena_vec {
});
}
+mod asm;
mod expr;
mod item;
mod pat;
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index fdcb68cf421..0e42e0e3793 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -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 {
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index fd976b119b7..b28c6f0d99c 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -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>,
@@ -25,6 +27,7 @@ fn parse_args<'a>(
ecx: &mut ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
+ is_global_asm: bool,
) -> Result> {
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 {
+fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option {
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, sp: Span, args: AsmArgs) -> P, sp: Span, args: AsmArgs) -> P(
@@ -659,8 +658,53 @@ pub fn expand_asm<'cx>(
sp: Span,
tts: TokenStream,
) -> Box {
- 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 {
+ 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)
diff --git a/compiler/rustc_builtin_macros/src/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs
deleted file mode 100644
index 76d87452927..00000000000
--- a/compiler/rustc_builtin_macros/src/global_asm.rs
+++ /dev/null
@@ -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 {
- 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