mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 18:13:50 +02:00
Use the same serial ordering within MTasks as we use in serial mode (#4994)
The goal here is to use as single ordering heuristic (which can be improved later) within MTasks as we do for serial code ordering. The heuristic itself is factored out into the new OrderMoveGraphSerializer. This also yields slightly nicer ordering than the previously use GraphStream, so we end up with fewer trigger (domain) conditionals in the MTasks, this can be worth a few percent speedup. This has the somewhat nice side-effect of reusing OrderMoveGraphVertex for both serial and parallel mode, so MTaskMoveGraphVertex can be removed. Serial mode yields identical output.
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Block code ordering
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// OrderMoveGraph implementation and related
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3OrderMoveGraph.h"
|
||||
|
||||
#include "V3Graph.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//======================================================================
|
||||
// OrderMoveDomScope implementation
|
||||
|
||||
OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap;
|
||||
|
||||
//======================================================================
|
||||
// OrderMoveVertex implementation
|
||||
|
||||
OrderMoveVertex::OrderMoveVertex(OrderMoveGraph& graph, OrderLogicVertex* lVtxp,
|
||||
const AstSenTree* domainp) VL_MT_DISABLED
|
||||
: V3GraphVertex{&graph},
|
||||
m_logicp{lVtxp},
|
||||
m_domScope{OrderMoveDomScope::getOrCreate(domainp, lVtxp ? lVtxp->scopep() : nullptr)} {
|
||||
UASSERT_OBJ(!lVtxp || lVtxp->domainp() == domainp, lVtxp, "Wrong domain for Move vertex");
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// OrderMoveGraphBuilder - for OrderMoveGraph::build
|
||||
|
||||
class OrderMoveGraphBuilder final {
|
||||
// NODE STATE
|
||||
// AstSenTree::user1p() -> AstSenTree: Original AstSenTree for trigger
|
||||
const VNUser1InUse m_user1InUse;
|
||||
|
||||
// TYPES
|
||||
using DomainMap = std::map<const AstSenTree*, OrderMoveVertex*>;
|
||||
|
||||
// MEMBERS
|
||||
const OrderGraph& m_orderGraph; // Input OrderGraph
|
||||
std::unique_ptr<OrderMoveGraph> m_moveGraphp{new OrderMoveGraph}; // Output OrderMoveGraph
|
||||
// Map from Trigger reference AstSenItem to the original AstSenTree
|
||||
const V3Order::TrigToSenMap& m_trigToSen;
|
||||
// Storage for domain -> OrderMoveVertex, maps held in OrderVarVertex::userp()
|
||||
std::deque<DomainMap> m_domainMaps;
|
||||
|
||||
// CONSTRUCTORS
|
||||
OrderMoveGraphBuilder(const OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen)
|
||||
: m_orderGraph{orderGraph}
|
||||
, m_trigToSen{trigToSen} {
|
||||
// How this works:
|
||||
// - Create a OrderMoveVertex for each OrderLogicVertex.
|
||||
// - Following each OrderLogicVertex, search forward in the context of its domain
|
||||
// - If we encounter another OrderLogicVertex in non-exclusive domain, make the
|
||||
// OrderMoveVertex->OrderMoveVertex edge.
|
||||
// - If we encounter an OrderVarVertex, make a Vertex for the (OrderVarVertex, domain)
|
||||
// pair and continue to search forward in the context of the same domain. Unless we
|
||||
// already created that pair, in which case, we've already done the forward search,
|
||||
// so stop.
|
||||
|
||||
// For each logic vertex, make a OrderMoveVertex, for each variable vertex, allocate
|
||||
// storage
|
||||
for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (OrderLogicVertex* const lvtxp = itp->cast<OrderLogicVertex>()) {
|
||||
lvtxp->userp(new OrderMoveVertex{*m_moveGraphp, lvtxp, lvtxp->domainp()});
|
||||
} else {
|
||||
// This is an OrderVarVertex
|
||||
m_domainMaps.emplace_back();
|
||||
itp->userp(&m_domainMaps.back());
|
||||
}
|
||||
}
|
||||
// Build edges between logic vertices
|
||||
for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (OrderLogicVertex* const lvtxp = itp->cast<OrderLogicVertex>()) {
|
||||
iterateLogicVertex(lvtxp);
|
||||
}
|
||||
}
|
||||
m_moveGraphp->removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue);
|
||||
m_moveGraphp->userClearVertices();
|
||||
}
|
||||
virtual ~OrderMoveGraphBuilder() = default;
|
||||
VL_UNCOPYABLE(OrderMoveGraphBuilder);
|
||||
VL_UNMOVABLE(OrderMoveGraphBuilder);
|
||||
|
||||
// METHODS
|
||||
|
||||
// Returns the AstSenItem that originally corresponds to this AstSenTree, or nullptr if no
|
||||
// original AstSenTree, or if the original AstSenTree had multiple AstSenItems.
|
||||
const AstSenItem* getOrigSenItem(AstSenTree* senTreep) {
|
||||
if (!senTreep->user1p()) {
|
||||
// Find the original simple AstSenTree, if any
|
||||
AstNode* const origp = [&]() -> AstSenItem* {
|
||||
// If more than one AstSenItems, then not a simple AstSenTree
|
||||
if (senTreep->sensesp()->nextp()) return nullptr;
|
||||
|
||||
// Find the original AstSenTree
|
||||
auto it = m_trigToSen.find(senTreep->sensesp());
|
||||
if (it == m_trigToSen.end()) return nullptr;
|
||||
|
||||
// If more than one AstSenItems on the original, then not a simple AstSenTree
|
||||
if (it->second->sensesp()->nextp()) return nullptr;
|
||||
|
||||
// Else we found it.
|
||||
return it->second->sensesp();
|
||||
}();
|
||||
|
||||
// We use the node itself as a sentinel to denote 'no original node'
|
||||
senTreep->user1p(origp ? origp : senTreep);
|
||||
}
|
||||
|
||||
return senTreep->user1p() == senTreep ? nullptr : VN_AS(senTreep->user1p(), SenItem);
|
||||
}
|
||||
|
||||
bool domainsExclusive(AstSenTree* fromp, AstSenTree* top) {
|
||||
// Return 'true' if we can prove that both 'from' and 'to' cannot both
|
||||
// be active on the same evaluation, or false if we can't prove this.
|
||||
//
|
||||
// This detects the case of 'always @(posedge clk)'
|
||||
// and 'always @(negedge clk)' being exclusive.
|
||||
//
|
||||
// Are there any other cases we need to handle? Maybe not,
|
||||
// because these are not exclusive:
|
||||
// always @(posedge A or posedge B)
|
||||
// always @(negedge A)
|
||||
//
|
||||
// ... unless you know more about A and B, which sounds hard.
|
||||
|
||||
const AstSenItem* const fromSenItemp = getOrigSenItem(fromp);
|
||||
if (!fromSenItemp) return false;
|
||||
const AstSenItem* const toSenItemp = getOrigSenItem(top);
|
||||
if (!toSenItemp) return false;
|
||||
|
||||
const AstNodeVarRef* const fromVarrefp = fromSenItemp->varrefp();
|
||||
if (!fromVarrefp) return false;
|
||||
const AstNodeVarRef* const toVarrefp = toSenItemp->varrefp();
|
||||
if (!toVarrefp) return false;
|
||||
|
||||
// We know nothing about the relationship between different clocks here,
|
||||
// so only proceed if strictly the same clock.
|
||||
if (fromVarrefp->varScopep() != toVarrefp->varScopep()) return false;
|
||||
|
||||
return fromSenItemp->edgeType().exclusiveEdge(toSenItemp->edgeType());
|
||||
}
|
||||
|
||||
void addEdge(OrderMoveVertex* srcp, OrderMoveVertex* dstp) {
|
||||
new V3GraphEdge{m_moveGraphp.get(), srcp, dstp, 1};
|
||||
}
|
||||
|
||||
void iterateLogicVertex(const OrderLogicVertex* lvtxp) {
|
||||
AstSenTree* const domainp = lvtxp->domainp();
|
||||
OrderMoveVertex* const lMoveVtxp = static_cast<OrderMoveVertex*>(lvtxp->userp());
|
||||
// Search forward from lvtxp, making new edges from lMoveVtxp forward
|
||||
for (V3GraphEdge* edgep = lvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() == 0) continue; // Was cut
|
||||
|
||||
// OrderGraph is a bipartite graph, so we know it's an OrderVarVertex
|
||||
const OrderVarVertex* const vvtxp = static_cast<const OrderVarVertex*>(edgep->top());
|
||||
|
||||
// Look up OrderMoveVertex for this domain on this variable
|
||||
DomainMap& mapp = *static_cast<DomainMap*>(vvtxp->userp());
|
||||
const auto pair = mapp.emplace(domainp, nullptr);
|
||||
// Reference to the mapped OrderMoveVertex
|
||||
OrderMoveVertex*& vMoveVtxp = pair.first->second;
|
||||
|
||||
// On first encounter, visit downstream logic dependent on this (var, domain)
|
||||
if (pair.second) vMoveVtxp = iterateVarVertex(vvtxp, domainp);
|
||||
|
||||
// If no downstream dependents from this variable, then there is no need to add this
|
||||
// variable as a dependent.
|
||||
if (!vMoveVtxp) continue;
|
||||
|
||||
// Add this (variable, domain) as dependent of the logic that writes it.
|
||||
addEdge(lMoveVtxp, vMoveVtxp);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the OrderMoveVertex for this (var, domain) pair, iff it has downstream dependencies,
|
||||
// otherwise return nullptr.
|
||||
OrderMoveVertex* iterateVarVertex(const OrderVarVertex* vvtxp, AstSenTree* domainp) {
|
||||
OrderMoveVertex* vMoveVtxp = nullptr;
|
||||
// Search forward from vvtxp, making new edges from vMoveVtxp forward
|
||||
for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() == 0) continue; // Was cut
|
||||
|
||||
// OrderGraph is a bipartite graph, so we know it's an OrderLogicVertex
|
||||
const OrderLogicVertex* const lVtxp = edgep->top()->as<OrderLogicVertex>();
|
||||
|
||||
// Do not construct dependencies across exclusive domains.
|
||||
if (domainsExclusive(domainp, lVtxp->domainp())) continue;
|
||||
|
||||
// there is a path from this vvtx to a logic vertex. Add the new edge.
|
||||
if (!vMoveVtxp) vMoveVtxp = new OrderMoveVertex{*m_moveGraphp, nullptr, domainp};
|
||||
OrderMoveVertex* const lMoveVxp = static_cast<OrderMoveVertex*>(lVtxp->userp());
|
||||
addEdge(vMoveVtxp, lMoveVxp);
|
||||
}
|
||||
return vMoveVtxp;
|
||||
}
|
||||
|
||||
public:
|
||||
static std::unique_ptr<OrderMoveGraph> apply(const OrderGraph& orderGraph,
|
||||
const V3Order::TrigToSenMap& trigToSen) {
|
||||
return std::move(OrderMoveGraphBuilder{orderGraph, trigToSen}.m_moveGraphp);
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// OrderMoveGraph implementation
|
||||
|
||||
std::unique_ptr<OrderMoveGraph> OrderMoveGraph::build(const OrderGraph& orderGraph,
|
||||
const V3Order::TrigToSenMap& trigToSen) {
|
||||
return OrderMoveGraphBuilder::apply(orderGraph, trigToSen);
|
||||
}
|
||||
Reference in New Issue
Block a user