Skip to content

Commit a8d478d

Browse files
committed
auto merge of #17208 : kmcallister/rust/llvm-diagnostics, r=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 #17116.
2 parents 8e28604 + ad9a1da commit a8d478d

File tree

7 files changed

+366
-39
lines changed

7 files changed

+366
-39
lines changed

src/librustc/back/write.rs

+56-11
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

+42-3
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
@@ -716,8 +751,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
716751
None |
717752
Some("2") => FullDebugInfo,
718753
Some(arg) => {
719-
early_error(format!("optimization level needs to be between \
720-
0-3 (instead was `{}`)",
754+
early_error(format!("debug info level needs to be between \
755+
0-2 (instead was `{}`)",
721756
arg).as_slice());
722757
}
723758
}
@@ -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/middle/trans/type_.rs

+7-14
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ use syntax::abi::{X86, X86_64, Arm, Mips, Mipsel};
2121

2222
use std::c_str::ToCStr;
2323
use std::mem;
24-
use std::string;
2524
use std::cell::RefCell;
2625
use std::collections::HashMap;
2726

28-
use libc::{c_uint, c_void, free};
27+
use libc::c_uint;
2928

3029
#[deriving(Clone, PartialEq, Show)]
3130
pub struct Type {
@@ -339,12 +338,9 @@ impl TypeNames {
339338
}
340339

341340
pub fn type_to_string(&self, ty: Type) -> String {
342-
unsafe {
343-
let s = llvm::LLVMTypeToString(ty.to_ref());
344-
let ret = string::raw::from_buf(s as *const u8);
345-
free(s as *mut c_void);
346-
ret
347-
}
341+
llvm::build_string(|s| unsafe {
342+
llvm::LLVMWriteTypeToString(ty.to_ref(), s);
343+
}).expect("non-UTF8 type description from LLVM")
348344
}
349345

350346
pub fn types_to_str(&self, tys: &[Type]) -> String {
@@ -353,11 +349,8 @@ impl TypeNames {
353349
}
354350

355351
pub fn val_to_string(&self, val: ValueRef) -> String {
356-
unsafe {
357-
let s = llvm::LLVMValueToString(val);
358-
let ret = string::raw::from_buf(s as *const u8);
359-
free(s as *mut c_void);
360-
ret
361-
}
352+
llvm::build_string(|s| unsafe {
353+
llvm::LLVMWriteValueToString(val, s);
354+
}).expect("nun-UTF8 value description from LLVM")
362355
}
363356
}

src/librustc_llvm/diagnostic.rs

+92
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+
}

0 commit comments

Comments
 (0)