auto merge of #17208 : kmcallister/rust/llvm-diagnostics, r=thestinger

I would like to map this information back to AST nodes, so that we can print remarks with spans, and so that remarks can be enabled on a per-function basis.  Unfortunately, doing this would require a lot more code restructuring — for example, we currently throw away the AST map and lots of other information before LLVM optimizations run.  So for the time being, we print the remarks with debug location strings from LLVM.  There's a warning if you use `-C remark` without `--debuginfo`.

Fixes #17116.
This commit is contained in:
bors 2014-09-15 16:56:03 +00:00
commit a8d478db51
7 changed files with 366 additions and 39 deletions

View File

@ -11,11 +11,11 @@
use back::lto;
use back::link::{get_cc_prog, remove};
use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames};
use driver::config::NoDebugInfo;
use driver::config::{NoDebugInfo, Passes, AllPasses};
use driver::session::Session;
use driver::config;
use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef};
use util::common::time;
use syntax::abi;
use syntax::codemap;
@ -28,9 +28,10 @@ use std::io::fs;
use std::iter::Unfold;
use std::ptr;
use std::str;
use std::mem;
use std::sync::{Arc, Mutex};
use std::task::TaskBuilder;
use libc::{c_uint, c_int};
use libc::{c_uint, c_int, c_void};
#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
@ -311,24 +312,52 @@ struct CodegenContext<'a> {
lto_ctxt: Option<(&'a Session, &'a [String])>,
// Handler to use for diagnostics produced during codegen.
handler: &'a Handler,
// LLVM optimizations for which we want to print remarks.
remark: Passes,
}
impl<'a> CodegenContext<'a> {
fn new(handler: &'a Handler) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: None,
handler: handler,
}
}
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: Some((sess, reachable)),
handler: sess.diagnostic().handler(),
remark: sess.opts.cg.remark.clone(),
}
}
}
struct DiagHandlerFreeVars<'a> {
llcx: ContextRef,
cgcx: &'a CodegenContext<'a>,
}
unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) {
let DiagHandlerFreeVars { llcx, cgcx }
= *mem::transmute::<_, *const DiagHandlerFreeVars>(user);
match llvm::diagnostic::Diagnostic::unpack(info) {
llvm::diagnostic::Optimization(opt) => {
let pass_name = CString::new(opt.pass_name, false);
let pass_name = pass_name.as_str().expect("got a non-UTF8 pass name from LLVM");
let enabled = match cgcx.remark {
AllPasses => true,
Passes(ref v) => v.iter().any(|s| s.as_slice() == pass_name),
};
if enabled {
let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc);
cgcx.handler.note(format!("optimization {:s} for {:s} at {:s}: {:s}",
opt.kind.describe(),
pass_name,
if loc.is_empty() { "[unknown]" } else { loc.as_slice() },
llvm::twine_to_string(opt.message)).as_slice());
}
}
_ => (),
}
}
// Unsafe due to LLVM calls.
unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
mtrans: ModuleTranslation,
@ -338,6 +367,17 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
let ModuleTranslation { llmod, llcx } = mtrans;
let tm = config.tm;
// llcx doesn't outlive this function, so we can put this on the stack.
let fv = DiagHandlerFreeVars {
llcx: llcx,
cgcx: cgcx,
};
if !cgcx.remark.is_empty() {
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler,
&fv as *const DiagHandlerFreeVars
as *mut c_void);
}
if config.emit_no_opt_bc {
let ext = format!("{}.no-opt.bc", name_extra);
output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
@ -785,13 +825,18 @@ fn run_work_multithreaded(sess: &Session,
for i in range(0, num_workers) {
let work_items_arc = work_items_arc.clone();
let diag_emitter = diag_emitter.clone();
let remark = sess.opts.cg.remark.clone();
let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() {
let diag_handler = mk_handler(box diag_emitter);
// Must construct cgcx inside the proc because it has non-Send
// fields.
let cgcx = CodegenContext::new(&diag_handler);
let cgcx = CodegenContext {
lto_ctxt: None,
handler: &diag_handler,
remark: remark,
};
loop {
// Avoid holding the lock for the entire duration of the match.

View File

@ -235,6 +235,21 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
--pretty flowgraph output", FLOWGRAPH_PRINT_ALL))
}
#[deriving(Clone)]
pub enum Passes {
Passes(Vec<String>),
AllPasses,
}
impl Passes {
pub fn is_empty(&self) -> bool {
match *self {
Passes(ref v) => v.is_empty(),
AllPasses => false,
}
}
}
/// Declare a macro that will define all CodegenOptions fields and parsers all
/// at once. The goal of this macro is to define an interface that can be
/// programmatically used by the option parser in order to initialize the struct
@ -261,7 +276,7 @@ macro_rules! cgoptions(
&[ $( (stringify!($opt), cgsetters::$opt, $desc) ),* ];
mod cgsetters {
use super::CodegenOptions;
use super::{CodegenOptions, Passes, AllPasses};
$(
pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool {
@ -310,6 +325,24 @@ macro_rules! cgoptions(
None => false
}
}
fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool {
match v {
Some("all") => {
*slot = AllPasses;
true
}
v => {
let mut passes = vec!();
if parse_list(&mut passes, v) {
*slot = Passes(passes);
true
} else {
false
}
}
}
}
}
) )
@ -356,6 +389,8 @@ cgoptions!(
"extra data to put in each output filename"),
codegen_units: uint = (1, parse_uint,
"divide crate into N units to optimize in parallel"),
remark: Passes = (Passes(Vec::new()), parse_passes,
"print remarks for these optimization passes (space separated, or \"all\")"),
)
pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
@ -716,8 +751,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
None |
Some("2") => FullDebugInfo,
Some(arg) => {
early_error(format!("optimization level needs to be between \
0-3 (instead was `{}`)",
early_error(format!("debug info level needs to be between \
0-2 (instead was `{}`)",
arg).as_slice());
}
}
@ -744,6 +779,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
}
let cg = build_codegen_options(matches);
if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
early_warn("-C remark will not show source locations without --debuginfo");
}
let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
Some("auto") => Auto,
Some("always") => Always,

View File

@ -21,11 +21,10 @@ use syntax::abi::{X86, X86_64, Arm, Mips, Mipsel};
use std::c_str::ToCStr;
use std::mem;
use std::string;
use std::cell::RefCell;
use std::collections::HashMap;
use libc::{c_uint, c_void, free};
use libc::c_uint;
#[deriving(Clone, PartialEq, Show)]
pub struct Type {
@ -339,12 +338,9 @@ impl TypeNames {
}
pub fn type_to_string(&self, ty: Type) -> String {
unsafe {
let s = llvm::LLVMTypeToString(ty.to_ref());
let ret = string::raw::from_buf(s as *const u8);
free(s as *mut c_void);
ret
}
llvm::build_string(|s| unsafe {
llvm::LLVMWriteTypeToString(ty.to_ref(), s);
}).expect("non-UTF8 type description from LLVM")
}
pub fn types_to_str(&self, tys: &[Type]) -> String {
@ -353,11 +349,8 @@ impl TypeNames {
}
pub fn val_to_string(&self, val: ValueRef) -> String {
unsafe {
let s = llvm::LLVMValueToString(val);
let ret = string::raw::from_buf(s as *const u8);
free(s as *mut c_void);
ret
}
llvm::build_string(|s| unsafe {
llvm::LLVMWriteValueToString(val, s);
}).expect("nun-UTF8 value description from LLVM")
}
}

View File

@ -0,0 +1,92 @@
// 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.
//! LLVM diagnostic reports.
use libc::c_char;
use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef};
pub enum OptimizationDiagnosticKind {
OptimizationRemark,
OptimizationMissed,
OptimizationAnalysis,
OptimizationFailure,
}
impl OptimizationDiagnosticKind {
pub fn describe(self) -> &'static str {
match self {
OptimizationRemark => "remark",
OptimizationMissed => "missed",
OptimizationAnalysis => "analysis",
OptimizationFailure => "failure",
}
}
}
pub struct OptimizationDiagnostic {
pub kind: OptimizationDiagnosticKind,
pub pass_name: *const c_char,
pub function: ValueRef,
pub debug_loc: DebugLocRef,
pub message: TwineRef,
}
impl OptimizationDiagnostic {
unsafe fn unpack(kind: OptimizationDiagnosticKind, di: DiagnosticInfoRef)
-> OptimizationDiagnostic {
let mut opt = OptimizationDiagnostic {
kind: kind,
pass_name: 0 as *const c_char,
function: 0 as ValueRef,
debug_loc: 0 as DebugLocRef,
message: 0 as TwineRef,
};
super::LLVMUnpackOptimizationDiagnostic(di,
&mut opt.pass_name,
&mut opt.function,
&mut opt.debug_loc,
&mut opt.message);
opt
}
}
pub enum Diagnostic {
Optimization(OptimizationDiagnostic),
/// LLVM has other types that we do not wrap here.
UnknownDiagnostic(DiagnosticInfoRef),
}
impl Diagnostic {
pub unsafe fn unpack(di: DiagnosticInfoRef) -> Diagnostic {
let kind = super::LLVMGetDiagInfoKind(di);
match kind {
super::DK_OptimizationRemark
=> Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)),
super::DK_OptimizationRemarkMissed
=> Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)),
super::DK_OptimizationRemarkAnalysis
=> Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)),
super::DK_OptimizationFailure
=> Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)),
_ => UnknownDiagnostic(di)
}
}
}

View File

@ -28,14 +28,17 @@
extern crate libc;
use std::c_str::ToCStr;
use std::cell::RefCell;
use std::{raw, mem};
use libc::{c_uint, c_ushort, uint64_t, c_int, size_t, c_char};
use libc::{c_longlong, c_ulonglong};
use libc::{c_longlong, c_ulonglong, c_void};
use debuginfo::{DIBuilderRef, DIDescriptor,
DIFile, DILexicalBlock, DISubprogram, DIType,
DIBasicType, DIDerivedType, DICompositeType,
DIVariable, DIGlobalVariable, DIArray, DISubrange};
pub mod archive_ro;
pub mod diagnostic;
pub type Opcode = u32;
pub type Bool = c_uint;
@ -79,6 +82,15 @@ pub enum Linkage {
CommonLinkage = 14,
}
#[repr(C)]
#[deriving(Show)]
pub enum DiagnosticSeverity {
Error,
Warning,
Remark,
Note,
}
#[deriving(Clone)]
pub enum Attribute {
ZExtAttribute = 1 << 0,
@ -358,6 +370,18 @@ pub enum CodeGenModel {
CodeModelLarge = 5,
}
#[repr(C)]
pub enum DiagnosticKind {
DK_InlineAsm = 0,
DK_StackSize,
DK_DebugMetadataVersion,
DK_SampleProfile,
DK_OptimizationRemark,
DK_OptimizationRemarkMissed,
DK_OptimizationRemarkAnalysis,
DK_OptimizationFailure,
}
// Opaque pointer types
pub enum Module_opaque {}
pub type ModuleRef = *mut Module_opaque;
@ -393,6 +417,14 @@ pub enum TargetMachine_opaque {}
pub type TargetMachineRef = *mut TargetMachine_opaque;
pub enum Archive_opaque {}
pub type ArchiveRef = *mut Archive_opaque;
pub enum Twine_opaque {}
pub type TwineRef = *mut Twine_opaque;
pub enum DiagnosticInfo_opaque {}
pub type DiagnosticInfoRef = *mut DiagnosticInfo_opaque;
pub enum DebugLoc_opaque {}
pub type DebugLocRef = *mut DebugLoc_opaque;
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
pub mod debuginfo {
use super::{ValueRef};
@ -1839,8 +1871,8 @@ extern {
-> ValueRef;
pub fn LLVMDICompositeTypeSetTypeArray(CompositeType: ValueRef, TypeArray: ValueRef);
pub fn LLVMTypeToString(Type: TypeRef) -> *const c_char;
pub fn LLVMValueToString(value_ref: ValueRef) -> *const c_char;
pub fn LLVMWriteTypeToString(Type: TypeRef, s: RustStringRef);
pub fn LLVMWriteValueToString(value_ref: ValueRef, s: RustStringRef);
pub fn LLVMIsAArgument(value_ref: ValueRef) -> ValueRef;
@ -1916,6 +1948,24 @@ extern {
pub fn LLVMRustGetSectionName(SI: SectionIteratorRef,
data: *mut *const c_char) -> c_int;
pub fn LLVMWriteTwineToString(T: TwineRef, s: RustStringRef);
pub fn LLVMContextSetDiagnosticHandler(C: ContextRef,
Handler: DiagnosticHandler,
DiagnosticContext: *mut c_void);
pub fn LLVMUnpackOptimizationDiagnostic(DI: DiagnosticInfoRef,
pass_name_out: *mut *const c_char,
function_out: *mut ValueRef,
debugloc_out: *mut DebugLocRef,
message_out: *mut TwineRef);
pub fn LLVMWriteDiagnosticInfoToString(DI: DiagnosticInfoRef, s: RustStringRef);
pub fn LLVMGetDiagInfoSeverity(DI: DiagnosticInfoRef) -> DiagnosticSeverity;
pub fn LLVMGetDiagInfoKind(DI: DiagnosticInfoRef) -> DiagnosticKind;
pub fn LLVMWriteDebugLocToString(C: ContextRef, DL: DebugLocRef, s: RustStringRef);
}
pub fn SetInstructionCallConv(instr: ValueRef, cc: CallConv) {
@ -2046,6 +2096,40 @@ pub fn get_param(llfn: ValueRef, index: c_uint) -> ValueRef {
}
}
pub enum RustString_opaque {}
pub type RustStringRef = *mut RustString_opaque;
type RustStringRepr = *mut RefCell<Vec<u8>>;
/// Appending to a Rust string -- used by raw_rust_string_ostream.
#[no_mangle]
pub unsafe extern "C" fn rust_llvm_string_write_impl(sr: RustStringRef,
ptr: *const c_char,
size: size_t) {
let slice: &[u8] = mem::transmute(raw::Slice {
data: ptr as *const u8,
len: size as uint,
});
let sr: RustStringRepr = mem::transmute(sr);
(*sr).borrow_mut().push_all(slice);
}
pub fn build_string(f: |RustStringRef|) -> Option<String> {
let mut buf = RefCell::new(Vec::new());
f(&mut buf as RustStringRepr as RustStringRef);
String::from_utf8(buf.unwrap()).ok()
}
pub unsafe fn twine_to_string(tr: TwineRef) -> String {
build_string(|s| LLVMWriteTwineToString(tr, s))
.expect("got a non-UTF8 Twine from LLVM")
}
pub unsafe fn debug_loc_to_string(c: ContextRef, tr: DebugLocRef) -> String {
build_string(|s| LLVMWriteDebugLocToString(c, tr, s))
.expect("got a non-UTF8 DebugLoc from LLVM")
}
// FIXME #15460 - create a public function that actually calls our
// static LLVM symbols. Otherwise the linker will just throw llvm
// away. We're just calling lots of stuff until we transitively get

View File

@ -11,6 +11,8 @@
#include "rustllvm.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#if LLVM_VERSION_MINOR >= 5
#include "llvm/IR/CallSite.h"
@ -645,22 +647,18 @@ extern "C" void LLVMDICompositeTypeSetTypeArray(
#endif
}
extern "C" char *LLVMTypeToString(LLVMTypeRef Type) {
std::string s;
llvm::raw_string_ostream os(s);
extern "C" void LLVMWriteTypeToString(LLVMTypeRef Type, RustStringRef str) {
raw_rust_string_ostream os(str);
unwrap<llvm::Type>(Type)->print(os);
return strdup(os.str().data());
}
extern "C" char *LLVMValueToString(LLVMValueRef Value) {
std::string s;
llvm::raw_string_ostream os(s);
extern "C" void LLVMWriteValueToString(LLVMValueRef Value, RustStringRef str) {
raw_rust_string_ostream os(str);
os << "(";
unwrap<llvm::Value>(Value)->getType()->print(os);
os << ":";
unwrap<llvm::Value>(Value)->print(os);
os << ")";
return strdup(os.str().data());
}
#if LLVM_VERSION_MINOR >= 5
@ -827,3 +825,49 @@ extern "C" LLVMTypeRef
LLVMRustArrayType(LLVMTypeRef ElementType, uint64_t ElementCount) {
return wrap(ArrayType::get(unwrap(ElementType), ElementCount));
}
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef)
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DebugLoc, LLVMDebugLocRef)
extern "C" void
LLVMWriteTwineToString(LLVMTwineRef T, RustStringRef str) {
raw_rust_string_ostream os(str);
unwrap(T)->print(os);
}
extern "C" void
LLVMUnpackOptimizationDiagnostic(
LLVMDiagnosticInfoRef di,
const char **pass_name_out,
LLVMValueRef *function_out,
LLVMDebugLocRef *debugloc_out,
LLVMTwineRef *message_out)
{
// Undefined to call this not on an optimization diagnostic!
llvm::DiagnosticInfoOptimizationBase *opt
= static_cast<llvm::DiagnosticInfoOptimizationBase*>(unwrap(di));
*pass_name_out = opt->getPassName();
*function_out = wrap(&opt->getFunction());
*debugloc_out = wrap(&opt->getDebugLoc());
*message_out = wrap(&opt->getMsg());
}
extern "C" void LLVMWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef di, RustStringRef str) {
raw_rust_string_ostream os(str);
DiagnosticPrinterRawOStream dp(os);
unwrap(di)->print(dp);
}
extern "C" int LLVMGetDiagInfoKind(LLVMDiagnosticInfoRef di) {
return unwrap(di)->getKind();
}
extern "C" void LLVMWriteDebugLocToString(
LLVMContextRef C,
LLVMDebugLocRef dl,
RustStringRef str)
{
raw_rust_string_ostream os(str);
unwrap(dl)->print(*unwrap(C), os);
}

View File

@ -69,3 +69,33 @@
#endif
void LLVMRustSetLastError(const char*);
typedef struct OpaqueRustString *RustStringRef;
typedef struct LLVMOpaqueTwine *LLVMTwineRef;
typedef struct LLVMOpaqueDebugLoc *LLVMDebugLocRef;
extern "C" void
rust_llvm_string_write_impl(RustStringRef str, const char *ptr, size_t size);
class raw_rust_string_ostream : public llvm::raw_ostream {
RustStringRef str;
uint64_t pos;
void write_impl(const char *ptr, size_t size) override {
rust_llvm_string_write_impl(str, ptr, size);
pos += size;
}
uint64_t current_pos() const override {
return pos;
}
public:
explicit raw_rust_string_ostream(RustStringRef str)
: str(str), pos(0) { }
~raw_rust_string_ostream() {
// LLVM requires this.
flush();
}
};