From b90aa660a883cbeea80638a9eb96e89b2f2fd5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Mon, 26 Oct 2015 20:54:34 +0100 Subject: [PATCH] Fix emitting asm and object file output at the same time The LLVM function to output file types that involve codegen can invalidate the IR while lowering. That means that the second time the IR is fed to those passes, it's invalid and the LLVM verifier complains. To workaround this, we can tell the function to skip the codegen passes the second time around. To do this, we tell it to start adding passes only after it has seen a pass that doesn't exist at all. Quite the hack, I know... Fixes #24876 --- src/librustc_llvm/lib.rs | 1 + src/librustc_trans/back/write.rs | 11 ++++++++--- src/rustllvm/PassWrapper.cpp | 15 +++++++++++++-- src/test/run-make/emit/Makefile | 7 +++++++ src/test/run-make/emit/test.rs | 19 +++++++++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 src/test/run-make/emit/Makefile create mode 100644 src/test/run-make/emit/test.rs diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index fe84cffa8c660..c7cccc4a481aa 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -2066,6 +2066,7 @@ extern { PM: PassManagerRef, M: ModuleRef, Output: *const c_char, + skip_codegen: bool, FileType: FileType) -> bool; pub fn LLVMRustPrintModule(PM: PassManagerRef, M: ModuleRef, diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 1fbbf82ba38e4..9c7d8f67aaca5 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -56,11 +56,12 @@ pub fn write_output_file( pm: llvm::PassManagerRef, m: ModuleRef, output: &Path, + skip_codegen: bool, file_type: llvm::FileType) { unsafe { let output_c = path2cstr(output); let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output_c.as_ptr(), file_type); + target, pm, m, output_c.as_ptr(), skip_codegen, file_type); if !result { llvm_err(handler, format!("could not write output to {}", output.display())); } @@ -543,15 +544,19 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, 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, + write_output_file(cgcx.handler, tm, cpm, llmod, &path, false, llvm::AssemblyFileType); }); } if config.emit_obj { + // If we already emitted asm code, the codegen has already been done for this module, + // so don't do it again. See LLVMRustWriteOutputFile for details. + let skip_codegen = config.emit_asm; let path = output_names.with_extension(&format!("{}.o", name_extra)); with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType); + write_output_file(cgcx.handler, tm, cpm, llmod, &path, skip_codegen, + llvm::ObjectFileType); }); } }); diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index b27a622136f74..7c4661c01a299 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -227,6 +227,7 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, LLVMModuleRef M, const char *path, + bool skipCodegen, TargetMachine::CodeGenFileType FileType) { PassManager *PM = unwrap(PMR); @@ -244,11 +245,21 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, return false; } + // HACK: addPassesToEmitFile() also adds some codegen passes which are + // MachinePasses that may modify IR in a way that it becomes invalid (we've + // seen this with stack coloring). So the IR verifier would abort. Therefore, + // when we want to emit more than one filetype in a single run, we want to + // run the codegen passes and the verifier only for the first filetype. + // Telling LLVM to only start adding passes after it has seen a pass that + // doesn't exist allows us to achieve that. + char notAPass; + AnalysisID startBefore = skipCodegen ? (AnalysisID)¬APass : nullptr; + #if LLVM_VERSION_MINOR >= 7 - unwrap(Target)->addPassesToEmitFile(*PM, OS, FileType, false); + unwrap(Target)->addPassesToEmitFile(*PM, OS, FileType, false, startBefore); #else formatted_raw_ostream FOS(OS); - unwrap(Target)->addPassesToEmitFile(*PM, FOS, FileType, false); + unwrap(Target)->addPassesToEmitFile(*PM, FOS, FileType, false, startBefore); #endif PM->run(*unwrap(M)); diff --git a/src/test/run-make/emit/Makefile b/src/test/run-make/emit/Makefile new file mode 100644 index 0000000000000..47caaf1ed6582 --- /dev/null +++ b/src/test/run-make/emit/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: + $(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test.rs + $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test.rs + $(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test.rs + $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test.rs diff --git a/src/test/run-make/emit/test.rs b/src/test/run-make/emit/test.rs new file mode 100644 index 0000000000000..ab69decbf007e --- /dev/null +++ b/src/test/run-make/emit/test.rs @@ -0,0 +1,19 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Checks for issue #24876 + +fn main() { + let mut v = 0; + for i in 0..0 { + v += i; + } + println!("{}", v) +}