Skip to content

Commit

Permalink
rustc: Update LLVM
Browse files Browse the repository at this point in the history
This commit updates the LLVM submodule in use to the current HEAD of the LLVM
repository. This is primarily being done to start picking up unwinding support
for MSVC, which is currently unimplemented in the revision of LLVM we are using.
Along the way a few changes had to be made:

* As usual, lots of C++ debuginfo bindings in LLVM changed, so there were some
  significant changes to our RustWrapper.cpp
* As usual, some pass management changed in LLVM, so clang was re-scrutinized to
  ensure that we're doing the same thing as clang.
* Some optimization options are now passed directly into the
  `PassManagerBuilder` instead of through CLI switches to LLVM.
* The `NoFramePointerElim` option was removed from LLVM, favoring instead the
  `no-frame-pointer-elim` function attribute instead.

Additionally, LLVM has picked up some new optimizations which required fixing an
existing soundness hole in the IR we generate. It appears that the current LLVM
we use does not expose this hole. When an enum is moved, the previous slot in
memory is overwritten with a bit pattern corresponding to "dropped". When the
drop glue for this slot is run, however, the switch on the discriminant can
often start executing the `unreachable` block of the switch due to the
discriminant now being outside the normal range. This was patched over locally
for now by having the `unreachable` block just change to a `ret void`.
  • Loading branch information
alexcrichton committed Jun 17, 2015
1 parent 71a8d31 commit f9d4149
Show file tree
Hide file tree
Showing 17 changed files with 302 additions and 109 deletions.
17 changes: 16 additions & 1 deletion src/librustc_llvm/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,9 @@ extern {
pub fn LLVMAddDereferenceableAttr(Fn: ValueRef, index: c_uint, bytes: uint64_t);
pub fn LLVMAddFunctionAttribute(Fn: ValueRef, index: c_uint, PA: uint64_t);
pub fn LLVMAddFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *const c_char);
pub fn LLVMAddFunctionAttrStringValue(Fn: ValueRef, index: c_uint,
Name: *const c_char,
Value: *const c_char);
pub fn LLVMRemoveFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *const c_char);
pub fn LLVMGetFunctionAttr(Fn: ValueRef) -> c_ulonglong;
pub fn LLVMRemoveFunctionAttr(Fn: ValueRef, val: c_ulonglong);
Expand Down Expand Up @@ -1920,6 +1923,7 @@ extern {
VarInfo: DIVariable,
AddrOps: *const i64,
AddrOpsCount: c_uint,
DL: ValueRef,
InsertAtEnd: BasicBlockRef)
-> ValueRef;

Expand All @@ -1928,6 +1932,7 @@ extern {
VarInfo: DIVariable,
AddrOps: *const i64,
AddrOpsCount: c_uint,
DL: ValueRef,
InsertBefore: ValueRef)
-> ValueRef;

Expand Down Expand Up @@ -2035,7 +2040,6 @@ extern {
Level: CodeGenOptLevel,
EnableSegstk: bool,
UseSoftFP: bool,
NoFramePointerElim: bool,
PositionIndependentExecutable: bool,
FunctionSections: bool,
DataSections: bool) -> TargetMachineRef;
Expand All @@ -2046,6 +2050,11 @@ extern {
pub fn LLVMRustAddBuilderLibraryInfo(PMB: PassManagerBuilderRef,
M: ModuleRef,
DisableSimplifyLibCalls: bool);
pub fn LLVMRustConfigurePassManagerBuilder(PMB: PassManagerBuilderRef,
OptLevel: CodeGenOptLevel,
MergeFunctions: bool,
SLPVectorize: bool,
LoopVectorize: bool);
pub fn LLVMRustAddLibraryInfo(PM: PassManagerRef, M: ModuleRef,
DisableSimplifyLibCalls: bool);
pub fn LLVMRustRunFunctionPassManager(PM: PassManagerRef, M: ModuleRef);
Expand Down Expand Up @@ -2116,6 +2125,12 @@ extern {
pub fn LLVMWriteSMDiagnosticToString(d: SMDiagnosticRef, s: RustStringRef);
}

// LLVM requires symbols from this library, but apparently they're not printed
// during llvm-config?
#[cfg(windows)]
#[link(name = "ole32")]
extern {}

pub fn SetInstructionCallConv(instr: ValueRef, cc: CallConv) {
unsafe {
LLVMSetInstructionCallConv(instr, cc as c_uint);
Expand Down
91 changes: 47 additions & 44 deletions src/librustc_trans/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use back::lto;
use back::link::{get_cc_prog, remove};
use session::config::{OutputFilenames, NoDebugInfo, Passes, SomePasses, AllPasses};
use session::config::{OutputFilenames, Passes, SomePasses, AllPasses};
use session::Session;
use session::config;
use llvm;
Expand Down Expand Up @@ -188,10 +188,6 @@ fn create_target_machine(sess: &Session) -> TargetMachineRef {
let opt_level = get_llvm_opt_level(sess.opts.optimize);
let use_softfp = sess.opts.cg.soft_float;

// FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter.
let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) ||
!sess.target.target.options.eliminate_frame_pointer;

let any_library = sess.crate_types.borrow().iter().any(|ty| {
*ty != config::CrateTypeExecutable
});
Expand Down Expand Up @@ -237,7 +233,6 @@ fn create_target_machine(sess: &Session) -> TargetMachineRef {
opt_level,
true /* EnableSegstk */,
use_softfp,
no_fp_elim,
!any_library && reloc_model == llvm::RelocPIC,
ffunction_sections,
fdata_sections,
Expand Down Expand Up @@ -279,6 +274,9 @@ struct ModuleConfig {
no_prepopulate_passes: bool,
no_builtins: bool,
time_passes: bool,
vectorize_loop: bool,
vectorize_slp: bool,
merge_functions: bool,
}

unsafe impl Send for ModuleConfig { }
Expand All @@ -301,6 +299,9 @@ impl ModuleConfig {
no_prepopulate_passes: false,
no_builtins: false,
time_passes: false,
vectorize_loop: false,
vectorize_slp: false,
merge_functions: false,
}
}

Expand All @@ -309,6 +310,18 @@ impl ModuleConfig {
self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
self.no_builtins = trans.no_builtins;
self.time_passes = sess.time_passes();

// Copy what clang does by turning on loop vectorization at O2 and
// slp vectorization at O3. Otherwise configure other optimization aspects
// of this pass manager builder.
self.vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
(sess.opts.optimize == config::Default ||
sess.opts.optimize == config::Aggressive);
self.vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
sess.opts.optimize == config::Aggressive;

self.merge_functions = sess.opts.optimize == config::Default ||
sess.opts.optimize == config::Aggressive;
}
}

Expand Down Expand Up @@ -448,27 +461,26 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
let pass = CString::new(pass).unwrap();
llvm::LLVMRustAddPass(fpm, pass.as_ptr())
};
if !config.no_verify { assert!(addpass("verify")); }

if !config.no_verify { assert!(addpass("verify")); }
if !config.no_prepopulate_passes {
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
populate_llvm_passes(fpm, mpm, llmod, opt_level,
config.no_builtins);
populate_llvm_passes(fpm, mpm, llmod, opt_level, &config);
}

for pass in &config.passes {
let pass = CString::new(pass.clone()).unwrap();
if !llvm::LLVMRustAddPass(mpm, pass.as_ptr()) {
cgcx.handler.warn(&format!("unknown pass {:?}, ignoring", pass));
if !addpass(pass) {
cgcx.handler.warn(&format!("unknown pass `{}`, ignoring",
pass));
}
}

for pass in &cgcx.plugin_passes {
let pass = CString::new(pass.clone()).unwrap();
if !llvm::LLVMRustAddPass(mpm, pass.as_ptr()) {
cgcx.handler.err(&format!("a plugin asked for LLVM pass {:?} but LLVM \
does not recognize it", pass));
if !addpass(pass) {
cgcx.handler.err(&format!("a plugin asked for LLVM pass \
`{}` but LLVM does not \
recognize it", pass));
}
}

Expand Down Expand Up @@ -520,7 +532,6 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
f(cpm);
llvm::LLVMDisposePassManager(cpm);
}

if config.emit_bc {
Expand All @@ -537,13 +548,15 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
let out = path2cstr(&out);
with_codegen(tm, llmod, config.no_builtins, |cpm| {
llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr());
llvm::LLVMDisposePassManager(cpm);
})
}

if config.emit_asm {
let path = output_names.with_extension(&format!("{}.s", name_extra));
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFileType);
write_output_file(cgcx.handler, tm, cpm, llmod, &path,
llvm::AssemblyFileType);
});
}

Expand Down Expand Up @@ -1008,25 +1021,16 @@ unsafe fn configure_llvm(sess: &Session) {
use std::sync::Once;
static INIT: Once = Once::new();

// Copy what clang does by turning on loop vectorization at O2 and
// slp vectorization at O3
let vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
(sess.opts.optimize == config::Default ||
sess.opts.optimize == config::Aggressive);
let vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
sess.opts.optimize == config::Aggressive;

let mut llvm_c_strs = Vec::new();
let mut llvm_args = Vec::new();

{
let mut add = |arg: &str| {
let s = CString::new(arg).unwrap();
llvm_args.push(s.as_ptr());
llvm_c_strs.push(s);
};
add("rustc"); // fake program name
if vectorize_loop { add("-vectorize-loops"); }
if vectorize_slp { add("-vectorize-slp"); }
if sess.time_llvm_passes() { add("-time-passes"); }
if sess.print_llvm_passes() { add("-debug-pass=Structure"); }

Expand Down Expand Up @@ -1084,41 +1088,40 @@ unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef,
mpm: llvm::PassManagerRef,
llmod: ModuleRef,
opt: llvm::CodeGenOptLevel,
no_builtins: bool) {
config: &ModuleConfig) {
// Create the PassManagerBuilder for LLVM. We configure it with
// reasonable defaults and prepare it to actually populate the pass
// manager.
let builder = llvm::LLVMPassManagerBuilderCreate();

llvm::LLVMRustConfigurePassManagerBuilder(builder, opt,
config.merge_functions,
config.vectorize_slp,
config.vectorize_loop);

llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins);

// Here we match what clang does (kinda). For O0 we only inline
// always-inline functions (but don't add lifetime intrinsics), at O1 we
// inline with lifetime intrinsics, and O2+ we add an inliner with a
// thresholds copied from clang.
match opt {
llvm::CodeGenLevelNone => {
// Don't add lifetime intrinsics at O0
llvm::LLVMRustAddAlwaysInlinePass(builder, false);
}
llvm::CodeGenLevelLess => {
llvm::LLVMRustAddAlwaysInlinePass(builder, true);
}
// numeric values copied from clang
llvm::CodeGenLevelDefault => {
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
225);
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225);
}
llvm::CodeGenLevelAggressive => {
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
275);
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);
}
}
llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint);
llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins);

// Use the builder to populate the function/module pass managers.
llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm);
llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm);
llvm::LLVMPassManagerBuilderDispose(builder);

match opt {
llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => {
llvm::LLVMRustAddPass(mpm, "mergefunc\0".as_ptr() as *const _);
}
_ => {}
};
}
21 changes: 18 additions & 3 deletions src/librustc_trans/trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,11 +1029,26 @@ pub fn fold_variants<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
}
General(ity, ref cases, _) => {
let ccx = bcx.ccx();
let unr_cx = fcx.new_temp_block("enum-variant-iter-unr");
Unreachable(unr_cx);

// See the comments in trans/base.rs for more information (inside
// iter_structural_ty), but the gist here is that if the enum's
// discriminant is *not* in the range that we're expecting (in which
// case we'll take the fall-through branch on the switch
// instruction) then we can't just optimize this to an Unreachable
// block.
//
// Currently we still have filling drop, so this means that the drop
// glue for enums may be called when the enum has been paved over
// with the "I've been dropped" value. In this case the default
// branch of the switch instruction will actually be taken at
// runtime, so the basic block isn't actually unreachable, so we
// need to make it do something with defined behavior. In this case
// we just return early from the function.
let ret_void_cx = fcx.new_temp_block("enum-variant-iter-ret-void");
RetVoid(ret_void_cx, DebugLoc::None);

let discr_val = trans_get_discr(bcx, r, value, None);
let llswitch = Switch(bcx, discr_val, unr_cx.llbb, cases.len());
let llswitch = Switch(bcx, discr_val, ret_void_cx.llbb, cases.len());
let bcx_next = fcx.new_temp_block("enum-variant-iter-next");

for (discr, case) in cases.iter().enumerate() {
Expand Down
15 changes: 15 additions & 0 deletions src/librustc_trans/trans/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use libc::{c_uint, c_ulonglong};
use llvm::{self, ValueRef, AttrHelper};
use middle::ty::{self, ClosureTyper};
use session::config::NoDebugInfo;
use syntax::abi;
use syntax::ast;
pub use syntax::attr::InlineAttr;
Expand Down Expand Up @@ -106,6 +107,20 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
use syntax::attr::*;
inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs));

// FIXME: #11906: Omitting frame pointers breaks retrieving the value of a
// parameter.
let no_fp_elim = (ccx.sess().opts.debuginfo != NoDebugInfo) ||
!ccx.sess().target.target.options.eliminate_frame_pointer;
if no_fp_elim {
unsafe {
let attr = "no-frame-pointer-elim\0".as_ptr() as *const _;
let val = "true\0".as_ptr() as *const _;
llvm::LLVMAddFunctionAttrStringValue(llfn,
llvm::FunctionIndex as c_uint,
attr, val);
}
}

for attr in attrs {
if attr.check_name("no_stack_check") {
split_stack(llfn, false);
Expand Down
20 changes: 17 additions & 3 deletions src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,23 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
}
(_match::Switch, Some(lldiscrim_a)) => {
cx = f(cx, lldiscrim_a, cx.tcx().types.isize);
let unr_cx = fcx.new_temp_block("enum-iter-unr");
Unreachable(unr_cx);
let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb,

// Create a fall-through basic block for the "else" case of
// the switch instruction we're about to generate. Note that
// we do **not** use an Unreachable instruction here, even
// though most of the time this basic block will never be hit.
//
// When an enum is dropped it's contents are currently
// overwritten to DTOR_DONE, which means the discriminant
// could have changed value to something not within the actual
// range of the discriminant. Currently this function is only
// used for drop glue so in this case we just return quickly
// from the outer function, and any other use case will only
// call this for an already-valid enum in which case the `ret
// void` will never be hit.
let ret_void_cx = fcx.new_temp_block("enum-iter-ret-void");
RetVoid(ret_void_cx, DebugLoc::None);
let llswitch = Switch(cx, lldiscrim_a, ret_void_cx.llbb,
n_variants);
let next_cx = fcx.new_temp_block("enum-iter-next");

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1673,7 +1673,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
DIB(cx),
containing_scope,
enum_name.as_ptr(),
UNKNOWN_FILE_METADATA,
file_metadata,
UNKNOWN_LINE_NUMBER,
bytes_to_bits(enum_type_size),
bytes_to_bits(enum_type_align),
Expand Down
Loading

0 comments on commit f9d4149

Please sign in to comment.