Skip to content

Commit ad9a1da

Browse files
author
Keegan McAllister
committed
Add -C remark for LLVM optimization remarks
Fixes #17116.
1 parent 225353d commit ad9a1da

File tree

6 files changed

+297
-13
lines changed

6 files changed

+297
-13
lines changed

src/librustc/back/write.rs

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
use back::lto;
1212
use back::link::{get_cc_prog, remove};
1313
use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames};
14-
use driver::config::NoDebugInfo;
14+
use driver::config::{NoDebugInfo, Passes, AllPasses};
1515
use driver::session::Session;
1616
use driver::config;
1717
use llvm;
18-
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
18+
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef};
1919
use util::common::time;
2020
use syntax::abi;
2121
use syntax::codemap;
@@ -28,9 +28,10 @@ use std::io::fs;
2828
use std::iter::Unfold;
2929
use std::ptr;
3030
use std::str;
31+
use std::mem;
3132
use std::sync::{Arc, Mutex};
3233
use std::task::TaskBuilder;
33-
use libc::{c_uint, c_int};
34+
use libc::{c_uint, c_int, c_void};
3435

3536

3637
#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
@@ -311,21 +312,49 @@ struct CodegenContext<'a> {
311312
lto_ctxt: Option<(&'a Session, &'a [String])>,
312313
// Handler to use for diagnostics produced during codegen.
313314
handler: &'a Handler,
315+
// LLVM optimizations for which we want to print remarks.
316+
remark: Passes,
314317
}
315318

316319
impl<'a> CodegenContext<'a> {
317-
fn new(handler: &'a Handler) -> CodegenContext<'a> {
318-
CodegenContext {
319-
lto_ctxt: None,
320-
handler: handler,
321-
}
322-
}
323-
324320
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
325321
CodegenContext {
326322
lto_ctxt: Some((sess, reachable)),
327323
handler: sess.diagnostic().handler(),
324+
remark: sess.opts.cg.remark.clone(),
325+
}
326+
}
327+
}
328+
329+
struct DiagHandlerFreeVars<'a> {
330+
llcx: ContextRef,
331+
cgcx: &'a CodegenContext<'a>,
332+
}
333+
334+
unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) {
335+
let DiagHandlerFreeVars { llcx, cgcx }
336+
= *mem::transmute::<_, *const DiagHandlerFreeVars>(user);
337+
338+
match llvm::diagnostic::Diagnostic::unpack(info) {
339+
llvm::diagnostic::Optimization(opt) => {
340+
let pass_name = CString::new(opt.pass_name, false);
341+
let pass_name = pass_name.as_str().expect("got a non-UTF8 pass name from LLVM");
342+
let enabled = match cgcx.remark {
343+
AllPasses => true,
344+
Passes(ref v) => v.iter().any(|s| s.as_slice() == pass_name),
345+
};
346+
347+
if enabled {
348+
let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc);
349+
cgcx.handler.note(format!("optimization {:s} for {:s} at {:s}: {:s}",
350+
opt.kind.describe(),
351+
pass_name,
352+
if loc.is_empty() { "[unknown]" } else { loc.as_slice() },
353+
llvm::twine_to_string(opt.message)).as_slice());
354+
}
328355
}
356+
357+
_ => (),
329358
}
330359
}
331360

@@ -338,6 +367,17 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
338367
let ModuleTranslation { llmod, llcx } = mtrans;
339368
let tm = config.tm;
340369

370+
// llcx doesn't outlive this function, so we can put this on the stack.
371+
let fv = DiagHandlerFreeVars {
372+
llcx: llcx,
373+
cgcx: cgcx,
374+
};
375+
if !cgcx.remark.is_empty() {
376+
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler,
377+
&fv as *const DiagHandlerFreeVars
378+
as *mut c_void);
379+
}
380+
341381
if config.emit_no_opt_bc {
342382
let ext = format!("{}.no-opt.bc", name_extra);
343383
output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
@@ -785,13 +825,18 @@ fn run_work_multithreaded(sess: &Session,
785825
for i in range(0, num_workers) {
786826
let work_items_arc = work_items_arc.clone();
787827
let diag_emitter = diag_emitter.clone();
828+
let remark = sess.opts.cg.remark.clone();
788829

789830
let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() {
790831
let diag_handler = mk_handler(box diag_emitter);
791832

792833
// Must construct cgcx inside the proc because it has non-Send
793834
// fields.
794-
let cgcx = CodegenContext::new(&diag_handler);
835+
let cgcx = CodegenContext {
836+
lto_ctxt: None,
837+
handler: &diag_handler,
838+
remark: remark,
839+
};
795840

796841
loop {
797842
// Avoid holding the lock for the entire duration of the match.

src/librustc/driver/config.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,21 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
235235
--pretty flowgraph output", FLOWGRAPH_PRINT_ALL))
236236
}
237237

238+
#[deriving(Clone)]
239+
pub enum Passes {
240+
Passes(Vec<String>),
241+
AllPasses,
242+
}
243+
244+
impl Passes {
245+
pub fn is_empty(&self) -> bool {
246+
match *self {
247+
Passes(ref v) => v.is_empty(),
248+
AllPasses => false,
249+
}
250+
}
251+
}
252+
238253
/// Declare a macro that will define all CodegenOptions fields and parsers all
239254
/// at once. The goal of this macro is to define an interface that can be
240255
/// programmatically used by the option parser in order to initialize the struct
@@ -261,7 +276,7 @@ macro_rules! cgoptions(
261276
&[ $( (stringify!($opt), cgsetters::$opt, $desc) ),* ];
262277

263278
mod cgsetters {
264-
use super::CodegenOptions;
279+
use super::{CodegenOptions, Passes, AllPasses};
265280

266281
$(
267282
pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool {
@@ -310,6 +325,24 @@ macro_rules! cgoptions(
310325
None => false
311326
}
312327
}
328+
329+
fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool {
330+
match v {
331+
Some("all") => {
332+
*slot = AllPasses;
333+
true
334+
}
335+
v => {
336+
let mut passes = vec!();
337+
if parse_list(&mut passes, v) {
338+
*slot = Passes(passes);
339+
true
340+
} else {
341+
false
342+
}
343+
}
344+
}
345+
}
313346
}
314347
) )
315348

@@ -356,6 +389,8 @@ cgoptions!(
356389
"extra data to put in each output filename"),
357390
codegen_units: uint = (1, parse_uint,
358391
"divide crate into N units to optimize in parallel"),
392+
remark: Passes = (Passes(Vec::new()), parse_passes,
393+
"print remarks for these optimization passes (space separated, or \"all\")"),
359394
)
360395

361396
pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
@@ -744,6 +779,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
744779
}
745780
let cg = build_codegen_options(matches);
746781

782+
if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
783+
early_warn("-C remark will not show source locations without --debuginfo");
784+
}
785+
747786
let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
748787
Some("auto") => Auto,
749788
Some("always") => Always,

src/librustc_llvm/diagnostic.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! LLVM diagnostic reports.
12+
13+
use libc::c_char;
14+
15+
use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef};
16+
17+
pub enum OptimizationDiagnosticKind {
18+
OptimizationRemark,
19+
OptimizationMissed,
20+
OptimizationAnalysis,
21+
OptimizationFailure,
22+
}
23+
24+
impl OptimizationDiagnosticKind {
25+
pub fn describe(self) -> &'static str {
26+
match self {
27+
OptimizationRemark => "remark",
28+
OptimizationMissed => "missed",
29+
OptimizationAnalysis => "analysis",
30+
OptimizationFailure => "failure",
31+
}
32+
}
33+
}
34+
35+
pub struct OptimizationDiagnostic {
36+
pub kind: OptimizationDiagnosticKind,
37+
pub pass_name: *const c_char,
38+
pub function: ValueRef,
39+
pub debug_loc: DebugLocRef,
40+
pub message: TwineRef,
41+
}
42+
43+
impl OptimizationDiagnostic {
44+
unsafe fn unpack(kind: OptimizationDiagnosticKind, di: DiagnosticInfoRef)
45+
-> OptimizationDiagnostic {
46+
47+
let mut opt = OptimizationDiagnostic {
48+
kind: kind,
49+
pass_name: 0 as *const c_char,
50+
function: 0 as ValueRef,
51+
debug_loc: 0 as DebugLocRef,
52+
message: 0 as TwineRef,
53+
};
54+
55+
super::LLVMUnpackOptimizationDiagnostic(di,
56+
&mut opt.pass_name,
57+
&mut opt.function,
58+
&mut opt.debug_loc,
59+
&mut opt.message);
60+
61+
opt
62+
}
63+
}
64+
65+
pub enum Diagnostic {
66+
Optimization(OptimizationDiagnostic),
67+
68+
/// LLVM has other types that we do not wrap here.
69+
UnknownDiagnostic(DiagnosticInfoRef),
70+
}
71+
72+
impl Diagnostic {
73+
pub unsafe fn unpack(di: DiagnosticInfoRef) -> Diagnostic {
74+
let kind = super::LLVMGetDiagInfoKind(di);
75+
76+
match kind {
77+
super::DK_OptimizationRemark
78+
=> Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)),
79+
80+
super::DK_OptimizationRemarkMissed
81+
=> Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)),
82+
83+
super::DK_OptimizationRemarkAnalysis
84+
=> Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)),
85+
86+
super::DK_OptimizationFailure
87+
=> Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)),
88+
89+
_ => UnknownDiagnostic(di)
90+
}
91+
}
92+
}

src/librustc_llvm/lib.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ use std::c_str::ToCStr;
3131
use std::cell::RefCell;
3232
use std::{raw, mem};
3333
use libc::{c_uint, c_ushort, uint64_t, c_int, size_t, c_char};
34-
use libc::{c_longlong, c_ulonglong};
34+
use libc::{c_longlong, c_ulonglong, c_void};
3535
use debuginfo::{DIBuilderRef, DIDescriptor,
3636
DIFile, DILexicalBlock, DISubprogram, DIType,
3737
DIBasicType, DIDerivedType, DICompositeType,
3838
DIVariable, DIGlobalVariable, DIArray, DISubrange};
3939

4040
pub mod archive_ro;
41+
pub mod diagnostic;
4142

4243
pub type Opcode = u32;
4344
pub type Bool = c_uint;
@@ -81,6 +82,15 @@ pub enum Linkage {
8182
CommonLinkage = 14,
8283
}
8384

85+
#[repr(C)]
86+
#[deriving(Show)]
87+
pub enum DiagnosticSeverity {
88+
Error,
89+
Warning,
90+
Remark,
91+
Note,
92+
}
93+
8494
#[deriving(Clone)]
8595
pub enum Attribute {
8696
ZExtAttribute = 1 << 0,
@@ -360,6 +370,18 @@ pub enum CodeGenModel {
360370
CodeModelLarge = 5,
361371
}
362372

373+
#[repr(C)]
374+
pub enum DiagnosticKind {
375+
DK_InlineAsm = 0,
376+
DK_StackSize,
377+
DK_DebugMetadataVersion,
378+
DK_SampleProfile,
379+
DK_OptimizationRemark,
380+
DK_OptimizationRemarkMissed,
381+
DK_OptimizationRemarkAnalysis,
382+
DK_OptimizationFailure,
383+
}
384+
363385
// Opaque pointer types
364386
pub enum Module_opaque {}
365387
pub type ModuleRef = *mut Module_opaque;
@@ -395,6 +417,14 @@ pub enum TargetMachine_opaque {}
395417
pub type TargetMachineRef = *mut TargetMachine_opaque;
396418
pub enum Archive_opaque {}
397419
pub type ArchiveRef = *mut Archive_opaque;
420+
pub enum Twine_opaque {}
421+
pub type TwineRef = *mut Twine_opaque;
422+
pub enum DiagnosticInfo_opaque {}
423+
pub type DiagnosticInfoRef = *mut DiagnosticInfo_opaque;
424+
pub enum DebugLoc_opaque {}
425+
pub type DebugLocRef = *mut DebugLoc_opaque;
426+
427+
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
398428

399429
pub mod debuginfo {
400430
use super::{ValueRef};
@@ -1918,6 +1948,24 @@ extern {
19181948

19191949
pub fn LLVMRustGetSectionName(SI: SectionIteratorRef,
19201950
data: *mut *const c_char) -> c_int;
1951+
1952+
pub fn LLVMWriteTwineToString(T: TwineRef, s: RustStringRef);
1953+
1954+
pub fn LLVMContextSetDiagnosticHandler(C: ContextRef,
1955+
Handler: DiagnosticHandler,
1956+
DiagnosticContext: *mut c_void);
1957+
1958+
pub fn LLVMUnpackOptimizationDiagnostic(DI: DiagnosticInfoRef,
1959+
pass_name_out: *mut *const c_char,
1960+
function_out: *mut ValueRef,
1961+
debugloc_out: *mut DebugLocRef,
1962+
message_out: *mut TwineRef);
1963+
1964+
pub fn LLVMWriteDiagnosticInfoToString(DI: DiagnosticInfoRef, s: RustStringRef);
1965+
pub fn LLVMGetDiagInfoSeverity(DI: DiagnosticInfoRef) -> DiagnosticSeverity;
1966+
pub fn LLVMGetDiagInfoKind(DI: DiagnosticInfoRef) -> DiagnosticKind;
1967+
1968+
pub fn LLVMWriteDebugLocToString(C: ContextRef, DL: DebugLocRef, s: RustStringRef);
19211969
}
19221970

19231971
pub fn SetInstructionCallConv(instr: ValueRef, cc: CallConv) {
@@ -2072,6 +2120,16 @@ pub fn build_string(f: |RustStringRef|) -> Option<String> {
20722120
String::from_utf8(buf.unwrap()).ok()
20732121
}
20742122

2123+
pub unsafe fn twine_to_string(tr: TwineRef) -> String {
2124+
build_string(|s| LLVMWriteTwineToString(tr, s))
2125+
.expect("got a non-UTF8 Twine from LLVM")
2126+
}
2127+
2128+
pub unsafe fn debug_loc_to_string(c: ContextRef, tr: DebugLocRef) -> String {
2129+
build_string(|s| LLVMWriteDebugLocToString(c, tr, s))
2130+
.expect("got a non-UTF8 DebugLoc from LLVM")
2131+
}
2132+
20752133
// FIXME #15460 - create a public function that actually calls our
20762134
// static LLVM symbols. Otherwise the linker will just throw llvm
20772135
// away. We're just calling lots of stuff until we transitively get

0 commit comments

Comments
 (0)