mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Support weight in coverage (#8472)
This commit is contained in:
@@ -305,6 +305,34 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
// Internals: VlFileLineDebug stores a SystemVerilog source code location. Used in
|
||||
// VlCoroutineHandle for debugging purposes, and to locate covergroup weight errors.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
|
||||
int m_lineno = 0; // Line number from sources
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
class VlExecutionProfilerBase;
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class VlFileLineDebug;
|
||||
|
||||
// Per-bin classification. A bin's kind is which set it lives in (structural),
|
||||
// not a per-bin field. Only Normal feeds coverage(); the rest are recorded.
|
||||
// Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with
|
||||
@@ -50,6 +52,9 @@ enum class VlCovBinKind : uint8_t {
|
||||
/// Bounded bin count, so random access by index is the primary usage.
|
||||
|
||||
class VlCoverpointIf VL_NOT_FINAL {
|
||||
// MEMBERS
|
||||
int32_t m_weight = 1; // option.weight; never negative
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
virtual ~VlCoverpointIf() = default;
|
||||
@@ -62,6 +67,12 @@ public:
|
||||
virtual std::string binName(uint32_t i) const = 0;
|
||||
// Bins covered / effective total (Normal set only) for the coverage calc
|
||||
virtual void coverageParts(double& covered, double& total) const = 0;
|
||||
/// Weight of this item in its covergroup instance's coverage (option.weight,
|
||||
/// IEEE 1800-2023 19.11)
|
||||
int32_t weight() const { return m_weight; }
|
||||
/// Load option.weight, as evaluated by the covergroup constructor. A negative
|
||||
/// weight is reported as an error, and counts as zero.
|
||||
void weight(uint32_t value, VlFileLineDebug fileline);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
@@ -1155,12 +1155,75 @@ void VlCoverCrossDyn::finalizeBins() {
|
||||
//=============================================================================
|
||||
// VlCovergroupInst
|
||||
|
||||
// IEEE 1800-2023 19.11: coverage is the weighted average of the contributions; with a zero
|
||||
// denominator it is 0.0, or 100.0 when the covergroup's weight is zero
|
||||
static double _vl_cov_calculate(double weighted, double weights, int32_t weight) VL_PURE {
|
||||
if (weights == 0.0) return weight ? 0.0 : 100.0;
|
||||
return weighted / weights;
|
||||
}
|
||||
|
||||
// IEEE 1800-2023 19.7: a weight shall be non-negative. A negative constant is rejected when
|
||||
// verilating; a weight that is negative only at run time is reported as it is loaded, and
|
||||
// counts as zero, so that coverage stays within 0..100.
|
||||
static int32_t _vl_cov_load_weight(const char* optionp, IData value,
|
||||
VlFileLineDebug fileline) VL_MT_SAFE {
|
||||
const int32_t weight = static_cast<int32_t>(value);
|
||||
if (VL_LIKELY(weight >= 0)) return weight;
|
||||
const char* filep = ""; // VlFileLineDebug keeps the location only under VL_DEBUG
|
||||
int line = 0;
|
||||
#ifdef VL_DEBUG
|
||||
filep = fileline.filename();
|
||||
line = fileline.lineno();
|
||||
#else
|
||||
static_cast<void>(fileline);
|
||||
#endif
|
||||
const std::string where = filep && filep[0]
|
||||
? std::string{filep} + ":" + std::to_string(line) + ": "
|
||||
: std::string{};
|
||||
VL_PRINTF_MT("%%Error: %sCoverage option '%s' is set to negative value '%d';"
|
||||
" weights must be non-negative (IEEE 1800-2023 19.7)\n",
|
||||
where.c_str(), optionp, static_cast<int>(weight));
|
||||
VL_STOP_MT(filep, line, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VlCoverpointIf::weight(uint32_t value, VlFileLineDebug fileline) {
|
||||
m_weight = _vl_cov_load_weight("option.weight", value, fileline);
|
||||
}
|
||||
|
||||
VlCoverCrossDyn* VlCovergroupInst::addCrossDyn() {
|
||||
VlCoverCrossDyn* const cxp = new VlCoverCrossDyn{};
|
||||
m_items.emplace_back(cxp);
|
||||
return cxp;
|
||||
}
|
||||
|
||||
void VlCovergroupInst::loadWeight() {
|
||||
// Only a new value, so that each negative value is reported once
|
||||
if (!m_weightp || *m_weightp == m_loadedWeight) return;
|
||||
m_loadedWeight = *m_weightp;
|
||||
m_weight = _vl_cov_load_weight("option.weight", m_loadedWeight, m_fileline);
|
||||
}
|
||||
|
||||
std::pair<double, double> VlCovergroupInst::coverageSums() const {
|
||||
double weighted = 0.0;
|
||||
double weights = 0.0;
|
||||
for (const auto& itemp : m_items) {
|
||||
double covered = 0.0;
|
||||
double total = 0.0;
|
||||
itemp->coverageParts(covered, total);
|
||||
if (total == 0.0) continue; // No bins: excluded from both sums
|
||||
weighted += itemp->weight() * (covered / total);
|
||||
weights += itemp->weight();
|
||||
}
|
||||
return {100.0 * weighted, weights};
|
||||
}
|
||||
|
||||
double VlCovergroupInst::coverage() {
|
||||
loadWeight();
|
||||
const std::pair<double, double> sums = coverageSums();
|
||||
return _vl_cov_calculate(sums.first, sums.second, m_weight);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlCovergroupType / VlCovRegistry
|
||||
|
||||
@@ -1174,19 +1237,19 @@ VlCovergroupInst* VlCovergroupType::newInstance() {
|
||||
return instp;
|
||||
}
|
||||
|
||||
void VlCovergroupType::foldResidue(const VlCovergroupInst* instp) {
|
||||
double covered = 0.0;
|
||||
double total = 0.0;
|
||||
instp->coverageParts(covered, total);
|
||||
void VlCovergroupType::foldResidue(VlCovergroupInst* instp) {
|
||||
const std::pair<double, double> sums = instp->coverageSums();
|
||||
// Nothing coverable: excluded from both sums, so it moves neither the mean
|
||||
// nor the denominator. Never-sampled is different: it has bins, none hit,
|
||||
// and folds as 0%.
|
||||
if (total == 0.0) return;
|
||||
// TODO(P5): IEEE 1800-2023 19.5 defines covergroup coverage as the weighted
|
||||
// mean of the per-item ratios, not the ratio of the summed parts. This
|
||||
// matches what the generated get_inst_coverage() computes today, so that a
|
||||
// live instance and the same instance one delta after death never disagree.
|
||||
m_retired.sumCoverage += 100.0 * covered / total;
|
||||
if (sums.second == 0.0) return;
|
||||
// The same weighted average of the items as get_inst_coverage(), so that a live
|
||||
// instance and the same instance one delta after death never disagree. With the
|
||||
// weight last loaded: the object that lent option.weight is gone, and nothing may
|
||||
// be reported here, as this can run after ~VerilatedContext (see ~VlCovRegistry).
|
||||
const int32_t weight = instp->weight();
|
||||
m_retired.sumCoverage += weight * (sums.first / sums.second);
|
||||
m_retired.sumWeight += weight;
|
||||
++m_retired.count;
|
||||
}
|
||||
|
||||
@@ -1231,9 +1294,28 @@ bool VlCovergroupType::anyAttached() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
double VlCovergroupType::coverage(IData typeWeight, VlFileLineDebug fileline) {
|
||||
if (typeWeight != m_loadedTypeWeight) { // Only a new value, as in loadWeight()
|
||||
m_loadedTypeWeight = typeWeight;
|
||||
m_typeWeight = _vl_cov_load_weight("type_option.weight", typeWeight, fileline);
|
||||
}
|
||||
// Instances that have died still count: their contribution is the residue
|
||||
double sumCoverage = m_retired.sumCoverage;
|
||||
double sumWeight = m_retired.sumWeight;
|
||||
for (const auto& instp : m_insts) {
|
||||
if (instp->retained()) continue; // Already folded into the residue
|
||||
instp->loadWeight();
|
||||
const std::pair<double, double> sums = instp->coverageSums();
|
||||
if (sums.second == 0.0) continue; // A covergroup without coverage does not contribute
|
||||
sumCoverage += instp->weight() * (sums.first / sums.second);
|
||||
sumWeight += instp->weight();
|
||||
}
|
||||
return _vl_cov_calculate(sumCoverage, sumWeight, m_typeWeight);
|
||||
}
|
||||
|
||||
double VlCovergroupType::retiredCoverage() const {
|
||||
if (m_retired.count == 0) return -1.0;
|
||||
return m_retired.sumCoverage / static_cast<double>(m_retired.count);
|
||||
if (m_retired.count == 0 || m_retired.sumWeight == 0.0) return -1.0;
|
||||
return m_retired.sumCoverage / m_retired.sumWeight;
|
||||
}
|
||||
|
||||
// Defined here, not in verilated.cpp, so that the registry costs nothing in a model with no
|
||||
@@ -1252,13 +1334,23 @@ VlCovRegistry* VerilatedContext::covergroupRegistryp() VL_MT_SAFE {
|
||||
return static_cast<VlCovRegistry*>(m_covergroupsp.get());
|
||||
}
|
||||
|
||||
VlCovergroupInst* VlCovRegistry::newCovergroupInst(const char* typeName) {
|
||||
VlCovergroupType* VlCovRegistry::findOrCreateType(const char* typeName) {
|
||||
VlCovergroupType*& typep = m_byName[typeName];
|
||||
if (!typep) { // First instance of this type
|
||||
if (!typep) { // First use of this type
|
||||
m_types.emplace_back(new VlCovergroupType{});
|
||||
typep = m_types.back().get();
|
||||
}
|
||||
return typep->newInstance();
|
||||
return typep;
|
||||
}
|
||||
|
||||
VlCovergroupInst* VlCovRegistry::newCovergroupInst(const char* typeName) {
|
||||
return findOrCreateType(typeName)->newInstance();
|
||||
}
|
||||
|
||||
double VlCovRegistry::typeCoverage(const char* typeName, IData typeWeight,
|
||||
VlFileLineDebug fileline) {
|
||||
// Also for a type never instantiated, whose node then remembers type_option.weight
|
||||
return findOrCreateType(typeName)->coverage(typeWeight, fileline);
|
||||
}
|
||||
|
||||
// A covergroup object can outlive the registry: models must be destroyed before
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedCovContext;
|
||||
@@ -490,6 +491,12 @@ class VlCovergroupInst final {
|
||||
uint32_t m_slot = 0; // Index into m_typep->m_insts; unlink-by-swap rewrites
|
||||
#endif
|
||||
uint32_t m_attachCount = 1; // SV handles bound here; 1 from construction
|
||||
// option.weight of the SV object that created this node, while that object
|
||||
// lives; borrowed through VlCovInstHandle::lendWeight().
|
||||
const IData* m_weightp = nullptr;
|
||||
IData m_loadedWeight = 1; // Last option.weight loaded through m_weightp
|
||||
int32_t m_weight = 1; // Weight in use, never negative; kept once the object is gone
|
||||
VlFileLineDebug m_fileline; // Covergroup declaration, where a negative weight is reported
|
||||
bool m_retained = false; // VM_COVERAGE: dead, but kept for registered count pointers
|
||||
|
||||
// Reads m_items to fold the residue; owns m_slot and m_retained.
|
||||
@@ -525,35 +532,49 @@ public:
|
||||
// here, and because it frees 'this'.
|
||||
bool attachDec() { return --m_attachCount == 0; }
|
||||
|
||||
// ---- instance weight (from VlCovInstHandle) ----
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_fileline = fileline;
|
||||
loadWeight();
|
||||
}
|
||||
// The lending object is being destroyed. Its members may already be gone, so
|
||||
// the weight is not read again; the last loaded value stays in effect.
|
||||
void unlendWeight(const IData* weightp) {
|
||||
if (m_weightp == weightp) m_weightp = nullptr;
|
||||
}
|
||||
/// Load option.weight from the lending object. SV writes the member directly
|
||||
/// (assignments, ref and output arguments, $value$plusargs, ...), so this is
|
||||
/// where a new value is seen, and checked once: a negative weight is reported as
|
||||
/// an error, and counts as zero.
|
||||
void loadWeight();
|
||||
/// Weight of this instance in its type's coverage (option.weight, IEEE
|
||||
/// 1800-2023 19.11.3), as last loaded; never negative.
|
||||
int32_t weight() const { return m_weight; }
|
||||
|
||||
// ---- introspection ----
|
||||
VlCovergroupType* typep() const { return m_typep; }
|
||||
uint32_t instId() const { return m_instId; }
|
||||
// True once retired but kept alive because the coverage database holds raw
|
||||
// pointers into this node's bin counts (VM_COVERAGE); see retire().
|
||||
bool retained() const { return m_retained; }
|
||||
// Sum of the instance's items' covered/total bin counts. Matches what the
|
||||
// generated get_inst_coverage() computes; see foldResidue().
|
||||
void coverageParts(double& covered, double& total) const {
|
||||
covered = 0.0;
|
||||
total = 0.0;
|
||||
for (const auto& itemp : m_items) {
|
||||
double c = 0.0;
|
||||
double t = 0.0;
|
||||
itemp->coverageParts(c, t);
|
||||
covered += c;
|
||||
total += t;
|
||||
}
|
||||
}
|
||||
/// IEEE 1800-2023 19.11 sums over the items whose coverage has a nonzero
|
||||
/// denominator: {the sum of each item's option.weight times its coverage
|
||||
/// (0..100), the sum of those weights}.
|
||||
std::pair<double, double> coverageSums() const;
|
||||
/// Instance coverage, as returned by get_inst_coverage(), in 0..100.
|
||||
double coverage();
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCovRetiredAvg
|
||||
/// Per-type residue: what survives an instance's death. Fixed size, so it does
|
||||
/// not grow with churn. Weight is 1 everywhere until option.weight is plumbed.
|
||||
/// not grow with churn. Each instance contributes with its option.weight.
|
||||
|
||||
struct VlCovRetiredAvg final {
|
||||
uint64_t count = 0; // Retired instances that contributed (nonzero denominator)
|
||||
double sumCoverage = 0.0; // Sigma of per-instance coverage, each in 0..100
|
||||
double sumCoverage = 0.0; // Sigma of per-instance weight * coverage (0..100)
|
||||
double sumWeight = 0.0; // Sigma of per-instance weight
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -569,11 +590,13 @@ class VlCovergroupType final {
|
||||
uint32_t m_createdInsts = 0; // Instances ever created; never decremented
|
||||
uint32_t m_nextInstId = 0; // Monotonic; slots are reused, ids never are
|
||||
VlCovRetiredAvg m_retired; // Contribution of every instance that has died
|
||||
IData m_loadedTypeWeight = 1; // Last type_option.weight loaded by coverage()
|
||||
int32_t m_typeWeight = 1; // type_option.weight in use, never negative
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Harvest instp's contribution into m_retired. Must run before instp is
|
||||
// unlinked: it reads the instance's items.
|
||||
void foldResidue(const VlCovergroupInst* instp);
|
||||
void foldResidue(VlCovergroupInst* instp);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -589,6 +612,12 @@ public:
|
||||
// True if any node here still has an SV handle bound to it, and so can be
|
||||
// retired again after the registry is destroyed. See ~VlCovRegistry.
|
||||
bool anyAttached() const;
|
||||
/// Type coverage, as returned by get_coverage(), in 0..100: the average of
|
||||
/// every instance's coverage, weighted by its option.weight (IEEE 1800-2023
|
||||
/// 19.11.3, type_option.merge_instances false). typeWeight is
|
||||
/// type_option.weight, which decides the result when no instance contributes;
|
||||
/// like option.weight, it is checked as it is loaded.
|
||||
double coverage(IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection ----
|
||||
// Test and debug only; generated code never calls these, and SV reaches them
|
||||
@@ -603,7 +632,8 @@ public:
|
||||
uint32_t createdInstanceCount() const { return m_createdInsts; }
|
||||
// Instances that have died and contributed to the residue.
|
||||
uint32_t retiredInstanceCount() const { return static_cast<uint32_t>(m_retired.count); }
|
||||
// Mean coverage over the retired instances only, in 0..100; -1.0 if none.
|
||||
// Weighted mean coverage over the retired instances only, in 0..100; -1.0 if
|
||||
// none contributed or their weights sum to zero.
|
||||
double retiredCoverage() const;
|
||||
};
|
||||
|
||||
@@ -621,6 +651,7 @@ class VlCovRegistry final : public VerilatedVirtualBase {
|
||||
|
||||
// PRIVATE METHODS
|
||||
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
|
||||
VlCovergroupType* findOrCreateType(const char* typeName);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -633,6 +664,9 @@ public:
|
||||
// generated covergroup class name, already --protect-ids obfuscated, and is
|
||||
// the same string that keys the coverage database's hier/page.
|
||||
VlCovergroupInst* newCovergroupInst(const char* typeName);
|
||||
/// Type coverage of a covergroup type (get_coverage()); see
|
||||
/// VlCovergroupType::coverage(). typeWeight is its type_option.weight.
|
||||
double typeCoverage(const char* typeName, IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection (see VlCovergroupType) ----
|
||||
// typeName is the obfuscated generated name, so a test using these under
|
||||
@@ -658,18 +692,21 @@ public:
|
||||
class VlCovInstHandle final {
|
||||
// MEMBERS
|
||||
VlCovergroupInst* m_p = nullptr; // Attach-counted; the registry owns the node
|
||||
const IData* m_weightp = nullptr; // Owning object's option.weight, if lent to m_p
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Drop one attach count, retiring the node if that was the last handle.
|
||||
// Nothing may touch instp afterwards: retire() may have freed it.
|
||||
static void release(VlCovergroupInst* instp) {
|
||||
static void release(VlCovergroupInst* instp, const IData* weightp) {
|
||||
if (VL_UNCOVERABLE(!instp)) return; // Never attach()ed; codegen always does
|
||||
instp->unlendWeight(weightp);
|
||||
if (instp->attachDec()) instp->typep()->retire(instp);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovInstHandle() = default;
|
||||
// The copy's owning object lends no weight; the node keeps reading the lender's.
|
||||
VlCovInstHandle(const VlCovInstHandle& o)
|
||||
: m_p{o.m_p} {
|
||||
if (VL_UNCOVERABLE(!m_p)) return; // Unbound source; see release above
|
||||
@@ -678,12 +715,18 @@ public:
|
||||
// Deleted, not implemented: nothing generates an assignment, and the
|
||||
// implicit one would copy m_p raw -- no attachInc, no release.
|
||||
VlCovInstHandle& operator=(const VlCovInstHandle&) = delete;
|
||||
~VlCovInstHandle() { release(m_p); }
|
||||
~VlCovInstHandle() { release(m_p, m_weightp); }
|
||||
|
||||
// METHODS
|
||||
// Bind to a freshly created node, taking over the attach count of 1 it was
|
||||
// created with. Called once, from the generated covergroup constructor.
|
||||
void attach(VlCovergroupInst* p) { m_p = p; }
|
||||
// Let the node read the owning object's option.weight until this handle is
|
||||
// destroyed. Called once, from the generated constructor, after attach().
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_p->lendWeight(weightp, fileline);
|
||||
}
|
||||
VlCovergroupInst* p() const { return m_p; }
|
||||
};
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
// them as such currently, so named with a unique prefix
|
||||
typedef struct {
|
||||
string name;
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -266,7 +266,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_covergroup_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -275,7 +275,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_coverpoint_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -284,7 +284,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_cross_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
bit strobe;
|
||||
@@ -294,14 +294,14 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_covergroup_type_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
real real_interval;
|
||||
} vl_coverpoint_type_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
} vl_cross_type_options_t;
|
||||
|
||||
@@ -64,35 +64,6 @@
|
||||
// Placeholder for compiling with --protect-ids
|
||||
#define VL_UNKNOWN "<unknown>"
|
||||
|
||||
//=============================================================================
|
||||
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
|
||||
// debugging purposes.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
|
||||
int m_lineno = 0; // Line number from sources
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
|
||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||
|
||||
Reference in New Issue
Block a user