From 5c6315a880b84e87f9fb12d6bab55aaa276a8da7 Mon Sep 17 00:00:00 2001 From: Nick Brereton Date: Tue, 14 Jul 2026 18:52:26 -0400 Subject: [PATCH] V3EmitCSyms: drop redundant varTriggersStructExpansion guard, add VPI test --- src/V3EmitCSyms.cpp | 23 ---------------------- test_regress/t/t_vpi_var.cpp | 38 ++++++++++++++++++++++++++++++++++++ test_regress/t/t_vpi_var.v | 8 ++++++++ 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 7a535df43..1631038e5 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -338,27 +338,6 @@ class EmitCSyms final : EmitCBaseVisitorConst { return stmt; } - // True if addUOrStructMemberVars()/addUnpackedArrayUOrStructMemberVars() - // would expand this var (residual path only). - static bool varTriggersStructExpansion(const AstVar* const varp) { - const AstNodeDType* const dtypep = varp->dtypeSkipRefp(); - if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType)) { - return !sdtypep->packed(); - } - if (VN_IS(dtypep, UnpackArrayDType)) { - const AstNodeDType* elemp = dtypep; - while (const AstUnpackArrayDType* const adtypep - = VN_CAST(elemp->skipRefp(), UnpackArrayDType)) { - elemp = adtypep->subDTypep(); - } - if (const AstNodeUOrStructDType* const sdtypep - = VN_CAST(elemp->skipRefp(), NodeUOrStructDType)) { - return !sdtypep->packed(); - } - } - return false; - } - // Computed once here so the forceable-eligibility check can't be // re-derived (and desynced) by the caller. enum class TableEntryKind : uint8_t { kTableRow, kForceableResidual, kPlainResidual }; @@ -379,8 +358,6 @@ class EmitCSyms final : EmitCBaseVisitorConst { const std::string vlEnumType = varp->vlEnumType(); if (needsEmittedEntSize(vlEnumType)) return TableEntryKind::kPlainResidual; // struct/union whole - if (varTriggersStructExpansion(varp)) - return TableEntryKind::kPlainResidual; // member expansion // Params are often 'static constexpr' (offsetof is invalid on those); // string params also need a runtime .c_str(). if (varp->isParam()) return TableEntryKind::kPlainResidual; diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 7c0eac4f4..9d3e4c3a9 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -671,6 +671,44 @@ int _mon_check_unpacked_struct_members() { CHECK_RESULT(tmpValue.value.integer, 0xace0); } + // unpacked array of a packed struct (element is a plain vector, not vpiStructVar) + { + TestVpiHandle vh210 = VPI_HANDLE("packed_struct_array_signal"); + CHECK_RESULT_NZ(vh210); + CHECK_RESULT(vpi_get(vpiType, vh210), vpiRegArray); + CHECK_RESULT(vpi_get(vpiSize, vh210), 2); + + TestVpiHandle vh211 = vpi_handle_by_index(vh210, 0); + CHECK_RESULT_NZ(vh211); + CHECK_RESULT(vpi_get(vpiType, vh211), vpiReg); + CHECK_RESULT(vpi_get(vpiSize, vh211), 8); + + s_vpi_value putValue; + putValue.format = vpiIntVal; + putValue.value.integer = 0x5a; + vpi_put_value(vh211, &putValue, NULL, vpiNoDelay); + vpi_get_value(vh211, &tmpValue); + CHECK_RESULT(tmpValue.value.integer, 0x5a); + + TestVpiHandle vh212 = vpi_handle_by_index(vh210, 1); + CHECK_RESULT_NZ(vh212); + CHECK_RESULT(vpi_get(vpiType, vh212), vpiReg); + putValue.value.integer = 0x33; + vpi_put_value(vh212, &putValue, NULL, vpiNoDelay); + vpi_get_value(vh212, &tmpValue); + CHECK_RESULT(tmpValue.value.integer, 0x33); + + vpi_get_value(vh211, &tmpValue); + CHECK_RESULT(tmpValue.value.integer, 0x5a); + + TestVpiHandle vh213 = vpi_handle_by_name( + const_cast(TestSimulator::rooted("packed_struct_array_signal[1]")), + nullptr); + CHECK_RESULT_NZ(vh213); + vpi_get_value(vh213, &tmpValue); + CHECK_RESULT(tmpValue.value.integer, 0x33); + } + // array of unpacked structs with unpacked-array members { TestVpiHandle vh170 = VPI_HANDLE("parent_struct_array"); diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index ea66b0712..5efca1a67 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -89,6 +89,14 @@ extern "C" int mon_check(); input wire unpacked_struct_t wire_struct_array_port [1:0] /*verilator public_flat_rw*/; unpacked_struct_t struct_array_signal [1:0] /*verilator public_flat_rw*/; + // Unpacked array of a packed (not unpacked) struct element + typedef struct packed { + logic [6:0] packed_member_a; + logic packed_member_b; + } packed_leaf_struct_t; + + packed_leaf_struct_t packed_struct_array_signal [1:0] /*verilator public_flat_rw*/; + typedef struct { logic [6:0] unsigned_member; logic signed [6:0] signed_member;