about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAmanieu d'Antras <amanieu@gmail.com>2021-04-11 20:51:28 +0100
committerAmanieu d'Antras <amanieu@gmail.com>2021-05-13 22:31:57 +0100
commit5918ee431717a276ea1a9c65d7c0009679a0643b (patch)
treeb9657afac0da6cacb582b6409db50281e8149f9a
parent952c5732c2d25a875f90e5cd5dd29a1a21c1d4a2 (diff)
downloadrust-5918ee431717a276ea1a9c65d7c0009679a0643b.tar.gz
rust-5918ee431717a276ea1a9c65d7c0009679a0643b.zip
Add support for const operands and options to global_asm!
On x86, the default syntax is also switched to Intel to match asm!
-rw-r--r--Cargo.lock4
-rw-r--r--compiler/rustc_ast/src/ast.rs10
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs46
-rw-r--r--compiler/rustc_ast/src/visit.rs55
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs328
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs321
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs225
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs102
-rw-r--r--compiler/rustc_builtin_macros/src/global_asm.rs68
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs45
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs32
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs34
-rw-r--r--compiler/rustc_codegen_ssa/src/mono_item.rs36
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/asm.rs15
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs2
-rw-r--r--compiler/rustc_hir/src/arena.rs1
-rw-r--r--compiler/rustc_hir/src/hir.rs7
-rw-r--r--compiler/rustc_hir/src/intravisit.rs47
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs214
-rw-r--r--compiler/rustc_mir/src/monomorphize/collector.rs21
-rw-r--r--compiler/rustc_passes/src/intrinsicck.rs27
-rw-r--r--compiler/rustc_resolve/src/late.rs6
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs5
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs5
-rw-r--r--library/alloc/Cargo.toml2
-rw-r--r--library/core/src/macros/mod.rs5
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/sys/sgx/abi/mod.rs2
-rw-r--r--src/doc/unstable-book/src/library-features/global-asm.md20
-rw-r--r--src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs2
-rw-r--r--src/test/ui/macros/global-asm.rs4
-rw-r--r--src/test/ui/macros/global-asm.stderr6
36 files changed, 928 insertions, 800 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f9a607c56db..d237997843c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -655,9 +655,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.39"
+version = "0.1.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
+checksum = "288a0d48b8155926ebb4552bdde3fa32744ce424c5de0a26ddbc68369aeb7172"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index ac69fa20202..fb012d9802f 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2279,14 +2279,6 @@ pub struct ForeignMod {
     pub items: Vec<P<ForeignItem>>,
 }
 
-/// Global inline assembly.
-///
-/// Also known as "module-level assembly" or "file-scoped assembly".
-#[derive(Clone, Encodable, Decodable, Debug, Copy)]
-pub struct GlobalAsm {
-    pub asm: Symbol,
-}
-
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct EnumDef {
     pub variants: Vec<Variant>,
@@ -2669,7 +2661,7 @@ pub enum ItemKind {
     /// E.g., `extern {}` or `extern "C" {}`.
     ForeignMod(ForeignMod),
     /// Module-level inline assembly (from `global_asm!()`).
-    GlobalAsm(GlobalAsm),
+    GlobalAsm(InlineAsm),
     /// A type alias (`type`).
     ///
     /// E.g., `type Foo = Bar<u8>;`.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 05f57f978c7..374a6ec972f 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -965,7 +965,7 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
             ModKind::Unloaded => {}
         },
         ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
-        ItemKind::GlobalAsm(_ga) => {}
+        ItemKind::GlobalAsm(asm) => noop_visit_inline_asm(asm, vis),
         ItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => {
             vis.visit_generics(generics);
             visit_bounds(bounds, vis);
@@ -1170,6 +1170,28 @@ pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonCo
     vis.visit_expr(value);
 }
 
+fn noop_visit_inline_asm<T: MutVisitor>(asm: &mut InlineAsm, vis: &mut T) {
+    for (op, _) in &mut asm.operands {
+        match op {
+            InlineAsmOperand::In { expr, .. }
+            | InlineAsmOperand::InOut { expr, .. }
+            | InlineAsmOperand::Sym { expr, .. } => vis.visit_expr(expr),
+            InlineAsmOperand::Out { expr, .. } => {
+                if let Some(expr) = expr {
+                    vis.visit_expr(expr);
+                }
+            }
+            InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+                vis.visit_expr(in_expr);
+                if let Some(out_expr) = out_expr {
+                    vis.visit_expr(out_expr);
+                }
+            }
+            InlineAsmOperand::Const { anon_const, .. } => vis.visit_anon_const(anon_const),
+        }
+    }
+}
+
 pub fn noop_visit_expr<T: MutVisitor>(
     Expr { kind, id, span, attrs, tokens }: &mut Expr,
     vis: &mut T,
@@ -1288,27 +1310,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
         ExprKind::Ret(expr) => {
             visit_opt(expr, |expr| vis.visit_expr(expr));
         }
-        ExprKind::InlineAsm(asm) => {
-            for (op, _) in &mut asm.operands {
-                match op {
-                    InlineAsmOperand::In { expr, .. }
-                    | InlineAsmOperand::InOut { expr, .. }
-                    | InlineAsmOperand::Sym { expr, .. } => vis.visit_expr(expr),
-                    InlineAsmOperand::Out { expr, .. } => {
-                        if let Some(expr) = expr {
-                            vis.visit_expr(expr);
-                        }
-                    }
-                    InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
-                        vis.visit_expr(in_expr);
-                        if let Some(out_expr) = out_expr {
-                            vis.visit_expr(out_expr);
-                        }
-                    }
-                    InlineAsmOperand::Const { anon_const, .. } => vis.visit_anon_const(anon_const),
-                }
-            }
-        }
+        ExprKind::InlineAsm(asm) => noop_visit_inline_asm(asm, vis),
         ExprKind::LlvmInlineAsm(asm) => {
             let LlvmInlineAsm {
                 asm: _,
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 3f35919ae6a..c50b334d3e9 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -90,9 +90,6 @@ pub trait Visitor<'ast>: Sized {
     fn visit_foreign_item(&mut self, i: &'ast ForeignItem) {
         walk_foreign_item(self, i)
     }
-    fn visit_global_asm(&mut self, ga: &'ast GlobalAsm) {
-        walk_global_asm(self, ga)
-    }
     fn visit_item(&mut self, i: &'ast Item) {
         walk_item(self, i)
     }
@@ -299,7 +296,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
         ItemKind::ForeignMod(ref foreign_module) => {
             walk_list!(visitor, visit_foreign_item, &foreign_module.items);
         }
-        ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga),
+        ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm),
         ItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty)) => {
             visitor.visit_generics(generics);
             walk_list!(visitor, visit_param_bound, bounds);
@@ -557,10 +554,6 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI
     }
 }
 
-pub fn walk_global_asm<'a, V: Visitor<'a>>(_: &mut V, _: &'a GlobalAsm) {
-    // Empty!
-}
-
 pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) {
     match *bound {
         GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier),
@@ -708,6 +701,28 @@ pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonCo
     visitor.visit_expr(&constant.value);
 }
 
+fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) {
+    for (op, _) in &asm.operands {
+        match op {
+            InlineAsmOperand::In { expr, .. }
+            | InlineAsmOperand::InOut { expr, .. }
+            | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr),
+            InlineAsmOperand::Out { expr, .. } => {
+                if let Some(expr) = expr {
+                    visitor.visit_expr(expr);
+                }
+            }
+            InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+                visitor.visit_expr(in_expr);
+                if let Some(out_expr) = out_expr {
+                    visitor.visit_expr(out_expr);
+                }
+            }
+            InlineAsmOperand::Const { anon_const, .. } => visitor.visit_anon_const(anon_const),
+        }
+    }
+}
+
 pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
     walk_list!(visitor, visit_attribute, expression.attrs.iter());
 
@@ -830,29 +845,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
         }
         ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
         ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
-        ExprKind::InlineAsm(ref ia) => {
-            for (op, _) in &ia.operands {
-                match op {
-                    InlineAsmOperand::In { expr, .. }
-                    | InlineAsmOperand::InOut { expr, .. }
-                    | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr),
-                    InlineAsmOperand::Out { expr, .. } => {
-                        if let Some(expr) = expr {
-                            visitor.visit_expr(expr);
-                        }
-                    }
-                    InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
-                        visitor.visit_expr(in_expr);
-                        if let Some(out_expr) = out_expr {
-                            visitor.visit_expr(out_expr);
-                        }
-                    }
-                    InlineAsmOperand::Const { anon_const, .. } => {
-                        visitor.visit_anon_const(anon_const)
-                    }
-                }
-            }
-        }
+        ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
         ExprKind::LlvmInlineAsm(ref ia) => {
             for &(_, ref input) in &ia.inputs {
                 visitor.visit_expr(input)
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
new file mode 100644
index 00000000000..6acdfa1b5f8
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -0,0 +1,328 @@
+use super::LoweringContext;
+
+use rustc_ast::*;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_span::{Span, Symbol};
+use rustc_target::asm;
+use std::collections::hash_map::Entry;
+use std::fmt::Write;
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+    crate fn lower_inline_asm(&mut self, sp: Span, asm: &InlineAsm) -> &'hir hir::InlineAsm<'hir> {
+        // Rustdoc needs to support asm! from foriegn architectures: don't try
+        // lowering the register contraints in this case.
+        let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch };
+        if asm_arch.is_none() && !self.sess.opts.actually_rustdoc {
+            struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit();
+        }
+        if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
+            && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
+            && !self.sess.opts.actually_rustdoc
+        {
+            self.sess
+                .struct_span_err(sp, "the `att_syntax` option is only supported on x86")
+                .emit();
+        }
+
+        // Lower operands to HIR. We use dummy register classes if an error
+        // occurs during lowering because we still need to be able to produce a
+        // valid HIR.
+        let sess = self.sess;
+        let operands: Vec<_> = asm
+            .operands
+            .iter()
+            .map(|(op, op_sp)| {
+                let lower_reg = |reg| match reg {
+                    InlineAsmRegOrRegClass::Reg(s) => {
+                        asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
+                            asm::InlineAsmReg::parse(
+                                asm_arch,
+                                |feature| sess.target_features.contains(&Symbol::intern(feature)),
+                                &sess.target,
+                                s,
+                            )
+                            .unwrap_or_else(|e| {
+                                let msg = format!("invalid register `{}`: {}", s.as_str(), e);
+                                sess.struct_span_err(*op_sp, &msg).emit();
+                                asm::InlineAsmReg::Err
+                            })
+                        } else {
+                            asm::InlineAsmReg::Err
+                        })
+                    }
+                    InlineAsmRegOrRegClass::RegClass(s) => {
+                        asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
+                            asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
+                                let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
+                                sess.struct_span_err(*op_sp, &msg).emit();
+                                asm::InlineAsmRegClass::Err
+                            })
+                        } else {
+                            asm::InlineAsmRegClass::Err
+                        })
+                    }
+                };
+
+                let op = match *op {
+                    InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
+                        reg: lower_reg(reg),
+                        expr: self.lower_expr_mut(expr),
+                    },
+                    InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
+                        reg: lower_reg(reg),
+                        late,
+                        expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
+                    },
+                    InlineAsmOperand::InOut { reg, late, ref expr } => {
+                        hir::InlineAsmOperand::InOut {
+                            reg: lower_reg(reg),
+                            late,
+                            expr: self.lower_expr_mut(expr),
+                        }
+                    }
+                    InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
+                        hir::InlineAsmOperand::SplitInOut {
+                            reg: lower_reg(reg),
+                            late,
+                            in_expr: self.lower_expr_mut(in_expr),
+                            out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
+                        }
+                    }
+                    InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const {
+                        anon_const: self.lower_anon_const(anon_const),
+                    },
+                    InlineAsmOperand::Sym { ref expr } => {
+                        hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
+                    }
+                };
+                (op, *op_sp)
+            })
+            .collect();
+
+        // Validate template modifiers against the register classes for the operands
+        for p in &asm.template {
+            if let InlineAsmTemplatePiece::Placeholder {
+                operand_idx,
+                modifier: Some(modifier),
+                span: placeholder_span,
+            } = *p
+            {
+                let op_sp = asm.operands[operand_idx].1;
+                match &operands[operand_idx].0 {
+                    hir::InlineAsmOperand::In { reg, .. }
+                    | hir::InlineAsmOperand::Out { reg, .. }
+                    | hir::InlineAsmOperand::InOut { reg, .. }
+                    | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
+                        let class = reg.reg_class();
+                        if class == asm::InlineAsmRegClass::Err {
+                            continue;
+                        }
+                        let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
+                        if !valid_modifiers.contains(&modifier) {
+                            let mut err = sess.struct_span_err(
+                                placeholder_span,
+                                "invalid asm template modifier for this register class",
+                            );
+                            err.span_label(placeholder_span, "template modifier");
+                            err.span_label(op_sp, "argument");
+                            if !valid_modifiers.is_empty() {
+                                let mut mods = format!("`{}`", valid_modifiers[0]);
+                                for m in &valid_modifiers[1..] {
+                                    let _ = write!(mods, ", `{}`", m);
+                                }
+                                err.note(&format!(
+                                    "the `{}` register class supports \
+                                     the following template modifiers: {}",
+                                    class.name(),
+                                    mods
+                                ));
+                            } else {
+                                err.note(&format!(
+                                    "the `{}` register class does not support template modifiers",
+                                    class.name()
+                                ));
+                            }
+                            err.emit();
+                        }
+                    }
+                    hir::InlineAsmOperand::Const { .. } => {
+                        let mut err = sess.struct_span_err(
+                            placeholder_span,
+                            "asm template modifiers are not allowed for `const` arguments",
+                        );
+                        err.span_label(placeholder_span, "template modifier");
+                        err.span_label(op_sp, "argument");
+                        err.emit();
+                    }
+                    hir::InlineAsmOperand::Sym { .. } => {
+                        let mut err = sess.struct_span_err(
+                            placeholder_span,
+                            "asm template modifiers are not allowed for `sym` arguments",
+                        );
+                        err.span_label(placeholder_span, "template modifier");
+                        err.span_label(op_sp, "argument");
+                        err.emit();
+                    }
+                }
+            }
+        }
+
+        let mut used_input_regs = FxHashMap::default();
+        let mut used_output_regs = FxHashMap::default();
+        let mut required_features: Vec<&str> = vec![];
+        for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
+            if let Some(reg) = op.reg() {
+                // Make sure we don't accidentally carry features from the
+                // previous iteration.
+                required_features.clear();
+
+                let reg_class = reg.reg_class();
+                if reg_class == asm::InlineAsmRegClass::Err {
+                    continue;
+                }
+
+                // We ignore target feature requirements for clobbers: if the
+                // feature is disabled then the compiler doesn't care what we
+                // do with the registers.
+                //
+                // Note that this is only possible for explicit register
+                // operands, which cannot be used in the asm string.
+                let is_clobber = matches!(
+                    op,
+                    hir::InlineAsmOperand::Out {
+                        reg: asm::InlineAsmRegOrRegClass::Reg(_),
+                        late: _,
+                        expr: None
+                    }
+                );
+
+                if !is_clobber {
+                    // Validate register classes against currently enabled target
+                    // features. We check that at least one type is available for
+                    // the current target.
+                    for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
+                        if let Some(feature) = feature {
+                            if self.sess.target_features.contains(&Symbol::intern(feature)) {
+                                required_features.clear();
+                                break;
+                            } else {
+                                required_features.push(feature);
+                            }
+                        } else {
+                            required_features.clear();
+                            break;
+                        }
+                    }
+                    // We are sorting primitive strs here and can use unstable sort here
+                    required_features.sort_unstable();
+                    required_features.dedup();
+                    match &required_features[..] {
+                        [] => {}
+                        [feature] => {
+                            let msg = format!(
+                                "register class `{}` requires the `{}` target feature",
+                                reg_class.name(),
+                                feature
+                            );
+                            sess.struct_span_err(op_sp, &msg).emit();
+                        }
+                        features => {
+                            let msg = format!(
+                                "register class `{}` requires at least one target feature: {}",
+                                reg_class.name(),
+                                features.join(", ")
+                            );
+                            sess.struct_span_err(op_sp, &msg).emit();
+                        }
+                    }
+                }
+
+                // Check for conflicts between explicit register operands.
+                if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
+                    let (input, output) = match op {
+                        hir::InlineAsmOperand::In { .. } => (true, false),
+
+                        // Late output do not conflict with inputs, but normal outputs do
+                        hir::InlineAsmOperand::Out { late, .. } => (!late, true),
+
+                        hir::InlineAsmOperand::InOut { .. }
+                        | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
+
+                        hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {
+                            unreachable!()
+                        }
+                    };
+
+                    // Flag to output the error only once per operand
+                    let mut skip = false;
+                    reg.overlapping_regs(|r| {
+                        let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
+                                         input| {
+                            match used_regs.entry(r) {
+                                Entry::Occupied(o) => {
+                                    if skip {
+                                        return;
+                                    }
+                                    skip = true;
+
+                                    let idx2 = *o.get();
+                                    let &(ref op2, op_sp2) = &operands[idx2];
+                                    let reg2 = match op2.reg() {
+                                        Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
+                                        _ => unreachable!(),
+                                    };
+
+                                    let msg = format!(
+                                        "register `{}` conflicts with register `{}`",
+                                        reg.name(),
+                                        reg2.name()
+                                    );
+                                    let mut err = sess.struct_span_err(op_sp, &msg);
+                                    err.span_label(op_sp, &format!("register `{}`", reg.name()));
+                                    err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
+
+                                    match (op, op2) {
+                                        (
+                                            hir::InlineAsmOperand::In { .. },
+                                            hir::InlineAsmOperand::Out { late, .. },
+                                        )
+                                        | (
+                                            hir::InlineAsmOperand::Out { late, .. },
+                                            hir::InlineAsmOperand::In { .. },
+                                        ) => {
+                                            assert!(!*late);
+                                            let out_op_sp = if input { op_sp2 } else { op_sp };
+                                            let msg = "use `lateout` instead of \
+                                                       `out` to avoid conflict";
+                                            err.span_help(out_op_sp, msg);
+                                        }
+                                        _ => {}
+                                    }
+
+                                    err.emit();
+                                }
+                                Entry::Vacant(v) => {
+                                    v.insert(idx);
+                                }
+                            }
+                        };
+                        if input {
+                            check(&mut used_input_regs, true);
+                        }
+                        if output {
+                            check(&mut used_output_regs, false);
+                        }
+                    });
+                }
+            }
+        }
+
+        let operands = self.arena.alloc_from_iter(operands);
+        let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
+        let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
+        let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans };
+        self.arena.alloc(hir_asm)
+    }
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index ea0770daf0e..483135ed3a3 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -3,7 +3,6 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericAr
 use rustc_ast::attr;
 use rustc_ast::ptr::P as AstP;
 use rustc_ast::*;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::struct_span_err;
@@ -15,9 +14,6 @@ use rustc_span::hygiene::ExpnId;
 use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
-use rustc_target::asm;
-use std::collections::hash_map::Entry;
-use std::fmt::Write;
 
 impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
@@ -222,7 +218,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let e = e.as_ref().map(|x| self.lower_expr(x));
                     hir::ExprKind::Ret(e)
                 }
-                ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm),
+                ExprKind::InlineAsm(ref asm) => {
+                    hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
+                }
                 ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
                 ExprKind::Struct(ref se) => {
                     let rest = match &se.rest {
@@ -1329,319 +1327,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         result
     }
 
-    fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> {
-        // Rustdoc needs to support asm! from foriegn architectures: don't try
-        // lowering the register contraints in this case.
-        let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch };
-        if asm_arch.is_none() && !self.sess.opts.actually_rustdoc {
-            struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit();
-        }
-        if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
-            && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
-            && !self.sess.opts.actually_rustdoc
-        {
-            self.sess
-                .struct_span_err(sp, "the `att_syntax` option is only supported on x86")
-                .emit();
-        }
-
-        // Lower operands to HIR. We use dummy register classes if an error
-        // occurs during lowering because we still need to be able to produce a
-        // valid HIR.
-        let sess = self.sess;
-        let operands: Vec<_> = asm
-            .operands
-            .iter()
-            .map(|(op, op_sp)| {
-                let lower_reg = |reg| match reg {
-                    InlineAsmRegOrRegClass::Reg(s) => {
-                        asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
-                            asm::InlineAsmReg::parse(
-                                asm_arch,
-                                |feature| sess.target_features.contains(&Symbol::intern(feature)),
-                                &sess.target,
-                                s,
-                            )
-                            .unwrap_or_else(|e| {
-                                let msg = format!("invalid register `{}`: {}", s.as_str(), e);
-                                sess.struct_span_err(*op_sp, &msg).emit();
-                                asm::InlineAsmReg::Err
-                            })
-                        } else {
-                            asm::InlineAsmReg::Err
-                        })
-                    }
-                    InlineAsmRegOrRegClass::RegClass(s) => {
-                        asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
-                            asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
-                                let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
-                                sess.struct_span_err(*op_sp, &msg).emit();
-                                asm::InlineAsmRegClass::Err
-                            })
-                        } else {
-                            asm::InlineAsmRegClass::Err
-                        })
-                    }
-                };
-
-                let op = match *op {
-                    InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
-                        reg: lower_reg(reg),
-                        expr: self.lower_expr_mut(expr),
-                    },
-                    InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
-                        reg: lower_reg(reg),
-                        late,
-                        expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
-                    },
-                    InlineAsmOperand::InOut { reg, late, ref expr } => {
-                        hir::InlineAsmOperand::InOut {
-                            reg: lower_reg(reg),
-                            late,
-                            expr: self.lower_expr_mut(expr),
-                        }
-                    }
-                    InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
-                        hir::InlineAsmOperand::SplitInOut {
-                            reg: lower_reg(reg),
-                            late,
-                            in_expr: self.lower_expr_mut(in_expr),
-                            out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
-                        }
-                    }
-                    InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const {
-                        anon_const: self.lower_anon_const(anon_const),
-                    },
-                    InlineAsmOperand::Sym { ref expr } => {
-                        hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
-                    }
-                };
-                (op, *op_sp)
-            })
-            .collect();
-
-        // Validate template modifiers against the register classes for the operands
-        for p in &asm.template {
-            if let InlineAsmTemplatePiece::Placeholder {
-                operand_idx,
-                modifier: Some(modifier),
-                span: placeholder_span,
-            } = *p
-            {
-                let op_sp = asm.operands[operand_idx].1;
-                match &operands[operand_idx].0 {
-                    hir::InlineAsmOperand::In { reg, .. }
-                    | hir::InlineAsmOperand::Out { reg, .. }
-                    | hir::InlineAsmOperand::InOut { reg, .. }
-                    | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
-                        let class = reg.reg_class();
-                        if class == asm::InlineAsmRegClass::Err {
-                            continue;
-                        }
-                        let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
-                        if !valid_modifiers.contains(&modifier) {
-                            let mut err = sess.struct_span_err(
-                                placeholder_span,
-                                "invalid asm template modifier for this register class",
-                            );
-                            err.span_label(placeholder_span, "template modifier");
-                            err.span_label(op_sp, "argument");
-                            if !valid_modifiers.is_empty() {
-                                let mut mods = format!("`{}`", valid_modifiers[0]);
-                                for m in &valid_modifiers[1..] {
-                                    let _ = write!(mods, ", `{}`", m);
-                                }
-                                err.note(&format!(
-                                    "the `{}` register class supports \
-                                     the following template modifiers: {}",
-                                    class.name(),
-                                    mods
-                                ));
-                            } else {
-                                err.note(&format!(
-                                    "the `{}` register class does not support template modifiers",
-                                    class.name()
-                                ));
-                            }
-                            err.emit();
-                        }
-                    }
-                    hir::InlineAsmOperand::Const { .. } => {
-                        let mut err = sess.struct_span_err(
-                            placeholder_span,
-                            "asm template modifiers are not allowed for `const` arguments",
-                        );
-                        err.span_label(placeholder_span, "template modifier");
-                        err.span_label(op_sp, "argument");
-                        err.emit();
-                    }
-                    hir::InlineAsmOperand::Sym { .. } => {
-                        let mut err = sess.struct_span_err(
-                            placeholder_span,
-                            "asm template modifiers are not allowed for `sym` arguments",
-                        );
-                        err.span_label(placeholder_span, "template modifier");
-                        err.span_label(op_sp, "argument");
-                        err.emit();
-                    }
-                }
-            }
-        }
-
-        let mut used_input_regs = FxHashMap::default();
-        let mut used_output_regs = FxHashMap::default();
-        let mut required_features: Vec<&str> = vec![];
-        for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
-            if let Some(reg) = op.reg() {
-                // Make sure we don't accidentally carry features from the
-                // previous iteration.
-                required_features.clear();
-
-                let reg_class = reg.reg_class();
-                if reg_class == asm::InlineAsmRegClass::Err {
-                    continue;
-                }
-
-                // We ignore target feature requirements for clobbers: if the
-                // feature is disabled then the compiler doesn't care what we
-                // do with the registers.
-                //
-                // Note that this is only possible for explicit register
-                // operands, which cannot be used in the asm string.
-                let is_clobber = matches!(
-                    op,
-                    hir::InlineAsmOperand::Out {
-                        reg: asm::InlineAsmRegOrRegClass::Reg(_),
-                        late: _,
-                        expr: None
-                    }
-                );
-
-                if !is_clobber {
-                    // Validate register classes against currently enabled target
-                    // features. We check that at least one type is available for
-                    // the current target.
-                    for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
-                        if let Some(feature) = feature {
-                            if self.sess.target_features.contains(&Symbol::intern(feature)) {
-                                required_features.clear();
-                                break;
-                            } else {
-                                required_features.push(feature);
-                            }
-                        } else {
-                            required_features.clear();
-                            break;
-                        }
-                    }
-                    // We are sorting primitive strs here and can use unstable sort here
-                    required_features.sort_unstable();
-                    required_features.dedup();
-                    match &required_features[..] {
-                        [] => {}
-                        [feature] => {
-                            let msg = format!(
-                                "register class `{}` requires the `{}` target feature",
-                                reg_class.name(),
-                                feature
-                            );
-                            sess.struct_span_err(op_sp, &msg).emit();
-                        }
-                        features => {
-                            let msg = format!(
-                                "register class `{}` requires at least one target feature: {}",
-                                reg_class.name(),
-                                features.join(", ")
-                            );
-                            sess.struct_span_err(op_sp, &msg).emit();
-                        }
-                    }
-                }
-
-                // Check for conflicts between explicit register operands.
-                if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
-                    let (input, output) = match op {
-                        hir::InlineAsmOperand::In { .. } => (true, false),
-                        // Late output do not conflict with inputs, but normal outputs do
-                        hir::InlineAsmOperand::Out { late, .. } => (!late, true),
-                        hir::InlineAsmOperand::InOut { .. }
-                        | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
-                        hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {
-                            unreachable!()
-                        }
-                    };
-
-                    // Flag to output the error only once per operand
-                    let mut skip = false;
-                    reg.overlapping_regs(|r| {
-                        let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
-                                         input| {
-                            match used_regs.entry(r) {
-                                Entry::Occupied(o) => {
-                                    if skip {
-                                        return;
-                                    }
-                                    skip = true;
-
-                                    let idx2 = *o.get();
-                                    let &(ref op2, op_sp2) = &operands[idx2];
-                                    let reg2 = match op2.reg() {
-                                        Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
-                                        _ => unreachable!(),
-                                    };
-
-                                    let msg = format!(
-                                        "register `{}` conflicts with register `{}`",
-                                        reg.name(),
-                                        reg2.name()
-                                    );
-                                    let mut err = sess.struct_span_err(op_sp, &msg);
-                                    err.span_label(op_sp, &format!("register `{}`", reg.name()));
-                                    err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
-
-                                    match (op, op2) {
-                                        (
-                                            hir::InlineAsmOperand::In { .. },
-                                            hir::InlineAsmOperand::Out { late, .. },
-                                        )
-                                        | (
-                                            hir::InlineAsmOperand::Out { late, .. },
-                                            hir::InlineAsmOperand::In { .. },
-                                        ) => {
-                                            assert!(!*late);
-                                            let out_op_sp = if input { op_sp2 } else { op_sp };
-                                            let msg = "use `lateout` instead of \
-                                                    `out` to avoid conflict";
-                                            err.span_help(out_op_sp, msg);
-                                        }
-                                        _ => {}
-                                    }
-
-                                    err.emit();
-                                }
-                                Entry::Vacant(v) => {
-                                    v.insert(idx);
-                                }
-                            }
-                        };
-                        if input {
-                            check(&mut used_input_regs, true);
-                        }
-                        if output {
-                            check(&mut used_output_regs, false);
-                        }
-                    });
-                }
-            }
-        }
-
-        let operands = self.arena.alloc_from_iter(operands);
-        let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
-        let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
-        let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans };
-        hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm))
-    }
-
     fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> {
         let inner = hir::LlvmInlineAsmInner {
             inputs: asm.inputs.iter().map(|&(c, _)| c).collect(),
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 5fd8f7eb33a..aa236a690ec 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -329,7 +329,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
                 }
             }
-            ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)),
+            ItemKind::GlobalAsm(ref asm) => {
+                hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm))
+            }
             ItemKind::TyAlias(box TyAliasKind(_, ref gen, _, Some(ref ty))) => {
                 // We lower
                 //
@@ -746,10 +748,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    fn lower_global_asm(&mut self, ga: &GlobalAsm) -> &'hir hir::GlobalAsm {
-        self.arena.alloc(hir::GlobalAsm { asm: ga.asm })
-    }
-
     fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
         let id = self.lower_node_id(v.id);
         self.lower_attrs(id, &v.attrs);
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index fe4459ccdc0..0439de0ee7b 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -77,6 +77,7 @@ macro_rules! arena_vec {
     });
 }
 
+mod asm;
 mod expr;
 mod item;
 mod pat;
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index fdcb68cf421..0e42e0e3793 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1164,9 +1164,9 @@ impl<'a> State<'a> {
                 self.print_foreign_mod(nmod, &item.attrs);
                 self.bclose(item.span);
             }
-            ast::ItemKind::GlobalAsm(ref ga) => {
+            ast::ItemKind::GlobalAsm(ref asm) => {
                 self.head(visibility_qualified(&item.vis, "global_asm!"));
-                self.s.word(ga.asm.to_string());
+                self.print_inline_asm(asm);
                 self.end();
             }
             ast::ItemKind::TyAlias(box ast::TyAliasKind(def, ref generics, ref bounds, ref ty)) => {
@@ -2066,117 +2066,8 @@ impl<'a> State<'a> {
                 }
             }
             ast::ExprKind::InlineAsm(ref a) => {
-                enum AsmArg<'a> {
-                    Template(String),
-                    Operand(&'a InlineAsmOperand),
-                    Options(InlineAsmOptions),
-                }
-
-                let mut args = vec![];
-                args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&a.template)));
-                args.extend(a.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
-                if !a.options.is_empty() {
-                    args.push(AsmArg::Options(a.options));
-                }
-
                 self.word("asm!");
-                self.popen();
-                self.commasep(Consistent, &args, |s, arg| match arg {
-                    AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
-                    AsmArg::Operand(op) => {
-                        let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r
-                        {
-                            InlineAsmRegOrRegClass::Reg(r) => {
-                                s.print_symbol(*r, ast::StrStyle::Cooked)
-                            }
-                            InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
-                        };
-                        match op {
-                            InlineAsmOperand::In { reg, expr } => {
-                                s.word("in");
-                                s.popen();
-                                print_reg_or_class(s, reg);
-                                s.pclose();
-                                s.space();
-                                s.print_expr(expr);
-                            }
-                            InlineAsmOperand::Out { reg, late, expr } => {
-                                s.word(if *late { "lateout" } else { "out" });
-                                s.popen();
-                                print_reg_or_class(s, reg);
-                                s.pclose();
-                                s.space();
-                                match expr {
-                                    Some(expr) => s.print_expr(expr),
-                                    None => s.word("_"),
-                                }
-                            }
-                            InlineAsmOperand::InOut { reg, late, expr } => {
-                                s.word(if *late { "inlateout" } else { "inout" });
-                                s.popen();
-                                print_reg_or_class(s, reg);
-                                s.pclose();
-                                s.space();
-                                s.print_expr(expr);
-                            }
-                            InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
-                                s.word(if *late { "inlateout" } else { "inout" });
-                                s.popen();
-                                print_reg_or_class(s, reg);
-                                s.pclose();
-                                s.space();
-                                s.print_expr(in_expr);
-                                s.space();
-                                s.word_space("=>");
-                                match out_expr {
-                                    Some(out_expr) => s.print_expr(out_expr),
-                                    None => s.word("_"),
-                                }
-                            }
-                            InlineAsmOperand::Const { anon_const } => {
-                                s.word("const");
-                                s.space();
-                                s.print_expr(&anon_const.value);
-                            }
-                            InlineAsmOperand::Sym { expr } => {
-                                s.word("sym");
-                                s.space();
-                                s.print_expr(expr);
-                            }
-                        }
-                    }
-                    AsmArg::Options(opts) => {
-                        s.word("options");
-                        s.popen();
-                        let mut options = vec![];
-                        if opts.contains(InlineAsmOptions::PURE) {
-                            options.push("pure");
-                        }
-                        if opts.contains(InlineAsmOptions::NOMEM) {
-                            options.push("nomem");
-                        }
-                        if opts.contains(InlineAsmOptions::READONLY) {
-                            options.push("readonly");
-                        }
-                        if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
-                            options.push("preserves_flags");
-                        }
-                        if opts.contains(InlineAsmOptions::NORETURN) {
-                            options.push("noreturn");
-                        }
-                        if opts.contains(InlineAsmOptions::NOSTACK) {
-                            options.push("nostack");
-                        }
-                        if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
-                            options.push("att_syntax");
-                        }
-                        s.commasep(Inconsistent, &options, |s, &opt| {
-                            s.word(opt);
-                        });
-                        s.pclose();
-                    }
-                });
-                self.pclose();
+                self.print_inline_asm(a);
             }
             ast::ExprKind::LlvmInlineAsm(ref a) => {
                 self.s.word("llvm_asm!");
@@ -2267,6 +2158,116 @@ impl<'a> State<'a> {
         self.end();
     }
 
+    fn print_inline_asm(&mut self, asm: &ast::InlineAsm) {
+        enum AsmArg<'a> {
+            Template(String),
+            Operand(&'a InlineAsmOperand),
+            Options(InlineAsmOptions),
+        }
+
+        let mut args = vec![];
+        args.push(AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template)));
+        args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
+        if !asm.options.is_empty() {
+            args.push(AsmArg::Options(asm.options));
+        }
+
+        self.popen();
+        self.commasep(Consistent, &args, |s, arg| match arg {
+            AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
+            AsmArg::Operand(op) => {
+                let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
+                    InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
+                    InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
+                };
+                match op {
+                    InlineAsmOperand::In { reg, expr } => {
+                        s.word("in");
+                        s.popen();
+                        print_reg_or_class(s, reg);
+                        s.pclose();
+                        s.space();
+                        s.print_expr(expr);
+                    }
+                    InlineAsmOperand::Out { reg, late, expr } => {
+                        s.word(if *late { "lateout" } else { "out" });
+                        s.popen();
+                        print_reg_or_class(s, reg);
+                        s.pclose();
+                        s.space();
+                        match expr {
+                            Some(expr) => s.print_expr(expr),
+                            None => s.word("_"),
+                        }
+                    }
+                    InlineAsmOperand::InOut { reg, late, expr } => {
+                        s.word(if *late { "inlateout" } else { "inout" });
+                        s.popen();
+                        print_reg_or_class(s, reg);
+                        s.pclose();
+                        s.space();
+                        s.print_expr(expr);
+                    }
+                    InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
+                        s.word(if *late { "inlateout" } else { "inout" });
+                        s.popen();
+                        print_reg_or_class(s, reg);
+                        s.pclose();
+                        s.space();
+                        s.print_expr(in_expr);
+                        s.space();
+                        s.word_space("=>");
+                        match out_expr {
+                            Some(out_expr) => s.print_expr(out_expr),
+                            None => s.word("_"),
+                        }
+                    }
+                    InlineAsmOperand::Const { anon_const } => {
+                        s.word("const");
+                        s.space();
+                        s.print_expr(&anon_const.value);
+                    }
+                    InlineAsmOperand::Sym { expr } => {
+                        s.word("sym");
+                        s.space();
+                        s.print_expr(expr);
+                    }
+                }
+            }
+            AsmArg::Options(opts) => {
+                s.word("options");
+                s.popen();
+                let mut options = vec![];
+                if opts.contains(InlineAsmOptions::PURE) {
+                    options.push("pure");
+                }
+                if opts.contains(InlineAsmOptions::NOMEM) {
+                    options.push("nomem");
+                }
+                if opts.contains(InlineAsmOptions::READONLY) {
+                    options.push("readonly");
+                }
+                if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
+                    options.push("preserves_flags");
+                }
+                if opts.contains(InlineAsmOptions::NORETURN) {
+                    options.push("noreturn");
+                }
+                if opts.contains(InlineAsmOptions::NOSTACK) {
+                    options.push("nostack");
+                }
+                if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
+                    options.push("att_syntax");
+                }
+                s.commasep(Inconsistent, &options, |s, &opt| {
+                    s.word(opt);
+                });
+                s.pclose();
+            }
+        });
+        self.pclose();
+    }
+
     crate fn print_local_decl(&mut self, loc: &ast::Local) {
         self.print_pat(&loc.pat);
         if let Some(ref ty) = loc.ty {
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index fd976b119b7..b28c6f0d99c 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -8,9 +8,11 @@ use rustc_expand::base::{self, *};
 use rustc_parse::parser::Parser;
 use rustc_parse_format as parse;
 use rustc_session::lint;
+use rustc_span::symbol::Ident;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{InnerSpan, Span};
 use rustc_target::asm::InlineAsmArch;
+use smallvec::smallvec;
 
 struct AsmArgs {
     templates: Vec<P<ast::Expr>>,
@@ -25,6 +27,7 @@ fn parse_args<'a>(
     ecx: &mut ExtCtxt<'a>,
     sp: Span,
     tts: TokenStream,
+    is_global_asm: bool,
 ) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
     let mut p = ecx.new_parser_from_tts(tts);
 
@@ -33,7 +36,7 @@ fn parse_args<'a>(
     }
 
     // Detect use of the legacy llvm_asm! syntax (which used to be called asm!)
-    if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
+    if !is_global_asm && p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
         let mut err =
             ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported");
         err.note("consider migrating to the new asm! syntax specified in RFC 2873");
@@ -84,7 +87,7 @@ fn parse_args<'a>(
 
         // Parse options
         if p.eat_keyword(sym::options) {
-            parse_options(&mut p, &mut args)?;
+            parse_options(&mut p, &mut args, is_global_asm)?;
             allow_templates = false;
             continue;
         }
@@ -103,19 +106,19 @@ fn parse_args<'a>(
         };
 
         let mut explicit_reg = false;
-        let op = if p.eat_keyword(kw::In) {
+        let op = if !is_global_asm && p.eat_keyword(kw::In) {
             let reg = parse_reg(&mut p, &mut explicit_reg)?;
             let expr = p.parse_expr()?;
             ast::InlineAsmOperand::In { reg, expr }
-        } else if p.eat_keyword(sym::out) {
+        } else if !is_global_asm && p.eat_keyword(sym::out) {
             let reg = parse_reg(&mut p, &mut explicit_reg)?;
             let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
             ast::InlineAsmOperand::Out { reg, expr, late: false }
-        } else if p.eat_keyword(sym::lateout) {
+        } else if !is_global_asm && p.eat_keyword(sym::lateout) {
             let reg = parse_reg(&mut p, &mut explicit_reg)?;
             let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
             ast::InlineAsmOperand::Out { reg, expr, late: true }
-        } else if p.eat_keyword(sym::inout) {
+        } else if !is_global_asm && p.eat_keyword(sym::inout) {
             let reg = parse_reg(&mut p, &mut explicit_reg)?;
             let expr = p.parse_expr()?;
             if p.eat(&token::FatArrow) {
@@ -125,7 +128,7 @@ fn parse_args<'a>(
             } else {
                 ast::InlineAsmOperand::InOut { reg, expr, late: false }
             }
-        } else if p.eat_keyword(sym::inlateout) {
+        } else if !is_global_asm && p.eat_keyword(sym::inlateout) {
             let reg = parse_reg(&mut p, &mut explicit_reg)?;
             let expr = p.parse_expr()?;
             if p.eat(&token::FatArrow) {
@@ -138,7 +141,7 @@ fn parse_args<'a>(
         } else if p.eat_keyword(kw::Const) {
             let anon_const = p.parse_anon_const_expr()?;
             ast::InlineAsmOperand::Const { anon_const }
-        } else if p.eat_keyword(sym::sym) {
+        } else if !is_global_asm && p.eat_keyword(sym::sym) {
             let expr = p.parse_expr()?;
             match expr.kind {
                 ast::ExprKind::Path(..) => {}
@@ -329,23 +332,27 @@ fn try_set_option<'a>(
     }
 }
 
-fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), DiagnosticBuilder<'a>> {
+fn parse_options<'a>(
+    p: &mut Parser<'a>,
+    args: &mut AsmArgs,
+    is_global_asm: bool,
+) -> Result<(), DiagnosticBuilder<'a>> {
     let span_start = p.prev_token.span;
 
     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
 
     while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
-        if p.eat_keyword(sym::pure) {
+        if !is_global_asm && p.eat_keyword(sym::pure) {
             try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
-        } else if p.eat_keyword(sym::nomem) {
+        } else if !is_global_asm && p.eat_keyword(sym::nomem) {
             try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM);
-        } else if p.eat_keyword(sym::readonly) {
+        } else if !is_global_asm && p.eat_keyword(sym::readonly) {
             try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY);
-        } else if p.eat_keyword(sym::preserves_flags) {
+        } else if !is_global_asm && p.eat_keyword(sym::preserves_flags) {
             try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS);
-        } else if p.eat_keyword(sym::noreturn) {
+        } else if !is_global_asm && p.eat_keyword(sym::noreturn) {
             try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN);
-        } else if p.eat_keyword(sym::nostack) {
+        } else if !is_global_asm && p.eat_keyword(sym::nostack) {
             try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
         } else if p.eat_keyword(sym::att_syntax) {
             try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
@@ -388,7 +395,7 @@ fn parse_reg<'a>(
     Ok(result)
 }
 
-fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast::Expr> {
+fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> {
     let mut template = vec![];
     // Register operands are implicitly used since they are not allowed to be
     // referenced in the template string.
@@ -415,7 +422,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
                     if let Some(mut err) = err {
                         err.emit();
                     }
-                    return DummyResult::raw_expr(sp, true);
+                    return None;
                 }
             };
 
@@ -492,7 +499,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
                 e.span_label(err_sp, label);
             }
             e.emit();
-            return DummyResult::raw_expr(sp, true);
+            return None;
         }
 
         curarg = parser.curarg;
@@ -643,15 +650,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
         }
     }
 
-    let inline_asm =
-        ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans };
-    P(ast::Expr {
-        id: ast::DUMMY_NODE_ID,
-        kind: ast::ExprKind::InlineAsm(P(inline_asm)),
-        span: sp,
-        attrs: ast::AttrVec::new(),
-        tokens: None,
-    })
+    Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
 }
 
 pub fn expand_asm<'cx>(
@@ -659,8 +658,53 @@ pub fn expand_asm<'cx>(
     sp: Span,
     tts: TokenStream,
 ) -> Box<dyn base::MacResult + 'cx> {
-    match parse_args(ecx, sp, tts) {
-        Ok(args) => MacEager::expr(expand_preparsed_asm(ecx, sp, args)),
+    match parse_args(ecx, sp, tts, false) {
+        Ok(args) => {
+            let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
+                P(ast::Expr {
+                    id: ast::DUMMY_NODE_ID,
+                    kind: ast::ExprKind::InlineAsm(P(inline_asm)),
+                    span: sp,
+                    attrs: ast::AttrVec::new(),
+                    tokens: None,
+                })
+            } else {
+                DummyResult::raw_expr(sp, true)
+            };
+            MacEager::expr(expr)
+        }
+        Err(mut err) => {
+            err.emit();
+            DummyResult::any(sp)
+        }
+    }
+}
+
+pub fn expand_global_asm<'cx>(
+    ecx: &'cx mut ExtCtxt<'_>,
+    sp: Span,
+    tts: TokenStream,
+) -> Box<dyn base::MacResult + 'cx> {
+    match parse_args(ecx, sp, tts, true) {
+        Ok(args) => {
+            if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
+                MacEager::items(smallvec![P(ast::Item {
+                    ident: Ident::invalid(),
+                    attrs: Vec::new(),
+                    id: ast::DUMMY_NODE_ID,
+                    kind: ast::ItemKind::GlobalAsm(inline_asm),
+                    vis: ast::Visibility {
+                        span: sp.shrink_to_lo(),
+                        kind: ast::VisibilityKind::Inherited,
+                        tokens: None,
+                    },
+                    span: ecx.with_def_site_ctxt(sp),
+                    tokens: None,
+                })])
+            } else {
+                DummyResult::any(sp)
+            }
+        }
         Err(mut err) => {
             err.emit();
             DummyResult::any(sp)
diff --git a/compiler/rustc_builtin_macros/src/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs
deleted file mode 100644
index 76d87452927..00000000000
--- a/compiler/rustc_builtin_macros/src/global_asm.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-//! Module-level assembly support.
-//!
-//! The macro defined here allows you to specify "top-level",
-//! "file-scoped", or "module-level" assembly. These synonyms
-//! all correspond to LLVM's module-level inline assembly instruction.
-//!
-//! For example, `global_asm!("some assembly here")` codegens to
-//! LLVM's `module asm "some assembly here"`. All of LLVM's caveats
-//! therefore apply.
-
-use rustc_ast as ast;
-use rustc_ast::ptr::P;
-use rustc_ast::token;
-use rustc_ast::tokenstream::TokenStream;
-use rustc_errors::DiagnosticBuilder;
-use rustc_expand::base::{self, *};
-use rustc_span::symbol::Ident;
-use rustc_span::Span;
-use smallvec::smallvec;
-
-pub fn expand_global_asm<'cx>(
-    cx: &'cx mut ExtCtxt<'_>,
-    sp: Span,
-    tts: TokenStream,
-) -> Box<dyn base::MacResult + 'cx> {
-    match parse_global_asm(cx, sp, tts) {
-        Ok(Some(global_asm)) => MacEager::items(smallvec![P(ast::Item {
-            ident: Ident::invalid(),
-            attrs: Vec::new(),
-            id: ast::DUMMY_NODE_ID,
-            kind: ast::ItemKind::GlobalAsm(global_asm),
-            vis: ast::Visibility {
-                span: sp.shrink_to_lo(),
-                kind: ast::VisibilityKind::Inherited,
-                tokens: None,
-            },
-            span: cx.with_def_site_ctxt(sp),
-            tokens: None,
-        })]),
-        Ok(None) => DummyResult::any(sp),
-        Err(mut err) => {
-            err.emit();
-            DummyResult::any(sp)
-        }
-    }
-}
-
-fn parse_global_asm<'a>(
-    cx: &mut ExtCtxt<'a>,
-    sp: Span,
-    tts: TokenStream,
-) -> Result<Option<ast::GlobalAsm>, DiagnosticBuilder<'a>> {
-    let mut p = cx.new_parser_from_tts(tts);
-
-    if p.token == token::Eof {
-        let mut err = cx.struct_span_err(sp, "macro requires a string literal as an argument");
-        err.span_label(sp, "string literal required");
-        return Err(err);
-    }
-
-    let expr = p.parse_expr()?;
-    let (asm, _) = match expr_to_string(cx, expr, "inline assembly must be a string literal") {
-        Some((s, st)) => (s, st),
-        None => return Ok(None),
-    };
-
-    Ok(Some(ast::GlobalAsm { asm }))
-}
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 65a141e1112..17b7793c7dd 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 e5f06551bb6..004e6bddaf3 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 ea08052a9d0..a571418c1f5 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 afd83bfcb56..955f658eb1c 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 72e9163b88e..2bd35fe9b14 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 8e79193759e..48d753e0d84 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<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx);
@@ -32,8 +33,35 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
             }
             MonoItem::GlobalAsm(item_id) => {
                 let item = cx.tcx().hir().item(item_id);
-                if let hir::ItemKind::GlobalAsm(ref ga) = item.kind {
-                    cx.codegen_global_asm(ga);
+                if let hir::ItemKind::GlobalAsm(ref asm) = item.kind {
+                    let operands: Vec<_> = asm
+                        .operands
+                        .iter()
+                        .map(|(op, op_sp)| match *op {
+                            hir::InlineAsmOperand::Const { ref anon_const } => {
+                                let anon_const_def_id =
+                                    cx.tcx().hir().local_def_id(anon_const.hir_id).to_def_id();
+                                let const_value =
+                                    cx.tcx().const_eval_poly(anon_const_def_id).unwrap_or_else(
+                                        |_| span_bug!(*op_sp, "asm const cannot be resolved"),
+                                    );
+                                let ty = cx
+                                    .tcx()
+                                    .typeck_body(anon_const.body)
+                                    .node_type(anon_const.hir_id);
+                                let string = common::asm_const_to_str(
+                                    cx.tcx(),
+                                    *op_sp,
+                                    const_value,
+                                    cx.layout_of(ty),
+                                );
+                                GlobalAsmOperandRef::Const { string }
+                            }
+                            _ => span_bug!(*op_sp, "invalid operand type for global_asm!"),
+                        })
+                        .collect();
+
+                    cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans);
                 } else {
                     span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
                 }
diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs
index 69931935c49..86f2781a766 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 be2e0ea230f..c529fbbf518 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 ddf82186169..b05ca381b8a 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 e689ae4d81d..91fd97a0d40 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<u8>`.
     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 0ce04a77a50..c08f1f53218 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 77d083fc5e9..fe02cc5de8c 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 fdefc890674..c0d327d4788 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 3f095d0e824..4532a0a350c 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 92f21191de4..ffa825b7d46 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 cb7589318d2..994206bd419 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 97b6f5cf412..5197b620f90 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 4f97c95bcb9..5793f5e681b 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 5d9b0f80d3a..b46a7aa138c 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 ab7b142ef3d..44e8af9bf54 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 f9536c4203d..231cc15b849 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 ce1155a977c..e241f5788b9 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 8e91a8d842c..791dec2ed69 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 8402afa5085..b8903e07cfd 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 c43bf83fe19..a8621a0c518 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);