about summary refs log tree commit diff
path: root/src/librustc_codegen_llvm/asm.rs
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-05-30 23:08:44 +0200
committerGitHub <noreply@github.com>2020-05-30 23:08:44 +0200
commitfadfcb644e3e549b5b080260d4ca4ea2696e7007 (patch)
tree11b724345164340def6c91133e43f1a2b21659a4 /src/librustc_codegen_llvm/asm.rs
parentf1661d23e386ecd2d2ebb7990fa4cb459b2896f6 (diff)
parentfc497f79b3a60e23da08680d66e6cfc00a716bcc (diff)
downloadrust-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
  |              ^^^^^^^^^^^^^^^^^^^
```
Diffstat (limited to 'src/librustc_codegen_llvm/asm.rs')
-rw-r--r--src/librustc_codegen_llvm/asm.rs32
1 files changed, 24 insertions, 8 deletions
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 {