From 5918ee431717a276ea1a9c65d7c0009679a0643b Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sun, 11 Apr 2021 20:51:28 +0100 Subject: [PATCH 1/7] Add support for const operands and options to global_asm! On x86, the default syntax is also switched to Intel to match asm! --- Cargo.lock | 4 +- compiler/rustc_ast/src/ast.rs | 10 +- compiler/rustc_ast/src/mut_visit.rs | 46 +-- compiler/rustc_ast/src/visit.rs | 55 ++- compiler/rustc_ast_lowering/src/asm.rs | 328 ++++++++++++++++++ compiler/rustc_ast_lowering/src/expr.rs | 321 +---------------- compiler/rustc_ast_lowering/src/item.rs | 8 +- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 225 ++++++------ compiler/rustc_builtin_macros/src/asm.rs | 102 ++++-- .../rustc_builtin_macros/src/global_asm.rs | 68 ---- compiler/rustc_builtin_macros/src/lib.rs | 3 +- .../rustc_codegen_cranelift/src/driver/aot.rs | 17 +- compiler/rustc_codegen_llvm/src/asm.rs | 45 ++- compiler/rustc_codegen_ssa/src/common.rs | 32 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 34 +- compiler/rustc_codegen_ssa/src/mono_item.rs | 36 +- compiler/rustc_codegen_ssa/src/traits/asm.rs | 15 +- compiler/rustc_codegen_ssa/src/traits/mod.rs | 2 +- compiler/rustc_hir/src/arena.rs | 1 - compiler/rustc_hir/src/hir.rs | 7 +- compiler/rustc_hir/src/intravisit.rs | 47 +-- compiler/rustc_hir_pretty/src/lib.rs | 214 ++++++------ .../rustc_mir/src/monomorphize/collector.rs | 21 +- compiler/rustc_passes/src/intrinsicck.rs | 27 ++ compiler/rustc_resolve/src/late.rs | 6 +- compiler/rustc_typeck/src/check/mod.rs | 5 +- compiler/rustc_typeck/src/collect/type_of.rs | 5 +- library/alloc/Cargo.toml | 2 +- library/core/src/macros/mod.rs | 5 +- library/std/Cargo.toml | 2 +- library/std/src/sys/sgx/abi/mod.rs | 2 +- .../src/library-features/global-asm.md | 20 +- .../enclave/src/main.rs | 2 +- src/test/ui/macros/global-asm.rs | 4 +- src/test/ui/macros/global-asm.stderr | 6 +- 36 files changed, 928 insertions(+), 800 deletions(-) create mode 100644 compiler/rustc_ast_lowering/src/asm.rs delete mode 100644 compiler/rustc_builtin_macros/src/global_asm.rs diff --git a/Cargo.lock b/Cargo.lock index f9a607c56db4f..d237997843c94 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 ac69fa2020250..fb012d9802f6c 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 05f57f978c7a4..374a6ec972fba 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 3f35919ae6a2a..c50b334d3e949 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 0000000000000..6acdfa1b5f803 --- /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 ea0770daf0eed..483135ed3a3af 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 5fd8f7eb33a1f..aa236a690ec79 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 fe4459ccdc0df..0439de0ee7bf9 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 fdcb68cf421c1..0e42e0e3793f5 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 fd976b119b748..b28c6f0d99c5e 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 76d874529270e..0000000000000 --- 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, 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 })) -} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 65a141e111237..17b7793c7ddc7 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -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, diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index e5f06551bb643..004e6bddaf3ee 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -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); } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index ea08052a9d0e2..a571418c1f5ff 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -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(), + ); } } } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index afd83bfcb5692..955f658eb1c7e 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -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), + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 72e9163b88e21..2bd35fe9b1435 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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 } => { diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 8e79193759eb4..48d753e0d8435 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -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>(&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") } diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 69931935c4963..86f2781a7663b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -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], + ); } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index be2e0ea230f30..c529fbbf518b6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -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; diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index ddf8218616988..b05ca381b8ab6 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -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>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e689ae4d81db7..91fd97a0d4020 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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`. TyAlias(&'hir Ty<'hir>, Generics<'hir>), /// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 0ce04a77a5056..c08f1f53218d6 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 77d083fc5e967..fe02cc5de8cb8 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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; diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index fdefc89067477..c0d327d478834 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -390,8 +390,27 @@ 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 { ref anon_const } => { + // Treat these the same way as ItemKind::Const + let anon_const_def_id = + tcx.hir().local_def_id(anon_const.hir_id).to_def_id(); + if let Ok(val) = tcx.const_eval_poly(anon_const_def_id) { + collect_const_value(tcx, val, &mut neighbors); + } + } + _ => span_bug!(*op_sp, "invalid operand type for global_asm!"), + } + } + } else { + span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") + } } } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 3f095d0e82427..4532a0a350cef 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -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> { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 92f21191de430..ffa825b7d46a3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -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!"), } } diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index cb7589318d2a7..994206bd41934 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -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 } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 97b6f5cf41211..5197b620f90ba 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -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, }) => diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 4f97c95bcb9ea..5793f5e681b48 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -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" diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5d9b0f80d3a6f..b46a7aa138cd1 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -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 */ }; } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index ab7b142ef3db0..44e8af9bf543b 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -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.40" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] } diff --git a/library/std/src/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs index f9536c4203df2..231cc15b8497f 100644 --- a/library/std/src/sys/sgx/abi/mod.rs +++ b/library/std/src/sys/sgx/abi/mod.rs @@ -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); diff --git a/src/doc/unstable-book/src/library-features/global-asm.md b/src/doc/unstable-book/src/library-features/global-asm.md index ce1155a977cd0..e241f5788b9d8 100644 --- a/src/doc/unstable-book/src/library-features/global-asm.md +++ b/src/doc/unstable-book/src/library-features/global-asm.md @@ -38,11 +38,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 +56,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() {} diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs index 8e91a8d842c62..791dec2ed692b 100644 --- a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs @@ -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; diff --git a/src/test/ui/macros/global-asm.rs b/src/test/ui/macros/global-asm.rs index 8402afa50857a..b8903e07cfd38 100644 --- a/src/test/ui/macros/global-asm.rs +++ b/src/test/ui/macros/global-asm.rs @@ -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 } diff --git a/src/test/ui/macros/global-asm.stderr b/src/test/ui/macros/global-asm.stderr index c43bf83fe1912..a8621a0c5185e 100644 --- a/src/test/ui/macros/global-asm.stderr +++ b/src/test/ui/macros/global-asm.stderr @@ -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); From 5a229e0e206ee78ef9712b06569cb55c238bac73 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 13 Apr 2021 18:11:11 +0100 Subject: [PATCH 2/7] Add tests for global_asm! --- src/test/assembly/asm/global_asm.rs | 14 +++ src/test/ui/asm/bad-arch.rs | 7 ++ src/test/ui/asm/bad-arch.stderr | 12 ++- src/test/ui/asm/bad-options.rs | 15 ++- src/test/ui/asm/bad-options.stderr | 38 ++++++- src/test/ui/asm/bad-template.rs | 21 +++- src/test/ui/asm/bad-template.stderr | 87 +++++++++++++++- src/test/ui/asm/const.rs | 6 +- src/test/ui/asm/duplicate-options.fixed | 5 +- src/test/ui/asm/duplicate-options.rs | 5 +- src/test/ui/asm/duplicate-options.stderr | 8 +- src/test/ui/asm/inline-syntax.arm.stderr | 28 +++-- src/test/ui/asm/inline-syntax.rs | 8 ++ src/test/ui/asm/inline-syntax.x86_64.stderr | 24 +++-- src/test/ui/asm/parse-error.rs | 36 ++++++- src/test/ui/asm/parse-error.stderr | 108 +++++++++++++++++++- src/test/ui/asm/type-check-2.rs | 10 +- src/test/ui/asm/type-check-2.stderr | 8 +- src/test/ui/asm/type-check-3.rs | 20 +++- src/test/ui/asm/type-check-3.stderr | 27 ++++- 20 files changed, 455 insertions(+), 32 deletions(-) create mode 100644 src/test/assembly/asm/global_asm.rs diff --git a/src/test/assembly/asm/global_asm.rs b/src/test/assembly/asm/global_asm.rs new file mode 100644 index 0000000000000..3a7f2fa1efb6b --- /dev/null +++ b/src/test/assembly/asm/global_asm.rs @@ -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)); diff --git a/src/test/ui/asm/bad-arch.rs b/src/test/ui/asm/bad-arch.rs index 763b5fe5cca8b..b491a70d1e217 100644 --- a/src/test/ui/asm/bad-arch.rs +++ b/src/test/ui/asm/bad-arch.rs @@ -8,6 +8,10 @@ macro_rules! asm { () => {}; } +#[rustc_builtin_macro] +macro_rules! global_asm { + () => {}; +} #[lang = "sized"] trait Sized {} @@ -17,3 +21,6 @@ fn main() { //~^ ERROR asm! is unsupported on this target } } + +global_asm!(""); +//~^ ERROR asm! is unsupported on this target diff --git a/src/test/ui/asm/bad-arch.stderr b/src/test/ui/asm/bad-arch.stderr index cb876f28650aa..d006320bf45bf 100644 --- a/src/test/ui/asm/bad-arch.stderr +++ b/src/test/ui/asm/bad-arch.stderr @@ -1,8 +1,16 @@ error[E0472]: asm! is unsupported on this target - --> $DIR/bad-arch.rs:16:9 + --> $DIR/bad-arch.rs:20:9 | LL | asm!(""); | ^^^^^^^^^ -error: aborting due to previous error +error[E0472]: asm! is unsupported on this target + --> $DIR/bad-arch.rs:25:1 + | +LL | global_asm!(""); + | ^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors diff --git a/src/test/ui/asm/bad-options.rs b/src/test/ui/asm/bad-options.rs index 755fc2ca238aa..923d65bfd96ed 100644 --- a/src/test/ui/asm/bad-options.rs +++ b/src/test/ui/asm/bad-options.rs @@ -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 diff --git a/src/test/ui/asm/bad-options.stderr b/src/test/ui/asm/bad-options.stderr index c5e8e2ccf44cc..4e27a6e2cb595 100644 --- a/src/test/ui/asm/bad-options.stderr +++ b/src/test/ui/asm/bad-options.stderr @@ -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 diff --git a/src/test/ui/asm/bad-template.rs b/src/test/ui/asm/bad-template.rs index 21ce8c6236d06..4404be4fa3a68 100644 --- a/src/test/ui/asm/bad-template.rs +++ b/src/test/ui/asm/bad-template.rs @@ -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 diff --git a/src/test/ui/asm/bad-template.stderr b/src/test/ui/asm/bad-template.stderr index 1aea7467ed0a7..d95663d2a734a 100644 --- a/src/test/ui/asm/bad-template.stderr +++ b/src/test/ui/asm/bad-template.stderr @@ -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 diff --git a/src/test/ui/asm/const.rs b/src/test/ui/asm/const.rs index 0f6a7cd44749e..fd940173c424c 100644 --- a/src/test/ui/asm/const.rs +++ b/src/test/ui/asm/const.rs @@ -2,7 +2,7 @@ // only-x86_64 // run-pass -#![feature(asm)] +#![feature(asm, global_asm)] fn const_generic() -> 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)); diff --git a/src/test/ui/asm/duplicate-options.fixed b/src/test/ui/asm/duplicate-options.fixed index f4672a50fd0f4..d4444e9c6ccd8 100644 --- a/src/test/ui/asm/duplicate-options.fixed +++ b/src/test/ui/asm/duplicate-options.fixed @@ -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 diff --git a/src/test/ui/asm/duplicate-options.rs b/src/test/ui/asm/duplicate-options.rs index 80292d7521a9a..fd28311984bf1 100644 --- a/src/test/ui/asm/duplicate-options.rs +++ b/src/test/ui/asm/duplicate-options.rs @@ -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 diff --git a/src/test/ui/asm/duplicate-options.stderr b/src/test/ui/asm/duplicate-options.stderr index cd8d743e031a5..53edf8fb91cf6 100644 --- a/src/test/ui/asm/duplicate-options.stderr +++ b/src/test/ui/asm/duplicate-options.stderr @@ -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 diff --git a/src/test/ui/asm/inline-syntax.arm.stderr b/src/test/ui/asm/inline-syntax.arm.stderr index bcae1d565f136..b1685bd4e027a 100644 --- a/src/test/ui/asm/inline-syntax.arm.stderr +++ b/src/test/ui/asm/inline-syntax.arm.stderr @@ -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 + --> :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 diff --git a/src/test/ui/asm/inline-syntax.rs b/src/test/ui/asm/inline-syntax.rs index 13ded19cfea91..2d54ef7bd6b40 100644 --- a/src/test/ui/asm/inline-syntax.rs +++ b/src/test/ui/asm/inline-syntax.rs @@ -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 diff --git a/src/test/ui/asm/inline-syntax.x86_64.stderr b/src/test/ui/asm/inline-syntax.x86_64.stderr index 02b29b09013dd..59c95194322aa 100644 --- a/src/test/ui/asm/inline-syntax.x86_64.stderr +++ b/src/test/ui/asm/inline-syntax.x86_64.stderr @@ -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 diff --git a/src/test/ui/asm/parse-error.rs b/src/test/ui/asm/parse-error.rs index f2e9d9ca08b24..e62e6668415bd 100644 --- a/src/test/ui/asm/parse-error.rs +++ b/src/test/ui/asm/parse-error.rs @@ -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 diff --git a/src/test/ui/asm/parse-error.stderr b/src/test/ui/asm/parse-error.stderr index 74918e1c40070..1877604a2358d 100644 --- a/src/test/ui/asm/parse-error.stderr +++ b/src/test/ui/asm/parse-error.stderr @@ -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 a macro (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 a macro (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`. diff --git a/src/test/ui/asm/type-check-2.rs b/src/test/ui/asm/type-check-2.rs index 0677167ccfe73..060a88fc93713 100644 --- a/src/test/ui/asm/type-check-2.rs +++ b/src/test/ui/asm/type-check-2.rs @@ -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 diff --git a/src/test/ui/asm/type-check-2.stderr b/src/test/ui/asm/type-check-2.stderr index c152560a64fca..f61f690bdb4dd 100644 --- a/src/test/ui/asm/type-check-2.stderr +++ b/src/test/ui/asm/type-check-2.stderr @@ -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 = 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`. diff --git a/src/test/ui/asm/type-check-3.rs b/src/test/ui/asm/type-check-3.rs index 6890baead8119..c2c1885ff166f 100644 --- a/src/test/ui/asm/type-check-3.rs +++ b/src/test/ui/asm/type-check-3.rs @@ -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(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 diff --git a/src/test/ui/asm/type-check-3.stderr b/src/test/ui/asm/type-check-3.stderr index 42497456ac31c..9f6989ca03d1b 100644 --- a/src/test/ui/asm/type-check-3.stderr +++ b/src/test/ui/asm/type-check-3.stderr @@ -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`. From 0df83f8e5e85268b0134f8c698e09b5c8d6b3ef6 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 21 Apr 2021 15:21:33 +0100 Subject: [PATCH 3/7] Update global_asm! documentation --- .../src/library-features/global-asm.md | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/doc/unstable-book/src/library-features/global-asm.md b/src/doc/unstable-book/src/library-features/global-asm.md index e241f5788b9d8..146d17b4638c8 100644 --- a/src/doc/unstable-book/src/library-features/global-asm.md +++ b/src/doc/unstable-book/src/library-features/global-asm.md @@ -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 @@ -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); +# } +``` ------------------------ From bb6bec1d55586abfb6aa6ca924994b09035752dc Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Mon, 26 Apr 2021 19:49:26 +0100 Subject: [PATCH 4/7] Clarify error message when both asm! and global_asm! are unsupported --- compiler/rustc_ast_lowering/src/asm.rs | 3 ++- src/test/ui/asm/bad-arch.rs | 4 ++-- src/test/ui/asm/bad-arch.stderr | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 6acdfa1b5f803..1c3fae2afe71f 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -15,7 +15,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // 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(); + 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)) diff --git a/src/test/ui/asm/bad-arch.rs b/src/test/ui/asm/bad-arch.rs index b491a70d1e217..3eeb76f3d0035 100644 --- a/src/test/ui/asm/bad-arch.rs +++ b/src/test/ui/asm/bad-arch.rs @@ -18,9 +18,9 @@ trait Sized {} fn main() { unsafe { asm!(""); - //~^ ERROR asm! is unsupported on this target + //~^ ERROR inline assembly is unsupported on this target } } global_asm!(""); -//~^ ERROR asm! is unsupported on this target +//~^ ERROR inline assembly is unsupported on this target diff --git a/src/test/ui/asm/bad-arch.stderr b/src/test/ui/asm/bad-arch.stderr index d006320bf45bf..3ca5bcc707e4d 100644 --- a/src/test/ui/asm/bad-arch.stderr +++ b/src/test/ui/asm/bad-arch.stderr @@ -1,10 +1,10 @@ -error[E0472]: asm! is unsupported on this target +error[E0472]: inline assembly is unsupported on this target --> $DIR/bad-arch.rs:20:9 | LL | asm!(""); | ^^^^^^^^^ -error[E0472]: asm! is unsupported on this target +error[E0472]: inline assembly is unsupported on this target --> $DIR/bad-arch.rs:25:1 | LL | global_asm!(""); From 40d9da4d8c9cad8cd78a3fcc47f40cb9335a07d7 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Mon, 26 Apr 2021 20:27:27 +0100 Subject: [PATCH 5/7] global_asm! consts do not depend on other items --- compiler/rustc_mir/src/monomorphize/collector.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index c0d327d478834..e4ad92eb1b1a1 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -397,13 +397,10 @@ fn collect_items_rec<'tcx>( if let hir::ItemKind::GlobalAsm(asm) = item.kind { for (op, op_sp) in asm.operands { match op { - hir::InlineAsmOperand::Const { ref anon_const } => { - // Treat these the same way as ItemKind::Const - let anon_const_def_id = - tcx.hir().local_def_id(anon_const.hir_id).to_def_id(); - if let Ok(val) = tcx.const_eval_poly(anon_const_def_id) { - collect_const_value(tcx, val, &mut neighbors); - } + 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!"), } From d9cf2ce28f30674c83fa62ca19a49ad87e39c413 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sun, 2 May 2021 22:14:31 +0100 Subject: [PATCH 6/7] Update compiler_builtins to 0.1.43 --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d237997843c94..6f82575c72b1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -655,9 +655,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.40" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288a0d48b8155926ebb4552bdde3fa32744ce424c5de0a26ddbc68369aeb7172" +checksum = "65af2dcae4779003dfa91aedc6ade7bdc7ba685944e50a8b4f9380df376a4466" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 44e8af9bf543b..922c2c2bb8c4d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -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.40" } +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'] } From a7ed6a5196996f00dd78a391a44af51ee8088058 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 13 May 2021 23:09:54 +0100 Subject: [PATCH 7/7] Fix tests --- src/test/ui/asm/bad-arch.stderr | 2 +- src/test/ui/asm/parse-error.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/ui/asm/bad-arch.stderr b/src/test/ui/asm/bad-arch.stderr index 3ca5bcc707e4d..6094643b038c2 100644 --- a/src/test/ui/asm/bad-arch.stderr +++ b/src/test/ui/asm/bad-arch.stderr @@ -10,7 +10,7 @@ error[E0472]: inline assembly is unsupported on this target LL | global_asm!(""); | ^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + = 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 diff --git a/src/test/ui/asm/parse-error.stderr b/src/test/ui/asm/parse-error.stderr index 1877604a2358d..afd262b851c09 100644 --- a/src/test/ui/asm/parse-error.stderr +++ b/src/test/ui/asm/parse-error.stderr @@ -260,7 +260,7 @@ error: asm template must be a string literal LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + = 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 @@ -268,7 +268,7 @@ error: asm template must be a string literal LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + = 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