diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 75ccbc92be1f6..0b59e5c841f51 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1267,7 +1267,7 @@ impl Expr { ExprKind::Break(..) => ExprPrecedence::Break, ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, + ExprKind::InlineAsm(..) | ExprKind::LlvmInlineAsm(..) => ExprPrecedence::InlineAsm, ExprKind::MacCall(..) => ExprPrecedence::Mac, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, @@ -1437,6 +1437,8 @@ pub enum ExprKind { /// Output of the `asm!()` macro. InlineAsm(P), + /// Output of the `llvm_asm!()` macro. + LlvmInlineAsm(P), /// A macro invocation; pre-expansion. MacCall(MacCall), @@ -2107,6 +2109,41 @@ pub struct InlineAsm { pub line_spans: Vec, } +/// Inline assembly dialect. +/// +/// E.g., `"intel"` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Hash, HashStable_Generic)] +pub enum LlvmAsmDialect { + Att, + Intel, +} + +/// LLVM-style inline assembly. +/// +/// E.g., `"={eax}"(result)` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct LlvmInlineAsmOutput { + pub constraint: Symbol, + pub expr: P, + pub is_rw: bool, + pub is_indirect: bool, +} + +/// LLVM-style inline assembly. +/// +/// E.g., `llvm_asm!("NOP");`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct LlvmInlineAsm { + pub asm: Symbol, + pub asm_str_style: StrStyle, + pub outputs: Vec, + pub inputs: Vec<(Symbol, P)>, + pub clobbers: Vec, + pub volatile: bool, + pub alignstack: bool, + pub dialect: LlvmAsmDialect, +} + /// A parameter in a function header. /// /// E.g., `bar: usize` as in `fn foo(bar: usize)`. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 32621eb5f2f66..785ebda9d34e4 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1373,6 +1373,23 @@ pub fn noop_visit_expr( visit_opt(expr, |expr| vis.visit_expr(expr)); } ExprKind::InlineAsm(asm) => noop_visit_inline_asm(asm, vis), + ExprKind::LlvmInlineAsm(asm) => { + let LlvmInlineAsm { + asm: _, + asm_str_style: _, + outputs, + inputs, + clobbers: _, + volatile: _, + alignstack: _, + dialect: _, + } = asm.deref_mut(); + for out in outputs { + let LlvmInlineAsmOutput { constraint: _, expr, is_rw: _, is_indirect: _ } = out; + vis.visit_expr(expr); + } + visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr)); + } ExprKind::MacCall(mac) => vis.visit_mac_call(mac), ExprKind::Struct(se) => { let StructExpr { qself, path, fields, rest } = se.deref_mut(); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ed16c25d921e4..b8a2d472dbbd3 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -862,6 +862,14 @@ 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 asm) => walk_inline_asm(visitor, asm), + ExprKind::LlvmInlineAsm(ref ia) => { + for &(_, ref input) in &ia.inputs { + visitor.visit_expr(input) + } + for output in &ia.outputs { + visitor.visit_expr(&output.expr) + } + } ExprKind::Yield(ref optional_expression) => { walk_list!(visitor, visit_expr, optional_expression); } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9442e0f1a1f35..2f047362ab2d1 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -225,6 +225,7 @@ impl<'hir> LoweringContext<'_, 'hir> { 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 { StructRest::Base(e) => Some(self.lower_expr(e)), @@ -1294,6 +1295,38 @@ impl<'hir> LoweringContext<'_, 'hir> { result } + fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> { + let inner = hir::LlvmInlineAsmInner { + inputs: asm.inputs.iter().map(|&(c, _)| c).collect(), + outputs: asm + .outputs + .iter() + .map(|out| hir::LlvmInlineAsmOutput { + constraint: out.constraint, + is_rw: out.is_rw, + is_indirect: out.is_indirect, + span: self.lower_span(out.expr.span), + }) + .collect(), + asm: asm.asm, + asm_str_style: asm.asm_str_style, + clobbers: asm.clobbers.clone(), + volatile: asm.volatile, + alignstack: asm.alignstack, + dialect: asm.dialect, + }; + let hir_asm = hir::LlvmInlineAsm { + inner, + inputs_exprs: self.arena.alloc_from_iter( + asm.inputs.iter().map(|&(_, ref input)| self.lower_expr_mut(input)), + ), + outputs_exprs: self + .arena + .alloc_from_iter(asm.outputs.iter().map(|out| self.lower_expr_mut(&out.expr))), + }; + hir::ExprKind::LlvmInlineAsm(self.arena.alloc(hir_asm)) + } + fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { hir::ExprField { hir_id: self.next_id(), diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a7c600fff785e..8762b5708dc72 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1022,6 +1022,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => { this.ban_let_expr(expr, elem); }, + ExprKind::LlvmInlineAsm(..) if !this.session.target.allow_asm => { + struct_span_err!( + this.session, + expr.span, + E0472, + "llvm_asm! is unsupported on this target" + ) + .emit(); + } ExprKind::Match(scrutinee, arms) => { this.visit_expr(scrutinee); for arm in arms { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 6435f1b6141e3..430a2b31cb40f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -495,6 +495,62 @@ impl<'a> State<'a> { self.word("asm!"); self.print_inline_asm(a); } + ast::ExprKind::LlvmInlineAsm(ref a) => { + self.word("llvm_asm!"); + self.popen(); + self.print_symbol(a.asm, a.asm_str_style); + self.word_space(":"); + + self.commasep(Inconsistent, &a.outputs, |s, out| { + let constraint = out.constraint.as_str(); + let mut ch = constraint.chars(); + match ch.next() { + Some('=') if out.is_rw => { + s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked) + } + _ => s.print_string(&constraint, ast::StrStyle::Cooked), + } + s.popen(); + s.print_expr(&out.expr); + s.pclose(); + }); + self.space(); + self.word_space(":"); + + self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| { + s.print_symbol(co, ast::StrStyle::Cooked); + s.popen(); + s.print_expr(o); + s.pclose(); + }); + self.space(); + self.word_space(":"); + + self.commasep(Inconsistent, &a.clobbers, |s, &co| { + s.print_symbol(co, ast::StrStyle::Cooked); + }); + + let mut options = vec![]; + if a.volatile { + options.push("volatile"); + } + if a.alignstack { + options.push("alignstack"); + } + if a.dialect == ast::LlvmAsmDialect::Intel { + options.push("intel"); + } + + if !options.is_empty() { + self.space(); + self.word_space(":"); + self.commasep(Inconsistent, &options, |s, &co| { + s.print_string(co, ast::StrStyle::Cooked); + }); + } + + self.pclose(); + } ast::ExprKind::MacCall(ref m) => self.print_mac(m), ast::ExprKind::Paren(ref e) => { self.popen(); diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 684eba82667fa..da1c2ada845bf 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -8,6 +8,7 @@ use rustc_mir_dataflow::ResultsVisitable; use rustc_mir_dataflow::{self, fmt::DebugWithContext, CallReturnPlaces, GenKill}; use rustc_mir_dataflow::{Analysis, Direction, Results}; use std::fmt; +use std::iter; use crate::{ places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid, @@ -384,6 +385,14 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { self.kill_borrows_on_place(trans, Place::from(local)); } + mir::StatementKind::LlvmInlineAsm(ref asm) => { + for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) { + if !kind.is_indirect && !kind.is_rw { + self.kill_borrows_on_place(trans, *output); + } + } + } + mir::StatementKind::FakeRead(..) | mir::StatementKind::SetDiscriminant { .. } | mir::StatementKind::StorageLive(..) diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index eec994f88b96e..70acbc9ee2dbc 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -16,6 +16,9 @@ pub fn categorize(context: PlaceContext) -> Option { PlaceContext::MutatingUse(MutatingUseContext::Store) | + // This is potentially both a def and a use... + PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput) | + // We let Call define the result in both the success and // unwind cases. This is not really correct, however it // does not seem to be observable due to the way that we diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 1a789009f0611..a191fe36366e6 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -5,11 +5,12 @@ use rustc_middle::mir::{BorrowKind, Mutability, Operand}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{Statement, StatementKind}; use rustc_middle::ty::TyCtxt; +use std::iter; use crate::{ borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, path_utils::*, AccessDepth, - Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read, ReadKind, - ReadOrWrite, Reservation, Shallow, Write, WriteKind, + Activation, ArtificialField, BorrowIndex, Deep, JustWrite, LocalMutationIsAllowed, MutateMode, + Read, ReadKind, ReadOrWrite, Reservation, Shallow, Write, WriteAndRead, WriteKind, }; pub(super) fn generate_invalidates<'tcx>( @@ -58,13 +59,37 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { StatementKind::Assign(box (lhs, rhs)) => { self.consume_rvalue(location, rhs); - self.mutate_place(location, *lhs, Shallow(None)); + self.mutate_place(location, *lhs, Shallow(None), JustWrite); } StatementKind::FakeRead(box (_, _)) => { // Only relevant for initialized/liveness/safety checks. } StatementKind::SetDiscriminant { place, variant_index: _ } => { - self.mutate_place(location, **place, Shallow(None)); + self.mutate_place(location, **place, Shallow(None), JustWrite); + } + StatementKind::LlvmInlineAsm(asm) => { + for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) { + if o.is_indirect { + // FIXME(eddyb) indirect inline asm outputs should + // be encoded through MIR place derefs instead. + self.access_place( + location, + *output, + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + ); + } else { + self.mutate_place( + location, + *output, + if o.is_rw { Deep } else { Shallow(None) }, + if o.is_rw { WriteAndRead } else { JustWrite }, + ); + } + } + for (_, input) in asm.inputs.iter() { + self.consume_operand(location, input); + } } StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { ref src, @@ -117,7 +142,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { target: _, unwind: _, } => { - self.mutate_place(location, *drop_place, Deep); + self.mutate_place(location, *drop_place, Deep, JustWrite); self.consume_operand(location, new_value); } TerminatorKind::Call { @@ -133,7 +158,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.consume_operand(location, arg); } if let Some((dest, _ /*bb*/)) = destination { - self.mutate_place(location, *dest, Deep); + self.mutate_place(location, *dest, Deep, JustWrite); } } TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { @@ -156,7 +181,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } - self.mutate_place(location, *resume_arg, Deep); + self.mutate_place(location, *resume_arg, Deep, JustWrite); } TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Invalidate all borrows of local places @@ -183,13 +208,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { if let Some(place) = place { - self.mutate_place(location, place, Shallow(None)); + self.mutate_place(location, place, Shallow(None), JustWrite); } } InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { self.consume_operand(location, in_value); if let Some(out_place) = out_place { - self.mutate_place(location, out_place, Shallow(None)); + self.mutate_place(location, out_place, Shallow(None), JustWrite); } } InlineAsmOperand::Const { value: _ } @@ -213,7 +238,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { /// Simulates mutation of a place. - fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) { + fn mutate_place( + &mut self, + location: Location, + place: Place<'tcx>, + kind: AccessDepth, + _mode: MutateMode, + ) { self.access_place( location, place, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 0007d8026e66b..219defbd210e1 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -43,6 +43,7 @@ use either::Either; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; +use std::iter; use std::mem; use std::rc::Rc; @@ -57,6 +58,7 @@ use rustc_mir_dataflow::MoveDataParamEnv; use self::diagnostics::{AccessKind, RegionName}; use self::location::LocationTable; use self::prefixes::PrefixSet; +use self::MutateMode::{JustWrite, WriteAndRead}; use facts::AllFacts; use self::path_utils::*; @@ -605,7 +607,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx StatementKind::Assign(box (lhs, ref rhs)) => { self.consume_rvalue(location, (rhs, span), flow_state); - self.mutate_place(location, (*lhs, span), Shallow(None), flow_state); + self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state); } StatementKind::FakeRead(box (_, ref place)) => { // Read for match doesn't access any memory and is used to @@ -626,8 +628,41 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx ); } StatementKind::SetDiscriminant { place, variant_index: _ } => { - self.mutate_place(location, (**place, span), Shallow(None), flow_state); + self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state); } + StatementKind::LlvmInlineAsm(ref asm) => { + for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) { + if o.is_indirect { + // FIXME(eddyb) indirect inline asm outputs should + // be encoded through MIR place derefs instead. + self.access_place( + location, + (*output, o.span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Use, + (output.as_ref(), o.span), + flow_state, + ); + } else { + self.mutate_place( + location, + (*output, o.span), + if o.is_rw { Deep } else { Shallow(None) }, + if o.is_rw { WriteAndRead } else { JustWrite }, + flow_state, + ); + } + } + for (_, input) in asm.inputs.iter() { + self.consume_operand(location, (input, span), flow_state); + } + } + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { .. }) => { @@ -692,7 +727,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx target: _, unwind: _, } => { - self.mutate_place(loc, (drop_place, span), Deep, flow_state); + self.mutate_place(loc, (drop_place, span), Deep, JustWrite, flow_state); self.consume_operand(loc, (new_value, span), flow_state); } TerminatorKind::Call { @@ -708,7 +743,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx self.consume_operand(loc, (arg, span), flow_state); } if let Some((dest, _ /*bb*/)) = *destination { - self.mutate_place(loc, (dest, span), Deep, flow_state); + self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state); } } TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { @@ -722,7 +757,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => { self.consume_operand(loc, (value, span), flow_state); - self.mutate_place(loc, (resume_arg, span), Deep, flow_state); + self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); } TerminatorKind::InlineAsm { @@ -740,7 +775,13 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { if let Some(place) = place { - self.mutate_place(loc, (place, span), Shallow(None), flow_state); + self.mutate_place( + loc, + (place, span), + Shallow(None), + JustWrite, + flow_state, + ); } } InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { @@ -750,6 +791,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx loc, (out_place, span), Shallow(None), + JustWrite, flow_state, ); } @@ -821,6 +863,12 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx } } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum MutateMode { + JustWrite, + WriteAndRead, +} + use self::AccessDepth::{Deep, Shallow}; use self::ReadOrWrite::{Activation, Read, Reservation, Write}; @@ -906,6 +954,7 @@ enum LocalMutationIsAllowed { #[derive(Copy, Clone, Debug)] enum InitializationRequiringAction { + Update, Borrow, MatchOn, Use, @@ -922,6 +971,7 @@ struct RootPlace<'tcx> { impl InitializationRequiringAction { fn as_noun(self) -> &'static str { match self { + InitializationRequiringAction::Update => "update", InitializationRequiringAction::Borrow => "borrow", InitializationRequiringAction::MatchOn => "use", // no good noun InitializationRequiringAction::Use => "use", @@ -932,6 +982,7 @@ impl InitializationRequiringAction { fn as_verb_in_past_tense(self) -> &'static str { match self { + InitializationRequiringAction::Update => "updated", InitializationRequiringAction::Borrow => "borrowed", InitializationRequiringAction::MatchOn => "matched on", InitializationRequiringAction::Use => "used", @@ -1170,10 +1221,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location: Location, place_span: (Place<'tcx>, Span), kind: AccessDepth, + mode: MutateMode, flow_state: &Flows<'cx, 'tcx>, ) { - // Write of P[i] or *P requires P init'd. - self.check_if_assigned_path_is_moved(location, place_span, flow_state); + // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. + match mode { + MutateMode::WriteAndRead => { + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Update, + (place_span.0.as_ref(), place_span.1), + flow_state, + ); + } + MutateMode::JustWrite => { + self.check_if_assigned_path_is_moved(location, place_span, flow_state); + } + } // Special case: you can assign an immutable local variable // (e.g., `x = ...`) so long as it has never been initialized diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e9fa33f656f31..313b1b70f588d 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1355,6 +1355,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) + | StatementKind::LlvmInlineAsm { .. } | StatementKind::Retag { .. } | StatementKind::Coverage(..) | StatementKind::Nop => {} diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 57ef46475ddd8..6bb3b953faa4f 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -50,6 +50,15 @@ pub fn parse_asm_args<'a>( return Err(diag.struct_span_err(sp, "requires at least a template string argument")); } + // Detect use of the legacy llvm_asm! syntax (which used to be called asm!) + if !is_global_asm && p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) { + let mut err = + diag.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("alternatively, switch to llvm_asm! to keep your code working as it is"); + return Err(err); + } + let first_template = p.parse_expr()?; let mut args = AsmArgs { templates: vec![first_template], diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index a05122394a06a..1c06666d9a700 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -38,6 +38,7 @@ mod env; mod format; mod format_foreign; mod global_allocator; +mod llvm_asm; mod log_syntax; mod source_util; mod test; @@ -81,6 +82,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { include_str: source_util::expand_include_str, include: source_util::expand_include, line: source_util::expand_line, + llvm_asm: llvm_asm::expand_llvm_asm, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, diff --git a/compiler/rustc_builtin_macros/src/llvm_asm.rs b/compiler/rustc_builtin_macros/src/llvm_asm.rs new file mode 100644 index 0000000000000..5d4f103f71810 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/llvm_asm.rs @@ -0,0 +1,303 @@ +// Llvm-style inline assembly support. +// +use State::*; + +use rustc_ast as ast; +use rustc_ast::ptr::P; +use rustc_ast::token::{self, Token}; +use rustc_ast::tokenstream::{self, TokenStream}; +use rustc_ast::LlvmAsmDialect; +use rustc_errors::{struct_span_err, PResult}; +use rustc_expand::base::*; +use rustc_parse::parser::Parser; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::Span; + +enum State { + Asm, + Outputs, + Inputs, + Clobbers, + Options, + StateNone, +} + +impl State { + fn next(&self) -> State { + match *self { + Asm => Outputs, + Outputs => Inputs, + Inputs => Clobbers, + Clobbers => Options, + Options => StateNone, + StateNone => StateNone, + } + } +} + +const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel]; + +pub fn expand_llvm_asm<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + let mut inline_asm = match parse_inline_asm(cx, sp, tts) { + Ok(Some(inline_asm)) => inline_asm, + Ok(None) => return DummyResult::any(sp), + Err(mut err) => { + err.emit(); + return DummyResult::any(sp); + } + }; + + // If there are no outputs, the inline assembly is executed just for its side effects, + // so ensure that it is volatile + if inline_asm.outputs.is_empty() { + inline_asm.volatile = true; + } + + MacEager::expr(P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::LlvmInlineAsm(P(inline_asm)), + span: cx.with_def_site_ctxt(sp), + attrs: ast::AttrVec::new(), + tokens: None, + })) +} + +fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> { + match p.parse_str_lit() { + Ok(str_lit) => Ok(str_lit.symbol_unescaped), + Err(opt_lit) => { + let span = opt_lit.map_or(p.token.span, |lit| lit.span); + let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); + err.span_label(span, "not a string literal"); + Err(err) + } + } +} + +fn parse_inline_asm<'a>( + cx: &mut ExtCtxt<'a>, + sp: Span, + tts: TokenStream, +) -> PResult<'a, Option> { + // Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being + // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription. + let first_colon = tts + .trees() + .position(|tt| { + matches!( + tt, + tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) + ) + }) + .unwrap_or(tts.len()); + let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect()); + let mut asm = kw::Empty; + let mut asm_str_style = None; + let mut outputs = Vec::new(); + let mut inputs = Vec::new(); + let mut clobs = Vec::new(); + let mut volatile = false; + let mut alignstack = false; + let mut dialect = LlvmAsmDialect::Att; + + let mut state = Asm; + + 'statement: loop { + match state { + Asm => { + if asm_str_style.is_some() { + // If we already have a string with instructions, + // ending up in Asm state again is an error. + return Err(struct_span_err!( + cx.sess.parse_sess.span_diagnostic, + sp, + E0660, + "malformed inline assembly" + )); + } + // Nested parser, stop before the first colon (see above). + let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect()); + + if p2.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 = p2.parse_expr()?; + let (s, style) = + match expr_to_string(cx, expr, "inline assembly must be a string literal") { + Some((s, st)) => (s, st), + None => return Ok(None), + }; + + // This is most likely malformed. + if p2.token != token::Eof { + let mut extra_tts = p2.parse_all_token_trees()?; + extra_tts.extend(tts.trees().skip(first_colon)); + p = cx.new_parser_from_tts(extra_tts.into_iter().collect()); + } + + asm = s; + asm_str_style = Some(style); + } + Outputs => { + while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { + if !outputs.is_empty() { + p.eat(&token::Comma); + } + + let constraint = parse_asm_str(&mut p)?; + + let span = p.prev_token.span; + + p.expect(&token::OpenDelim(token::Paren))?; + let expr = p.parse_expr()?; + p.expect(&token::CloseDelim(token::Paren))?; + + // Expands a read+write operand into two operands. + // + // Use '+' modifier when you want the same expression + // to be both an input and an output at the same time. + // It's the opposite of '=&' which means that the memory + // cannot be shared with any other operand (usually when + // a register is clobbered early.) + let constraint_str = constraint.as_str(); + let mut ch = constraint_str.chars(); + let output = match ch.next() { + Some('=') => None, + Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))), + _ => { + struct_span_err!( + cx.sess.parse_sess.span_diagnostic, + span, + E0661, + "output operand constraint lacks '=' or '+'" + ) + .emit(); + None + } + }; + + let is_rw = output.is_some(); + let is_indirect = constraint_str.contains('*'); + outputs.push(ast::LlvmInlineAsmOutput { + constraint: output.unwrap_or(constraint), + expr, + is_rw, + is_indirect, + }); + } + } + Inputs => { + while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { + if !inputs.is_empty() { + p.eat(&token::Comma); + } + + let constraint = parse_asm_str(&mut p)?; + + if constraint.as_str().starts_with('=') { + struct_span_err!( + cx.sess.parse_sess.span_diagnostic, + p.prev_token.span, + E0662, + "input operand constraint contains '='" + ) + .emit(); + } else if constraint.as_str().starts_with('+') { + struct_span_err!( + cx.sess.parse_sess.span_diagnostic, + p.prev_token.span, + E0663, + "input operand constraint contains '+'" + ) + .emit(); + } + + p.expect(&token::OpenDelim(token::Paren))?; + let input = p.parse_expr()?; + p.expect(&token::CloseDelim(token::Paren))?; + + inputs.push((constraint, input)); + } + } + Clobbers => { + while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { + if !clobs.is_empty() { + p.eat(&token::Comma); + } + + let s = parse_asm_str(&mut p)?; + + if OPTIONS.iter().any(|&opt| s == opt) { + cx.span_warn(p.prev_token.span, "expected a clobber, found an option"); + } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') { + struct_span_err!( + cx.sess.parse_sess.span_diagnostic, + p.prev_token.span, + E0664, + "clobber should not be surrounded by braces" + ) + .emit(); + } + + clobs.push(s); + } + } + Options => { + let option = parse_asm_str(&mut p)?; + + if option == sym::volatile { + // Indicates that the inline assembly has side effects + // and must not be optimized out along with its outputs. + volatile = true; + } else if option == sym::alignstack { + alignstack = true; + } else if option == sym::intel { + dialect = LlvmAsmDialect::Intel; + } else { + cx.span_warn(p.prev_token.span, "unrecognized option"); + } + + if p.token == token::Comma { + p.eat(&token::Comma); + } + } + StateNone => (), + } + + loop { + // MOD_SEP is a double colon '::' without space in between. + // When encountered, the state must be advanced twice. + match (&p.token.kind, state.next(), state.next().next()) { + (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => { + p.bump(); + break 'statement; + } + (&token::Colon, st, _) | (&token::ModSep, _, st) => { + p.bump(); + state = st; + } + (&token::Eof, ..) => break 'statement, + _ => break, + } + } + } + + Ok(Some(ast::LlvmInlineAsm { + asm, + asm_str_style: asm_str_style.unwrap(), + outputs, + inputs, + clobbers: clobs, + volatile, + alignstack, + dialect, + })) +} diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index a9ff710c91ed6..b54b94fd72bbd 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -777,6 +777,18 @@ fn codegen_stmt<'tcx>( | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) => {} + StatementKind::LlvmInlineAsm(asm) => { + match asm.asm.asm.as_str().trim() { + "" => { + // Black box + } + _ => fx.tcx.sess.span_fatal( + stmt.source_info.span, + "Legacy `llvm_asm!` inline assembly is not supported. \ + Try using the new `asm!` instead.", + ), + } + } StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"), StatementKind::CopyNonOverlapping(inner) => { let dst = codegen_operand(fx, &inner.dst); diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 4657791345b8e..fe4e89547f7d6 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -512,7 +512,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( { return None; } - StatementKind::CopyNonOverlapping(_) => { + StatementKind::LlvmInlineAsm(_) | StatementKind::CopyNonOverlapping(_) => { return None; } // conservative handling StatementKind::Assign(_) diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 8a74c4c07e0cf..ba0f284534c1a 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -4,6 +4,7 @@ use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; +use rustc_hir::LlvmInlineAsmInner; use rustc_middle::{bug, ty::Instance}; use rustc_span::Span; use rustc_target::asm::*; @@ -105,6 +106,17 @@ enum ConstraintOrRegister { impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec>>, _inputs: Vec>, span: Span) -> bool { + self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`") + .help("consider using the `asm!` macro instead") + .emit(); + + // We return `true` even if we've failed to generate the asm + // because we want to suppress the "malformed inline assembly" error + // generated by the frontend. + true + } + fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], _instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) { if options.contains(InlineAsmOptions::MAY_UNWIND) { self.sess() diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 96c7d884b7b20..04118bb1b471f 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -8,10 +8,13 @@ use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; +use rustc_ast::LlvmAsmDialect; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; +use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug, ty::Instance}; use rustc_span::{Pos, Span}; @@ -23,6 +26,100 @@ use smallvec::SmallVec; use tracing::debug; impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { + fn codegen_llvm_inline_asm( + &mut self, + ia: &hir::LlvmInlineAsmInner, + outputs: Vec>, + mut inputs: Vec<&'ll Value>, + span: Span, + ) -> bool { + let mut ext_constraints = vec![]; + let mut output_types = vec![]; + + // Prepare the output operands + let mut indirect_outputs = vec![]; + for (i, (out, &place)) in ia.outputs.iter().zip(&outputs).enumerate() { + if out.is_rw { + let operand = self.load_operand(place); + if let OperandValue::Immediate(_) = operand.val { + inputs.push(operand.immediate()); + } + ext_constraints.push(i.to_string()); + } + if out.is_indirect { + let operand = self.load_operand(place); + if let OperandValue::Immediate(_) = operand.val { + indirect_outputs.push(operand.immediate()); + } + } else { + output_types.push(place.layout.llvm_type(self.cx)); + } + } + if !indirect_outputs.is_empty() { + indirect_outputs.extend_from_slice(&inputs); + inputs = indirect_outputs; + } + + let clobbers = ia.clobbers.iter().map(|s| format!("~{{{}}}", &s)); + + // Default per-arch clobbers + // Basically what clang does + let arch_clobbers = match &self.sess().target.arch[..] { + "x86" | "x86_64" => &["~{dirflag}", "~{fpsr}", "~{flags}"][..], + "mips" | "mips64" => &["~{$1}"], + _ => &[], + }; + + let all_constraints = ia + .outputs + .iter() + .map(|out| out.constraint.to_string()) + .chain(ia.inputs.iter().map(|s| s.to_string())) + .chain(ext_constraints) + .chain(clobbers) + .chain(arch_clobbers.iter().map(|s| (*s).to_string())) + .collect::>() + .join(","); + + debug!("Asm Constraints: {}", &all_constraints); + + // Depending on how many outputs we have, the return type is different + let num_outputs = output_types.len(); + let output_type = match num_outputs { + 0 => self.type_void(), + 1 => output_types[0], + _ => self.type_struct(&output_types, false), + }; + + let asm = ia.asm.as_str(); + let r = inline_asm_call( + self, + &asm, + &all_constraints, + &inputs, + output_type, + ia.volatile, + ia.alignstack, + ia.dialect, + &[span], + false, + None, + ); + if r.is_none() { + return false; + } + let r = r.unwrap(); + + // Again, based on how many outputs we have + let outputs = ia.outputs.iter().zip(&outputs).filter(|&(o, _)| !o.is_indirect); + for (i, (_, &place)) in outputs.enumerate() { + let v = if num_outputs == 1 { r } else { self.extract_value(r, i as u64) }; + OperandValue::Immediate(v).store(self, place); + } + + true + } + fn codegen_inline_asm( &mut self, template: &[InlineAsmTemplatePiece], @@ -256,9 +353,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => { - llvm::AsmDialect::Intel + LlvmAsmDialect::Intel } - _ => llvm::AsmDialect::Att, + _ => LlvmAsmDialect::Att, }; let result = inline_asm_call( self, @@ -363,7 +460,7 @@ pub(crate) fn inline_asm_call<'ll>( output: &'ll llvm::Type, volatile: bool, alignstack: bool, - dia: llvm::AsmDialect, + dia: LlvmAsmDialect, line_spans: &[Span], unwind: bool, dest_catch_funclet: Option<( @@ -406,7 +503,7 @@ pub(crate) fn inline_asm_call<'ll>( cons.len(), volatile, alignstack, - dia, + llvm::AsmDialect::from_generic(dia), can_throw, ); @@ -430,7 +527,7 @@ pub(crate) fn inline_asm_call<'ll>( // we just encode the start position of each line. // FIXME: Figure out a way to pass the entire line spans. let mut srcloc = vec![]; - if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { + if dia == LlvmAsmDialect::Intel && line_spans.len() > 1 { // LLVM inserts an extra line to add the ".intel_syntax", so add // a dummy srcloc entry for it. // diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index fe149b4c8c2ff..1ec1c1241ac0a 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -7,6 +7,7 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; +use rustc_ast as ast; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh}; use rustc_codegen_ssa::common::span_invalid_monomorphization_error; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -352,7 +353,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.type_void(), true, false, - llvm::AsmDialect::Att, + ast::LlvmAsmDialect::Att, &[span], false, None, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 375b9927c8672..1cca5e0b549f6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -447,13 +447,22 @@ pub enum MetadataType { } /// LLVMRustAsmDialect -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone)] #[repr(C)] pub enum AsmDialect { Att, Intel, } +impl AsmDialect { + pub fn from_generic(asm: rustc_ast::LlvmAsmDialect) -> Self { + match asm { + rustc_ast::LlvmAsmDialect::Att => AsmDialect::Att, + rustc_ast::LlvmAsmDialect::Intel => AsmDialect::Intel, + } + } +} + /// LLVMRustCodeGenOptLevel #[derive(Copy, Clone, PartialEq)] #[repr(C)] diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index d768d4920c5b4..b1b3f1d6d81b9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -211,6 +211,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> PlaceContext::MutatingUse( MutatingUseContext::Store + | MutatingUseContext::LlvmAsmOutput | MutatingUseContext::AsmOutput | MutatingUseContext::Borrow | MutatingUseContext::AddressOf diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 773dc2adcfa0c..2c96987d3399e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,7 +1,9 @@ +use rustc_errors::struct_span_err; use rustc_middle::mir; use super::FunctionCx; use super::LocalRef; +use super::OperandValue; use crate::traits::BuilderMethods; use crate::traits::*; @@ -64,6 +66,51 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } bx } + mir::StatementKind::LlvmInlineAsm(ref asm) => { + let outputs = asm + .outputs + .iter() + .map(|output| self.codegen_place(&mut bx, output.as_ref())) + .collect(); + + let input_vals = asm.inputs.iter().fold( + Vec::with_capacity(asm.inputs.len()), + |mut acc, (span, input)| { + let op = self.codegen_operand(&mut bx, input); + if let OperandValue::Immediate(_) = op.val { + acc.push(op.immediate()); + } else { + struct_span_err!( + bx.sess(), + span.to_owned(), + E0669, + "invalid value for constraint in inline assembly" + ) + .emit(); + } + acc + }, + ); + + if input_vals.len() == asm.inputs.len() { + let res = bx.codegen_llvm_inline_asm( + &asm.asm, + outputs, + input_vals, + statement.source_info.span, + ); + if !res { + struct_span_err!( + bx.sess(), + statement.source_info.span, + E0668, + "malformed inline assembly" + ) + .emit(); + } + } + bx + } mir::StatementKind::Coverage(box ref coverage) => { self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope); bx diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 11111a7974410..65f3c754d2dcc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -3,6 +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::LlvmInlineAsmInner; use rustc_middle::ty::Instance; use rustc_span::Span; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -41,6 +42,15 @@ pub enum GlobalAsmOperandRef { } pub trait AsmBuilderMethods<'tcx>: BackendTypes { + /// Take an inline assembly expression and splat it out via LLVM + fn codegen_llvm_inline_asm( + &mut self, + ia: &LlvmInlineAsmInner, + outputs: Vec>, + inputs: Vec, + span: Span, + ) -> bool; + /// Take an inline assembly expression and splat it out via LLVM fn codegen_inline_asm( &mut self, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 4272bfd5d6c6f..57b0f8271872c 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -137,6 +137,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} + + LlvmInlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"), } self.stack_mut()[frame_idx].loc.as_mut().unwrap().statement_index += 1; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index e203c79030d20..92d15449ff9ce 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -690,6 +690,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_statement(statement, location); match statement.kind { + StatementKind::LlvmInlineAsm { .. } => { + self.check_op(ops::InlineAsm); + } + StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } | StatementKind::FakeRead(..) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 263959f3cb376..2bfa023196a0f 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -358,6 +358,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } StatementKind::StorageLive(..) | StatementKind::StorageDead(..) + | StatementKind::LlvmInlineAsm(..) | StatementKind::Coverage(_) | StatementKind::Nop => {} } diff --git a/compiler/rustc_error_codes/src/error_codes/E0660.md b/compiler/rustc_error_codes/src/error_codes/E0660.md index abf90275915e0..26d35f2620cb2 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0660.md +++ b/compiler/rustc_error_codes/src/error_codes/E0660.md @@ -1,9 +1,12 @@ -#### Note: this error code is no longer emitted by the compiler. - The argument to the `llvm_asm` macro is not well-formed. Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0660 llvm_asm!("nop" "nop"); ``` + +Considering that this would be a long explanation, we instead recommend you +take a look at the [`llvm_asm`] chapter of the Unstable book: + +[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0661.md b/compiler/rustc_error_codes/src/error_codes/E0661.md index 245f755cddd0d..0b8ba7fbbedac 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0661.md +++ b/compiler/rustc_error_codes/src/error_codes/E0661.md @@ -1,10 +1,13 @@ -#### Note: this error code is no longer emitted by the compiler. - An invalid syntax was passed to the second argument of an `llvm_asm` macro line. Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0661 let a; llvm_asm!("nop" : "r"(a)); ``` + +Considering that this would be a long explanation, we instead recommend you +take a look at the [`llvm_asm`] chapter of the Unstable book: + +[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0662.md b/compiler/rustc_error_codes/src/error_codes/E0662.md index ffb716f9957a0..8c1bab8d0410d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0662.md +++ b/compiler/rustc_error_codes/src/error_codes/E0662.md @@ -1,13 +1,16 @@ -#### Note: this error code is no longer emitted by the compiler. - An invalid input operand constraint was passed to the `llvm_asm` macro (third line). Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0662 llvm_asm!("xor %eax, %eax" : : "=test"("a") ); ``` + +Considering that this would be a long explanation, we instead recommend you +take a look at the [`llvm_asm`] chapter of the Unstable book: + +[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0663.md b/compiler/rustc_error_codes/src/error_codes/E0663.md index 351cfaca29c19..53ffd3373a51c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0663.md +++ b/compiler/rustc_error_codes/src/error_codes/E0663.md @@ -1,13 +1,16 @@ -#### Note: this error code is no longer emitted by the compiler. - An invalid input operand constraint was passed to the `llvm_asm` macro (third line). Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0663 llvm_asm!("xor %eax, %eax" : : "+test"("a") ); ``` + +Considering that this would be a long explanation, we instead recommend you +take a look at the [`llvm_asm`] chapter of the Unstable book: + +[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0664.md b/compiler/rustc_error_codes/src/error_codes/E0664.md index 34135d5db3338..f8e72cd330a31 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0664.md +++ b/compiler/rustc_error_codes/src/error_codes/E0664.md @@ -1,13 +1,16 @@ -#### Note: this error code is no longer emitted by the compiler. - A clobber was surrounded by braces in the `llvm_asm` macro. Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0664 llvm_asm!("mov $$0x200, %eax" : : : "{eax}" ); ``` + +Considering that this would be a long explanation, we instead recommend you +take a look at the [`llvm_asm`] chapter of the Unstable book: + +[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0668.md b/compiler/rustc_error_codes/src/error_codes/E0668.md index 393aabe2894c7..b6fedfe53fce8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0668.md +++ b/compiler/rustc_error_codes/src/error_codes/E0668.md @@ -1,10 +1,8 @@ -#### Note: this error code is no longer emitted by the compiler. - Malformed inline assembly rejected by LLVM. Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0668 #![feature(llvm_asm)] fn main() { diff --git a/compiler/rustc_error_codes/src/error_codes/E0669.md b/compiler/rustc_error_codes/src/error_codes/E0669.md index 2be8f04eda0b6..f078c441b3421 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0669.md +++ b/compiler/rustc_error_codes/src/error_codes/E0669.md @@ -1,10 +1,8 @@ -#### Note: this error code is no longer emitted by the compiler. - Cannot convert inline assembly operand to a single LLVM value. Erroneous code example: -```ignore (no longer emitted) +```compile_fail,E0669 #![feature(llvm_asm)] fn main() { diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 27ec461906419..edad00ed6a2fe 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -30,6 +30,7 @@ macro_rules! arena_types { [] impl_item_ref: rustc_hir::ImplItemRef, [] item: rustc_hir::Item<'tcx>, [] inline_asm: rustc_hir::InlineAsm<'tcx>, + [] llvm_inline_asm: rustc_hir::LlvmInlineAsm<'tcx>, [] local: rustc_hir::Local<'tcx>, [] mod_: rustc_hir::Mod<'tcx>, [] owner_info: rustc_hir::OwnerInfo<'tcx>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 10871df3ab9d0..f176a971494bc 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -5,8 +5,8 @@ use crate::intravisit::FnKind; use crate::LangItem; use rustc_ast::util::parser::ExprPrecedence; -use rustc_ast::{self as ast, CrateSugar}; -use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy}; +use rustc_ast::{self as ast, CrateSugar, LlvmAsmDialect}; +use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, TraitObjectSyntax, UintTy}; pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto}; pub use rustc_ast::{CaptureBy, Movability, Mutability}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -1508,6 +1508,7 @@ impl Expr<'_> { ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, + ExprKind::LlvmInlineAsm(..) => ExprPrecedence::InlineAsm, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, ExprKind::Yield(..) => ExprPrecedence::Yield, @@ -1567,6 +1568,7 @@ impl Expr<'_> { | ExprKind::Loop(..) | ExprKind::Assign(..) | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::Lit(_) | ExprKind::ConstBlock(..) @@ -1649,6 +1651,7 @@ impl Expr<'_> { | ExprKind::Loop(..) | ExprKind::Assign(..) | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::ConstBlock(..) | ExprKind::Box(..) @@ -1789,6 +1792,8 @@ pub enum ExprKind<'hir> { /// Inline assembly (from `asm!`), with its outputs and inputs. InlineAsm(&'hir InlineAsm<'hir>), + /// Inline assembly (from `llvm_asm!`), with its outputs and inputs. + LlvmInlineAsm(&'hir LlvmInlineAsm<'hir>), /// A struct or struct-like variant literal expression. /// @@ -2423,6 +2428,36 @@ pub struct InlineAsm<'hir> { pub line_spans: &'hir [Span], } +#[derive(Copy, Clone, Encodable, Decodable, Debug, Hash, HashStable_Generic, PartialEq)] +pub struct LlvmInlineAsmOutput { + pub constraint: Symbol, + pub is_rw: bool, + pub is_indirect: bool, + pub span: Span, +} + +// NOTE(eddyb) This is used within MIR as well, so unlike the rest of the HIR, +// it needs to be `Clone` and `Decodable` and use plain `Vec` instead of +// arena-allocated slice. +#[derive(Clone, Encodable, Decodable, Debug, Hash, HashStable_Generic, PartialEq)] +pub struct LlvmInlineAsmInner { + pub asm: Symbol, + pub asm_str_style: StrStyle, + pub outputs: Vec, + pub inputs: Vec, + pub clobbers: Vec, + pub volatile: bool, + pub alignstack: bool, + pub dialect: LlvmAsmDialect, +} + +#[derive(Debug, HashStable_Generic)] +pub struct LlvmInlineAsm<'hir> { + pub inner: LlvmInlineAsmInner, + pub outputs_exprs: &'hir [Expr<'hir>], + pub inputs_exprs: &'hir [Expr<'hir>], +} + /// Represents a parameter in a function header. #[derive(Debug, HashStable_Generic)] pub struct Param<'hir> { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 1b40f3d390ee5..894cfffa96d22 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1223,6 +1223,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::InlineAsm(ref asm) => { walk_inline_asm(visitor, asm); } + ExprKind::LlvmInlineAsm(ref asm) => { + walk_list!(visitor, visit_expr, asm.outputs_exprs); + walk_list!(visitor, visit_expr, asm.inputs_exprs); + } ExprKind::Yield(ref subexpression, _) => { visitor.visit_expr(subexpression); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index b3042c61002c4..e7e79caa32f43 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1580,6 +1580,67 @@ impl<'a> State<'a> { self.word("asm!"); self.print_inline_asm(asm); } + hir::ExprKind::LlvmInlineAsm(ref a) => { + let i = &a.inner; + self.word("llvm_asm!"); + self.popen(); + self.print_symbol(i.asm, i.asm_str_style); + self.word_space(":"); + + let mut out_idx = 0; + self.commasep(Inconsistent, &i.outputs, |s, out| { + let constraint = out.constraint.as_str(); + let mut ch = constraint.chars(); + match ch.next() { + Some('=') if out.is_rw => { + s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked) + } + _ => s.print_string(&constraint, ast::StrStyle::Cooked), + } + s.popen(); + s.print_expr(&a.outputs_exprs[out_idx]); + s.pclose(); + out_idx += 1; + }); + self.space(); + self.word_space(":"); + + let mut in_idx = 0; + self.commasep(Inconsistent, &i.inputs, |s, &co| { + s.print_symbol(co, ast::StrStyle::Cooked); + s.popen(); + s.print_expr(&a.inputs_exprs[in_idx]); + s.pclose(); + in_idx += 1; + }); + self.space(); + self.word_space(":"); + + self.commasep(Inconsistent, &i.clobbers, |s, &co| { + s.print_symbol(co, ast::StrStyle::Cooked); + }); + + let mut options = vec![]; + if i.volatile { + options.push("volatile"); + } + if i.alignstack { + options.push("alignstack"); + } + if i.dialect == ast::LlvmAsmDialect::Intel { + options.push("intel"); + } + + if !options.is_empty() { + self.space(); + self.word_space(":"); + self.commasep(Inconsistent, &options, |s, &co| { + s.print_string(co, ast::StrStyle::Cooked); + }); + } + + self.pclose(); + } hir::ExprKind::Yield(ref expr, _) => { self.word_space("yield"); self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 0a4f84558fee4..af4e6f234e111 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1596,6 +1596,10 @@ pub enum StatementKind<'tcx> { /// End the current live range for the storage of the local. StorageDead(Local), + /// Executes a piece of inline Assembly. Stored in a Box to keep the size + /// of `StatementKind` low. + LlvmInlineAsm(Box>), + /// Retag references in the given place, ensuring they got fresh tags. This is /// part of the Stacked Borrows model. These statements are currently only interpreted /// by miri and only generated when "-Z mir-emit-retag" is passed. @@ -1715,6 +1719,13 @@ pub enum FakeReadCause { ForIndex, } +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +pub struct LlvmInlineAsm<'tcx> { + pub asm: hir::LlvmInlineAsmInner, + pub outputs: Box<[Place<'tcx>]>, + pub inputs: Box<[(Span, Operand<'tcx>)]>, +} + impl Debug for Statement<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::StatementKind::*; @@ -1739,6 +1750,9 @@ impl Debug for Statement<'_> { SetDiscriminant { ref place, variant_index } => { write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) } + LlvmInlineAsm(ref asm) => { + write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) + } AscribeUserType(box (ref place, ref c_ty), ref variance) => { write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 965d30a7b92c9..4e1e098352679 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -245,6 +245,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { SetDiscriminant { .. } => "SetDiscriminant", StorageLive(..) => "StorageLive", StorageDead(..) => "StorageDead", + LlvmInlineAsm(..) => "LlvmInlineAsm", Retag(..) => "Retag", AscribeUserType(..) => "AscribeUserType", Coverage(..) => "Coverage", diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index aeb0f956eb4f7..1ac5cadb81af0 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -413,6 +413,19 @@ macro_rules! make_mir_visitor { location ); } + StatementKind::LlvmInlineAsm(asm) => { + for output in & $($mutability)? asm.outputs[..] { + self.visit_place( + output, + PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput), + location + ); + } + for (span, input) in & $($mutability)? asm.inputs[..] { + self.visit_span(span); + self.visit_operand(input, location); + } + } StatementKind::Retag(kind, place) => { self.visit_retag(kind, place, location); } @@ -1174,6 +1187,10 @@ pub enum NonMutatingUseContext { pub enum MutatingUseContext { /// Appears as LHS of an assignment. Store, + /// Can often be treated as a `Store`, but needs to be separate because + /// ASM is allowed to read outputs as well, so a `Store`-`LlvmAsmOutput` sequence + /// cannot be simplified the way a `Store`-`Store` can be. + LlvmAsmOutput, /// Output operand of an inline assembly block. AsmOutput, /// Destination of a call. @@ -1263,6 +1280,7 @@ impl PlaceContext { PlaceContext::MutatingUse( MutatingUseContext::Store | MutatingUseContext::Call + | MutatingUseContext::LlvmAsmOutput | MutatingUseContext::AsmOutput, ) ) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index b31cc17a9594d..37eeaa724c54b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -446,6 +446,12 @@ pub enum ExprKind<'tcx> { }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), + /// Inline LLVM assembly, i.e. `llvm_asm!()`. + LlvmInlineAsm { + asm: &'tcx hir::LlvmInlineAsmInner, + outputs: Box<[ExprId]>, + inputs: Box<[ExprId]>, + }, /// A `yield` expression. Yield { value: ExprId, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 451fa46638771..2dde26b09597d 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -144,6 +144,14 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp } } ThreadLocalRef(_) => {} + LlvmInlineAsm { ref outputs, ref inputs, asm: _ } => { + for &out_expr in &**outputs { + visitor.visit_expr(&visitor.thir()[out_expr]); + } + for &in_expr in &**inputs { + visitor.visit_expr(&visitor.thir()[in_expr]); + } + } Yield { value } => visitor.visit_expr(&visitor.thir()[value]), } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 5c7910db362d2..8942ad63f8853 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -226,6 +226,7 @@ TrivialTypeFoldableAndLiftImpls! { ::rustc_hir::def_id::DefId, ::rustc_hir::def_id::LocalDefId, ::rustc_hir::HirId, + ::rustc_hir::LlvmInlineAsmInner, ::rustc_hir::MatchSource, ::rustc_hir::Mutability, ::rustc_hir::Unsafety, diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 3b3120cf04b7f..0675ad4c47451 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -572,6 +572,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ConstBlock { .. } | ExprKind::StaticRef { .. } | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::Yield { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } => { diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 3f8a1a3f79504..660d73f0a2892 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -353,6 +353,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Continue { .. } | ExprKind::Return { .. } | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } => { // these do not have corresponding `Rvalue` variants, diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index bcece39c6206e..c7fc1657651a7 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -67,7 +67,8 @@ impl Category { | ExprKind::Repeat { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } - | ExprKind::ThreadLocalRef(_) => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::ThreadLocalRef(_) + | ExprKind::LlvmInlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index a8f623dbe4693..8f822dee818e5 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -467,7 +467,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // These cases don't actually need a destination - ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { + ExprKind::Assign { .. } + | ExprKind::AssignOp { .. } + | ExprKind::LlvmInlineAsm { .. } => { unpack!(block = this.stmt_expr(block, expr, None)); this.cfg.push_assign_unit(block, source_info, destination, this.tcx); block.unit() diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 46c616ff36241..e381e040f79ab 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -101,6 +101,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BreakableTarget::Return, source_info, ), + ExprKind::LlvmInlineAsm { asm, ref outputs, ref inputs } => { + debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr); + this.block_context.push(BlockFrame::SubExpr); + let outputs = outputs + .into_iter() + .copied() + .map(|output| unpack!(block = this.as_place(block, &this.thir[output]))) + .collect::>() + .into_boxed_slice(); + let inputs = inputs + .into_iter() + .copied() + .map(|input| { + let input = &this.thir[input]; + (input.span, unpack!(block = this.as_local_operand(block, &input))) + }) + .collect::>() + .into_boxed_slice(); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::LlvmInlineAsm(Box::new(LlvmInlineAsm { + asm: asm.clone(), + outputs, + inputs, + })), + }, + ); + this.block_context.pop(); + block.unit() + } _ => { assert!( statement_scope.is_some(), diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index eadce3dc9c467..50061a73dc591 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -332,6 +332,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::Box { .. } | ExprKind::If { .. } | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::LogicalOp { .. } | ExprKind::Use { .. } => { // We don't need to save the old value and restore it @@ -379,7 +380,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { self.requires_unsafe(expr.span, DerefOfRawPointer); } } - ExprKind::InlineAsm { .. } => { + ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => { self.requires_unsafe(expr.span, UseOfInlineAssembly); } ExprKind::Adt(box Adt { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index e6afc89baa03b..52fbddfc5d9b1 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -553,6 +553,12 @@ impl<'tcx> Cx<'tcx> { line_spans: asm.line_spans, }, + hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { + asm: &asm.inner, + outputs: self.mirror_exprs(asm.outputs_exprs), + inputs: self.mirror_exprs(asm.inputs_exprs), + }, + hir::ExprKind::ConstBlock(ref anon_const) => { let tcx = self.tcx; let local_def_id = tcx.hir().local_def_id(anon_const.hir_id); diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 602ccec76a68c..97f493a94e185 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -176,6 +176,7 @@ impl DefUse { // All other contexts are uses... PlaceContext::MutatingUse( MutatingUseContext::AddressOf + | MutatingUseContext::LlvmAsmOutput | MutatingUseContext::Borrow | MutatingUseContext::Drop | MutatingUseContext::Retag, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 60cde6546dcfc..896377f2bc307 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -134,6 +134,11 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc | StatementKind::SetDiscriminant { box place, .. } => { trans.gen(place.local); } + StatementKind::LlvmInlineAsm(asm) => { + for place in &*asm.outputs { + trans.gen(place.local); + } + } // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 26bbc34e780bb..2e00b4f9a5e7c 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -4,6 +4,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use smallvec::{smallvec, SmallVec}; +use std::iter; use std::mem; use super::abs_domain::Lift; @@ -292,6 +293,16 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { StatementKind::FakeRead(box (_, place)) => { self.create_move_path(*place); } + StatementKind::LlvmInlineAsm(ref asm) => { + for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) { + if !kind.is_indirect { + self.gather_init(output.as_ref(), InitKind::Deep); + } + } + for (_, input) in asm.inputs.iter() { + self.gather_operand(input); + } + } StatementKind::StorageLive(_) => {} StatementKind::StorageDead(local) => { self.gather_move(Place::from(*local)); diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index f5d82315c4e38..aa6e617c6e58d 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -106,6 +106,10 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { // safe (at least as emitted during MIR construction) } + StatementKind::LlvmInlineAsm { .. } => self.require_unsafe( + UnsafetyViolationKind::General, + UnsafetyViolationDetails::UseOfInlineAssembly, + ), StatementKind::CopyNonOverlapping(..) => unreachable!(), } self.super_statement(statement, location); @@ -209,6 +213,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { MutatingUseContext::Store | MutatingUseContext::Drop | MutatingUseContext::AsmOutput + | MutatingUseContext::LlvmAsmOutput ) ); // If this is just an assignment, determine if the assigned type needs dropping. diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index e1dbf90b5b9e7..19b4cc894ef60 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -928,7 +928,8 @@ impl Visitor<'_> for CanConstProp { // These could be propagated with a smarter analysis or just some careful thinking about // whether they'd be fine right now. - MutatingUse(MutatingUseContext::Yield) + MutatingUse(MutatingUseContext::LlvmAsmOutput) + | MutatingUse(MutatingUseContext::Yield) | MutatingUse(MutatingUseContext::Drop) | MutatingUse(MutatingUseContext::Retag) // These can't ever be propagated under any scheme, as we can't reason about indirect diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 5be745bc1de38..b3254b8007720 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -810,7 +810,8 @@ impl Visitor<'_> for CanConstProp { // These could be propagated with a smarter analysis or just some careful thinking about // whether they'd be fine right now. - MutatingUse(MutatingUseContext::Yield) + MutatingUse(MutatingUseContext::LlvmAsmOutput) + | MutatingUse(MutatingUseContext::Yield) | MutatingUse(MutatingUseContext::Drop) | MutatingUse(MutatingUseContext::Retag) // These can't ever be propagated under any scheme, as we can't reason about indirect diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 5e366d7fb7dde..2c7f38b2a4b15 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -827,6 +827,7 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option | StatementKind::CopyNonOverlapping(..) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } + | StatementKind::LlvmInlineAsm(_) | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) => { Some(statement.source_info.span) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 5d0b58e9c5360..e797556918d02 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -529,6 +529,25 @@ impl<'a> Conflicts<'a> { // eliminate the resulting self-assignments automatically. StatementKind::Assign(_) => {} + StatementKind::LlvmInlineAsm(asm) => { + // Inputs and outputs must not overlap. + for (_, input) in &*asm.inputs { + if let Some(in_place) = input.place() { + if !in_place.is_indirect() { + for out_place in &*asm.outputs { + if !out_place.is_indirect() && !in_place.is_indirect() { + self.record_local_conflict( + in_place.local, + out_place.local, + "aliasing llvm_asm! operands", + ); + } + } + } + } + } + } + StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 04b5c4e091958..19b5ed0bf7338 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1439,6 +1439,9 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { self.check_assigned_place(*lhs, |this| this.visit_rvalue(rhs, location)); } + // FIXME: Does `llvm_asm!` have any aliasing requirements? + StatementKind::LlvmInlineAsm(_) => {} + StatementKind::FakeRead(..) | StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(_) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 03b9ecc959695..8bb4fb7c1e8a5 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -50,6 +50,7 @@ impl RemoveNoopLandingPads { StatementKind::Assign { .. } | StatementKind::SetDiscriminant { .. } + | StatementKind::LlvmInlineAsm { .. } | StatementKind::CopyNonOverlapping(..) | StatementKind::Retag { .. } => { return false; diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index d265720e18296..612fce71f9167 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -239,6 +239,10 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData< } } + // If inline assembly is found, we probably should + // not try to analyze the code + StatementKind::LlvmInlineAsm(_) => return false, + // These statements have no influence on the place // we are interested in StatementKind::FakeRead(_) @@ -316,6 +320,10 @@ fn find_determining_place<'tcx>( | StatementKind::CopyNonOverlapping(_) | StatementKind::Nop => {} + // If inline assembly is found, we probably should + // not try to analyze the code + StatementKind::LlvmInlineAsm(_) => return None, + // If the discriminant is set, it is always set // as a constant, so the job is already done. // As we are **ignoring projections**, if the place diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index d8b58ce53f838..80ac7d4730ec0 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -481,7 +481,8 @@ impl UsedLocals { impl<'tcx> Visitor<'tcx> for UsedLocals { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { - StatementKind::CopyNonOverlapping(..) + StatementKind::LlvmInlineAsm(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::Retag(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs index ce4b45062e8de..042d5f507475e 100644 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ b/compiler/rustc_mir_transform/src/simplify_try.rs @@ -631,6 +631,10 @@ impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> { .filter(|(_, bb)| { // Reaching `unreachable` is UB so assume it doesn't happen. bb.terminator().kind != TerminatorKind::Unreachable + // But `asm!(...)` could abort the program, + // so we cannot assume that the `unreachable` terminator itself is reachable. + // FIXME(Centril): use a normalization pass instead of a check. + || bb.statements.iter().any(|stmt| matches!(stmt.kind, StatementKind::LlvmInlineAsm(..))) }) .peekable(); diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index f916ca36217b4..9e755ab141a48 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -23,14 +23,23 @@ impl MirPass<'_> for UnreachablePropagation { for (bb, bb_data) in traversal::postorder(body) { let terminator = bb_data.terminator(); - if terminator.kind == TerminatorKind::Unreachable { + // HACK: If the block contains any asm statement it is not regarded as unreachable. + // This is a temporary solution that handles possibly diverging asm statements. + // Accompanying testcases: mir-opt/unreachable_asm.rs and mir-opt/unreachable_asm_2.rs + let asm_stmt_in_block = || { + bb_data.statements.iter().any(|stmt: &Statement<'_>| { + matches!(stmt.kind, StatementKind::LlvmInlineAsm(..)) + }) + }; + + if terminator.kind == TerminatorKind::Unreachable && !asm_stmt_in_block() { unreachable_blocks.insert(bb); } else { let is_unreachable = |succ: BasicBlock| unreachable_blocks.contains(&succ); let terminator_kind_opt = remove_successors(&terminator.kind, is_unreachable); if let Some(terminator_kind) = terminator_kind_opt { - if terminator_kind == TerminatorKind::Unreachable { + if terminator_kind == TerminatorKind::Unreachable && !asm_stmt_in_block() { unreachable_blocks.insert(bb); } replacements.insert(bb, terminator_kind); diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 7298aba7e8763..79b36f5e6d7c3 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -103,6 +103,7 @@ use rustc_span::Span; use std::collections::VecDeque; use std::io; use std::io::prelude::*; +use std::iter; use std::rc::Rc; mod rwu_table; @@ -468,6 +469,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err @@ -1088,6 +1090,26 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { succ } + hir::ExprKind::LlvmInlineAsm(ref asm) => { + let ia = &asm.inner; + let outputs = asm.outputs_exprs; + let inputs = asm.inputs_exprs; + let succ = iter::zip(&ia.outputs, outputs).rev().fold(succ, |succ, (o, output)| { + // see comment on places + // in propagate_through_place_components() + if o.is_indirect { + self.propagate_through_expr(output, succ) + } else { + let acc = if o.is_rw { ACC_WRITE | ACC_READ } else { ACC_WRITE }; + let succ = self.write_place(output, succ, acc); + self.propagate_through_place_components(output, succ) + } + }); + + // Inputs are executed first. Propagate last because of rev order + self.propagate_through_exprs(inputs, succ) + } + hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Err @@ -1358,6 +1380,20 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } } + hir::ExprKind::LlvmInlineAsm(ref asm) => { + for input in asm.inputs_exprs { + this.visit_expr(input); + } + + // Output operands must be places + for (o, output) in iter::zip(&asm.inner.outputs, asm.outputs_exprs) { + if !o.is_indirect { + this.check_place(output); + } + this.visit_expr(output); + } + } + hir::ExprKind::Let(let_expr) => { this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {}); } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 02f6b4060599e..9d21e7be0f6df 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -222,6 +222,22 @@ impl<'tcx> CheckInlineAssembly<'tcx> { self.check_inline_asm(asm, span); } + ExprKind::LlvmInlineAsm(..) => { + self.items.push((ItemKind::Asm, span)); + self.tcx.struct_span_lint_hir( + UNDEFINED_NAKED_FUNCTION_ABI, + expr.hir_id, + span, + |lint| { + lint.build( + "the LLVM-style inline assembly is unsupported in naked functions", + ) + .help("use the new asm! syntax specified in RFC 2873") + .emit(); + }, + ); + } + ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => { hir::intravisit::walk_expr(self, expr); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2737c26708bc4..028ec131e78a5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -828,6 +828,7 @@ symbols! { linker, lint_reasons, literal, + llvm_asm, load, loaded_from_disk, local, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 2b9cc75f1b753..b9c7a6b09ad99 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -596,7 +596,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { // let expressions imply control flow ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => self.error(node.span, "control flow is not supported in generic constants")?, - ExprKind::InlineAsm { .. } => { + ExprKind::LlvmInlineAsm { .. } | ExprKind::InlineAsm { .. } => { self.error(node.span, "assembly is not supported in generic constants")? } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 82641a489f66a..fbb3c39f21f6d 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -284,6 +284,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]), ExprKind::InlineAsm(asm) => self.check_expr_asm(asm), + ExprKind::LlvmInlineAsm(asm) => { + for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) { + self.check_expr(expr); + } + tcx.mk_unit() + } ExprKind::Break(destination, ref expr_opt) => { self.check_expr_break(destination, expr_opt.as_deref(), expr) } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index 75a8ad8a159b5..870e36211a6f1 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -201,6 +201,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | ExprKind::Struct(..) | ExprKind::Repeat(..) | ExprKind::Yield(..) + | ExprKind::LlvmInlineAsm(_) | ExprKind::Err => (), } } @@ -460,6 +461,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { | ExprKind::Struct(..) | ExprKind::Tup(..) | ExprKind::Type(..) + | ExprKind::LlvmInlineAsm(_) | ExprKind::Unary(..) | ExprKind::Yield(..) => intravisit::walk_expr(self, expr), } diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 055e391d7069e..39a66936f5712 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -17,6 +17,7 @@ use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; +use std::iter; use crate::mem_categorization as mc; @@ -376,6 +377,17 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } + hir::ExprKind::LlvmInlineAsm(ia) => { + for (o, output) in iter::zip(&ia.inner.outputs, ia.outputs_exprs) { + if o.is_indirect { + self.consume_expr(output); + } else { + self.mutate_expr(output); + } + } + self.consume_exprs(ia.inputs_exprs); + } + hir::ExprKind::Continue(..) | hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 85e9a670ffbf1..3d2b4d8b40b3c 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -378,6 +378,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 7dfcc36ce9391..83eb03e98c0b3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -183,6 +183,7 @@ #![feature(intrinsics)] #![feature(lang_items)] #![feature(link_llvm_intrinsics)] +#![feature(llvm_asm)] #![feature(min_specialization)] #![feature(mixed_integer_ops)] #![feature(must_not_suspend)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 74c94680e47e5..ee8911cf0e68f 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1417,6 +1417,32 @@ pub(crate) mod builtin { ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; } + /// LLVM-style inline assembly. + /// + /// Read the [unstable book] for the usage. + /// + /// [unstable book]: ../unstable-book/library-features/llvm-asm.html + #[unstable( + feature = "llvm_asm", + issue = "70173", + reason = "prefer using the new asm! syntax instead" + )] + #[rustc_deprecated( + since = "1.56", + reason = "will be removed from the compiler, use asm! instead" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! llvm_asm { + ("assembly template" + : $("output"(operand),)* + : $("input"(operand),)* + : $("clobbers",)* + : $("options",)*) => { + /* compiler built-in */ + }; + } + /// Prints passed tokens into the standard output. #[unstable( feature = "log_syntax", diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index b566e211cd89d..89ac28cc55409 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -56,8 +56,8 @@ pub use crate::hash::macros::Hash; #[doc(no_inline)] pub use crate::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, + format_args_nl, include, include_bytes, include_str, line, llvm_asm, log_syntax, module_path, + option_env, stringify, trace_macros, }; #[unstable( diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 819ec10a4b4b6..b3c8ca25b4f83 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -244,6 +244,8 @@ #![feature(intra_doc_pointers)] #![feature(lang_items)] #![feature(linkage)] +#![feature(llvm_asm)] +#![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -589,8 +591,8 @@ pub use core::{ #[allow(deprecated)] pub use core::{ assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, - env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, - module_path, option_env, stringify, trace_macros, + env, file, format_args, format_args_nl, include, include_bytes, include_str, line, llvm_asm, + log_syntax, module_path, option_env, stringify, trace_macros, }; #[unstable( diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 0226c4d7a2581..82851837f4955 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -40,8 +40,9 @@ pub use crate::result::Result::{self, Err, Ok}; #[doc(no_inline)] pub use core::prelude::v1::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, + format_args_nl, include, include_bytes, include_str, line, llvm_asm, log_syntax, module_path, + option_env, stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, + PartialOrd, }; #[unstable( diff --git a/src/doc/unstable-book/src/library-features/llvm-asm.md b/src/doc/unstable-book/src/library-features/llvm-asm.md new file mode 100644 index 0000000000000..094124998b6e7 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/llvm-asm.md @@ -0,0 +1,190 @@ +# `llvm_asm` + +The tracking issue for this feature is: [#70173] + +[#70173]: https://github.com/rust-lang/rust/issues/70173 + +------------------------ + +For extremely low-level manipulations and performance reasons, one +might wish to control the CPU directly. Rust supports using inline +assembly to do this via the `llvm_asm!` macro. + +```rust,ignore (pseudo-code) +llvm_asm!(assembly template + : output operands + : input operands + : clobbers + : options + ); +``` + +Any use of `llvm_asm` is feature gated (requires `#![feature(llvm_asm)]` on the +crate to allow) and of course requires an `unsafe` block. + +> **Note**: the examples here are given in x86/x86-64 assembly, but +> all platforms are supported. + +## Assembly template + +The `assembly template` is the only required parameter and must be a +literal string (i.e. `""`) + +```rust +#![feature(llvm_asm)] + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn foo() { + unsafe { + llvm_asm!("NOP"); + } +} + +// Other platforms: +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn foo() { /* ... */ } + +fn main() { + // ... + foo(); + // ... +} +``` + +(The `feature(llvm_asm)` and `#[cfg]`s are omitted from now on.) + +Output operands, input operands, clobbers and options are all optional +but you must add the right number of `:` if you skip them: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { unsafe { +llvm_asm!("xor %eax, %eax" + : + : + : "eax" + ); +# } } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +Whitespace also doesn't matter: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { unsafe { +llvm_asm!("xor %eax, %eax" ::: "eax"); +# } } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +## Operands + +Input and output operands follow the same format: `: +"constraints1"(expr1), "constraints2"(expr2), ..."`. Output operand +expressions must be mutable place, or not yet assigned: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn add(a: i32, b: i32) -> i32 { + let c: i32; + unsafe { + llvm_asm!("add $2, $0" + : "=r"(c) + : "0"(a), "r"(b) + ); + } + c +} +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn add(a: i32, b: i32) -> i32 { a + b } + +fn main() { + assert_eq!(add(3, 14159), 14162) +} +``` + +If you would like to use real operands in this position, however, +you are required to put curly braces `{}` around the register that +you want, and you are required to put the specific size of the +operand. This is useful for very low level programming, where +which register you use is important: + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# unsafe fn read_byte_in(port: u16) -> u8 { +let result: u8; +llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port)); +result +# } +``` + +## Clobbers + +Some instructions modify registers which might otherwise have held +different values so we use the clobbers list to indicate to the +compiler not to assume any values loaded into those registers will +stay valid. + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { unsafe { +// Put the value 0x200 in eax: +llvm_asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "eax"); +# } } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +Input and output registers need not be listed since that information +is already communicated by the given constraints. Otherwise, any other +registers used either implicitly or explicitly should be listed. + +If the assembly changes the condition code register `cc` should be +specified as one of the clobbers. Similarly, if the assembly modifies +memory, `memory` should also be specified. + +## Options + +The last section, `options` is specific to Rust. The format is comma +separated literal strings (i.e. `:"foo", "bar", "baz"`). It's used to +specify some extra info about the inline assembly: + +Current valid options are: + +1. `volatile` - specifying this is analogous to + `__asm__ __volatile__ (...)` in gcc/clang. +2. `alignstack` - certain instructions expect the stack to be + aligned a certain way (i.e. SSE) and specifying this indicates to + the compiler to insert its usual stack alignment code +3. `intel` - use intel syntax instead of the default AT&T. + +```rust +# #![feature(llvm_asm)] +# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +# fn main() { +let result: i32; +unsafe { + llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel") +} +println!("eax is currently {}", result); +# } +# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +# fn main() {} +``` + +## More Information + +The current implementation of the `llvm_asm!` macro is a direct binding to [LLVM's +inline assembler expressions][llvm-docs], so be sure to check out [their +documentation as well][llvm-docs] for more information about clobbers, +constraints, etc. + +[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions diff --git a/src/llvm-project b/src/llvm-project index 9168e236c548d..7b86890ec8f73 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 9168e236c548d1d0e9938ee6dd4cdbd308fdfd72 +Subproject commit 7b86890ec8f7336ddcbcb7e80c7767fd747b286e diff --git a/src/test/codegen/no-output-asm-is-volatile.rs b/src/test/codegen/no-output-asm-is-volatile.rs new file mode 100644 index 0000000000000..40376218908d1 --- /dev/null +++ b/src/test/codegen/no-output-asm-is-volatile.rs @@ -0,0 +1,14 @@ +// compile-flags: -O + +#![feature(llvm_asm)] +#![crate_type = "lib"] + +// Check that inline assembly expressions without any outputs +// are marked as having side effects / being volatile + +// CHECK-LABEL: @assembly +#[no_mangle] +pub fn assembly() { + unsafe { llvm_asm!("") } +// CHECK: tail call void asm sideeffect "", {{.*}} +} diff --git a/src/test/incremental/hashes/inline_asm.rs b/src/test/incremental/hashes/inline_asm.rs index bb836f203f5c0..1ddb345e5664c 100644 --- a/src/test/incremental/hashes/inline_asm.rs +++ b/src/test/incremental/hashes/inline_asm.rs @@ -18,19 +18,23 @@ #![allow(warnings)] #![feature(rustc_attrs)] +#![feature(llvm_asm)] #![crate_type="rlib"] -use std::arch::asm; + // Change template #[cfg(any(cfail1,cfail4))] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn change_template(_a: i32) -> i32 { +pub fn change_template(a: i32) -> i32 { let c: i32; unsafe { - asm!("mov {0}, 1", - out(reg) c - ); + llvm_asm!("add 1, $0" + : "=r"(c) + : "0"(a) + : + : + ); } c } @@ -41,12 +45,15 @@ pub fn change_template(_a: i32) -> i32 { #[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")] #[rustc_clean(cfg="cfail6")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn change_template(_a: i32) -> i32 { +pub fn change_template(a: i32) -> i32 { let c: i32; unsafe { - asm!("mov {0}, 2", - out(reg) c - ); + llvm_asm!("add 2, $0" + : "=r"(c) + : "0"(a) + : + : + ); } c } @@ -60,10 +67,12 @@ pub fn change_output(a: i32) -> i32 { let mut _out1: i32 = 0; let mut _out2: i32 = 0; unsafe { - asm!("mov {0}, {1}", - out(reg) _out1, - in(reg) a - ); + llvm_asm!("add 1, $0" + : "=r"(_out1) + : "0"(a) + : + : + ); } _out1 } @@ -78,10 +87,12 @@ pub fn change_output(a: i32) -> i32 { let mut _out1: i32 = 0; let mut _out2: i32 = 0; unsafe { - asm!("mov {0}, {1}", - out(reg) _out2, - in(reg) a - ); + llvm_asm!("add 1, $0" + : "=r"(_out2) + : "0"(a) + : + : + ); } _out1 } @@ -94,10 +105,12 @@ pub fn change_output(a: i32) -> i32 { pub fn change_input(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); } _out } @@ -111,10 +124,12 @@ pub fn change_input(_a: i32, _b: i32) -> i32 { pub fn change_input(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _b - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_b) + : + : + ); } _out } @@ -127,10 +142,12 @@ pub fn change_input(_a: i32, _b: i32) -> i32 { pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a, - in("eax") _b); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a), "r"(_b) + : + : + ); } _out } @@ -144,26 +161,30 @@ pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { pub fn change_input_constraint(_a: i32, _b: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a, - in("ecx") _b); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "r"(_a), "0"(_b) + : + : + ); } _out } + // Change clobber #[cfg(any(cfail1,cfail4))] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn change_clobber(_a: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a, - lateout("ecx") _ - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + :/*--*/ + : + ); } _out } @@ -177,11 +198,12 @@ pub fn change_clobber(_a: i32) -> i32 { pub fn change_clobber(_a: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a, - lateout("edx") _ - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : "eax" + : + ); } _out } @@ -194,11 +216,12 @@ pub fn change_clobber(_a: i32) -> i32 { pub fn change_options(_a: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a, - options(readonly), - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + :/*-------*/ + ); } _out } @@ -212,11 +235,12 @@ pub fn change_options(_a: i32) -> i32 { pub fn change_options(_a: i32) -> i32 { let _out; unsafe { - asm!("mov {0}, {1}", - out(reg) _out, - in(reg) _a, - options(nomem ), - ); + llvm_asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : "volatile" + ); } _out } diff --git a/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..c60997be5d3c3 --- /dev/null +++ b/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff @@ -0,0 +1,75 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm.rs:10:11: 10:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + let _3: Empty; // in scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + let mut _4: i32; // in scope 0 at $DIR/unreachable_asm.rs:12:13: 12:19 + let _5: (); // in scope 0 at $DIR/unreachable_asm.rs:14:9: 18:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_asm.rs:14:12: 14:16 + let _7: (); // in scope 0 at $DIR/unreachable_asm.rs:21:9: 21:37 + let mut _8: !; // in scope 0 at $DIR/unreachable_asm.rs:22:9: 22:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable_asm.rs:11:17: 11:19 + } + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable_asm.rs:12:13: 12:19 + scope 3 { + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + _1 = empty() -> bb1; // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + // mir::Constant + // + span: $DIR/unreachable_asm.rs:11:23: 11:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + switchInt(move _2) -> [1_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + } + + bb2: { + StorageLive(_3); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + StorageLive(_4); // scope 0 at $DIR/unreachable_asm.rs:12:13: 12:19 + StorageLive(_5); // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + _6 = const true; // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + switchInt(move _6) -> [false: bb4, otherwise: bb3]; // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + } + + bb3: { + _4 = const 21_i32; // scope 2 at $DIR/unreachable_asm.rs:15:13: 15:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:14:17: 16:10 + goto -> bb5; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb4: { + _4 = const 42_i32; // scope 2 at $DIR/unreachable_asm.rs:17:13: 17:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:16:16: 18:10 + goto -> bb5; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb5: { + StorageDead(_6); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 + StorageDead(_5); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 + StorageLive(_7); // scope 2 at $DIR/unreachable_asm.rs:21:9: 21:37 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm.rs:21:18: 21:34 + _7 = const (); // scope 3 at $DIR/unreachable_asm.rs:21:9: 21:37 + StorageDead(_7); // scope 2 at $DIR/unreachable_asm.rs:21:36: 21:37 + StorageLive(_8); // scope 2 at $DIR/unreachable_asm.rs:22:9: 22:21 + unreachable; // scope 2 at $DIR/unreachable_asm.rs:22:15: 22:17 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/unreachable_asm.rs:23:6: 23:6 + StorageDead(_1); // scope 0 at $DIR/unreachable_asm.rs:24:1: 24:2 + return; // scope 0 at $DIR/unreachable_asm.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/unreachable_asm.rs b/src/test/mir-opt/unreachable_asm.rs new file mode 100644 index 0000000000000..cbef05a3c05cc --- /dev/null +++ b/src/test/mir-opt/unreachable_asm.rs @@ -0,0 +1,24 @@ +#![feature(llvm_asm)] + +enum Empty {} + +fn empty() -> Option { + None +} + +// EMIT_MIR unreachable_asm.main.UnreachablePropagation.diff +fn main() { + if let Some(_x) = empty() { + let mut _y; + + if true { + _y = 21; + } else { + _y = 42; + } + + // asm instruction stops unreachable propagation to if else blocks bb4 and bb5. + unsafe { llvm_asm!("NOP"); } + match _x { } + } +} diff --git a/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..28c5f031dbbe9 --- /dev/null +++ b/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff @@ -0,0 +1,85 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm_2.rs:10:11: 10:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + let _3: Empty; // in scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + let mut _4: i32; // in scope 0 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + let _5: (); // in scope 0 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + let _7: (); // in scope 0 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + let _8: (); // in scope 0 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + let mut _9: !; // in scope 0 at $DIR/unreachable_asm_2.rs:24:9: 24:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + } + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + scope 3 { + } + scope 4 { + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + _1 = empty() -> bb1; // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:11:23: 11:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 +- switchInt(move _2) -> [1_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 ++ switchInt(move _2) -> [1_isize: bb2, otherwise: bb5]; // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + } + + bb2: { + StorageLive(_3); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + StorageLive(_4); // scope 0 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + StorageLive(_5); // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + _6 = const true; // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + switchInt(move _6) -> [false: bb4, otherwise: bb3]; // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + } + + bb3: { + StorageLive(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm_2.rs:16:22: 16:38 + _7 = const (); // scope 3 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + StorageDead(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:40: 16:41 + _4 = const 21_i32; // scope 2 at $DIR/unreachable_asm_2.rs:17:13: 17:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:14:17: 18:10 +- goto -> bb5; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 ++ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + + bb4: { + StorageLive(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 4 at $DIR/unreachable_asm_2.rs:20:22: 20:38 + _8 = const (); // scope 4 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + StorageDead(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:40: 20:41 + _4 = const 42_i32; // scope 2 at $DIR/unreachable_asm_2.rs:21:13: 21:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:18:16: 22:10 +- goto -> bb5; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 ++ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + + bb5: { +- StorageDead(_6); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 +- StorageDead(_5); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 +- StorageLive(_9); // scope 2 at $DIR/unreachable_asm_2.rs:24:9: 24:21 +- unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:24:15: 24:17 +- } +- +- bb6: { + _0 = const (); // scope 0 at $DIR/unreachable_asm_2.rs:25:6: 25:6 + StorageDead(_1); // scope 0 at $DIR/unreachable_asm_2.rs:26:1: 26:2 + return; // scope 0 at $DIR/unreachable_asm_2.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/unreachable_asm_2.rs b/src/test/mir-opt/unreachable_asm_2.rs new file mode 100644 index 0000000000000..e0d8e725147a3 --- /dev/null +++ b/src/test/mir-opt/unreachable_asm_2.rs @@ -0,0 +1,26 @@ +#![feature(llvm_asm)] + +enum Empty {} + +fn empty() -> Option { + None +} + +// EMIT_MIR unreachable_asm_2.main.UnreachablePropagation.diff +fn main() { + if let Some(_x) = empty() { + let mut _y; + + if true { + // asm instruction stops unreachable propagation to block bb3. + unsafe { llvm_asm!("NOP"); } + _y = 21; + } else { + // asm instruction stops unreachable propagation to block bb3. + unsafe { llvm_asm!("NOP"); } + _y = 42; + } + + match _x { } + } +} diff --git a/src/test/pretty/llvm-asm-clobbers.rs b/src/test/pretty/llvm-asm-clobbers.rs new file mode 100644 index 0000000000000..2c09646e47e4a --- /dev/null +++ b/src/test/pretty/llvm-asm-clobbers.rs @@ -0,0 +1,3 @@ +#![feature(llvm_asm)] + +pub fn main() { unsafe { llvm_asm!("" : : : "hello", "world") }; } diff --git a/src/test/pretty/llvm-asm-options.rs b/src/test/pretty/llvm-asm-options.rs new file mode 100644 index 0000000000000..86a881bfbd18a --- /dev/null +++ b/src/test/pretty/llvm-asm-options.rs @@ -0,0 +1,11 @@ +#![feature(llvm_asm)] + +// pp-exact + +pub fn main() { + unsafe { + llvm_asm!("" : : : : "volatile"); + llvm_asm!("" : : : : "alignstack"); + llvm_asm!("" : : : : "intel"); + } +} diff --git a/src/test/pretty/raw-str-nonexpr.rs b/src/test/pretty/raw-str-nonexpr.rs index 7af80979b4349..41227898f24a8 100644 --- a/src/test/pretty/raw-str-nonexpr.rs +++ b/src/test/pretty/raw-str-nonexpr.rs @@ -1,8 +1,8 @@ // pp-exact +#![feature(llvm_asm)] + #[cfg(foo = r#"just parse this"#)] extern crate blah as blah; -use std::arch::asm; - -fn main() { unsafe { asm!(r###"blah"###); } } +fn main() { unsafe { llvm_asm!(r###"blah"###); } } diff --git a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs index e7b9694d9f2ad..2e81667cf39c6 100644 --- a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs +++ b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-ret.rs @@ -1,11 +1,12 @@ +#![feature(llvm_asm)] #![crate_type="lib"] -use std::arch::asm; #[deny(unreachable_code)] pub fn exit(n: usize) -> i32 { unsafe { // Pretend this asm is an exit() syscall. - asm!("/*{0}*/", in(reg) n); + llvm_asm!("" :: "r"(n) :: "volatile"); + // Can't actually reach this point, but rustc doesn't know that. } // This return value is just here to generate some extra code for a return // value, making it easier for the test script to detect whether the diff --git a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs index ec85db733df04..fb3848b0db617 100644 --- a/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs +++ b/src/test/run-make-fulldeps/intrinsic-unreachable/exit-unreachable.rs @@ -1,6 +1,5 @@ -#![feature(core_intrinsics)] +#![feature(llvm_asm, core_intrinsics)] #![crate_type="lib"] -use std::arch::asm; use std::intrinsics; @@ -8,7 +7,7 @@ use std::intrinsics; pub fn exit(n: usize) -> i32 { unsafe { // Pretend this asm is an exit() syscall. - asm!("/*{0}*/", in(reg) n); + llvm_asm!("" :: "r"(n) :: "volatile"); intrinsics::unreachable() } // This return value is just here to generate some extra code for a return diff --git a/src/test/ui/abi/abi-sysv64-register-usage.rs b/src/test/ui/abi/abi-sysv64-register-usage.rs index 9404e71d0fe24..3450bb69eb428 100644 --- a/src/test/ui/abi/abi-sysv64-register-usage.rs +++ b/src/test/ui/abi/abi-sysv64-register-usage.rs @@ -7,19 +7,32 @@ // ignore-aarch64 // needs-asm-support #![feature(asm_sym)] +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! #[cfg(target_arch = "x86_64")] -pub extern "sysv64" fn all_the_registers(rdi: i64, rsi: i64, rdx: i64, - rcx: i64, r8 : i64, r9 : i64, - xmm0: f32, xmm1: f32, xmm2: f32, - xmm3: f32, xmm4: f32, xmm5: f32, - xmm6: f32, xmm7: f32) -> i64 { +pub extern "sysv64" fn all_the_registers( + rdi: i64, + rsi: i64, + rdx: i64, + rcx: i64, + r8: i64, + r9: i64, + xmm0: f32, + xmm1: f32, + xmm2: f32, + xmm3: f32, + xmm4: f32, + xmm5: f32, + xmm6: f32, + xmm7: f32, +) -> i64 { assert_eq!(rdi, 1); assert_eq!(rsi, 2); assert_eq!(rdx, 3); assert_eq!(rcx, 4); - assert_eq!(r8, 5); - assert_eq!(r9, 6); + assert_eq!(r8, 5); + assert_eq!(r9, 6); assert_eq!(xmm0, 1.0f32); assert_eq!(xmm1, 2.0f32); assert_eq!(xmm2, 4.0f32); @@ -53,37 +66,37 @@ pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct #[cfg(target_arch = "x86_64")] pub fn main() { - use std::arch::asm; - let result: i64; unsafe { - asm!("mov rdi, 1", - "mov rsi, 2", - "mov rdx, 3", - "mov rcx, 4", - "mov r8, 5", - "mov r9, 6", - "mov eax, 0x3F800000", - "movd xmm0, eax", - "mov eax, 0x40000000", - "movd xmm1, eax", - "mov eax, 0x40800000", - "movd xmm2, eax", - "mov eax, 0x41000000", - "movd xmm3, eax", - "mov eax, 0x41800000", - "movd xmm4, eax", - "mov eax, 0x42000000", - "movd xmm5, eax", - "mov eax, 0x42800000", - "movd xmm6, eax", - "mov eax, 0x43000000", - "movd xmm7, eax", - "call {0}", - sym all_the_registers, - out("rax") result, - clobber_abi("sysv64"), - ); + llvm_asm!("mov rdi, 1; + mov rsi, 2; + mov rdx, 3; + mov rcx, 4; + mov r8, 5; + mov r9, 6; + mov eax, 0x3F800000; + movd xmm0, eax; + mov eax, 0x40000000; + movd xmm1, eax; + mov eax, 0x40800000; + movd xmm2, eax; + mov eax, 0x41000000; + movd xmm3, eax; + mov eax, 0x41800000; + movd xmm4, eax; + mov eax, 0x42000000; + movd xmm5, eax; + mov eax, 0x42800000; + movd xmm6, eax; + mov eax, 0x43000000; + movd xmm7, eax; + call r10 + " + : "={rax}"(result) + : "{r10}"(all_the_registers as usize) + : "rdi", "rsi", "rdx", "rcx", "r8", "r9", "r11", "cc", "memory" + : "intel", "alignstack" + ) } assert_eq!(result, 42); diff --git a/src/test/ui/asm/naked-functions.rs b/src/test/ui/asm/naked-functions.rs index 9c6e810dfce6d..7c7d974b9ac23 100644 --- a/src/test/ui/asm/naked-functions.rs +++ b/src/test/ui/asm/naked-functions.rs @@ -3,9 +3,11 @@ // ignore-spirv // ignore-wasm32 +#![feature(llvm_asm)] #![feature(naked_functions)] #![feature(asm_const, asm_sym, asm_unwind)] #![crate_type = "lib"] +#![allow(deprecated)] // llvm_asm! use std::arch::asm; @@ -99,6 +101,16 @@ pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { inner } +#[naked] +unsafe extern "C" fn llvm() -> ! { + //~^ WARN naked functions must contain a single asm block + //~| WARN this was previously accepted + llvm_asm!(""); + //~^ WARN LLVM-style inline assembly is unsupported in naked functions + //~| WARN this was previously accepted + core::hint::unreachable_unchecked(); +} + #[naked] unsafe extern "C" fn invalid_options() { asm!("", options(nomem, preserves_flags, noreturn)); diff --git a/src/test/ui/asm/naked-functions.stderr b/src/test/ui/asm/naked-functions.stderr index 35dc9cca33b5a..be4502e6c27e3 100644 --- a/src/test/ui/asm/naked-functions.stderr +++ b/src/test/ui/asm/naked-functions.stderr @@ -1,43 +1,72 @@ error: asm with the `pure` option must have at least one output +<<<<<<< HEAD --> $DIR/naked-functions.rs:110:14 +======= + --> $DIR/naked-functions.rs:136:14 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters +<<<<<<< HEAD --> $DIR/naked-functions.rs:20:5 +======= + --> $DIR/naked-functions.rs:23:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters +<<<<<<< HEAD --> $DIR/naked-functions.rs:22:5 +======= + --> $DIR/naked-functions.rs:25:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters +<<<<<<< HEAD --> $DIR/naked-functions.rs:24:6 +======= + --> $DIR/naked-functions.rs:27:6 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters +<<<<<<< HEAD --> $DIR/naked-functions.rs:26:5 +======= + --> $DIR/naked-functions.rs:29:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | P { x, y }: P, | ^^^^^^^^^^ error: referencing function parameters is not allowed in naked functions +<<<<<<< HEAD --> $DIR/naked-functions.rs:35:5 +======= + --> $DIR/naked-functions.rs:39:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | a + 1 | ^ | = help: follow the calling convention in asm block to use parameters +<<<<<<< HEAD error[E0787]: naked functions must contain a single asm block --> $DIR/naked-functions.rs:33:1 +======= +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:36:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / pub unsafe extern "C" fn inc(a: u32) -> u32 { LL | | @@ -48,21 +77,35 @@ LL | | } | |_^ error: referencing function parameters is not allowed in naked functions +<<<<<<< HEAD --> $DIR/naked-functions.rs:41:31 +======= + --> $DIR/naked-functions.rs:45:31 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!("/* {0} */", in(reg) a, options(noreturn)); | ^ | = help: follow the calling convention in asm block to use parameters +<<<<<<< HEAD error[E0787]: only `const` and `sym` operands are supported in naked functions --> $DIR/naked-functions.rs:41:23 +======= +warning: only `const` and `sym` operands are supported in naked functions + --> $DIR/naked-functions.rs:45:23 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!("/* {0} */", in(reg) a, options(noreturn)); | ^^^^^^^^^ +<<<<<<< HEAD error[E0787]: naked functions must contain a single asm block --> $DIR/naked-functions.rs:47:1 +======= +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:52:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { LL | | @@ -71,8 +114,13 @@ LL | | (|| a + 1)() LL | | } | |_^ +<<<<<<< HEAD error[E0787]: only `const` and `sym` operands are supported in naked functions --> $DIR/naked-functions.rs:64:10 +======= +warning: only `const` and `sym` operands are supported in naked functions + --> $DIR/naked-functions.rs:72:10 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | in(reg) a, | ^^^^^^^^^ @@ -86,8 +134,13 @@ LL | lateout(reg) d, LL | out(reg) e, | ^^^^^^^^^^ +<<<<<<< HEAD error[E0787]: asm in naked functions must use `noreturn` option --> $DIR/naked-functions.rs:62:5 +======= +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:69:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / asm!("/* {0} {1} {2} {3} {4} {5} {6} */", LL | | @@ -103,8 +156,13 @@ help: consider specifying that the asm block is responsible for returning from t LL | sym G, options(noreturn), | +++++++++++++++++++ +<<<<<<< HEAD error[E0787]: naked functions must contain a single asm block --> $DIR/naked-functions.rs:53:1 +======= +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:59:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / pub unsafe extern "C" fn unsupported_operands() { LL | | @@ -123,16 +181,26 @@ LL | | ); LL | | } | |_^ +<<<<<<< HEAD error[E0787]: naked functions must contain a single asm block --> $DIR/naked-functions.rs:76:1 +======= +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:85:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / pub extern "C" fn missing_assembly() { LL | | LL | | } | |_^ +<<<<<<< HEAD error[E0787]: asm in naked functions must use `noreturn` option --> $DIR/naked-functions.rs:83:5 +======= +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:94:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!(""); | ^^^^^^^^ @@ -142,8 +210,13 @@ help: consider specifying that the asm block is responsible for returning from t LL | asm!("", options(noreturn)); | +++++++++++++++++++ +<<<<<<< HEAD error[E0787]: asm in naked functions must use `noreturn` option --> $DIR/naked-functions.rs:85:5 +======= +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:97:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!(""); | ^^^^^^^^ @@ -153,8 +226,13 @@ help: consider specifying that the asm block is responsible for returning from t LL | asm!("", options(noreturn)); | +++++++++++++++++++ +<<<<<<< HEAD error[E0787]: asm in naked functions must use `noreturn` option --> $DIR/naked-functions.rs:87:5 +======= +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:100:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!(""); | ^^^^^^^^ @@ -164,8 +242,13 @@ help: consider specifying that the asm block is responsible for returning from t LL | asm!("", options(noreturn)); | +++++++++++++++++++ +<<<<<<< HEAD error[E0787]: naked functions must contain a single asm block --> $DIR/naked-functions.rs:81:1 +======= +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:91:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / pub extern "C" fn too_many_asm_blocks() { LL | | @@ -183,15 +266,24 @@ LL | | } | |_^ error: referencing function parameters is not allowed in naked functions +<<<<<<< HEAD --> $DIR/naked-functions.rs:96:11 +======= + --> $DIR/naked-functions.rs:111:11 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | *&y | ^ | = help: follow the calling convention in asm block to use parameters +<<<<<<< HEAD error[E0787]: naked functions must contain a single asm block --> $DIR/naked-functions.rs:94:5 +======= +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:108:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | / pub extern "C" fn inner(y: usize) -> usize { LL | | @@ -201,20 +293,62 @@ LL | | LL | | } | |_____^ +<<<<<<< HEAD error[E0787]: asm options unsupported in naked functions: `nomem`, `preserves_flags` --> $DIR/naked-functions.rs:104:5 +======= +warning: the LLVM-style inline assembly is unsupported in naked functions + --> $DIR/naked-functions.rs:121:5 + | +LL | llvm_asm!(""); + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 + = help: use the new asm! syntax specified in RFC 2873 + = note: this warning originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: naked functions must contain a single asm block + --> $DIR/naked-functions.rs:118:1 + | +LL | / unsafe extern "C" fn llvm() -> ! { +LL | | +LL | | +LL | | llvm_asm!(""); +... | +LL | | core::hint::unreachable_unchecked(); + | | ------------------------------------ non-asm is unsupported in naked functions +LL | | } + | |_^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #32408 + +warning: asm options unsupported in naked functions: `nomem`, `preserves_flags` + --> $DIR/naked-functions.rs:129:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!("", options(nomem, preserves_flags, noreturn)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +<<<<<<< HEAD error[E0787]: asm options unsupported in naked functions: `nostack`, `pure`, `readonly` --> $DIR/naked-functions.rs:110:5 +======= +warning: asm options unsupported in naked functions: `nostack`, `pure`, `readonly` + --> $DIR/naked-functions.rs:136:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +<<<<<<< HEAD error[E0787]: asm in naked functions must use `noreturn` option --> $DIR/naked-functions.rs:110:5 +======= +warning: asm in naked functions must use `noreturn` option + --> $DIR/naked-functions.rs:136:5 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -231,7 +365,11 @@ LL | asm!("", options(noreturn, may_unwind)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: Rust ABI is unsupported in naked functions +<<<<<<< HEAD --> $DIR/naked-functions.rs:123:15 +======= + --> $DIR/naked-functions.rs:145:15 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | pub unsafe fn default_abi() { | ^^^^^^^^^^^ @@ -239,47 +377,85 @@ LL | pub unsafe fn default_abi() { = note: `#[warn(undefined_naked_function_abi)]` on by default warning: Rust ABI is unsupported in naked functions +<<<<<<< HEAD --> $DIR/naked-functions.rs:129:15 +======= + --> $DIR/naked-functions.rs:151:15 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | pub unsafe fn rust_abi() { | ^^^^^^^^ +<<<<<<< HEAD error: naked functions cannot be inlined --> $DIR/naked-functions.rs:169:1 +======= +warning: naked functions cannot be inlined + --> $DIR/naked-functions.rs:191:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | #[inline] | ^^^^^^^^^ +<<<<<<< HEAD error: naked functions cannot be inlined --> $DIR/naked-functions.rs:176:1 +======= +warning: naked functions cannot be inlined + --> $DIR/naked-functions.rs:199:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ +<<<<<<< HEAD error: naked functions cannot be inlined --> $DIR/naked-functions.rs:183:1 +======= +warning: naked functions cannot be inlined + --> $DIR/naked-functions.rs:207:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ +<<<<<<< HEAD error: naked functions cannot be inlined --> $DIR/naked-functions.rs:190:1 +======= +warning: naked functions cannot be inlined + --> $DIR/naked-functions.rs:215:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | #[inline] | ^^^^^^^^^ +<<<<<<< HEAD error: naked functions cannot be inlined --> $DIR/naked-functions.rs:192:1 +======= +warning: naked functions cannot be inlined + --> $DIR/naked-functions.rs:218:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ +<<<<<<< HEAD error: naked functions cannot be inlined --> $DIR/naked-functions.rs:194:1 +======= +warning: naked functions cannot be inlined + --> $DIR/naked-functions.rs:221:1 +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu | LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ +<<<<<<< HEAD error: aborting due to 30 previous errors; 2 warnings emitted +======= +error: aborting due to 8 previous errors; 25 warnings emitted +>>>>>>> parent of a34c0797528... Auto merge of #92816 - tmiasko:rm-llvm-asm, r=Amanieu For more information about this error, try `rustc --explain E0787`. diff --git a/src/test/ui/ast-json/ast-json-ice.rs b/src/test/ui/ast-json/ast-json-ice.rs index ce93e4b5d4b2d..1a19883ae1550 100644 --- a/src/test/ui/ast-json/ast-json-ice.rs +++ b/src/test/ui/ast-json/ast-json-ice.rs @@ -8,6 +8,9 @@ // check-pass // dont-check-compiler-stdout - don't check for any AST change. +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + enum V { A(i32), B { f: [i64; 3 + 4] } @@ -24,6 +27,12 @@ macro_rules! call_println { } fn main() { + #[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] + unsafe { llvm_asm!(""::::); } + let x: (i32) = 35; let y = x as i64<> + 5; diff --git a/src/test/ui/borrowck/borrowck-asm.rs b/src/test/ui/borrowck/borrowck-asm.rs new file mode 100644 index 0000000000000..0d202c1b452d5 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-asm.rs @@ -0,0 +1,83 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "mips", + target_arch = "mips64"))] +mod test_cases { + fn is_move() { + let y: &mut isize; + let x = &mut 0isize; + unsafe { + llvm_asm!("nop" : : "r"(x)); + } + let z = x; //~ ERROR use of moved value: `x` + } + + fn in_is_read() { + let mut x = 3; + let y = &mut x; + unsafe { + llvm_asm!("nop" : : "r"(x)); //~ ERROR cannot use + } + let z = y; + } + + fn out_is_assign() { + let x = 3; + unsafe { + llvm_asm!("nop" : "=r"(x)); //~ ERROR cannot assign twice + } + let mut a = &mut 3; + let b = &*a; + unsafe { + llvm_asm!("nop" : "=r"(a)); // OK, Shallow write to `a` + } + let c = b; + let d = *a; + } + + fn rw_is_assign() { + let x = 3; + unsafe { + llvm_asm!("nop" : "+r"(x)); //~ ERROR cannot assign twice + } + } + + fn indirect_is_not_init() { + let x: i32; + unsafe { + llvm_asm!("nop" : "=*r"(x)); //~ ERROR use of possibly-uninitialized variable + } + } + + fn rw_is_read() { + let mut x = &mut 3; + let y = &*x; + unsafe { + llvm_asm!("nop" : "+r"(x)); //~ ERROR cannot assign to `x` because it is borrowed + } + let z = y; + } + + fn two_moves() { + let x = &mut 2; + unsafe { + llvm_asm!("nop" : : "r"(x), "r"(x) ); //~ ERROR use of moved value + } + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/borrowck-asm.stderr b/src/test/ui/borrowck/borrowck-asm.stderr new file mode 100644 index 0000000000000..ff5847d826e99 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-asm.stderr @@ -0,0 +1,81 @@ +error[E0382]: use of moved value: `x` + --> $DIR/borrowck-asm.rs:26:17 + | +LL | let x = &mut 0isize; + | - move occurs because `x` has type `&mut isize`, which does not implement the `Copy` trait +LL | unsafe { +LL | llvm_asm!("nop" : : "r"(x)); + | - value moved here +LL | } +LL | let z = x; + | ^ value used here after move + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/borrowck-asm.rs:33:37 + | +LL | let y = &mut x; + | ------ borrow of `x` occurs here +LL | unsafe { +LL | llvm_asm!("nop" : : "r"(x)); + | ^ use of borrowed `x` +LL | } +LL | let z = y; + | - borrow later used here + +error[E0384]: cannot assign twice to immutable variable `x` + --> $DIR/borrowck-asm.rs:41:36 + | +LL | let x = 3; + | - + | | + | first assignment to `x` + | help: consider making this binding mutable: `mut x` +LL | unsafe { +LL | llvm_asm!("nop" : "=r"(x)); + | ^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` + --> $DIR/borrowck-asm.rs:55:36 + | +LL | let x = 3; + | - + | | + | first assignment to `x` + | help: consider making this binding mutable: `mut x` +LL | unsafe { +LL | llvm_asm!("nop" : "+r"(x)); + | ^ cannot assign twice to immutable variable + +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/borrowck-asm.rs:62:37 + | +LL | llvm_asm!("nop" : "=*r"(x)); + | ^ use of possibly-uninitialized `x` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/borrowck-asm.rs:70:36 + | +LL | let y = &*x; + | --- borrow of `x` occurs here +LL | unsafe { +LL | llvm_asm!("nop" : "+r"(x)); + | ^ assignment to borrowed `x` occurs here +LL | } +LL | let z = y; + | - borrow later used here + +error[E0382]: use of moved value: `x` + --> $DIR/borrowck-asm.rs:78:45 + | +LL | let x = &mut 2; + | - move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait +LL | unsafe { +LL | llvm_asm!("nop" : : "r"(x), "r"(x) ); + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0381, E0382, E0384, E0503, E0506. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/consts/miri_unleashed/inline_asm.rs b/src/test/ui/consts/miri_unleashed/inline_asm.rs index 6971170337d41..1bb22a1301abf 100644 --- a/src/test/ui/consts/miri_unleashed/inline_asm.rs +++ b/src/test/ui/consts/miri_unleashed/inline_asm.rs @@ -1,13 +1,24 @@ // compile-flags: -Zunleash-the-miri-inside-of-you // only-x86_64 +#![feature(llvm_asm)] #![allow(const_err)] +#![allow(deprecated)] // llvm_asm! use std::arch::asm; fn main() {} // Make sure we catch executing inline assembly. -static TEST_BAD: () = { +static TEST_BAD1: () = { + unsafe { llvm_asm!("xor %eax, %eax" ::: "eax"); } + //~^ ERROR could not evaluate static initializer + //~| NOTE inline assembly is not supported + //~| NOTE in this expansion of llvm_asm! + //~| NOTE in this expansion of llvm_asm! +}; + +// Make sure we catch executing inline assembly. +static TEST_BAD2: () = { unsafe { asm!("nop"); } //~^ ERROR could not evaluate static initializer //~| NOTE inline assembly is not supported diff --git a/src/test/ui/consts/miri_unleashed/inline_asm.stderr b/src/test/ui/consts/miri_unleashed/inline_asm.stderr index 595b859cbcebb..34ac808ed1702 100644 --- a/src/test/ui/consts/miri_unleashed/inline_asm.stderr +++ b/src/test/ui/consts/miri_unleashed/inline_asm.stderr @@ -1,5 +1,13 @@ error[E0080]: could not evaluate static initializer - --> $DIR/inline_asm.rs:11:14 + --> $DIR/inline_asm.rs:13:14 + | +LL | unsafe { llvm_asm!("xor %eax, %eax" ::: "eax"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inline assembly is not supported + | + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: could not evaluate static initializer + --> $DIR/inline_asm.rs:22:14 | LL | unsafe { asm!("nop"); } | ^^^^^^^^^^^ inline assembly is not supported @@ -7,11 +15,17 @@ LL | unsafe { asm!("nop"); } warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/inline_asm.rs:11:14 + --> $DIR/inline_asm.rs:13:14 + | +LL | unsafe { llvm_asm!("xor %eax, %eax" ::: "eax"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/inline_asm.rs:22:14 | LL | unsafe { asm!("nop"); } | ^^^^^^^^^^^ + = note: this warning originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/error-codes/E0660.rs b/src/test/ui/error-codes/E0660.rs new file mode 100644 index 0000000000000..43af2406f5ee4 --- /dev/null +++ b/src/test/ui/error-codes/E0660.rs @@ -0,0 +1,10 @@ +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + let a; + llvm_asm!("nop" "nop"); + //~^ ERROR E0660 + llvm_asm!("nop" "nop" : "=r"(a)); + //~^ ERROR E0660 +} diff --git a/src/test/ui/error-codes/E0660.stderr b/src/test/ui/error-codes/E0660.stderr new file mode 100644 index 0000000000000..d9d2f35251b9b --- /dev/null +++ b/src/test/ui/error-codes/E0660.stderr @@ -0,0 +1,15 @@ +error[E0660]: malformed inline assembly + --> $DIR/E0660.rs:6:5 + | +LL | llvm_asm!("nop" "nop"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0660]: malformed inline assembly + --> $DIR/E0660.rs:8:5 + | +LL | llvm_asm!("nop" "nop" : "=r"(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0660`. diff --git a/src/test/ui/error-codes/E0661.rs b/src/test/ui/error-codes/E0661.rs new file mode 100644 index 0000000000000..854675c36391c --- /dev/null +++ b/src/test/ui/error-codes/E0661.rs @@ -0,0 +1,10 @@ +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + let a; //~ ERROR type annotations needed + llvm_asm!("nop" : "r"(a)); + //~^ ERROR E0661 +} diff --git a/src/test/ui/error-codes/E0661.stderr b/src/test/ui/error-codes/E0661.stderr new file mode 100644 index 0000000000000..73745ef97758d --- /dev/null +++ b/src/test/ui/error-codes/E0661.stderr @@ -0,0 +1,16 @@ +error[E0661]: output operand constraint lacks '=' or '+' + --> $DIR/E0661.rs:8:23 + | +LL | llvm_asm!("nop" : "r"(a)); + | ^^^ + +error[E0282]: type annotations needed + --> $DIR/E0661.rs:7:9 + | +LL | let a; + | ^ consider giving `a` a type + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0282, E0661. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/error-codes/E0662.rs b/src/test/ui/error-codes/E0662.rs new file mode 100644 index 0000000000000..679a88c3433c3 --- /dev/null +++ b/src/test/ui/error-codes/E0662.rs @@ -0,0 +1,11 @@ +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + llvm_asm!("xor %eax, %eax" + : + : "=test"("a") //~ ERROR E0662 + ); +} diff --git a/src/test/ui/error-codes/E0662.stderr b/src/test/ui/error-codes/E0662.stderr new file mode 100644 index 0000000000000..f6695d700fbd4 --- /dev/null +++ b/src/test/ui/error-codes/E0662.stderr @@ -0,0 +1,9 @@ +error[E0662]: input operand constraint contains '=' + --> $DIR/E0662.rs:9:17 + | +LL | : "=test"("a") + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0662`. diff --git a/src/test/ui/error-codes/E0663.rs b/src/test/ui/error-codes/E0663.rs new file mode 100644 index 0000000000000..b82f1adfb426d --- /dev/null +++ b/src/test/ui/error-codes/E0663.rs @@ -0,0 +1,11 @@ +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + llvm_asm!("xor %eax, %eax" + : + : "+test"("a") //~ ERROR E0663 + ); +} diff --git a/src/test/ui/error-codes/E0663.stderr b/src/test/ui/error-codes/E0663.stderr new file mode 100644 index 0000000000000..5f8dede0242f3 --- /dev/null +++ b/src/test/ui/error-codes/E0663.stderr @@ -0,0 +1,9 @@ +error[E0663]: input operand constraint contains '+' + --> $DIR/E0663.rs:9:17 + | +LL | : "+test"("a") + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0663`. diff --git a/src/test/ui/error-codes/E0664.rs b/src/test/ui/error-codes/E0664.rs new file mode 100644 index 0000000000000..d2730f04f4547 --- /dev/null +++ b/src/test/ui/error-codes/E0664.rs @@ -0,0 +1,12 @@ +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + llvm_asm!("mov $$0x200, %eax" + : + : + : "{eax}" //~ ERROR E0664 + ); +} diff --git a/src/test/ui/error-codes/E0664.stderr b/src/test/ui/error-codes/E0664.stderr new file mode 100644 index 0000000000000..5e6836f28e3de --- /dev/null +++ b/src/test/ui/error-codes/E0664.stderr @@ -0,0 +1,9 @@ +error[E0664]: clobber should not be surrounded by braces + --> $DIR/E0664.rs:10:17 + | +LL | : "{eax}" + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0664`. diff --git a/src/test/ui/feature-gates/feature-gate-asm.rs b/src/test/ui/feature-gates/feature-gate-asm.rs new file mode 100644 index 0000000000000..556219b98a93a --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-asm.rs @@ -0,0 +1,10 @@ +// only-x86_64 + +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + llvm_asm!(""); + //~^ ERROR prefer using the new asm! syntax instead + } +} diff --git a/src/test/ui/feature-gates/feature-gate-asm.stderr b/src/test/ui/feature-gates/feature-gate-asm.stderr new file mode 100644 index 0000000000000..72ba70d0d9149 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-asm.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature 'llvm_asm': prefer using the new asm! syntax instead + --> $DIR/feature-gate-asm.rs:7:9 + | +LL | llvm_asm!(""); + | ^^^^^^^^ + | + = note: see issue #70173 for more information + = help: add `#![feature(llvm_asm)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-asm2.rs b/src/test/ui/feature-gates/feature-gate-asm2.rs new file mode 100644 index 0000000000000..712e3a56fd87f --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-asm2.rs @@ -0,0 +1,10 @@ +// only-x86_64 + +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + println!("{:?}", llvm_asm!("")); + //~^ ERROR prefer using the new asm! syntax instead + } +} diff --git a/src/test/ui/feature-gates/feature-gate-asm2.stderr b/src/test/ui/feature-gates/feature-gate-asm2.stderr new file mode 100644 index 0000000000000..0297fec16dd72 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-asm2.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature 'llvm_asm': prefer using the new asm! syntax instead + --> $DIR/feature-gate-asm2.rs:7:26 + | +LL | println!("{:?}", llvm_asm!("")); + | ^^^^^^^^ + | + = note: see issue #70173 for more information + = help: add `#![feature(llvm_asm)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/issues/issue-9129.rs b/src/test/ui/issues/issue-9129.rs index 780a6419c5f30..825fed5c722e0 100644 --- a/src/test/ui/issues/issue-9129.rs +++ b/src/test/ui/issues/issue-9129.rs @@ -2,6 +2,7 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(deprecated)] // llvm_asm! // ignore-pretty unreported #![feature(box_syntax)] @@ -12,6 +13,7 @@ impl bomb for S { fn boom(&self, _: Ident) { } } pub struct Ident { name: usize } +// macro_rules! int3 { () => ( unsafe { llvm_asm!( "int3" ); } ) } macro_rules! int3 { () => ( { } ) } fn Ident_new() -> Ident { diff --git a/src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs b/src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs new file mode 100644 index 0000000000000..d178c65dff497 --- /dev/null +++ b/src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs @@ -0,0 +1,13 @@ +// build-fail +// dont-check-compiler-stderr +// compile-flags: -C codegen-units=2 +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + llvm_asm!("nowayisthisavalidinstruction"); //~ ERROR instruction + } +} diff --git a/src/test/ui/llvm-asm/asm-src-loc.rs b/src/test/ui/llvm-asm/asm-src-loc.rs new file mode 100644 index 0000000000000..4506c12a0e6b9 --- /dev/null +++ b/src/test/ui/llvm-asm/asm-src-loc.rs @@ -0,0 +1,12 @@ +// build-fail +// dont-check-compiler-stderr +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + llvm_asm!("nowayisthisavalidinstruction"); //~ ERROR instruction + } +} diff --git a/src/test/ui/llvm-asm/inline-asm-bad-constraint.rs b/src/test/ui/llvm-asm/inline-asm-bad-constraint.rs new file mode 100644 index 0000000000000..6a2ce111cb313 --- /dev/null +++ b/src/test/ui/llvm-asm/inline-asm-bad-constraint.rs @@ -0,0 +1,41 @@ +// Test that the compiler will catch invalid inline assembly constraints. + +// build-fail +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +extern "C" { + fn foo(a: usize); +} + +fn main() { + bad_register_constraint(); + bad_input(); + wrong_size_output(); +} + +// Issue #54130 +fn bad_register_constraint() { + let rax: u64; + unsafe { + llvm_asm!("" :"={rax"(rax)) //~ ERROR E0668 + }; + println!("Accumulator is: {}", rax); +} + +// Issue #54376 +fn bad_input() { + unsafe { + llvm_asm!("callq $0" : : "0"(foo)) //~ ERROR E0668 + }; +} + +fn wrong_size_output() { + let rax: u64 = 0; + unsafe { + llvm_asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668 + } + println!("rax: {}", rax); +} diff --git a/src/test/ui/llvm-asm/inline-asm-bad-constraint.stderr b/src/test/ui/llvm-asm/inline-asm-bad-constraint.stderr new file mode 100644 index 0000000000000..a624829f149c8 --- /dev/null +++ b/src/test/ui/llvm-asm/inline-asm-bad-constraint.stderr @@ -0,0 +1,27 @@ +error[E0668]: malformed inline assembly + --> $DIR/inline-asm-bad-constraint.rs:23:9 + | +LL | llvm_asm!("" :"={rax"(rax)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0668]: malformed inline assembly + --> $DIR/inline-asm-bad-constraint.rs:31:9 + | +LL | llvm_asm!("callq $0" : : "0"(foo)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0668]: malformed inline assembly + --> $DIR/inline-asm-bad-constraint.rs:38:9 + | +LL | llvm_asm!("addb $1, $0" : "={rax}"((0i32, rax))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0668`. diff --git a/src/test/ui/llvm-asm/inline-asm-bad-operand.rs b/src/test/ui/llvm-asm/inline-asm-bad-operand.rs new file mode 100644 index 0000000000000..1746c487b1e32 --- /dev/null +++ b/src/test/ui/llvm-asm/inline-asm-bad-operand.rs @@ -0,0 +1,60 @@ +// Test that the compiler will catch passing invalid values to inline assembly +// operands. + +// build-fail +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[repr(C)] +struct MyPtr(usize); + +fn main() { + issue_37433(); + issue_37437(); + issue_40187(); + issue_54067(); + multiple_errors(); +} + +fn issue_37433() { + unsafe { + llvm_asm!("" :: "r"("")); //~ ERROR E0669 + } + + unsafe { + let target = MyPtr(0); + llvm_asm!("ret" : : "{rdi}"(target)); //~ ERROR E0669 + } +} + +fn issue_37437() { + let hello: &str = "hello"; + // this should fail... + unsafe { llvm_asm!("" :: "i"(hello)) }; //~ ERROR E0669 + // but this should succeed. + unsafe { llvm_asm!("" :: "r"(hello.as_ptr())) }; +} + +fn issue_40187() { + let arr: [u8; 1] = [0; 1]; + unsafe { + llvm_asm!("movups $1, %xmm0"::"m"(arr)); //~ ERROR E0669 + } +} + +fn issue_54067() { + let addr: Option = Some(123); + unsafe { + llvm_asm!("mov sp, $0"::"r"(addr)); //~ ERROR E0669 + } +} + +fn multiple_errors() { + let addr: (u32, u32) = (1, 2); + unsafe { + llvm_asm!("mov sp, $0"::"r"(addr), //~ ERROR E0669 + "r"("hello e0669")); //~ ERROR E0669 + } +} diff --git a/src/test/ui/llvm-asm/inline-asm-bad-operand.stderr b/src/test/ui/llvm-asm/inline-asm-bad-operand.stderr new file mode 100644 index 0000000000000..8bb8a7ae82ebd --- /dev/null +++ b/src/test/ui/llvm-asm/inline-asm-bad-operand.stderr @@ -0,0 +1,45 @@ +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:23:29 + | +LL | llvm_asm!("" :: "r"("")); + | ^^ + +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:28:37 + | +LL | llvm_asm!("ret" : : "{rdi}"(target)); + | ^^^^^^ + +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:35:34 + | +LL | unsafe { llvm_asm!("" :: "i"(hello)) }; + | ^^^^^ + +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:43:43 + | +LL | llvm_asm!("movups $1, %xmm0"::"m"(arr)); + | ^^^ + +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:50:37 + | +LL | llvm_asm!("mov sp, $0"::"r"(addr)); + | ^^^^ + +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:57:37 + | +LL | llvm_asm!("mov sp, $0"::"r"(addr), + | ^^^^ + +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/inline-asm-bad-operand.rs:58:37 + | +LL | ... "r"("hello e0669")); + | ^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0669`. diff --git a/src/test/ui/llvm-asm/issue-14936.rs b/src/test/ui/llvm-asm/issue-14936.rs new file mode 100644 index 0000000000000..46e5a2a80925f --- /dev/null +++ b/src/test/ui/llvm-asm/issue-14936.rs @@ -0,0 +1,49 @@ +// build-pass +#![allow(unused_macros)] +#![allow(dead_code)] +#![allow(deprecated)] // llvm_asm! +#![feature(llvm_asm)] + +type History = Vec<&'static str>; + +fn wrap(x:A, which: &'static str, history: &mut History) -> A { + history.push(which); + x +} + +macro_rules! demo { + ( $output_constraint:tt ) => { + { + let mut x: isize = 0; + let y: isize = 1; + + let mut history: History = vec![]; + unsafe { + llvm_asm!("mov ($1), $0" + : $output_constraint (*wrap(&mut x, "out", &mut history)) + : "r"(&wrap(y, "in", &mut history)) + :: "volatile"); + } + assert_eq!((x,y), (1,1)); + let b: &[_] = &["out", "in"]; + assert_eq!(history, b); + } + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn main() { + fn out_write_only_expr_then_in_expr() { + demo!("=r") + } + + fn out_read_write_expr_then_in_expr() { + demo!("+r") + } + + out_write_only_expr_then_in_expr(); + out_read_write_expr_then_in_expr(); +} + +#[cfg(all(not(target_arch = "x86"), not(target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/issue-23458.rs b/src/test/ui/llvm-asm/issue-23458.rs new file mode 100644 index 0000000000000..d640828c16dce --- /dev/null +++ b/src/test/ui/llvm-asm/issue-23458.rs @@ -0,0 +1,12 @@ +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! +// compile-flags: -Ccodegen-units=1 +// build-fail +// only-x86_64 + +fn main() { + unsafe { + llvm_asm!("int $3"); //~ ERROR too few operands for instruction + //~| ERROR invalid operand in inline asm + } +} diff --git a/src/test/ui/llvm-asm/issue-23458.stderr b/src/test/ui/llvm-asm/issue-23458.stderr new file mode 100644 index 0000000000000..69e458f7ca098 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-23458.stderr @@ -0,0 +1,20 @@ +error: invalid operand in inline asm: 'int $3' + --> $DIR/issue-23458.rs:9:9 + | +LL | llvm_asm!("int $3"); + | ^ + +error: too few operands for instruction + --> $DIR/issue-23458.rs:9:9 + | +LL | llvm_asm!("int $3"); + | ^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | int + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/llvm-asm/issue-33264.rs b/src/test/ui/llvm-asm/issue-33264.rs new file mode 100644 index 0000000000000..8ebf0cfe217f6 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-33264.rs @@ -0,0 +1,30 @@ +// build-pass +// only-x86_64 + +#![allow(dead_code, non_upper_case_globals)] +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[repr(C)] +pub struct D32x4(f32,f32,f32,f32); + +impl D32x4 { + fn add(&self, vec: Self) -> Self { + unsafe { + let ret: Self; + llvm_asm!(" + movaps $1, %xmm1 + movaps $2, %xmm2 + addps %xmm1, %xmm2 + movaps $xmm1, $0 + " + : "=r"(ret) + : "1"(self), "2"(vec) + : "xmm1", "xmm2" + ); + ret + } + } +} + +fn main() { } diff --git a/src/test/ui/llvm-asm/issue-37366.rs b/src/test/ui/llvm-asm/issue-37366.rs new file mode 100644 index 0000000000000..acc2f3e9f5fe1 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-37366.rs @@ -0,0 +1,16 @@ +// check-pass +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +macro_rules! interrupt_handler { + () => { + unsafe fn _interrupt_handler() { + llvm_asm!("pop eax" :::: "intel"); + } + } +} +interrupt_handler!{} + +fn main() {} diff --git a/src/test/ui/llvm-asm/issue-37433.rs b/src/test/ui/llvm-asm/issue-37433.rs new file mode 100644 index 0000000000000..1c362e8aba058 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-37433.rs @@ -0,0 +1,12 @@ +// build-fail +// ignore-emscripten no llvm_asm! support + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + llvm_asm!("" :: "r"("")); + //~^ ERROR: invalid value for constraint in inline assembly + } +} diff --git a/src/test/ui/llvm-asm/issue-37433.stderr b/src/test/ui/llvm-asm/issue-37433.stderr new file mode 100644 index 0000000000000..44a8eb32b7ccb --- /dev/null +++ b/src/test/ui/llvm-asm/issue-37433.stderr @@ -0,0 +1,9 @@ +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/issue-37433.rs:9:29 + | +LL | llvm_asm!("" :: "r"("")); + | ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0669`. diff --git a/src/test/ui/llvm-asm/issue-51431.rs b/src/test/ui/llvm-asm/issue-51431.rs new file mode 100644 index 0000000000000..706b714766150 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-51431.rs @@ -0,0 +1,12 @@ +// build-fail +// ignore-emscripten no llvm_asm! support + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + llvm_asm! {"mov $0,$1"::"0"("bx"),"1"(0x00)} + //~^ ERROR: invalid value for constraint in inline assembly + } +} diff --git a/src/test/ui/llvm-asm/issue-51431.stderr b/src/test/ui/llvm-asm/issue-51431.stderr new file mode 100644 index 0000000000000..35c8c1b66f6a7 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-51431.stderr @@ -0,0 +1,9 @@ +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/issue-51431.rs:9:37 + | +LL | llvm_asm! {"mov $0,$1"::"0"("bx"),"1"(0x00)} + | ^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0669`. diff --git a/src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.rs b/src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.rs new file mode 100644 index 0000000000000..d9fe7cada278f --- /dev/null +++ b/src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.rs @@ -0,0 +1,27 @@ +// Regression test for Issue #53787: Fix ICE when creating a label in inline assembler with macros. + +// build-fail +// ignore-emscripten + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +macro_rules! fake_jump { + ($id:expr) => { + unsafe { + llvm_asm!( + " + jmp $0 + lea eax, [ebx] + xor eax, 0xDEADBEEF + retn + $0: + "::"0"($id)::"volatile", "intel"); + } + }; +} + +fn main() { + fake_jump!("FirstFunc"); //~ ERROR invalid value for constraint in inline assembly + println!("Hello, world!"); +} diff --git a/src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.stderr b/src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.stderr new file mode 100644 index 0000000000000..fd755e30d5868 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.stderr @@ -0,0 +1,9 @@ +error[E0669]: invalid value for constraint in inline assembly + --> $DIR/issue-53787-inline-assembler-macro.rs:25:16 + | +LL | fake_jump!("FirstFunc"); + | ^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0669`. diff --git a/src/test/ui/llvm-asm/issue-54067.rs b/src/test/ui/llvm-asm/issue-54067.rs new file mode 100644 index 0000000000000..6e931b9003a17 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-54067.rs @@ -0,0 +1,13 @@ +// check-pass +// ignore-emscripten no llvm_asm! support + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +pub fn boot(addr: Option) { + unsafe { + llvm_asm!("mov sp, $0"::"r" (addr)); + } +} + +fn main() {} diff --git a/src/test/ui/llvm-asm/issue-62046.rs b/src/test/ui/llvm-asm/issue-62046.rs new file mode 100644 index 0000000000000..38b5f1a186287 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-62046.rs @@ -0,0 +1,12 @@ +// build-fail +// ignore-emscripten no asm! support + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + llvm_asm!("nop" : "+r"("r15")); + //~^ malformed inline assembly + } +} diff --git a/src/test/ui/llvm-asm/issue-62046.stderr b/src/test/ui/llvm-asm/issue-62046.stderr new file mode 100644 index 0000000000000..ae271afe2625e --- /dev/null +++ b/src/test/ui/llvm-asm/issue-62046.stderr @@ -0,0 +1,11 @@ +error[E0668]: malformed inline assembly + --> $DIR/issue-62046.rs:9:9 + | +LL | llvm_asm!("nop" : "+r"("r15")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0668`. diff --git a/src/test/ui/llvm-asm/issue-69092.rs b/src/test/ui/llvm-asm/issue-69092.rs new file mode 100644 index 0000000000000..ea1b80cc9b06b --- /dev/null +++ b/src/test/ui/llvm-asm/issue-69092.rs @@ -0,0 +1,13 @@ +// build-fail +// ignore-emscripten no asm! support +// The error message differs slightly between LLVM versions +// min-llvm-version: 13.0 +// Regression test for #69092 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { llvm_asm!(".ascii \"Xen\0\""); } + //~^ ERROR: expected string +} diff --git a/src/test/ui/llvm-asm/issue-69092.stderr b/src/test/ui/llvm-asm/issue-69092.stderr new file mode 100644 index 0000000000000..28c5fbbca3c60 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-69092.stderr @@ -0,0 +1,14 @@ +error: expected string + --> $DIR/issue-69092.rs:11:14 + | +LL | unsafe { llvm_asm!(".ascii \"Xen\0\""); } + | ^ + | +note: instantiated into assembly here + --> :1:9 + | +LL | .ascii "Xen + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs new file mode 100644 index 0000000000000..c15009f15e68c --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs @@ -0,0 +1,27 @@ +// ignore-android +// ignore-arm +// ignore-aarch64 +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64"))] + +pub fn main() { + unsafe { + // clobber formatted as register input/output + llvm_asm!("xor %eax, %eax" : : : "{eax}"); + //~^ ERROR clobber should not be surrounded by braces + } +} diff --git a/src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr new file mode 100644 index 0000000000000..bb72a14285316 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr @@ -0,0 +1,9 @@ +error[E0664]: clobber should not be surrounded by braces + --> $DIR/llvm-asm-bad-clobber.rs:24:42 + | +LL | llvm_asm!("xor %eax, %eax" : : : "{eax}"); + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0664`. diff --git a/src/test/ui/llvm-asm/llvm-asm-concat-src.rs b/src/test/ui/llvm-asm/llvm-asm-concat-src.rs new file mode 100644 index 0000000000000..722eb07a27002 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-concat-src.rs @@ -0,0 +1,10 @@ +// run-pass +// pretty-expanded FIXME #23616 +// ignore-emscripten no asm + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +pub fn main() { + unsafe { llvm_asm!(concat!("", "")) }; +} diff --git a/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs new file mode 100644 index 0000000000000..1bccb0b73a89d --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs @@ -0,0 +1,36 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + let y: isize; + unsafe { + llvm_asm!("mov $1, $0" : "=r"(x) : "=r"(5)); //~ ERROR operand constraint contains '=' + llvm_asm!("mov $1, $0" : "=r"(y) : "+r"(5)); //~ ERROR operand constraint contains '+' + } + foo(x); + foo(y); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr new file mode 100644 index 0000000000000..f6c618e4c1352 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr @@ -0,0 +1,16 @@ +error[E0662]: input operand constraint contains '=' + --> $DIR/llvm-asm-in-bad-modifier.rs:25:44 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "=r"(5)); + | ^^^^ + +error[E0663]: input operand constraint contains '+' + --> $DIR/llvm-asm-in-bad-modifier.rs:26:44 + | +LL | llvm_asm!("mov $1, $0" : "=r"(y) : "+r"(5)); + | ^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0662, E0663. +For more information about an error, try `rustc --explain E0662`. diff --git a/src/test/ui/llvm-asm/llvm-asm-in-moved.rs b/src/test/ui/llvm-asm/llvm-asm-in-moved.rs new file mode 100644 index 0000000000000..f7b0fe5566cb7 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-moved.rs @@ -0,0 +1,32 @@ +// run-pass + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! +#![allow(dead_code)] + +use std::cell::Cell; + +#[repr(C)] +struct NoisyDrop<'a>(&'a Cell<&'static str>); +impl<'a> Drop for NoisyDrop<'a> { + fn drop(&mut self) { + self.0.set("destroyed"); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn main() { + let status = Cell::new("alive"); + { + let _y: Box; + let x = Box::new(NoisyDrop(&status)); + unsafe { + llvm_asm!("mov $1, $0" : "=r"(_y) : "r"(x)); + } + assert_eq!(status.get(), "alive"); + } + assert_eq!(status.get(), "destroyed"); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs b/src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs new file mode 100644 index 0000000000000..2429b512e86d9 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs @@ -0,0 +1,57 @@ +// run-pass + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn next_power_of_2(n: u32) -> u32 { + let mut tmp = n; + llvm_asm!("dec $0" : "+rm"(tmp) :: "cc"); + let mut shift = 1_u32; + while shift <= 16 { + llvm_asm!( + "shr %cl, $2 + or $2, $0 + shl $$1, $1" + : "+&rm"(tmp), "+{ecx}"(shift) : "r"(tmp) : "cc" + ); + } + llvm_asm!("inc $0" : "+rm"(tmp) :: "cc"); + return tmp; +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + unsafe { + assert_eq!(64, next_power_of_2(37)); + assert_eq!(2147483648, next_power_of_2(2147483647)); + } + + let mut y: isize = 5; + let x: isize; + unsafe { + // Treat the output as initialization. + llvm_asm!( + "shl $2, $1 + add $3, $1 + mov $1, $0" + : "=r"(x), "+r"(y) : "i"(3_usize), "ir"(7_usize) : "cc" + ); + } + assert_eq!(x, 47); + assert_eq!(y, 47); + + let mut x = x + 1; + assert_eq!(x, 48); + + unsafe { + // Assignment to mutable. + // Early clobber "&": + // Forbids the use of a single register by both operands. + llvm_asm!("shr $$2, $1; add $1, $0" : "+&r"(x) : "r"(x) : "cc"); + } + assert_eq!(x, 60); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs b/src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs new file mode 100644 index 0000000000000..441c62b04b561 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs @@ -0,0 +1,44 @@ +// run-pass + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn read(ptr: &u32) -> u32 { + let out: u32; + unsafe { + llvm_asm!("mov $1, $0" : "=r" (out) : "*m" (ptr)); + } + out +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn write(ptr: &mut u32, val: u32) { + unsafe { + llvm_asm!("mov $1, $0" : "=*m" (ptr) : "r" (val)); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn replace(ptr: &mut u32, val: u32) -> u32 { + let out: u32; + unsafe { + llvm_asm!("mov $0, $1; mov $2, $0" : "+*m" (ptr), "=&r" (out) : "r" (val)); + } + out +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + let a = 1; + assert_eq!(read(&a), 1); + let mut b = 2; + write(&mut b, 3); + assert_eq!(b, 3); + let mut c = 4; + assert_eq!(replace(&mut c, 5), 4); + assert_eq!(c, 5); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs b/src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs new file mode 100644 index 0000000000000..ecd0c2f9734f1 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs @@ -0,0 +1,13 @@ +// build-pass +// only-x86_64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + unsafe { + // "nop" :: "r"(x) : "eax" : "volatile" + let x = 10; + llvm_asm!("\x6Eop" :: "\x72"(x) : "\x65ax" : "\x76olatile"); + } +} diff --git a/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs new file mode 100644 index 0000000000000..04f0972ed4b28 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs @@ -0,0 +1,37 @@ +// check-pass +// ignore-android +// ignore-arm +// ignore-aarch64 +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64"))] +fn main() { + // assignment not dead + let mut x: isize = 0; + unsafe { + // extra colon + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc"); + //~^ WARNING unrecognized option + } + assert_eq!(x, 5); + + unsafe { + // comma in place of a colon + llvm_asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile"); + //~^ WARNING expected a clobber, found an option + } + assert_eq!(x, 13); +} diff --git a/src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr new file mode 100644 index 0000000000000..d1250d99b8f33 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr @@ -0,0 +1,14 @@ +warning: unrecognized option + --> $DIR/llvm-asm-misplaced-option.rs:26:69 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc"); + | ^^^^ + +warning: expected a clobber, found an option + --> $DIR/llvm-asm-misplaced-option.rs:33:85 + | +LL | llvm_asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile"); + | ^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs new file mode 100644 index 0000000000000..7f51b50f2586f --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs @@ -0,0 +1,36 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + x = 1; + foo(x); + unsafe { + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5)); + //~^ ERROR cannot assign twice to immutable variable `x` + } + foo(x); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr new file mode 100644 index 0000000000000..390c032460127 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr @@ -0,0 +1,14 @@ +error[E0384]: cannot assign twice to immutable variable `x` + --> $DIR/llvm-asm-out-assign-imm.rs:26:39 + | +LL | let x: isize; + | - help: consider making this binding mutable: `mut x` +LL | x = 1; + | ----- first assignment to `x` +... +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5)); + | ^ cannot assign twice to immutable variable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0384`. diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign.rs b/src/test/ui/llvm-asm/llvm-asm-out-assign.rs new file mode 100644 index 0000000000000..c5f4a9a198e51 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign.rs @@ -0,0 +1,26 @@ +// run-pass + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm!s + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + let x: isize; + unsafe { + // Treat the output as initialization. + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(5_usize)); + } + assert_eq!(x, 5); + + let mut x = x + 1; + assert_eq!(x, 6); + + unsafe { + // Assignment to mutable. + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(x + 7)); + } + assert_eq!(x, 13); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs new file mode 100644 index 0000000000000..c74d156cf3eae --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs @@ -0,0 +1,33 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + unsafe { + llvm_asm!("mov $1, $0" : "r"(x) : "r"(5)); //~ ERROR output operand constraint lacks '=' + } + foo(x); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr new file mode 100644 index 0000000000000..f426170ad8738 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr @@ -0,0 +1,9 @@ +error[E0661]: output operand constraint lacks '=' or '+' + --> $DIR/llvm-asm-out-no-modifier.rs:24:34 + | +LL | llvm_asm!("mov $1, $0" : "r"(x) : "r"(5)); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0661`. diff --git a/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs new file mode 100644 index 0000000000000..161add7e56a76 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs @@ -0,0 +1,34 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// ignore-sparc +// ignore-sparc64 +// ignore-mips +// ignore-mips64 + +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn foo(x: isize) { println!("{}", x); } + +#[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64"))] +pub fn main() { + let x: isize; + unsafe { + llvm_asm!("mov $1, $0" : "=r"(x) : "r"(x)); + //~^ ERROR use of possibly-uninitialized variable: `x` + } + foo(x); +} + +#[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64")))] +pub fn main() {} diff --git a/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr new file mode 100644 index 0000000000000..7e57dc45aad49 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr @@ -0,0 +1,9 @@ +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/llvm-asm-out-read-uninit.rs:24:48 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "r"(x)); + | ^ use of possibly-uninitialized `x` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0381`. diff --git a/src/test/ui/llvm-asm/llvm-asm-parse-errors.rs b/src/test/ui/llvm-asm/llvm-asm-parse-errors.rs new file mode 100644 index 0000000000000..cdd0b943b71e6 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-parse-errors.rs @@ -0,0 +1,16 @@ +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + +fn main() { + llvm_asm!(); //~ ERROR requires a string literal as an argument + llvm_asm!("nop" : struct); //~ ERROR expected string literal + llvm_asm!("mov %eax, $$0x2" : struct); //~ ERROR expected string literal + llvm_asm!("mov %eax, $$0x2" : "={eax}" struct); //~ ERROR expected `(` + llvm_asm!("mov %eax, $$0x2" : "={eax}"(struct)); //~ ERROR expected expression + llvm_asm!("in %dx, %al" : "={al}"(result) : struct); //~ ERROR expected string literal + llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); //~ ERROR expected `(` + llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); //~ ERROR expected expression + llvm_asm!("mov $$0x200, %eax" : : : struct); //~ ERROR expected string literal + llvm_asm!("mov eax, 2" : "={eax}"(foo) : : : struct); //~ ERROR expected string literal + llvm_asm!(123); //~ ERROR inline assembly must be a string literal +} diff --git a/src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr b/src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr new file mode 100644 index 0000000000000..715d05beaaee9 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr @@ -0,0 +1,68 @@ +error: macro requires a string literal as an argument + --> $DIR/llvm-asm-parse-errors.rs:5:5 + | +LL | llvm_asm!(); + | ^^^^^^^^^^^ string literal required + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:6:23 + | +LL | llvm_asm!("nop" : struct); + | ^^^^^^ not a string literal + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:7:35 + | +LL | llvm_asm!("mov %eax, $$0x2" : struct); + | ^^^^^^ not a string literal + +error: expected `(`, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:8:44 + | +LL | llvm_asm!("mov %eax, $$0x2" : "={eax}" struct); + | ^^^^^^ expected `(` + +error: expected expression, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:9:44 + | +LL | llvm_asm!("mov %eax, $$0x2" : "={eax}"(struct)); + | ^^^^^^ expected expression + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:10:49 + | +LL | llvm_asm!("in %dx, %al" : "={al}"(result) : struct); + | ^^^^^^ not a string literal + +error: expected `(`, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:11:56 + | +LL | llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); + | ^^^^^^ expected `(` + +error: expected expression, found keyword `struct` + --> $DIR/llvm-asm-parse-errors.rs:12:56 + | +LL | llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); + | ^^^^^^ expected expression + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:13:41 + | +LL | llvm_asm!("mov $$0x200, %eax" : : : struct); + | ^^^^^^ not a string literal + +error: expected string literal + --> $DIR/llvm-asm-parse-errors.rs:14:50 + | +LL | llvm_asm!("mov eax, 2" : "={eax}"(foo) : : : struct); + | ^^^^^^ not a string literal + +error: inline assembly must be a string literal + --> $DIR/llvm-asm-parse-errors.rs:15:15 + | +LL | llvm_asm!(123); + | ^^^ + +error: aborting due to 11 previous errors + diff --git a/src/test/ui/macros/macros-nonfatal-errors.rs b/src/test/ui/macros/macros-nonfatal-errors.rs index 98f64aa6f8025..3bab95083b6d9 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.rs +++ b/src/test/ui/macros/macros-nonfatal-errors.rs @@ -3,9 +3,11 @@ // test that errors in a (selection) of macros don't kill compilation // immediately, so that we get more errors listed at a time. +#![feature(llvm_asm)] #![feature(trace_macros, concat_idents)] #![feature(stmt_expr_attributes, arbitrary_enum_discriminant)] #![feature(derive_default_enum)] +#![allow(deprecated)] // llvm_asm! use std::arch::asm; diff --git a/src/test/ui/macros/macros-nonfatal-errors.stderr b/src/test/ui/macros/macros-nonfatal-errors.stderr index 79e8db9c1d429..9a360206e6eea 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.stderr +++ b/src/test/ui/macros/macros-nonfatal-errors.stderr @@ -1,41 +1,41 @@ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:14:5 + --> $DIR/macros-nonfatal-errors.rs:16:5 | LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:19:36 + --> $DIR/macros-nonfatal-errors.rs:21:36 | LL | struct DefaultInnerAttrTupleStruct(#[default] ()); | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:23:1 + --> $DIR/macros-nonfatal-errors.rs:25:1 | LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:27:1 + --> $DIR/macros-nonfatal-errors.rs:29:1 | LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:37:11 + --> $DIR/macros-nonfatal-errors.rs:39:11 | LL | Foo = #[default] 0, | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:38:14 + --> $DIR/macros-nonfatal-errors.rs:40:14 | LL | Bar([u8; #[default] 1]), | ^^^^^^^^^^ error: no default declared - --> $DIR/macros-nonfatal-errors.rs:43:10 + --> $DIR/macros-nonfatal-errors.rs:45:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -44,7 +44,7 @@ LL | #[derive(Default)] = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: multiple declared defaults - --> $DIR/macros-nonfatal-errors.rs:49:10 + --> $DIR/macros-nonfatal-errors.rs:51:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -62,7 +62,7 @@ LL | Baz, = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[default]` attribute does not accept a value - --> $DIR/macros-nonfatal-errors.rs:61:5 + --> $DIR/macros-nonfatal-errors.rs:63:5 | LL | #[default = 1] | ^^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ LL | #[default = 1] = help: try using `#[default]` error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:69:5 + --> $DIR/macros-nonfatal-errors.rs:71:5 | LL | #[default] | ---------- `#[default]` used here @@ -81,13 +81,13 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing this - --> $DIR/macros-nonfatal-errors.rs:68:5 + --> $DIR/macros-nonfatal-errors.rs:70:5 | LL | #[default] | ^^^^^^^^^^ error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:79:5 + --> $DIR/macros-nonfatal-errors.rs:81:5 | LL | #[default] | ---------- `#[default]` used here @@ -99,7 +99,7 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing these - --> $DIR/macros-nonfatal-errors.rs:76:5 + --> $DIR/macros-nonfatal-errors.rs:78:5 | LL | #[default] | ^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:86:5 + --> $DIR/macros-nonfatal-errors.rs:88:5 | LL | Foo {}, | ^^^ @@ -117,7 +117,7 @@ LL | Foo {}, = help: consider a manual implementation of `Default` error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:94:5 + --> $DIR/macros-nonfatal-errors.rs:96:5 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -127,37 +127,43 @@ LL | Foo, = help: consider a manual implementation of `Default` error: asm template must be a string literal - --> $DIR/macros-nonfatal-errors.rs:99:10 + --> $DIR/macros-nonfatal-errors.rs:101:10 | LL | asm!(invalid); | ^^^^^^^ +error: inline assembly must be a string literal + --> $DIR/macros-nonfatal-errors.rs:102:15 + | +LL | llvm_asm!(invalid); + | ^^^^^^^ + error: concat_idents! requires ident args - --> $DIR/macros-nonfatal-errors.rs:102:5 + --> $DIR/macros-nonfatal-errors.rs:104:5 | LL | concat_idents!("not", "idents"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:104:17 + --> $DIR/macros-nonfatal-errors.rs:106:17 | LL | option_env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:105:10 + --> $DIR/macros-nonfatal-errors.rs:107:10 | LL | env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:106:10 + --> $DIR/macros-nonfatal-errors.rs:108:10 | LL | env!(foo, abr, baz); | ^^^ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined - --> $DIR/macros-nonfatal-errors.rs:107:5 + --> $DIR/macros-nonfatal-errors.rs:109:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +171,7 @@ LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: format argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:109:13 + --> $DIR/macros-nonfatal-errors.rs:111:13 | LL | format!(invalid); | ^^^^^^^ @@ -176,19 +182,19 @@ LL | format!("{}", invalid); | +++++ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:111:14 + --> $DIR/macros-nonfatal-errors.rs:113:14 | LL | include!(invalid); | ^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:113:18 + --> $DIR/macros-nonfatal-errors.rs:115:18 | LL | include_str!(invalid); | ^^^^^^^ error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) - --> $DIR/macros-nonfatal-errors.rs:114:5 + --> $DIR/macros-nonfatal-errors.rs:116:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -196,13 +202,13 @@ LL | include_str!("i'd be quite surprised if a file with this name existed") = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:115:20 + --> $DIR/macros-nonfatal-errors.rs:117:20 | LL | include_bytes!(invalid); | ^^^^^^^ error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) - --> $DIR/macros-nonfatal-errors.rs:116:5 + --> $DIR/macros-nonfatal-errors.rs:118:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,16 +216,10 @@ LL | include_bytes!("i'd be quite surprised if a file with this name existed = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) error: trace_macros! accepts only `true` or `false` - --> $DIR/macros-nonfatal-errors.rs:118:5 + --> $DIR/macros-nonfatal-errors.rs:120:5 | LL | trace_macros!(invalid); | ^^^^^^^^^^^^^^^^^^^^^^ -error: cannot find macro `llvm_asm` in this scope - --> $DIR/macros-nonfatal-errors.rs:100:5 - | -LL | llvm_asm!(invalid); - | ^^^^^^^^ - error: aborting due to 27 previous errors diff --git a/src/test/ui/runtime/out-of-stack.rs b/src/test/ui/runtime/out-of-stack.rs index 73c31cd9721f6..ce02553051d24 100644 --- a/src/test/ui/runtime/out-of-stack.rs +++ b/src/test/ui/runtime/out-of-stack.rs @@ -2,11 +2,12 @@ #![allow(unused_must_use)] #![allow(unconditional_recursion)] +#![allow(deprecated)] // llvm_asm! // ignore-android: FIXME (#20004) // ignore-emscripten no processes // ignore-sgx no processes -#![feature(core_intrinsics)] +#![feature(llvm_asm)] #![feature(rustc_private)] #[cfg(unix)] @@ -16,10 +17,11 @@ use std::env; use std::process::Command; use std::thread; +// lifted from the test module // Inlining to avoid llvm turning the recursive functions into tail calls, // which doesn't consume stack. #[inline(always)] -pub fn black_box(dummy: T) { std::intrinsics::black_box(dummy); } +pub fn black_box(dummy: T) { unsafe { llvm_asm!("" : : "r"(&dummy)) } } fn silent_recurse() { let buf = [0u8; 1000]; diff --git a/src/test/ui/unsafe/inline_asm.mir.stderr b/src/test/ui/unsafe/inline_asm.mir.stderr index 633f1edb26cef..fee93dc070d5b 100644 --- a/src/test/ui/unsafe/inline_asm.mir.stderr +++ b/src/test/ui/unsafe/inline_asm.mir.stderr @@ -1,11 +1,20 @@ error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/inline_asm.rs:8:5 + --> $DIR/inline_asm.rs:11:5 | LL | asm!("nop"); | ^^^^^^^^^^^ use of inline assembly | = note: inline assembly is entirely unchecked and can cause undefined behavior -error: aborting due to previous error +error[E0133]: use of inline assembly is unsafe and requires unsafe function or block + --> $DIR/inline_asm.rs:12:5 + | +LL | llvm_asm!("nop"); + | ^^^^^^^^^^^^^^^^ use of inline assembly + | + = note: inline assembly is entirely unchecked and can cause undefined behavior + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/inline_asm.rs b/src/test/ui/unsafe/inline_asm.rs index 12c7efe4f50b6..7c1f86ac0e091 100644 --- a/src/test/ui/unsafe/inline_asm.rs +++ b/src/test/ui/unsafe/inline_asm.rs @@ -2,8 +2,12 @@ // [thir]compile-flags: -Z thir-unsafeck // needs-asm-support +#![feature(llvm_asm)] +#![allow(deprecated)] // llvm_asm! + use std::arch::asm; fn main() { asm!("nop"); //~ ERROR use of inline assembly is unsafe and requires unsafe function or block + llvm_asm!("nop"); //~ ERROR use of inline assembly is unsafe and requires unsafe function or block } diff --git a/src/test/ui/unsafe/inline_asm.thir.stderr b/src/test/ui/unsafe/inline_asm.thir.stderr index 633f1edb26cef..fee93dc070d5b 100644 --- a/src/test/ui/unsafe/inline_asm.thir.stderr +++ b/src/test/ui/unsafe/inline_asm.thir.stderr @@ -1,11 +1,20 @@ error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/inline_asm.rs:8:5 + --> $DIR/inline_asm.rs:11:5 | LL | asm!("nop"); | ^^^^^^^^^^^ use of inline assembly | = note: inline assembly is entirely unchecked and can cause undefined behavior -error: aborting due to previous error +error[E0133]: use of inline assembly is unsafe and requires unsafe function or block + --> $DIR/inline_asm.rs:12:5 + | +LL | llvm_asm!("nop"); + | ^^^^^^^^^^^^^^^^ use of inline assembly + | + = note: inline assembly is entirely unchecked and can cause undefined behavior + = note: this error originates in the macro `llvm_asm` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index fe3911983421b..8a5d526993ec8 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -508,6 +508,7 @@ fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId, | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) | ExprKind::Yield(..) => true, diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 1ae2e20c1e060..57697ac8457b6 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -498,7 +498,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.loops.pop(); }, ExprKind::Block(block, _) => self.visit_block(block), - ExprKind::InlineAsm(_) => { + ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => { self.can_use_entry = false; }, _ => { diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index a0b2302662e64..e00bda5eb4bd4 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -181,6 +181,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { ExprKind::Struct(_, _, None) | ExprKind::Yield(_, _) | ExprKind::Closure(_, _, _, _, _) + | ExprKind::LlvmInlineAsm(_) | ExprKind::Path(_) | ExprKind::ConstBlock(_) | ExprKind::Lit(_) diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index b5dd27ff80de4..1b7eec20ecc89 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -567,6 +567,7 @@ fn ident_difference_expr_with_base_location( | (Repeat(_, _), Repeat(_, _)) | (Struct(_), Struct(_)) | (MacCall(_), MacCall(_)) + | (LlvmInlineAsm(_), LlvmInlineAsm(_)) | (InlineAsm(_), InlineAsm(_)) | (Ret(_), Ret(_)) | (Continue(_), Continue(_)) diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index d23c85c033b2f..51e24f33f11c3 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -547,6 +547,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("InlineAsm(_)"); out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); }, + ExprKind::LlvmInlineAsm(_) => { + kind!("LlvmInlineAsm(_)"); + out!("// unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); + }, ExprKind::Struct(qpath, fields, base) => { bind!(self, qpath, fields); opt_bind!(self, base); diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index dc48ea3f4f99d..109fd49726840 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -301,6 +301,19 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { } } }, + hir::ExprKind::LlvmInlineAsm(asm) => { + let inputs = &asm.inputs_exprs; + let outputs = &asm.outputs_exprs; + println!("{}LlvmInlineAsm", ind); + println!("{}inputs:", ind); + for e in inputs.iter() { + print_expr(cx, e, indent + 1); + } + println!("{}outputs:", ind); + for e in outputs.iter() { + print_expr(cx, e, indent + 1); + } + }, hir::ExprKind::Struct(path, fields, ref base) => { println!("{}Struct", ind); println!("{}path: {:?}", ind, path); diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index a6ef6d79fc023..3b640e75437e7 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -175,6 +175,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS | ExprKind::Continue(_) | ExprKind::Ret(_) | ExprKind::InlineAsm(_) + | ExprKind::LlvmInlineAsm(_) | ExprKind::Yield(..) | ExprKind::Err => { self.eagerness = ForceNoChange; diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 00594f4d42add..7d67d680c5196 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -686,7 +686,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } self.hash_pat(pat); }, - ExprKind::Err => {}, + ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {}, ExprKind::Lit(ref l) => { l.node.hash(&mut self.s); }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 62e144398012d..4191c0af1912c 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -776,7 +776,8 @@ pub fn can_move_expr_to_closure_no_visit<'tcx>( | ExprKind::Continue(_) | ExprKind::Ret(_) | ExprKind::Yield(..) - | ExprKind::InlineAsm(_) => false, + | ExprKind::InlineAsm(_) + | ExprKind::LlvmInlineAsm(_) => false, // Accessing a field of a local value can only be done if the type isn't // partially moved. ExprKind::Field( diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 891531951c1a0..fb174f88a90a4 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -213,6 +213,8 @@ fn check_statement<'tcx>( // just an assignment StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), + StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => { check_operand(tcx, dst, span, body)?; check_operand(tcx, src, span, body)?; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 1fc9979f3dd7d..af4fb77163c12 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -147,6 +147,7 @@ impl<'a> Sugg<'a> { | hir::ExprKind::Field(..) | hir::ExprKind::Index(..) | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Lit(..) | hir::ExprKind::Loop(..) @@ -204,6 +205,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::ForLoop(..) | ast::ExprKind::Index(..) | ast::ExprKind::InlineAsm(..) + | ast::ExprKind::LlvmInlineAsm(..) | ast::ExprKind::ConstBlock(..) | ast::ExprKind::Lit(..) | ast::ExprKind::Loop(..) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 4f333cd27cefe..b84757cb2eecf 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -334,7 +334,9 @@ pub(crate) fn format_expr( // satisfy our width restrictions. // Style Guide RFC for InlineAsm variant pending // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 - ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), + ast::ExprKind::LlvmInlineAsm(..) | ast::ExprKind::InlineAsm(..) => { + Some(context.snippet(expr.span).to_owned()) + } ast::ExprKind::TryBlock(ref block) => { if let rw @ Some(_) = rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 35512e78fa6e2..75e3780117285 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -507,6 +507,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Err | ast::ExprKind::Field(..) | ast::ExprKind::InlineAsm(..) + | ast::ExprKind::LlvmInlineAsm(..) | ast::ExprKind::Let(..) | ast::ExprKind::Path(..) | ast::ExprKind::Range(..)