diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index 627d455f06e..53bdf500a21 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -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. diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 6547d1ed3eb..f7b0a178734 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -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), + 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 @@ -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, diff --git a/src/librustc_llvm/diagnostic.rs b/src/librustc_llvm/diagnostic.rs new file mode 100644 index 00000000000..6e1368ec3f9 --- /dev/null +++ b/src/librustc_llvm/diagnostic.rs @@ -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 or the MIT license +// , 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) + } + } +} diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 354448085b9..690b288043d 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -31,13 +31,14 @@ 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; @@ -81,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, @@ -360,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; @@ -395,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}; @@ -1918,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) { @@ -2072,6 +2120,16 @@ pub fn build_string(f: |RustStringRef|) -> Option { 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 diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 5893b582cd8..7896ce2ba76 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -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" @@ -823,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(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); +} diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index 92f94b0e8e5..54b0c2506c7 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -71,6 +71,8 @@ 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);