summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/mir/coverage.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/mir/coverage.rs')
-rw-r--r--compiler/rustc_middle/src/mir/coverage.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
new file mode 100644
index 000000000..efa946452
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -0,0 +1,186 @@
+//! Metadata from source code coverage analysis and instrumentation.
+
+use rustc_macros::HashStable;
+use rustc_span::Symbol;
+
+use std::cmp::Ord;
+use std::fmt::{self, Debug, Formatter};
+
+rustc_index::newtype_index! {
+ /// An ExpressionOperandId value is assigned directly from either a
+ /// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32()
+ /// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a
+ /// constant value of `0`.
+ pub struct ExpressionOperandId {
+ derive [HashStable]
+ DEBUG_FORMAT = "ExpressionOperandId({})",
+ MAX = 0xFFFF_FFFF,
+ }
+}
+
+impl ExpressionOperandId {
+ /// An expression operand for a "zero counter", as described in the following references:
+ ///
+ /// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter>
+ /// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#tag>
+ /// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter-expressions>
+ ///
+ /// This operand can be used to count two or more separate code regions with a single counter,
+ /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for
+ /// one of the code regions, and inserting `CounterExpression`s ("add ZERO to the counter") in
+ /// the coverage map for the other code regions.
+ pub const ZERO: Self = Self::from_u32(0);
+}
+
+rustc_index::newtype_index! {
+ pub struct CounterValueReference {
+ derive [HashStable]
+ DEBUG_FORMAT = "CounterValueReference({})",
+ MAX = 0xFFFF_FFFF,
+ }
+}
+
+impl CounterValueReference {
+ /// Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO.
+ pub const START: Self = Self::from_u32(1);
+
+ /// Returns explicitly-requested zero-based version of the counter id, used
+ /// during codegen. LLVM expects zero-based indexes.
+ pub fn zero_based_index(self) -> u32 {
+ let one_based_index = self.as_u32();
+ debug_assert!(one_based_index > 0);
+ one_based_index - 1
+ }
+}
+
+rustc_index::newtype_index! {
+ /// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32()
+ ///
+ /// Values descend from u32::MAX.
+ pub struct InjectedExpressionId {
+ derive [HashStable]
+ DEBUG_FORMAT = "InjectedExpressionId({})",
+ MAX = 0xFFFF_FFFF,
+ }
+}
+
+rustc_index::newtype_index! {
+ /// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32()
+ ///
+ /// Values ascend from 0.
+ pub struct InjectedExpressionIndex {
+ derive [HashStable]
+ DEBUG_FORMAT = "InjectedExpressionIndex({})",
+ MAX = 0xFFFF_FFFF,
+ }
+}
+
+rustc_index::newtype_index! {
+ /// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their
+ /// array position in the LLVM coverage map "Expressions" array, which is assembled during the
+ /// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s.
+ pub struct MappedExpressionIndex {
+ derive [HashStable]
+ DEBUG_FORMAT = "MappedExpressionIndex({})",
+ MAX = 0xFFFF_FFFF,
+ }
+}
+
+impl From<CounterValueReference> for ExpressionOperandId {
+ #[inline]
+ fn from(v: CounterValueReference) -> ExpressionOperandId {
+ ExpressionOperandId::from(v.as_u32())
+ }
+}
+
+impl From<InjectedExpressionId> for ExpressionOperandId {
+ #[inline]
+ fn from(v: InjectedExpressionId) -> ExpressionOperandId {
+ ExpressionOperandId::from(v.as_u32())
+ }
+}
+
+#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub enum CoverageKind {
+ Counter {
+ function_source_hash: u64,
+ id: CounterValueReference,
+ },
+ Expression {
+ id: InjectedExpressionId,
+ lhs: ExpressionOperandId,
+ op: Op,
+ rhs: ExpressionOperandId,
+ },
+ Unreachable,
+}
+
+impl CoverageKind {
+ pub fn as_operand_id(&self) -> ExpressionOperandId {
+ use CoverageKind::*;
+ match *self {
+ Counter { id, .. } => ExpressionOperandId::from(id),
+ Expression { id, .. } => ExpressionOperandId::from(id),
+ Unreachable => bug!("Unreachable coverage cannot be part of an expression"),
+ }
+ }
+
+ pub fn is_expression(&self) -> bool {
+ matches!(self, Self::Expression { .. })
+ }
+}
+
+impl Debug for CoverageKind {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ use CoverageKind::*;
+ match self {
+ Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
+ Expression { id, lhs, op, rhs } => write!(
+ fmt,
+ "Expression({:?}) = {} {} {}",
+ id.index(),
+ lhs.index(),
+ if *op == Op::Add { "+" } else { "-" },
+ rhs.index(),
+ ),
+ Unreachable => write!(fmt, "Unreachable"),
+ }
+ }
+}
+
+#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub struct CodeRegion {
+ pub file_name: Symbol,
+ pub start_line: u32,
+ pub start_col: u32,
+ pub end_line: u32,
+ pub end_col: u32,
+}
+
+impl Debug for CodeRegion {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ write!(
+ fmt,
+ "{}:{}:{} - {}:{}",
+ self.file_name, self.start_line, self.start_col, self.end_line, self.end_col
+ )
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum Op {
+ Subtract,
+ Add,
+}
+
+impl Op {
+ pub fn is_add(&self) -> bool {
+ matches!(self, Self::Add)
+ }
+
+ pub fn is_subtract(&self) -> bool {
+ matches!(self, Self::Subtract)
+ }
+}