Add -C remark for LLVM optimization remarks

Fixes #17116.
This commit is contained in:
Keegan McAllister 2014-09-12 08:17:58 -07:00
parent 225353d8bb
commit ad9a1daa81
6 changed files with 297 additions and 13 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::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 @@ pub fn basic_codegen_options() -> CodegenOptions {
&[ $( (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 @@ fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool {
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 @@ fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool {
"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,

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

@ -31,13 +31,14 @@
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 @@ pub fn LLVMRustArchiveReadSection(AR: ArchiveRef, name: *const c_char,
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

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"
@ -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);
}

View File

@ -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);