mirror of
https://github.com/verilator/verilator.git
synced 2025-07-31 07:56:10 +00:00
Support for condition operator on class objects (#4214)
This commit is contained in:
parent
e9135598b3
commit
ba82d43ca1
@ -1817,6 +1817,7 @@ public:
|
||||
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a nullptr
|
||||
inline bool isClassHandleValue() const;
|
||||
inline bool isZero() const;
|
||||
inline bool isOne() const;
|
||||
inline bool isNeqZero() const;
|
||||
|
@ -41,6 +41,10 @@ bool AstNode::isString() const VL_MT_STABLE {
|
||||
}
|
||||
bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
bool AstNode::isClassHandleValue() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|
||||
|| VN_IS(dtypep(), ClassRefDType);
|
||||
}
|
||||
bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
|
@ -122,6 +122,31 @@ private:
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
}
|
||||
void visit(AstNodeCond* nodep) override {
|
||||
// All class types are castable to each other. If they are of different types,
|
||||
// a compilation error will be thrown, so an explicit cast is required. Types were
|
||||
// already checked by V3Width and dtypep of a condition operator is a type of their
|
||||
// common base class, so both classes can be safetly casted.
|
||||
const AstClassRefDType* const thenClassDtypep
|
||||
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
|
||||
const AstClassRefDType* const elseClassDtypep
|
||||
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
|
||||
const bool castRequired = thenClassDtypep && elseClassDtypep
|
||||
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
|
||||
if (castRequired) {
|
||||
const AstClass* const commonBaseClassp
|
||||
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
|
||||
if (thenClassDtypep->classp() != commonBaseClassp) {
|
||||
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
|
||||
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
|
||||
}
|
||||
if (elseClassDtypep->classp() != commonBaseClassp) {
|
||||
AstNodeExpr* elsep = nodep->elsep()->unlinkFrBack();
|
||||
nodep->elsep(new AstCCast{elsep->fileline(), elsep, nodep});
|
||||
}
|
||||
}
|
||||
visit(static_cast<AstNodeTriop*>(nodep));
|
||||
}
|
||||
void visit(AstNodeTriop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
|
||||
|
@ -3405,9 +3405,9 @@ private:
|
||||
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
||||
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}");
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
||||
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
|
||||
// Prefer constants on left, since that often needs a shift, it lets
|
||||
// constant red remove the shift
|
||||
|
@ -1044,7 +1044,9 @@ public:
|
||||
}
|
||||
void visit(AstCCast* nodep) override {
|
||||
// Extending a value of the same word width is just a NOP.
|
||||
if (nodep->size() <= VL_IDATASIZE) {
|
||||
if (const AstClassRefDType* const classDtypep = VN_CAST(nodep->dtypep(), ClassRefDType)) {
|
||||
puts("(" + classDtypep->cType("", false, false) + ")(");
|
||||
} else if (nodep->size() <= VL_IDATASIZE) {
|
||||
puts("(IData)(");
|
||||
} else {
|
||||
puts("(QData)(");
|
||||
|
@ -503,9 +503,26 @@ private:
|
||||
// the size of this subexpression only.
|
||||
// Second call (final()) m_vup->width() is probably the expression size, so
|
||||
// the expression includes the size of the output too.
|
||||
if (nodep->thenp()->dtypep()->skipRefp() == nodep->elsep()->dtypep()->skipRefp()) {
|
||||
const AstNodeDType* const thenDTypep = nodep->thenp()->dtypep();
|
||||
const AstNodeDType* const elseDTypep = nodep->elsep()->dtypep();
|
||||
if (thenDTypep->skipRefp() == elseDTypep->skipRefp()) {
|
||||
// TODO might need a broader equation, use the Castable function?
|
||||
nodep->dtypeFrom(nodep->thenp()->dtypep());
|
||||
nodep->dtypeFrom(thenDTypep);
|
||||
} else if (nodep->thenp()->isClassHandleValue()
|
||||
|| nodep->elsep()->isClassHandleValue()) {
|
||||
AstNodeDType* commonClassTypep = nullptr;
|
||||
if (nodep->thenp()->isClassHandleValue() && nodep->elsep()->isClassHandleValue()) {
|
||||
// Get the most-deriving class type that both arguments can be casted to.
|
||||
commonClassTypep = getCommonClassTypep(nodep->thenp(), nodep->elsep());
|
||||
}
|
||||
if (commonClassTypep) {
|
||||
nodep->dtypep(commonClassTypep);
|
||||
} else {
|
||||
nodep->v3error("Incompatible types of operands of condition operator: "
|
||||
<< thenDTypep->prettyTypeName() << " and "
|
||||
<< elseDTypep->prettyTypeName());
|
||||
nodep->dtypeFrom(thenDTypep);
|
||||
}
|
||||
} else if (nodep->thenp()->isDouble() || nodep->elsep()->isDouble()) {
|
||||
nodep->dtypeSetDouble();
|
||||
} else if (nodep->thenp()->isString() || nodep->elsep()->isString()) {
|
||||
@ -7309,6 +7326,30 @@ private:
|
||||
if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp());
|
||||
return false;
|
||||
}
|
||||
AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) {
|
||||
// Return the class type that both nodep1 and nodep2 are castable to.
|
||||
// If both are null, return the type of null constant.
|
||||
// If one is a class and one is null, return AstClassRefDType that points to that class.
|
||||
// If no common class type exists, return nullptr.
|
||||
|
||||
// First handle cases with null values and when one class is a super class of the other.
|
||||
if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2);
|
||||
const Castable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2);
|
||||
if (castable == SAMEISH || castable == COMPATIBLE) {
|
||||
return nodep1->dtypep()->cloneTree(false);
|
||||
} else if (castable == DYNAMIC_CLASS) {
|
||||
return nodep2->dtypep()->cloneTree(false);
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType);
|
||||
while (classDtypep1) {
|
||||
const Castable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2);
|
||||
if (castable == COMPATIBLE) return classDtypep1->cloneTree(false);
|
||||
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// METHODS - casting
|
||||
|
21
test_regress/t/t_class_assign_cond.pl
Executable file
21
test_regress/t/t_class_assign_cond.pl
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 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(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
70
test_regress/t/t_class_assign_cond.v
Normal file
70
test_regress/t/t_class_assign_cond.v
Normal file
@ -0,0 +1,70 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Cls;
|
||||
int f;
|
||||
function new(int x);
|
||||
f = x;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ExtendCls extends Cls;
|
||||
function new(int x);
|
||||
super.new(x);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class AnotherExtendCls extends Cls;
|
||||
function new(int x);
|
||||
super.new(x);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ExtendExtendCls extends ExtendCls;
|
||||
function new(int x);
|
||||
super.new(x);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
initial begin
|
||||
Cls cls1 = null, cls2 = null;
|
||||
ExtendCls ext_cls = null;
|
||||
AnotherExtendCls an_ext_cls = null;
|
||||
ExtendExtendCls ext_ext_cls = null;
|
||||
|
||||
cls1 = (cls1 == null) ? cls2 : cls1;
|
||||
if (cls1 != null) $stop;
|
||||
|
||||
cls1 = new(1);
|
||||
cls1 = (cls1 == null) ? cls2 : cls1;
|
||||
if (cls1.f != 1) $stop;
|
||||
|
||||
cls1 = (cls1 != null) ? cls2 : cls1;
|
||||
if (cls1 != null) $stop;
|
||||
|
||||
cls1 = new(1);
|
||||
cls2 = new(2);
|
||||
cls1 = (cls1 != null) ? cls2 : cls1;
|
||||
if (cls1.f != 2) $stop;
|
||||
|
||||
cls1 = null;
|
||||
cls1 = (ext_cls != null) ? ext_cls : cls2;
|
||||
if (cls1.f != 2) $stop;
|
||||
|
||||
ext_cls = new(3);
|
||||
cls1 = (ext_cls != null) ? ext_cls : cls2;
|
||||
if (cls1.f != 3) $stop;
|
||||
|
||||
ext_ext_cls = new(4);
|
||||
an_ext_cls = new(5);
|
||||
cls1 = (ext_ext_cls.f != 4) ? ext_ext_cls : an_ext_cls;
|
||||
if (cls1.f != 5) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
21
test_regress/t/t_class_assign_cond_bad.out
Normal file
21
test_regress/t/t_class_assign_cond_bad.out
Normal file
@ -0,0 +1,21 @@
|
||||
%Error: t/t_class_assign_cond_bad.v:22:25: Incompatible types of operands of condition operator: CLASSREFDTYPE 'Cls1' and CLASSREFDTYPE 'Cls2'
|
||||
: ... In instance t
|
||||
22 | c1 = (c1 != null) ? c1 : c2;
|
||||
| ^
|
||||
%Error: t/t_class_assign_cond_bad.v:23:10: Assign RHS expects a CLASSREFDTYPE 'Cls1', got CLASSREFDTYPE 'Cls2'
|
||||
: ... In instance t
|
||||
23 | c1 = (c1 != null) ? c2 : c2;
|
||||
| ^
|
||||
%Error: t/t_class_assign_cond_bad.v:24:25: Incompatible types of operands of condition operator: BASICDTYPE 'logic' and CLASSREFDTYPE 'Cls2'
|
||||
: ... In instance t
|
||||
24 | c2 = (c1 == null) ? 1'b1 : c2;
|
||||
| ^
|
||||
%Error: t/t_class_assign_cond_bad.v:24:10: Assign RHS expects a CLASSREFDTYPE 'Cls2', got BASICDTYPE 'logic'
|
||||
: ... In instance t
|
||||
24 | c2 = (c1 == null) ? 1'b1 : c2;
|
||||
| ^
|
||||
%Error: t/t_class_assign_cond_bad.v:25:29: Incompatible types of operands of condition operator: CLASSREFDTYPE 'ExtCls1' and CLASSREFDTYPE 'Cls1'
|
||||
: ... In instance t
|
||||
25 | ext_c1 = (c1 == null) ? ext_c1 : c1;
|
||||
| ^
|
||||
%Error: Exiting due to
|
19
test_regress/t/t_class_assign_cond_bad.pl
Executable file
19
test_regress/t/t_class_assign_cond_bad.pl
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 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(
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
27
test_regress/t/t_class_assign_cond_bad.v
Normal file
27
test_regress/t/t_class_assign_cond_bad.v
Normal file
@ -0,0 +1,27 @@
|
||||
// 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 Cls1;
|
||||
endclass
|
||||
|
||||
class Cls2;
|
||||
endclass
|
||||
|
||||
class ExtCls1;
|
||||
endclass
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
Cls1 c1;
|
||||
Cls2 c2;
|
||||
ExtCls1 ext_c1;
|
||||
|
||||
initial begin
|
||||
c1 = (c1 != null) ? c1 : c2;
|
||||
c1 = (c1 != null) ? c2 : c2;
|
||||
c2 = (c1 == null) ? 1'b1 : c2;
|
||||
ext_c1 = (c1 == null) ? ext_c1 : c1;
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user