Replace ExpressionOperandId with enum Operand

Because the three kinds of operand are now distinguished explicitly, we no
longer need fiddly code to disambiguate counter IDs and expression IDs based on
the total number of counters/expressions in a function.

This does increase the size of operands from 4 bytes to 8 bytes, but that
shouldn't be a big deal since they are mostly stored inside boxed structures,
and the current coverage code is not particularly size-optimized anyway.
This commit is contained in:
Zalathar 2023-06-26 22:28:48 +10:00
parent 5a808d40f4
commit 1a014d42f4
11 changed files with 96 additions and 147 deletions

View file

@ -65,9 +65,9 @@ impl CoverageCounters {
fn make_expression<F>(
&mut self,
lhs: ExpressionOperandId,
lhs: Operand,
op: Op,
rhs: ExpressionOperandId,
rhs: Operand,
debug_block_label_fn: F,
) -> CoverageKind
where
@ -81,13 +81,13 @@ impl CoverageCounters {
expression
}
pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind {
pub fn make_identity_counter(&mut self, counter_operand: Operand) -> CoverageKind {
let some_debug_block_label = if self.debug_counters.is_enabled() {
self.debug_counters.some_block_label(counter_operand).cloned()
} else {
None
};
self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || {
self.make_expression(counter_operand, Op::Add, Operand::Zero, || {
some_debug_block_label.clone()
})
}
@ -199,7 +199,7 @@ impl<'a> BcbCounters<'a> {
&mut self,
traversal: &mut TraverseCoverageGraphWithLoops,
branching_bcb: BasicCoverageBlock,
branching_counter_operand: ExpressionOperandId,
branching_counter_operand: Operand,
collect_intermediate_expressions: &mut Vec<CoverageKind>,
) -> Result<(), Error> {
let branches = self.bcb_branches(branching_bcb);
@ -261,7 +261,7 @@ impl<'a> BcbCounters<'a> {
" [new intermediate expression: {}]",
self.format_counter(&intermediate_expression)
);
let intermediate_expression_operand = intermediate_expression.as_operand_id();
let intermediate_expression_operand = intermediate_expression.as_operand();
collect_intermediate_expressions.push(intermediate_expression);
some_sumup_counter_operand.replace(intermediate_expression_operand);
}
@ -298,7 +298,7 @@ impl<'a> BcbCounters<'a> {
&mut self,
bcb: BasicCoverageBlock,
collect_intermediate_expressions: &mut Vec<CoverageKind>,
) -> Result<ExpressionOperandId, Error> {
) -> Result<Operand, Error> {
self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1)
}
@ -307,7 +307,7 @@ impl<'a> BcbCounters<'a> {
bcb: BasicCoverageBlock,
collect_intermediate_expressions: &mut Vec<CoverageKind>,
debug_indent_level: usize,
) -> Result<ExpressionOperandId, Error> {
) -> Result<Operand, Error> {
// If the BCB already has a counter, return it.
if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() {
debug!(
@ -316,7 +316,7 @@ impl<'a> BcbCounters<'a> {
bcb,
self.format_counter(counter_kind),
);
return Ok(counter_kind.as_operand_id());
return Ok(counter_kind.as_operand());
}
// A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
@ -383,7 +383,7 @@ impl<'a> BcbCounters<'a> {
NESTED_INDENT.repeat(debug_indent_level),
self.format_counter(&intermediate_expression)
);
let intermediate_expression_operand = intermediate_expression.as_operand_id();
let intermediate_expression_operand = intermediate_expression.as_operand();
collect_intermediate_expressions.push(intermediate_expression);
some_sumup_edge_counter_operand.replace(intermediate_expression_operand);
}
@ -408,7 +408,7 @@ impl<'a> BcbCounters<'a> {
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
collect_intermediate_expressions: &mut Vec<CoverageKind>,
) -> Result<ExpressionOperandId, Error> {
) -> Result<Operand, Error> {
self.recursive_get_or_make_edge_counter_operand(
from_bcb,
to_bcb,
@ -423,7 +423,7 @@ impl<'a> BcbCounters<'a> {
to_bcb: BasicCoverageBlock,
collect_intermediate_expressions: &mut Vec<CoverageKind>,
debug_indent_level: usize,
) -> Result<ExpressionOperandId, Error> {
) -> Result<Operand, Error> {
// If the source BCB has only one successor (assumed to be the given target), an edge
// counter is unnecessary. Just get or make a counter for the source BCB.
let successors = self.bcb_successors(from_bcb).iter();
@ -444,7 +444,7 @@ impl<'a> BcbCounters<'a> {
to_bcb,
self.format_counter(counter_kind)
);
return Ok(counter_kind.as_operand_id());
return Ok(counter_kind.as_operand());
}
// Make a new counter to count this edge.

View file

@ -246,7 +246,7 @@ impl Default for ExpressionFormat {
}
}
/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `ExpressionOperandId`) to
/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `Operand`) to
/// the `CoverageKind` data and optional label (normally, the counter's associated
/// `BasicCoverageBlock` format string, if any).
///
@ -258,7 +258,7 @@ impl Default for ExpressionFormat {
/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be
/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`.
pub(super) struct DebugCounters {
some_counters: Option<FxHashMap<ExpressionOperandId, DebugCounter>>,
some_counters: Option<FxHashMap<Operand, DebugCounter>>,
}
impl DebugCounters {
@ -277,14 +277,14 @@ impl DebugCounters {
pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option<String>) {
if let Some(counters) = &mut self.some_counters {
let id = counter_kind.as_operand_id();
let id = counter_kind.as_operand();
counters
.try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
.expect("attempt to add the same counter_kind to DebugCounters more than once");
}
}
pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> {
pub fn some_block_label(&self, operand: Operand) -> Option<&String> {
self.some_counters.as_ref().and_then(|counters| {
counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref())
})
@ -323,24 +323,24 @@ impl DebugCounters {
}
}
let id = counter_kind.as_operand_id();
let id = counter_kind.as_operand();
if self.some_counters.is_some() && (counter_format.block || !counter_format.id) {
let counters = self.some_counters.as_ref().unwrap();
if let Some(DebugCounter { some_block_label: Some(block_label), .. }) =
counters.get(&id)
{
return if counter_format.id {
format!("{}#{}", block_label, id.index())
format!("{}#{:?}", block_label, id)
} else {
block_label.to_string()
};
}
}
format!("#{}", id.index())
format!("#{:?}", id)
}
fn format_operand(&self, operand: ExpressionOperandId) -> String {
if operand.index() == 0 {
fn format_operand(&self, operand: Operand) -> String {
if matches!(operand, Operand::Zero) {
return String::from("0");
}
if let Some(counters) = &self.some_counters {
@ -358,7 +358,7 @@ impl DebugCounters {
return self.format_counter_kind(counter_kind);
}
}
format!("#{}", operand.index())
format!("#{:?}", operand)
}
}
@ -485,8 +485,7 @@ impl GraphvizData {
/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs
/// and/or a `CoverageGraph` graphviz output).
pub(super) struct UsedExpressions {
some_used_expression_operands:
Option<FxHashMap<ExpressionOperandId, Vec<InjectedExpressionId>>>,
some_used_expression_operands: Option<FxHashMap<Operand, Vec<InjectedExpressionId>>>,
some_unused_expressions:
Option<Vec<(CoverageKind, Option<BasicCoverageBlock>, BasicCoverageBlock)>>,
}
@ -517,7 +516,7 @@ impl UsedExpressions {
pub fn expression_is_used(&self, expression: &CoverageKind) -> bool {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
used_expression_operands.contains_key(&expression.as_operand_id())
used_expression_operands.contains_key(&expression.as_operand())
} else {
false
}
@ -530,7 +529,7 @@ impl UsedExpressions {
target_bcb: BasicCoverageBlock,
) {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
if !used_expression_operands.contains_key(&expression.as_operand_id()) {
if !used_expression_operands.contains_key(&expression.as_operand()) {
self.some_unused_expressions.as_mut().unwrap().push((
expression.clone(),
edge_from_bcb,

View file

@ -345,10 +345,7 @@ impl BasicCoverageBlockData {
&mir_body[self.last_bb()].terminator()
}
pub fn set_counter(
&mut self,
counter_kind: CoverageKind,
) -> Result<ExpressionOperandId, Error> {
pub fn set_counter(&mut self, counter_kind: CoverageKind) -> Result<Operand, Error> {
debug_assert!(
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
// have an expression (to be injected into an existing `BasicBlock` represented by this
@ -356,7 +353,7 @@ impl BasicCoverageBlockData {
self.edge_from_bcbs.is_none() || counter_kind.is_expression(),
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
);
let operand = counter_kind.as_operand_id();
let operand = counter_kind.as_operand();
if let Some(replaced) = self.counter_kind.replace(counter_kind) {
Error::from_string(format!(
"attempt to set a BasicCoverageBlock coverage counter more than once; \
@ -381,7 +378,7 @@ impl BasicCoverageBlockData {
&mut self,
from_bcb: BasicCoverageBlock,
counter_kind: CoverageKind,
) -> Result<ExpressionOperandId, Error> {
) -> Result<Operand, Error> {
if level_enabled!(tracing::Level::DEBUG) {
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
// have an expression (to be injected into an existing `BasicBlock` represented by this
@ -393,7 +390,7 @@ impl BasicCoverageBlockData {
));
}
}
let operand = counter_kind.as_operand_id();
let operand = counter_kind.as_operand();
if let Some(replaced) =
self.edge_from_bcbs.get_or_insert_default().insert(from_bcb, counter_kind)
{

View file

@ -304,7 +304,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() {
self.coverage_counters.make_identity_counter(counter_operand)
} else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() {
bcb_counters[bcb] = Some(counter_kind.as_operand_id());
bcb_counters[bcb] = Some(counter_kind.as_operand());
debug_used_expressions.add_expression_operands(&counter_kind);
counter_kind
} else {

View file

@ -47,39 +47,24 @@ impl CoverageVisitor {
/// final computed number of counters should be the number of all `CoverageKind::Counter`
/// statements in the MIR *plus one* for the implicit `ZERO` counter.
#[inline(always)]
fn update_num_counters(&mut self, counter_id: u32) {
fn update_num_counters(&mut self, counter_id: CounterValueReference) {
let counter_id = counter_id.as_u32();
self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
}
/// Computes an expression index for each expression ID, and updates `num_expressions` to the
/// maximum encountered index plus 1.
#[inline(always)]
fn update_num_expressions(&mut self, expression_id: u32) {
let expression_index = u32::MAX - expression_id;
fn update_num_expressions(&mut self, expression_id: InjectedExpressionId) {
let expression_index = u32::MAX - expression_id.as_u32();
self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1);
}
fn update_from_expression_operand(&mut self, operand_id: u32) {
if operand_id >= self.info.num_counters {
let operand_as_expression_index = u32::MAX - operand_id;
if operand_as_expression_index >= self.info.num_expressions {
// The operand ID is outside the known range of counter IDs and also outside the
// known range of expression IDs. In either case, the result of a missing operand
// (if and when used in an expression) will be zero, so from a computation
// perspective, it doesn't matter whether it is interpreted as a counter or an
// expression.
//
// However, the `num_counters` and `num_expressions` query results are used to
// allocate arrays when generating the coverage map (during codegen), so choose
// the type that grows either `num_counters` or `num_expressions` the least.
if operand_id - self.info.num_counters
< operand_as_expression_index - self.info.num_expressions
{
self.update_num_counters(operand_id)
} else {
self.update_num_expressions(operand_id)
}
}
fn update_from_expression_operand(&mut self, operand: Operand) {
match operand {
Operand::Counter(id) => self.update_num_counters(id),
Operand::Expression(id) => self.update_num_expressions(id),
Operand::Zero => {}
}
}
@ -100,19 +85,15 @@ impl CoverageVisitor {
if self.add_missing_operands {
match coverage.kind {
CoverageKind::Expression { lhs, rhs, .. } => {
self.update_from_expression_operand(u32::from(lhs));
self.update_from_expression_operand(u32::from(rhs));
self.update_from_expression_operand(lhs);
self.update_from_expression_operand(rhs);
}
_ => {}
}
} else {
match coverage.kind {
CoverageKind::Counter { id, .. } => {
self.update_num_counters(u32::from(id));
}
CoverageKind::Expression { id, .. } => {
self.update_num_expressions(u32::from(id));
}
CoverageKind::Counter { id, .. } => self.update_num_counters(id),
CoverageKind::Expression { id, .. } => self.update_num_expressions(id),
_ => {}
}
}