about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libfmt_macros/lib.rs24
-rw-r--r--src/librustc_ast/ast.rs3
-rw-r--r--src/librustc_ast_lowering/expr.rs3
-rw-r--r--src/librustc_builtin_macros/asm.rs10
-rw-r--r--src/librustc_codegen_llvm/asm.rs32
-rw-r--r--src/librustc_codegen_llvm/back/write.rs51
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs9
-rw-r--r--src/librustc_codegen_ssa/back/write.rs39
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs12
-rw-r--r--src/librustc_codegen_ssa/traits/asm.rs2
-rw-r--r--src/librustc_hir/hir.rs1
-rw-r--r--src/librustc_middle/arena.rs2
-rw-r--r--src/librustc_middle/mir/mod.rs6
-rw-r--r--src/librustc_middle/mir/type_foldable.rs10
-rw-r--r--src/librustc_middle/mir/visit.rs1
-rw-r--r--src/librustc_mir/borrow_check/invalidation.rs8
-rw-r--r--src/librustc_mir/borrow_check/mod.rs8
-rw-r--r--src/librustc_mir/dataflow/framework/direction.rs2
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs8
-rw-r--r--src/librustc_mir_build/build/expr/into.rs3
-rw-r--r--src/librustc_mir_build/hair/cx/expr.rs1
-rw-r--r--src/librustc_mir_build/hair/mod.rs1
-rw-r--r--src/librustc_span/lib.rs12
-rw-r--r--src/rustllvm/RustWrapper.cpp31
-rw-r--r--src/test/ui/asm/srcloc.rs41
-rw-r--r--src/test/ui/asm/srcloc.stderr74
-rw-r--r--src/test/ui/issues/issue-23458.stderr15
-rw-r--r--src/test/ui/llvm-asm/issue-69092.rs2
-rw-r--r--src/test/ui/llvm-asm/issue-69092.stderr13
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