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
  |              ^^^^^^^^^^^^^^^^^^^
```
This commit is contained in:
Ralf Jung 2020-05-30 23:08:44 +02:00 committed by GitHub
commit fadfcb644e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 367 additions and 57 deletions

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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))
}

View File

@ -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,

View File

@ -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 {

View File

@ -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");
report_inline_asm(cgcx, &msg, cookie);
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, 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) => {

View File

@ -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,

View File

@ -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();

View File

@ -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,
);
}

View File

@ -52,7 +52,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes {
template: &[InlineAsmTemplatePiece],
operands: &[InlineAsmOperandRef<'tcx, Self>],
options: InlineAsmOptions,
span: Span,
line_spans: &[Span],
);
}

View File

@ -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)]

View File

@ -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);
)
}

View File

@ -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, ", ")?;

View File

@ -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 }
}

View File

@ -535,6 +535,7 @@ macro_rules! make_mir_visitor {
template: _,
operands,
options: _,
line_spans: _,
destination: _,
} => {
for op in operands {

View File

@ -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 }

View File

@ -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 }

View File

@ -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);
}

View File

@ -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 }

View File

@ -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 {

View File

@ -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 {

View File

@ -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,

View File

@ -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

View File

@ -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,

41
src/test/ui/asm/srcloc.rs Normal file
View File

@ -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'
}
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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