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:
bors 2021-10-08 06:16:31 +00:00
commit 3013b26947
65 changed files with 1034 additions and 123 deletions

View File

@ -1965,6 +1965,10 @@ dependencies = [
"walkdir",
]
[[package]]
name = "lld-wrapper"
version = "0.1.0"
[[package]]
name = "lock_api"
version = "0.4.1"

View File

@ -36,6 +36,7 @@ members = [
"src/tools/jsondocck",
"src/tools/html-checker",
"src/tools/bump-stage0",
"src/tools/lld-wrapper",
]
exclude = [

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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(

View File

@ -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);

View File

@ -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,

View File

@ -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();

View File

@ -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));
}

View File

@ -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,

View File

@ -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")));

View File

@ -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)

View File

@ -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

View File

@ -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,
}
}
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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>,

View File

@ -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)
}
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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],

View File

@ -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 {

View File

@ -570,6 +570,7 @@ symbols! {
dyn_metadata,
dyn_trait,
edition_macro_pats,
edition_panic,
eh_catch_typeinfo,
eh_personality,
emit_enum,

View File

@ -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
}

View File

@ -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)*); })
}

View File

@ -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.

View File

@ -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>() {

View File

@ -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;

View File

@ -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),

View File

@ -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
))
}

View File

@ -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\"?>")

View File

@ -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(

View File

@ -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<()> {

View File

@ -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<()> {

View File

@ -5,3 +5,4 @@ pub mod concurrency;
pub mod exit_code;
pub mod isatty;
pub mod metrics;
pub mod shuffle;

View 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()
}

View File

@ -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>();

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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`

View File

@ -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

View File

@ -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

View 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");
}
}

View 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

View File

@ -0,0 +1,5 @@
extern crate raw_dylib_test;
fn main() {
raw_dylib_test::library_function();
}

View File

@ -0,0 +1,5 @@
#include <stdio.h>
void exported_function() {
printf("exported_function\n");
}

View File

@ -0,0 +1,3 @@
LIBRARY exporter
EXPORTS
exported_function @13 NONAME

View 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();
}
}

View File

@ -0,0 +1 @@
exported_function

View File

@ -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() {}

View File

@ -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

View 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() {}

View 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

View File

@ -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();
}

View File

@ -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

View File

@ -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() {}

View File

@ -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

View 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
}

View 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

View File

@ -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,

View 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 = []

View 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()));
}