Internals: Fix annotation checker not considering base class virtual function annotations (#5459)

This commit is contained in:
Bartłomiej Chmiel 2024-10-01 03:34:34 +02:00 committed by GitHub
parent 13a1240359
commit 1a31aa5d62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 195 additions and 132 deletions

View File

@ -48,9 +48,11 @@ def fully_qualified_name(node):
if node.kind == CursorKind.TRANSLATION_UNIT:
return []
res = fully_qualified_name(node.semantic_parent)
displayname = node.displayname
displayname = [displayname] if displayname else []
if res:
return res + ([node.displayname] if node.displayname else [])
return [node.displayname] if node.displayname else []
return res + displayname
return displayname
# Returns True, if `class_node` contains node
@ -139,31 +141,32 @@ class VlAnnotations:
result = VlAnnotations()
for node in nodes:
if node.kind == CursorKind.ANNOTATE_ATTR:
if node.displayname == "MT_START":
displayname = node.displayname
if displayname == "MT_START":
result.mt_start = True
elif node.displayname == "MT_SAFE":
elif displayname == "MT_SAFE":
result.mt_safe = True
elif node.displayname == "MT_STABLE":
elif displayname == "MT_STABLE":
result.stable_tree = True
elif node.displayname == "MT_SAFE_POSTINIT":
elif displayname == "MT_SAFE_POSTINIT":
result.mt_safe_postinit = True
elif node.displayname == "MT_UNSAFE":
elif displayname == "MT_UNSAFE":
result.mt_unsafe = True
elif node.displayname == "MT_UNSAFE_ONE":
elif displayname == "MT_UNSAFE_ONE":
result.mt_unsafe_one = True
elif node.displayname == "MT_DISABLED":
elif displayname == "MT_DISABLED":
result.mt_disabled = True
elif node.displayname == "PURE":
elif displayname == "PURE":
result.pure = True
elif node.displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
elif displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
result.acquire = True
elif node.displayname in ["RELEASE", "RELEASE_SHARED"]:
elif displayname in ["RELEASE", "RELEASE_SHARED"]:
result.release = True
elif node.displayname == "REQUIRES":
elif displayname == "REQUIRES":
result.requires = True
elif node.displayname in ["EXCLUDES", "MT_SAFE_EXCLUDES"]:
elif displayname in ["EXCLUDES", "MT_SAFE_EXCLUDES"]:
result.excludes = True
elif node.displayname == "GUARDED_BY":
elif displayname == "GUARDED_BY":
result.guarded = True
# Attributes are always at the beginning
elif not node.kind.is_attribute():
@ -304,6 +307,7 @@ class CallAnnotationsValidator:
self._defines: dict[str, str] = {}
self._call_location: Optional[FunctionInfo] = None
self._caller: Optional[FunctionInfo] = None
self._base_func_declarations: dict[str, clang.cindex.Cursor] = {}
self._constructor_context: list[clang.cindex.Cursor] = []
self._level: int = 0
@ -695,6 +699,14 @@ class CallAnnotationsValidator:
def process_function_definition(self, node: clang.cindex.Cursor):
[supported, refd, annotations, _] = self.get_referenced_node_info(node)
# Fetch virtual annotations from base class.
# Set refd to virtual definition if present.
signature = node.displayname
if signature in self._base_func_declarations:
refd = self._base_func_declarations[signature]
virtual_annotations = VlAnnotations.from_nodes_list(refd.get_children())
annotations = annotations | virtual_annotations
if refd and self._is_ignored_def(node, refd):
return None
@ -719,7 +731,7 @@ class CallAnnotationsValidator:
def_annotations.mt_disabled = True
def_annotations.excludes = True
if not (def_annotations.is_empty() or def_annotations == annotations):
if def_annotations != annotations:
# Use definition's annotations for the diagnostic
# source (i.e. the definition)
self._caller = FunctionInfo.from_node(node, refd, def_annotations)
@ -728,9 +740,10 @@ class CallAnnotationsValidator:
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
# Use concatenation of definition and declaration annotations
# for calls validation.
self._caller = FunctionInfo.from_node(node, refd, def_annotations | annotations)
else:
# Use concatenation of definition and declaration annotations
# for calls validation.
self._caller = FunctionInfo.from_node(node, refd, def_annotations | annotations)
prev_call_location = self._call_location
self._call_location = self._caller
@ -758,16 +771,30 @@ class CallAnnotationsValidator:
# Nodes not located inside definition
def dispatch_node(self, node: clang.cindex.Cursor):
if node.kind in [
kind = node.kind
if kind is CursorKind.CXX_BASE_SPECIFIER:
# Get referenced virtual declarations from base class.
for base in node.get_children():
if base.referenced:
for declaration in base.referenced.get_children():
self._base_func_declarations[declaration.displayname] = declaration
elif kind in [
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.CONSTRUCTOR,
CursorKind.CONVERSION_FUNCTION
]:
if node.is_definition():
return self.process_function_definition(node)
# else:
return self.process_function_declaration(node)
return self.iterate_children(node.get_children(), self.dispatch_node)
result = self.iterate_children(node.get_children(), self.dispatch_node)
# Clean declarations if class declaration processing is finished.
if kind in [
CursorKind.CLASS_DECL, CursorKind.STRUCT_DECL, CursorKind.UNION_DECL,
CursorKind.ENUM_DECL, CursorKind.UNEXPOSED_DECL
]:
self._base_func_declarations = {}
return result
def process_translation_unit(self, translation_unit: clang.cindex.TranslationUnit):
self._level += 1

View File

@ -2362,8 +2362,9 @@ public:
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
AstNodeDType* findLogicRangeDType(const VNumRange& range, int widthMin,
VSigning numeric) const;
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const;
VSigning numeric) const VL_MT_STABLE;
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin,
VSigning numeric) const VL_MT_STABLE;
AstNodeDType* findBasicDType(VBasicDTypeKwd kwd) const;
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);

View File

@ -54,7 +54,7 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const VL_MT_STABLE;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
/// Require VlUnpacked, instead of [] for POD elements.
/// A non-POD object is always compound, but some POD elements
/// are compound when methods calls operate on object, or when
@ -74,7 +74,7 @@ public:
virtual int widthAlignBytes() const = 0;
// (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,...
virtual int widthTotalBytes() const = 0;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
// Iff has a non-null refDTypep(), as generic node function
virtual AstNodeDType* virtRefDTypep() const { return nullptr; }
// Iff has refDTypep(), set as generic node function
@ -86,7 +86,7 @@ public:
// Assignable equivalence. Call skipRefp() on this and samep before calling
virtual bool similarDType(const AstNodeDType* samep) const = 0;
// Iff has a non-null subDTypep(), as generic node function
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
virtual AstNodeDType* subDTypep() const VL_MT_STABLE { return nullptr; }
virtual bool isFourstate() const;
// Ideally an IEEE $typename
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
@ -105,14 +105,14 @@ public:
m_numeric = nodep->m_numeric;
}
//
int width() const VL_MT_SAFE { return m_width; }
int width() const VL_MT_STABLE { return m_width; }
void numeric(VSigning flag) { m_numeric = flag; }
bool isSigned() const VL_MT_SAFE { return m_numeric.isSigned(); }
bool isSigned() const VL_MT_STABLE { return m_numeric.isSigned(); }
bool isNosign() const VL_MT_SAFE { return m_numeric.isNosign(); }
VSigning numeric() const { return m_numeric; }
int widthWords() const VL_MT_SAFE { return VL_WORDS_I(width()); }
int widthMin() const VL_MT_SAFE { // If sized, the size,
// if unsized the min digits to represent it
VSigning numeric() const VL_MT_STABLE { return m_numeric; }
int widthWords() const VL_MT_STABLE { return VL_WORDS_I(width()); }
int widthMin() const VL_MT_STABLE { // If sized, the size,
// if unsized the min digits to represent it
return m_widthMin ? m_widthMin : m_width;
}
int widthPow2() const;
@ -218,7 +218,7 @@ public:
string prettyDTypeName(bool) const override;
bool isCompound() const override { return !packed(); }
// For basicp() we reuse the size to indicate a "fake" basic type of same size
AstBasicDType* basicp() const override {
AstBasicDType* basicp() const override VL_MT_STABLE {
if (!m_packed) return nullptr;
return (isFourstate()
? VN_AS(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
@ -244,9 +244,11 @@ public:
static bool packedUnsup() { return true; }
void isFourstate(bool flag) { m_isFourstate = flag; }
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; }
static int lo() { return 0; }
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
static int lo() VL_MT_STABLE { return 0; }
int hi() const VL_MT_STABLE {
return dtypep()->width() - 1;
} // Packed classes look like arrays
VNumRange declRange() const VL_MT_STABLE { return VNumRange{hi(), lo()}; }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; }
};
@ -269,8 +271,8 @@ public:
}
ASTGEN_MEMBERS_AstEnumItem;
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
void name(const string& flag) override { m_name = flag; }
};
@ -416,8 +418,8 @@ public:
return m.m_keyword;
}
bool isBitLogic() const { return keyword().isBitLogic(); }
bool isDouble() const VL_MT_SAFE { return keyword().isDouble(); }
bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; }
bool isDouble() const VL_MT_STABLE { return keyword().isDouble(); }
bool isEvent() const VL_MT_STABLE { return keyword() == VBasicDTypeKwd::EVENT; }
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; }
@ -434,7 +436,7 @@ public:
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_SAFE { return keyword().isString(); }
bool isString() const VL_MT_STABLE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); }
bool isRanged() const { return rangep() || m.m_nrange.ranged(); }
bool isDpiBitVec() const { // DPI uses svBitVecVal
@ -479,7 +481,7 @@ public:
// METHODS
// Will be removed in V3Width, which relies on this
// being a child not a dtype pointed node
bool maybePointedTo() const override { return false; }
bool maybePointedTo() const override VL_MT_SAFE { return false; }
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
@ -563,7 +565,7 @@ public:
int widthTotalBytes() const override { return 0; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
AstClass* classp() const VL_MT_STABLE { return m_classp; }
@ -624,10 +626,10 @@ public:
dtypep(this);
}
ASTGEN_MEMBERS_AstConstraintRefDType;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool undead() const override { return true; }
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
@ -749,10 +751,10 @@ public:
}
ASTGEN_MEMBERS_AstEmptyQueueDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool undead() const override { return true; }
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
@ -931,8 +933,8 @@ public:
ASTGEN_MEMBERS_AstMemberDType;
void dumpSmall(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeUOrStructDType* getChildStructp() const;
AstNodeDType* subDTypep() const override VL_MT_STABLE {
@ -979,11 +981,11 @@ public:
}
ASTGEN_MEMBERS_AstNBACommitQueueDType;
AstNodeDType* subDTypep() const override { return m_subDTypep; }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_subDTypep; }
bool partial() const { return m_partial; }
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override { return nullptr; }
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return 1; }
@ -1025,8 +1027,8 @@ public:
int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); }
// METHODS
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
void name(const string& flag) override { m_name = flag; }
VVarType varType() const { return m_varType; } // * = Type of variable
bool isParam() const { return true; }
@ -1044,7 +1046,7 @@ public:
explicit AstParseTypeDType(FileLine* fl)
: ASTGEN_SUPER_ParseTypeDType(fl) {}
ASTGEN_MEMBERS_AstParseTypeDType;
AstNodeDType* dtypep() const { return nullptr; }
AstNodeDType* dtypep() const VL_MT_STABLE { return nullptr; }
// METHODS
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
@ -1263,10 +1265,10 @@ public:
}
ASTGEN_MEMBERS_AstStreamDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool undead() const override { return true; }
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
@ -1331,10 +1333,10 @@ public:
}
ASTGEN_MEMBERS_AstVoidDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool undead() const override { return true; }
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }

View File

@ -52,7 +52,7 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
// TODO: The only AstNodeExpr without dtype is AstArg. Otherwise this could be final.
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCFunc::emitOpName
virtual string emitC() = 0;
@ -525,7 +525,7 @@ public:
int instrCount() const override { return widthInstrs(); }
VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
void varp(AstVar* varp) {
m_varp = varp;
dtypeFrom((AstNode*)varp);
@ -578,7 +578,7 @@ public:
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstArg;
bool hasDType() const override { return false; }
bool hasDType() const override VL_MT_SAFE { return false; }
string name() const override VL_MT_STABLE { return m_name; } // * = Pin name, ""=go by number
void name(const string& name) override { m_name = name; }
bool emptyConnectNoNext() const { return !exprp() && name() == "" && !nextp(); }
@ -2025,12 +2025,12 @@ public:
}
ASTGEN_MEMBERS_AstSelLoopVars;
bool same(const AstNode* /*samep*/) const override { return true; }
bool maybePointedTo() const override { return false; }
bool maybePointedTo() const override VL_MT_SAFE { return false; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
bool hasDType() const override { return false; }
bool hasDType() const override VL_MT_SAFE { return false; }
};
class AstSetAssoc final : public AstNodeExpr {
// Set an assoc array element and return object, '{}
@ -4704,8 +4704,8 @@ public:
int widthConst() const { return VN_AS(widthp(), Const)->toSInt(); }
int lsbConst() const { return VN_AS(lsbp(), Const)->toSInt(); }
int msbConst() const { return lsbConst() + widthConst() - 1; }
VNumRange& declRange() { return m_declRange; }
const VNumRange& declRange() const { return m_declRange; }
VNumRange& declRange() VL_MT_STABLE { return m_declRange; }
const VNumRange& declRange() const VL_MT_STABLE { return m_declRange; }
void declRange(const VNumRange& flag) { m_declRange = flag; }
int declElWidth() const { return m_declElWidth; }
void declElWidth(int flag) { m_declElWidth = flag; }
@ -4739,8 +4739,8 @@ public:
bool same(const AstNode*) const override { return true; }
int instrCount() const override { return 10; } // Removed before matters
// For widthConst()/loConst etc, see declRange().elements() and other VNumRange methods
VNumRange& declRange() { return m_declRange; }
const VNumRange& declRange() const { return m_declRange; }
VNumRange& declRange() VL_MT_STABLE { return m_declRange; }
const VNumRange& declRange() const VL_MT_STABLE { return m_declRange; }
void declRange(const VNumRange& flag) { m_declRange = flag; }
};
class AstSubstrN final : public AstNodeTriop {

View File

@ -125,7 +125,7 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool isGateOptimizable() const override {
return !((m_dpiExport || m_dpiImport) && !m_dpiPure);
}
@ -263,7 +263,7 @@ public:
ASTGEN_MEMBERS_AstNodeModule;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; }
virtual bool timescaleMatters() const = 0;
// ACCESSORS
@ -371,7 +371,7 @@ public:
ASTGEN_MEMBERS_AstNodeAssign;
// Clone single node, just get same type back.
virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
virtual bool cleanRhs() const { return true; }
int instrCount() const override { return widthInstrs(); }
bool same(const AstNode*) const override { return true; }
@ -421,7 +421,7 @@ public:
void name(const string& name) override { m_name = name; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
VAssertType type() const { return m_type; }
VAssertType type() const VL_MT_SAFE { return m_type; }
VAssertDirectiveType directive() const { return m_directive; }
bool immediate() const {
return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE
@ -673,7 +673,7 @@ public:
}
ASTGEN_MEMBERS_AstCFunc;
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
bool same(const AstNode* samep) const override {
@ -830,7 +830,7 @@ public:
void cloneRelink() override {} // TODO V3Param shouldn't require avoiding cloneRelinkGen
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
// ACCESSORS
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
void name(const string& name) override { m_name = name; }
@ -872,7 +872,7 @@ public:
void dumpJson(std::ostream& str) const override;
// ACCESSORS
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
void name(const string& name) override { m_name = name; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
@ -900,7 +900,7 @@ public:
void dumpJson(std::ostream& str) const override;
// ACCESSORS
string name() const override VL_MT_STABLE { return m_cellp->name(); }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
string origModName() const {
return m_cellp->origModName();
@ -926,7 +926,7 @@ public:
ASTGEN_MEMBERS_AstClassExtends;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
string verilogKwd() const override { return isImplements() ? "implements" : "extends"; }
// Class being extended (after link and instantiation if needed)
AstClass* classOrNullp() const;
@ -988,7 +988,7 @@ public:
VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
};
class AstConstPool final : public AstNode {
// Container for const static data
@ -1004,7 +1004,7 @@ class AstConstPool final : public AstNode {
public:
explicit AstConstPool(FileLine* fl);
ASTGEN_MEMBERS_AstConstPool;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
void cloneRelink() override { V3ERROR_NA; }
AstModule* modp() const { return m_modp; }
@ -1036,7 +1036,7 @@ public:
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool same(const AstNode* /*samep*/) const override { return true; }
void isStatic(bool flag) { m_isStatic = flag; }
bool isStatic() const { return m_isStatic; }
@ -1174,8 +1174,8 @@ public:
this->valuep(valuep);
}
ASTGEN_MEMBERS_AstInitItem;
bool maybePointedTo() const override { return true; }
bool hasDType() const override { return false; } // See valuep()'s dtype instead
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool hasDType() const override VL_MT_SAFE { return false; } // See valuep()'s dtype instead
};
class AstIntfRef final : public AstNode {
// An interface reference
@ -1224,7 +1224,7 @@ public:
this->addVarsp(varsp);
}
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
ASTGEN_MEMBERS_AstModport;
};
class AstModportFTaskRef final : public AstNode {
@ -1269,7 +1269,7 @@ public:
string name() const override VL_MT_STABLE { return m_name; }
void direction(const VDirection& flag) { m_direction = flag; }
VDirection direction() const { return m_direction; }
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
void varp(AstVar* varp) { m_varp = varp; }
};
class AstNetlist final : public AstNode {
@ -1489,7 +1489,7 @@ public:
this->propp(propp);
}
ASTGEN_MEMBERS_AstPropSpec;
bool hasDType() const override {
bool hasDType() const override VL_MT_SAFE {
return true;
} // Used under Cover, which expects a bool child
};
@ -1538,7 +1538,7 @@ public:
BROKEN_RTN(!m_modp);
return nullptr;
}
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; }
void dump(std::ostream& str) const override;
@ -1620,7 +1620,7 @@ public:
ASTGEN_MEMBERS_AstSenTree;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool isMulti() const { return m_multi; }
void multi(bool flag) { m_multi = true; }
// METHODS
@ -1670,7 +1670,7 @@ class AstTopScope final : public AstNode {
public:
ASTGEN_MEMBERS_AstTopScope;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
};
class AstTypeTable final : public AstNode {
// Container for hash of standard data types
@ -1689,7 +1689,7 @@ class AstTypeTable final : public AstNode {
public:
explicit AstTypeTable(FileLine* fl);
ASTGEN_MEMBERS_AstTypeTable;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
void cloneRelink() override { V3ERROR_NA; }
AstBasicDType* findBasicDType(FileLine* fl, VBasicDTypeKwd kwd);
AstBasicDType* findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, int width, int widthMin,
@ -1733,8 +1733,8 @@ public:
}
// METHODS
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
void name(const string& flag) override { m_name = flag; }
bool attrPublic() const { return m_attrPublic; }
void attrPublic(bool flag) { m_attrPublic = flag; }
@ -1752,7 +1752,7 @@ public:
ASTGEN_MEMBERS_AstTypedefFwd;
// METHODS
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
};
class AstUdpTable final : public AstNode {
// @astgen op1 := linesp : List[AstUdpTableLine]
@ -1772,7 +1772,7 @@ public:
, m_text{text} {}
ASTGEN_MEMBERS_AstUdpTableLine;
string name() const override VL_MT_STABLE { return m_text; }
string text() const { return m_text; }
string text() const VL_MT_SAFE { return m_text; }
};
class AstVar final : public AstNode {
// A variable (in/out/wire/reg/param) inside a module
@ -1933,9 +1933,9 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string name() const override VL_MT_STABLE VL_MT_SAFE { return m_name; } // * = Var name
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string origName() const override { return m_origName; } // * = Original name
void origName(const string& name) { m_origName = name; }
VVarType varType() const VL_MT_SAFE { return m_varType; } // * = Type of variable
@ -2177,12 +2177,12 @@ public:
}
cloneRelinkGen();
}
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return scopep()->name() + "->" + varp()->name(); }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
void scopep(AstScope* nodep) { m_scopep = nodep; }
@ -2243,7 +2243,7 @@ public:
this->fvarp(fvarp);
}
ASTGEN_MEMBERS_AstFunc;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstFunc{fileline(), name, nullptr, nullptr};
}
@ -2256,7 +2256,7 @@ public:
AstLet(FileLine* fl, const string& name)
: ASTGEN_SUPER_Let(fl, name, nullptr) {}
ASTGEN_MEMBERS_AstLet;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
return nullptr;
@ -2269,7 +2269,7 @@ public:
AstProperty(FileLine* fl, const string& name, AstNode* stmtp)
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
ASTGEN_MEMBERS_AstProperty;
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstProperty{fileline(), name, nullptr};
}
@ -2337,13 +2337,13 @@ public:
: ASTGEN_SUPER_Class(fl, name) {}
ASTGEN_MEMBERS_AstClass;
string verilogKwd() const override { return "class"; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool timescaleMatters() const override { return false; }
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
AstNode* membersp() const { return stmtsp(); }
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
bool isExtended() const { return m_extended; }
void isExtended(bool flag) { m_extended = flag; }
@ -2568,7 +2568,7 @@ public:
bool same(const AstNode* /*samep*/) const override { return true; }
// Will be removed in V3Width, which relies on this
// being a child not a dtype pointed node
bool maybePointedTo() const override { return false; }
bool maybePointedTo() const override VL_MT_SAFE { return false; }
};
class AstRange final : public AstNodeRange {
// Range specification, for use under variables and cells
@ -2807,7 +2807,7 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
int binNum() const { return m_binNum; }
void binNum(int flag) { m_binNum = flag; }
int offset() const { return m_offset; }
@ -3092,7 +3092,7 @@ public:
ASTGEN_MEMBERS_AstJumpBlock;
const char* broken() const override;
int instrCount() const override { return 0; }
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool same(const AstNode* /*samep*/) const override { return true; }
int labelNum() const { return m_labelNum; }
void labelNum(int flag) { m_labelNum = flag; }
@ -3138,7 +3138,7 @@ public:
: ASTGEN_SUPER_JumpLabel(fl)
, m_blockp{blockp} {}
ASTGEN_MEMBERS_AstJumpLabel;
bool maybePointedTo() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
const char* broken() const override {
BROKEN_RTN(!blockp()->brokeExistsAbove());
BROKEN_RTN(blockp()->labelp() != this);
@ -3403,8 +3403,8 @@ public:
int instrCount() const override { return 100; } // Large...
ASTGEN_MEMBERS_AstTraceDecl;
string name() const override VL_MT_STABLE { return m_showname; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool same(const AstNode* samep) const override { return false; }
string showname() const { return m_showname; } // * = Var name
// Details on what we're tracing
@ -3445,7 +3445,7 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; }
bool hasDType() const override { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool same(const AstNode* samep) const override {
return declp() == VN_DBG_AS(samep, TraceInc)->declp();
}

View File

@ -20,7 +20,7 @@
// If operands are constant, replace this node with constant.
//*************************************************************************
#define VL_MT_DISABLED_CODE_UNIT 1
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "config_build.h"
#include "verilatedos.h"

View File

@ -282,7 +282,7 @@ public:
class ConstructorCallsUnsafeLocalFunction {
public:
void unsafe_function() VL_MT_UNSAFE{};
void unsafe_function() VL_MT_UNSAFE {};
ConstructorCallsUnsafeLocalFunction() { unsafe_function(); }
};
class ConstructorCallsStaticFunctionNoAnnotation {
@ -367,6 +367,27 @@ public:
ConstructorCallsGlobalObjectMember() { dummyGlobalVar.d.dummy_function2(); }
};
namespace VirtualInheritance {
struct Base1 {
virtual int func(int a, int b) const VL_PURE = 0;
virtual ~Base1() = default;
};
struct Base2 {
virtual int func() const VL_PURE = 0;
virtual ~Base2() = default;
};
struct Derived final : public Base1, Base2 {
int func(int a, int b) const override VL_PURE { return notPure(); }
int func() const override VL_PURE { return notPure(); }
static int notPure() {
static int s_counter;
return ++s_counter;
}
};
} //namespace VirtualInheritance
class TestClassConstructor {
void safe_function_unsafe_constructor_bad() VL_MT_SAFE {
ConstructorCallsUnsafeLocalFunction f{};
@ -408,6 +429,7 @@ class TestClassConstructor {
void safe_function_calls_constructor_global_object_member_bad() VL_MT_STABLE {
ConstructorCallsGlobalObjectMember f{};
}
void virtual_function_mismatch() { VirtualInheritance::Derived pd{}; }
};
#endif // T_DIST_ATTRIBUTES_MT_ENABLED_H_

View File

@ -709,49 +709,59 @@ t/t_dist_attributes/mt_enabled.h:124: []
t/t_dist_attributes/mt_enabled.cpp:75: [requires] TestClass::scm_ua_VL_REQUIRES(VerilatedMutex &)
%Error: "TestClassConstructor::safe_function_calls_constructor_global_object_bad()" is stable_tree but calls non-stable_tree or non-mtsafe
t/t_dist_attributes/mt_enabled.h:405: [stable_tree] TestClassConstructor::safe_function_calls_constructor_global_object_bad()
t/t_dist_attributes/mt_enabled.h:426: [stable_tree] TestClassConstructor::safe_function_calls_constructor_global_object_bad()
t/t_dist_attributes/mt_enabled.h:355: [] DummyClass::dummy_function()
%Error: "TestClassConstructor::safe_function_calls_constructor_global_object_member_bad()" is stable_tree but calls non-stable_tree or non-mtsafe
t/t_dist_attributes/mt_enabled.h:408: [stable_tree] TestClassConstructor::safe_function_calls_constructor_global_object_member_bad()
t/t_dist_attributes/mt_enabled.h:429: [stable_tree] TestClassConstructor::safe_function_calls_constructor_global_object_member_bad()
t/t_dist_attributes/mt_enabled.h:350: [] DummyClass2::dummy_function2()
%Error: "TestClassConstructor::safe_function_calls_constructor_local_calls_class_global_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:402: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_class_global_bad()
t/t_dist_attributes/mt_enabled.h:423: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_class_global_bad()
t/t_dist_attributes/mt_enabled.h:280: [] StaticClass::static_class_function()
%Error: "TestClassConstructor::safe_function_calls_constructor_local_calls_global_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:399: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_global_bad()
t/t_dist_attributes/mt_enabled.h:420: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_global_bad()
t/t_dist_attributes/mt_enabled.h:276: [] static_function()
%Error: "TestClassConstructor::safe_function_calls_constructor_with_unsafepointer_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:391: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafepointer_bad()
t/t_dist_attributes/mt_enabled.h:412: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafepointer_bad()
t/t_dist_attributes/mt_enabled.h:311: [mt_unsafe] UnsafeFunction::unsafe_function()
%Error: "TestClassConstructor::safe_function_calls_constructor_with_unsafereference_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:395: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafereference_bad()
t/t_dist_attributes/mt_enabled.h:416: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafereference_bad()
t/t_dist_attributes/mt_enabled.h:311: [mt_unsafe] UnsafeFunction::unsafe_function()
%Error: "TestClassConstructor::safe_function_local_function_global_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:377: [mt_safe] TestClassConstructor::safe_function_local_function_global_bad()
t/t_dist_attributes/mt_enabled.h:398: [mt_safe] TestClassConstructor::safe_function_local_function_global_bad()
t/t_dist_attributes/mt_enabled.h:276: [] static_function()
%Error: "TestClassConstructor::safe_function_static_constructor_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:374: [mt_safe] TestClassConstructor::safe_function_static_constructor_bad()
t/t_dist_attributes/mt_enabled.h:395: [mt_safe] TestClassConstructor::safe_function_static_constructor_bad()
t/t_dist_attributes/mt_enabled.h:276: [] static_function()
%Error: "TestClassConstructor::safe_function_unsafe_constructor_bad()" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.h:371: [mt_safe] TestClassConstructor::safe_function_unsafe_constructor_bad()
t/t_dist_attributes/mt_enabled.h:392: [mt_safe] TestClassConstructor::safe_function_unsafe_constructor_bad()
t/t_dist_attributes/mt_enabled.h:285: [mt_unsafe] ConstructorCallsUnsafeLocalFunction::unsafe_function()
%Error: "UnannotatedMtDisabledClass::unannotatedMtDisabledMethodBad()" defined in a file marked as VL_MT_DISABLED_CODE_UNIT has declaration(s) without VL_MT_DISABLED annotation
t/t_dist_attributes/mt_disabled.cpp:23: [mt_disabled, excludes] UnannotatedMtDisabledClass::unannotatedMtDisabledMethodBad()
t/t_dist_attributes/mt_disabled.h:27: [mt_disabled, excludes] UnannotatedMtDisabledClass::unannotatedMtDisabledMethodBad() [declaration]
t/t_dist_attributes/mt_disabled.cpp:23: [] UnannotatedMtDisabledClass::unannotatedMtDisabledMethodBad()
t/t_dist_attributes/mt_disabled.h:27: [] UnannotatedMtDisabledClass::unannotatedMtDisabledMethodBad()
%Error: "UnannotatedMtDisabledClass::unannotatedMtDisabledStaticMethodBad()" defined in a file marked as VL_MT_DISABLED_CODE_UNIT has declaration(s) without VL_MT_DISABLED annotation
t/t_dist_attributes/mt_disabled.cpp:26: [mt_disabled, excludes] UnannotatedMtDisabledClass::unannotatedMtDisabledStaticMethodBad()
t/t_dist_attributes/mt_disabled.h:28: [mt_disabled, excludes] UnannotatedMtDisabledClass::unannotatedMtDisabledStaticMethodBad() [declaration]
t/t_dist_attributes/mt_disabled.cpp:26: [] UnannotatedMtDisabledClass::unannotatedMtDisabledStaticMethodBad()
t/t_dist_attributes/mt_disabled.h:28: [] UnannotatedMtDisabledClass::unannotatedMtDisabledStaticMethodBad()
%Error: "VirtualInheritance::Base1::func(int, int)" is pure but calls non-pure function(s)
t/t_dist_attributes/mt_enabled.h:382: [pure] VirtualInheritance::Base1::func(int, int)
t/t_dist_attributes/mt_enabled.h:384: [] VirtualInheritance::Derived::notPure()
%Error: "VirtualInheritance::Base2::func()" is pure but calls non-pure function(s)
t/t_dist_attributes/mt_enabled.h:383: [pure] VirtualInheritance::Base2::func()
t/t_dist_attributes/mt_enabled.h:384: [] VirtualInheritance::Derived::notPure()
%Error: "ifh_test_caller_func_VL_MT_SAFE(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s)
t/t_dist_attributes/mt_enabled.cpp:53: [mt_safe] ifh_test_caller_func_VL_MT_SAFE(VerilatedMutex &)
t/t_dist_attributes/mt_enabled.h:94: [] ifh_NO_ANNOTATION(VerilatedMutex &)
@ -1169,7 +1179,8 @@ t/t_dist_attributes/mt_enabled.cpp:60: [release]
t/t_dist_attributes/mt_enabled.cpp:60: [requires] sfc_VL_REQUIRES(VerilatedMutex &)
%Error: "unannotatedMtDisabledFunctionBad()" defined in a file marked as VL_MT_DISABLED_CODE_UNIT has declaration(s) without VL_MT_DISABLED annotation
t/t_dist_attributes/mt_disabled.cpp:20: [mt_disabled, excludes] unannotatedMtDisabledFunctionBad()
t/t_dist_attributes/mt_disabled.h:20: [mt_disabled, excludes] unannotatedMtDisabledFunctionBad() [declaration]
t/t_dist_attributes/mt_disabled.cpp:20: [] unannotatedMtDisabledFunctionBad()
t/t_dist_attributes/mt_disabled.h:20: [] unannotatedMtDisabledFunctionBad()
t/t_dist_attributes/mt_disabled.h:23: [] unannotatedMtDisabledFunctionBad()
Number of functions reported unsafe: 229
Number of functions reported unsafe: 230

View File

@ -74,7 +74,7 @@ test.run(
# headers from the `../include` directory.
cmd=[
"python3", aroot + "/nodist/clang_check_attributes", "--verilator-root=.",
"--compile-commands-dir=" + test.obj_dir, srcfiles_str
"--compile-commands-dir=" + test.obj_dir, "--jobs=1", srcfiles_str
])
test.files_identical(test.run_log_filename, test.golden_filename)