Internals: Replace AstMTaskBody with AstCFunc(#6280) (#6628)

AstMTaskBody is somewhat redundant and is problematic for #6280. We used
to wrap all MTasks in a CFunc before emit anyway. Now we create that
CFunc when we create the ExecMTask in V3OrderParallel, and subsequently
use the CFunc to represent the contents of the MTask. Final output and
optimizations are the same, but internals are simplified to move
towards #6280.

No functional change.
This commit is contained in:
Geza Lore
2025-11-03 06:32:03 +00:00
committed by GitHub
parent d066504bb9
commit d3ca79368c
12 changed files with 156 additions and 205 deletions
+15 -15
View File
@@ -1763,7 +1763,7 @@ class DpiThreadsVisitor final : public VNVisitorConst {
public:
// CONSTRUCTORS
explicit DpiThreadsVisitor(AstMTaskBody* nodep) { iterateConst(nodep); }
explicit DpiThreadsVisitor(AstCFunc* nodep) { iterateConst(nodep); }
int threads() const { return m_threads; }
~DpiThreadsVisitor() override = default;
@@ -2431,8 +2431,9 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, OrderMoveGraph& mov
if (dumpGraphLevel() >= 9) moveGraph.dumpDotFilePrefixed(tag + "_ordermv_pruned");
// Create the AstExecGraph node which represents the execution of the MTask graph.
FileLine* const rootFlp = v3Global.rootp()->fileline();
AstExecGraph* const execGraphp = new AstExecGraph{rootFlp, tag};
FileLine* const flp = v3Global.rootp()->fileline();
AstScope* const scopep = v3Global.rootp()->topScopep()->scopep();
AstExecGraph* const execGraphp = new AstExecGraph{flp, tag};
V3Graph* const depGraphp = execGraphp->depGraphp();
// Translate the LogicMTask graph into the corresponding ExecMTask graph,
@@ -2468,24 +2469,23 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, OrderMoveGraph& mov
VL_DO_DANGLING(mVtxp->unlinkDelete(&moveGraph), mVtxp);
}
// We have 2 objects, because AstMTaskBody is an AstNode, and ExecMTask is a GraphVertex.
// To combine them would involve multiple inheritance.
// Construct the actual MTaskBody
AstMTaskBody* const bodyp = new AstMTaskBody{rootFlp};
execGraphp->addMTaskBodiesp(bodyp);
bodyp->addStmtsp(emitter.getStmts());
UASSERT_OBJ(bodyp->stmtsp(), bodyp, "Should not try to create empty MTask");
// Create the ExecMTask
ExecMTask* const execMTaskp = new ExecMTask{depGraphp, bodyp};
if (!v3Global.opt.hierBlocks().empty())
execMTaskp->threads(DpiThreadsVisitor{bodyp}.threads());
ExecMTask* const execMTaskp = new ExecMTask{execGraphp, scopep, emitter.getStmts()};
if (!v3Global.opt.hierBlocks().empty()) {
execMTaskp->threads(DpiThreadsVisitor{execMTaskp->funcp()}.threads());
}
const bool newEntry = logicMTaskToExecMTask.emplace(mTaskp, execMTaskp).second;
UASSERT_OBJ(newEntry, mTaskp, "LogicMTasks should be processed in dependencyorder");
UINFO(3, "Final '" << tag << "' LogicMTask " << mTaskp->id() << " maps to ExecMTask"
<< execMTaskp->id());
// For code analysis purposes, we can pretend the AstExecGraph runs the
// MTasks sequentially, in some topological order that respects edges.
// The order they are created here happens to be just such an order.
AstCCall* const callp = new AstCCall{flp, execMTaskp->funcp()};
callp->dtypeSetVoid();
execGraphp->addStmtsp(callp->makeStmt());
// Add the dependency edges between ExecMTasks
for (const V3GraphEdge& edge : mTaskp->inEdges()) {
const V3GraphVertex* fromVxp = edge.fromp();