diff options
| author | Ralf Jung <post@ralfj.de> | 2020-05-30 23:08:44 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-30 23:08:44 +0200 |
| commit | fadfcb644e3e549b5b080260d4ca4ea2696e7007 (patch) | |
| tree | 11b724345164340def6c91133e43f1a2b21659a4 | |
| parent | f1661d23e386ecd2d2ebb7990fa4cb459b2896f6 (diff) | |
| parent | fc497f79b3a60e23da08680d66e6cfc00a716bcc (diff) | |
| download | rust-fadfcb644e3e549b5b080260d4ca4ea2696e7007.tar.gz rust-fadfcb644e3e549b5b080260d4ca4ea2696e7007.zip | |
Rollup merge of #72625 - Amanieu:asm-srcloc, r=petrochenkov
Improve inline asm error diagnostics
Previously we were just using the raw LLVM error output (with line, caret, etc) as the diagnostic message, which ends up looking rather out of place with our existing diagnostics.
The new diagnostics properly format the diagnostics and also take advantage of LLVM's per-line `srcloc` attribute to map an error in inline assembly directly to the relevant line of source code.
Incidentally also fixes #71639 by disabling `srcloc` metadata during LTO builds since we don't know what crate it might have come from. We can only resolve `srcloc`s from the currently crate since it indexes into the source map for the current crate.
Fixes #72664
Fixes #71639
r? @petrochenkov
### Old style
```rust
#![feature(llvm_asm)]
fn main() {
unsafe {
let _x: i32;
llvm_asm!(
"mov $0, $1
invalid_instruction $0, $1
mov $0, $1"
: "=&r" (_x)
: "r" (0)
:: "intel"
);
}
}
```
```
error: <inline asm>:3:14: error: invalid instruction mnemonic 'invalid_instruction'
invalid_instruction ecx, eax
^~~~~~~~~~~~~~~~~~~
--> src/main.rs:6:9
|
6 | / llvm_asm!(
7 | | "mov $0, $1
8 | | invalid_instruction $0, $1
9 | | mov $0, $1"
... |
12 | | :: "intel"
13 | | );
| |__________^
```
### New style
```rust
#![feature(asm)]
fn main() {
unsafe {
asm!(
"mov {0}, {1}
invalid_instruction {0}, {1}
mov {0}, {1}",
out(reg) _,
in(reg) 0i64,
);
}
}
```
```
error: invalid instruction mnemonic 'invalid_instruction'
--> test.rs:7:14
|
7 | invalid_instruction {0}, {1}
| ^
|
note: instantiated into assembly here
--> <inline asm>:3:14
|
3 | invalid_instruction rax, rcx
| ^^^^^^^^^^^^^^^^^^^
```
29 files changed, 367 insertions, 57 deletions
diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 677c027f17b..23bf7b35419 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -191,6 +191,11 @@ pub struct Parser<'a> { append_newline: bool, /// Whether this formatting string is a literal or it comes from a macro. is_literal: bool, + /// Start position of the current line. + cur_line_start: usize, + /// Start and end byte offset of every line of the format string. Excludes + /// newline characters and leading whitespace. + pub line_spans: Vec<InnerSpan>, } impl<'a> Iterator for Parser<'a> { @@ -235,10 +240,15 @@ impl<'a> Iterator for Parser<'a> { None } } - '\n' => Some(String(self.string(pos))), _ => Some(String(self.string(pos))), } } else { + if self.is_literal && self.cur_line_start != self.input.len() { + let start = self.to_span_index(self.cur_line_start); + let end = self.to_span_index(self.input.len()); + self.line_spans.push(start.to(end)); + self.cur_line_start = self.input.len(); + } None } } @@ -266,6 +276,8 @@ impl<'a> Parser<'a> { last_opening_brace: None, append_newline, is_literal, + cur_line_start: 0, + line_spans: vec![], } } @@ -433,7 +445,17 @@ impl<'a> Parser<'a> { '{' | '}' => { return &self.input[start..pos]; } + '\n' if self.is_literal => { + let start = self.to_span_index(self.cur_line_start); + let end = self.to_span_index(pos); + self.line_spans.push(start.to(end)); + self.cur_line_start = pos + 1; + self.cur.next(); + } _ => { + if self.is_literal && pos == self.cur_line_start && c.is_whitespace() { + self.cur_line_start = pos + c.len_utf8(); + } self.cur.next(); } } diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 30bb5c0bffa..efcf95ec706 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -1252,7 +1252,7 @@ pub enum ExprKind { Ret(Option<P<Expr>>), /// Output of the `asm!()` macro. - InlineAsm(InlineAsm), + InlineAsm(P<InlineAsm>), /// Output of the `llvm_asm!()` macro. LlvmInlineAsm(P<LlvmInlineAsm>), @@ -1971,6 +1971,7 @@ pub struct InlineAsm { pub template: Vec<InlineAsmTemplatePiece>, pub operands: Vec<(InlineAsmOperand, Span)>, pub options: InlineAsmOptions, + pub line_spans: Vec<Span>, } /// Inline assembly dialect. diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs index b7f2e9a9050..c9037da377e 100644 --- a/src/librustc_ast_lowering/expr.rs +++ b/src/librustc_ast_lowering/expr.rs @@ -1267,7 +1267,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let operands = self.arena.alloc_from_iter(operands); let template = self.arena.alloc_from_iter(asm.template.iter().cloned()); - let hir_asm = hir::InlineAsm { template, operands, options: asm.options }; + let line_spans = self.arena.alloc_slice(&asm.line_spans[..]); + let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans }; hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm)) } diff --git a/src/librustc_builtin_macros/asm.rs b/src/librustc_builtin_macros/asm.rs index 224b52b239f..19fae635572 100644 --- a/src/librustc_builtin_macros/asm.rs +++ b/src/librustc_builtin_macros/asm.rs @@ -513,10 +513,16 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast } } - let inline_asm = ast::InlineAsm { template, operands, options: args.options }; + let line_spans = if parser.line_spans.is_empty() { + vec![template_sp] + } else { + parser.line_spans.iter().map(|span| template_span.from_inner(*span)).collect() + }; + + let inline_asm = ast::InlineAsm { template, operands, options: args.options, line_spans }; P(ast::Expr { id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(inline_asm), + kind: ast::ExprKind::InlineAsm(P(inline_asm)), span: sp, attrs: ast::AttrVec::new(), tokens: None, diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index f3b46dd322a..9d4a30f23a2 100644 --- a/src/librustc_codegen_llvm/asm.rs +++ b/src/librustc_codegen_llvm/asm.rs @@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_span::Span; +use rustc_span::{Pos, Span}; use rustc_target::abi::*; use rustc_target::asm::*; @@ -97,7 +97,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { ia.volatile, ia.alignstack, ia.dialect, - span, + &[span], ); if r.is_none() { return false; @@ -119,7 +119,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, - span: Span, + line_spans: &[Span], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -287,9 +287,9 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { volatile, alignstack, dialect, - span, + line_spans, ) - .unwrap_or_else(|| span_bug!(span, "LLVM asm constraint validation failed")); + .unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed")); if options.contains(InlineAsmOptions::PURE) { if options.contains(InlineAsmOptions::NOMEM) { @@ -341,7 +341,7 @@ fn inline_asm_call( volatile: bool, alignstack: bool, dia: LlvmAsmDialect, - span: Span, + line_spans: &[Span], ) -> Option<&'ll Value> { let volatile = if volatile { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False }; @@ -382,8 +382,24 @@ fn inline_asm_call( key.len() as c_uint, ); - let val: &'ll Value = bx.const_i32(span.ctxt().outer_expn().as_u32() as i32); - llvm::LLVMSetMetadata(call, kind, llvm::LLVMMDNodeInContext(bx.llcx, &val, 1)); + // srcloc contains one integer for each line of assembly code. + // Unfortunately this isn't enough to encode a full span so instead + // 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 == LlvmAsmDialect::Intel && line_spans.len() > 1 { + // LLVM inserts an extra line to add the ".intel_syntax", so add + // a dummy srcloc entry for it. + // + // Don't do this if we only have 1 line span since that may be + // due to the asm template string coming from a macro. LLVM will + // default to the first srcloc for lines that don't have an + // associated srcloc. + srcloc.push(bx.const_i32(0)); + } + srcloc.extend(line_spans.iter().map(|span| bx.const_i32(span.lo().to_u32() as i32))); + let md = llvm::LLVMMDNodeInContext(bx.llcx, srcloc.as_ptr(), srcloc.len() as u32); + llvm::LLVMSetMetadata(call, kind, md); Some(call) } else { diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 57e018bba6a..02a9294930d 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -23,6 +23,7 @@ use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath}; use rustc_session::Session; +use rustc_span::InnerSpan; use rustc_target::spec::{CodeModel, RelocModel}; use libc::{c_char, c_int, c_uint, c_void, size_t}; @@ -238,12 +239,19 @@ impl<'a> Drop for DiagnosticHandlers<'a> { } } -unsafe extern "C" fn report_inline_asm( +fn report_inline_asm( cgcx: &CodegenContext<LlvmCodegenBackend>, - msg: &str, - cookie: c_uint, + msg: String, + mut cookie: c_uint, + source: Option<(String, Vec<InnerSpan>)>, ) { - cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_owned()); + // In LTO build we may get srcloc values from other crates which are invalid + // since they use a different source map. To be safe we just suppress these + // in LTO builds. + if matches!(cgcx.lto, Lto::Fat | Lto::Thin) { + cookie = 0; + } + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, source); } unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void, cookie: c_uint) { @@ -252,10 +260,37 @@ unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void } let (cgcx, _) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler)); - let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) - .expect("non-UTF8 SMDiagnostic"); + // Recover the post-substitution assembly code from LLVM for better + // diagnostics. + let mut have_source = false; + let mut buffer = String::new(); + let mut loc = 0; + let mut ranges = [0; 8]; + let mut num_ranges = ranges.len() / 2; + let msg = llvm::build_string(|msg| { + buffer = llvm::build_string(|buffer| { + have_source = llvm::LLVMRustUnpackSMDiagnostic( + diag, + msg, + buffer, + &mut loc, + ranges.as_mut_ptr(), + &mut num_ranges, + ); + }) + .expect("non-UTF8 inline asm"); + }) + .expect("non-UTF8 SMDiagnostic"); + + let source = have_source.then(|| { + let mut spans = vec![InnerSpan::new(loc as usize, loc as usize)]; + for i in 0..num_ranges { + spans.push(InnerSpan::new(ranges[i * 2] as usize, ranges[i * 2 + 1] as usize)); + } + (buffer, spans) + }); - report_inline_asm(cgcx, &msg, cookie); + report_inline_asm(cgcx, msg, cookie, source); } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { @@ -266,7 +301,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { - report_inline_asm(cgcx, &llvm::twine_to_string(inline.message), inline.cookie); + report_inline_asm(cgcx, llvm::twine_to_string(inline.message), inline.cookie, None); } llvm::diagnostic::Optimization(opt) => { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 3fb7ff3cb8d..759c2bf1b85 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -2070,7 +2070,14 @@ extern "C" { ); #[allow(improper_ctypes)] - pub fn LLVMRustWriteSMDiagnosticToString(d: &SMDiagnostic, s: &RustString); + pub fn LLVMRustUnpackSMDiagnostic( + d: &SMDiagnostic, + message_out: &RustString, + buffer_out: &RustString, + loc_out: &mut c_uint, + ranges_out: *mut c_uint, + num_ranges: &mut usize, + ) -> bool; pub fn LLVMRustWriteArchive( Dst: *const c_char, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index 9e03c283cfb..cb5c95c11fa 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -31,9 +31,9 @@ use rustc_session::cgu_reuse_tracker::CguReuseTracker; use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; use rustc_session::config::{Passes, Sanitizer, SwitchWithOptPath}; use rustc_session::Session; -use rustc_span::hygiene::ExpnId; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::any::Any; @@ -1551,7 +1551,7 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B> enum SharedEmitterMessage { Diagnostic(Diagnostic), - InlineAsmError(u32, String), + InlineAsmError(u32, String, Option<(String, Vec<InnerSpan>)>), AbortIfErrors, Fatal(String), } @@ -1572,8 +1572,13 @@ impl SharedEmitter { (SharedEmitter { sender }, SharedEmitterMain { receiver }) } - pub fn inline_asm_error(&self, cookie: u32, msg: String) { - drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); + pub fn inline_asm_error( + &self, + cookie: u32, + msg: String, + source: Option<(String, Vec<InnerSpan>)>, + ) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, source))); } pub fn fatal(&self, msg: &str) { @@ -1626,8 +1631,30 @@ impl SharedEmitterMain { } handler.emit_diagnostic(&d); } - Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { - sess.span_err(ExpnId::from_u32(cookie).expn_data().call_site, &msg) + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, source)) => { + let msg = msg.strip_prefix("error: ").unwrap_or(&msg); + + // If the cookie is 0 then we don't have span information. + let mut err = if cookie == 0 { + sess.struct_err(&msg) + } else { + let pos = BytePos::from_u32(cookie); + let span = Span::with_root_ctxt(pos, pos); + sess.struct_span_err(span, &msg) + }; + + // Point to the generated assembly if it is available. + if let Some((buffer, spans)) = source { + let source = sess + .source_map() + .new_source_file(FileName::inline_asm_source_code(&buffer), buffer); + let source_span = Span::with_root_ctxt(source.start_pos, source.end_pos); + let spans: Vec<_> = + spans.iter().map(|sp| source_span.from_inner(*sp)).collect(); + err.span_note(spans, "instantiated into assembly here"); + } + + err.emit(); } Ok(SharedEmitterMessage::AbortIfErrors) => { sess.abort_if_errors(); diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 2a6d1abba9e..6c18e753d79 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -831,6 +831,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { template: &[ast::InlineAsmTemplatePiece], operands: &[mir::InlineAsmOperand<'tcx>], options: ast::InlineAsmOptions, + line_spans: &[Span], destination: Option<mir::BasicBlock>, ) { let span = terminator.source_info.span; @@ -930,7 +931,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) .collect(); - bx.codegen_inline_asm(template, &operands, options, span); + bx.codegen_inline_asm(template, &operands, options, line_spans); if let Some(target) = destination { helper.funclet_br(self, &mut bx, target); @@ -1033,7 +1034,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("borrowck false edges in codegen") } - mir::TerminatorKind::InlineAsm { template, ref operands, options, destination } => { + mir::TerminatorKind::InlineAsm { + template, + ref operands, + options, + line_spans, + destination, + } => { self.codegen_asm_terminator( helper, bx, @@ -1041,6 +1048,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { template, operands, options, + line_spans, destination, ); } diff --git a/src/librustc_codegen_ssa/traits/asm.rs b/src/librustc_codegen_ssa/traits/asm.rs index 0abfdfde780..b6b57744f95 100644 --- a/src/librustc_codegen_ssa/traits/asm.rs +++ b/src/librustc_codegen_ssa/traits/asm.rs @@ -52,7 +52,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, - span: Span, + line_spans: &[Span], ); } diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 35cff668581..0194dc9f90b 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2106,6 +2106,7 @@ pub struct InlineAsm<'hir> { pub template: &'hir [InlineAsmTemplatePiece], pub operands: &'hir [InlineAsmOperand<'hir>], pub options: InlineAsmOptions, + pub line_spans: &'hir [Span], } #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic, PartialEq)] diff --git a/src/librustc_middle/arena.rs b/src/librustc_middle/arena.rs index 9b9207312e8..d5212ec5c30 100644 --- a/src/librustc_middle/arena.rs +++ b/src/librustc_middle/arena.rs @@ -82,6 +82,8 @@ macro_rules! arena_types { // (during lowering) and the `librustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::ast::InlineAsmTemplatePiece, + // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. + [decode] span: rustc_span::Span, ], $tcx); ) } diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 0e9c133e2c3..b54b09918ab 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -1194,6 +1194,10 @@ pub enum TerminatorKind<'tcx> { /// Miscellaneous options for the inline assembly. options: InlineAsmOptions, + /// Source spans for each line of the inline assembly code. These are + /// used to map assembler errors back to the line in the source code. + line_spans: &'tcx [Span], + /// Destination block after the inline assembly returns, unless it is /// diverging (InlineAsmOptions::NORETURN). destination: Option<BasicBlock>, @@ -1596,7 +1600,7 @@ impl<'tcx> TerminatorKind<'tcx> { } FalseEdges { .. } => write!(fmt, "falseEdges"), FalseUnwind { .. } => write!(fmt, "falseUnwind"), - InlineAsm { template, ref operands, options, destination: _ } => { + InlineAsm { template, ref operands, options, .. } => { write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?; for op in operands { write!(fmt, ", ")?; diff --git a/src/librustc_middle/mir/type_foldable.rs b/src/librustc_middle/mir/type_foldable.rs index bb7001c1207..b0207b469fa 100644 --- a/src/librustc_middle/mir/type_foldable.rs +++ b/src/librustc_middle/mir/type_foldable.rs @@ -78,9 +78,13 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { FalseEdges { real_target, imaginary_target } } FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, - InlineAsm { template, ref operands, options, destination } => { - InlineAsm { template, operands: operands.fold_with(folder), options, destination } - } + InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm { + template, + operands: operands.fold_with(folder), + options, + line_spans, + destination, + }, }; Terminator { source_info: self.source_info, kind } } diff --git a/src/librustc_middle/mir/visit.rs b/src/librustc_middle/mir/visit.rs index a29b7b75294..035e6e55a97 100644 --- a/src/librustc_middle/mir/visit.rs +++ b/src/librustc_middle/mir/visit.rs @@ -535,6 +535,7 @@ macro_rules! make_mir_visitor { template: _, operands, options: _, + line_spans: _, destination: _, } => { for op in operands { diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs index 178e3db17cd..0b59e29b66c 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/src/librustc_mir/borrow_check/invalidation.rs @@ -183,7 +183,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } } - TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _, + } => { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index e3098fc1cfc..268fbfcf395 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -722,7 +722,13 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); } - TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _, + } => { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } diff --git a/src/librustc_mir/dataflow/framework/direction.rs b/src/librustc_mir/dataflow/framework/direction.rs index 97b14ea771b..9e2a28853e1 100644 --- a/src/librustc_mir/dataflow/framework/direction.rs +++ b/src/librustc_mir/dataflow/framework/direction.rs @@ -482,7 +482,7 @@ impl Direction for Forward { } } - InlineAsm { template: _, operands: _, options: _, destination } => { + InlineAsm { template: _, operands: _, options: _, line_spans: _, destination } => { if let Some(target) = destination { propagate(target, exit_state); } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 427ab1ca5cd..e35d853c928 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -411,7 +411,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); } } - TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => { + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _ + } => { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } diff --git a/src/librustc_mir_build/build/expr/into.rs b/src/librustc_mir_build/build/expr/into.rs index ff3c7ee3ee8..e7733deee4d 100644 --- a/src/librustc_mir_build/build/expr/into.rs +++ b/src/librustc_mir_build/build/expr/into.rs @@ -310,7 +310,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } - ExprKind::InlineAsm { template, operands, options } => { + ExprKind::InlineAsm { template, operands, options, line_spans } => { use crate::hair; use rustc_middle::mir; let operands = operands @@ -368,6 +368,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { template, operands, options, + line_spans, destination: if options.contains(InlineAsmOptions::NORETURN) { None } else { diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index fd3c48b38ba..969bb4f7259 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -513,6 +513,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( }) .collect(), options: asm.options, + line_spans: asm.line_spans, }, hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { diff --git a/src/librustc_mir_build/hair/mod.rs b/src/librustc_mir_build/hair/mod.rs index aba7a7a1b42..0a1c68e83a9 100644 --- a/src/librustc_mir_build/hair/mod.rs +++ b/src/librustc_mir_build/hair/mod.rs @@ -283,6 +283,7 @@ crate enum ExprKind<'tcx> { template: &'tcx [InlineAsmTemplatePiece], operands: Vec<InlineAsmOperand<'tcx>>, options: InlineAsmOptions, + line_spans: &'tcx [Span], }, LlvmInlineAsm { asm: &'tcx hir::LlvmInlineAsmInner, diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs index 58cdb87158a..616876d4b02 100644 --- a/src/librustc_span/lib.rs +++ b/src/librustc_span/lib.rs @@ -101,6 +101,8 @@ pub enum FileName { /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), DocTest(PathBuf, isize), + /// Post-substitution inline assembly from LLVM + InlineAsm(u64), } impl std::fmt::Display for FileName { @@ -116,6 +118,7 @@ impl std::fmt::Display for FileName { CliCrateAttr(_) => write!(fmt, "<crate attribute>"), Custom(ref s) => write!(fmt, "<{}>", s), DocTest(ref path, _) => write!(fmt, "{}", path.display()), + InlineAsm(_) => write!(fmt, "<inline asm>"), } } } @@ -139,7 +142,8 @@ impl FileName { | CliCrateAttr(_) | Custom(_) | QuoteExpansion(_) - | DocTest(_, _) => false, + | DocTest(_, _) + | InlineAsm(_) => false, } } @@ -182,6 +186,12 @@ impl FileName { pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { FileName::DocTest(path, line) } + + pub fn inline_asm_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::InlineAsm(hasher.finish()) + } } /// Spans represent a region of code, used for error reporting. Positions in spans diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 24f35627d10..6fac2662506 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -1216,10 +1216,33 @@ extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); } -extern "C" void LLVMRustWriteSMDiagnosticToString(LLVMSMDiagnosticRef D, - RustStringRef Str) { - RawRustStringOstream OS(Str); - unwrap(D)->print("", OS); +extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, + RustStringRef MessageOut, + RustStringRef BufferOut, + unsigned* LocOut, + unsigned* RangesOut, + size_t* NumRanges) { + SMDiagnostic& D = *unwrap(DRef); + RawRustStringOstream MessageOS(MessageOut); + MessageOS << D.getMessage(); + + if (D.getLoc() == SMLoc()) + return false; + + const SourceMgr &LSM = *D.getSourceMgr(); + const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); + + *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); + + *NumRanges = std::min(*NumRanges, D.getRanges().size()); + size_t LineStart = *LocOut - (size_t)D.getColumnNo(); + for (size_t i = 0; i < *NumRanges; i++) { + RangesOut[i * 2] = LineStart + D.getRanges()[i].first; + RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; + } + + return true; } extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, diff --git a/src/test/ui/asm/srcloc.rs b/src/test/ui/asm/srcloc.rs new file mode 100644 index 00000000000..7af6f620a98 --- /dev/null +++ b/src/test/ui/asm/srcloc.rs @@ -0,0 +1,41 @@ +// no-system-llvm +// only-x86_64 +// build-fail + +#![feature(asm)] + +// Checks that inline asm errors are mapped to the correct line in the source code. + +fn main() { + unsafe { + asm!("invalid_instruction"); + //~^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(" + invalid_instruction + "); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(r#" + invalid_instruction + "#); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(" + mov eax, eax + invalid_instruction + mov eax, eax + "); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(r#" + mov eax, eax + invalid_instruction + mov eax, eax + "#); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(concat!("invalid", "_", "instruction")); + //~^ ERROR: invalid instruction mnemonic 'invalid_instruction' + } +} diff --git a/src/test/ui/asm/srcloc.stderr b/src/test/ui/asm/srcloc.stderr new file mode 100644 index 00000000000..57a4fbb9742 --- /dev/null +++ b/src/test/ui/asm/srcloc.stderr @@ -0,0 +1,74 @@ +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:11:15 + | +LL | asm!("invalid_instruction"); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:15:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> <inline asm>:3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:20:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> <inline asm>:3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:26:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> <inline asm>:4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:33:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> <inline asm>:4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:38:14 + | +LL | asm!(concat!("invalid", "_", "instruction")); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/issues/issue-23458.stderr b/src/test/ui/issues/issue-23458.stderr index 81f06e63975..a6500b9bb4c 100644 --- a/src/test/ui/issues/issue-23458.stderr +++ b/src/test/ui/issues/issue-23458.stderr @@ -2,16 +2,19 @@ error: invalid operand in inline asm: 'int $3' --> $DIR/issue-23458.rs:8:9 | LL | llvm_asm!("int $3"); - | ^^^^^^^^^^^^^^^^^^^^ - -error: <inline asm>:1:2: error: too few operands for instruction - int - ^ + | ^ +error: too few operands for instruction --> $DIR/issue-23458.rs:8:9 | LL | llvm_asm!("int $3"); - | ^^^^^^^^^^^^^^^^^^^^ + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:2 + | +LL | int + | ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/llvm-asm/issue-69092.rs b/src/test/ui/llvm-asm/issue-69092.rs index ecce7bfdf5b..96c019b760e 100644 --- a/src/test/ui/llvm-asm/issue-69092.rs +++ b/src/test/ui/llvm-asm/issue-69092.rs @@ -6,5 +6,5 @@ fn main() { unsafe { llvm_asm!(".ascii \"Xen\0\""); } - //~^ ERROR: <inline asm>:1:9: error: expected string in '.ascii' directive + //~^ ERROR: expected string in '.ascii' directive } diff --git a/src/test/ui/llvm-asm/issue-69092.stderr b/src/test/ui/llvm-asm/issue-69092.stderr index 35f77edc3c4..2ca86cf7c1b 100644 --- a/src/test/ui/llvm-asm/issue-69092.stderr +++ b/src/test/ui/llvm-asm/issue-69092.stderr @@ -1,11 +1,14 @@ -error: <inline asm>:1:9: error: expected string in '.ascii' directive - .ascii "Xen - ^ - +error: expected string in '.ascii' directive --> $DIR/issue-69092.rs:8:14 | LL | unsafe { llvm_asm!(".ascii \"Xen\0\""); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:9 + | +LL | .ascii "Xen + | ^ error: aborting due to previous error |
