mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Auto merge of #89659 - workingjubilee:rollup-0vggc69, r=workingjubilee
Rollup of 8 pull requests Successful merges: - #87918 (Enable AutoFDO.) - #88137 (On macOS, make strip="symbols" not pass any options to strip) - #88772 (Fixed confusing wording on Result::map_or_else.) - #89025 (Implement `#[link_ordinal(n)]`) - #89082 (Implement #85440 (Random test ordering)) - #89288 (Wrapper for `-Z gcc-ld=lld` to invoke rust-lld with the correct flavor) - #89476 (Correct decoding of foreign expansions during incr. comp.) - #89622 (Use correct edition for panic in [debug_]assert!().) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
3013b26947
@ -1965,6 +1965,10 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lld-wrapper"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.1"
|
||||
|
@ -36,6 +36,7 @@ members = [
|
||||
"src/tools/jsondocck",
|
||||
"src/tools/html-checker",
|
||||
"src/tools/bump-stage0",
|
||||
"src/tools/lld-wrapper",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
@ -1,10 +1,10 @@
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
|
||||
use crate::panic::use_panic_2021;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_expand::base::*;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
@ -28,7 +28,7 @@ pub fn expand_assert<'cx>(
|
||||
let sp = cx.with_call_site_ctxt(span);
|
||||
|
||||
let panic_call = if let Some(tokens) = custom_message {
|
||||
let path = if span.rust_2021() {
|
||||
let path = if use_panic_2021(span) {
|
||||
// On edition 2021, we always call `$crate::panic::panic_2021!()`.
|
||||
Path {
|
||||
span: sp,
|
||||
|
@ -2,6 +2,7 @@ use rustc_ast::ptr::P;
|
||||
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
|
||||
use rustc_ast::*;
|
||||
use rustc_expand::base::*;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -19,7 +20,7 @@ pub fn expand_panic<'cx>(
|
||||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> Box<dyn MacResult + 'cx> {
|
||||
let panic = if sp.rust_2021() { sym::panic_2021 } else { sym::panic_2015 };
|
||||
let panic = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
|
||||
|
||||
let sp = cx.with_call_site_ctxt(sp);
|
||||
|
||||
@ -46,3 +47,19 @@ pub fn expand_panic<'cx>(
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn use_panic_2021(mut span: Span) -> bool {
|
||||
// To determine the editon, we check the first span up the expansion
|
||||
// stack that does not have #[allow_internal_unstable(edition_panic)].
|
||||
// (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.)
|
||||
loop {
|
||||
let expn = span.ctxt().outer_expn_data();
|
||||
if let Some(features) = expn.allow_internal_unstable {
|
||||
if features.iter().any(|&f| f == sym::edition_panic) {
|
||||
span = expn.call_site;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break expn.edition >= Edition::Edition2021;
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +263,10 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
|
||||
attributes::emit_uwtable(llfn, true);
|
||||
}
|
||||
|
||||
if cx.sess().opts.debugging_opts.profile_sample_use.is_some() {
|
||||
llvm::AddFunctionAttrString(llfn, Function, cstr!("use-sample-profile"));
|
||||
}
|
||||
|
||||
// FIXME: none of these three functions interact with source level attributes.
|
||||
set_frame_pointer_type(cx, llfn);
|
||||
set_instrument_function(cx, llfn);
|
||||
|
@ -163,13 +163,13 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
|
||||
// All import names are Rust identifiers and therefore cannot contain \0 characters.
|
||||
// FIXME: when support for #[link_name] implemented, ensure that import.name values don't
|
||||
// have any \0 characters
|
||||
let import_name_vector: Vec<CString> = dll_imports
|
||||
let import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> = dll_imports
|
||||
.iter()
|
||||
.map(|import: &DllImport| {
|
||||
if self.config.sess.target.arch == "x86" {
|
||||
LlvmArchiveBuilder::i686_decorated_name(import)
|
||||
(LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal)
|
||||
} else {
|
||||
CString::new(import.name.to_string()).unwrap()
|
||||
(CString::new(import.name.to_string()).unwrap(), import.ordinal)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@ -184,9 +184,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
|
||||
dll_imports.iter().map(|import| import.name.to_string()).collect::<Vec<_>>().join(", "),
|
||||
);
|
||||
|
||||
let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_vector
|
||||
let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_and_ordinal_vector
|
||||
.iter()
|
||||
.map(|name_z| LLVMRustCOFFShortExport::from_name(name_z.as_ptr()))
|
||||
.map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal))
|
||||
.collect();
|
||||
let result = unsafe {
|
||||
crate::llvm::LLVMRustWriteImportLibrary(
|
||||
|
@ -370,6 +370,13 @@ fn get_pgo_use_path(config: &ModuleConfig) -> Option<CString> {
|
||||
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
|
||||
}
|
||||
|
||||
fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> {
|
||||
config
|
||||
.pgo_sample_use
|
||||
.as_ref()
|
||||
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
|
||||
}
|
||||
|
||||
pub(crate) fn should_use_new_llvm_pass_manager(config: &ModuleConfig) -> bool {
|
||||
// The new pass manager is enabled by default for LLVM >= 13.
|
||||
// This matches Clang, which also enables it since Clang 13.
|
||||
@ -389,6 +396,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
let using_thin_buffers = opt_stage == llvm::OptStage::PreLinkThinLTO || config.bitcode_needed();
|
||||
let pgo_gen_path = get_pgo_gen_path(config);
|
||||
let pgo_use_path = get_pgo_use_path(config);
|
||||
let pgo_sample_use_path = get_pgo_sample_use_path(config);
|
||||
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
|
||||
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
|
||||
let sanitizer_options = if !is_lto {
|
||||
@ -439,6 +447,8 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
config.instrument_coverage,
|
||||
config.instrument_gcov,
|
||||
pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
config.debug_info_for_profiling,
|
||||
llvm_selfprofiler,
|
||||
selfprofile_before_pass_callback,
|
||||
selfprofile_after_pass_callback,
|
||||
@ -544,6 +554,9 @@ pub(crate) unsafe fn optimize(
|
||||
if config.instrument_coverage {
|
||||
llvm::LLVMRustAddPass(mpm, find_pass("instrprof").unwrap());
|
||||
}
|
||||
if config.debug_info_for_profiling {
|
||||
llvm::LLVMRustAddPass(mpm, find_pass("add-discriminators").unwrap());
|
||||
}
|
||||
|
||||
add_sanitizer_passes(config, &mut extra_passes);
|
||||
|
||||
@ -1001,6 +1014,7 @@ pub unsafe fn with_llvm_pmb(
|
||||
let inline_threshold = config.inline_threshold;
|
||||
let pgo_gen_path = get_pgo_gen_path(config);
|
||||
let pgo_use_path = get_pgo_use_path(config);
|
||||
let pgo_sample_use_path = get_pgo_sample_use_path(config);
|
||||
|
||||
llvm::LLVMRustConfigurePassManagerBuilder(
|
||||
builder,
|
||||
@ -1011,6 +1025,7 @@ pub unsafe fn with_llvm_pmb(
|
||||
prepare_for_thin_lto,
|
||||
pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
|
||||
pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
|
||||
pgo_sample_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
|
||||
);
|
||||
|
||||
llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);
|
||||
|
@ -34,11 +34,18 @@ pub enum LLVMRustResult {
|
||||
#[repr(C)]
|
||||
pub struct LLVMRustCOFFShortExport {
|
||||
pub name: *const c_char,
|
||||
pub ordinal_present: bool,
|
||||
// value of `ordinal` only important when `ordinal_present` is true
|
||||
pub ordinal: u16,
|
||||
}
|
||||
|
||||
impl LLVMRustCOFFShortExport {
|
||||
pub fn from_name(name: *const c_char) -> LLVMRustCOFFShortExport {
|
||||
LLVMRustCOFFShortExport { name }
|
||||
pub fn new(name: *const c_char, ordinal: Option<u16>) -> LLVMRustCOFFShortExport {
|
||||
LLVMRustCOFFShortExport {
|
||||
name,
|
||||
ordinal_present: ordinal.is_some(),
|
||||
ordinal: ordinal.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2176,6 +2183,7 @@ extern "C" {
|
||||
PrepareForThinLTO: bool,
|
||||
PGOGenPath: *const c_char,
|
||||
PGOUsePath: *const c_char,
|
||||
PGOSampleUsePath: *const c_char,
|
||||
);
|
||||
pub fn LLVMRustAddLibraryInfo(
|
||||
PM: &PassManager<'a>,
|
||||
@ -2210,6 +2218,8 @@ extern "C" {
|
||||
PGOUsePath: *const c_char,
|
||||
InstrumentCoverage: bool,
|
||||
InstrumentGCOV: bool,
|
||||
PGOSampleUsePath: *const c_char,
|
||||
DebugInfoForProfiling: bool,
|
||||
llvm_selfprofiler: *mut c_void,
|
||||
begin_callback: SelfProfileBeforePassCallback,
|
||||
end_callback: SelfProfileAfterPassCallback,
|
||||
|
@ -1024,14 +1024,20 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
||||
}
|
||||
|
||||
if sess.target.is_like_osx {
|
||||
if let Some(option) = osx_strip_opt(sess.opts.debugging_opts.strip) {
|
||||
strip_symbols_in_osx(sess, &out_filename, option);
|
||||
match sess.opts.debugging_opts.strip {
|
||||
Strip::Debuginfo => strip_symbols_in_osx(sess, &out_filename, Some("-S")),
|
||||
Strip::Symbols => strip_symbols_in_osx(sess, &out_filename, None),
|
||||
Strip::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: &str) {
|
||||
let prog = Command::new("strip").arg(option).arg(out_filename).output();
|
||||
fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: Option<&str>) {
|
||||
let mut cmd = Command::new("strip");
|
||||
if let Some(option) = option {
|
||||
cmd.arg(option);
|
||||
}
|
||||
let prog = cmd.arg(out_filename).output();
|
||||
match prog {
|
||||
Ok(prog) => {
|
||||
if !prog.status.success() {
|
||||
@ -1049,14 +1055,6 @@ fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: &str
|
||||
}
|
||||
}
|
||||
|
||||
fn osx_strip_opt<'a>(strip: Strip) -> Option<&'a str> {
|
||||
match strip {
|
||||
Strip::Debuginfo => Some("-S"),
|
||||
Strip::Symbols => Some("-x"),
|
||||
Strip::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn escape_string(s: &[u8]) -> String {
|
||||
str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| {
|
||||
let mut x = "Non-UTF-8 output: ".to_string();
|
||||
|
@ -286,6 +286,9 @@ impl<'a> GccLinker<'a> {
|
||||
config::OptLevel::Aggressive => "O3",
|
||||
};
|
||||
|
||||
if let Some(path) = &self.sess.opts.debugging_opts.profile_sample_use {
|
||||
self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
|
||||
};
|
||||
self.linker_arg(&format!("-plugin-opt={}", opt_level));
|
||||
self.linker_arg(&format!("-plugin-opt=mcpu={}", self.target_cpu));
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ pub struct ModuleConfig {
|
||||
|
||||
pub pgo_gen: SwitchWithOptPath,
|
||||
pub pgo_use: Option<PathBuf>,
|
||||
pub pgo_sample_use: Option<PathBuf>,
|
||||
pub debug_info_for_profiling: bool,
|
||||
pub instrument_coverage: bool,
|
||||
pub instrument_gcov: bool,
|
||||
|
||||
@ -176,6 +178,8 @@ impl ModuleConfig {
|
||||
SwitchWithOptPath::Disabled
|
||||
),
|
||||
pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
|
||||
pgo_sample_use: if_regular!(sess.opts.debugging_opts.profile_sample_use.clone(), None),
|
||||
debug_info_for_profiling: sess.opts.debugging_opts.debug_info_for_profiling,
|
||||
instrument_coverage: if_regular!(sess.instrument_coverage(), false),
|
||||
instrument_gcov: if_regular!(
|
||||
// compiler_builtins overrides the codegen-units settings,
|
||||
|
@ -715,6 +715,7 @@ fn test_debugging_options_tracking_hash() {
|
||||
tracked!(chalk, true);
|
||||
tracked!(codegen_backend, Some("abc".to_string()));
|
||||
tracked!(crate_attr, vec!["abc".to_string()]);
|
||||
tracked!(debug_info_for_profiling, true);
|
||||
tracked!(debug_macros, true);
|
||||
tracked!(dep_info_omit_d_target, true);
|
||||
tracked!(dual_proc_macros, true);
|
||||
@ -752,6 +753,7 @@ fn test_debugging_options_tracking_hash() {
|
||||
tracked!(profile, true);
|
||||
tracked!(profile_emit, Some(PathBuf::from("abc")));
|
||||
tracked!(profiler_runtime, "abc".to_string());
|
||||
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
|
||||
tracked!(relax_elf_relocations, Some(true));
|
||||
tracked!(relro_level, Some(RelroLevel::Full));
|
||||
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
#include "llvm/Transforms/IPO/AlwaysInliner.h"
|
||||
#include "llvm/Transforms/IPO/FunctionImport.h"
|
||||
#include "llvm/Transforms/Utils/AddDiscriminators.h"
|
||||
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm-c/Transforms/PassManagerBuilder.h"
|
||||
@ -39,6 +40,7 @@
|
||||
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
|
||||
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
|
||||
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
|
||||
#include "llvm/Transforms/Utils.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -523,7 +525,7 @@ extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
|
||||
extern "C" void LLVMRustConfigurePassManagerBuilder(
|
||||
LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel,
|
||||
bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO,
|
||||
const char* PGOGenPath, const char* PGOUsePath) {
|
||||
const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath) {
|
||||
unwrap(PMBR)->MergeFunctions = MergeFunctions;
|
||||
unwrap(PMBR)->SLPVectorize = SLPVectorize;
|
||||
unwrap(PMBR)->OptLevel = fromRust(OptLevel);
|
||||
@ -531,13 +533,14 @@ extern "C" void LLVMRustConfigurePassManagerBuilder(
|
||||
unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO;
|
||||
|
||||
if (PGOGenPath) {
|
||||
assert(!PGOUsePath);
|
||||
assert(!PGOUsePath && !PGOSampleUsePath);
|
||||
unwrap(PMBR)->EnablePGOInstrGen = true;
|
||||
unwrap(PMBR)->PGOInstrGen = PGOGenPath;
|
||||
}
|
||||
if (PGOUsePath) {
|
||||
assert(!PGOGenPath);
|
||||
} else if (PGOUsePath) {
|
||||
assert(!PGOSampleUsePath);
|
||||
unwrap(PMBR)->PGOInstrUse = PGOUsePath;
|
||||
} else if (PGOSampleUsePath) {
|
||||
unwrap(PMBR)->PGOSampleUse = PGOSampleUsePath;
|
||||
}
|
||||
}
|
||||
|
||||
@ -759,6 +762,7 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
LLVMRustSanitizerOptions *SanitizerOptions,
|
||||
const char *PGOGenPath, const char *PGOUsePath,
|
||||
bool InstrumentCoverage, bool InstrumentGCOV,
|
||||
const char *PGOSampleUsePath, bool DebugInfoForProfiling,
|
||||
void* LlvmSelfProfiler,
|
||||
LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
|
||||
LLVMRustSelfProfileAfterPassCallback AfterPassCallback,
|
||||
@ -797,11 +801,19 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
|
||||
Optional<PGOOptions> PGOOpt;
|
||||
if (PGOGenPath) {
|
||||
assert(!PGOUsePath);
|
||||
PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr);
|
||||
assert(!PGOUsePath && !PGOSampleUsePath);
|
||||
PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
} else if (PGOUsePath) {
|
||||
assert(!PGOGenPath);
|
||||
PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse);
|
||||
assert(!PGOSampleUsePath);
|
||||
PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
} else if (PGOSampleUsePath) {
|
||||
PGOOpt = PGOOptions(PGOSampleUsePath, "", "", PGOOptions::SampleUse,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
} else if (DebugInfoForProfiling) {
|
||||
PGOOpt = PGOOptions("", "", "", PGOOptions::NoAction,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_GE(12, 0) && !LLVM_VERSION_GE(13,0)
|
||||
|
@ -1753,10 +1753,11 @@ LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
|
||||
}
|
||||
|
||||
// This struct contains all necessary info about a symbol exported from a DLL.
|
||||
// At the moment, it's just the symbol's name, but we use a separate struct to
|
||||
// make it easier to add other information like ordinal later.
|
||||
struct LLVMRustCOFFShortExport {
|
||||
const char* name;
|
||||
bool ordinal_present;
|
||||
// The value of `ordinal` is only meaningful if `ordinal_present` is true.
|
||||
uint16_t ordinal;
|
||||
};
|
||||
|
||||
// Machine must be a COFF machine type, as defined in PE specs.
|
||||
@ -1772,13 +1773,15 @@ extern "C" LLVMRustResult LLVMRustWriteImportLibrary(
|
||||
ConvertedExports.reserve(NumExports);
|
||||
|
||||
for (size_t i = 0; i < NumExports; ++i) {
|
||||
bool ordinal_present = Exports[i].ordinal_present;
|
||||
uint16_t ordinal = ordinal_present ? Exports[i].ordinal : 0;
|
||||
ConvertedExports.push_back(llvm::object::COFFShortExport{
|
||||
Exports[i].name, // Name
|
||||
std::string{}, // ExtName
|
||||
std::string{}, // SymbolName
|
||||
std::string{}, // AliasTarget
|
||||
0, // Ordinal
|
||||
false, // Noname
|
||||
ordinal, // Ordinal
|
||||
ordinal_present, // Noname
|
||||
false, // Data
|
||||
false, // Private
|
||||
false // Constant
|
||||
|
@ -433,6 +433,12 @@ impl Collector<'tcx> {
|
||||
}
|
||||
}
|
||||
};
|
||||
DllImport { name: item.ident.name, ordinal: None, calling_convention, span: item.span }
|
||||
|
||||
DllImport {
|
||||
name: item.ident.name,
|
||||
ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal,
|
||||
calling_convention,
|
||||
span: item.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1632,7 +1632,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
self.def_path_hash_map.def_path_hash_to_def_index(&hash)
|
||||
}
|
||||
|
||||
fn expn_hash_to_expn_id(&self, index_guess: u32, hash: ExpnHash) -> ExpnId {
|
||||
fn expn_hash_to_expn_id(&self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId {
|
||||
debug_assert_eq!(ExpnId::from_hash(hash), None);
|
||||
let index_guess = ExpnIndex::from_u32(index_guess);
|
||||
let old_hash = self.root.expn_hashes.get(self, index_guess).map(|lazy| lazy.decode(self));
|
||||
@ -1654,8 +1654,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
let i = ExpnIndex::from_u32(i);
|
||||
if let Some(hash) = self.root.expn_hashes.get(self, i) {
|
||||
map.insert(hash.decode(self), i);
|
||||
} else {
|
||||
panic!("Missing expn_hash entry for {:?}", i);
|
||||
}
|
||||
}
|
||||
map
|
||||
@ -1663,7 +1661,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
map[&hash]
|
||||
};
|
||||
|
||||
let data = self.root.expn_data.get(self, index).unwrap().decode(self);
|
||||
let data = self.root.expn_data.get(self, index).unwrap().decode((self, sess));
|
||||
rustc_span::hygiene::register_expn_id(self.cnum, index, data, hash)
|
||||
}
|
||||
|
||||
|
@ -506,7 +506,13 @@ impl CrateStore for CStore {
|
||||
DefId { krate: cnum, index: def_index }
|
||||
}
|
||||
|
||||
fn expn_hash_to_expn_id(&self, cnum: CrateNum, index_guess: u32, hash: ExpnHash) -> ExpnId {
|
||||
self.get_crate_data(cnum).expn_hash_to_expn_id(index_guess, hash)
|
||||
fn expn_hash_to_expn_id(
|
||||
&self,
|
||||
sess: &Session,
|
||||
cnum: CrateNum,
|
||||
index_guess: u32,
|
||||
hash: ExpnHash,
|
||||
) -> ExpnId {
|
||||
self.get_crate_data(cnum).expn_hash_to_expn_id(sess, index_guess, hash)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ pub struct CodegenFnAttrs {
|
||||
/// imported function has in the dynamic library. Note that this must not
|
||||
/// be set when `link_name` is set. This is for foreign items with the
|
||||
/// "raw-dylib" kind.
|
||||
pub link_ordinal: Option<usize>,
|
||||
pub link_ordinal: Option<u16>,
|
||||
/// The `#[target_feature(enable = "...")]` attribute and the enabled
|
||||
/// features (only enabled features are supported right now).
|
||||
pub target_features: Vec<Symbol>,
|
||||
|
@ -664,22 +664,32 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for ExpnId {
|
||||
|
||||
let data: ExpnData = decoder
|
||||
.with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA))?;
|
||||
rustc_span::hygiene::register_local_expn_id(data, hash)
|
||||
let expn_id = rustc_span::hygiene::register_local_expn_id(data, hash);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
let mut hcx = decoder.tcx.create_stable_hashing_context();
|
||||
let mut hasher = StableHasher::new();
|
||||
hcx.while_hashing_spans(true, |hcx| {
|
||||
expn_id.expn_data().hash_stable(hcx, &mut hasher)
|
||||
});
|
||||
let local_hash: u64 = hasher.finish();
|
||||
debug_assert_eq!(hash.local_hash(), local_hash);
|
||||
}
|
||||
|
||||
expn_id
|
||||
} else {
|
||||
let index_guess = decoder.foreign_expn_data[&hash];
|
||||
decoder.tcx.cstore_untracked().expn_hash_to_expn_id(krate, index_guess, hash)
|
||||
decoder.tcx.cstore_untracked().expn_hash_to_expn_id(
|
||||
decoder.tcx.sess,
|
||||
krate,
|
||||
index_guess,
|
||||
hash,
|
||||
)
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
let mut hcx = decoder.tcx.create_stable_hashing_context();
|
||||
let mut hasher = StableHasher::new();
|
||||
hcx.while_hashing_spans(true, |hcx| expn_id.expn_data().hash_stable(hcx, &mut hasher));
|
||||
let local_hash: u64 = hasher.finish();
|
||||
debug_assert_eq!(hash.local_hash(), local_hash);
|
||||
}
|
||||
|
||||
debug_assert_eq!(expn_id.krate, krate);
|
||||
Ok(expn_id)
|
||||
}
|
||||
}
|
||||
|
@ -2009,6 +2009,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||
);
|
||||
}
|
||||
|
||||
if debugging_opts.profile_sample_use.is_some()
|
||||
&& (cg.profile_generate.enabled() || cg.profile_use.is_some())
|
||||
{
|
||||
early_error(
|
||||
error_format,
|
||||
"option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
|
||||
);
|
||||
}
|
||||
|
||||
if debugging_opts.instrument_coverage.is_some()
|
||||
&& debugging_opts.instrument_coverage != Some(InstrumentCoverage::Off)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use crate::search_paths::PathKind;
|
||||
use crate::utils::NativeLibKind;
|
||||
use crate::Session;
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::sync::{self, MetadataRef};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE};
|
||||
@ -193,7 +194,13 @@ pub trait CrateStore: std::fmt::Debug {
|
||||
|
||||
/// Fetch a DefId from a DefPathHash for a foreign crate.
|
||||
fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId;
|
||||
fn expn_hash_to_expn_id(&self, cnum: CrateNum, index_guess: u32, hash: ExpnHash) -> ExpnId;
|
||||
fn expn_hash_to_expn_id(
|
||||
&self,
|
||||
sess: &Session,
|
||||
cnum: CrateNum,
|
||||
index_guess: u32,
|
||||
hash: ExpnHash,
|
||||
) -> ExpnId;
|
||||
}
|
||||
|
||||
pub type CrateStoreDyn = dyn CrateStore + sync::Sync;
|
||||
|
@ -1040,6 +1040,8 @@ options! {
|
||||
"combine CGUs into a single one"),
|
||||
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
|
||||
"inject the given attribute in the crate"),
|
||||
debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
|
||||
"emit discriminators and other data necessary for AutoFDO"),
|
||||
debug_macros: bool = (false, parse_bool, [TRACKED],
|
||||
"emit line numbers debug info inside macros (default: no)"),
|
||||
deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED],
|
||||
@ -1242,6 +1244,8 @@ options! {
|
||||
(default based on relative source path)"),
|
||||
profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
|
||||
"name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
|
||||
profile_sample_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
|
||||
"use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"),
|
||||
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
|
||||
"enable queries of the dependency graph for regression testing (default: no)"),
|
||||
query_stats: bool = (false, parse_bool, [UNTRACKED],
|
||||
|
@ -1353,6 +1353,16 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same for sample profile data.
|
||||
if let Some(ref path) = sess.opts.debugging_opts.profile_sample_use {
|
||||
if !path.exists() {
|
||||
sess.err(&format!(
|
||||
"File `{}` passed to `-C profile-sample-use` does not exist.",
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Unwind tables cannot be disabled if the target requires them.
|
||||
if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables {
|
||||
if sess.target.requires_uwtable && !include_uwtables {
|
||||
|
@ -570,6 +570,7 @@ symbols! {
|
||||
dyn_metadata,
|
||||
dyn_trait,
|
||||
edition_macro_pats,
|
||||
edition_panic,
|
||||
eh_catch_typeinfo,
|
||||
eh_personality,
|
||||
emit_enum,
|
||||
|
@ -2861,6 +2861,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
} else if attr.has_name(sym::link_name) {
|
||||
codegen_fn_attrs.link_name = attr.value_str();
|
||||
} else if attr.has_name(sym::link_ordinal) {
|
||||
if link_ordinal_span.is_some() {
|
||||
tcx.sess
|
||||
.struct_span_err(
|
||||
attr.span,
|
||||
"multiple `link_ordinal` attributes on a single definition",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
link_ordinal_span = Some(attr.span);
|
||||
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
|
||||
codegen_fn_attrs.link_ordinal = ordinal;
|
||||
@ -3156,22 +3164,41 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<usize> {
|
||||
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
|
||||
use rustc_ast::{Lit, LitIntType, LitKind};
|
||||
let meta_item_list = attr.meta_item_list();
|
||||
let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
|
||||
let sole_meta_list = match meta_item_list {
|
||||
Some([item]) => item.literal(),
|
||||
Some(_) => {
|
||||
tcx.sess
|
||||
.struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
|
||||
.note("the attribute requires exactly one argument")
|
||||
.emit();
|
||||
return None;
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
|
||||
if *ordinal <= usize::MAX as u128 {
|
||||
Some(*ordinal as usize)
|
||||
// According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
|
||||
// the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
|
||||
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
|
||||
// to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
|
||||
//
|
||||
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
|
||||
// both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
|
||||
// a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
|
||||
// for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
|
||||
// library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
|
||||
// if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
|
||||
// about LINK.EXE failing.)
|
||||
if *ordinal <= u16::MAX as u128 {
|
||||
Some(*ordinal as u16)
|
||||
} else {
|
||||
let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
|
||||
tcx.sess
|
||||
.struct_span_err(attr.span, &msg)
|
||||
.note("the value may not exceed `usize::MAX`")
|
||||
.note("the value may not exceed `u16::MAX`")
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
|
@ -210,6 +210,7 @@ pub macro assert_matches {
|
||||
#[macro_export]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "debug_assert_macro"]
|
||||
#[allow_internal_unstable(edition_panic)]
|
||||
macro_rules! debug_assert {
|
||||
($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert!($($arg)*); })
|
||||
}
|
||||
|
@ -795,9 +795,8 @@ impl<T, E> Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `Result<T, E>` to `U` by applying a provided default fallback
|
||||
/// function to a contained [`Err`] value, or a provided function to a
|
||||
/// contained [`Ok`] value.
|
||||
/// Maps a `Result<T, E>` to `U` by applying fallback function `default` to
|
||||
/// a contained [`Err`] value, or function `f` to a contained [`Ok`] value.
|
||||
///
|
||||
/// This function can be used to unpack a successful result
|
||||
/// while handling an error.
|
||||
|
@ -21,6 +21,8 @@ pub struct TestOpts {
|
||||
pub nocapture: bool,
|
||||
pub color: ColorConfig,
|
||||
pub format: OutputFormat,
|
||||
pub shuffle: bool,
|
||||
pub shuffle_seed: Option<u64>,
|
||||
pub test_threads: Option<usize>,
|
||||
pub skip: Vec<String>,
|
||||
pub time_options: Option<TestTimeOptions>,
|
||||
@ -138,6 +140,13 @@ fn optgroups() -> getopts::Options {
|
||||
|
||||
`CRITICAL_TIME` here means the limit that should not be exceeded by test.
|
||||
",
|
||||
)
|
||||
.optflag("", "shuffle", "Run tests in random order")
|
||||
.optopt(
|
||||
"",
|
||||
"shuffle-seed",
|
||||
"Run tests in random order; seed the random number generator with SEED",
|
||||
"SEED",
|
||||
);
|
||||
opts
|
||||
}
|
||||
@ -155,6 +164,12 @@ By default, all tests are run in parallel. This can be altered with the
|
||||
--test-threads flag or the RUST_TEST_THREADS environment variable when running
|
||||
tests (set it to 1).
|
||||
|
||||
By default, the tests are run in alphabetical order. Use --shuffle or set
|
||||
RUST_TEST_SHUFFLE to run the tests in random order. Pass the generated
|
||||
"shuffle seed" to --shuffle-seed (or set RUST_TEST_SHUFFLE_SEED) to run the
|
||||
tests in the same order again. Note that --shuffle and --shuffle-seed do not
|
||||
affect whether the tests are run in parallel.
|
||||
|
||||
All tests have their standard output and standard error captured by default.
|
||||
This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
|
||||
environment variable to a value other than "0". Logging is not captured by default.
|
||||
@ -218,6 +233,21 @@ macro_rules! unstable_optflag {
|
||||
}};
|
||||
}
|
||||
|
||||
// Gets the option value and checks if unstable features are enabled.
|
||||
macro_rules! unstable_optopt {
|
||||
($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
|
||||
let opt = $matches.opt_str($option_name);
|
||||
if !$allow_unstable && opt.is_some() {
|
||||
return Err(format!(
|
||||
"The \"{}\" option is only accepted on the nightly compiler with -Z unstable-options",
|
||||
$option_name
|
||||
));
|
||||
}
|
||||
|
||||
opt
|
||||
}};
|
||||
}
|
||||
|
||||
// Implementation of `parse_opts` that doesn't care about help message
|
||||
// and returns a `Result`.
|
||||
fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
||||
@ -227,6 +257,8 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
||||
let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
|
||||
let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
|
||||
let time_options = get_time_options(&matches, allow_unstable)?;
|
||||
let shuffle = get_shuffle(&matches, allow_unstable)?;
|
||||
let shuffle_seed = get_shuffle_seed(&matches, allow_unstable)?;
|
||||
|
||||
let include_ignored = matches.opt_present("include-ignored");
|
||||
let quiet = matches.opt_present("quiet");
|
||||
@ -260,6 +292,8 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
||||
nocapture,
|
||||
color,
|
||||
format,
|
||||
shuffle,
|
||||
shuffle_seed,
|
||||
test_threads,
|
||||
skip,
|
||||
time_options,
|
||||
@ -303,6 +337,46 @@ fn get_time_options(
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
fn get_shuffle(matches: &getopts::Matches, allow_unstable: bool) -> OptPartRes<bool> {
|
||||
let mut shuffle = unstable_optflag!(matches, allow_unstable, "shuffle");
|
||||
if !shuffle && allow_unstable {
|
||||
shuffle = match env::var("RUST_TEST_SHUFFLE") {
|
||||
Ok(val) => &val != "0",
|
||||
Err(_) => false,
|
||||
};
|
||||
}
|
||||
|
||||
Ok(shuffle)
|
||||
}
|
||||
|
||||
fn get_shuffle_seed(matches: &getopts::Matches, allow_unstable: bool) -> OptPartRes<Option<u64>> {
|
||||
let mut shuffle_seed = match unstable_optopt!(matches, allow_unstable, "shuffle-seed") {
|
||||
Some(n_str) => match n_str.parse::<u64>() {
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"argument for --shuffle-seed must be a number \
|
||||
(error: {})",
|
||||
e
|
||||
));
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
if shuffle_seed.is_none() && allow_unstable {
|
||||
shuffle_seed = match env::var("RUST_TEST_SHUFFLE_SEED") {
|
||||
Ok(val) => match val.parse::<u64>() {
|
||||
Ok(n) => Some(n),
|
||||
Err(_) => panic!("RUST_TEST_SHUFFLE_SEED is `{}`, should be a number.", val),
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
}
|
||||
|
||||
Ok(shuffle_seed)
|
||||
}
|
||||
|
||||
fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
|
||||
let test_threads = match matches.opt_str("test-threads") {
|
||||
Some(n_str) => match n_str.parse::<usize>() {
|
||||
|
@ -225,9 +225,9 @@ fn on_test_event(
|
||||
out: &mut dyn OutputFormatter,
|
||||
) -> io::Result<()> {
|
||||
match (*event).clone() {
|
||||
TestEvent::TeFiltered(ref filtered_tests) => {
|
||||
TestEvent::TeFiltered(ref filtered_tests, shuffle_seed) => {
|
||||
st.total = filtered_tests.len();
|
||||
out.write_run_start(filtered_tests.len())?;
|
||||
out.write_run_start(filtered_tests.len(), shuffle_seed)?;
|
||||
}
|
||||
TestEvent::TeFilteredOut(filtered_out) => {
|
||||
st.filtered_out = filtered_out;
|
||||
|
@ -28,7 +28,7 @@ impl CompletedTest {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TestEvent {
|
||||
TeFiltered(Vec<TestDesc>),
|
||||
TeFiltered(Vec<TestDesc>, Option<u64>),
|
||||
TeWait(TestDesc),
|
||||
TeResult(CompletedTest),
|
||||
TeTimeout(TestDesc),
|
||||
|
@ -60,10 +60,15 @@ impl<T: Write> JsonFormatter<T> {
|
||||
}
|
||||
|
||||
impl<T: Write> OutputFormatter for JsonFormatter<T> {
|
||||
fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
|
||||
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
|
||||
let shuffle_seed_json = if let Some(shuffle_seed) = shuffle_seed {
|
||||
format!(r#", "shuffle_seed": {}"#, shuffle_seed)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
self.writeln_message(&*format!(
|
||||
r#"{{ "type": "suite", "event": "started", "test_count": {} }}"#,
|
||||
test_count
|
||||
r#"{{ "type": "suite", "event": "started", "test_count": {}{} }}"#,
|
||||
test_count, shuffle_seed_json
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,11 @@ impl<T: Write> JunitFormatter<T> {
|
||||
}
|
||||
|
||||
impl<T: Write> OutputFormatter for JunitFormatter<T> {
|
||||
fn write_run_start(&mut self, _test_count: usize) -> io::Result<()> {
|
||||
fn write_run_start(
|
||||
&mut self,
|
||||
_test_count: usize,
|
||||
_shuffle_seed: Option<u64>,
|
||||
) -> io::Result<()> {
|
||||
// We write xml header on run start
|
||||
self.out.write_all(b"\n")?;
|
||||
self.write_message("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
|
||||
|
@ -18,7 +18,7 @@ pub(crate) use self::pretty::PrettyFormatter;
|
||||
pub(crate) use self::terse::TerseFormatter;
|
||||
|
||||
pub(crate) trait OutputFormatter {
|
||||
fn write_run_start(&mut self, test_count: usize) -> io::Result<()>;
|
||||
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()>;
|
||||
fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()>;
|
||||
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()>;
|
||||
fn write_result(
|
||||
|
@ -181,9 +181,14 @@ impl<T: Write> PrettyFormatter<T> {
|
||||
}
|
||||
|
||||
impl<T: Write> OutputFormatter for PrettyFormatter<T> {
|
||||
fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
|
||||
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
|
||||
let noun = if test_count != 1 { "tests" } else { "test" };
|
||||
self.write_plain(&format!("\nrunning {} {}\n", test_count, noun))
|
||||
let shuffle_seed_msg = if let Some(shuffle_seed) = shuffle_seed {
|
||||
format!(" (shuffle seed: {})", shuffle_seed)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
self.write_plain(&format!("\nrunning {} {}{}\n", test_count, noun, shuffle_seed_msg))
|
||||
}
|
||||
|
||||
fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
|
||||
|
@ -170,10 +170,15 @@ impl<T: Write> TerseFormatter<T> {
|
||||
}
|
||||
|
||||
impl<T: Write> OutputFormatter for TerseFormatter<T> {
|
||||
fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
|
||||
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
|
||||
self.total_test_count = test_count;
|
||||
let noun = if test_count != 1 { "tests" } else { "test" };
|
||||
self.write_plain(&format!("\nrunning {} {}\n", test_count, noun))
|
||||
let shuffle_seed_msg = if let Some(shuffle_seed) = shuffle_seed {
|
||||
format!(" (shuffle seed: {})", shuffle_seed)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
self.write_plain(&format!("\nrunning {} {}{}\n", test_count, noun, shuffle_seed_msg))
|
||||
}
|
||||
|
||||
fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
|
||||
|
@ -5,3 +5,4 @@ pub mod concurrency;
|
||||
pub mod exit_code;
|
||||
pub mod isatty;
|
||||
pub mod metrics;
|
||||
pub mod shuffle;
|
||||
|
67
library/test/src/helpers/shuffle.rs
Normal file
67
library/test/src/helpers/shuffle.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::cli::TestOpts;
|
||||
use crate::types::{TestDescAndFn, TestId, TestName};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub fn get_shuffle_seed(opts: &TestOpts) -> Option<u64> {
|
||||
opts.shuffle_seed.or_else(|| {
|
||||
if opts.shuffle {
|
||||
Some(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Failed to get system time")
|
||||
.as_nanos() as u64,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) {
|
||||
let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect();
|
||||
let test_names_hash = calculate_hash(&test_names);
|
||||
let mut rng = Rng::new(shuffle_seed, test_names_hash);
|
||||
shuffle(&mut rng, tests);
|
||||
}
|
||||
|
||||
// `shuffle` is from `rust-analyzer/src/cli/analysis_stats.rs`.
|
||||
fn shuffle<T>(rng: &mut Rng, slice: &mut [T]) {
|
||||
for i in 0..slice.len() {
|
||||
randomize_first(rng, &mut slice[i..]);
|
||||
}
|
||||
|
||||
fn randomize_first<T>(rng: &mut Rng, slice: &mut [T]) {
|
||||
assert!(!slice.is_empty());
|
||||
let idx = rng.rand_range(0..slice.len() as u64) as usize;
|
||||
slice.swap(0, idx);
|
||||
}
|
||||
}
|
||||
|
||||
struct Rng {
|
||||
state: u64,
|
||||
extra: u64,
|
||||
}
|
||||
|
||||
impl Rng {
|
||||
fn new(seed: u64, extra: u64) -> Self {
|
||||
Self { state: seed, extra }
|
||||
}
|
||||
|
||||
fn rand_range(&mut self, range: core::ops::Range<u64>) -> u64 {
|
||||
self.rand_u64() % (range.end - range.start) + range.start
|
||||
}
|
||||
|
||||
fn rand_u64(&mut self) -> u64 {
|
||||
self.state = calculate_hash(&(self.state, self.extra));
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
||||
// `calculate_hash` is from `core/src/hash/mod.rs`.
|
||||
fn calculate_hash<T: core::hash::Hash>(t: &T) -> u64 {
|
||||
let mut s = DefaultHasher::new();
|
||||
t.hash(&mut s);
|
||||
s.finish()
|
||||
}
|
@ -91,6 +91,7 @@ mod tests;
|
||||
use event::{CompletedTest, TestEvent};
|
||||
use helpers::concurrency::get_concurrency;
|
||||
use helpers::exit_code::get_exit_code;
|
||||
use helpers::shuffle::{get_shuffle_seed, shuffle_tests};
|
||||
use options::{Concurrent, RunStrategy};
|
||||
use test_result::*;
|
||||
use time::TestExecTime;
|
||||
@ -247,7 +248,9 @@ where
|
||||
|
||||
let filtered_descs = filtered_tests.iter().map(|t| t.desc.clone()).collect();
|
||||
|
||||
let event = TestEvent::TeFiltered(filtered_descs);
|
||||
let shuffle_seed = get_shuffle_seed(opts);
|
||||
|
||||
let event = TestEvent::TeFiltered(filtered_descs, shuffle_seed);
|
||||
notify_about_test_event(event)?;
|
||||
|
||||
let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests
|
||||
@ -259,7 +262,11 @@ where
|
||||
let concurrency = opts.test_threads.unwrap_or_else(get_concurrency);
|
||||
|
||||
let mut remaining = filtered_tests;
|
||||
remaining.reverse();
|
||||
if let Some(shuffle_seed) = shuffle_seed {
|
||||
shuffle_tests(shuffle_seed, &mut remaining);
|
||||
} else {
|
||||
remaining.reverse();
|
||||
}
|
||||
let mut pending = 0;
|
||||
|
||||
let (tx, rx) = channel::<CompletedTest>();
|
||||
|
@ -45,6 +45,8 @@ impl TestOpts {
|
||||
nocapture: false,
|
||||
color: AutoColor,
|
||||
format: OutputFormat::Pretty,
|
||||
shuffle: false,
|
||||
shuffle_seed: None,
|
||||
test_threads: None,
|
||||
skip: vec![],
|
||||
time_options: None,
|
||||
@ -565,11 +567,7 @@ pub fn exact_filter_match() {
|
||||
assert_eq!(exact.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn sort_tests() {
|
||||
let mut opts = TestOpts::new();
|
||||
opts.run_tests = true;
|
||||
|
||||
fn sample_tests() -> Vec<TestDescAndFn> {
|
||||
let names = vec![
|
||||
"sha1::test".to_string(),
|
||||
"isize::test_to_str".to_string(),
|
||||
@ -583,26 +581,32 @@ pub fn sort_tests() {
|
||||
"test::run_include_ignored_option".to_string(),
|
||||
"test::sort_tests".to_string(),
|
||||
];
|
||||
let tests = {
|
||||
fn testfn() {}
|
||||
let mut tests = Vec::new();
|
||||
for name in &names {
|
||||
let test = TestDescAndFn {
|
||||
desc: TestDesc {
|
||||
name: DynTestName((*name).clone()),
|
||||
ignore: false,
|
||||
should_panic: ShouldPanic::No,
|
||||
allow_fail: false,
|
||||
compile_fail: false,
|
||||
no_run: false,
|
||||
test_type: TestType::Unknown,
|
||||
},
|
||||
testfn: DynTestFn(Box::new(testfn)),
|
||||
};
|
||||
tests.push(test);
|
||||
}
|
||||
tests
|
||||
};
|
||||
fn testfn() {}
|
||||
let mut tests = Vec::new();
|
||||
for name in &names {
|
||||
let test = TestDescAndFn {
|
||||
desc: TestDesc {
|
||||
name: DynTestName((*name).clone()),
|
||||
ignore: false,
|
||||
should_panic: ShouldPanic::No,
|
||||
allow_fail: false,
|
||||
compile_fail: false,
|
||||
no_run: false,
|
||||
test_type: TestType::Unknown,
|
||||
},
|
||||
testfn: DynTestFn(Box::new(testfn)),
|
||||
};
|
||||
tests.push(test);
|
||||
}
|
||||
tests
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn sort_tests() {
|
||||
let mut opts = TestOpts::new();
|
||||
opts.run_tests = true;
|
||||
|
||||
let tests = sample_tests();
|
||||
let filtered = filter_tests(&opts, tests);
|
||||
|
||||
let expected = vec![
|
||||
@ -624,6 +628,71 @@ pub fn sort_tests() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn shuffle_tests() {
|
||||
let mut opts = TestOpts::new();
|
||||
opts.shuffle = true;
|
||||
|
||||
let shuffle_seed = get_shuffle_seed(&opts).unwrap();
|
||||
|
||||
let left =
|
||||
sample_tests().into_iter().enumerate().map(|(i, e)| (TestId(i), e)).collect::<Vec<_>>();
|
||||
let mut right =
|
||||
sample_tests().into_iter().enumerate().map(|(i, e)| (TestId(i), e)).collect::<Vec<_>>();
|
||||
|
||||
assert!(left.iter().zip(&right).all(|(a, b)| a.1.desc.name == b.1.desc.name));
|
||||
|
||||
helpers::shuffle::shuffle_tests(shuffle_seed, right.as_mut_slice());
|
||||
|
||||
assert!(left.iter().zip(right).any(|(a, b)| a.1.desc.name != b.1.desc.name));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn shuffle_tests_with_seed() {
|
||||
let mut opts = TestOpts::new();
|
||||
opts.shuffle = true;
|
||||
|
||||
let shuffle_seed = get_shuffle_seed(&opts).unwrap();
|
||||
|
||||
let mut left =
|
||||
sample_tests().into_iter().enumerate().map(|(i, e)| (TestId(i), e)).collect::<Vec<_>>();
|
||||
let mut right =
|
||||
sample_tests().into_iter().enumerate().map(|(i, e)| (TestId(i), e)).collect::<Vec<_>>();
|
||||
|
||||
helpers::shuffle::shuffle_tests(shuffle_seed, left.as_mut_slice());
|
||||
helpers::shuffle::shuffle_tests(shuffle_seed, right.as_mut_slice());
|
||||
|
||||
assert!(left.iter().zip(right).all(|(a, b)| a.1.desc.name == b.1.desc.name));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn order_depends_on_more_than_seed() {
|
||||
let mut opts = TestOpts::new();
|
||||
opts.shuffle = true;
|
||||
|
||||
let shuffle_seed = get_shuffle_seed(&opts).unwrap();
|
||||
|
||||
let mut left_tests = sample_tests();
|
||||
let mut right_tests = sample_tests();
|
||||
|
||||
left_tests.pop();
|
||||
right_tests.remove(0);
|
||||
|
||||
let mut left =
|
||||
left_tests.into_iter().enumerate().map(|(i, e)| (TestId(i), e)).collect::<Vec<_>>();
|
||||
let mut right =
|
||||
right_tests.into_iter().enumerate().map(|(i, e)| (TestId(i), e)).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(left.len(), right.len());
|
||||
|
||||
assert!(left.iter().zip(&right).all(|(a, b)| a.0 == b.0));
|
||||
|
||||
helpers::shuffle::shuffle_tests(shuffle_seed, left.as_mut_slice());
|
||||
helpers::shuffle::shuffle_tests(shuffle_seed, right.as_mut_slice());
|
||||
|
||||
assert!(left.iter().zip(right).any(|(a, b)| a.0 != b.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_metricmap_compare() {
|
||||
let mut m1 = MetricMap::new();
|
||||
|
@ -1136,14 +1136,14 @@ impl Step for Assemble {
|
||||
// for `-Z gcc-ld=lld`
|
||||
let gcc_ld_dir = libdir_bin.join("gcc-ld");
|
||||
t!(fs::create_dir(&gcc_ld_dir));
|
||||
builder.copy(
|
||||
&lld_install.join("bin").join(&src_exe),
|
||||
&gcc_ld_dir.join(exe("ld", target_compiler.host)),
|
||||
);
|
||||
builder.copy(
|
||||
&lld_install.join("bin").join(&src_exe),
|
||||
&gcc_ld_dir.join(exe("ld64", target_compiler.host)),
|
||||
);
|
||||
for flavor in ["ld", "ld64"] {
|
||||
let lld_wrapper_exe = builder.ensure(crate::tool::LldWrapper {
|
||||
compiler: build_compiler,
|
||||
target: target_compiler.host,
|
||||
flavor_feature: flavor,
|
||||
});
|
||||
builder.copy(&lld_wrapper_exe, &gcc_ld_dir.join(exe(flavor, target_compiler.host)));
|
||||
}
|
||||
}
|
||||
|
||||
// Similarly, copy `llvm-dwp` into libdir for Split DWARF. Only copy it when the LLVM
|
||||
|
@ -409,11 +409,14 @@ impl Step for Rustc {
|
||||
let rust_lld = exe("rust-lld", compiler.host);
|
||||
builder.copy(&src_dir.join(&rust_lld), &dst_dir.join(&rust_lld));
|
||||
// for `-Z gcc-ld=lld`
|
||||
let gcc_lld_dir = dst_dir.join("gcc-ld");
|
||||
t!(fs::create_dir(&gcc_lld_dir));
|
||||
builder.copy(&src_dir.join(&rust_lld), &gcc_lld_dir.join(exe("ld", compiler.host)));
|
||||
builder
|
||||
.copy(&src_dir.join(&rust_lld), &gcc_lld_dir.join(exe("ld64", compiler.host)));
|
||||
let gcc_lld_src_dir = src_dir.join("gcc-ld");
|
||||
let gcc_lld_dst_dir = dst_dir.join("gcc-ld");
|
||||
t!(fs::create_dir(&gcc_lld_dst_dir));
|
||||
for flavor in ["ld", "ld64"] {
|
||||
let exe_name = exe(flavor, compiler.host);
|
||||
builder
|
||||
.copy(&gcc_lld_src_dir.join(&exe_name), &gcc_lld_dst_dir.join(&exe_name));
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over llvm-dwp if it's there
|
||||
|
@ -664,6 +664,38 @@ impl Step for Cargo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct LldWrapper {
|
||||
pub compiler: Compiler,
|
||||
pub target: TargetSelection,
|
||||
pub flavor_feature: &'static str,
|
||||
}
|
||||
|
||||
impl Step for LldWrapper {
|
||||
type Output = PathBuf;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.never()
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) -> PathBuf {
|
||||
let src_exe = builder
|
||||
.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "lld-wrapper",
|
||||
mode: Mode::ToolStd,
|
||||
path: "src/tools/lld-wrapper",
|
||||
is_optional_tool: false,
|
||||
source_type: SourceType::InTree,
|
||||
extra_features: vec![self.flavor_feature.to_owned()],
|
||||
})
|
||||
.expect("expected to build -- essential tool");
|
||||
|
||||
src_exe
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tool_extended {
|
||||
(($sel:ident, $builder:ident),
|
||||
$($name:ident,
|
||||
|
@ -181,6 +181,40 @@ unstable-options` flag. See [tracking issue
|
||||
#64888](https://github.com/rust-lang/rust/issues/64888) and the [unstable
|
||||
docs](../../unstable-book/compiler-flags/report-time.html) for more information.
|
||||
|
||||
#### `--shuffle`
|
||||
|
||||
Runs the tests in random order, as opposed to the default alphabetical order.
|
||||
|
||||
This may also be specified by setting the `RUST_TEST_SHUFFLE` environment
|
||||
variable to anything but `0`.
|
||||
|
||||
The random number generator seed that is output can be passed to
|
||||
[`--shuffle-seed`](#--shuffle-seed-seed) to run the tests in the same order
|
||||
again.
|
||||
|
||||
Note that `--shuffle` does not affect whether the tests are run in parallel. To
|
||||
run the tests in random order sequentially, use `--shuffle --test-threads 1`.
|
||||
|
||||
⚠️ 🚧 This option is [unstable](#unstable-options), and requires the `-Z
|
||||
unstable-options` flag. See [tracking issue
|
||||
#89583](https://github.com/rust-lang/rust/issues/89583) for more information.
|
||||
|
||||
#### `--shuffle-seed` _SEED_
|
||||
|
||||
Like [`--shuffle`](#--shuffle), but seeds the random number generator with
|
||||
_SEED_. Thus, calling the test harness with `--shuffle-seed` _SEED_ twice runs
|
||||
the tests in the same order both times.
|
||||
|
||||
_SEED_ is any 64-bit unsigned integer, for example, one produced by
|
||||
[`--shuffle`](#--shuffle).
|
||||
|
||||
This can also be specified with the `RUST_TEST_SHUFFLE_SEED` environment
|
||||
variable.
|
||||
|
||||
⚠️ 🚧 This option is [unstable](#unstable-options), and requires the `-Z
|
||||
unstable-options` flag. See [tracking issue
|
||||
#89583](https://github.com/rust-lang/rust/issues/89583) for more information.
|
||||
|
||||
### Output options
|
||||
|
||||
The following options affect the output behavior.
|
||||
@ -197,7 +231,7 @@ to the console. Usually the output is captured, and only displayed if the test
|
||||
fails.
|
||||
|
||||
This may also be specified by setting the `RUST_TEST_NOCAPTURE` environment
|
||||
variable set to anything but `0`.
|
||||
variable to anything but `0`.
|
||||
|
||||
#### `--show-output`
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
# `debug-info-for-profiling
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Automatic Feedback Directed Optimization (AFDO) is a method for using sampling
|
||||
based profiles to guide optimizations. This is contrasted with other methods of
|
||||
FDO or profile-guided optimization (PGO) which use instrumented profiling.
|
||||
|
||||
Unlike PGO (controlled by the `rustc` flags `-Cprofile-generate` and
|
||||
`-Cprofile-use`), a binary being profiled does not perform significantly worse,
|
||||
and thus it's possible to profile binaries used in real workflows and not
|
||||
necessary to construct artificial workflows.
|
||||
|
||||
## Use
|
||||
|
||||
In order to use AFDO, the target platform must be Linux running on an `x86_64`
|
||||
architecture with the performance profiler `perf` available. In addition, the
|
||||
external tool `create_llvm_prof` from [this repository] must be used.
|
||||
|
||||
Given a Rust file `main.rs`, we can produce an optimized binary as follows:
|
||||
|
||||
```shell
|
||||
rustc -O -Zdebug-info-for-profiling main.rs -o main
|
||||
perf record -b ./main
|
||||
create_llvm_prof --binary=main --out=code.prof
|
||||
rustc -O -Zprofile-sample-use=code.prof main.rs -o main2
|
||||
```
|
||||
|
||||
The `perf` command produces a profile `perf.data`, which is then used by the
|
||||
`create_llvm_prof` command to create `code.prof`. This final profile is then
|
||||
used by `rustc` to guide optimizations in producing the binary `main2`.
|
||||
|
||||
[this repository]: https://github.com/google/autofdo
|
@ -0,0 +1,10 @@
|
||||
# `profile-sample-use
|
||||
|
||||
---
|
||||
|
||||
`-Zprofile-sample-use=code.prof` directs `rustc` to use the profile
|
||||
`code.prof` as a source for Automatic Feedback Directed Optimization (AFDO).
|
||||
See the documentation of [`-Zdebug-info-for-profiling`] for more information
|
||||
on using AFDO.
|
||||
|
||||
[`-Zdebug-info-for-profiling`]: debug_info_for_profiling.html
|
11
src/test/incremental/mir-opt.rs
Normal file
11
src/test/incremental/mir-opt.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// MIR optimizations can create expansions after the TyCtxt has been created.
|
||||
// This test verifies that those expansions can be decoded correctly.
|
||||
|
||||
// revisions:rpass1 rpass2
|
||||
// compile-flags: -Z query-dep-graph -Z mir-opt-level=3
|
||||
|
||||
fn main() {
|
||||
if std::env::var("a").is_ok() {
|
||||
println!("b");
|
||||
}
|
||||
}
|
18
src/test/run-make/raw-dylib-link-ordinal/Makefile
Normal file
18
src/test/run-make/raw-dylib-link-ordinal/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
# Test the behavior of #[link(.., kind = "raw-dylib")] and #[link_ordinal] on windows-msvc
|
||||
|
||||
# only-windows-msvc
|
||||
|
||||
-include ../../run-make-fulldeps/tools.mk
|
||||
|
||||
all:
|
||||
$(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c)
|
||||
$(CC) "$(TMPDIR)"/exporter.obj exporter.def -link -dll -out:"$(TMPDIR)"/exporter.dll
|
||||
$(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs
|
||||
$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
|
||||
"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
|
||||
|
||||
ifdef RUSTC_BLESS_TEST
|
||||
cp "$(TMPDIR)"/output.txt output.txt
|
||||
else
|
||||
$(DIFF) output.txt "$(TMPDIR)"/output.txt
|
||||
endif
|
5
src/test/run-make/raw-dylib-link-ordinal/driver.rs
Normal file
5
src/test/run-make/raw-dylib-link-ordinal/driver.rs
Normal file
@ -0,0 +1,5 @@
|
||||
extern crate raw_dylib_test;
|
||||
|
||||
fn main() {
|
||||
raw_dylib_test::library_function();
|
||||
}
|
5
src/test/run-make/raw-dylib-link-ordinal/exporter.c
Normal file
5
src/test/run-make/raw-dylib-link-ordinal/exporter.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void exported_function() {
|
||||
printf("exported_function\n");
|
||||
}
|
3
src/test/run-make/raw-dylib-link-ordinal/exporter.def
Normal file
3
src/test/run-make/raw-dylib-link-ordinal/exporter.def
Normal file
@ -0,0 +1,3 @@
|
||||
LIBRARY exporter
|
||||
EXPORTS
|
||||
exported_function @13 NONAME
|
13
src/test/run-make/raw-dylib-link-ordinal/lib.rs
Normal file
13
src/test/run-make/raw-dylib-link-ordinal/lib.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#![feature(raw_dylib)]
|
||||
|
||||
#[link(name = "exporter", kind = "raw-dylib")]
|
||||
extern {
|
||||
#[link_ordinal(13)]
|
||||
fn imported_function();
|
||||
}
|
||||
|
||||
pub fn library_function() {
|
||||
unsafe {
|
||||
imported_function();
|
||||
}
|
||||
}
|
1
src/test/run-make/raw-dylib-link-ordinal/output.txt
Normal file
1
src/test/run-make/raw-dylib-link-ordinal/output.txt
Normal file
@ -0,0 +1 @@
|
||||
exported_function
|
@ -0,0 +1,11 @@
|
||||
#![feature(raw_dylib)]
|
||||
//~^ WARN the feature `raw_dylib` is incomplete
|
||||
|
||||
#[link(name = "foo")]
|
||||
extern "C" {
|
||||
#[link_ordinal()]
|
||||
//~^ ERROR incorrect number of arguments to `#[link_ordinal]`
|
||||
fn foo();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,19 @@
|
||||
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/link-ordinal-missing-argument.rs:1:12
|
||||
|
|
||||
LL | #![feature(raw_dylib)]
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
|
||||
|
||||
error: incorrect number of arguments to `#[link_ordinal]`
|
||||
--> $DIR/link-ordinal-missing-argument.rs:6:5
|
||||
|
|
||||
LL | #[link_ordinal()]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the attribute requires exactly one argument
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
13
src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs
Normal file
13
src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs
Normal file
@ -0,0 +1,13 @@
|
||||
// only-windows-msvc
|
||||
#![feature(raw_dylib)]
|
||||
//~^ WARN the feature `raw_dylib` is incomplete
|
||||
|
||||
#[link(name = "foo", kind = "raw-dylib")]
|
||||
extern "C" {
|
||||
#[link_ordinal(1)]
|
||||
#[link_ordinal(2)]
|
||||
//~^ ERROR multiple `link_ordinal` attributes on a single definition
|
||||
fn foo();
|
||||
}
|
||||
|
||||
fn main() {}
|
17
src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr
Normal file
17
src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr
Normal file
@ -0,0 +1,17 @@
|
||||
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/link-ordinal-multiple.rs:2:12
|
||||
|
|
||||
LL | #![feature(raw_dylib)]
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
|
||||
|
||||
error: multiple `link_ordinal` attributes on a single definition
|
||||
--> $DIR/link-ordinal-multiple.rs:8:5
|
||||
|
|
||||
LL | #[link_ordinal(2)]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
#[link(name = "foo")]
|
||||
extern "C" {
|
||||
#[link_ordinal(18446744073709551616)]
|
||||
//~^ ERROR ordinal value in `link_ordinal` is too large: `18446744073709551616`
|
||||
#[link_ordinal(72436)]
|
||||
//~^ ERROR ordinal value in `link_ordinal` is too large: `72436`
|
||||
fn foo();
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,13 @@ LL | #![feature(raw_dylib)]
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
|
||||
|
||||
error: ordinal value in `link_ordinal` is too large: `18446744073709551616`
|
||||
error: ordinal value in `link_ordinal` is too large: `72436`
|
||||
--> $DIR/link-ordinal-too-large.rs:6:5
|
||||
|
|
||||
LL | #[link_ordinal(18446744073709551616)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | #[link_ordinal(72436)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the value may not exceed `usize::MAX`
|
||||
= note: the value may not exceed `u16::MAX`
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
#![feature(raw_dylib)]
|
||||
//~^ WARN the feature `raw_dylib` is incomplete
|
||||
|
||||
#[link(name = "foo")]
|
||||
extern "C" {
|
||||
#[link_ordinal(3, 4)]
|
||||
//~^ ERROR incorrect number of arguments to `#[link_ordinal]`
|
||||
fn foo();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,19 @@
|
||||
warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/link-ordinal-too-many-arguments.rs:1:12
|
||||
|
|
||||
LL | #![feature(raw_dylib)]
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
|
||||
|
||||
error: incorrect number of arguments to `#[link_ordinal]`
|
||||
--> $DIR/link-ordinal-too-many-arguments.rs:6:5
|
||||
|
|
||||
LL | #[link_ordinal(3, 4)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the attribute requires exactly one argument
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
24
src/test/ui/rust-2021/panic.rs
Normal file
24
src/test/ui/rust-2021/panic.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// edition:2021
|
||||
|
||||
fn main() {
|
||||
debug_assert!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
assert!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
panic!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
|
||||
std::debug_assert!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
std::assert!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
std::panic!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
|
||||
core::debug_assert!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
core::assert!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
core::panic!(false, 123);
|
||||
//~^ ERROR must be a string literal
|
||||
}
|
101
src/test/ui/rust-2021/panic.stderr
Normal file
101
src/test/ui/rust-2021/panic.stderr
Normal file
@ -0,0 +1,101 @@
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:4:26
|
||||
|
|
||||
LL | debug_assert!(false, 123);
|
||||
| ^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | debug_assert!(false, "{}", 123);
|
||||
| +++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:6:20
|
||||
|
|
||||
LL | assert!(false, 123);
|
||||
| ^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | assert!(false, "{}", 123);
|
||||
| +++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:8:12
|
||||
|
|
||||
LL | panic!(false, 123);
|
||||
| ^^^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | panic!("{} {}", false, 123);
|
||||
| ++++++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:11:31
|
||||
|
|
||||
LL | std::debug_assert!(false, 123);
|
||||
| ^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | std::debug_assert!(false, "{}", 123);
|
||||
| +++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:13:25
|
||||
|
|
||||
LL | std::assert!(false, 123);
|
||||
| ^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | std::assert!(false, "{}", 123);
|
||||
| +++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:15:17
|
||||
|
|
||||
LL | std::panic!(false, 123);
|
||||
| ^^^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | std::panic!("{} {}", false, 123);
|
||||
| ++++++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:18:32
|
||||
|
|
||||
LL | core::debug_assert!(false, 123);
|
||||
| ^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | core::debug_assert!(false, "{}", 123);
|
||||
| +++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:20:26
|
||||
|
|
||||
LL | core::assert!(false, 123);
|
||||
| ^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | core::assert!(false, "{}", 123);
|
||||
| +++++
|
||||
|
||||
error: format argument must be a string literal
|
||||
--> $DIR/panic.rs:22:18
|
||||
|
|
||||
LL | core::panic!(false, 123);
|
||||
| ^^^^^
|
||||
|
|
||||
help: you might be missing a string literal to format with
|
||||
|
|
||||
LL | core::panic!("{} {}", false, 123);
|
||||
| ++++++++
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
@ -506,6 +506,8 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
|
||||
Err(_) => false,
|
||||
},
|
||||
color: config.color,
|
||||
shuffle: false,
|
||||
shuffle_seed: None,
|
||||
test_threads: None,
|
||||
skip: vec![],
|
||||
list: false,
|
||||
|
11
src/tools/lld-wrapper/Cargo.toml
Normal file
11
src/tools/lld-wrapper/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "lld-wrapper"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
ld = []
|
||||
ld64 = []
|
125
src/tools/lld-wrapper/src/main.rs
Normal file
125
src/tools/lld-wrapper/src/main.rs
Normal file
@ -0,0 +1,125 @@
|
||||
//! Script to invoke the bundled rust-lld with the correct flavor. The flavor is selected by
|
||||
//! feature.
|
||||
//!
|
||||
//! lld supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first
|
||||
//! two arguments the `<flavor>` command line interface is used to process the remaining arguments.
|
||||
//! If no `-flavor` argument is present the flavor is determined by the executable name.
|
||||
//!
|
||||
//! In Rust with `-Z gcc-ld=lld` we have gcc or clang invoke rust-lld. Since there is no way to
|
||||
//! make gcc/clang pass `-flavor <flavor>` as the first two arguments in the linker invocation
|
||||
//! and since Windows does not support symbolic links for files this wrapper is used in place of a
|
||||
//! symblic link. It execs `../rust-lld -flavor ld` if the feature `ld` is enabled and
|
||||
//! `../rust-lld -flavor ld64` if `ld64` is enabled. On Windows it spawns a `..\rust-lld.exe`
|
||||
//! child process.
|
||||
|
||||
#[cfg(not(any(feature = "ld", feature = "ld64")))]
|
||||
compile_error!("One of the features ld and ld64 must be enabled.");
|
||||
|
||||
#[cfg(all(feature = "ld", feature = "ld64"))]
|
||||
compile_error!("Only one of the feature ld or ld64 can be enabled.");
|
||||
|
||||
#[cfg(feature = "ld")]
|
||||
const FLAVOR: &str = "ld";
|
||||
|
||||
#[cfg(feature = "ld64")]
|
||||
const FLAVOR: &str = "ld64";
|
||||
|
||||
use std::env;
|
||||
use std::fmt::Display;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
|
||||
trait ResultExt<T, E> {
|
||||
fn unwrap_or_exit_with(self, context: &str) -> T;
|
||||
}
|
||||
|
||||
impl<T, E> ResultExt<T, E> for Result<T, E>
|
||||
where
|
||||
E: Display,
|
||||
{
|
||||
fn unwrap_or_exit_with(self, context: &str) -> T {
|
||||
match self {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
eprintln!("lld-wrapper: {}: {}", context, e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait OptionExt<T> {
|
||||
fn unwrap_or_exit_with(self, context: &str) -> T;
|
||||
}
|
||||
|
||||
impl<T> OptionExt<T> for Option<T> {
|
||||
fn unwrap_or_exit_with(self, context: &str) -> T {
|
||||
match self {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
eprintln!("lld-wrapper: {}", context);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to rust-lld in the parent directory.
|
||||
///
|
||||
/// Exits if the parent directory cannot be determined.
|
||||
fn get_rust_lld_path(current_exe_path: &Path) -> PathBuf {
|
||||
let mut rust_lld_exe_name = "rust-lld".to_owned();
|
||||
rust_lld_exe_name.push_str(env::consts::EXE_SUFFIX);
|
||||
let mut rust_lld_path = current_exe_path
|
||||
.parent()
|
||||
.unwrap_or_exit_with("directory containing current executable could not be determined")
|
||||
.parent()
|
||||
.unwrap_or_exit_with("parent directory could not be determined")
|
||||
.to_owned();
|
||||
rust_lld_path.push(rust_lld_exe_name);
|
||||
rust_lld_path
|
||||
}
|
||||
|
||||
/// Returns the command for invoking rust-lld with the correct flavor.
|
||||
///
|
||||
/// Exits on error.
|
||||
fn get_rust_lld_command(current_exe_path: &Path) -> process::Command {
|
||||
let rust_lld_path = get_rust_lld_path(current_exe_path);
|
||||
let mut command = process::Command::new(rust_lld_path);
|
||||
command.arg("-flavor");
|
||||
command.arg(FLAVOR);
|
||||
command.args(env::args_os().skip(1));
|
||||
command
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn exec_lld(mut command: process::Command) {
|
||||
use std::os::unix::prelude::CommandExt;
|
||||
Result::<(), _>::Err(command.exec()).unwrap_or_exit_with("could not exec rust-lld");
|
||||
unreachable!("lld-wrapper: after exec without error");
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn exec_lld(mut command: process::Command) {
|
||||
// Windows has no exec(), spawn a child process and wait for it
|
||||
let exit_status = command.status().unwrap_or_exit_with("error running rust-lld child process");
|
||||
if !exit_status.success() {
|
||||
match exit_status.code() {
|
||||
Some(code) => {
|
||||
// return the original lld exit code
|
||||
process::exit(code)
|
||||
}
|
||||
None => {
|
||||
eprintln!("lld-wrapper: rust-lld child process exited with error: {}", exit_status,);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let current_exe_path =
|
||||
env::current_exe().unwrap_or_exit_with("could not get the path of the current executable");
|
||||
|
||||
exec_lld(get_rust_lld_command(current_exe_path.as_ref()));
|
||||
}
|
Loading…
Reference in New Issue
Block a user