Add costs

This commit is contained in:
Geza Lore 2025-11-08 08:51:54 +00:00
parent 0c10bfaf8a
commit ecb9cd3ba1
2 changed files with 53 additions and 24 deletions

View File

@ -578,6 +578,7 @@ public:
static constexpr int INSTR_COUNT_STR = 100; // String ops static constexpr int INSTR_COUNT_STR = 100; // String ops
static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
static constexpr int INSTR_COUNT_SYM = 5; // Syms ctor/dtor statements
// ACCESSORS // ACCESSORS
virtual string name() const VL_MT_STABLE { return ""; } virtual string name() const VL_MT_STABLE { return ""; }

View File

@ -125,6 +125,12 @@ class EmitCSyms final : EmitCBaseVisitorConst {
std::vector<std::string> getSymCtorStmts(); std::vector<std::string> getSymCtorStmts();
std::vector<std::string> getSymDtorStmts(); std::vector<std::string> getSymDtorStmts();
static size_t stmtCost(const std::string& stmt) {
if (stmt.empty()) return 0;
if (VString::startsWith(stmt, "/")) return 0;
return static_cast<size_t>(AstNode::INSTR_COUNT_SYM);
}
static void nameCheck(AstNode* nodep) { static void nameCheck(AstNode* nodep) {
// Prevent GCC compile time error; name check all things that reach C++ code // Prevent GCC compile time error; name check all things that reach C++ code
if (nodep->name().empty()) return; if (nodep->name().empty()) return;
@ -873,61 +879,83 @@ std::vector<std::string> EmitCSyms::getSymDtorStmts() {
} }
void EmitCSyms::emitSplit(std::vector<std::string>& stmts, const std::string name, void EmitCSyms::emitSplit(std::vector<std::string>& stmts, const std::string name,
size_t maxStmts) { size_t maxCost) {
const std::string baseName = m_symsFileBase + "__" + name; const std::string baseName = m_symsFileBase + "__" + name;
size_t nSplits = 0; size_t nSubFunctions = 0;
// Reduce into a balanced tree of sub-function calls until we end up with a single statement
while (stmts.size() > 1) { while (stmts.size() > 1) {
size_t nSubFunctions = 0; size_t nSplits = 0;
for (size_t i = 0; i < stmts.size(); i += maxStmts) { size_t nStmts = stmts.size();
const std::string nStr = std::to_string(nSplits++); for (size_t splitStart = 0, splitEnd = 0; splitStart < nStmts; splitStart = splitEnd) {
// Gather up at at most 'maxCost' worth of statements in this split,
// but always at least 2 (if less than 2, the reduction makes no
// progress and the loop will not terminate).
size_t cost = 0;
while (((cost < maxCost) || (splitEnd - splitStart < 2)) && splitEnd < nStmts) {
cost += stmtCost(stmts[splitEnd++]);
}
UASSERT(splitStart < splitEnd, "Empty split");
// Create new sub-function and emit current range of statementss
const std::string nStr = std::to_string(nSubFunctions++);
// Name of sub-function we are emitting now // Name of sub-function we are emitting now
const std::string funcName = symClassName() + "__" + name + "__" + nStr; const std::string funcName = symClassName() + "__" + name + "__" + nStr;
m_splitFuncNames.emplace_back(funcName); m_splitFuncNames.emplace_back(funcName);
// Open split file // Open split file
openOutputFile(baseName + "__" + nStr + "__Slow.cpp"); openOutputFile(baseName + "__" + nStr + "__Slow.cpp");
// Emit header // Emit header
emitSymImpPreamble(); emitSymImpPreamble();
// Open sub-function definition in the split file // Open sub-function definition in the split file
puts("void " + symClassName() + "::" + funcName + "() {\n"); puts("void " + symClassName() + "::" + funcName + "() {\n");
// Emit statements // Emit statements
for (size_t j = 0; j < maxStmts; ++j) { for (size_t j = splitStart; j < splitEnd; ++j) {
if (i + j >= stmts.size()) break;
m_ofp->putsNoTracking(" "); m_ofp->putsNoTracking(" ");
m_ofp->putsNoTracking(stmts.at(i + j)); m_ofp->putsNoTracking(stmts[j]);
m_ofp->putsNoTracking("\n"); m_ofp->putsNoTracking("\n");
} }
// Close sub-function // Close sub-function
puts("}\n"); puts("}\n");
// Close split file // Close split file
closeOutputFile(); closeOutputFile();
// Replace statements with a call to the sub-function // Replace statements with a call to the sub-function
stmts[nSubFunctions++] = funcName + "();"; stmts[nSplits++] = funcName + "();";
} }
stmts.resize(nSubFunctions); // The statements at the front are now the calls to the sub-functions, drop the rest
stmts.resize(nSplits);
} }
} }
void EmitCSyms::emitSymImp(AstNetlist* netlistp) { void EmitCSyms::emitSymImp(AstNetlist* netlistp) {
UINFO(6, __FUNCTION__ << ": "); UINFO(6, __FUNCTION__ << ": ");
const size_t optSplit = static_cast<size_t>(v3Global.opt.outputSplitCFuncs()); // Get the body of the constructor and destructor
// Allow at least 2 statements per function, to ensure tree reduction
// actually makes forward progress. Also assume each statement is worth 5
// units as each of these are somewhat complicated, not just simple ops.
const size_t maxStmts = optSplit ? std::max<size_t>(optSplit / 5, 2) //
: std::numeric_limits<size_t>::max();
std::vector<std::string> ctorStmts = getSymCtorStmts(); std::vector<std::string> ctorStmts = getSymCtorStmts();
std::vector<std::string> dtorStmts = getSymDtorStmts(); std::vector<std::string> dtorStmts = getSymDtorStmts();
// Split them if needed - fudge factor for all other contents in main file // Check if needs splitting and if so split into sub-functions
if (ctorStmts.size() + dtorStmts.size() + 200 >= maxStmts) { if (const size_t maxCost = static_cast<size_t>(v3Global.opt.outputSplitCFuncs())) {
// Splitting files, so using parallel build. size_t totalCost = 200; // Starting from 200 to consider all other contents in main file
v3Global.useParallelBuild(true); if (totalCost <= maxCost) {
emitSplit(ctorStmts, "ctor", maxStmts); for (const std::string& stmt : ctorStmts) {
emitSplit(dtorStmts, "dtor", maxStmts); totalCost += stmtCost(stmt);
if (totalCost > maxCost) break;
}
}
if (totalCost <= maxCost) {
for (const std::string& stmt : dtorStmts) {
totalCost += stmtCost(stmt);
if (totalCost > maxCost) break;
}
}
// Split them if needed
if (totalCost > maxCost) {
v3Global.useParallelBuild(true); // Splitting files, so using parallel build.
emitSplit(ctorStmts, "ctor", maxCost);
emitSplit(dtorStmts, "dtor", maxCost);
}
} }
openOutputFile(m_symsFileBase + "__Slow.cpp"); openOutputFile(m_symsFileBase + "__Slow.cpp");