mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
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:
parent
952c5732c2
commit
5918ee4317
@ -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",
|
||||
|
@ -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>;`.
|
||||
|
@ -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: _,
|
||||
|
@ -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)
|
||||
|
328
compiler/rustc_ast_lowering/src/asm.rs
Normal file
328
compiler/rustc_ast_lowering/src/asm.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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);
|
||||
|
@ -77,6 +77,7 @@ macro_rules! arena_vec {
|
||||
});
|
||||
}
|
||||
|
||||
mod asm;
|
||||
mod expr;
|
||||
mod item;
|
||||
mod pat;
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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 }))
|
||||
}
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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 } => {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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],
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>,
|
||||
|
@ -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;`.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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!"),
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}) =>
|
||||
|
@ -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"
|
||||
|
@ -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 */
|
||||
};
|
||||
}
|
||||
|
@ -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'] }
|
||||
|
@ -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);
|
||||
|
@ -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() {}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user