Add support for const operands and options to global_asm!

On x86, the default syntax is also switched to Intel to match asm!
This commit is contained in:
Amanieu d'Antras 2021-04-11 20:51:28 +01:00
parent 952c5732c2
commit 5918ee4317
36 changed files with 928 additions and 800 deletions

View File

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

View File

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

View File

@ -965,7 +965,7 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
ModKind::Unloaded => {} ModKind::Unloaded => {}
}, },
ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), 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)) => { ItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => {
vis.visit_generics(generics); vis.visit_generics(generics);
visit_bounds(bounds, vis); 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); 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>( pub fn noop_visit_expr<T: MutVisitor>(
Expr { kind, id, span, attrs, tokens }: &mut Expr, Expr { kind, id, span, attrs, tokens }: &mut Expr,
vis: &mut T, vis: &mut T,
@ -1288,27 +1310,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
ExprKind::Ret(expr) => { ExprKind::Ret(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr)); visit_opt(expr, |expr| vis.visit_expr(expr));
} }
ExprKind::InlineAsm(asm) => { ExprKind::InlineAsm(asm) => noop_visit_inline_asm(asm, vis),
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::LlvmInlineAsm(asm) => { ExprKind::LlvmInlineAsm(asm) => {
let LlvmInlineAsm { let LlvmInlineAsm {
asm: _, asm: _,

View File

@ -90,9 +90,6 @@ pub trait Visitor<'ast>: Sized {
fn visit_foreign_item(&mut self, i: &'ast ForeignItem) { fn visit_foreign_item(&mut self, i: &'ast ForeignItem) {
walk_foreign_item(self, i) 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) { fn visit_item(&mut self, i: &'ast Item) {
walk_item(self, i) 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) => { ItemKind::ForeignMod(ref foreign_module) => {
walk_list!(visitor, visit_foreign_item, &foreign_module.items); 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)) => { ItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty)) => {
visitor.visit_generics(generics); visitor.visit_generics(generics);
walk_list!(visitor, visit_param_bound, bounds); 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) { pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) {
match *bound { match *bound {
GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier), 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); 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) { pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
walk_list!(visitor, visit_attribute, expression.attrs.iter()); 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::MacCall(ref mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(ref ia) => { ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
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::LlvmInlineAsm(ref ia) => { ExprKind::LlvmInlineAsm(ref ia) => {
for &(_, ref input) in &ia.inputs { for &(_, ref input) in &ia.inputs {
visitor.visit_expr(input) visitor.visit_expr(input)

View File

@ -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)
}
}

View File

@ -3,7 +3,6 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericAr
use rustc_ast::attr; use rustc_ast::attr;
use rustc_ast::ptr::P as AstP; use rustc_ast::ptr::P as AstP;
use rustc_ast::*; use rustc_ast::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::struct_span_err; 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::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP}; 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> { impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'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)); let e = e.as_ref().map(|x| self.lower_expr(x));
hir::ExprKind::Ret(e) 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::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
ExprKind::Struct(ref se) => { ExprKind::Struct(ref se) => {
let rest = match &se.rest { let rest = match &se.rest {
@ -1329,319 +1327,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
result 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> { fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> {
let inner = hir::LlvmInlineAsmInner { let inner = hir::LlvmInlineAsmInner {
inputs: asm.inputs.iter().map(|&(c, _)| c).collect(), inputs: asm.inputs.iter().map(|&(c, _)| c).collect(),

View File

@ -329,7 +329,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), .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))) => { ItemKind::TyAlias(box TyAliasKind(_, ref gen, _, Some(ref ty))) => {
// We lower // 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> { fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
let id = self.lower_node_id(v.id); let id = self.lower_node_id(v.id);
self.lower_attrs(id, &v.attrs); self.lower_attrs(id, &v.attrs);

View File

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

View File

@ -1164,9 +1164,9 @@ impl<'a> State<'a> {
self.print_foreign_mod(nmod, &item.attrs); self.print_foreign_mod(nmod, &item.attrs);
self.bclose(item.span); self.bclose(item.span);
} }
ast::ItemKind::GlobalAsm(ref ga) => { ast::ItemKind::GlobalAsm(ref asm) => {
self.head(visibility_qualified(&item.vis, "global_asm!")); self.head(visibility_qualified(&item.vis, "global_asm!"));
self.s.word(ga.asm.to_string()); self.print_inline_asm(asm);
self.end(); self.end();
} }
ast::ItemKind::TyAlias(box ast::TyAliasKind(def, ref generics, ref bounds, ref ty)) => { 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) => { 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.word("asm!");
self.popen(); self.print_inline_asm(a);
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();
} }
ast::ExprKind::LlvmInlineAsm(ref a) => { ast::ExprKind::LlvmInlineAsm(ref a) => {
self.s.word("llvm_asm!"); self.s.word("llvm_asm!");
@ -2267,6 +2158,116 @@ impl<'a> State<'a> {
self.end(); 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) { crate fn print_local_decl(&mut self, loc: &ast::Local) {
self.print_pat(&loc.pat); self.print_pat(&loc.pat);
if let Some(ref ty) = loc.ty { if let Some(ref ty) = loc.ty {

View File

@ -8,9 +8,11 @@ use rustc_expand::base::{self, *};
use rustc_parse::parser::Parser; use rustc_parse::parser::Parser;
use rustc_parse_format as parse; use rustc_parse_format as parse;
use rustc_session::lint; use rustc_session::lint;
use rustc_span::symbol::Ident;
use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{InnerSpan, Span}; use rustc_span::{InnerSpan, Span};
use rustc_target::asm::InlineAsmArch; use rustc_target::asm::InlineAsmArch;
use smallvec::smallvec;
struct AsmArgs { struct AsmArgs {
templates: Vec<P<ast::Expr>>, templates: Vec<P<ast::Expr>>,
@ -25,6 +27,7 @@ fn parse_args<'a>(
ecx: &mut ExtCtxt<'a>, ecx: &mut ExtCtxt<'a>,
sp: Span, sp: Span,
tts: TokenStream, tts: TokenStream,
is_global_asm: bool,
) -> Result<AsmArgs, DiagnosticBuilder<'a>> { ) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
let mut p = ecx.new_parser_from_tts(tts); 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!) // 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 = let mut err =
ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported"); 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"); err.note("consider migrating to the new asm! syntax specified in RFC 2873");
@ -84,7 +87,7 @@ fn parse_args<'a>(
// Parse options // Parse options
if p.eat_keyword(sym::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; allow_templates = false;
continue; continue;
} }
@ -103,19 +106,19 @@ fn parse_args<'a>(
}; };
let mut explicit_reg = false; 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 reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?; let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, 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 reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false } 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 reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true } 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 reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?; let expr = p.parse_expr()?;
if p.eat(&token::FatArrow) { if p.eat(&token::FatArrow) {
@ -125,7 +128,7 @@ fn parse_args<'a>(
} else { } else {
ast::InlineAsmOperand::InOut { reg, expr, late: false } 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 reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?; let expr = p.parse_expr()?;
if p.eat(&token::FatArrow) { if p.eat(&token::FatArrow) {
@ -138,7 +141,7 @@ fn parse_args<'a>(
} else if p.eat_keyword(kw::Const) { } else if p.eat_keyword(kw::Const) {
let anon_const = p.parse_anon_const_expr()?; let anon_const = p.parse_anon_const_expr()?;
ast::InlineAsmOperand::Const { anon_const } 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()?; let expr = p.parse_expr()?;
match expr.kind { match expr.kind {
ast::ExprKind::Path(..) => {} 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; let span_start = p.prev_token.span;
p.expect(&token::OpenDelim(token::DelimToken::Paren))?; p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
while !p.eat(&token::CloseDelim(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); 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); 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); 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); 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); 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); try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
} else if p.eat_keyword(sym::att_syntax) { } else if p.eat_keyword(sym::att_syntax) {
try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX); try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
@ -388,7 +395,7 @@ fn parse_reg<'a>(
Ok(result) 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![]; let mut template = vec![];
// Register operands are implicitly used since they are not allowed to be // Register operands are implicitly used since they are not allowed to be
// referenced in the template string. // 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 { if let Some(mut err) = err {
err.emit(); 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.span_label(err_sp, label);
} }
e.emit(); e.emit();
return DummyResult::raw_expr(sp, true); return None;
} }
curarg = parser.curarg; curarg = parser.curarg;
@ -643,15 +650,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast
} }
} }
let inline_asm = Some(ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans })
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,
})
} }
pub fn expand_asm<'cx>( pub fn expand_asm<'cx>(
@ -659,8 +658,53 @@ pub fn expand_asm<'cx>(
sp: Span, sp: Span,
tts: TokenStream, tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> { ) -> Box<dyn base::MacResult + 'cx> {
match parse_args(ecx, sp, tts) { match parse_args(ecx, sp, tts, false) {
Ok(args) => MacEager::expr(expand_preparsed_asm(ecx, sp, args)), 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(mut err) => {
err.emit(); err.emit();
DummyResult::any(sp) DummyResult::any(sp)

View File

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

View File

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

View File

@ -3,6 +3,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_codegen_ssa::back::linker::LinkerInfo; use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; 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::Static(def_id) => crate::constant::codegen_static(tcx, &mut module, def_id),
MonoItem::GlobalAsm(item_id) => { MonoItem::GlobalAsm(item_id) => {
let item = cx.tcx.hir().item(item_id); let item = cx.tcx.hir().item(item_id);
if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind { if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind {
cx.global_asm.push_str(&*asm.as_str()); if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
cx.global_asm.push_str("\n\n"); 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 { } else {
bug!("Expected GlobalAsm found {:?}", item); bug!("Expected GlobalAsm found {:?}", item);
} }

View File

@ -356,10 +356,49 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
} }
impl AsmMethods for CodegenCx<'ll, 'tcx> { impl AsmMethods for CodegenCx<'ll, 'tcx> {
fn codegen_global_asm(&self, ga: &hir::GlobalAsm) { fn codegen_global_asm(
let asm = ga.asm.as_str(); &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 { unsafe {
llvm::LLVMRustAppendModuleInlineAsm(self.llmod, asm.as_ptr().cast(), asm.len()); llvm::LLVMRustAppendModuleInlineAsm(
self.llmod,
template_str.as_ptr().cast(),
template_str.len(),
);
} }
} }
} }

View File

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

View File

@ -12,7 +12,6 @@ use crate::MemFlags;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::AssertKind; use rustc_middle::mir::AssertKind;
use rustc_middle::mir::{self, SwitchTargets}; use rustc_middle::mir::{self, SwitchTargets};
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; 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 let const_value = self
.eval_mir_constant(value) .eval_mir_constant(value)
.unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved"));
let ty = value.ty(); let string = common::asm_const_to_str(
let size = bx.layout_of(ty).size; bx.tcx(),
let scalar = match const_value { span,
ConstValue::Scalar(s) => s, const_value,
_ => span_bug!( bx.layout_of(value.ty()),
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),
};
InlineAsmOperandRef::Const { string } InlineAsmOperandRef::Const { string }
} }
mir::InlineAsmOperand::SymFn { ref value } => { mir::InlineAsmOperand::SymFn { ref value } => {

View File

@ -1,10 +1,11 @@
use crate::base; use crate::base;
use crate::common;
use crate::traits::*; use crate::traits::*;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::layout::HasTyCtxt;
use rustc_target::abi::LayoutOf;
use rustc_middle::mir::mono::MonoItem;
pub trait MonoItemExt<'a, 'tcx> { pub trait MonoItemExt<'a, 'tcx> {
fn define<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx); 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) => { MonoItem::GlobalAsm(item_id) => {
let item = cx.tcx().hir().item(item_id); let item = cx.tcx().hir().item(item_id);
if let hir::ItemKind::GlobalAsm(ref ga) = item.kind { if let hir::ItemKind::GlobalAsm(ref asm) = item.kind {
cx.codegen_global_asm(ga); 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 { } else {
span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
} }

View File

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

View File

@ -29,7 +29,7 @@ mod type_;
mod write; mod write;
pub use self::abi::AbiBuilderMethods; 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::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::builder::{BuilderMethods, OverflowOp};
pub use self::consts::ConstMethods; pub use self::consts::ConstMethods;

View File

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

View File

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

View File

@ -589,8 +589,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
visitor.visit_id(item.hir_id()); visitor.visit_id(item.hir_id());
walk_list!(visitor, visit_foreign_item_ref, items); walk_list!(visitor, visit_foreign_item_ref, items);
} }
ItemKind::GlobalAsm(_) => { ItemKind::GlobalAsm(asm) => {
visitor.visit_id(item.hir_id()); visitor.visit_id(item.hir_id());
walk_inline_asm(visitor, asm);
} }
ItemKind::TyAlias(ref ty, ref generics) => { ItemKind::TyAlias(ref ty, ref generics) => {
visitor.visit_id(item.hir_id()); 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) { 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_id(hir_id);
visitor.visit_path(path, 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); walk_list!(visitor, visit_expr, optional_expression);
} }
ExprKind::InlineAsm(ref asm) => { ExprKind::InlineAsm(ref asm) => {
for (op, _op_sp) in asm.operands { walk_inline_asm(visitor, asm);
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::LlvmInlineAsm(ref asm) => { ExprKind::LlvmInlineAsm(ref asm) => {
walk_list!(visitor, visit_expr, asm.outputs_exprs); walk_list!(visitor, visit_expr, asm.outputs_exprs);

View File

@ -660,9 +660,9 @@ impl<'a> State<'a> {
} }
self.bclose(item.span); self.bclose(item.span);
} }
hir::ItemKind::GlobalAsm(ref ga) => { hir::ItemKind::GlobalAsm(ref asm) => {
self.head(visibility_qualified(&item.vis, "global asm")); self.head(visibility_qualified(&item.vis, "global_asm!"));
self.s.word(ga.asm.to_string()); self.print_inline_asm(asm);
self.end() self.end()
} }
hir::ItemKind::TyAlias(ref ty, ref generics) => { hir::ItemKind::TyAlias(ref ty, ref generics) => {
@ -1352,6 +1352,110 @@ impl<'a> State<'a> {
self.word(lit.node.to_lit_token().to_string()) 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<'_>) { pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
self.maybe_print_comment(expr.span.lo()); self.maybe_print_comment(expr.span.lo());
self.print_outer_attributes(self.attrs(expr.hir_id)); 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); self.print_expr_maybe_paren(&expr, parser::PREC_JUMP);
} }
} }
hir::ExprKind::InlineAsm(ref a) => { hir::ExprKind::InlineAsm(ref asm) => {
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));
}
self.word("asm!"); self.word("asm!");
self.popen(); self.print_inline_asm(asm);
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();
} }
hir::ExprKind::LlvmInlineAsm(ref a) => { hir::ExprKind::LlvmInlineAsm(ref a) => {
let i = &a.inner; let i = &a.inner;

View File

@ -390,8 +390,27 @@ fn collect_items_rec<'tcx>(
collect_neighbours(tcx, instance, &mut neighbors); collect_neighbours(tcx, instance, &mut neighbors);
}); });
} }
MonoItem::GlobalAsm(..) => { MonoItem::GlobalAsm(item_id) => {
recursion_depth_reset = None; 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")
}
} }
} }

View File

@ -405,6 +405,33 @@ impl Visitor<'tcx> for ItemVisitor<'tcx> {
ExprVisitor { tcx: self.tcx, param_env, typeck_results }.visit_body(body); ExprVisitor { tcx: self.tcx, param_env, typeck_results }.visit_body(body);
self.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> { impl Visitor<'tcx> for ExprVisitor<'tcx> {

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ edition = "2018"
[dependencies] [dependencies]
core = { path = "../core" } 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] [dev-dependencies]
rand = "0.7" rand = "0.7"

View File

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

View File

@ -17,7 +17,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" } panic_abort = { path = "../panic_abort" }
core = { path = "../core" } core = { path = "../core" }
libc = { version = "0.2.93", default-features = false, features = ['rustc-dep-of-std'] } 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 } profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" } unwind = { path = "../unwind" }
hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] } hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] }

View File

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

View File

@ -38,11 +38,11 @@ And a more complicated usage looks like this:
# mod x86 { # mod x86 {
pub mod sally { pub mod sally {
global_asm!(r#" global_asm!(
.global foo ".global foo",
foo: "foo:",
jmp baz "jmp baz",
"#); );
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn baz() {} pub unsafe extern "C" fn baz() {}
@ -56,11 +56,11 @@ extern "C" {
} }
pub mod harry { pub mod harry {
global_asm!(r#" global_asm!(
.global bar ".global bar",
bar: "bar:",
jmp quux "jmp quux",
"#); );
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn quux() {} pub unsafe extern "C" fn quux() {}

View File

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

View File

@ -1,7 +1,7 @@
#![feature(global_asm)] #![feature(global_asm)]
fn main() { 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!(struct); //~ ERROR expected expression
global_asm!(123); //~ ERROR inline assembly must be a string literal global_asm!(123); //~ ERROR asm template must be a string literal
} }

View File

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