Skip to content

Commit

Permalink
auto merge of rust-lang#17208 : kmcallister/rust/llvm-diagnostics, r=…
Browse files Browse the repository at this point in the history
…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 rust-lang#17116.
  • Loading branch information
bors committed Sep 15, 2014
2 parents 8e28604 + ad9a1da commit a8d478d
Show file tree
Hide file tree
Showing 7 changed files with 366 additions and 39 deletions.
67 changes: 56 additions & 11 deletions src/librustc/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)]
Expand Down Expand Up @@ -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());
}
}

_ => (),
}
}

Expand All @@ -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| {
Expand Down Expand Up @@ -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.
Expand Down
45 changes: 42 additions & 3 deletions src/librustc/driver/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
}
}
}
}
) )

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
}
}
Expand All @@ -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,
Expand Down
21 changes: 7 additions & 14 deletions src/librustc/middle/trans/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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")
}
}
92 changes: 92 additions & 0 deletions src/librustc_llvm/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Loading

0 comments on commit a8d478d

Please sign in to comment.