mirror of
https://github.com/verilator/verilator.git
synced 2026-09-02 10:48:27 +02:00
Improve Dfg type system (#6390)
Added a mini type system for Dfg using DfgDataType to replace Dfg's use of AstNodeDType. This is much more restricted and represents only the types Dfg can handle in a canonical form. This will be needed when adding more support for unpacked arrays and maybe unpacked structs one day. Also added an internal type checker for DfgGraphs which encodes all the assumptions the code makes about type relationships in the graph. Run this in a few places with --debug-check. Fix resulting fallout.
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Type system used by DFG
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2025 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Dfg.h"
|
||||
#include "V3DfgPasses.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgDataType
|
||||
|
||||
const DfgDataType* DfgDataType::s_nullTypep{nullptr};
|
||||
std::unordered_map<uint32_t, const DfgDataType*> DfgDataType::s_packedTypes{};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Type checker - for internal validation only
|
||||
|
||||
void V3DfgPasses::typeCheck(const DfgGraph& dfg) {
|
||||
dfg.forEachVertex([&](const DfgVertex& vtx) {
|
||||
#define CHECK(cond, msg) \
|
||||
UASSERT_OBJ(cond, &vtx, \
|
||||
"Dfg type error for vertex " << vtx.typeName() << " in " << dfg.name() << ": " \
|
||||
<< msg);
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::Const: {
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::VarArray:
|
||||
case VDfgType::VarPacked: {
|
||||
const DfgVertexVar& v = *vtx.as<DfgVertexVar>();
|
||||
CHECK(!v.defaultp() || v.defaultp()->dtype() == v.dtype(), "'defaultp' should match");
|
||||
CHECK(!v.srcp() || v.srcp()->dtype() == v.dtype(), "'srcp' should match");
|
||||
return;
|
||||
}
|
||||
case VDfgType::SpliceArray:
|
||||
case VDfgType::SplicePacked: {
|
||||
const DfgVertexSplice& v = *vtx.as<DfgVertexSplice>();
|
||||
v.foreachDriver([&](const DfgVertex& src, uint32_t lo) {
|
||||
CHECK(src.dtype() == DfgDataType::select(v.dtype(), lo, src.size()), "driver");
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
case VDfgType::Logic: {
|
||||
CHECK(vtx.dtype().isNull(), "Should be Null type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::Unresolved: {
|
||||
CHECK(!vtx.dtype().isNull(), "Should not be Null type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::UnitArray: {
|
||||
const DfgUnitArray& v = *vtx.as<DfgUnitArray>();
|
||||
CHECK(v.isArray(), "Should be Array type");
|
||||
CHECK(v.size() == 1, "Should be one element");
|
||||
CHECK(v.srcp()->dtype() == v.dtype().elemDtype(), "Input should be the element type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::Sel: {
|
||||
const DfgSel& v = *vtx.as<DfgSel>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
CHECK(v.dtype() == DfgDataType::select(v.srcp()->dtype(), v.lsb(), v.size()), "sel");
|
||||
return;
|
||||
}
|
||||
case VDfgType::Mux: {
|
||||
const DfgMux& v = *vtx.as<DfgMux>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
CHECK(v.fromp()->isPacked(), "Source operand should be Packed type");
|
||||
CHECK(v.fromp()->size() >= v.size(), "Source operand should not be narrower");
|
||||
CHECK(v.lsbp()->isPacked(), "Index should be Packed type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::ArraySel: {
|
||||
const DfgArraySel& v = *vtx.as<DfgArraySel>();
|
||||
CHECK(v.dtype() == v.fromp()->dtype().elemDtype(), "Element type should match");
|
||||
CHECK(v.bitp()->isPacked(), "Index should be Packed type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Add:
|
||||
case VDfgType::And:
|
||||
case VDfgType::BufIf1:
|
||||
case VDfgType::Div:
|
||||
case VDfgType::DivS:
|
||||
case VDfgType::ModDiv:
|
||||
case VDfgType::ModDivS:
|
||||
case VDfgType::Mul:
|
||||
case VDfgType::MulS:
|
||||
case VDfgType::Or:
|
||||
case VDfgType::Sub:
|
||||
case VDfgType::Xor: {
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
CHECK(vtx.inputp(0)->dtype() == vtx.dtype(), "LHS should be same type");
|
||||
CHECK(vtx.inputp(1)->dtype() == vtx.dtype(), "RHS should be same type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Negate:
|
||||
case VDfgType::Not: {
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
CHECK(vtx.inputp(0)->dtype() == vtx.dtype(), "Input should be same type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::ShiftL:
|
||||
case VDfgType::ShiftR:
|
||||
case VDfgType::ShiftRS: {
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
CHECK(vtx.inputp(0)->dtype() == vtx.dtype(), "LHS should be same type");
|
||||
CHECK(vtx.inputp(1)->isPacked(), "RHS should be Packed type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Concat: {
|
||||
const DfgConcat& v = *vtx.as<DfgConcat>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
CHECK(v.lhsp()->isPacked(), "LHS should be Packed type");
|
||||
CHECK(v.rhsp()->isPacked(), "RHS should be Packed type");
|
||||
CHECK(v.size() == v.rhsp()->size() + v.lhsp()->size(), "Concat result mismatch");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Replicate: {
|
||||
// TODO: model DfgReplicate without an explicit 'countp' which is always constant
|
||||
const DfgReplicate& v = *vtx.as<DfgReplicate>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
CHECK(v.srcp()->isPacked(), "'srcp' should be same type");
|
||||
CHECK(v.countp()->isPacked(), "'countp' should be Packed type");
|
||||
CHECK(v.size() % v.srcp()->size() == 0, "Not a replicate");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::StreamL:
|
||||
case VDfgType::StreamR: {
|
||||
// TODO: model these without an explicit slice size which is always constant (?)
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
CHECK(vtx.inputp(0)->dtype() == vtx.dtype(), "LHS should be same type");
|
||||
CHECK(vtx.inputp(1)->isPacked(), "Slice size should be Packed type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Eq:
|
||||
case VDfgType::EqCase:
|
||||
case VDfgType::EqWild:
|
||||
case VDfgType::Neq:
|
||||
case VDfgType::NeqCase:
|
||||
case VDfgType::NeqWild:
|
||||
case VDfgType::Gt:
|
||||
case VDfgType::GtS:
|
||||
case VDfgType::Gte:
|
||||
case VDfgType::GteS:
|
||||
case VDfgType::Lt:
|
||||
case VDfgType::LtS:
|
||||
case VDfgType::Lte:
|
||||
case VDfgType::LteS: {
|
||||
CHECK(vtx.dtype() == DfgDataType::packed(1), "Should be 1-bit");
|
||||
CHECK(vtx.inputp(0)->dtype() == vtx.inputp(1)->dtype(), "Sides should match");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Extend:
|
||||
case VDfgType::ExtendS: {
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
CHECK(vtx.inputp(0)->isPacked(), "Operand should be same type");
|
||||
CHECK(vtx.inputp(0)->size() < vtx.size(), "Operand should be narrower");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::LogAnd:
|
||||
case VDfgType::LogEq:
|
||||
case VDfgType::LogIf:
|
||||
case VDfgType::LogOr: {
|
||||
CHECK(vtx.dtype() == DfgDataType::packed(1), "Should be 1-bit");
|
||||
CHECK(vtx.inputp(0)->isPacked(), "LHS should be Packed type");
|
||||
CHECK(vtx.inputp(1)->isPacked(), "RHS should be Packed type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::LogNot:
|
||||
case VDfgType::RedAnd:
|
||||
case VDfgType::RedOr:
|
||||
case VDfgType::RedXor: {
|
||||
CHECK(vtx.dtype() == DfgDataType::packed(1), "Should be 1-bit");
|
||||
CHECK(vtx.inputp(0)->isPacked(), "Operand should be Packed type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Cond: {
|
||||
const DfgCond& v = *vtx.as<DfgCond>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
CHECK(v.condp()->isPacked(), "Condition should be Packed type");
|
||||
CHECK(v.thenp()->dtype() == v.dtype(), "Then should be same type");
|
||||
CHECK(v.elsep()->dtype() == v.dtype(), "Else should be same type");
|
||||
return;
|
||||
}
|
||||
|
||||
case VDfgType::Pow:
|
||||
case VDfgType::PowSS:
|
||||
case VDfgType::PowSU:
|
||||
case VDfgType::PowUS: {
|
||||
CHECK(vtx.isPacked(), "Should be Packed type");
|
||||
CHECK(vtx.inputp(0)->dtype() == vtx.dtype(), "LHS should be same type");
|
||||
CHECK(vtx.inputp(1)->isPacked(), "RHS should be Packed type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#undef CHECK
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user