Fix jumping over object initialization (#4411)

This commit is contained in:
Krzysztof Boroński 2023-08-11 18:28:37 +02:00 committed by GitHub
parent 8d512c3187
commit b752faa107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 0 deletions

View File

@ -699,6 +699,16 @@ public:
&& finalsp() == nullptr;
}
};
class AstCLocalScope final : public AstNode {
// Pack statements into an unnamed scope when generating C++
// @astgen op1 := stmtsp : List[AstNode]
public:
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
: ASTGEN_SUPER_CLocalScope(fl) {
this->addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCLocalScope;
};
class AstCUse final : public AstNode {
// C++ use of a class or #include; indicates need of forward declaration
// Parents: NODEMODULE

View File

@ -2288,6 +2288,13 @@ private:
iterateChildren(nodep);
}
}
void visit(AstCLocalScope* nodep) override {
iterateChildren(nodep);
if (!nodep->stmtsp()) {
nodep->unlinkFrBack();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstScope* nodep) override {
// No ASSIGNW removals under scope, we've long eliminated INITIALs
VL_RESTORER(m_wremove);

View File

@ -883,6 +883,11 @@ public:
iterateAndNextConstNull(nodep->endStmtsp());
puts("}\n");
}
void visit(AstCLocalScope* nodep) override {
puts("{\n");
iterateAndNextConstNull(nodep->stmtsp());
puts("}\n");
}
void visit(AstJumpGo* nodep) override {
puts("goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
}

View File

@ -52,6 +52,7 @@
#include "V3Ast.h"
#include "V3Const.h"
#include "V3EmitV.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3MemberMap.h"
#include "V3SenExprBuilder.h"
@ -388,6 +389,7 @@ private:
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
double m_timescaleFactor = 1.0; // Factor to scale delays by
int m_forkCnt = 0; // Number of forks inside a module
bool m_underJumpBlock = false; // True if we are inside of a jump-block
// Unique names
V3UniqueNames m_contAssignVarNames{"__VassignWtmp"}; // Names for temp AssignW vars
@ -594,6 +596,17 @@ private:
m_netlistp->typeTablep()->addTypesp(m_forkDtp);
return m_forkDtp;
}
// Move `insertBeforep` into `AstCLocalScope` if necessary to avoid jumping over
// a variable initialization that whould be inserted before `insertBeforep`. All
// access to this variable shoule be contained within returned `AstCLocalScope`.
AstCLocalScope* addCLocalScope(FileLine* const flp, AstNode* const insertBeforep) const {
if (!insertBeforep || !m_underJumpBlock) return nullptr;
VNRelinker handle;
insertBeforep->unlinkFrBack(&handle);
AstCLocalScope* const cscopep = new AstCLocalScope{flp, insertBeforep};
handle.relink(cscopep);
return cscopep;
}
// Create a temp variable and optionally put it before the specified node (mark local if so)
AstVarScope* createTemp(FileLine* const flp, const std::string& name,
AstNodeDType* const dtypep, AstNode* const insertBeforep = nullptr) {
@ -625,6 +638,7 @@ private:
FileLine* const flp = forkp->fileline();
// If we're in a function, insert the sync var directly before the fork
AstNode* const insertBeforep = m_classp ? forkp : nullptr;
addCLocalScope(flp, insertBeforep);
AstVarScope* forkVscp
= createTemp(flp, forkp->name() + "__sync", getCreateForkSyncDTypep(), insertBeforep);
unsigned joinCount = 0; // Needed for join counter
@ -691,6 +705,11 @@ private:
new AstCStmt{nodep->fileline(), "vlProcess->state(VlProcess::FINISHED);\n"});
}
}
void visit(AstJumpBlock* nodep) override {
VL_RESTORER(m_underJumpBlock);
m_underJumpBlock = true;
visit(static_cast<AstNodeStmt*>(nodep));
}
void visit(AstAlways* nodep) override {
if (nodep->user1SetOnce()) return;
VL_RESTORER(m_procp);
@ -908,6 +927,7 @@ private:
// Insert new vars before the timing control if we're in a function; in a process we can't
// do that. These intra-assignment vars will later be passed to forked processes by value.
AstNode* const insertBeforep = m_classp ? controlp : nullptr;
addCLocalScope(flp, insertBeforep);
// Function for replacing values with intermediate variables
const auto replaceWithIntermediate = [&](AstNodeExpr* const valuep,
const std::string& name) {

View File

@ -0,0 +1,23 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2023 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
scenarios(simulator => 1);
compile(
verilator_flags2 => ["--exe --main --timing"],
make_main => 0,
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2023 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
class Foo;
string arra[2];
string arrb[string];
function new();
arra = '{"baz", "boo"};
arrb = '{"baz": "inga!", "boo": "..."};
endfunction
task automatic return_before_fork(bit b);
if (b) begin
// This is going to translate to a `goto` statement.
return;
end
// This will instantiate a `VlForkSync` object (local to the call, as we are under a class)
fork
#10 $display("forked process");
join
// This is where we jump to from the aforementioned goto. If we don't wrap the `VlForkSync`
// object in a scope that ends before that point, we will end up calling its destructor
// without having it initialized first.
endtask
task automatic return_before_select(bit b, int idx);
if (b) return; // goto
// This will create two temporary strings used to select from `arrb` and assign to it.
arrb[arra[idx]] = #10 "yah!";
// jump here
endtask
endclass
module t();
initial begin
Foo foo;
foo = new;
foo.return_before_fork(1'b1);
foo.return_before_select(1'b1, 1);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule