Skip to content

Commit 52bfceb

Browse files
authored
Rollup merge of #113428 - Zalathar:operand, r=davidtwco
coverage: Replace `ExpressionOperandId` with enum `Operand` *This is one step in my larger coverage refactoring ambitions described at <https://github.com/rust-lang/compiler-team/issues/645>.* LLVM coverage has a concept of “mapping expressions” that allow a span's execution count to be computed as a simple arithmetic expression over other counters/expressions, instead of requiring a dedicated physical counter for every control-flow branch. These expressions have an operator (`+` or `-`) and two operands. Operands are currently represented as `ExpressionOperandId`, which wraps a `u32` with the following semantics: - 0 represents a special counter that always has a value of zero - Values ascending from 1 represent counter IDs - Values descending from `u32::MAX` represent the IDs of other expressions --- This change replaces that whole `ExpressionOperandId` scheme with a simple enum that explicitly distinguishes between the three cases. This lets us remove a lot of fiddly code for dealing with the different operand kinds: - Previously it was only possible to distinguish between counter-ID operands and expression-ID operands by comparing the operand ID with the total number of counters in a function. This is unnecessary now that the enum distinguishes them explicitly. - There's no need for expression IDs to descend from `u32::MAX` and then get translated into zero-based indices in certain places. Now that they ascend from zero, they can be used as indices directly. - There's no need to reserve ID number 0 for the special zero operand, since it can just have its own variant in the enum, so counter IDs can count up from 0. (Making counter IDs ascend from 0 also lets us fix an off-by-one error in the query for counting the total number of counters, which would cause LLVM to emit an extra unused counter for every instrumented function.) --- This PR may be easiest to review as individual patches, since that breaks it up into clearly distinct parts: - Replace a `u32` wrapper with an explicit enum, without changing the semantics of the underlying IDs being stored. - Change the numbering scheme used by `Operand::Expression` to make expression IDs ascend from 0 (instead of descending from `u32::MAX`). - Change the numbering scheme used by `Operand::Counter` to make counter IDs ascend from 0 (instead of ascending from 1).
2 parents c97af34 + 3920e07 commit 52bfceb

15 files changed

+163
-250
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex};
1+
use rustc_middle::mir::coverage::{CounterId, MappedExpressionIndex};
22

33
/// Must match the layout of `LLVMRustCounterKind`.
44
#[derive(Copy, Clone, Debug)]
@@ -36,11 +36,9 @@ impl Counter {
3636
Self { kind: CounterKind::Zero, id: 0 }
3737
}
3838

39-
/// Constructs a new `Counter` of kind `CounterValueReference`, and converts
40-
/// the given 1-based counter_id to the required 0-based equivalent for
41-
/// the `Counter` encoding.
42-
pub fn counter_value_reference(counter_id: CounterValueReference) -> Self {
43-
Self { kind: CounterKind::CounterValueReference, id: counter_id.zero_based_index() }
39+
/// Constructs a new `Counter` of kind `CounterValueReference`.
40+
pub fn counter_value_reference(counter_id: CounterId) -> Self {
41+
Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() }
4442
}
4543

4644
/// Constructs a new `Counter` of kind `Expression`.

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+25-62
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,22 @@ pub use super::ffi::*;
33
use rustc_index::{IndexSlice, IndexVec};
44
use rustc_middle::bug;
55
use rustc_middle::mir::coverage::{
6-
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId,
7-
InjectedExpressionIndex, MappedExpressionIndex, Op,
6+
CodeRegion, CounterId, ExpressionId, MappedExpressionIndex, Op, Operand,
87
};
98
use rustc_middle::ty::Instance;
109
use rustc_middle::ty::TyCtxt;
1110

1211
#[derive(Clone, Debug, PartialEq)]
1312
pub struct Expression {
14-
lhs: ExpressionOperandId,
13+
lhs: Operand,
1514
op: Op,
16-
rhs: ExpressionOperandId,
15+
rhs: Operand,
1716
region: Option<CodeRegion>,
1817
}
1918

2019
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
2120
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
22-
/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they
23-
/// can both be operands in an expression. This struct also stores the `function_source_hash`,
21+
/// for a given Function. This struct also stores the `function_source_hash`,
2422
/// computed during instrumentation, and forwarded with counters.
2523
///
2624
/// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap
@@ -34,8 +32,8 @@ pub struct FunctionCoverage<'tcx> {
3432
instance: Instance<'tcx>,
3533
source_hash: u64,
3634
is_used: bool,
37-
counters: IndexVec<CounterValueReference, Option<CodeRegion>>,
38-
expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>,
35+
counters: IndexVec<CounterId, Option<CodeRegion>>,
36+
expressions: IndexVec<ExpressionId, Option<Expression>>,
3937
unreachable_regions: Vec<CodeRegion>,
4038
}
4139

@@ -82,48 +80,36 @@ impl<'tcx> FunctionCoverage<'tcx> {
8280
}
8381

8482
/// Adds a code region to be counted by an injected counter intrinsic.
85-
pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) {
83+
pub fn add_counter(&mut self, id: CounterId, region: CodeRegion) {
8684
if let Some(previous_region) = self.counters[id].replace(region.clone()) {
8785
assert_eq!(previous_region, region, "add_counter: code region for id changed");
8886
}
8987
}
9088

9189
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
92-
/// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression
93-
/// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in
94-
/// any order, and expressions can still be assigned contiguous (though descending) IDs, without
95-
/// knowing what the last counter ID will be.
96-
///
97-
/// When storing the expression data in the `expressions` vector in the `FunctionCoverage`
98-
/// struct, its vector index is computed, from the given expression ID, by subtracting from
99-
/// `u32::MAX`.
100-
///
101-
/// Since the expression operands (`lhs` and `rhs`) can reference either counters or
102-
/// expressions, an operand that references an expression also uses its original ID, descending
103-
/// from `u32::MAX`. Theses operands are translated only during code generation, after all
104-
/// counters and expressions have been added.
90+
/// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
91+
/// between operands that are counter IDs and operands that are expression IDs.
10592
pub fn add_counter_expression(
10693
&mut self,
107-
expression_id: InjectedExpressionId,
108-
lhs: ExpressionOperandId,
94+
expression_id: ExpressionId,
95+
lhs: Operand,
10996
op: Op,
110-
rhs: ExpressionOperandId,
97+
rhs: Operand,
11198
region: Option<CodeRegion>,
11299
) {
113100
debug!(
114101
"add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}",
115102
expression_id, lhs, op, rhs, region
116103
);
117-
let expression_index = self.expression_index(u32::from(expression_id));
118104
debug_assert!(
119-
expression_index.as_usize() < self.expressions.len(),
120-
"expression_index {} is out of range for expressions.len() = {}
105+
expression_id.as_usize() < self.expressions.len(),
106+
"expression_id {} is out of range for expressions.len() = {}
121107
for {:?}",
122-
expression_index.as_usize(),
108+
expression_id.as_usize(),
123109
self.expressions.len(),
124110
self,
125111
);
126-
if let Some(previous_expression) = self.expressions[expression_index].replace(Expression {
112+
if let Some(previous_expression) = self.expressions[expression_id].replace(Expression {
127113
lhs,
128114
op,
129115
rhs,
@@ -186,14 +172,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
186172

187173
// This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or
188174
// `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type
189-
// and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range
190-
// of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value
191-
// matches the injected counter index); and any other value is converted into a
192-
// `CounterKind::Expression` with the expression's `new_index`.
175+
// and value.
193176
//
194177
// Expressions will be returned from this function in a sequential vector (array) of
195178
// `CounterExpression`, so the expression IDs must be mapped from their original,
196-
// potentially sparse set of indexes, originally in reverse order from `u32::MAX`.
179+
// potentially sparse set of indexes.
197180
//
198181
// An `Expression` as an operand will have already been encountered as an `Expression` with
199182
// operands, so its new_index will already have been generated (as a 1-up index value).
@@ -206,34 +189,19 @@ impl<'tcx> FunctionCoverage<'tcx> {
206189
// `expression_index`s lower than the referencing `Expression`. Therefore, it is
207190
// reasonable to look up the new index of an expression operand while the `new_indexes`
208191
// vector is only complete up to the current `ExpressionIndex`.
209-
let id_to_counter = |new_indexes: &IndexSlice<
210-
InjectedExpressionIndex,
211-
Option<MappedExpressionIndex>,
212-
>,
213-
id: ExpressionOperandId| {
214-
if id == ExpressionOperandId::ZERO {
215-
Some(Counter::zero())
216-
} else if id.index() < self.counters.len() {
217-
debug_assert!(
218-
id.index() > 0,
219-
"ExpressionOperandId indexes for counters are 1-based, but this id={}",
220-
id.index()
221-
);
222-
// Note: Some codegen-injected Counters may be only referenced by `Expression`s,
223-
// and may not have their own `CodeRegion`s,
224-
let index = CounterValueReference::from(id.index());
225-
// Note, the conversion to LLVM `Counter` adjusts the index to be zero-based.
226-
Some(Counter::counter_value_reference(index))
227-
} else {
228-
let index = self.expression_index(u32::from(id));
192+
type NewIndexes = IndexSlice<ExpressionId, Option<MappedExpressionIndex>>;
193+
let id_to_counter = |new_indexes: &NewIndexes, operand: Operand| match operand {
194+
Operand::Zero => Some(Counter::zero()),
195+
Operand::Counter(id) => Some(Counter::counter_value_reference(id)),
196+
Operand::Expression(id) => {
229197
self.expressions
230-
.get(index)
198+
.get(id)
231199
.expect("expression id is out of range")
232200
.as_ref()
233201
// If an expression was optimized out, assume it would have produced a count
234202
// of zero. This ensures that expressions dependent on optimized-out
235203
// expressions are still valid.
236-
.map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression))
204+
.map_or(Some(Counter::zero()), |_| new_indexes[id].map(Counter::expression))
237205
}
238206
};
239207

@@ -340,9 +308,4 @@ impl<'tcx> FunctionCoverage<'tcx> {
340308
fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
341309
self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
342310
}
343-
344-
fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex {
345-
debug_assert!(id_descending_from_max >= self.counters.len() as u32);
346-
InjectedExpressionIndex::from(u32::MAX - id_descending_from_max)
347-
}
348311
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ use rustc_hir as hir;
1616
use rustc_hir::def_id::DefId;
1717
use rustc_llvm::RustString;
1818
use rustc_middle::bug;
19-
use rustc_middle::mir::coverage::{
20-
CodeRegion, CounterValueReference, CoverageKind, ExpressionOperandId, InjectedExpressionId, Op,
21-
};
19+
use rustc_middle::mir::coverage::{CodeRegion, CounterId, CoverageKind, ExpressionId, Op, Operand};
2220
use rustc_middle::mir::Coverage;
2321
use rustc_middle::ty;
2422
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
@@ -33,7 +31,7 @@ mod ffi;
3331
pub(crate) mod map_data;
3432
pub mod mapgen;
3533

36-
const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START;
34+
const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START;
3735

3836
const VAR_ALIGN_BYTES: usize = 8;
3937

@@ -125,7 +123,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
125123
let fn_name = bx.get_pgo_func_name_var(instance);
126124
let hash = bx.const_u64(function_source_hash);
127125
let num_counters = bx.const_u32(coverageinfo.num_counters);
128-
let index = bx.const_u32(id.zero_based_index());
126+
let index = bx.const_u32(id.as_u32());
129127
debug!(
130128
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
131129
fn_name, hash, num_counters, index,
@@ -178,7 +176,7 @@ impl<'tcx> Builder<'_, '_, 'tcx> {
178176
fn add_coverage_counter(
179177
&mut self,
180178
instance: Instance<'tcx>,
181-
id: CounterValueReference,
179+
id: CounterId,
182180
region: CodeRegion,
183181
) -> bool {
184182
if let Some(coverage_context) = self.coverage_context() {
@@ -202,10 +200,10 @@ impl<'tcx> Builder<'_, '_, 'tcx> {
202200
fn add_coverage_counter_expression(
203201
&mut self,
204202
instance: Instance<'tcx>,
205-
id: InjectedExpressionId,
206-
lhs: ExpressionOperandId,
203+
id: ExpressionId,
204+
lhs: Operand,
207205
op: Op,
208-
rhs: ExpressionOperandId,
206+
rhs: Operand,
209207
region: Option<CodeRegion>,
210208
) -> bool {
211209
if let Some(coverage_context) = self.coverage_context() {

0 commit comments

Comments
 (0)