current commit
251004.9 · deps: update V8 to 14.1.146.11
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_WASM_WASM_CODE_COVERAGE_H_
#define V8_WASM_WASM_CODE_COVERAGE_H_

#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif  // !V8_ENABLE_WEBASSEMBLY

#include <stddef.h>
#include <stdint.h>

#include "src/base/platform/mutex.h"
#include "src/base/vector.h"
#include "src/wasm/wasm-opcodes.h"

namespace v8::internal::wasm {

// Represents a range of code byte offsets in the form [start, end], inclusive
// at both ends.
// Offsets are calculated from the start of the function wire bytes, not from
// the start of the module.
struct WasmCodeRange {
  constexpr WasmCodeRange() = default;
  constexpr WasmCodeRange(int start, int end) : start(start), end(end) {}

  constexpr bool operator==(const WasmCodeRange& rhs) const = default;

  int start = kNoCodePosition;
  int end = kNoCodePosition;

 private:
  static constexpr int kNoCodePosition = -1;
};

class WasmFunctionCoverageData {
 public:
  explicit WasmFunctionCoverageData(
      base::Vector<const WasmCodeRange> code_ranges) {
    counts_ = base::OwnedVector<uint32_t>::New(code_ranges.size());
    code_ranges_ = base::OwnedCopyOf(code_ranges);
  }

  const base::Vector<const WasmCodeRange> code_ranges() const {
    return base::VectorOf(code_ranges_);
  }

  const base::Vector<uint32_t> counters() const {
    return base::VectorOf(counts_);
  }

 private:
  base::OwnedVector<const WasmCodeRange> code_ranges_;
  base::OwnedVector<uint32_t> counts_;
};

class WasmModuleCoverageData {
 public:
  explicit WasmModuleCoverageData(uint32_t declared_function_count) {
    function_data_ =
        base::OwnedVector<std::unique_ptr<WasmFunctionCoverageData>>::New(
            declared_function_count);
  }

  WasmFunctionCoverageData* InstallCoverageData(
      int declared_function_index,
      base::Vector<const WasmCodeRange> code_ranges) {
    base::MutexGuard guard{&mutex_};

    if (!function_data_[declared_function_index]) {
      function_data_[declared_function_index] =
          std::make_unique<WasmFunctionCoverageData>(code_ranges);
    } else {
      DCHECK_EQ(function_data_[declared_function_index]->code_ranges(),
                code_ranges);
    }
    return function_data_[declared_function_index].get();
  }

  size_t function_count() const { return function_data_.size(); }

  WasmFunctionCoverageData* GetFunctionCoverageData(
      size_t function_index) const {
    base::MutexGuard guard{&mutex_};

    DCHECK_LT(function_index, function_data_.size());
    return function_data_[function_index].get();
  }

 private:
  // There is a single WasmModuleCoverageData per NativeModule, that can be
  // accessed concurrently from multiple Isolates.
  mutable base::Mutex mutex_;
  base::OwnedVector<std::unique_ptr<WasmFunctionCoverageData>> function_data_;
};

template <typename FullDecoder>
class WasmCoverageInstrumentation {
 public:
  WasmCoverageInstrumentation(WasmFunctionCoverageData* coverage_data) {
    DCHECK(v8_flags.wasm_code_coverage);
    DCHECK_NOT_NULL(coverage_data);
    coverage_code_ranges_ = coverage_data->code_ranges();
    coverage_current_code_range_ = coverage_code_ranges_.begin();
    coverage_count_array_addr_ =
        reinterpret_cast<Address>(coverage_data->counters().data());
  }

  template <typename EmitCoverageIncFunc>
  void EmitCoverageInstrumentationIfReachable(
      FullDecoder* decoder, WasmOpcode opcode,
      EmitCoverageIncFunc emit_coverage_inc) {
    if (!decoder->control_at(0)->reachable()) return;

    if (coverage_current_code_range_ == coverage_code_ranges_.end()) return;

    while (coverage_current_code_range_->end < decoder->position()) {
      ++coverage_current_code_range_;
    }
    if (coverage_current_code_range_->start <= decoder->position()) {
      // Emit code coverage instrumentation.
      Address counter_addr =
          coverage_count_array_addr_ +
          (coverage_current_code_range_ - coverage_code_ranges_.begin()) *
              sizeof(uint32_t);
      emit_coverage_inc(counter_addr);
      ++coverage_current_code_range_;
    }
  }

 private:
  // A precalculated set of code ranges that define the basic blocks in the
  // current function. Note that the NativeModule can be deleted during the
  // compilation, but these code ranges are kept alive via a shared_ptr in the
  // CompilationEnv.
  base::Vector<const WasmCodeRange> coverage_code_ranges_;

  // Points to the current WasmCodeRange during compilation, it's used to decide
  // whether we are in a new basic block and we need to emit coverage
  // instrumentation.
  const WasmCodeRange* coverage_current_code_range_ = nullptr;

  // Points to the start of an array of uint32_t counters that represent the
  // coverage count for each basic block in the function. We emit
  // instrumentation code to increase the counter with {emit_inc_i32_at}.
  Address coverage_count_array_addr_ = 0;
};

}  // namespace v8::internal::wasm
#endif  // V8_WASM_WASM_CODE_COVERAGE_H_