Skip to content

Translate inline assembly errors back to source locations #17592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 29, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mk/crates.mk
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ DEPS_graphviz := std
DEPS_green := std native:context_switch
DEPS_rustuv := std native:uv native:uv_support
DEPS_native := std
DEPS_syntax := std term serialize log fmt_macros debug arena
DEPS_syntax := std term serialize log fmt_macros debug arena libc
DEPS_rustc := syntax flate arena serialize getopts rbml \
time log graphviz debug rustc_llvm rustc_back
DEPS_rustc_llvm := native:rustllvm libc std
Expand Down
43 changes: 36 additions & 7 deletions src/librustc/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use driver::session::Session;
use driver::config;
use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef};
use llvm::SMDiagnosticRef;
use util::common::time;
use syntax::abi;
use syntax::codemap;
Expand Down Expand Up @@ -326,14 +327,40 @@ impl<'a> CodegenContext<'a> {
}
}

struct DiagHandlerFreeVars<'a> {
struct HandlerFreeVars<'a> {
llcx: ContextRef,
cgcx: &'a CodegenContext<'a>,
}

unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef,
user: *const c_void,
cookie: c_uint) {
use syntax::codemap::ExpnId;

let HandlerFreeVars { cgcx, .. }
= *mem::transmute::<_, *const HandlerFreeVars>(user);

let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s))
.expect("non-UTF8 SMDiagnostic");

match cgcx.lto_ctxt {
Some((sess, _)) => {
sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info {
Some(ei) => sess.span_err(ei.call_site, msg.as_slice()),
None => sess.err(msg.as_slice()),
});
}

None => {
cgcx.handler.err(msg.as_slice());
cgcx.handler.note("build without -C codegen-units for more exact errors");
}
}
}

unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) {
let DiagHandlerFreeVars { llcx, cgcx }
= *mem::transmute::<_, *const DiagHandlerFreeVars>(user);
let HandlerFreeVars { llcx, cgcx }
= *mem::transmute::<_, *const HandlerFreeVars>(user);

match llvm::diagnostic::Diagnostic::unpack(info) {
llvm::diagnostic::Optimization(opt) => {
Expand Down Expand Up @@ -368,14 +395,16 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
let tm = config.tm;

// llcx doesn't outlive this function, so we can put this on the stack.
let fv = DiagHandlerFreeVars {
let fv = HandlerFreeVars {
llcx: llcx,
cgcx: cgcx,
};
let fv = &fv as *const HandlerFreeVars as *mut c_void;

llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv);

if !cgcx.remark.is_empty() {
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler,
&fv as *const DiagHandlerFreeVars
as *mut c_void);
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
}

if config.emit_no_opt_bc {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
sess.opts.output_types.as_slice(),
outputs));
}

sess.abort_if_errors();
}

/// Run the linker on any artifacts that resulted from the LLVM run.
Expand Down
14 changes: 14 additions & 0 deletions src/librustc/middle/trans/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use middle::trans::type_::Type;
use std::c_str::ToCStr;
use std::string::String;
use syntax::ast;
use libc::{c_uint, c_char};

// Take an inline assembly expression and splat it out via LLVM
pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
Expand Down Expand Up @@ -141,6 +142,19 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
}
}

// Store expn_id in a metadata node so we can map LLVM errors
// back to source locations. See #17552.
unsafe {
let key = "srcloc";
let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx().llcx(),
key.as_ptr() as *const c_char, key.len() as c_uint);

let val: llvm::ValueRef = C_i32(bcx.ccx(), ia.expn_id.to_llvm_cookie());

llvm::LLVMSetMetadata(r, kind,
llvm::LLVMMDNodeInContext(bcx.ccx().llcx(), &val, 1));
}

return bcx;

}
Expand Down
9 changes: 9 additions & 0 deletions src/librustc_llvm/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,11 @@ pub enum DiagnosticInfo_opaque {}
pub type DiagnosticInfoRef = *mut DiagnosticInfo_opaque;
pub enum DebugLoc_opaque {}
pub type DebugLocRef = *mut DebugLoc_opaque;
pub enum SMDiagnostic_opaque {}
pub type SMDiagnosticRef = *mut SMDiagnostic_opaque;

pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);

pub mod debuginfo {
use super::{ValueRef};
Expand Down Expand Up @@ -1967,6 +1970,12 @@ extern {
pub fn LLVMGetDiagInfoKind(DI: DiagnosticInfoRef) -> DiagnosticKind;

pub fn LLVMWriteDebugLocToString(C: ContextRef, DL: DebugLocRef, s: RustStringRef);

pub fn LLVMSetInlineAsmDiagnosticHandler(C: ContextRef,
H: InlineAsmDiagHandler,
CX: *mut c_void);

pub fn LLVMWriteSMDiagnosticToString(d: SMDiagnosticRef, s: RustStringRef);
}

pub fn SetInstructionCallConv(instr: ValueRef, cc: CallConv) {
Expand Down
5 changes: 3 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

// The Rust abstract syntax tree.

use codemap::{Span, Spanned, DUMMY_SP};
use codemap::{Span, Spanned, DUMMY_SP, ExpnId};
use abi::Abi;
use ast_util;
use owned_slice::OwnedSlice;
Expand Down Expand Up @@ -983,7 +983,8 @@ pub struct InlineAsm {
pub clobbers: InternedString,
pub volatile: bool,
pub alignstack: bool,
pub dialect: AsmDialect
pub dialect: AsmDialect,
pub expn_id: ExpnId,
}

/// represents an argument in a function header
Expand Down
14 changes: 13 additions & 1 deletion src/libsyntax/codemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ source code snippets, etc.
use serialize::{Encodable, Decodable, Encoder, Decoder};
use std::cell::RefCell;
use std::rc::Rc;
use libc::c_uint;

pub trait Pos {
fn from_uint(n: uint) -> Self;
Expand Down Expand Up @@ -223,11 +224,22 @@ pub struct ExpnInfo {
pub callee: NameAndSpan
}

#[deriving(PartialEq, Eq, Clone, Show, Hash)]
#[deriving(PartialEq, Eq, Clone, Show, Hash, Encodable, Decodable)]
pub struct ExpnId(u32);

pub static NO_EXPANSION: ExpnId = ExpnId(-1);

impl ExpnId {
pub fn from_llvm_cookie(cookie: c_uint) -> ExpnId {
ExpnId(cookie as u32)
}

pub fn to_llvm_cookie(self) -> i32 {
let ExpnId(cookie) = self;
cookie as i32
}
}

pub type FileName = String;

pub struct FileLines {
Expand Down
13 changes: 12 additions & 1 deletion src/libsyntax/ext/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/

use ast;
use codemap;
use codemap::Span;
use ext::base;
use ext::base::*;
Expand Down Expand Up @@ -198,6 +199,15 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
}
}

let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
call_site: sp,
callee: codemap::NameAndSpan {
name: "asm".to_string(),
format: codemap::MacroBang,
span: None,
},
});

MacExpr::new(P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprInlineAsm(ast::InlineAsm {
Expand All @@ -208,7 +218,8 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
clobbers: token::intern_and_get_ident(cons.as_slice()),
volatile: volatile,
alignstack: alignstack,
dialect: dialect
dialect: dialect,
expn_id: expn_id,
}),
span: sp
}))
Expand Down
6 changes: 4 additions & 2 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
clobbers,
volatile,
alignstack,
dialect
dialect,
expn_id,
}) => ExprInlineAsm(InlineAsm {
inputs: inputs.move_map(|(c, input)| {
(c, folder.fold_expr(input))
Expand All @@ -1292,7 +1293,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
clobbers: clobbers,
volatile: volatile,
alignstack: alignstack,
dialect: dialect
dialect: dialect,
expn_id: expn_id,
}),
ExprMac(mac) => ExprMac(folder.fold_mac(mac)),
ExprStruct(path, fields, maybe_expr) => {
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extern crate debug;
#[phase(plugin, link)] extern crate log;
extern crate serialize;
extern crate term;
extern crate libc;

pub mod util {
pub mod interner;
Expand Down
15 changes: 15 additions & 0 deletions src/rustllvm/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,3 +871,18 @@ extern "C" void LLVMWriteDebugLocToString(
raw_rust_string_ostream os(str);
unwrap(dl)->print(*unwrap(C), os);
}

DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)

extern "C" void LLVMSetInlineAsmDiagnosticHandler(
LLVMContextRef C,
LLVMContext::InlineAsmDiagHandlerTy H,
void *CX)
{
unwrap(C)->setInlineAsmDiagnosticHandler(H, CX);
}

extern "C" void LLVMWriteSMDiagnosticToString(LLVMSMDiagnosticRef d, RustStringRef str) {
raw_rust_string_ostream os(str);
unwrap(d)->print("", os);
}
1 change: 1 addition & 0 deletions src/rustllvm/rustllvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void LLVMRustSetLastError(const char*);
typedef struct OpaqueRustString *RustStringRef;
typedef struct LLVMOpaqueTwine *LLVMTwineRef;
typedef struct LLVMOpaqueDebugLoc *LLVMDebugLocRef;
typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef;

extern "C" void
rust_llvm_string_write_impl(RustStringRef str, const char *ptr, size_t size);
Expand Down
20 changes: 20 additions & 0 deletions src/test/compile-fail/asm-src-loc-codegen-units.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// compile-flags: -C codegen-units=2
// error-pattern: build without -C codegen-units for more exact errors

#![feature(asm)]

fn main() {
unsafe {
asm!("nowayisthisavalidinstruction");
}
}
17 changes: 17 additions & 0 deletions src/test/compile-fail/asm-src-loc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(asm)]

fn main() {
unsafe {
asm!("nowayisthisavalidinstruction"); //~ ERROR invalid instruction mnemonic
}
}