about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/back/write.rs67
-rw-r--r--src/librustc/driver/config.rs41
-rw-r--r--src/librustc_llvm/diagnostic.rs92
-rw-r--r--src/librustc_llvm/lib.rs60
-rw-r--r--src/rustllvm/RustWrapper.cpp48
-rw-r--r--src/rustllvm/rustllvm.h2
6 files changed, 297 insertions, 13 deletions
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,21 +312,49 @@ 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());
+            }
         }
+
+        _ => (),
     }
 }
 
@@ -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<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
@@ -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 <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)
+        }
+    }
+}
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> {
     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<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);
+}
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);