authorbors <bors@rust-lang.org> 2024-09-17 03:40:29 UTC
committerbors <bors@rust-lang.org> 2024-09-17 03:40:29 UTC
logc8dff289a0c931e138350f3baef0fab6d3309f80
treee1dc41383b1d8f2168cd94185804e66dc8b38b94
parente2dc1a1c0f97a90319181a721ab317210307617a
parent558b302af739045bb2685707947169ad853645ea

Auto merge of #130456 - matthiaskrgr:rollup-h2qvk1f, r=matthiaskrgr

Rollup of 4 pull requests Successful merges: - #130380 (coverage: Clarify some parts of coverage counter creation) - #130427 (run_make_support: rectify symlink handling) - #130447 (rustc_llvm: update for llvm/llvm-project@2ae968a0d9fb61606b020e898d88…) - #130448 (fix: Remove duplicate `LazyLock` example.) r? `@ghost` `@rustbot` modify labels: rollup

10 files changed, 334 insertions(+), 235 deletions(-)

compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h+6-1
......@@ -25,7 +25,6 @@
2525#include "llvm/Target/TargetMachine.h"
2626#include "llvm/Target/TargetOptions.h"
2727#include "llvm/Transforms/IPO.h"
28#include "llvm/Transforms/Instrumentation.h"
2928#include "llvm/Transforms/Scalar.h"
3029
3130#define LLVM_VERSION_GE(major, minor) \
......@@ -34,6 +33,12 @@
3433
3534#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor)))
3635
36#if LLVM_VERSION_GE(20, 0)
37#include "llvm/Transforms/Utils/Instrumentation.h"
38#else
39#include "llvm/Transforms/Instrumentation.h"
40#endif
41
3742#include "llvm/IR/LegacyPassManager.h"
3843
3944#include "llvm/Bitcode/BitcodeReader.h"
compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp-1
......@@ -39,7 +39,6 @@
3939#include "llvm/TargetParser/Host.h"
4040#endif
4141#include "llvm/Support/TimeProfiler.h"
42#include "llvm/Transforms/Instrumentation.h"
4342#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
4443#include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h"
4544#if LLVM_VERSION_GE(19, 0)
compiler/rustc_mir_transform/src/coverage/counters.rs+109-122
......@@ -95,11 +95,33 @@ impl CoverageCounters {
9595 this
9696 }
9797
98 fn make_counter(&mut self, site: CounterIncrementSite) -> BcbCounter {
98 /// Shared helper used by [`Self::make_phys_node_counter`] and
99 /// [`Self::make_phys_edge_counter`]. Don't call this directly.
100 fn make_counter_inner(&mut self, site: CounterIncrementSite) -> BcbCounter {
99101 let id = self.counter_increment_sites.push(site);
100102 BcbCounter::Counter { id }
101103 }
102104
105 /// Creates a new physical counter attached a BCB node.
106 /// The node must not already have a counter.
107 fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
108 let counter = self.make_counter_inner(CounterIncrementSite::Node { bcb });
109 debug!(?bcb, ?counter, "node gets a physical counter");
110 self.set_bcb_counter(bcb, counter)
111 }
112
113 /// Creates a new physical counter attached to a BCB edge.
114 /// The edge must not already have a counter.
115 fn make_phys_edge_counter(
116 &mut self,
117 from_bcb: BasicCoverageBlock,
118 to_bcb: BasicCoverageBlock,
119 ) -> BcbCounter {
120 let counter = self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb });
121 debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter");
122 self.set_bcb_edge_counter(from_bcb, to_bcb, counter)
123 }
124
103125 fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
104126 let new_expr = BcbExpression { lhs, op, rhs };
105127 *self
......@@ -294,25 +316,27 @@ impl<'a> MakeBcbCounters<'a> {
294316
295317 let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice();
296318
297 // If this node doesn't have multiple out-edges, or all of its out-edges
298 // already have counters, then we don't need to create edge counters.
299 let needs_out_edge_counters = successors.len() > 1
300 && successors.iter().any(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb));
301 if !needs_out_edge_counters {
319 // If this node's out-edges won't sum to the node's counter,
320 // then there's no reason to create edge counters here.
321 if !self.basic_coverage_blocks[from_bcb].is_out_summable {
302322 return;
303323 }
304324
305 if tracing::enabled!(tracing::Level::DEBUG) {
306 let _span =
307 debug_span!("node has some out-edges without counters", ?from_bcb).entered();
308 for &to_bcb in successors {
309 debug!(?to_bcb, counter=?self.edge_counter(from_bcb, to_bcb));
310 }
311 }
325 // Determine the set of out-edges that don't yet have edge counters.
326 let candidate_successors = self.basic_coverage_blocks.successors[from_bcb]
327 .iter()
328 .copied()
329 .filter(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb))
330 .collect::<Vec<_>>();
331 debug!(?candidate_successors);
312332
313 // Of the out-edges that don't have counters yet, one can be given an expression
314 // (computed from the other out-edges) instead of a dedicated counter.
315 let expression_to_bcb = self.choose_out_edge_for_expression(traversal, from_bcb);
333 // If there are out-edges without counters, choose one to be given an expression
334 // (computed from this node and the other out-edges) instead of a physical counter.
335 let Some(expression_to_bcb) =
336 self.choose_out_edge_for_expression(traversal, &candidate_successors)
337 else {
338 return;
339 };
316340
317341 // For each out-edge other than the one that was chosen to get an expression,
318342 // ensure that it has a counter (existing counter/expression or a new counter),
......@@ -324,10 +348,11 @@ impl<'a> MakeBcbCounters<'a> {
324348 .filter(|&to_bcb| to_bcb != expression_to_bcb)
325349 .map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb))
326350 .collect::<Vec<_>>();
327 let sum_of_all_other_out_edges: BcbCounter = self
328 .coverage_counters
329 .make_sum(&other_out_edge_counters)
330 .expect("there must be at least one other out-edge");
351 let Some(sum_of_all_other_out_edges) =
352 self.coverage_counters.make_sum(&other_out_edge_counters)
353 else {
354 return;
355 };
331356
332357 // Now create an expression for the chosen edge, by taking the counter
333358 // for its source node and subtracting the sum of its sibling out-edges.
......@@ -338,10 +363,13 @@ impl<'a> MakeBcbCounters<'a> {
338363 );
339364
340365 debug!("{expression_to_bcb:?} gets an expression: {expression:?}");
341 if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) {
342 self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
343 } else {
366 if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(expression_to_bcb) {
367 // This edge normally wouldn't get its own counter, so attach the expression
368 // to its target node instead, so that `edge_has_no_counter` can see it.
369 assert_eq!(sole_pred, from_bcb);
344370 self.coverage_counters.set_bcb_counter(expression_to_bcb, expression);
371 } else {
372 self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
345373 }
346374 }
347375
......@@ -353,28 +381,21 @@ impl<'a> MakeBcbCounters<'a> {
353381 return counter_kind;
354382 }
355383
356 // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
357 // Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the
358 // program results in a tight infinite loop, but it should still compile.
359 let one_path_to_target = !self.basic_coverage_blocks.bcb_has_multiple_in_edges(bcb);
360 if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
361 let counter_kind =
362 self.coverage_counters.make_counter(CounterIncrementSite::Node { bcb });
363 if one_path_to_target {
364 debug!("{bcb:?} gets a new counter: {counter_kind:?}");
365 } else {
366 debug!(
367 "{bcb:?} has itself as its own predecessor. It can't be part of its own \
368 Expression sum, so it will get its own new counter: {counter_kind:?}. \
369 (Note, the compiled code will generate an infinite loop.)",
370 );
371 }
372 return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
384 let predecessors = self.basic_coverage_blocks.predecessors[bcb].as_slice();
385
386 // Handle cases where we can't compute a node's count from its in-edges:
387 // - START_BCB has no in-edges, so taking the sum would panic (or be wrong).
388 // - For nodes with one in-edge, or that directly loop to themselves,
389 // trying to get the in-edge counts would require this node's counter,
390 // leading to infinite recursion.
391 if predecessors.len() <= 1 || predecessors.contains(&bcb) {
392 debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor");
393 return self.coverage_counters.make_phys_node_counter(bcb);
373394 }
374395
375396 // A BCB with multiple incoming edges can compute its count by ensuring that counters
376397 // exist for each of those edges, and then adding them up to get a total count.
377 let in_edge_counters = self.basic_coverage_blocks.predecessors[bcb]
398 let in_edge_counters = predecessors
378399 .iter()
379400 .copied()
380401 .map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb))
......@@ -394,16 +415,19 @@ impl<'a> MakeBcbCounters<'a> {
394415 from_bcb: BasicCoverageBlock,
395416 to_bcb: BasicCoverageBlock,
396417 ) -> BcbCounter {
397 // If the target BCB has only one in-edge (i.e. this one), then create
398 // a node counter instead, since it will have the same value.
399 if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
400 assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]);
418 // If the target node has exactly one in-edge (i.e. this one), then just
419 // use the node's counter, since it will have the same value.
420 if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) {
421 assert_eq!(sole_pred, from_bcb);
422 // This call must take care not to invoke `get_or_make_edge` for
423 // this edge, since that would result in infinite recursion!
401424 return self.get_or_make_node_counter(to_bcb);
402425 }
403426
404 // If the source BCB has only one successor (assumed to be the given target), an edge
405 // counter is unnecessary. Just get or make a counter for the source BCB.
406 if self.bcb_successors(from_bcb).len() == 1 {
427 // If the source node has exactly one out-edge (i.e. this one) and would have
428 // the same execution count as that edge, then just use the node's counter.
429 if let Some(simple_succ) = self.basic_coverage_blocks.simple_successor(from_bcb) {
430 assert_eq!(simple_succ, to_bcb);
407431 return self.get_or_make_node_counter(from_bcb);
408432 }
409433
......@@ -416,118 +440,81 @@ impl<'a> MakeBcbCounters<'a> {
416440 }
417441
418442 // Make a new counter to count this edge.
419 let counter_kind =
420 self.coverage_counters.make_counter(CounterIncrementSite::Edge { from_bcb, to_bcb });
421 debug!("Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}");
422 self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
443 self.coverage_counters.make_phys_edge_counter(from_bcb, to_bcb)
423444 }
424445
425 /// Choose one of the out-edges of `from_bcb` to receive an expression
426 /// instead of a physical counter, and returns that edge's target node.
427 ///
428 /// - Precondition: The node must have at least one out-edge without a counter.
429 /// - Postcondition: The selected edge does not have an edge counter.
446 /// Given a set of candidate out-edges (represented by their successor node),
447 /// choose one to be given a counter expression instead of a physical counter.
430448 fn choose_out_edge_for_expression(
431449 &self,
432450 traversal: &TraverseCoverageGraphWithLoops<'_>,
433 from_bcb: BasicCoverageBlock,
434 ) -> BasicCoverageBlock {
435 if let Some(reloop_target) = self.find_good_reloop_edge(traversal, from_bcb) {
436 assert!(self.edge_has_no_counter(from_bcb, reloop_target));
451 candidate_successors: &[BasicCoverageBlock],
452 ) -> Option<BasicCoverageBlock> {
453 // Try to find a candidate that leads back to the top of a loop,
454 // because reloop edges tend to be executed more times than loop-exit edges.
455 if let Some(reloop_target) = self.find_good_reloop_edge(traversal, &candidate_successors) {
437456 debug!("Selecting reloop target {reloop_target:?} to get an expression");
438 return reloop_target;
457 return Some(reloop_target);
439458 }
440459
441 // We couldn't identify a "good" edge, so just choose any edge that
442 // doesn't already have a counter.
443 let arbitrary_target = self
444 .bcb_successors(from_bcb)
445 .iter()
446 .copied()
447 .find(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb))
448 .expect("precondition: at least one out-edge without a counter");
460 // We couldn't identify a "good" edge, so just choose an arbitrary one.
461 let arbitrary_target = candidate_successors.first().copied()?;
449462 debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression");
450 arbitrary_target
463 Some(arbitrary_target)
451464 }
452465
453 /// Tries to find an edge that leads back to the top of a loop, and that
454 /// doesn't already have a counter. Such edges are good candidates to
455 /// be given an expression (instead of a physical counter), because they
456 /// will tend to be executed more times than a loop-exit edge.
466 /// Given a set of candidate out-edges (represented by their successor node),
467 /// tries to find one that leads back to the top of a loop.
468 ///
469 /// Reloop edges are good candidates for counter expressions, because they
470 /// will tend to be executed more times than a loop-exit edge, so it's nice
471 /// for them to be able to avoid a physical counter increment.
457472 fn find_good_reloop_edge(
458473 &self,
459474 traversal: &TraverseCoverageGraphWithLoops<'_>,
460 from_bcb: BasicCoverageBlock,
475 candidate_successors: &[BasicCoverageBlock],
461476 ) -> Option<BasicCoverageBlock> {
462 let successors = self.bcb_successors(from_bcb);
477 // If there are no candidates, avoid iterating over the loop stack.
478 if candidate_successors.is_empty() {
479 return None;
480 }
463481
464482 // Consider each loop on the current traversal context stack, top-down.
465483 for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
466 let mut all_edges_exit_this_loop = true;
467
468 // Try to find an out-edge that doesn't exit this loop and doesn't
469 // already have a counter.
470 for &target_bcb in successors {
484 // Try to find a candidate edge that doesn't exit this loop.
485 for &target_bcb in candidate_successors {
471486 // An edge is a reloop edge if its target dominates any BCB that has
472487 // an edge back to the loop header. (Otherwise it's an exit edge.)
473488 let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| {
474489 self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb)
475490 });
476
477491 if is_reloop_edge {
478 all_edges_exit_this_loop = false;
479 if self.edge_has_no_counter(from_bcb, target_bcb) {
480 // We found a good out-edge to be given an expression.
481 return Some(target_bcb);
482 }
483 // Keep looking for another reloop edge without a counter.
484 } else {
485 // This edge exits the loop.
492 // We found a good out-edge to be given an expression.
493 return Some(target_bcb);
486494 }
487495 }
488496
489 if !all_edges_exit_this_loop {
490 // We found one or more reloop edges, but all of them already
491 // have counters. Let the caller choose one of the other edges.
492 debug!("All reloop edges had counters; skipping the other loops");
493 return None;
494 }
495
496 // All of the out-edges exit this loop, so keep looking for a good
497 // reloop edge for one of the outer loops.
497 // All of the candidate edges exit this loop, so keep looking
498 // for a good reloop edge for one of the outer loops.
498499 }
499500
500501 None
501502 }
502503
503 #[inline]
504 fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] {
505 &self.basic_coverage_blocks.predecessors[bcb]
506 }
507
508 #[inline]
509 fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] {
510 &self.basic_coverage_blocks.successors[bcb]
511 }
512
513504 #[inline]
514505 fn edge_has_no_counter(
515506 &self,
516507 from_bcb: BasicCoverageBlock,
517508 to_bcb: BasicCoverageBlock,
518509 ) -> bool {
519 self.edge_counter(from_bcb, to_bcb).is_none()
520 }
510 let edge_counter =
511 if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) {
512 assert_eq!(sole_pred, from_bcb);
513 self.coverage_counters.bcb_counters[to_bcb]
514 } else {
515 self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)).copied()
516 };
521517
522 fn edge_counter(
523 &self,
524 from_bcb: BasicCoverageBlock,
525 to_bcb: BasicCoverageBlock,
526 ) -> Option<&BcbCounter> {
527 if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
528 self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
529 } else {
530 self.coverage_counters.bcb_counters[to_bcb].as_ref()
531 }
518 edge_counter.is_none()
532519 }
533520}
compiler/rustc_mir_transform/src/coverage/graph.rs+61-29
......@@ -87,7 +87,11 @@ impl CoverageGraph {
8787 for &bb in basic_blocks.iter() {
8888 bb_to_bcb[bb] = Some(bcb);
8989 }
90 let bcb_data = BasicCoverageBlockData::from(basic_blocks);
90
91 let is_out_summable = basic_blocks.last().map_or(false, |&bb| {
92 bcb_filtered_successors(mir_body[bb].terminator()).is_out_summable()
93 });
94 let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable };
9195 debug!("adding bcb{}: {:?}", bcb.index(), bcb_data);
9296 bcbs.push(bcb_data);
9397 };
......@@ -161,23 +165,33 @@ impl CoverageGraph {
161165 self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
162166 }
163167
164 /// Returns true if the given node has 2 or more in-edges, i.e. 2 or more
165 /// predecessors.
166 ///
167 /// This property is interesting to code that assigns counters to nodes and
168 /// edges, because if a node _doesn't_ have multiple in-edges, then there's
169 /// no benefit in having a separate counter for its in-edge, because it
170 /// would have the same value as the node's own counter.
171 ///
172 /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
173 #[inline(always)]
174 pub(crate) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool {
175 // Even though bcb0 conceptually has an extra virtual in-edge due to
176 // being the entry point, we've already asserted that it has no _other_
177 // in-edges, so there's no possibility of it having _multiple_ in-edges.
178 // (And since its virtual in-edge doesn't exist in the graph, that edge
179 // can't have a separate counter anyway.)
180 self.predecessors[bcb].len() > 1
168 /// Returns the source of this node's sole in-edge, if it has exactly one.
169 /// That edge can be assumed to have the same execution count as the node
170 /// itself (in the absence of panics).
171 pub(crate) fn sole_predecessor(
172 &self,
173 to_bcb: BasicCoverageBlock,
174 ) -> Option<BasicCoverageBlock> {
175 // Unlike `simple_successor`, there is no need for extra checks here.
176 if let &[from_bcb] = self.predecessors[to_bcb].as_slice() { Some(from_bcb) } else { None }
177 }
178
179 /// Returns the target of this node's sole out-edge, if it has exactly
180 /// one, but only if that edge can be assumed to have the same execution
181 /// count as the node itself (in the absence of panics).
182 pub(crate) fn simple_successor(
183 &self,
184 from_bcb: BasicCoverageBlock,
185 ) -> Option<BasicCoverageBlock> {
186 // If a node's count is the sum of its out-edges, and it has exactly
187 // one out-edge, then that edge has the same count as the node.
188 if self.bcbs[from_bcb].is_out_summable
189 && let &[to_bcb] = self.successors[from_bcb].as_slice()
190 {
191 Some(to_bcb)
192 } else {
193 None
194 }
181195 }
182196}
183197
......@@ -266,14 +280,16 @@ rustc_index::newtype_index! {
266280#[derive(Debug, Clone)]
267281pub(crate) struct BasicCoverageBlockData {
268282 pub(crate) basic_blocks: Vec<BasicBlock>,
283
284 /// If true, this node's execution count can be assumed to be the sum of the
285 /// execution counts of all of its **out-edges** (assuming no panics).
286 ///
287 /// Notably, this is false for a node ending with [`TerminatorKind::Yield`],
288 /// because the yielding coroutine might not be resumed.
289 pub(crate) is_out_summable: bool,
269290}
270291
271292impl BasicCoverageBlockData {
272 fn from(basic_blocks: Vec<BasicBlock>) -> Self {
273 assert!(basic_blocks.len() > 0);
274 Self { basic_blocks }
275 }
276
277293 #[inline(always)]
278294 pub(crate) fn leader_bb(&self) -> BasicBlock {
279295 self.basic_blocks[0]
......@@ -295,6 +311,9 @@ enum CoverageSuccessors<'a> {
295311 Chainable(BasicBlock),
296312 /// The block cannot be combined into the same BCB as its successor(s).
297313 NotChainable(&'a [BasicBlock]),
314 /// Yield terminators are not chainable, and their execution count can also
315 /// differ from the execution count of their out-edge.
316 Yield(BasicBlock),
298317}
299318
300319impl CoverageSuccessors<'_> {
......@@ -302,6 +321,17 @@ impl CoverageSuccessors<'_> {
302321 match self {
303322 Self::Chainable(_) => true,
304323 Self::NotChainable(_) => false,
324 Self::Yield(_) => false,
325 }
326 }
327
328 /// Returns true if the terminator itself is assumed to have the same
329 /// execution count as the sum of its out-edges (assuming no panics).
330 fn is_out_summable(&self) -> bool {
331 match self {
332 Self::Chainable(_) => true,
333 Self::NotChainable(_) => true,
334 Self::Yield(_) => false,
305335 }
306336 }
307337}
......@@ -312,7 +342,9 @@ impl IntoIterator for CoverageSuccessors<'_> {
312342
313343 fn into_iter(self) -> Self::IntoIter {
314344 match self {
315 Self::Chainable(bb) => Some(bb).into_iter().chain((&[]).iter().copied()),
345 Self::Chainable(bb) | Self::Yield(bb) => {
346 Some(bb).into_iter().chain((&[]).iter().copied())
347 }
316348 Self::NotChainable(bbs) => None.into_iter().chain(bbs.iter().copied()),
317349 }
318350 }
......@@ -331,7 +363,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
331363
332364 // A yield terminator has exactly 1 successor, but should not be chained,
333365 // because its resume edge has a different execution count.
334 Yield { ref resume, .. } => CoverageSuccessors::NotChainable(std::slice::from_ref(resume)),
366 Yield { resume, .. } => CoverageSuccessors::Yield(resume),
335367
336368 // These terminators have exactly one coverage-relevant successor,
337369 // and can be chained into it.
......@@ -341,15 +373,15 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
341373 | FalseUnwind { real_target: target, .. }
342374 | Goto { target } => CoverageSuccessors::Chainable(target),
343375
344 // A call terminator can normally be chained, except when they have no
345 // successor because they are known to diverge.
376 // A call terminator can normally be chained, except when it has no
377 // successor because it is known to diverge.
346378 Call { target: maybe_target, .. } => match maybe_target {
347379 Some(target) => CoverageSuccessors::Chainable(target),
348380 None => CoverageSuccessors::NotChainable(&[]),
349381 },
350382
351 // An inline asm terminator can normally be chained, except when it diverges or uses asm
352 // goto.
383 // An inline asm terminator can normally be chained, except when it
384 // diverges or uses asm goto.
353385 InlineAsm { ref targets, .. } => {
354386 if let [target] = targets[..] {
355387 CoverageSuccessors::Chainable(target)
library/std/src/sync/lazy_lock.rs-2
......@@ -44,8 +44,6 @@ union Data<T, F> {
4444///
4545/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`.
4646/// let _ = &*DEEP_THOUGHT;
47/// // The `String` is retrieved from the `LazyLock` and returned as `&String`.
48/// let _ = &*DEEP_THOUGHT;
4947/// ```
5048///
5149/// Initialize fields with `LazyLock`.
src/tools/run-make-support/src/fs.rs+130-50
......@@ -1,42 +1,6 @@
11use std::io;
22use std::path::{Path, PathBuf};
33
4// FIXME(jieyouxu): modify create_symlink to panic on windows.
5
6/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix.
7#[cfg(target_family = "windows")]
8pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
9 if link.as_ref().exists() {
10 std::fs::remove_dir(link.as_ref()).unwrap();
11 }
12 if original.as_ref().is_file() {
13 std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!(
14 "failed to create symlink {:?} for {:?}",
15 link.as_ref().display(),
16 original.as_ref().display(),
17 ));
18 } else {
19 std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()).expect(&format!(
20 "failed to create symlink {:?} for {:?}",
21 link.as_ref().display(),
22 original.as_ref().display(),
23 ));
24 }
25}
26
27/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix.
28#[cfg(target_family = "unix")]
29pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
30 if link.as_ref().exists() {
31 std::fs::remove_dir(link.as_ref()).unwrap();
32 }
33 std::os::unix::fs::symlink(original.as_ref(), link.as_ref()).expect(&format!(
34 "failed to create symlink {:?} for {:?}",
35 link.as_ref().display(),
36 original.as_ref().display(),
37 ));
38}
39
404/// Copy a directory into another.
415pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
426 fn copy_dir_all_inner(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
......@@ -50,7 +14,31 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
5014 if ty.is_dir() {
5115 copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?;
5216 } else if ty.is_symlink() {
53 copy_symlink(entry.path(), dst.join(entry.file_name()))?;
17 // Traverse symlink once to find path of target entity.
18 let target_path = std::fs::read_link(entry.path())?;
19
20 let new_symlink_path = dst.join(entry.file_name());
21 #[cfg(windows)]
22 {
23 use std::os::windows::fs::FileTypeExt;
24 if ty.is_symlink_dir() {
25 std::os::windows::fs::symlink_dir(&target_path, new_symlink_path)?;
26 } else {
27 // Target may be a file or another symlink, in any case we can use
28 // `symlink_file` here.
29 std::os::windows::fs::symlink_file(&target_path, new_symlink_path)?;
30 }
31 }
32 #[cfg(unix)]
33 {
34 std::os::unix::fs::symlink(target_path, new_symlink_path)?;
35 }
36 #[cfg(not(any(windows, unix)))]
37 {
38 // Technically there's also wasi, but I have no clue about wasi symlink
39 // semantics and which wasi targets / environment support symlinks.
40 unimplemented!("unsupported target");
41 }
5442 } else {
5543 std::fs::copy(entry.path(), dst.join(entry.file_name()))?;
5644 }
......@@ -69,12 +57,6 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
6957 }
7058}
7159
72fn copy_symlink<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
73 let target_path = std::fs::read_link(from).unwrap();
74 create_symlink(target_path, to);
75 Ok(())
76}
77
7860/// Helper for reading entries in a given directory.
7961pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F) {
8062 for entry in read_dir(dir) {
......@@ -85,8 +67,17 @@ pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F
8567/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message.
8668#[track_caller]
8769pub fn remove_file<P: AsRef<Path>>(path: P) {
88 std::fs::remove_file(path.as_ref())
89 .expect(&format!("the file in path \"{}\" could not be removed", path.as_ref().display()));
70 if let Err(e) = std::fs::remove_file(path.as_ref()) {
71 panic!("failed to remove file at `{}`: {e}", path.as_ref().display());
72 }
73}
74
75/// A wrapper around [`std::fs::remove_dir`] which includes the directory path in the panic message.
76#[track_caller]
77pub fn remove_dir<P: AsRef<Path>>(path: P) {
78 if let Err(e) = std::fs::remove_dir(path.as_ref()) {
79 panic!("failed to remove directory at `{}`: {e}", path.as_ref().display());
80 }
9081}
9182
9283/// A wrapper around [`std::fs::copy`] which includes the file path in the panic message.
......@@ -165,13 +156,32 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) {
165156 ));
166157}
167158
168/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message.
159/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. Note
160/// that this will traverse symlinks and will return metadata about the target file. Use
161/// [`symlink_metadata`] if you don't want to traverse symlinks.
162///
163/// See [`std::fs::metadata`] docs for more details.
169164#[track_caller]
170165pub fn metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata {
171 std::fs::metadata(path.as_ref()).expect(&format!(
172 "the file's metadata in path \"{}\" could not be read",
173 path.as_ref().display()
174 ))
166 match std::fs::metadata(path.as_ref()) {
167 Ok(m) => m,
168 Err(e) => panic!("failed to read file metadata at `{}`: {e}", path.as_ref().display()),
169 }
170}
171
172/// A wrapper around [`std::fs::symlink_metadata`] which includes the file path in the panic
173/// message. Note that this will not traverse symlinks and will return metadata about the filesystem
174/// entity itself. Use [`metadata`] if you want to traverse symlinks.
175///
176/// See [`std::fs::symlink_metadata`] docs for more details.
177#[track_caller]
178pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata {
179 match std::fs::symlink_metadata(path.as_ref()) {
180 Ok(m) => m,
181 Err(e) => {
182 panic!("failed to read file metadata (shallow) at `{}`: {e}", path.as_ref().display())
183 }
184 }
175185}
176186
177187/// A wrapper around [`std::fs::rename`] which includes the file path in the panic message.
......@@ -205,3 +215,73 @@ pub fn shallow_find_dir_entries<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
205215 }
206216 output
207217}
218
219/// Create a new symbolic link to a directory.
220///
221/// # Removing the symlink
222///
223/// - On Windows, a symlink-to-directory needs to be removed with a corresponding [`fs::remove_dir`]
224/// and not [`fs::remove_file`].
225/// - On Unix, remove the symlink with [`fs::remove_file`].
226///
227/// [`fs::remove_dir`]: crate::fs::remove_dir
228/// [`fs::remove_file`]: crate::fs::remove_file
229pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
230 #[cfg(unix)]
231 {
232 if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) {
233 panic!(
234 "failed to create symlink: original=`{}`, link=`{}`: {e}",
235 original.as_ref().display(),
236 link.as_ref().display()
237 );
238 }
239 }
240 #[cfg(windows)]
241 {
242 if let Err(e) = std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()) {
243 panic!(
244 "failed to create symlink-to-directory: original=`{}`, link=`{}`: {e}",
245 original.as_ref().display(),
246 link.as_ref().display()
247 );
248 }
249 }
250 #[cfg(not(any(windows, unix)))]
251 {
252 unimplemented!("target family not currently supported")
253 }
254}
255
256/// Create a new symbolic link to a file.
257///
258/// # Removing the symlink
259///
260/// On both Windows and Unix, a symlink-to-file needs to be removed with a corresponding
261/// [`fs::remove_file`](crate::fs::remove_file) and not [`fs::remove_dir`](crate::fs::remove_dir).
262pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) {
263 #[cfg(unix)]
264 {
265 if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) {
266 panic!(
267 "failed to create symlink: original=`{}`, link=`{}`: {e}",
268 original.as_ref().display(),
269 link.as_ref().display()
270 );
271 }
272 }
273 #[cfg(windows)]
274 {
275 if let Err(e) = std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()) {
276 panic!(
277 "failed to create symlink-to-file: original=`{}`, link=`{}`: {e}",
278 original.as_ref().display(),
279 link.as_ref().display()
280 );
281 }
282 }
283 #[cfg(not(any(windows, unix)))]
284 {
285 unimplemented!("target family not currently supported")
286 }
287}
tests/run-make/invalid-symlink-search-path/rmake.rs+9-8
......@@ -1,13 +1,14 @@
1// In this test, the symlink created is invalid (valid relative to the root, but not
2// relatively to where it is located), and used to cause an internal
3// compiler error (ICE) when passed as a library search path. This was fixed in #26044,
4// and this test checks that the invalid symlink is instead simply ignored.
1// In this test, the symlink created is invalid (valid relative to the root, but not relatively to
2// where it is located), and used to cause an internal compiler error (ICE) when passed as a library
3// search path. This was fixed in #26044, and this test checks that the invalid symlink is instead
4// simply ignored.
5//
56// See https://github.com/rust-lang/rust/issues/26006
67
78//@ needs-symlink
89//Reason: symlink requires elevated permission in Windows
910
10use run_make_support::{rfs, rustc};
11use run_make_support::{path, rfs, rustc};
1112
1213fn main() {
1314 // We create two libs: `bar` which depends on `foo`. We need to compile `foo` first.
......@@ -20,9 +21,9 @@ fn main() {
2021 .metadata("foo")
2122 .output("out/foo/libfoo.rlib")
2223 .run();
23 rfs::create_dir("out/bar");
24 rfs::create_dir("out/bar/deps");
25 rfs::create_symlink("out/foo/libfoo.rlib", "out/bar/deps/libfoo.rlib");
24 rfs::create_dir_all("out/bar/deps");
25 rfs::symlink_file(path("out/foo/libfoo.rlib"), path("out/bar/deps/libfoo.rlib"));
26
2627 // Check that the invalid symlink does not cause an ICE
2728 rustc()
2829 .input("in/bar/lib.rs")
tests/run-make/symlinked-extern/rmake.rs+12-12
......@@ -1,22 +1,22 @@
1// Crates that are resolved normally have their path canonicalized and all
2// symlinks resolved. This did not happen for paths specified
3// using the --extern option to rustc, which could lead to rustc thinking
4// that it encountered two different versions of a crate, when it's
5// actually the same version found through different paths.
6// See https://github.com/rust-lang/rust/pull/16505
7
8// This test checks that --extern and symlinks together
9// can result in successful compilation.
1// Crates that are resolved normally have their path canonicalized and all symlinks resolved. This
2// did not happen for paths specified using the `--extern` option to rustc, which could lead to
3// rustc thinking that it encountered two different versions of a crate, when it's actually the same
4// version found through different paths.
5//
6// This test checks that `--extern` and symlinks together can result in successful compilation.
7//
8// See <https://github.com/rust-lang/rust/pull/16505>.
109
1110//@ ignore-cross-compile
1211//@ needs-symlink
1312
14use run_make_support::{cwd, rfs, rustc};
13use run_make_support::{cwd, path, rfs, rustc};
1514
1615fn main() {
1716 rustc().input("foo.rs").run();
1817 rfs::create_dir_all("other");
19 rfs::create_symlink("libfoo.rlib", "other");
18 rfs::symlink_file(path("libfoo.rlib"), path("other").join("libfoo.rlib"));
19
2020 rustc().input("bar.rs").library_search_path(cwd()).run();
21 rustc().input("baz.rs").extern_("foo", "other").library_search_path(cwd()).run();
21 rustc().input("baz.rs").extern_("foo", "other/libfoo.rlib").library_search_path(cwd()).run();
2222}
tests/run-make/symlinked-libraries/rmake.rs+6-9
......@@ -1,18 +1,15 @@
1// When a directory and a symlink simultaneously exist with the same name,
2// setting that name as the library search path should not cause rustc
3// to avoid looking in the symlink and cause an error. This test creates
4// a directory and a symlink named "other", and places the library in the symlink.
5// If it succeeds, the library was successfully found.
6// See https://github.com/rust-lang/rust/issues/12459
1// Avoid erroring on symlinks pointing to the same file that are present in the library search path.
2//
3// See <https://github.com/rust-lang/rust/issues/12459>.
74
85//@ ignore-cross-compile
96//@ needs-symlink
107
11use run_make_support::{dynamic_lib_name, rfs, rustc};
8use run_make_support::{cwd, dynamic_lib_name, path, rfs, rustc};
129
1310fn main() {
1411 rustc().input("foo.rs").arg("-Cprefer-dynamic").run();
1512 rfs::create_dir_all("other");
16 rfs::create_symlink(dynamic_lib_name("foo"), "other");
17 rustc().input("bar.rs").library_search_path("other").run();
13 rfs::symlink_file(dynamic_lib_name("foo"), path("other").join(dynamic_lib_name("foo")));
14 rustc().input("bar.rs").library_search_path(cwd()).library_search_path("other").run();
1815}
tests/run-make/symlinked-rlib/rmake.rs+1-1
......@@ -12,6 +12,6 @@ use run_make_support::{cwd, rfs, rustc};
1212
1313fn main() {
1414 rustc().input("foo.rs").crate_type("rlib").output("foo.xxx").run();
15 rfs::create_symlink("foo.xxx", "libfoo.rlib");
15 rfs::symlink_file("foo.xxx", "libfoo.rlib");
1616 rustc().input("bar.rs").library_search_path(cwd()).run();
1717}