mirror of
https://git.FreeBSD.org/src.git
synced 2024-10-18 02:19:39 +00:00
Merge llvm-project main llvmorg-18-init-16595-g7c00a5be5cde
This updates llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp to llvm-project main llvmorg-18-init-16595-g7c00a5be5cde. PR: 276104 MFC after: 1 month
This commit is contained in:
commit
1db9f3b21e
@ -25,6 +25,7 @@
|
||||
#include "clang/Basic/Sanitizers.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/Frontend/HLSL/HLSLResource.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/VersionTuple.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
@ -2044,6 +2044,14 @@ class RequiresExprBodyDecl : public Decl, public DeclContext {
|
||||
// Implement isa/cast/dyncast/etc.
|
||||
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||
static bool classofKind(Kind K) { return K == RequiresExprBody; }
|
||||
|
||||
static DeclContext *castToDeclContext(const RequiresExprBodyDecl *D) {
|
||||
return static_cast<DeclContext *>(const_cast<RequiresExprBodyDecl *>(D));
|
||||
}
|
||||
|
||||
static RequiresExprBodyDecl *castFromDeclContext(const DeclContext *DC) {
|
||||
return static_cast<RequiresExprBodyDecl *>(const_cast<DeclContext *>(DC));
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents a static or instance method of a struct/union/class.
|
||||
|
@ -252,6 +252,8 @@ class TextNodeDumper
|
||||
void VisitGotoStmt(const GotoStmt *Node);
|
||||
void VisitCaseStmt(const CaseStmt *Node);
|
||||
void VisitReturnStmt(const ReturnStmt *Node);
|
||||
void VisitCoawaitExpr(const CoawaitExpr *Node);
|
||||
void VisitCoreturnStmt(const CoreturnStmt *Node);
|
||||
void VisitCompoundStmt(const CompoundStmt *Node);
|
||||
void VisitConstantExpr(const ConstantExpr *Node);
|
||||
void VisitCallExpr(const CallExpr *Node);
|
||||
|
@ -4224,6 +4224,8 @@ class FunctionProtoType final
|
||||
ExceptionSpecInfo() = default;
|
||||
|
||||
ExceptionSpecInfo(ExceptionSpecificationType EST) : Type(EST) {}
|
||||
|
||||
void instantiate();
|
||||
};
|
||||
|
||||
/// Extra information about a function prototype. ExtProtoInfo is not
|
||||
|
@ -66,7 +66,7 @@ class UnsafeBufferUsageHandler {
|
||||
|
||||
/// Invoked when an unsafe operation over raw pointers is found.
|
||||
virtual void handleUnsafeOperation(const Stmt *Operation,
|
||||
bool IsRelatedToDecl) = 0;
|
||||
bool IsRelatedToDecl, ASTContext &Ctx) = 0;
|
||||
|
||||
/// Invoked when a fix is suggested against a variable. This function groups
|
||||
/// all variables that must be fixed together (i.e their types must be changed
|
||||
|
@ -30,6 +30,7 @@ WARNING_GADGET(Decrement)
|
||||
WARNING_GADGET(ArraySubscript)
|
||||
WARNING_GADGET(PointerArithmetic)
|
||||
WARNING_GADGET(UnsafeBufferUsageAttr)
|
||||
WARNING_GADGET(DataInvocation)
|
||||
FIXABLE_GADGET(ULCArraySubscript) // `DRE[any]` in an Unspecified Lvalue Context
|
||||
FIXABLE_GADGET(DerefSimplePtrArithFixable)
|
||||
FIXABLE_GADGET(PointerDereference)
|
||||
|
@ -1215,7 +1215,9 @@ class CFG {
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
class BuildOptions {
|
||||
std::bitset<Stmt::lastStmtConstant> alwaysAddMask;
|
||||
// Stmt::lastStmtConstant has the same value as the last Stmt kind,
|
||||
// so make sure we add one to account for this!
|
||||
std::bitset<Stmt::lastStmtConstant + 1> alwaysAddMask;
|
||||
|
||||
public:
|
||||
using ForcedBlkExprs = llvm::DenseMap<const Stmt *, const CFGBlock *>;
|
||||
|
@ -143,6 +143,11 @@ def ExternalGlobalVar : SubsetSubject<Var,
|
||||
!S->isLocalExternDecl()}],
|
||||
"external global variables">;
|
||||
|
||||
def NonTLSGlobalVar : SubsetSubject<Var,
|
||||
[{S->hasGlobalStorage() &&
|
||||
S->getTLSKind() == 0}],
|
||||
"non-TLS global variables">;
|
||||
|
||||
def InlineFunction : SubsetSubject<Function,
|
||||
[{S->isInlineSpecified()}], "inline functions">;
|
||||
|
||||
@ -431,6 +436,7 @@ def TargetAArch64 : TargetArch<["aarch64", "aarch64_be", "aarch64_32"]>;
|
||||
def TargetAnyArm : TargetArch<!listconcat(TargetARM.Arches, TargetAArch64.Arches)>;
|
||||
def TargetAVR : TargetArch<["avr"]>;
|
||||
def TargetBPF : TargetArch<["bpfel", "bpfeb"]>;
|
||||
def TargetLoongArch : TargetArch<["loongarch32", "loongarch64"]>;
|
||||
def TargetMips32 : TargetArch<["mips", "mipsel"]>;
|
||||
def TargetAnyMips : TargetArch<["mips", "mipsel", "mips64", "mips64el"]>;
|
||||
def TargetMSP430 : TargetArch<["msp430"]>;
|
||||
@ -1121,6 +1127,14 @@ def CoroLifetimeBound : InheritableAttr {
|
||||
let SimpleHandler = 1;
|
||||
}
|
||||
|
||||
def CoroDisableLifetimeBound : InheritableAttr {
|
||||
let Spellings = [Clang<"coro_disable_lifetimebound">];
|
||||
let Subjects = SubjectList<[Function]>;
|
||||
let LangOpts = [CPlusPlus];
|
||||
let Documentation = [CoroLifetimeBoundDoc];
|
||||
let SimpleHandler = 1;
|
||||
}
|
||||
|
||||
// OSObject-based attributes.
|
||||
def OSConsumed : InheritableParamAttr {
|
||||
let Spellings = [Clang<"os_consumed">];
|
||||
@ -2730,6 +2744,15 @@ def PragmaClangTextSection : InheritableAttr {
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def CodeModel : InheritableAttr, TargetSpecificAttr<TargetLoongArch> {
|
||||
let Spellings = [GCC<"model">];
|
||||
let Args = [EnumArgument<"Model", "llvm::CodeModel::Model",
|
||||
["normal", "medium", "extreme"], ["Small", "Medium", "Large"],
|
||||
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>];
|
||||
let Subjects = SubjectList<[NonTLSGlobalVar], ErrorDiag>;
|
||||
let Documentation = [CodeModelDocs];
|
||||
}
|
||||
|
||||
def Sentinel : InheritableAttr {
|
||||
let Spellings = [GCC<"sentinel">];
|
||||
let Args = [DefaultIntArgument<"Sentinel", 0>,
|
||||
|
@ -57,6 +57,15 @@ global variable or function should be in after translation.
|
||||
let Heading = "section, __declspec(allocate)";
|
||||
}
|
||||
|
||||
def CodeModelDocs : Documentation {
|
||||
let Category = DocCatVariable;
|
||||
let Content = [{
|
||||
The ``model`` attribute allows overriding the translation unit's
|
||||
code model (specified by ``-mcmodel``) for a specific global variable.
|
||||
}];
|
||||
let Heading = "model";
|
||||
}
|
||||
|
||||
def UsedDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
@ -7671,9 +7680,12 @@ The ``[[clang::coro_lifetimebound]]`` is a class attribute which can be applied
|
||||
to a coroutine return type (`CRT`_) (i.e.
|
||||
it should also be annotated with ``[[clang::coro_return_type]]``).
|
||||
|
||||
All parameters of a function are considered to be lifetime bound. See `documentation`_
|
||||
of ``[[clang::lifetimebound]]`` for more details.
|
||||
if the function returns a coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``.
|
||||
All parameters of a function are considered to be lifetime bound if the function returns a
|
||||
coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``.
|
||||
This lifetime bound analysis can be disabled for a coroutine wrapper or a coroutine by annotating the function
|
||||
with ``[[clang::coro_disable_lifetimebound]]`` function attribute .
|
||||
See `documentation`_ of ``[[clang::lifetimebound]]`` for details about lifetime bound analysis.
|
||||
|
||||
|
||||
Reference parameters of a coroutine are susceptible to capturing references to temporaries or local variables.
|
||||
|
||||
@ -7703,7 +7715,7 @@ Both coroutines and coroutine wrappers are part of this analysis.
|
||||
};
|
||||
|
||||
Task<int> coro(const int& a) { co_return a + 1; }
|
||||
Task<int> [[clang::coro_wrapper]] coro_wrapper(const int& a, const int& b) {
|
||||
[[clang::coro_wrapper]] Task<int> coro_wrapper(const int& a, const int& b) {
|
||||
return a > b ? coro(a) : coro(b);
|
||||
}
|
||||
Task<int> temporary_reference() {
|
||||
@ -7718,6 +7730,21 @@ Both coroutines and coroutine wrappers are part of this analysis.
|
||||
return coro(a); // warning: returning address of stack variable `a`.
|
||||
}
|
||||
|
||||
This analysis can be disabled for all calls to a particular function by annotating the function
|
||||
with function attribute ``[[clang::coro_disable_lifetimebound]]``.
|
||||
For example, this could be useful for coroutine wrappers which accept reference parameters
|
||||
but do not pass them to the underlying coroutine or pass them by value.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
Task<int> coro(int a) { co_return a + 1; }
|
||||
[[clang::coro_wrapper, clang::coro_disable_lifetimebound]] Task<int> coro_wrapper(const int& a) {
|
||||
return coro(a + 1);
|
||||
}
|
||||
void use() {
|
||||
auto task = coro_wrapper(1); // use of temporary is fine as the argument is not lifetime bound.
|
||||
}
|
||||
|
||||
.. _`documentation`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
|
||||
.. _`CRT`: https://clang.llvm.org/docs/AttributeReference.html#coro-return-type
|
||||
}];
|
||||
|
@ -209,6 +209,7 @@ CODEGENOPT(CoverageMapping , 1, 0) ///< Generate coverage mapping regions to
|
||||
///< enable code coverage analysis.
|
||||
CODEGENOPT(DumpCoverageMapping , 1, 0) ///< Dump the generated coverage mapping
|
||||
///< regions.
|
||||
CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.
|
||||
|
||||
/// If -fpcc-struct-return or -freg-struct-return is specified.
|
||||
ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
|
||||
|
@ -349,6 +349,9 @@ def warn_invalid_feature_combination : Warning<
|
||||
def warn_target_unrecognized_env : Warning<
|
||||
"mismatch between architecture and environment in target triple '%0'; did you mean '%1'?">,
|
||||
InGroup<InvalidCommandLineArgument>;
|
||||
def warn_knl_knm_isa_support_removed : Warning<
|
||||
"KNL, KNM related Intel Xeon Phi CPU's specific ISA's supports will be removed in LLVM 19.">,
|
||||
InGroup<DiagGroup<"knl-knm-isa-support-removed">>;
|
||||
|
||||
// Source manager
|
||||
def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
|
||||
|
@ -786,4 +786,7 @@ def warn_android_unversioned_fallback : Warning<
|
||||
" directories will not be used in Clang 19. Provide a versioned directory"
|
||||
" for the target version or lower instead.">,
|
||||
InGroup<DiagGroup<"android-unversioned-fallback">>;
|
||||
|
||||
def err_drv_triple_version_invalid : Error<
|
||||
"version '%0' in target triple '%1' is invalid">;
|
||||
}
|
||||
|
@ -1364,6 +1364,8 @@ def err_acc_invalid_clause : Error<"invalid OpenACC clause %0">;
|
||||
def err_acc_missing_directive : Error<"expected OpenACC directive">;
|
||||
def err_acc_invalid_open_paren
|
||||
: Error<"expected clause-list or newline in OpenACC directive">;
|
||||
def err_acc_invalid_default_clause_kind
|
||||
: Error<"invalid value for 'default' clause; expected 'present' or 'none'">;
|
||||
|
||||
// OpenMP support.
|
||||
def warn_pragma_omp_ignored : Warning<
|
||||
|
@ -3415,6 +3415,8 @@ def warn_objc_redundant_literal_use : Warning<
|
||||
def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", "
|
||||
"\"local-dynamic\", \"initial-exec\" or \"local-exec\"">;
|
||||
|
||||
def err_attr_codemodel_arg : Error<"code model '%0' is not supported on this target">;
|
||||
|
||||
def err_aix_attr_unsupported_tls_model : Error<"TLS model '%0' is not yet supported on AIX">;
|
||||
|
||||
def err_tls_var_aligned_over_maximum : Error<
|
||||
@ -6158,23 +6160,19 @@ def err_illegal_initializer_type : Error<"illegal initializer type %0">;
|
||||
def ext_init_list_type_narrowing : ExtWarn<
|
||||
"type %0 cannot be narrowed to %1 in initializer list">,
|
||||
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
||||
// *_narrowing_const_reference diagnostics have the same messages, but are
|
||||
// controlled by -Wc++11-narrowing-const-reference for narrowing involving a
|
||||
// const reference.
|
||||
def ext_init_list_type_narrowing_const_reference : ExtWarn<
|
||||
"type %0 cannot be narrowed to %1 in initializer list">,
|
||||
ext_init_list_type_narrowing.Summary>,
|
||||
InGroup<CXX11NarrowingConstReference>, DefaultError, SFINAEFailure;
|
||||
def ext_init_list_variable_narrowing : ExtWarn<
|
||||
"non-constant-expression cannot be narrowed from type %0 to %1 in "
|
||||
"initializer list">, InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
||||
def ext_init_list_variable_narrowing_const_reference : ExtWarn<
|
||||
"non-constant-expression cannot be narrowed from type %0 to %1 in "
|
||||
"initializer list">, InGroup<CXX11NarrowingConstReference>, DefaultError, SFINAEFailure;
|
||||
ext_init_list_variable_narrowing.Summary>, InGroup<CXX11NarrowingConstReference>, DefaultError, SFINAEFailure;
|
||||
def ext_init_list_constant_narrowing : ExtWarn<
|
||||
"constant expression evaluates to %0 which cannot be narrowed to type %1">,
|
||||
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
||||
def ext_init_list_constant_narrowing_const_reference : ExtWarn<
|
||||
"constant expression evaluates to %0 which cannot be narrowed to type %1">,
|
||||
ext_init_list_constant_narrowing.Summary>,
|
||||
InGroup<CXX11NarrowingConstReference>, DefaultError, SFINAEFailure;
|
||||
def warn_init_list_type_narrowing : Warning<
|
||||
"type %0 cannot be narrowed to %1 in initializer list in C++11">,
|
||||
@ -12064,7 +12062,7 @@ def warn_unsafe_buffer_variable : Warning<
|
||||
InGroup<UnsafeBufferUsage>, DefaultIgnore;
|
||||
def warn_unsafe_buffer_operation : Warning<
|
||||
"%select{unsafe pointer operation|unsafe pointer arithmetic|"
|
||||
"unsafe buffer access|function introduces unsafe buffer manipulation}0">,
|
||||
"unsafe buffer access|function introduces unsafe buffer manipulation|unsafe invocation of span::data}0">,
|
||||
InGroup<UnsafeBufferUsage>, DefaultIgnore;
|
||||
def note_unsafe_buffer_operation : Note<
|
||||
"used%select{| in pointer arithmetic| in buffer access}0 here">;
|
||||
|
@ -100,16 +100,24 @@ class ObjCRuntime {
|
||||
bool isLegacyDispatchDefaultForArch(llvm::Triple::ArchType Arch) {
|
||||
// The GNUstep runtime uses a newer dispatch method by default from
|
||||
// version 1.6 onwards
|
||||
if (getKind() == GNUstep && getVersion() >= VersionTuple(1, 6)) {
|
||||
if (Arch == llvm::Triple::arm ||
|
||||
Arch == llvm::Triple::x86 ||
|
||||
Arch == llvm::Triple::x86_64)
|
||||
return false;
|
||||
}
|
||||
else if ((getKind() == MacOSX) && isNonFragile() &&
|
||||
(getVersion() >= VersionTuple(10, 0)) &&
|
||||
(getVersion() < VersionTuple(10, 6)))
|
||||
return Arch != llvm::Triple::x86_64;
|
||||
if (getKind() == GNUstep) {
|
||||
switch (Arch) {
|
||||
case llvm::Triple::arm:
|
||||
case llvm::Triple::x86:
|
||||
case llvm::Triple::x86_64:
|
||||
return !(getVersion() >= VersionTuple(1, 6));
|
||||
case llvm::Triple::aarch64:
|
||||
case llvm::Triple::mips64:
|
||||
return !(getVersion() >= VersionTuple(1, 9));
|
||||
case llvm::Triple::riscv64:
|
||||
return !(getVersion() >= VersionTuple(2, 2));
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
} else if ((getKind() == MacOSX) && isNonFragile() &&
|
||||
(getVersion() >= VersionTuple(10, 0)) &&
|
||||
(getVersion() < VersionTuple(10, 6)))
|
||||
return Arch != llvm::Triple::x86_64;
|
||||
// Except for deployment target of 10.5 or less,
|
||||
// Mac runtimes use legacy dispatch everywhere now.
|
||||
return true;
|
||||
|
@ -72,25 +72,42 @@ enum class OpenACCAtomicKind {
|
||||
|
||||
/// Represents the kind of an OpenACC clause.
|
||||
enum class OpenACCClauseKind {
|
||||
// 'finalize' clause, allowed on 'exit data' directive.
|
||||
/// 'finalize' clause, allowed on 'exit data' directive.
|
||||
Finalize,
|
||||
// 'if_present' clause, allowed on 'host_data' and 'update' directives.
|
||||
/// 'if_present' clause, allowed on 'host_data' and 'update' directives.
|
||||
IfPresent,
|
||||
// 'seq' clause, allowed on 'loop' and 'routine' directives.
|
||||
/// 'seq' clause, allowed on 'loop' and 'routine' directives.
|
||||
Seq,
|
||||
// 'independent' clause, allowed on 'loop' directives.
|
||||
/// 'independent' clause, allowed on 'loop' directives.
|
||||
Independent,
|
||||
// 'auto' clause, allowed on 'loop' directives.
|
||||
/// 'auto' clause, allowed on 'loop' directives.
|
||||
Auto,
|
||||
// 'worker' clause, allowed on 'loop' and 'routine' directives.
|
||||
/// 'worker' clause, allowed on 'loop' and 'routine' directives.
|
||||
Worker,
|
||||
// 'vector' clause, allowed on 'loop' and 'routine' directives. Takes no
|
||||
// arguments for 'routine', so the 'loop' version is not yet implemented
|
||||
// completely.
|
||||
/// 'vector' clause, allowed on 'loop' and 'routine' directives. Takes no
|
||||
/// arguments for 'routine', so the 'loop' version is not yet implemented
|
||||
/// completely.
|
||||
Vector,
|
||||
// 'nohost' clause, allowed on 'routine' directives.
|
||||
/// 'nohost' clause, allowed on 'routine' directives.
|
||||
NoHost,
|
||||
// Represents an invalid clause, for the purposes of parsing.
|
||||
/// 'default' clause, allowed on parallel, serial, kernel (and compound)
|
||||
/// constructs.
|
||||
Default,
|
||||
/// 'if' clause, allowed on all the Compute Constructs, Data Constructs,
|
||||
/// Executable Constructs, and Combined Constructs.
|
||||
If,
|
||||
/// 'self' clause, allowed on Compute and Combined Constructs, plus 'update'.
|
||||
Self,
|
||||
/// Represents an invalid clause, for the purposes of parsing.
|
||||
Invalid,
|
||||
};
|
||||
|
||||
enum class OpenACCDefaultClauseKind {
|
||||
/// 'none' option.
|
||||
None,
|
||||
/// 'present' option.
|
||||
Present,
|
||||
/// Not a valid option.
|
||||
Invalid,
|
||||
};
|
||||
} // namespace clang
|
||||
|
@ -1976,39 +1976,37 @@ def SVFMINQV: SInst<"svminqv[_{d}]", "{Pd", "hfd", MergeNone, "aarch64_sve_fminq
|
||||
}
|
||||
|
||||
let TargetGuard = "sve2p1|sme2" in {
|
||||
//FIXME: Replace IsStreamingCompatible with IsStreamingOrHasSVE2p1 when available
|
||||
def SVPEXT_SINGLE : SInst<"svpext_lane_{d}", "P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext", [IsStreamingCompatible], [ImmCheck<1, ImmCheck0_3>]>;
|
||||
def SVPEXT_X2 : SInst<"svpext_lane_{d}_x2", "2.P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext_x2", [IsStreamingCompatible], [ImmCheck<1, ImmCheck0_1>]>;
|
||||
def SVPEXT_SINGLE : SInst<"svpext_lane_{d}", "P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext", [IsStreamingOrSVE2p1], [ImmCheck<1, ImmCheck0_3>]>;
|
||||
def SVPEXT_X2 : SInst<"svpext_lane_{d}_x2", "2.P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext_x2", [IsStreamingOrSVE2p1], [ImmCheck<1, ImmCheck0_1>]>;
|
||||
|
||||
def SVWHILEGE_COUNT : SInst<"svwhilege_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilege_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEGT_COUNT : SInst<"svwhilegt_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilegt_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELE_COUNT : SInst<"svwhilele_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilele_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELT_COUNT : SInst<"svwhilelt_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilelt_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELO_COUNT : SInst<"svwhilelt_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilelo_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELS_COUNT : SInst<"svwhilele_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilels_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEHI_COUNT : SInst<"svwhilegt_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilehi_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEHS_COUNT : SInst<"svwhilege_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilehs_{d}", [IsOverloadNone], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEGE_COUNT : SInst<"svwhilege_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilege_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEGT_COUNT : SInst<"svwhilegt_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilegt_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELE_COUNT : SInst<"svwhilele_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilele_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELT_COUNT : SInst<"svwhilelt_{d}[_{1}]", "}lli", "QcQsQiQl", MergeNone, "aarch64_sve_whilelt_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELO_COUNT : SInst<"svwhilelt_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilelo_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILELS_COUNT : SInst<"svwhilele_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilels_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEHI_COUNT : SInst<"svwhilegt_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilehi_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
def SVWHILEHS_COUNT : SInst<"svwhilege_{d}[_{1}]", "}nni", "QcQsQiQl", MergeNone, "aarch64_sve_whilehs_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<2, ImmCheck2_4_Mul2>]>;
|
||||
}
|
||||
|
||||
multiclass MultiVecLoad<string i> {
|
||||
// FIXME: Replace IsStreamingCompatible with IsStreamingOrHasSVE2p1 when available (SME2 requires __arm_streaming)
|
||||
def SV # NAME # B_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "cUc", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "sUshb", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "iUif", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "lUld", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "cUc", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "sUshb", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "iUif", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "lUld", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # B_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "cUc", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "sUshb", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "iUif", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_X2 : MInst<"sv" # i # "[_{2}]_x2", "2}c", "lUld", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "cUc", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "sUshb", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "iUif", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_X4 : MInst<"sv" # i # "[_{2}]_x4", "4}c", "lUld", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
|
||||
def SV # NAME # B_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "cUc", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "sUshb", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "iUif", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "lUld", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "cUc", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "sUshb", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "iUif", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "lUld", [IsStructLoad, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # B_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "cUc", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "sUshb", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "iUif", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}]_x2", "2}cl", "lUld", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "cUc", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "sUshb", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "iUif", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "lUld", [IsStructLoad, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
}
|
||||
|
||||
let TargetGuard = "sve2p1|sme2" in {
|
||||
@ -2017,24 +2015,23 @@ let TargetGuard = "sve2p1|sme2" in {
|
||||
}
|
||||
|
||||
multiclass MultiVecStore<string i> {
|
||||
// FIXME: Replace IsStreamingCompatible with IsStreamingOrHasSVE2p1 when available (SME2 requires __arm_streaming)
|
||||
def SV # NAME # B_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "cUc", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "sUshb", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "iUif", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "lUld", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "cUc", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "sUshb", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "iUif", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "lUld", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # B_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "cUc", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "sUshb", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "iUif", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_X2 : MInst<"sv" # i # "[_{2}_x2]", "v}p2", "lUld", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "cUc", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "sUshb", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "iUif", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_X4 : MInst<"sv" # i # "[_{2}_x4]", "v}p4", "lUld", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
|
||||
def SV # NAME # B_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "cUc", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "sUshb", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "iUif", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "lUld", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "cUc", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "sUshb", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "iUif", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "lUld", [IsStructStore, IsStreamingCompatible], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # B_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "cUc", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # H_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "sUshb", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # W_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "iUif", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # D_VNUM_X2 : MInst<"sv" # i # "_vnum" # "[_{2}_x2]", "v}pl2", "lUld", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x2">;
|
||||
def SV # NAME # B_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "cUc", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # H_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "sUshb", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # W_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "iUif", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
def SV # NAME # D_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "lUld", [IsStructStore, IsStreamingOrSVE2p1], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">;
|
||||
}
|
||||
|
||||
let TargetGuard = "sve2p1|sme2" in {
|
||||
@ -2051,21 +2048,20 @@ def SVDOT_LANE_X2_U : SInst<"svdot_lane[_{d}_{2}_{3}]", "ddhhi", "Ui", MergeNone
|
||||
def SVDOT_LANE_X2_F : SInst<"svdot_lane[_{d}_{2}_{3}]", "ddhhi", "f", MergeNone, "aarch64_sve_fdot_lane_x2", [], [ImmCheck<3, ImmCheck0_3>]>;
|
||||
}
|
||||
|
||||
let TargetGuard = "sve2p1|sme" in {
|
||||
def SVSCLAMP : SInst<"svclamp[_{d}]", "dddd", "csil", MergeNone, "aarch64_sve_sclamp", [], []>;
|
||||
def SVUCLAMP : SInst<"svclamp[_{d}]", "dddd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp", [], []>;
|
||||
let TargetGuard = "sve2p1|sme2" in {
|
||||
def SVSCLAMP : SInst<"svclamp[_{d}]", "dddd", "csil", MergeNone, "aarch64_sve_sclamp", [IsStreamingOrSVE2p1], []>;
|
||||
def SVUCLAMP : SInst<"svclamp[_{d}]", "dddd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp", [IsStreamingOrSVE2p1], []>;
|
||||
|
||||
defm SVREVD : SInstZPZ<"svrevd", "csilUcUsUiUlbhfd", "aarch64_sve_revd">;
|
||||
}
|
||||
|
||||
let TargetGuard = "sve2p1|sme2" in {
|
||||
//FIXME: Replace IsStreamingCompatible with IsStreamingOrHasSVE2p1 when available
|
||||
def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone, IsStreamingCompatible], []>;
|
||||
def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], []>;
|
||||
|
||||
def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone, IsStreamingCompatible]>;
|
||||
def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone, IsStreamingOrSVE2p1]>;
|
||||
|
||||
def SVFCLAMP : SInst<"svclamp[_{d}]", "dddd", "hfd", MergeNone, "aarch64_sve_fclamp", [IsStreamingCompatible], []>;
|
||||
def SVCNTP_COUNT : SInst<"svcntp_{d}", "n}i", "QcQsQiQl", MergeNone, "aarch64_sve_cntp_{d}", [IsOverloadNone, IsStreamingCompatible], [ImmCheck<1, ImmCheck2_4_Mul2>]>;
|
||||
def SVFCLAMP : SInst<"svclamp[_{d}]", "dddd", "hfd", MergeNone, "aarch64_sve_fclamp", [IsStreamingOrSVE2p1], []>;
|
||||
def SVCNTP_COUNT : SInst<"svcntp_{d}", "n}i", "QcQsQiQl", MergeNone, "aarch64_sve_cntp_{d}", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<1, ImmCheck2_4_Mul2>]>;
|
||||
}
|
||||
|
||||
let TargetGuard = "(sve2|sme2),b16b16" in {
|
||||
@ -2326,10 +2322,9 @@ let TargetGuard = "sme2" in {
|
||||
|
||||
let TargetGuard = "sve2p1|sme2" in {
|
||||
// == BFloat16 multiply-subtract ==
|
||||
// FIXME: Make all of these IsStreamingOrSVE2p1 once that is added
|
||||
def SVBFMLSLB : SInst<"svbfmlslb[_{d}]", "dd$$", "f", MergeNone, "aarch64_sve_bfmlslb", [IsOverloadNone, IsStreamingCompatible], []>;
|
||||
def SVBFMLSLT : SInst<"svbfmlslt[_{d}]", "dd$$", "f", MergeNone, "aarch64_sve_bfmlslt", [IsOverloadNone, IsStreamingCompatible], []>;
|
||||
def SVBFMLSLB : SInst<"svbfmlslb[_{d}]", "dd$$", "f", MergeNone, "aarch64_sve_bfmlslb", [IsOverloadNone, IsStreamingOrSVE2p1], []>;
|
||||
def SVBFMLSLT : SInst<"svbfmlslt[_{d}]", "dd$$", "f", MergeNone, "aarch64_sve_bfmlslt", [IsOverloadNone, IsStreamingOrSVE2p1], []>;
|
||||
|
||||
def SVBFMLSLB_LANE : SInst<"svbfmlslb_lane[_{d}]", "dd$$i", "f", MergeNone, "aarch64_sve_bfmlslb_lane", [IsOverloadNone, IsStreamingCompatible], [ImmCheck<3, ImmCheck0_7>]>;
|
||||
def SVBFMLSLT_LANE : SInst<"svbfmlslt_lane[_{d}]", "dd$$i", "f", MergeNone, "aarch64_sve_bfmlslt_lane", [IsOverloadNone, IsStreamingCompatible], [ImmCheck<3, ImmCheck0_7>]>;
|
||||
def SVBFMLSLB_LANE : SInst<"svbfmlslb_lane[_{d}]", "dd$$i", "f", MergeNone, "aarch64_sve_bfmlslb_lane", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<3, ImmCheck0_7>]>;
|
||||
def SVBFMLSLT_LANE : SInst<"svbfmlslt_lane[_{d}]", "dd$$i", "f", MergeNone, "aarch64_sve_bfmlslt_lane", [IsOverloadNone, IsStreamingOrSVE2p1], [ImmCheck<3, ImmCheck0_7>]>;
|
||||
}
|
||||
|
@ -227,6 +227,7 @@ def IsPreservesZA : FlagType<0x10000000000>;
|
||||
def IsReadZA : FlagType<0x20000000000>;
|
||||
def IsWriteZA : FlagType<0x40000000000>;
|
||||
def IsReductionQV : FlagType<0x80000000000>;
|
||||
def IsStreamingOrSVE2p1 : FlagType<0x80000000000>; // Use for intrinsics that are common between sme/sme2 and sve2p1.
|
||||
|
||||
// These must be kept in sync with the flags in include/clang/Basic/TargetBuiltins.h
|
||||
class ImmCheckType<int val> {
|
||||
|
@ -121,11 +121,11 @@ multiclass RVVVQMACCDODBuiltinSet<list<list<string>> suffixes_prototypes> {
|
||||
}
|
||||
|
||||
multiclass RVVVQMACCQOQBuiltinSet<list<list<string>> suffixes_prototypes> {
|
||||
let OverloadedName = NAME,
|
||||
Name = NAME,
|
||||
HasMasked = false,
|
||||
Log2LMUL = [-1, 0, 1, 2] in
|
||||
defm NAME : RVVOutOp1Op2BuiltinSet<NAME, "s", suffixes_prototypes>;
|
||||
let OverloadedName = NAME,
|
||||
Name = NAME,
|
||||
HasMasked = false,
|
||||
Log2LMUL = [-1, 0, 1, 2] in
|
||||
defm NAME : RVVOutOp1Op2BuiltinSet<NAME, "s", suffixes_prototypes>;
|
||||
}
|
||||
|
||||
multiclass RVVVFNRCLIPBuiltinSet<string suffix, string prototype, string type_range> {
|
||||
|
@ -773,6 +773,8 @@ def gcc_install_dir_EQ : Joined<["--"], "gcc-install-dir=">,
|
||||
def gcc_toolchain : Joined<["--"], "gcc-toolchain=">, Flags<[NoXarchOption]>,
|
||||
HelpText<"Specify a directory where Clang can find 'include' and 'lib{,32,64}/gcc{,-cross}/$triple/$version'. "
|
||||
"Clang will use the GCC installation with the largest version">;
|
||||
def gcc_triple_EQ : Joined<["--"], "gcc-triple=">,
|
||||
HelpText<"Search for the GCC installation with the specified triple.">;
|
||||
def CC : Flag<["-"], "CC">, Visibility<[ClangOption, CC1Option]>,
|
||||
Group<Preprocessor_Group>,
|
||||
HelpText<"Include comments from within macros in preprocessed output">,
|
||||
@ -1695,6 +1697,12 @@ defm coverage_mapping : BoolFOption<"coverage-mapping",
|
||||
"Generate coverage mapping to enable code coverage analysis">,
|
||||
NegFlag<SetFalse, [], [ClangOption], "Disable code coverage analysis">, BothFlags<
|
||||
[], [ClangOption, CLOption]>>;
|
||||
defm mcdc_coverage : BoolFOption<"coverage-mcdc",
|
||||
CodeGenOpts<"MCDCCoverage">, DefaultFalse,
|
||||
PosFlag<SetTrue, [], [ClangOption, CC1Option],
|
||||
"Enable MC/DC criteria when generating code coverage">,
|
||||
NegFlag<SetFalse, [], [ClangOption], "Disable MC/DC coverage criteria">,
|
||||
BothFlags<[], [ClangOption, CLOption]>>;
|
||||
def fprofile_generate : Flag<["-"], "fprofile-generate">,
|
||||
Group<f_Group>, Visibility<[ClangOption, CLOption]>,
|
||||
HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">;
|
||||
@ -5192,9 +5200,9 @@ def nogpulib : Flag<["-"], "nogpulib">, MarshallingInfoFlag<LangOpts<"NoGPULib">
|
||||
Visibility<[ClangOption, CC1Option]>,
|
||||
HelpText<"Do not link device library for CUDA/HIP device compilation">;
|
||||
def : Flag<["-"], "nocudalib">, Alias<nogpulib>;
|
||||
def gpulibc : Flag<["-"], "gpulibc">, Visibility<[ClangOption, CC1Option]>,
|
||||
def gpulibc : Flag<["-"], "gpulibc">, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
|
||||
HelpText<"Link the LLVM C Library for GPUs">;
|
||||
def nogpulibc : Flag<["-"], "nogpulibc">, Visibility<[ClangOption, CC1Option]>;
|
||||
def nogpulibc : Flag<["-"], "nogpulibc">, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>;
|
||||
def nodefaultlibs : Flag<["-"], "nodefaultlibs">;
|
||||
def nodriverkitlib : Flag<["-"], "nodriverkitlib">;
|
||||
def nofixprebinding : Flag<["-"], "nofixprebinding">;
|
||||
|
@ -10263,11 +10263,13 @@ class Sema final {
|
||||
~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); }
|
||||
};
|
||||
|
||||
// Unlike the above, this evaluates constraints, which should only happen at
|
||||
// 'constraint checking' time.
|
||||
// Must be used instead of SubstExpr at 'constraint checking' time.
|
||||
ExprResult
|
||||
SubstConstraintExpr(Expr *E,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
// Unlike the above, this does not evaluates constraints.
|
||||
ExprResult SubstConstraintExprWithoutSatisfaction(
|
||||
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
|
||||
/// Substitute the given template arguments into a list of
|
||||
/// expressions, expanding pack expansions if required.
|
||||
@ -11344,9 +11346,12 @@ class Sema final {
|
||||
/// rigorous semantic checking in the new mapped directives.
|
||||
bool mapLoopConstruct(llvm::SmallVector<OMPClause *> &ClausesWithoutBind,
|
||||
ArrayRef<OMPClause *> Clauses,
|
||||
OpenMPBindClauseKind BindKind,
|
||||
OpenMPBindClauseKind &BindKind,
|
||||
OpenMPDirectiveKind &Kind,
|
||||
OpenMPDirectiveKind &PrevMappedDirective);
|
||||
OpenMPDirectiveKind &PrevMappedDirective,
|
||||
SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
const DeclarationNameInfo &DirName,
|
||||
OpenMPDirectiveKind CancelRegion);
|
||||
|
||||
public:
|
||||
/// The declarator \p D defines a function in the scope \p S which is nested
|
||||
@ -12967,7 +12972,7 @@ class Sema final {
|
||||
QualType FindCompositeObjCPointerType(ExprResult &LHS, ExprResult &RHS,
|
||||
SourceLocation QuestionLoc);
|
||||
|
||||
bool DiagnoseConditionalForNull(Expr *LHSExpr, Expr *RHSExpr,
|
||||
bool DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr,
|
||||
SourceLocation QuestionLoc);
|
||||
|
||||
void DiagnoseAlwaysNonNullPointer(Expr *E,
|
||||
|
@ -564,6 +564,7 @@ enum class TemplateSubstitutionKind : char {
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs;
|
||||
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
|
||||
LocalInstantiationScope *StartingScope = nullptr;
|
||||
// Whether to evaluate the C++20 constraints or simply substitute into them.
|
||||
bool EvaluateConstraints = true;
|
||||
|
||||
/// A list of out-of-line class template partial
|
||||
|
@ -2748,21 +2748,20 @@ bool ASTContext::hasUniqueObjectRepresentations(
|
||||
QualType Ty, bool CheckIfTriviallyCopyable) const {
|
||||
// C++17 [meta.unary.prop]:
|
||||
// The predicate condition for a template specialization
|
||||
// has_unique_object_representations<T> shall be
|
||||
// satisfied if and only if:
|
||||
// has_unique_object_representations<T> shall be satisfied if and only if:
|
||||
// (9.1) - T is trivially copyable, and
|
||||
// (9.2) - any two objects of type T with the same value have the same
|
||||
// object representation, where two objects
|
||||
// of array or non-union class type are considered to have the same value
|
||||
// if their respective sequences of
|
||||
// direct subobjects have the same values, and two objects of union type
|
||||
// are considered to have the same
|
||||
// value if they have the same active member and the corresponding members
|
||||
// have the same value.
|
||||
// object representation, where:
|
||||
// - two objects of array or non-union class type are considered to have
|
||||
// the same value if their respective sequences of direct subobjects
|
||||
// have the same values, and
|
||||
// - two objects of union type are considered to have the same value if
|
||||
// they have the same active member and the corresponding members have
|
||||
// the same value.
|
||||
// The set of scalar types for which this condition holds is
|
||||
// implementation-defined. [ Note: If a type has padding
|
||||
// bits, the condition does not hold; otherwise, the condition holds true
|
||||
// for unsigned integral types. -- end note ]
|
||||
// implementation-defined. [ Note: If a type has padding bits, the condition
|
||||
// does not hold; otherwise, the condition holds true for unsigned integral
|
||||
// types. -- end note ]
|
||||
assert(!Ty.isNull() && "Null QualType sent to unique object rep check");
|
||||
|
||||
// Arrays are unique only if their element type is unique.
|
||||
|
@ -2034,23 +2034,25 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
|
||||
return ToDCOrErr.takeError();
|
||||
}
|
||||
|
||||
DeclContext *ToDC = *ToDCOrErr;
|
||||
// Remove all declarations, which may be in wrong order in the
|
||||
// lexical DeclContext and then add them in the proper order.
|
||||
for (auto *D : FromDC->decls()) {
|
||||
if (!MightNeedReordering(D))
|
||||
continue;
|
||||
if (const auto *FromRD = dyn_cast<RecordDecl>(FromDC)) {
|
||||
DeclContext *ToDC = *ToDCOrErr;
|
||||
// Remove all declarations, which may be in wrong order in the
|
||||
// lexical DeclContext and then add them in the proper order.
|
||||
for (auto *D : FromRD->decls()) {
|
||||
if (!MightNeedReordering(D))
|
||||
continue;
|
||||
|
||||
assert(D && "DC contains a null decl");
|
||||
if (Decl *ToD = Importer.GetAlreadyImportedOrNull(D)) {
|
||||
// Remove only the decls which we successfully imported.
|
||||
assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
|
||||
// Remove the decl from its wrong place in the linked list.
|
||||
ToDC->removeDecl(ToD);
|
||||
// Add the decl to the end of the linked list.
|
||||
// This time it will be at the proper place because the enclosing for
|
||||
// loop iterates in the original (good) order of the decls.
|
||||
ToDC->addDeclInternal(ToD);
|
||||
assert(D && "DC contains a null decl");
|
||||
if (Decl *ToD = Importer.GetAlreadyImportedOrNull(D)) {
|
||||
// Remove only the decls which we successfully imported.
|
||||
assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
|
||||
// Remove the decl from its wrong place in the linked list.
|
||||
ToDC->removeDecl(ToD);
|
||||
// Add the decl to the end of the linked list.
|
||||
// This time it will be at the proper place because the enclosing for
|
||||
// loop iterates in the original (good) order of the decls.
|
||||
ToDC->addDeclInternal(ToD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6141,6 +6143,11 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl(
|
||||
InsertPos))
|
||||
// Add this partial specialization to the class template.
|
||||
ClassTemplate->AddPartialSpecialization(PartSpec2, InsertPos);
|
||||
if (Expected<ClassTemplatePartialSpecializationDecl *> ToInstOrErr =
|
||||
import(PartialSpec->getInstantiatedFromMember()))
|
||||
PartSpec2->setInstantiatedFromMember(*ToInstOrErr);
|
||||
else
|
||||
return ToInstOrErr.takeError();
|
||||
|
||||
updateLookupTableForTemplateParameters(*ToTPList);
|
||||
} else { // Not a partial specialization.
|
||||
|
@ -1463,8 +1463,9 @@ IsStructurallyEquivalentLambdas(StructuralEquivalenceContext &Context,
|
||||
}
|
||||
|
||||
/// Determine if context of a class is equivalent.
|
||||
static bool IsRecordContextStructurallyEquivalent(RecordDecl *D1,
|
||||
RecordDecl *D2) {
|
||||
static bool
|
||||
IsRecordContextStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
||||
RecordDecl *D1, RecordDecl *D2) {
|
||||
// The context should be completely equal, including anonymous and inline
|
||||
// namespaces.
|
||||
// We compare objects as part of full translation units, not subtrees of
|
||||
@ -1491,6 +1492,12 @@ static bool IsRecordContextStructurallyEquivalent(RecordDecl *D1,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto *D1Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC1)) {
|
||||
auto *D2Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC2);
|
||||
if (!IsStructurallyEquivalent(Context, D1Spec, D2Spec))
|
||||
return false;
|
||||
}
|
||||
|
||||
DC1 = DC1->getParent()->getNonTransparentContext();
|
||||
DC2 = DC2->getParent()->getNonTransparentContext();
|
||||
}
|
||||
@ -1544,7 +1551,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
||||
// If the records occur in different context (namespace), these should be
|
||||
// different. This is specially important if the definition of one or both
|
||||
// records is missing.
|
||||
if (!IsRecordContextStructurallyEquivalent(D1, D2))
|
||||
if (!IsRecordContextStructurallyEquivalent(Context, D1, D2))
|
||||
return false;
|
||||
|
||||
// If both declarations are class template specializations, we know
|
||||
|
@ -603,6 +603,8 @@ ExprDependence clang::computeDependence(PredefinedExpr *E) {
|
||||
ExprDependence clang::computeDependence(CallExpr *E,
|
||||
llvm::ArrayRef<Expr *> PreArgs) {
|
||||
auto D = E->getCallee()->getDependence();
|
||||
if (E->getType()->isDependentType())
|
||||
D |= ExprDependence::Type;
|
||||
for (auto *A : llvm::ArrayRef(E->getArgs(), E->getNumArgs())) {
|
||||
if (A)
|
||||
D |= A->getDependence();
|
||||
|
@ -2835,7 +2835,7 @@ CharUnits VarDecl::getFlexibleArrayInitChars(const ASTContext &Ctx) const {
|
||||
if (!Ty || !Ty->getDecl()->hasFlexibleArrayMember())
|
||||
return CharUnits::Zero();
|
||||
auto *List = dyn_cast<InitListExpr>(getInit()->IgnoreParens());
|
||||
if (!List)
|
||||
if (!List || List->getNumInits() == 0)
|
||||
return CharUnits::Zero();
|
||||
const Expr *FlexibleInit = List->getInit(List->getNumInits() - 1);
|
||||
auto InitTy = Ctx.getAsConstantArrayType(FlexibleInit->getType());
|
||||
|
@ -930,20 +930,14 @@ const AttrVec &Decl::getAttrs() const {
|
||||
|
||||
Decl *Decl::castFromDeclContext (const DeclContext *D) {
|
||||
Decl::Kind DK = D->getDeclKind();
|
||||
switch(DK) {
|
||||
switch (DK) {
|
||||
#define DECL(NAME, BASE)
|
||||
#define DECL_CONTEXT(NAME) \
|
||||
case Decl::NAME: \
|
||||
return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D));
|
||||
#define DECL_CONTEXT_BASE(NAME)
|
||||
#define DECL_CONTEXT(NAME) \
|
||||
case Decl::NAME: \
|
||||
return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D));
|
||||
#include "clang/AST/DeclNodes.inc"
|
||||
default:
|
||||
#define DECL(NAME, BASE)
|
||||
#define DECL_CONTEXT_BASE(NAME) \
|
||||
if (DK >= first##NAME && DK <= last##NAME) \
|
||||
return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D));
|
||||
#include "clang/AST/DeclNodes.inc"
|
||||
llvm_unreachable("a decl that inherits DeclContext isn't handled");
|
||||
default:
|
||||
llvm_unreachable("a decl that inherits DeclContext isn't handled");
|
||||
}
|
||||
}
|
||||
|
||||
@ -951,18 +945,12 @@ DeclContext *Decl::castToDeclContext(const Decl *D) {
|
||||
Decl::Kind DK = D->getKind();
|
||||
switch(DK) {
|
||||
#define DECL(NAME, BASE)
|
||||
#define DECL_CONTEXT(NAME) \
|
||||
case Decl::NAME: \
|
||||
return static_cast<NAME##Decl *>(const_cast<Decl *>(D));
|
||||
#define DECL_CONTEXT_BASE(NAME)
|
||||
#define DECL_CONTEXT(NAME) \
|
||||
case Decl::NAME: \
|
||||
return static_cast<NAME##Decl *>(const_cast<Decl *>(D));
|
||||
#include "clang/AST/DeclNodes.inc"
|
||||
default:
|
||||
#define DECL(NAME, BASE)
|
||||
#define DECL_CONTEXT_BASE(NAME) \
|
||||
if (DK >= first##NAME && DK <= last##NAME) \
|
||||
return static_cast<NAME##Decl *>(const_cast<Decl *>(D));
|
||||
#include "clang/AST/DeclNodes.inc"
|
||||
llvm_unreachable("a decl that inherits DeclContext isn't handled");
|
||||
default:
|
||||
llvm_unreachable("a decl that inherits DeclContext isn't handled");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1129,20 +1117,14 @@ DeclContext::DeclContext(Decl::Kind K) {
|
||||
}
|
||||
|
||||
bool DeclContext::classof(const Decl *D) {
|
||||
switch (D->getKind()) {
|
||||
Decl::Kind DK = D->getKind();
|
||||
switch (DK) {
|
||||
#define DECL(NAME, BASE)
|
||||
#define DECL_CONTEXT(NAME) case Decl::NAME:
|
||||
#define DECL_CONTEXT_BASE(NAME)
|
||||
#include "clang/AST/DeclNodes.inc"
|
||||
return true;
|
||||
default:
|
||||
#define DECL(NAME, BASE)
|
||||
#define DECL_CONTEXT_BASE(NAME) \
|
||||
if (D->getKind() >= Decl::first##NAME && \
|
||||
D->getKind() <= Decl::last##NAME) \
|
||||
return true;
|
||||
#include "clang/AST/DeclNodes.inc"
|
||||
return false;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,7 +488,6 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
|
||||
return NoMatchPromotionTypeConfusion;
|
||||
break;
|
||||
case BuiltinType::Half:
|
||||
case BuiltinType::Float16:
|
||||
case BuiltinType::Float:
|
||||
if (T == C.DoubleTy)
|
||||
return MatchPromotion;
|
||||
|
@ -290,10 +290,10 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
||||
}
|
||||
|
||||
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
|
||||
if (!CheckDummy(S, OpPC, Ptr))
|
||||
return false;
|
||||
if (!CheckLive(S, OpPC, Ptr, AK_Read))
|
||||
return false;
|
||||
if (!CheckDummy(S, OpPC, Ptr))
|
||||
return false;
|
||||
if (!CheckExtern(S, OpPC, Ptr))
|
||||
return false;
|
||||
if (!CheckRange(S, OpPC, Ptr, AK_Read))
|
||||
|
@ -1813,9 +1813,6 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
|
||||
const T &Offset = S.Stk.pop<T>();
|
||||
const Pointer &Ptr = S.Stk.peek<Pointer>();
|
||||
|
||||
if (!CheckArray(S, OpPC, Ptr))
|
||||
return false;
|
||||
|
||||
if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
|
||||
return false;
|
||||
|
||||
@ -1843,9 +1840,6 @@ inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
|
||||
const T &Offset = S.Stk.pop<T>();
|
||||
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
||||
|
||||
if (!CheckArray(S, OpPC, Ptr))
|
||||
return false;
|
||||
|
||||
if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
|
||||
return false;
|
||||
|
||||
|
@ -1094,6 +1094,16 @@ void clang::TextNodeDumper::VisitReturnStmt(const ReturnStmt *Node) {
|
||||
}
|
||||
}
|
||||
|
||||
void clang::TextNodeDumper::VisitCoawaitExpr(const CoawaitExpr *Node) {
|
||||
if (Node->isImplicit())
|
||||
OS << " implicit";
|
||||
}
|
||||
|
||||
void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) {
|
||||
if (Node->isImplicit())
|
||||
OS << " implicit";
|
||||
}
|
||||
|
||||
void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
|
||||
if (Node->hasAPValueResult())
|
||||
AddChild("value",
|
||||
|
@ -3414,6 +3414,13 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
|
||||
llvm_unreachable("Invalid calling convention.");
|
||||
}
|
||||
|
||||
void FunctionProtoType::ExceptionSpecInfo::instantiate() {
|
||||
assert(Type == EST_Uninstantiated);
|
||||
NoexceptExpr =
|
||||
cast<FunctionProtoType>(SourceTemplate->getType())->getNoexceptExpr();
|
||||
Type = EST_DependentNoexcept;
|
||||
}
|
||||
|
||||
FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
|
||||
QualType canonical,
|
||||
const ExtProtoInfo &epi)
|
||||
|
@ -50,12 +50,7 @@
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
|
||||
static StringRef StripTrailingDots(StringRef s) {
|
||||
for (StringRef::size_type i = s.size(); i != 0; --i)
|
||||
if (s[i - 1] != '.')
|
||||
return s.substr(0, i);
|
||||
return {};
|
||||
}
|
||||
static StringRef StripTrailingDots(StringRef s) { return s.rtrim('.'); }
|
||||
|
||||
PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
|
||||
Kind k, DisplayHint hint)
|
||||
|
@ -721,6 +721,34 @@ class UnsafeBufferUsageAttrGadget : public WarningGadget {
|
||||
DeclUseList getClaimedVarUseSites() const override { return {}; }
|
||||
};
|
||||
|
||||
// Warning gadget for unsafe invocation of span::data method.
|
||||
// Triggers when the pointer returned by the invocation is immediately
|
||||
// cast to a larger type.
|
||||
|
||||
class DataInvocationGadget : public WarningGadget {
|
||||
constexpr static const char *const OpTag = "data_invocation_expr";
|
||||
const ExplicitCastExpr *Op;
|
||||
|
||||
public:
|
||||
DataInvocationGadget(const MatchFinder::MatchResult &Result)
|
||||
: WarningGadget(Kind::DataInvocation),
|
||||
Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
|
||||
|
||||
static bool classof(const Gadget *G) {
|
||||
return G->getKind() == Kind::DataInvocation;
|
||||
}
|
||||
|
||||
static Matcher matcher() {
|
||||
return stmt(
|
||||
explicitCastExpr(has(cxxMemberCallExpr(callee(cxxMethodDecl(
|
||||
hasName("data"), ofClass(hasName("std::span")))))))
|
||||
.bind(OpTag));
|
||||
}
|
||||
const Stmt *getBaseStmt() const override { return Op; }
|
||||
|
||||
DeclUseList getClaimedVarUseSites() const override { return {}; }
|
||||
};
|
||||
|
||||
// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
|
||||
// Context (see `isInUnspecifiedLvalueContext`).
|
||||
// Note here `[]` is the built-in subscript operator.
|
||||
@ -2657,8 +2685,8 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
|
||||
// every problematic operation and consider it done. No need to deal
|
||||
// with fixable gadgets, no need to group operations by variable.
|
||||
for (const auto &G : WarningGadgets) {
|
||||
Handler.handleUnsafeOperation(G->getBaseStmt(),
|
||||
/*IsRelatedToDecl=*/false);
|
||||
Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false,
|
||||
D->getASTContext());
|
||||
}
|
||||
|
||||
// This return guarantees that most of the machine doesn't run when
|
||||
@ -2893,7 +2921,8 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
|
||||
Tracker, Handler, VarGrpMgr);
|
||||
|
||||
for (const auto &G : UnsafeOps.noVar) {
|
||||
Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false);
|
||||
Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false,
|
||||
D->getASTContext());
|
||||
}
|
||||
|
||||
for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
|
||||
@ -2904,7 +2933,8 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
|
||||
: FixItList{},
|
||||
D);
|
||||
for (const auto &G : WarningGadgets) {
|
||||
Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true);
|
||||
Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true,
|
||||
D->getASTContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/TargetParser/ARMTargetParser.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::targets;
|
||||
@ -837,6 +838,69 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts,
|
||||
if (Opts.RWPI)
|
||||
Builder.defineMacro("__ARM_RWPI", "1");
|
||||
|
||||
// Macros for enabling co-proc intrinsics
|
||||
uint64_t FeatureCoprocBF = 0;
|
||||
switch (ArchKind) {
|
||||
default:
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV4:
|
||||
case llvm::ARM::ArchKind::ARMV4T:
|
||||
// Filter __arm_ldcl and __arm_stcl in acle.h
|
||||
FeatureCoprocBF = isThumb() ? 0 : FEATURE_COPROC_B1;
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV5T:
|
||||
FeatureCoprocBF = isThumb() ? 0 : FEATURE_COPROC_B1 | FEATURE_COPROC_B2;
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV5TE:
|
||||
case llvm::ARM::ArchKind::ARMV5TEJ:
|
||||
if (!isThumb())
|
||||
FeatureCoprocBF =
|
||||
FEATURE_COPROC_B1 | FEATURE_COPROC_B2 | FEATURE_COPROC_B3;
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV6:
|
||||
case llvm::ARM::ArchKind::ARMV6K:
|
||||
case llvm::ARM::ArchKind::ARMV6KZ:
|
||||
case llvm::ARM::ArchKind::ARMV6T2:
|
||||
if (!isThumb() || ArchKind == llvm::ARM::ArchKind::ARMV6T2)
|
||||
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B2 |
|
||||
FEATURE_COPROC_B3 | FEATURE_COPROC_B4;
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV7A:
|
||||
case llvm::ARM::ArchKind::ARMV7R:
|
||||
case llvm::ARM::ArchKind::ARMV7M:
|
||||
case llvm::ARM::ArchKind::ARMV7S:
|
||||
case llvm::ARM::ArchKind::ARMV7EM:
|
||||
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B2 |
|
||||
FEATURE_COPROC_B3 | FEATURE_COPROC_B4;
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV8A:
|
||||
case llvm::ARM::ArchKind::ARMV8R:
|
||||
case llvm::ARM::ArchKind::ARMV8_1A:
|
||||
case llvm::ARM::ArchKind::ARMV8_2A:
|
||||
case llvm::ARM::ArchKind::ARMV8_3A:
|
||||
case llvm::ARM::ArchKind::ARMV8_4A:
|
||||
case llvm::ARM::ArchKind::ARMV8_5A:
|
||||
case llvm::ARM::ArchKind::ARMV8_6A:
|
||||
case llvm::ARM::ArchKind::ARMV8_7A:
|
||||
case llvm::ARM::ArchKind::ARMV8_8A:
|
||||
case llvm::ARM::ArchKind::ARMV8_9A:
|
||||
case llvm::ARM::ArchKind::ARMV9A:
|
||||
case llvm::ARM::ArchKind::ARMV9_1A:
|
||||
case llvm::ARM::ArchKind::ARMV9_2A:
|
||||
case llvm::ARM::ArchKind::ARMV9_3A:
|
||||
case llvm::ARM::ArchKind::ARMV9_4A:
|
||||
// Filter __arm_cdp, __arm_ldcl, __arm_stcl in arm_acle.h
|
||||
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B3;
|
||||
break;
|
||||
case llvm::ARM::ArchKind::ARMV8MMainline:
|
||||
case llvm::ARM::ArchKind::ARMV8_1MMainline:
|
||||
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B2 |
|
||||
FEATURE_COPROC_B3 | FEATURE_COPROC_B4;
|
||||
break;
|
||||
}
|
||||
Builder.defineMacro("__ARM_FEATURE_COPROC",
|
||||
"0x" + Twine::utohexstr(FeatureCoprocBF));
|
||||
|
||||
if (ArchKind == llvm::ARM::ArchKind::XSCALE)
|
||||
Builder.defineMacro("__XSCALE__");
|
||||
|
||||
|
@ -100,6 +100,19 @@ class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public TargetInfo {
|
||||
};
|
||||
uint32_t HW_FP;
|
||||
|
||||
enum {
|
||||
/// __arm_cdp __arm_ldc, __arm_ldcl, __arm_stc,
|
||||
/// __arm_stcl, __arm_mcr and __arm_mrc
|
||||
FEATURE_COPROC_B1 = (1 << 0),
|
||||
/// __arm_cdp2, __arm_ldc2, __arm_stc2, __arm_ldc2l,
|
||||
/// __arm_stc2l, __arm_mcr2 and __arm_mrc2
|
||||
FEATURE_COPROC_B2 = (1 << 1),
|
||||
/// __arm_mcrr, __arm_mrrc
|
||||
FEATURE_COPROC_B3 = (1 << 2),
|
||||
/// __arm_mcrr2, __arm_mrrc2
|
||||
FEATURE_COPROC_B4 = (1 << 3),
|
||||
};
|
||||
|
||||
void setABIAAPCS();
|
||||
void setABIAPCS(bool IsAAPCS16);
|
||||
|
||||
|
@ -146,7 +146,9 @@ class LLVM_LIBRARY_VISIBILITY AVRTargetInfo : public TargetInfo {
|
||||
case 'R': // Integer constant (Range: -6 to 5)
|
||||
Info.setRequiresImmediate(-6, 5);
|
||||
return true;
|
||||
case 'G': // Floating point constant
|
||||
case 'G': // Floating point constant 0.0
|
||||
Info.setRequiresImmediate(0);
|
||||
return true;
|
||||
case 'Q': // A memory address based on Y or Z pointer with displacement.
|
||||
return true;
|
||||
}
|
||||
|
@ -237,22 +237,15 @@ ArrayRef<Builtin::Info> RISCVTargetInfo::getTargetBuiltins() const {
|
||||
|
||||
static std::vector<std::string>
|
||||
collectNonISAExtFeature(ArrayRef<std::string> FeaturesNeedOverride, int XLen) {
|
||||
auto ParseResult =
|
||||
llvm::RISCVISAInfo::parseFeatures(XLen, FeaturesNeedOverride);
|
||||
|
||||
if (!ParseResult) {
|
||||
consumeError(ParseResult.takeError());
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatureVector();
|
||||
|
||||
std::vector<std::string> NonISAExtFeatureVec;
|
||||
|
||||
auto IsNonISAExtFeature = [](const std::string &Feature) {
|
||||
assert(Feature.size() > 1 && (Feature[0] == '+' || Feature[0] == '-'));
|
||||
StringRef Ext = StringRef(Feature).drop_front(); // drop the +/-
|
||||
return !llvm::RISCVISAInfo::isSupportedExtensionFeature(Ext);
|
||||
};
|
||||
llvm::copy_if(FeaturesNeedOverride, std::back_inserter(NonISAExtFeatureVec),
|
||||
[&](const std::string &Feat) {
|
||||
return !llvm::is_contained(ImpliedFeatures, Feat);
|
||||
});
|
||||
IsNonISAExtFeature);
|
||||
|
||||
return NonISAExtFeatureVec;
|
||||
}
|
||||
@ -303,7 +296,7 @@ bool RISCVTargetInfo::initFeatureMap(
|
||||
}
|
||||
|
||||
// RISCVISAInfo makes implications for ISA features
|
||||
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatureVector();
|
||||
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatures();
|
||||
|
||||
// parseFeatures normalizes the feature set by dropping any explicit
|
||||
// negatives, and non-extension features. We need to preserve the later
|
||||
@ -420,7 +413,7 @@ static void handleFullArchString(StringRef FullArchStr,
|
||||
// Forward the invalid FullArchStr.
|
||||
Features.push_back("+" + FullArchStr.str());
|
||||
} else {
|
||||
std::vector<std::string> FeatStrings = (*RII)->toFeatureVector();
|
||||
std::vector<std::string> FeatStrings = (*RII)->toFeatures();
|
||||
Features.insert(Features.end(), FeatStrings.begin(), FeatStrings.end());
|
||||
}
|
||||
}
|
||||
|
@ -295,11 +295,13 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
|
||||
HasAVX512BF16 = true;
|
||||
} else if (Feature == "+avx512er") {
|
||||
HasAVX512ER = true;
|
||||
Diags.Report(diag::warn_knl_knm_isa_support_removed);
|
||||
} else if (Feature == "+avx512fp16") {
|
||||
HasAVX512FP16 = true;
|
||||
HasLegalHalfType = true;
|
||||
} else if (Feature == "+avx512pf") {
|
||||
HasAVX512PF = true;
|
||||
Diags.Report(diag::warn_knl_knm_isa_support_removed);
|
||||
} else if (Feature == "+avx512dq") {
|
||||
HasAVX512DQ = true;
|
||||
} else if (Feature == "+avx512bitalg") {
|
||||
@ -358,6 +360,7 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
|
||||
HasPREFETCHI = true;
|
||||
} else if (Feature == "+prefetchwt1") {
|
||||
HasPREFETCHWT1 = true;
|
||||
Diags.Report(diag::warn_knl_knm_isa_support_removed);
|
||||
} else if (Feature == "+clzero") {
|
||||
HasCLZERO = true;
|
||||
} else if (Feature == "+cldemote") {
|
||||
|
@ -856,6 +856,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
|
||||
EnterCXXTryStmt(*cast<CXXTryStmt>(Body), true);
|
||||
|
||||
incrementProfileCounter(Body);
|
||||
maybeCreateMCDCCondBitmap();
|
||||
|
||||
RunCleanupsScope RunCleanups(*this);
|
||||
|
||||
@ -1444,8 +1445,10 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
|
||||
}
|
||||
|
||||
Stmt *Body = Dtor->getBody();
|
||||
if (Body)
|
||||
if (Body) {
|
||||
incrementProfileCounter(Body);
|
||||
maybeCreateMCDCCondBitmap();
|
||||
}
|
||||
|
||||
// The call to operator delete in a deleting destructor happens
|
||||
// outside of the function-try-block, which means it's always
|
||||
@ -1548,6 +1551,7 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args)
|
||||
LexicalScope Scope(*this, RootCS->getSourceRange());
|
||||
|
||||
incrementProfileCounter(RootCS);
|
||||
maybeCreateMCDCCondBitmap();
|
||||
AssignmentMemcpyizer AM(*this, AssignOp, Args);
|
||||
for (auto *I : RootCS->body())
|
||||
AM.emitAssignment(I);
|
||||
|
@ -4564,6 +4564,12 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
|
||||
if (LHSCondVal) { // If we have 1 && X, just emit X.
|
||||
CGF.incrementProfileCounter(E);
|
||||
|
||||
// If the top of the logical operator nest, reset the MCDC temp to 0.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeResetMCDCCondBitmap(E);
|
||||
|
||||
CGF.MCDCLogOpStack.push_back(E);
|
||||
|
||||
Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS());
|
||||
|
||||
// If we're generating for profiling or coverage, generate a branch to a
|
||||
@ -4572,6 +4578,7 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
|
||||
// "FalseBlock" after the increment is done.
|
||||
if (InstrumentRegions &&
|
||||
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
|
||||
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
|
||||
llvm::BasicBlock *FBlock = CGF.createBasicBlock("land.end");
|
||||
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt");
|
||||
Builder.CreateCondBr(RHSCond, RHSBlockCnt, FBlock);
|
||||
@ -4581,6 +4588,11 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
|
||||
CGF.EmitBlock(FBlock);
|
||||
}
|
||||
|
||||
CGF.MCDCLogOpStack.pop_back();
|
||||
// If the top of the logical operator nest, update the MCDC bitmap.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeUpdateMCDCTestVectorBitmap(E);
|
||||
|
||||
// ZExt result to int or bool.
|
||||
return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "land.ext");
|
||||
}
|
||||
@ -4590,6 +4602,12 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
|
||||
return llvm::Constant::getNullValue(ResTy);
|
||||
}
|
||||
|
||||
// If the top of the logical operator nest, reset the MCDC temp to 0.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeResetMCDCCondBitmap(E);
|
||||
|
||||
CGF.MCDCLogOpStack.push_back(E);
|
||||
|
||||
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("land.end");
|
||||
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("land.rhs");
|
||||
|
||||
@ -4622,6 +4640,7 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
|
||||
// condition coverage.
|
||||
if (InstrumentRegions &&
|
||||
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
|
||||
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
|
||||
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt");
|
||||
Builder.CreateCondBr(RHSCond, RHSBlockCnt, ContBlock);
|
||||
CGF.EmitBlock(RHSBlockCnt);
|
||||
@ -4639,6 +4658,11 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
|
||||
// Insert an entry into the phi node for the edge with the value of RHSCond.
|
||||
PN->addIncoming(RHSCond, RHSBlock);
|
||||
|
||||
CGF.MCDCLogOpStack.pop_back();
|
||||
// If the top of the logical operator nest, update the MCDC bitmap.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeUpdateMCDCTestVectorBitmap(E);
|
||||
|
||||
// Artificial location to preserve the scope information
|
||||
{
|
||||
auto NL = ApplyDebugLocation::CreateArtificial(CGF);
|
||||
@ -4680,6 +4704,12 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
|
||||
if (!LHSCondVal) { // If we have 0 || X, just emit X.
|
||||
CGF.incrementProfileCounter(E);
|
||||
|
||||
// If the top of the logical operator nest, reset the MCDC temp to 0.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeResetMCDCCondBitmap(E);
|
||||
|
||||
CGF.MCDCLogOpStack.push_back(E);
|
||||
|
||||
Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS());
|
||||
|
||||
// If we're generating for profiling or coverage, generate a branch to a
|
||||
@ -4688,6 +4718,7 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
|
||||
// "FalseBlock" after the increment is done.
|
||||
if (InstrumentRegions &&
|
||||
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
|
||||
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
|
||||
llvm::BasicBlock *FBlock = CGF.createBasicBlock("lor.end");
|
||||
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt");
|
||||
Builder.CreateCondBr(RHSCond, FBlock, RHSBlockCnt);
|
||||
@ -4697,6 +4728,11 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
|
||||
CGF.EmitBlock(FBlock);
|
||||
}
|
||||
|
||||
CGF.MCDCLogOpStack.pop_back();
|
||||
// If the top of the logical operator nest, update the MCDC bitmap.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeUpdateMCDCTestVectorBitmap(E);
|
||||
|
||||
// ZExt result to int or bool.
|
||||
return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "lor.ext");
|
||||
}
|
||||
@ -4706,6 +4742,12 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
|
||||
return llvm::ConstantInt::get(ResTy, 1);
|
||||
}
|
||||
|
||||
// If the top of the logical operator nest, reset the MCDC temp to 0.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeResetMCDCCondBitmap(E);
|
||||
|
||||
CGF.MCDCLogOpStack.push_back(E);
|
||||
|
||||
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("lor.end");
|
||||
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("lor.rhs");
|
||||
|
||||
@ -4742,6 +4784,7 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
|
||||
// condition coverage.
|
||||
if (InstrumentRegions &&
|
||||
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
|
||||
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
|
||||
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt");
|
||||
Builder.CreateCondBr(RHSCond, ContBlock, RHSBlockCnt);
|
||||
CGF.EmitBlock(RHSBlockCnt);
|
||||
@ -4755,6 +4798,11 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
|
||||
CGF.EmitBlock(ContBlock);
|
||||
PN->addIncoming(RHSCond, RHSBlock);
|
||||
|
||||
CGF.MCDCLogOpStack.pop_back();
|
||||
// If the top of the logical operator nest, update the MCDC bitmap.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeUpdateMCDCTestVectorBitmap(E);
|
||||
|
||||
// ZExt result to int.
|
||||
return Builder.CreateZExtOrBitCast(PN, ResTy, "lor.ext");
|
||||
}
|
||||
@ -4899,6 +4947,10 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
|
||||
return Builder.CreateSelect(CondV, LHS, RHS, "cond");
|
||||
}
|
||||
|
||||
// If the top of the logical operator nest, reset the MCDC temp to 0.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeResetMCDCCondBitmap(condExpr);
|
||||
|
||||
llvm::BasicBlock *LHSBlock = CGF.createBasicBlock("cond.true");
|
||||
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("cond.false");
|
||||
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("cond.end");
|
||||
@ -4934,6 +4986,11 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
|
||||
llvm::PHINode *PN = Builder.CreatePHI(LHS->getType(), 2, "cond");
|
||||
PN->addIncoming(LHS, LHSBlock);
|
||||
PN->addIncoming(RHS, RHSBlock);
|
||||
|
||||
// If the top of the logical operator nest, update the MCDC bitmap.
|
||||
if (CGF.MCDCLogOpStack.empty())
|
||||
CGF.maybeUpdateMCDCTestVectorBitmap(condExpr);
|
||||
|
||||
return PN;
|
||||
}
|
||||
|
||||
@ -5292,8 +5349,8 @@ static GEPOffsetAndOverflow EmitGEPOffsetInBytes(Value *BasePtr, Value *GEPVal,
|
||||
} else {
|
||||
// Otherwise this is array-like indexing. The local offset is the index
|
||||
// multiplied by the element size.
|
||||
auto *ElementSize = llvm::ConstantInt::get(
|
||||
IntPtrTy, DL.getTypeAllocSize(GTI.getIndexedType()));
|
||||
auto *ElementSize =
|
||||
llvm::ConstantInt::get(IntPtrTy, GTI.getSequentialElementStride(DL));
|
||||
auto *IndexS = Builder.CreateIntCast(Index, IntPtrTy, /*isSigned=*/true);
|
||||
LocalOffset = eval(BO_Mul, ElementSize, IndexS);
|
||||
}
|
||||
|
@ -1851,6 +1851,8 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
|
||||
llvm::GlobalValue::HiddenVisibility :
|
||||
llvm::GlobalValue::DefaultVisibility;
|
||||
OffsetVar->setVisibility(ivarVisibility);
|
||||
if (ivarVisibility != llvm::GlobalValue::HiddenVisibility)
|
||||
CGM.setGVProperties(OffsetVar, OID->getClassInterface());
|
||||
ivarBuilder.add(OffsetVar);
|
||||
// Ivar size
|
||||
ivarBuilder.addInt(Int32Ty,
|
||||
|
@ -837,7 +837,19 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
|
||||
if (!ThenCount && !getCurrentProfileCount() &&
|
||||
CGM.getCodeGenOpts().OptimizationLevel)
|
||||
LH = Stmt::getLikelihood(S.getThen(), S.getElse());
|
||||
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
|
||||
|
||||
// When measuring MC/DC, always fully evaluate the condition up front using
|
||||
// EvaluateExprAsBool() so that the test vector bitmap can be updated prior to
|
||||
// executing the body of the if.then or if.else. This is useful for when
|
||||
// there is a 'return' within the body, but this is particularly beneficial
|
||||
// when one if-stmt is nested within another if-stmt so that all of the MC/DC
|
||||
// updates are kept linear and consistent.
|
||||
if (!CGM.getCodeGenOpts().MCDCCoverage)
|
||||
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
|
||||
else {
|
||||
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
|
||||
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
|
||||
}
|
||||
|
||||
// Emit the 'then' code.
|
||||
EmitBlock(ThenBlock);
|
||||
|
@ -1256,6 +1256,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
|
||||
|
||||
void CodeGenFunction::EmitFunctionBody(const Stmt *Body) {
|
||||
incrementProfileCounter(Body);
|
||||
maybeCreateMCDCCondBitmap();
|
||||
if (const CompoundStmt *S = dyn_cast<CompoundStmt>(Body))
|
||||
EmitCompoundStmtWithoutScope(*S);
|
||||
else
|
||||
@ -1601,6 +1602,13 @@ bool CodeGenFunction::mightAddDeclToScope(const Stmt *S) {
|
||||
bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
|
||||
bool &ResultBool,
|
||||
bool AllowLabels) {
|
||||
// If MC/DC is enabled, disable folding so that we can instrument all
|
||||
// conditions to yield complete test vectors. We still keep track of
|
||||
// folded conditions during region mapping and visualization.
|
||||
if (!AllowLabels && CGM.getCodeGenOpts().hasProfileClangInstr() &&
|
||||
CGM.getCodeGenOpts().MCDCCoverage)
|
||||
return false;
|
||||
|
||||
llvm::APSInt ResultInt;
|
||||
if (!ConstantFoldsToSimpleInteger(Cond, ResultInt, AllowLabels))
|
||||
return false;
|
||||
@ -1629,16 +1637,20 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Strip parentheses and simplistic logical-NOT operators.
|
||||
const Expr *CodeGenFunction::stripCond(const Expr *C) {
|
||||
while (const UnaryOperator *Op = dyn_cast<UnaryOperator>(C->IgnoreParens())) {
|
||||
if (Op->getOpcode() != UO_LNot)
|
||||
break;
|
||||
C = Op->getSubExpr();
|
||||
}
|
||||
return C->IgnoreParens();
|
||||
}
|
||||
|
||||
/// Determine whether the given condition is an instrumentable condition
|
||||
/// (i.e. no "&&" or "||").
|
||||
bool CodeGenFunction::isInstrumentedCondition(const Expr *C) {
|
||||
// Bypass simplistic logical-NOT operator before determining whether the
|
||||
// condition contains any other logical operator.
|
||||
if (const UnaryOperator *UnOp = dyn_cast<UnaryOperator>(C->IgnoreParens()))
|
||||
if (UnOp->getOpcode() == UO_LNot)
|
||||
C = UnOp->getSubExpr();
|
||||
|
||||
const BinaryOperator *BOp = dyn_cast<BinaryOperator>(C->IgnoreParens());
|
||||
const BinaryOperator *BOp = dyn_cast<BinaryOperator>(stripCond(C));
|
||||
return (!BOp || !BOp->isLogicalOp());
|
||||
}
|
||||
|
||||
@ -1717,17 +1729,19 @@ void CodeGenFunction::EmitBranchToCounterBlock(
|
||||
/// statement) to the specified blocks. Based on the condition, this might try
|
||||
/// to simplify the codegen of the conditional based on the branch.
|
||||
/// \param LH The value of the likelihood attribute on the True branch.
|
||||
void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
llvm::BasicBlock *TrueBlock,
|
||||
llvm::BasicBlock *FalseBlock,
|
||||
uint64_t TrueCount,
|
||||
Stmt::Likelihood LH) {
|
||||
/// \param ConditionalOp Used by MC/DC code coverage to track the result of the
|
||||
/// ConditionalOperator (ternary) through a recursive call for the operator's
|
||||
/// LHS and RHS nodes.
|
||||
void CodeGenFunction::EmitBranchOnBoolExpr(
|
||||
const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
|
||||
uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
|
||||
Cond = Cond->IgnoreParens();
|
||||
|
||||
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
|
||||
|
||||
// Handle X && Y in a condition.
|
||||
if (CondBOp->getOpcode() == BO_LAnd) {
|
||||
MCDCLogOpStack.push_back(CondBOp);
|
||||
|
||||
// If we have "1 && X", simplify the code. "0 && X" would have constant
|
||||
// folded if the case was simple enough.
|
||||
bool ConstantBool = false;
|
||||
@ -1735,8 +1749,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
ConstantBool) {
|
||||
// br(1 && X) -> br(X).
|
||||
incrementProfileCounter(CondBOp);
|
||||
return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
|
||||
FalseBlock, TrueCount, LH);
|
||||
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
|
||||
FalseBlock, TrueCount, LH);
|
||||
MCDCLogOpStack.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have "X && 1", simplify the code to use an uncond branch.
|
||||
@ -1744,8 +1760,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) &&
|
||||
ConstantBool) {
|
||||
// br(X && 1) -> br(X).
|
||||
return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock,
|
||||
FalseBlock, TrueCount, LH, CondBOp);
|
||||
EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock,
|
||||
FalseBlock, TrueCount, LH, CondBOp);
|
||||
MCDCLogOpStack.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
// Emit the LHS as a conditional. If the LHS conditional is false, we
|
||||
@ -1774,11 +1792,13 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
|
||||
FalseBlock, TrueCount, LH);
|
||||
eval.end(*this);
|
||||
|
||||
MCDCLogOpStack.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CondBOp->getOpcode() == BO_LOr) {
|
||||
MCDCLogOpStack.push_back(CondBOp);
|
||||
|
||||
// If we have "0 || X", simplify the code. "1 || X" would have constant
|
||||
// folded if the case was simple enough.
|
||||
bool ConstantBool = false;
|
||||
@ -1786,8 +1806,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
!ConstantBool) {
|
||||
// br(0 || X) -> br(X).
|
||||
incrementProfileCounter(CondBOp);
|
||||
return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock,
|
||||
FalseBlock, TrueCount, LH);
|
||||
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock,
|
||||
FalseBlock, TrueCount, LH);
|
||||
MCDCLogOpStack.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have "X || 0", simplify the code to use an uncond branch.
|
||||
@ -1795,10 +1817,11 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) &&
|
||||
!ConstantBool) {
|
||||
// br(X || 0) -> br(X).
|
||||
return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock,
|
||||
FalseBlock, TrueCount, LH, CondBOp);
|
||||
EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock,
|
||||
FalseBlock, TrueCount, LH, CondBOp);
|
||||
MCDCLogOpStack.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
// Emit the LHS as a conditional. If the LHS conditional is true, we
|
||||
// want to jump to the TrueBlock.
|
||||
llvm::BasicBlock *LHSFalse = createBasicBlock("lor.lhs.false");
|
||||
@ -1829,14 +1852,20 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
RHSCount, LH);
|
||||
|
||||
eval.end(*this);
|
||||
|
||||
MCDCLogOpStack.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const UnaryOperator *CondUOp = dyn_cast<UnaryOperator>(Cond)) {
|
||||
// br(!x, t, f) -> br(x, f, t)
|
||||
if (CondUOp->getOpcode() == UO_LNot) {
|
||||
// Avoid doing this optimization when instrumenting a condition for MC/DC.
|
||||
// LNot is taken as part of the condition for simplicity, and changing its
|
||||
// sense negatively impacts test vector tracking.
|
||||
bool MCDCCondition = CGM.getCodeGenOpts().hasProfileClangInstr() &&
|
||||
CGM.getCodeGenOpts().MCDCCoverage &&
|
||||
isInstrumentedCondition(Cond);
|
||||
if (CondUOp->getOpcode() == UO_LNot && !MCDCCondition) {
|
||||
// Negate the count.
|
||||
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
|
||||
// The values of the enum are chosen to make this negation possible.
|
||||
@ -1876,14 +1905,14 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
{
|
||||
ApplyDebugLocation DL(*this, Cond);
|
||||
EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
|
||||
LHSScaledTrueCount, LH);
|
||||
LHSScaledTrueCount, LH, CondOp);
|
||||
}
|
||||
cond.end(*this);
|
||||
|
||||
cond.begin(*this);
|
||||
EmitBlock(RHSBlock);
|
||||
EmitBranchOnBoolExpr(CondOp->getRHS(), TrueBlock, FalseBlock,
|
||||
TrueCount - LHSScaledTrueCount, LH);
|
||||
TrueCount - LHSScaledTrueCount, LH, CondOp);
|
||||
cond.end(*this);
|
||||
|
||||
return;
|
||||
@ -1906,6 +1935,21 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
|
||||
CondV = EvaluateExprAsBool(Cond);
|
||||
}
|
||||
|
||||
// If not at the top of the logical operator nest, update MCDC temp with the
|
||||
// boolean result of the evaluated condition.
|
||||
if (!MCDCLogOpStack.empty()) {
|
||||
const Expr *MCDCBaseExpr = Cond;
|
||||
// When a nested ConditionalOperator (ternary) is encountered in a boolean
|
||||
// expression, MC/DC tracks the result of the ternary, and this is tied to
|
||||
// the ConditionalOperator expression and not the ternary's LHS or RHS. If
|
||||
// this is the case, the ConditionalOperator expression is passed through
|
||||
// the ConditionalOp parameter and then used as the MCDC base expression.
|
||||
if (ConditionalOp)
|
||||
MCDCBaseExpr = ConditionalOp;
|
||||
|
||||
maybeUpdateMCDCCondBitmap(MCDCBaseExpr, CondV);
|
||||
}
|
||||
|
||||
llvm::MDNode *Weights = nullptr;
|
||||
llvm::MDNode *Unpredictable = nullptr;
|
||||
|
||||
|
@ -287,6 +287,9 @@ class CodeGenFunction : public CodeGenTypeCache {
|
||||
/// nest would extend.
|
||||
SmallVector<llvm::CanonicalLoopInfo *, 4> OMPLoopNestStack;
|
||||
|
||||
/// Stack to track the Logical Operator recursion nest for MC/DC.
|
||||
SmallVector<const BinaryOperator *, 16> MCDCLogOpStack;
|
||||
|
||||
/// Number of nested loop to be consumed by the last surrounding
|
||||
/// loop-associated directive.
|
||||
int ExpectedOMPLoopDepth = 0;
|
||||
@ -1521,6 +1524,9 @@ class CodeGenFunction : public CodeGenTypeCache {
|
||||
|
||||
CodeGenPGO PGO;
|
||||
|
||||
/// Bitmap used by MC/DC to track condition outcomes of a boolean expression.
|
||||
Address MCDCCondBitmapAddr = Address::invalid();
|
||||
|
||||
/// Calculate branch weights appropriate for PGO data
|
||||
llvm::MDNode *createProfileWeights(uint64_t TrueCount,
|
||||
uint64_t FalseCount) const;
|
||||
@ -1539,6 +1545,52 @@ class CodeGenFunction : public CodeGenTypeCache {
|
||||
PGO.setCurrentStmt(S);
|
||||
}
|
||||
|
||||
bool isMCDCCoverageEnabled() const {
|
||||
return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
|
||||
CGM.getCodeGenOpts().MCDCCoverage &&
|
||||
!CurFn->hasFnAttribute(llvm::Attribute::NoProfile));
|
||||
}
|
||||
|
||||
/// Allocate a temp value on the stack that MCDC can use to track condition
|
||||
/// results.
|
||||
void maybeCreateMCDCCondBitmap() {
|
||||
if (isMCDCCoverageEnabled()) {
|
||||
PGO.emitMCDCParameters(Builder);
|
||||
MCDCCondBitmapAddr =
|
||||
CreateIRTemp(getContext().UnsignedIntTy, "mcdc.addr");
|
||||
}
|
||||
}
|
||||
|
||||
bool isBinaryLogicalOp(const Expr *E) const {
|
||||
const BinaryOperator *BOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
|
||||
return (BOp && BOp->isLogicalOp());
|
||||
}
|
||||
|
||||
/// Zero-init the MCDC temp value.
|
||||
void maybeResetMCDCCondBitmap(const Expr *E) {
|
||||
if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
|
||||
PGO.emitMCDCCondBitmapReset(Builder, E, MCDCCondBitmapAddr);
|
||||
PGO.setCurrentStmt(E);
|
||||
}
|
||||
}
|
||||
|
||||
/// Increment the profiler's counter for the given expression by \p StepV.
|
||||
/// If \p StepV is null, the default increment is 1.
|
||||
void maybeUpdateMCDCTestVectorBitmap(const Expr *E) {
|
||||
if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
|
||||
PGO.emitMCDCTestVectorBitmapUpdate(Builder, E, MCDCCondBitmapAddr);
|
||||
PGO.setCurrentStmt(E);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the MCDC temp value with the condition's evaluated result.
|
||||
void maybeUpdateMCDCCondBitmap(const Expr *E, llvm::Value *Val) {
|
||||
if (isMCDCCoverageEnabled()) {
|
||||
PGO.emitMCDCCondBitmapUpdate(Builder, E, MCDCCondBitmapAddr, Val);
|
||||
PGO.setCurrentStmt(E);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the profiler's count for the given statement.
|
||||
uint64_t getProfileCount(const Stmt *S) {
|
||||
return PGO.getStmtCount(S).value_or(0);
|
||||
@ -4626,6 +4678,9 @@ class CodeGenFunction : public CodeGenTypeCache {
|
||||
bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result,
|
||||
bool AllowLabels = false);
|
||||
|
||||
/// Ignore parentheses and logical-NOT to track conditions consistently.
|
||||
static const Expr *stripCond(const Expr *C);
|
||||
|
||||
/// isInstrumentedCondition - Determine whether the given condition is an
|
||||
/// instrumentable condition (i.e. no "&&" or "||").
|
||||
static bool isInstrumentedCondition(const Expr *C);
|
||||
@ -4648,7 +4703,8 @@ class CodeGenFunction : public CodeGenTypeCache {
|
||||
/// evaluate to true based on PGO data.
|
||||
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
|
||||
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
|
||||
Stmt::Likelihood LH = Stmt::LH_None);
|
||||
Stmt::Likelihood LH = Stmt::LH_None,
|
||||
const Expr *ConditionalOp = nullptr);
|
||||
|
||||
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
|
||||
/// nonnull, if \p LHS is marked _Nonnull.
|
||||
|
@ -4869,6 +4869,10 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, llvm::Type *Ty,
|
||||
isExternallyVisible(D->getLinkageAndVisibility().getLinkage()))
|
||||
GV->setSection(".cp.rodata");
|
||||
|
||||
// Handle code model attribute
|
||||
if (const auto *CMA = D->getAttr<CodeModelAttr>())
|
||||
GV->setCodeModel(CMA->getModel());
|
||||
|
||||
// Check if we a have a const declaration with an initializer, we may be
|
||||
// able to emit it as available_externally to expose it's value to the
|
||||
// optimizer.
|
||||
|
@ -161,13 +161,24 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
|
||||
PGOHash Hash;
|
||||
/// The map of statements to counters.
|
||||
llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
|
||||
/// The next bitmap byte index to assign.
|
||||
unsigned NextMCDCBitmapIdx;
|
||||
/// The map of statements to MC/DC bitmap coverage objects.
|
||||
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
|
||||
/// Maximum number of supported MC/DC conditions in a boolean expression.
|
||||
unsigned MCDCMaxCond;
|
||||
/// The profile version.
|
||||
uint64_t ProfileVersion;
|
||||
/// Diagnostics Engine used to report warnings.
|
||||
DiagnosticsEngine &Diag;
|
||||
|
||||
MapRegionCounters(PGOHashVersion HashVersion, uint64_t ProfileVersion,
|
||||
llvm::DenseMap<const Stmt *, unsigned> &CounterMap)
|
||||
llvm::DenseMap<const Stmt *, unsigned> &CounterMap,
|
||||
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap,
|
||||
unsigned MCDCMaxCond, DiagnosticsEngine &Diag)
|
||||
: NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
|
||||
ProfileVersion(ProfileVersion) {}
|
||||
NextMCDCBitmapIdx(0), MCDCBitmapMap(MCDCBitmapMap),
|
||||
MCDCMaxCond(MCDCMaxCond), ProfileVersion(ProfileVersion), Diag(Diag) {}
|
||||
|
||||
// Blocks and lambdas are handled as separate functions, so we need not
|
||||
// traverse them in the parent context.
|
||||
@ -207,15 +218,126 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
|
||||
return Type;
|
||||
}
|
||||
|
||||
/// The following stacks are used with dataTraverseStmtPre() and
|
||||
/// dataTraverseStmtPost() to track the depth of nested logical operators in a
|
||||
/// boolean expression in a function. The ultimate purpose is to keep track
|
||||
/// of the number of leaf-level conditions in the boolean expression so that a
|
||||
/// profile bitmap can be allocated based on that number.
|
||||
///
|
||||
/// The stacks are also used to find error cases and notify the user. A
|
||||
/// standard logical operator nest for a boolean expression could be in a form
|
||||
/// similar to this: "x = a && b && c && (d || f)"
|
||||
unsigned NumCond = 0;
|
||||
bool SplitNestedLogicalOp = false;
|
||||
SmallVector<const Stmt *, 16> NonLogOpStack;
|
||||
SmallVector<const BinaryOperator *, 16> LogOpStack;
|
||||
|
||||
// Hook: dataTraverseStmtPre() is invoked prior to visiting an AST Stmt node.
|
||||
bool dataTraverseStmtPre(Stmt *S) {
|
||||
/// If MC/DC is not enabled, MCDCMaxCond will be set to 0. Do nothing.
|
||||
if (MCDCMaxCond == 0)
|
||||
return true;
|
||||
|
||||
/// At the top of the logical operator nest, reset the number of conditions.
|
||||
if (LogOpStack.empty())
|
||||
NumCond = 0;
|
||||
|
||||
if (const Expr *E = dyn_cast<Expr>(S)) {
|
||||
const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
|
||||
if (BinOp && BinOp->isLogicalOp()) {
|
||||
/// Check for "split-nested" logical operators. This happens when a new
|
||||
/// boolean expression logical-op nest is encountered within an existing
|
||||
/// boolean expression, separated by a non-logical operator. For
|
||||
/// example, in "x = (a && b && c && foo(d && f))", the "d && f" case
|
||||
/// starts a new boolean expression that is separated from the other
|
||||
/// conditions by the operator foo(). Split-nested cases are not
|
||||
/// supported by MC/DC.
|
||||
SplitNestedLogicalOp = SplitNestedLogicalOp || !NonLogOpStack.empty();
|
||||
|
||||
LogOpStack.push_back(BinOp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Keep track of non-logical operators. These are OK as long as we don't
|
||||
/// encounter a new logical operator after seeing one.
|
||||
if (!LogOpStack.empty())
|
||||
NonLogOpStack.push_back(S);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hook: dataTraverseStmtPost() is invoked by the AST visitor after visiting
|
||||
// an AST Stmt node. MC/DC will use it to to signal when the top of a
|
||||
// logical operation (boolean expression) nest is encountered.
|
||||
bool dataTraverseStmtPost(Stmt *S) {
|
||||
/// If MC/DC is not enabled, MCDCMaxCond will be set to 0. Do nothing.
|
||||
if (MCDCMaxCond == 0)
|
||||
return true;
|
||||
|
||||
if (const Expr *E = dyn_cast<Expr>(S)) {
|
||||
const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
|
||||
if (BinOp && BinOp->isLogicalOp()) {
|
||||
assert(LogOpStack.back() == BinOp);
|
||||
LogOpStack.pop_back();
|
||||
|
||||
/// At the top of logical operator nest:
|
||||
if (LogOpStack.empty()) {
|
||||
/// Was the "split-nested" logical operator case encountered?
|
||||
if (SplitNestedLogicalOp) {
|
||||
unsigned DiagID = Diag.getCustomDiagID(
|
||||
DiagnosticsEngine::Warning,
|
||||
"unsupported MC/DC boolean expression; "
|
||||
"contains an operation with a nested boolean expression. "
|
||||
"Expression will not be covered");
|
||||
Diag.Report(S->getBeginLoc(), DiagID);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Was the maximum number of conditions encountered?
|
||||
if (NumCond > MCDCMaxCond) {
|
||||
unsigned DiagID = Diag.getCustomDiagID(
|
||||
DiagnosticsEngine::Warning,
|
||||
"unsupported MC/DC boolean expression; "
|
||||
"number of conditions (%0) exceeds max (%1). "
|
||||
"Expression will not be covered");
|
||||
Diag.Report(S->getBeginLoc(), DiagID) << NumCond << MCDCMaxCond;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, allocate the number of bytes required for the bitmap
|
||||
// based on the number of conditions. Must be at least 1-byte long.
|
||||
MCDCBitmapMap[BinOp] = NextMCDCBitmapIdx;
|
||||
unsigned SizeInBits = std::max<unsigned>(1L << NumCond, CHAR_BIT);
|
||||
NextMCDCBitmapIdx += SizeInBits / CHAR_BIT;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LogOpStack.empty())
|
||||
NonLogOpStack.pop_back();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// The RHS of all logical operators gets a fresh counter in order to count
|
||||
/// how many times the RHS evaluates to true or false, depending on the
|
||||
/// semantics of the operator. This is only valid for ">= v7" of the profile
|
||||
/// version so that we facilitate backward compatibility.
|
||||
/// version so that we facilitate backward compatibility. In addition, in
|
||||
/// order to use MC/DC, count the number of total LHS and RHS conditions.
|
||||
bool VisitBinaryOperator(BinaryOperator *S) {
|
||||
if (ProfileVersion >= llvm::IndexedInstrProf::Version7)
|
||||
if (S->isLogicalOp() &&
|
||||
CodeGenFunction::isInstrumentedCondition(S->getRHS()))
|
||||
CounterMap[S->getRHS()] = NextCounter++;
|
||||
if (S->isLogicalOp()) {
|
||||
if (CodeGenFunction::isInstrumentedCondition(S->getLHS()))
|
||||
NumCond++;
|
||||
|
||||
if (CodeGenFunction::isInstrumentedCondition(S->getRHS())) {
|
||||
if (ProfileVersion >= llvm::IndexedInstrProf::Version7)
|
||||
CounterMap[S->getRHS()] = NextCounter++;
|
||||
|
||||
NumCond++;
|
||||
}
|
||||
}
|
||||
return Base::VisitBinaryOperator(S);
|
||||
}
|
||||
|
||||
@ -851,8 +973,22 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
|
||||
ProfileVersion = PGOReader->getVersion();
|
||||
}
|
||||
|
||||
// If MC/DC is enabled, set the MaxConditions to a preset value. Otherwise,
|
||||
// set it to zero. This value impacts the number of conditions accepted in a
|
||||
// given boolean expression, which impacts the size of the bitmap used to
|
||||
// track test vector execution for that boolean expression. Because the
|
||||
// bitmap scales exponentially (2^n) based on the number of conditions seen,
|
||||
// the maximum value is hard-coded at 6 conditions, which is more than enough
|
||||
// for most embedded applications. Setting a maximum value prevents the
|
||||
// bitmap footprint from growing too large without the user's knowledge. In
|
||||
// the future, this value could be adjusted with a command-line option.
|
||||
unsigned MCDCMaxConditions = (CGM.getCodeGenOpts().MCDCCoverage) ? 6 : 0;
|
||||
|
||||
RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
|
||||
MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap);
|
||||
RegionMCDCBitmapMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
|
||||
MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap,
|
||||
*RegionMCDCBitmapMap, MCDCMaxConditions,
|
||||
CGM.getDiags());
|
||||
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
|
||||
Walker.TraverseDecl(const_cast<FunctionDecl *>(FD));
|
||||
else if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D))
|
||||
@ -863,6 +999,7 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
|
||||
Walker.TraverseDecl(const_cast<CapturedDecl *>(CD));
|
||||
assert(Walker.NextCounter > 0 && "no entry counter mapped for decl");
|
||||
NumRegionCounters = Walker.NextCounter;
|
||||
MCDCBitmapBytes = Walker.NextMCDCBitmapIdx;
|
||||
FunctionHash = Walker.Hash.finalize();
|
||||
}
|
||||
|
||||
@ -894,9 +1031,11 @@ void CodeGenPGO::emitCounterRegionMapping(const Decl *D) {
|
||||
|
||||
std::string CoverageMapping;
|
||||
llvm::raw_string_ostream OS(CoverageMapping);
|
||||
CoverageMappingGen MappingGen(*CGM.getCoverageMapping(),
|
||||
CGM.getContext().getSourceManager(),
|
||||
CGM.getLangOpts(), RegionCounterMap.get());
|
||||
RegionCondIDMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
|
||||
CoverageMappingGen MappingGen(
|
||||
*CGM.getCoverageMapping(), CGM.getContext().getSourceManager(),
|
||||
CGM.getLangOpts(), RegionCounterMap.get(), RegionMCDCBitmapMap.get(),
|
||||
RegionCondIDMap.get());
|
||||
MappingGen.emitCounterMapping(D, OS);
|
||||
OS.flush();
|
||||
|
||||
@ -972,6 +1111,108 @@ void CodeGenPGO::emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S,
|
||||
ArrayRef(Args));
|
||||
}
|
||||
|
||||
bool CodeGenPGO::canEmitMCDCCoverage(const CGBuilderTy &Builder) {
|
||||
return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
|
||||
CGM.getCodeGenOpts().MCDCCoverage && Builder.GetInsertBlock());
|
||||
}
|
||||
|
||||
void CodeGenPGO::emitMCDCParameters(CGBuilderTy &Builder) {
|
||||
if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
|
||||
return;
|
||||
|
||||
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
|
||||
|
||||
// Emit intrinsic representing MCDC bitmap parameters at function entry.
|
||||
// This is used by the instrumentation pass, but it isn't actually lowered to
|
||||
// anything.
|
||||
llvm::Value *Args[3] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
|
||||
Builder.getInt64(FunctionHash),
|
||||
Builder.getInt32(MCDCBitmapBytes)};
|
||||
Builder.CreateCall(
|
||||
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_parameters), Args);
|
||||
}
|
||||
|
||||
void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
|
||||
const Expr *S,
|
||||
Address MCDCCondBitmapAddr) {
|
||||
if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
|
||||
return;
|
||||
|
||||
S = S->IgnoreParens();
|
||||
|
||||
auto ExprMCDCBitmapMapIterator = RegionMCDCBitmapMap->find(S);
|
||||
if (ExprMCDCBitmapMapIterator == RegionMCDCBitmapMap->end())
|
||||
return;
|
||||
|
||||
// Extract the ID of the global bitmap associated with this expression.
|
||||
unsigned MCDCTestVectorBitmapID = ExprMCDCBitmapMapIterator->second;
|
||||
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
|
||||
|
||||
// Emit intrinsic responsible for updating the global bitmap corresponding to
|
||||
// a boolean expression. The index being set is based on the value loaded
|
||||
// from a pointer to a dedicated temporary value on the stack that is itself
|
||||
// updated via emitMCDCCondBitmapReset() and emitMCDCCondBitmapUpdate(). The
|
||||
// index represents an executed test vector.
|
||||
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
|
||||
Builder.getInt64(FunctionHash),
|
||||
Builder.getInt32(MCDCBitmapBytes),
|
||||
Builder.getInt32(MCDCTestVectorBitmapID),
|
||||
MCDCCondBitmapAddr.getPointer()};
|
||||
Builder.CreateCall(
|
||||
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_tvbitmap_update), Args);
|
||||
}
|
||||
|
||||
void CodeGenPGO::emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
|
||||
Address MCDCCondBitmapAddr) {
|
||||
if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
|
||||
return;
|
||||
|
||||
S = S->IgnoreParens();
|
||||
|
||||
if (RegionMCDCBitmapMap->find(S) == RegionMCDCBitmapMap->end())
|
||||
return;
|
||||
|
||||
// Emit intrinsic that resets a dedicated temporary value on the stack to 0.
|
||||
Builder.CreateStore(Builder.getInt32(0), MCDCCondBitmapAddr);
|
||||
}
|
||||
|
||||
void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
|
||||
Address MCDCCondBitmapAddr,
|
||||
llvm::Value *Val) {
|
||||
if (!canEmitMCDCCoverage(Builder) || !RegionCondIDMap)
|
||||
return;
|
||||
|
||||
// Even though, for simplicity, parentheses and unary logical-NOT operators
|
||||
// are considered part of their underlying condition for both MC/DC and
|
||||
// branch coverage, the condition IDs themselves are assigned and tracked
|
||||
// using the underlying condition itself. This is done solely for
|
||||
// consistency since parentheses and logical-NOTs are ignored when checking
|
||||
// whether the condition is actually an instrumentable condition. This can
|
||||
// also make debugging a bit easier.
|
||||
S = CodeGenFunction::stripCond(S);
|
||||
|
||||
auto ExprMCDCConditionIDMapIterator = RegionCondIDMap->find(S);
|
||||
if (ExprMCDCConditionIDMapIterator == RegionCondIDMap->end())
|
||||
return;
|
||||
|
||||
// Extract the ID of the condition we are setting in the bitmap.
|
||||
unsigned CondID = ExprMCDCConditionIDMapIterator->second;
|
||||
assert(CondID > 0 && "Condition has no ID!");
|
||||
|
||||
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
|
||||
|
||||
// Emit intrinsic that updates a dedicated temporary value on the stack after
|
||||
// a condition is evaluated. After the set of conditions has been updated,
|
||||
// the resulting value is used to update the boolean expression's bitmap.
|
||||
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
|
||||
Builder.getInt64(FunctionHash),
|
||||
Builder.getInt32(CondID - 1),
|
||||
MCDCCondBitmapAddr.getPointer(), Val};
|
||||
Builder.CreateCall(
|
||||
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_condbitmap_update),
|
||||
Args);
|
||||
}
|
||||
|
||||
void CodeGenPGO::setValueProfilingFlag(llvm::Module &M) {
|
||||
if (CGM.getCodeGenOpts().hasProfileClangInstr())
|
||||
M.addModuleFlag(llvm::Module::Warning, "EnableValueProfiling",
|
||||
|
@ -33,8 +33,11 @@ class CodeGenPGO {
|
||||
|
||||
std::array <unsigned, llvm::IPVK_Last + 1> NumValueSites;
|
||||
unsigned NumRegionCounters;
|
||||
unsigned MCDCBitmapBytes;
|
||||
uint64_t FunctionHash;
|
||||
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionCounterMap;
|
||||
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionMCDCBitmapMap;
|
||||
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionCondIDMap;
|
||||
std::unique_ptr<llvm::DenseMap<const Stmt *, uint64_t>> StmtCountMap;
|
||||
std::unique_ptr<llvm::InstrProfRecord> ProfRecord;
|
||||
std::vector<uint64_t> RegionCounts;
|
||||
@ -43,7 +46,8 @@ class CodeGenPGO {
|
||||
public:
|
||||
CodeGenPGO(CodeGenModule &CGModule)
|
||||
: CGM(CGModule), FuncNameVar(nullptr), NumValueSites({{0}}),
|
||||
NumRegionCounters(0), FunctionHash(0), CurrentRegionCount(0) {}
|
||||
NumRegionCounters(0), MCDCBitmapBytes(0), FunctionHash(0),
|
||||
CurrentRegionCount(0) {}
|
||||
|
||||
/// Whether or not we have PGO region data for the current function. This is
|
||||
/// false both when we have no data at all and when our data has been
|
||||
@ -103,10 +107,18 @@ class CodeGenPGO {
|
||||
bool IsInMainFile);
|
||||
bool skipRegionMappingForDecl(const Decl *D);
|
||||
void emitCounterRegionMapping(const Decl *D);
|
||||
bool canEmitMCDCCoverage(const CGBuilderTy &Builder);
|
||||
|
||||
public:
|
||||
void emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S,
|
||||
llvm::Value *StepV);
|
||||
void emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
|
||||
Address MCDCCondBitmapAddr);
|
||||
void emitMCDCParameters(CGBuilderTy &Builder);
|
||||
void emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
|
||||
Address MCDCCondBitmapAddr);
|
||||
void emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
|
||||
Address MCDCCondBitmapAddr, llvm::Value *Val);
|
||||
|
||||
/// Return the region count for the counter at the given index.
|
||||
uint64_t getRegionCount(const Stmt *S) {
|
||||
|
@ -95,6 +95,8 @@ void CoverageSourceInfo::updateNextTokLoc(SourceLocation Loc) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
using MCDCConditionID = CounterMappingRegion::MCDCConditionID;
|
||||
using MCDCParameters = CounterMappingRegion::MCDCParameters;
|
||||
|
||||
/// A region of source code that can be mapped to a counter.
|
||||
class SourceMappingRegion {
|
||||
@ -104,6 +106,9 @@ class SourceMappingRegion {
|
||||
/// Secondary Counter used for Branch Regions for "False" branches.
|
||||
std::optional<Counter> FalseCount;
|
||||
|
||||
/// Parameters used for Modified Condition/Decision Coverage
|
||||
MCDCParameters MCDCParams;
|
||||
|
||||
/// The region's starting location.
|
||||
std::optional<SourceLocation> LocStart;
|
||||
|
||||
@ -122,11 +127,18 @@ class SourceMappingRegion {
|
||||
}
|
||||
|
||||
SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,
|
||||
MCDCParameters MCDCParams,
|
||||
std::optional<SourceLocation> LocStart,
|
||||
std::optional<SourceLocation> LocEnd,
|
||||
bool GapRegion = false)
|
||||
: Count(Count), FalseCount(FalseCount), LocStart(LocStart),
|
||||
LocEnd(LocEnd), GapRegion(GapRegion) {}
|
||||
: Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
|
||||
LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) {}
|
||||
|
||||
SourceMappingRegion(MCDCParameters MCDCParams,
|
||||
std::optional<SourceLocation> LocStart,
|
||||
std::optional<SourceLocation> LocEnd)
|
||||
: MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
|
||||
GapRegion(false) {}
|
||||
|
||||
const Counter &getCounter() const { return Count; }
|
||||
|
||||
@ -163,6 +175,10 @@ class SourceMappingRegion {
|
||||
void setGap(bool Gap) { GapRegion = Gap; }
|
||||
|
||||
bool isBranch() const { return FalseCount.has_value(); }
|
||||
|
||||
bool isMCDCDecision() const { return MCDCParams.NumConditions != 0; }
|
||||
|
||||
const MCDCParameters &getMCDCParams() const { return MCDCParams; }
|
||||
};
|
||||
|
||||
/// Spelling locations for the start and end of a source region.
|
||||
@ -454,8 +470,13 @@ class CoverageMappingBuilder {
|
||||
SR.LineEnd, SR.ColumnEnd));
|
||||
} else if (Region.isBranch()) {
|
||||
MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
|
||||
Region.getCounter(), Region.getFalseCounter(), *CovFileID,
|
||||
SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd));
|
||||
Region.getCounter(), Region.getFalseCounter(),
|
||||
Region.getMCDCParams(), *CovFileID, SR.LineStart, SR.ColumnStart,
|
||||
SR.LineEnd, SR.ColumnEnd));
|
||||
} else if (Region.isMCDCDecision()) {
|
||||
MappingRegions.push_back(CounterMappingRegion::makeDecisionRegion(
|
||||
Region.getMCDCParams(), *CovFileID, SR.LineStart, SR.ColumnStart,
|
||||
SR.LineEnd, SR.ColumnEnd));
|
||||
} else {
|
||||
MappingRegions.push_back(CounterMappingRegion::makeRegion(
|
||||
Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
|
||||
@ -542,6 +563,239 @@ struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {
|
||||
}
|
||||
};
|
||||
|
||||
/// A wrapper object for maintaining stacks to track the resursive AST visitor
|
||||
/// walks for the purpose of assigning IDs to leaf-level conditions measured by
|
||||
/// MC/DC. The object is created with a reference to the MCDCBitmapMap that was
|
||||
/// created during the initial AST walk. The presence of a bitmap associated
|
||||
/// with a boolean expression (top-level logical operator nest) indicates that
|
||||
/// the boolean expression qualified for MC/DC. The resulting condition IDs
|
||||
/// are preserved in a map reference that is also provided during object
|
||||
/// creation.
|
||||
struct MCDCCoverageBuilder {
|
||||
|
||||
/// The AST walk recursively visits nested logical-AND or logical-OR binary
|
||||
/// operator nodes and then visits their LHS and RHS children nodes. As this
|
||||
/// happens, the algorithm will assign IDs to each operator's LHS and RHS side
|
||||
/// as the walk moves deeper into the nest. At each level of the recursive
|
||||
/// nest, the LHS and RHS may actually correspond to larger subtrees (not
|
||||
/// leaf-conditions). If this is the case, when that node is visited, the ID
|
||||
/// assigned to the subtree is re-assigned to its LHS, and a new ID is given
|
||||
/// to its RHS. At the end of the walk, all leaf-level conditions will have a
|
||||
/// unique ID -- keep in mind that the final set of IDs may not be in
|
||||
/// numerical order from left to right.
|
||||
///
|
||||
/// Example: "x = (A && B) || (C && D) || (D && F)"
|
||||
///
|
||||
/// Visit Depth1:
|
||||
/// (A && B) || (C && D) || (D && F)
|
||||
/// ^-------LHS--------^ ^-RHS--^
|
||||
/// ID=1 ID=2
|
||||
///
|
||||
/// Visit LHS-Depth2:
|
||||
/// (A && B) || (C && D)
|
||||
/// ^-LHS--^ ^-RHS--^
|
||||
/// ID=1 ID=3
|
||||
///
|
||||
/// Visit LHS-Depth3:
|
||||
/// (A && B)
|
||||
/// LHS RHS
|
||||
/// ID=1 ID=4
|
||||
///
|
||||
/// Visit RHS-Depth3:
|
||||
/// (C && D)
|
||||
/// LHS RHS
|
||||
/// ID=3 ID=5
|
||||
///
|
||||
/// Visit RHS-Depth2: (D && F)
|
||||
/// LHS RHS
|
||||
/// ID=2 ID=6
|
||||
///
|
||||
/// Visit Depth1:
|
||||
/// (A && B) || (C && D) || (D && F)
|
||||
/// ID=1 ID=4 ID=3 ID=5 ID=2 ID=6
|
||||
///
|
||||
/// A node ID of '0' always means MC/DC isn't being tracked.
|
||||
///
|
||||
/// As the AST walk proceeds recursively, the algorithm will also use stacks
|
||||
/// to track the IDs of logical-AND and logical-OR operations on the RHS so
|
||||
/// that it can be determined which nodes are executed next, depending on how
|
||||
/// a LHS or RHS of a logical-AND or logical-OR is evaluated. This
|
||||
/// information relies on the assigned IDs and are embedded within the
|
||||
/// coverage region IDs of each branch region associated with a leaf-level
|
||||
/// condition. This information helps the visualization tool reconstruct all
|
||||
/// possible test vectors for the purposes of MC/DC analysis. if a "next" node
|
||||
/// ID is '0', it means it's the end of the test vector. The following rules
|
||||
/// are used:
|
||||
///
|
||||
/// For logical-AND ("LHS && RHS"):
|
||||
/// - If LHS is TRUE, execution goes to the RHS node.
|
||||
/// - If LHS is FALSE, execution goes to the LHS node of the next logical-OR.
|
||||
/// If that does not exist, execution exits (ID == 0).
|
||||
///
|
||||
/// - If RHS is TRUE, execution goes to LHS node of the next logical-AND.
|
||||
/// If that does not exist, execution exits (ID == 0).
|
||||
/// - If RHS is FALSE, execution goes to the LHS node of the next logical-OR.
|
||||
/// If that does not exist, execution exits (ID == 0).
|
||||
///
|
||||
/// For logical-OR ("LHS || RHS"):
|
||||
/// - If LHS is TRUE, execution goes to the LHS node of the next logical-AND.
|
||||
/// If that does not exist, execution exits (ID == 0).
|
||||
/// - If LHS is FALSE, execution goes to the RHS node.
|
||||
///
|
||||
/// - If RHS is TRUE, execution goes to LHS node of the next logical-AND.
|
||||
/// If that does not exist, execution exits (ID == 0).
|
||||
/// - If RHS is FALSE, execution goes to the LHS node of the next logical-OR.
|
||||
/// If that does not exist, execution exits (ID == 0).
|
||||
///
|
||||
/// Finally, the condition IDs are also used when instrumenting the code to
|
||||
/// indicate a unique offset into a temporary bitmap that represents the true
|
||||
/// or false evaluation of that particular condition.
|
||||
///
|
||||
/// NOTE regarding the use of CodeGenFunction::stripCond(). Even though, for
|
||||
/// simplicity, parentheses and unary logical-NOT operators are considered
|
||||
/// part of their underlying condition for both MC/DC and branch coverage, the
|
||||
/// condition IDs themselves are assigned and tracked using the underlying
|
||||
/// condition itself. This is done solely for consistency since parentheses
|
||||
/// and logical-NOTs are ignored when checking whether the condition is
|
||||
/// actually an instrumentable condition. This can also make debugging a bit
|
||||
/// easier.
|
||||
|
||||
private:
|
||||
CodeGenModule &CGM;
|
||||
|
||||
llvm::SmallVector<MCDCConditionID> AndRHS;
|
||||
llvm::SmallVector<MCDCConditionID> OrRHS;
|
||||
llvm::SmallVector<const BinaryOperator *> NestLevel;
|
||||
llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDs;
|
||||
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
|
||||
MCDCConditionID NextID = 1;
|
||||
bool NotMapped = false;
|
||||
|
||||
/// Is this a logical-AND operation?
|
||||
bool isLAnd(const BinaryOperator *E) const {
|
||||
return E->getOpcode() == BO_LAnd;
|
||||
}
|
||||
|
||||
/// Push an ID onto the corresponding RHS stack.
|
||||
void pushRHS(const BinaryOperator *E) {
|
||||
llvm::SmallVector<MCDCConditionID> &rhs = isLAnd(E) ? AndRHS : OrRHS;
|
||||
rhs.push_back(CondIDs[CodeGenFunction::stripCond(E->getRHS())]);
|
||||
}
|
||||
|
||||
/// Pop an ID from the corresponding RHS stack.
|
||||
void popRHS(const BinaryOperator *E) {
|
||||
llvm::SmallVector<MCDCConditionID> &rhs = isLAnd(E) ? AndRHS : OrRHS;
|
||||
if (!rhs.empty())
|
||||
rhs.pop_back();
|
||||
}
|
||||
|
||||
/// If the expected ID is on top, pop it off the corresponding RHS stack.
|
||||
void popRHSifTop(const BinaryOperator *E) {
|
||||
if (!OrRHS.empty() && CondIDs[E] == OrRHS.back())
|
||||
OrRHS.pop_back();
|
||||
else if (!AndRHS.empty() && CondIDs[E] == AndRHS.back())
|
||||
AndRHS.pop_back();
|
||||
}
|
||||
|
||||
public:
|
||||
MCDCCoverageBuilder(CodeGenModule &CGM,
|
||||
llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
|
||||
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap)
|
||||
: CGM(CGM), CondIDs(CondIDMap), MCDCBitmapMap(MCDCBitmapMap) {}
|
||||
|
||||
/// Return the ID of the RHS of the next, upper nest-level logical-OR.
|
||||
MCDCConditionID getNextLOrCondID() const {
|
||||
return OrRHS.empty() ? 0 : OrRHS.back();
|
||||
}
|
||||
|
||||
/// Return the ID of the RHS of the next, upper nest-level logical-AND.
|
||||
MCDCConditionID getNextLAndCondID() const {
|
||||
return AndRHS.empty() ? 0 : AndRHS.back();
|
||||
}
|
||||
|
||||
/// Return the ID of a given condition.
|
||||
MCDCConditionID getCondID(const Expr *Cond) const {
|
||||
auto I = CondIDs.find(CodeGenFunction::stripCond(Cond));
|
||||
if (I == CondIDs.end())
|
||||
return 0;
|
||||
else
|
||||
return I->second;
|
||||
}
|
||||
|
||||
/// Push the binary operator statement to track the nest level and assign IDs
|
||||
/// to the operator's LHS and RHS. The RHS may be a larger subtree that is
|
||||
/// broken up on successive levels.
|
||||
void pushAndAssignIDs(const BinaryOperator *E) {
|
||||
if (!CGM.getCodeGenOpts().MCDCCoverage)
|
||||
return;
|
||||
|
||||
// If binary expression is disqualified, don't do mapping.
|
||||
if (NestLevel.empty() && MCDCBitmapMap.find(CodeGenFunction::stripCond(
|
||||
E)) == MCDCBitmapMap.end())
|
||||
NotMapped = true;
|
||||
|
||||
// Push Stmt on 'NestLevel' stack to keep track of nest location.
|
||||
NestLevel.push_back(E);
|
||||
|
||||
// Don't go any further if we don't need to map condition IDs.
|
||||
if (NotMapped)
|
||||
return;
|
||||
|
||||
// If the operator itself has an assigned ID, this means it represents a
|
||||
// larger subtree. In this case, pop its ID out of the RHS stack and
|
||||
// assign that ID to its LHS node. Its RHS will receive a new ID.
|
||||
if (CondIDs.find(CodeGenFunction::stripCond(E)) != CondIDs.end()) {
|
||||
// If Stmt has an ID, assign its ID to LHS
|
||||
CondIDs[CodeGenFunction::stripCond(E->getLHS())] = CondIDs[E];
|
||||
|
||||
// Since the operator's LHS assumes the operator's same ID, pop the
|
||||
// operator from the RHS stack so that if LHS short-circuits, it won't be
|
||||
// incorrectly re-used as the node executed next.
|
||||
popRHSifTop(E);
|
||||
} else {
|
||||
// Otherwise, assign ID+1 to LHS.
|
||||
CondIDs[CodeGenFunction::stripCond(E->getLHS())] = NextID++;
|
||||
}
|
||||
|
||||
// Assign ID+1 to RHS.
|
||||
CondIDs[CodeGenFunction::stripCond(E->getRHS())] = NextID++;
|
||||
|
||||
// Push ID of Stmt's RHS so that LHS nodes know about it
|
||||
pushRHS(E);
|
||||
}
|
||||
|
||||
/// Pop the binary operator from the next level. If the walk is at the top of
|
||||
/// the next, assign the total number of conditions.
|
||||
unsigned popAndReturnCondCount(const BinaryOperator *E) {
|
||||
if (!CGM.getCodeGenOpts().MCDCCoverage)
|
||||
return 0;
|
||||
|
||||
unsigned TotalConds = 0;
|
||||
|
||||
// Pop Stmt from 'NestLevel' stack.
|
||||
assert(NestLevel.back() == E);
|
||||
NestLevel.pop_back();
|
||||
|
||||
// Reset state if not doing mapping.
|
||||
if (NestLevel.empty() && NotMapped) {
|
||||
NotMapped = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pop RHS ID.
|
||||
popRHS(E);
|
||||
|
||||
// If at the parent (NestLevel=0), set conds and reset.
|
||||
if (NestLevel.empty()) {
|
||||
TotalConds = NextID - 1;
|
||||
|
||||
// Reset ID back to beginning.
|
||||
NextID = 1;
|
||||
}
|
||||
return TotalConds;
|
||||
}
|
||||
};
|
||||
|
||||
/// A StmtVisitor that creates coverage mapping regions which map
|
||||
/// from the source code locations to the PGO counters.
|
||||
struct CounterCoverageMappingBuilder
|
||||
@ -550,8 +804,14 @@ struct CounterCoverageMappingBuilder
|
||||
/// The map of statements to count values.
|
||||
llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
|
||||
|
||||
/// The map of statements to bitmap coverage object values.
|
||||
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
|
||||
|
||||
/// A stack of currently live regions.
|
||||
std::vector<SourceMappingRegion> RegionStack;
|
||||
llvm::SmallVector<SourceMappingRegion> RegionStack;
|
||||
|
||||
/// An object to manage MCDC regions.
|
||||
MCDCCoverageBuilder MCDCBuilder;
|
||||
|
||||
CounterExpressionBuilder Builder;
|
||||
|
||||
@ -589,6 +849,8 @@ struct CounterCoverageMappingBuilder
|
||||
return Counter::getCounter(CounterMap[S]);
|
||||
}
|
||||
|
||||
unsigned getRegionBitmap(const Stmt *S) { return MCDCBitmapMap[S]; }
|
||||
|
||||
/// Push a region onto the stack.
|
||||
///
|
||||
/// Returns the index on the stack where the region was pushed. This can be
|
||||
@ -596,7 +858,9 @@ struct CounterCoverageMappingBuilder
|
||||
size_t pushRegion(Counter Count,
|
||||
std::optional<SourceLocation> StartLoc = std::nullopt,
|
||||
std::optional<SourceLocation> EndLoc = std::nullopt,
|
||||
std::optional<Counter> FalseCount = std::nullopt) {
|
||||
std::optional<Counter> FalseCount = std::nullopt,
|
||||
MCDCConditionID ID = 0, MCDCConditionID TrueID = 0,
|
||||
MCDCConditionID FalseID = 0) {
|
||||
|
||||
if (StartLoc && !FalseCount) {
|
||||
MostRecentLocation = *StartLoc;
|
||||
@ -615,7 +879,19 @@ struct CounterCoverageMappingBuilder
|
||||
StartLoc = std::nullopt;
|
||||
if (EndLoc && EndLoc->isInvalid())
|
||||
EndLoc = std::nullopt;
|
||||
RegionStack.emplace_back(Count, FalseCount, StartLoc, EndLoc);
|
||||
RegionStack.emplace_back(Count, FalseCount,
|
||||
MCDCParameters{0, 0, ID, TrueID, FalseID},
|
||||
StartLoc, EndLoc);
|
||||
|
||||
return RegionStack.size() - 1;
|
||||
}
|
||||
|
||||
size_t pushRegion(unsigned BitmapIdx, unsigned Conditions,
|
||||
std::optional<SourceLocation> StartLoc = std::nullopt,
|
||||
std::optional<SourceLocation> EndLoc = std::nullopt) {
|
||||
|
||||
RegionStack.emplace_back(MCDCParameters{BitmapIdx, Conditions}, StartLoc,
|
||||
EndLoc);
|
||||
|
||||
return RegionStack.size() - 1;
|
||||
}
|
||||
@ -746,7 +1022,9 @@ struct CounterCoverageMappingBuilder
|
||||
/// and add it to the function's SourceRegions. A branch region tracks a
|
||||
/// "True" counter and a "False" counter for boolean expressions that
|
||||
/// result in the generation of a branch.
|
||||
void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt) {
|
||||
void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt,
|
||||
MCDCConditionID ID = 0, MCDCConditionID TrueID = 0,
|
||||
MCDCConditionID FalseID = 0) {
|
||||
// Check for NULL conditions.
|
||||
if (!C)
|
||||
return;
|
||||
@ -764,13 +1042,21 @@ struct CounterCoverageMappingBuilder
|
||||
// CodeGenFunction.c always returns false, but that is very heavy-handed.
|
||||
if (ConditionFoldsToBool(C))
|
||||
popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C),
|
||||
Counter::getZero()));
|
||||
Counter::getZero(), ID, TrueID, FalseID));
|
||||
else
|
||||
// Otherwise, create a region with the True counter and False counter.
|
||||
popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt));
|
||||
popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt, ID,
|
||||
TrueID, FalseID));
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Decision Region with a BitmapIdx and number of Conditions. This
|
||||
/// type of region "contains" branch regions, one for each of the conditions.
|
||||
/// The visualization tool will group everything together.
|
||||
void createDecisionRegion(const Expr *C, unsigned BitmapIdx, unsigned Conds) {
|
||||
popRegions(pushRegion(BitmapIdx, Conds, getStart(C), getEnd(C)));
|
||||
}
|
||||
|
||||
/// Create a Branch Region around a SwitchCase for code coverage
|
||||
/// and add it to the function's SourceRegions.
|
||||
void createSwitchCaseRegion(const SwitchCase *SC, Counter TrueCnt,
|
||||
@ -851,8 +1137,12 @@ struct CounterCoverageMappingBuilder
|
||||
// we've seen this region.
|
||||
if (StartLocs.insert(Loc).second) {
|
||||
if (I.isBranch())
|
||||
SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(), Loc,
|
||||
getEndOfFileOrMacro(Loc), I.isBranch());
|
||||
SourceRegions.emplace_back(
|
||||
I.getCounter(), I.getFalseCounter(),
|
||||
MCDCParameters{0, 0, I.getMCDCParams().ID,
|
||||
I.getMCDCParams().TrueID,
|
||||
I.getMCDCParams().FalseID},
|
||||
Loc, getEndOfFileOrMacro(Loc), I.isBranch());
|
||||
else
|
||||
SourceRegions.emplace_back(I.getCounter(), Loc,
|
||||
getEndOfFileOrMacro(Loc));
|
||||
@ -971,9 +1261,13 @@ struct CounterCoverageMappingBuilder
|
||||
|
||||
CounterCoverageMappingBuilder(
|
||||
CoverageMappingModuleGen &CVM,
|
||||
llvm::DenseMap<const Stmt *, unsigned> &CounterMap, SourceManager &SM,
|
||||
const LangOptions &LangOpts)
|
||||
: CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap) {}
|
||||
llvm::DenseMap<const Stmt *, unsigned> &CounterMap,
|
||||
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap,
|
||||
llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
|
||||
SourceManager &SM, const LangOptions &LangOpts)
|
||||
: CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
|
||||
MCDCBitmapMap(MCDCBitmapMap),
|
||||
MCDCBuilder(CVM.getCodeGenModule(), CondIDMap, MCDCBitmapMap) {}
|
||||
|
||||
/// Write the mapping data to the output stream
|
||||
void write(llvm::raw_ostream &OS) {
|
||||
@ -1519,6 +1813,9 @@ struct CounterCoverageMappingBuilder
|
||||
}
|
||||
|
||||
void VisitBinLAnd(const BinaryOperator *E) {
|
||||
// Keep track of Binary Operator and assign MCDC condition IDs
|
||||
MCDCBuilder.pushAndAssignIDs(E);
|
||||
|
||||
extendRegion(E->getLHS());
|
||||
propagateCounts(getRegion().getCounter(), E->getLHS());
|
||||
handleFileExit(getEnd(E->getLHS()));
|
||||
@ -1527,6 +1824,11 @@ struct CounterCoverageMappingBuilder
|
||||
extendRegion(E->getRHS());
|
||||
propagateCounts(getRegionCounter(E), E->getRHS());
|
||||
|
||||
// Process Binary Operator and create MCDC Decision Region if top-level
|
||||
unsigned NumConds = 0;
|
||||
if ((NumConds = MCDCBuilder.popAndReturnCondCount(E)))
|
||||
createDecisionRegion(E, getRegionBitmap(E), NumConds);
|
||||
|
||||
// Extract the RHS's Execution Counter.
|
||||
Counter RHSExecCnt = getRegionCounter(E);
|
||||
|
||||
@ -1536,13 +1838,30 @@ struct CounterCoverageMappingBuilder
|
||||
// Extract the Parent Region Counter.
|
||||
Counter ParentCnt = getRegion().getCounter();
|
||||
|
||||
// Extract the MCDC condition IDs (returns 0 if not needed).
|
||||
MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID();
|
||||
MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID();
|
||||
MCDCConditionID LHSid = MCDCBuilder.getCondID(E->getLHS());
|
||||
MCDCConditionID RHSid = MCDCBuilder.getCondID(E->getRHS());
|
||||
|
||||
// Create Branch Region around LHS condition.
|
||||
// MC/DC: For "LHS && RHS"
|
||||
// - If LHS is TRUE, execution goes to the RHS.
|
||||
// - If LHS is FALSE, execution goes to the LHS of the next logical-OR.
|
||||
// If that does not exist, execution exits (ID == 0).
|
||||
createBranchRegion(E->getLHS(), RHSExecCnt,
|
||||
subtractCounters(ParentCnt, RHSExecCnt));
|
||||
subtractCounters(ParentCnt, RHSExecCnt), LHSid, RHSid,
|
||||
NextOrID);
|
||||
|
||||
// Create Branch Region around RHS condition.
|
||||
// MC/DC: For "LHS && RHS"
|
||||
// - If RHS is TRUE, execution goes to LHS of the next logical-AND.
|
||||
// If that does not exist, execution exits (ID == 0).
|
||||
// - If RHS is FALSE, execution goes to the LHS of the next logical-OR.
|
||||
// If that does not exist, execution exits (ID == 0).
|
||||
createBranchRegion(E->getRHS(), RHSTrueCnt,
|
||||
subtractCounters(RHSExecCnt, RHSTrueCnt));
|
||||
subtractCounters(RHSExecCnt, RHSTrueCnt), RHSid,
|
||||
NextAndID, NextOrID);
|
||||
}
|
||||
|
||||
// Determine whether the right side of OR operation need to be visited.
|
||||
@ -1556,6 +1875,9 @@ struct CounterCoverageMappingBuilder
|
||||
}
|
||||
|
||||
void VisitBinLOr(const BinaryOperator *E) {
|
||||
// Keep track of Binary Operator and assign MCDC condition IDs
|
||||
MCDCBuilder.pushAndAssignIDs(E);
|
||||
|
||||
extendRegion(E->getLHS());
|
||||
Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());
|
||||
handleFileExit(getEnd(E->getLHS()));
|
||||
@ -1564,6 +1886,11 @@ struct CounterCoverageMappingBuilder
|
||||
extendRegion(E->getRHS());
|
||||
propagateCounts(getRegionCounter(E), E->getRHS());
|
||||
|
||||
// Process Binary Operator and create MCDC Decision Region if top-level
|
||||
unsigned NumConds = 0;
|
||||
if ((NumConds = MCDCBuilder.popAndReturnCondCount(E)))
|
||||
createDecisionRegion(E, getRegionBitmap(E), NumConds);
|
||||
|
||||
// Extract the RHS's Execution Counter.
|
||||
Counter RHSExecCnt = getRegionCounter(E);
|
||||
|
||||
@ -1577,13 +1904,28 @@ struct CounterCoverageMappingBuilder
|
||||
// Extract the Parent Region Counter.
|
||||
Counter ParentCnt = getRegion().getCounter();
|
||||
|
||||
// Extract the MCDC condition IDs (returns 0 if not needed).
|
||||
MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID();
|
||||
MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID();
|
||||
MCDCConditionID LHSid = MCDCBuilder.getCondID(E->getLHS());
|
||||
MCDCConditionID RHSid = MCDCBuilder.getCondID(E->getRHS());
|
||||
|
||||
// Create Branch Region around LHS condition.
|
||||
// MC/DC: For "LHS || RHS"
|
||||
// - If LHS is TRUE, execution goes to the LHS of the next logical-AND.
|
||||
// If that does not exist, execution exits (ID == 0).
|
||||
// - If LHS is FALSE, execution goes to the RHS.
|
||||
createBranchRegion(E->getLHS(), subtractCounters(ParentCnt, RHSExecCnt),
|
||||
RHSExecCnt);
|
||||
RHSExecCnt, LHSid, NextAndID, RHSid);
|
||||
|
||||
// Create Branch Region around RHS condition.
|
||||
// MC/DC: For "LHS || RHS"
|
||||
// - If RHS is TRUE, execution goes to LHS of the next logical-AND.
|
||||
// If that does not exist, execution exits (ID == 0).
|
||||
// - If RHS is FALSE, execution goes to the LHS of the next logical-OR.
|
||||
// If that does not exist, execution exits (ID == 0).
|
||||
createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt),
|
||||
RHSFalseCnt);
|
||||
RHSFalseCnt, RHSid, NextAndID, NextOrID);
|
||||
}
|
||||
|
||||
void VisitLambdaExpr(const LambdaExpr *LE) {
|
||||
@ -1633,11 +1975,23 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
|
||||
|
||||
OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart
|
||||
<< " -> " << R.LineEnd << ":" << R.ColumnEnd << " = ";
|
||||
Ctx.dump(R.Count, OS);
|
||||
|
||||
if (R.Kind == CounterMappingRegion::BranchRegion) {
|
||||
OS << ", ";
|
||||
Ctx.dump(R.FalseCount, OS);
|
||||
if (R.Kind == CounterMappingRegion::MCDCDecisionRegion) {
|
||||
OS << "M:" << R.MCDCParams.BitmapIdx;
|
||||
OS << ", C:" << R.MCDCParams.NumConditions;
|
||||
} else {
|
||||
Ctx.dump(R.Count, OS);
|
||||
|
||||
if (R.Kind == CounterMappingRegion::BranchRegion ||
|
||||
R.Kind == CounterMappingRegion::MCDCBranchRegion) {
|
||||
OS << ", ";
|
||||
Ctx.dump(R.FalseCount, OS);
|
||||
}
|
||||
}
|
||||
|
||||
if (R.Kind == CounterMappingRegion::MCDCBranchRegion) {
|
||||
OS << " [" << R.MCDCParams.ID << "," << R.MCDCParams.TrueID;
|
||||
OS << "," << R.MCDCParams.FalseID << "] ";
|
||||
}
|
||||
|
||||
if (R.Kind == CounterMappingRegion::ExpansionRegion)
|
||||
@ -1846,8 +2200,9 @@ unsigned CoverageMappingModuleGen::getFileID(FileEntryRef File) {
|
||||
|
||||
void CoverageMappingGen::emitCounterMapping(const Decl *D,
|
||||
llvm::raw_ostream &OS) {
|
||||
assert(CounterMap);
|
||||
CounterCoverageMappingBuilder Walker(CVM, *CounterMap, SM, LangOpts);
|
||||
assert(CounterMap && MCDCBitmapMap);
|
||||
CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCBitmapMap,
|
||||
*CondIDMap, SM, LangOpts);
|
||||
Walker.VisitDecl(D);
|
||||
Walker.write(OS);
|
||||
}
|
||||
|
@ -150,16 +150,22 @@ class CoverageMappingGen {
|
||||
SourceManager &SM;
|
||||
const LangOptions &LangOpts;
|
||||
llvm::DenseMap<const Stmt *, unsigned> *CounterMap;
|
||||
llvm::DenseMap<const Stmt *, unsigned> *MCDCBitmapMap;
|
||||
llvm::DenseMap<const Stmt *, unsigned> *CondIDMap;
|
||||
|
||||
public:
|
||||
CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM,
|
||||
const LangOptions &LangOpts)
|
||||
: CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(nullptr) {}
|
||||
: CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(nullptr),
|
||||
MCDCBitmapMap(nullptr), CondIDMap(nullptr) {}
|
||||
|
||||
CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM,
|
||||
const LangOptions &LangOpts,
|
||||
llvm::DenseMap<const Stmt *, unsigned> *CounterMap)
|
||||
: CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(CounterMap) {}
|
||||
llvm::DenseMap<const Stmt *, unsigned> *CounterMap,
|
||||
llvm::DenseMap<const Stmt *, unsigned> *MCDCBitmapMap,
|
||||
llvm::DenseMap<const Stmt *, unsigned> *CondIDMap)
|
||||
: CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(CounterMap),
|
||||
MCDCBitmapMap(MCDCBitmapMap), CondIDMap(CondIDMap) {}
|
||||
|
||||
/// Emit the coverage mapping data which maps the regions of
|
||||
/// code to counters that will be used to find the execution
|
||||
|
@ -1797,6 +1797,9 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
|
||||
} else if (k == BuiltinType::Float || k == BuiltinType::Double ||
|
||||
k == BuiltinType::Float16 || k == BuiltinType::BFloat16) {
|
||||
Current = SSE;
|
||||
} else if (k == BuiltinType::Float128) {
|
||||
Lo = SSE;
|
||||
Hi = SSEUp;
|
||||
} else if (k == BuiltinType::LongDouble) {
|
||||
const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
|
||||
if (LDF == &llvm::APFloat::IEEEquad()) {
|
||||
|
@ -1430,6 +1430,17 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
|
||||
const ToolChain &TC = getToolChain(
|
||||
*UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));
|
||||
|
||||
if (TC.getTriple().isAndroid()) {
|
||||
llvm::Triple Triple = TC.getTriple();
|
||||
StringRef TripleVersionName = Triple.getEnvironmentVersionString();
|
||||
|
||||
if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "") {
|
||||
Diags.Report(diag::err_drv_triple_version_invalid)
|
||||
<< TripleVersionName << TC.getTripleString();
|
||||
ContainsError = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Report warning when arm64EC option is overridden by specified target
|
||||
if ((TC.getTriple().getArch() != llvm::Triple::aarch64 ||
|
||||
TC.getTriple().getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) &&
|
||||
|
@ -221,6 +221,7 @@ void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple,
|
||||
bool IsN64 = ABIName == "64";
|
||||
bool IsPIC = false;
|
||||
bool NonPIC = false;
|
||||
bool HasNaN2008Opt = false;
|
||||
|
||||
Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC,
|
||||
options::OPT_fpic, options::OPT_fno_pic,
|
||||
@ -285,9 +286,10 @@ void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple,
|
||||
if (Arg *A = Args.getLastArg(options::OPT_mnan_EQ)) {
|
||||
StringRef Val = StringRef(A->getValue());
|
||||
if (Val == "2008") {
|
||||
if (mips::getIEEE754Standard(CPUName) & mips::Std2008)
|
||||
if (mips::getIEEE754Standard(CPUName) & mips::Std2008) {
|
||||
Features.push_back("+nan2008");
|
||||
else {
|
||||
HasNaN2008Opt = true;
|
||||
} else {
|
||||
Features.push_back("-nan2008");
|
||||
D.Diag(diag::warn_target_unsupported_nan2008) << CPUName;
|
||||
}
|
||||
@ -323,6 +325,8 @@ void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple,
|
||||
D.Diag(diag::err_drv_unsupported_option_argument)
|
||||
<< A->getSpelling() << Val;
|
||||
}
|
||||
} else if (HasNaN2008Opt) {
|
||||
Features.push_back("+abs2008");
|
||||
}
|
||||
|
||||
AddTargetFeature(Args, Features, options::OPT_msingle_float,
|
||||
|
@ -42,9 +42,9 @@ static bool getArchFeatures(const Driver &D, StringRef Arch,
|
||||
return false;
|
||||
}
|
||||
|
||||
(*ISAInfo)->toFeatures(
|
||||
Features, [&Args](const Twine &Str) { return Args.MakeArgString(Str); },
|
||||
/*AddAllExtensions=*/true);
|
||||
for (const std::string &Str : (*ISAInfo)->toFeatures(/*AddAllExtension=*/true,
|
||||
/*IgnoreUnknown=*/false))
|
||||
Features.push_back(Args.MakeArgString(Str));
|
||||
|
||||
if (EnableExperimentalExtensions)
|
||||
Features.push_back(Args.MakeArgString("+experimental"));
|
||||
|
@ -293,9 +293,8 @@ void BareMetal::addClangTargetOptions(const ArgList &DriverArgs,
|
||||
|
||||
void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
|
||||
ArgStringList &CC1Args) const {
|
||||
if (DriverArgs.hasArg(options::OPT_nostdinc) ||
|
||||
DriverArgs.hasArg(options::OPT_nostdlibinc) ||
|
||||
DriverArgs.hasArg(options::OPT_nostdincxx))
|
||||
if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc,
|
||||
options::OPT_nostdincxx))
|
||||
return;
|
||||
|
||||
const Driver &D = getDriver();
|
||||
|
@ -698,6 +698,17 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C,
|
||||
CmdArgs.push_back("-fcoverage-mapping");
|
||||
}
|
||||
|
||||
if (Args.hasFlag(options::OPT_fmcdc_coverage, options::OPT_fno_mcdc_coverage,
|
||||
false)) {
|
||||
if (!Args.hasFlag(options::OPT_fcoverage_mapping,
|
||||
options::OPT_fno_coverage_mapping, false))
|
||||
D.Diag(clang::diag::err_drv_argument_only_allowed_with)
|
||||
<< "-fcoverage-mcdc"
|
||||
<< "-fcoverage-mapping";
|
||||
|
||||
CmdArgs.push_back("-fcoverage-mcdc");
|
||||
}
|
||||
|
||||
if (Arg *A = Args.getLastArg(options::OPT_ffile_compilation_dir_EQ,
|
||||
options::OPT_fcoverage_compilation_dir_EQ)) {
|
||||
if (A->getOption().matches(options::OPT_ffile_compilation_dir_EQ))
|
||||
|
@ -2251,6 +2251,15 @@ void Generic_GCC::GCCInstallationDetector::init(
|
||||
return;
|
||||
}
|
||||
|
||||
// If --gcc-triple is specified use this instead of trying to
|
||||
// auto-detect a triple.
|
||||
if (const Arg *A =
|
||||
Args.getLastArg(clang::driver::options::OPT_gcc_triple_EQ)) {
|
||||
StringRef GCCTriple = A->getValue();
|
||||
CandidateTripleAliases.clear();
|
||||
CandidateTripleAliases.push_back(GCCTriple);
|
||||
}
|
||||
|
||||
// Compute the set of prefixes for our search.
|
||||
SmallVector<std::string, 8> Prefixes;
|
||||
StringRef GCCToolchainDir = getGCCToolchainDir(Args, D.SysRoot);
|
||||
|
@ -471,12 +471,23 @@ findClangRelativeSysroot(const Driver &D, const llvm::Triple &LiteralTriple,
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
static bool looksLikeMinGWSysroot(const std::string &Directory) {
|
||||
StringRef Sep = llvm::sys::path::get_separator();
|
||||
if (!llvm::sys::fs::exists(Directory + Sep + "include" + Sep + "_mingw.h"))
|
||||
return false;
|
||||
if (!llvm::sys::fs::exists(Directory + Sep + "lib" + Sep + "libkernel32.a"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
toolchains::MinGW::MinGW(const Driver &D, const llvm::Triple &Triple,
|
||||
const ArgList &Args)
|
||||
: ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args),
|
||||
RocmInstallation(D, Triple, Args) {
|
||||
getProgramPaths().push_back(getDriver().getInstalledDir());
|
||||
|
||||
std::string InstallBase =
|
||||
std::string(llvm::sys::path::parent_path(getDriver().getInstalledDir()));
|
||||
// The sequence for detecting a sysroot here should be kept in sync with
|
||||
// the testTriple function below.
|
||||
llvm::Triple LiteralTriple = getLiteralTriple(D, getTriple());
|
||||
@ -487,13 +498,17 @@ toolchains::MinGW::MinGW(const Driver &D, const llvm::Triple &Triple,
|
||||
else if (llvm::ErrorOr<std::string> TargetSubdir = findClangRelativeSysroot(
|
||||
getDriver(), LiteralTriple, getTriple(), SubdirName))
|
||||
Base = std::string(llvm::sys::path::parent_path(TargetSubdir.get()));
|
||||
// If the install base of Clang seems to have mingw sysroot files directly
|
||||
// in the toplevel include and lib directories, use this as base instead of
|
||||
// looking for a triple prefixed GCC in the path.
|
||||
else if (looksLikeMinGWSysroot(InstallBase))
|
||||
Base = InstallBase;
|
||||
else if (llvm::ErrorOr<std::string> GPPName =
|
||||
findGcc(LiteralTriple, getTriple()))
|
||||
Base = std::string(llvm::sys::path::parent_path(
|
||||
llvm::sys::path::parent_path(GPPName.get())));
|
||||
else
|
||||
Base = std::string(
|
||||
llvm::sys::path::parent_path(getDriver().getInstalledDir()));
|
||||
Base = InstallBase;
|
||||
|
||||
Base += llvm::sys::path::get_separator();
|
||||
findGccLibDir(LiteralTriple);
|
||||
@ -778,9 +793,15 @@ static bool testTriple(const Driver &D, const llvm::Triple &Triple,
|
||||
if (D.SysRoot.size())
|
||||
return true;
|
||||
llvm::Triple LiteralTriple = getLiteralTriple(D, Triple);
|
||||
std::string InstallBase =
|
||||
std::string(llvm::sys::path::parent_path(D.getInstalledDir()));
|
||||
if (llvm::ErrorOr<std::string> TargetSubdir =
|
||||
findClangRelativeSysroot(D, LiteralTriple, Triple, SubdirName))
|
||||
return true;
|
||||
// If the install base itself looks like a mingw sysroot, we'll use that
|
||||
// - don't use any potentially unrelated gcc to influence what triple to use.
|
||||
if (looksLikeMinGWSysroot(InstallBase))
|
||||
return false;
|
||||
if (llvm::ErrorOr<std::string> GPPName = findGcc(LiteralTriple, Triple))
|
||||
return true;
|
||||
// If we neither found a colocated sysroot or a matching gcc executable,
|
||||
|
@ -5151,6 +5151,14 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
|
||||
return true;
|
||||
if (Left.IsUnterminatedLiteral)
|
||||
return true;
|
||||
// FIXME: Breaking after newlines seems useful in general. Turn this into an
|
||||
// option and recognize more cases like endl etc, and break independent of
|
||||
// what comes after operator lessless.
|
||||
if (Right.is(tok::lessless) && Right.Next &&
|
||||
Right.Next->is(tok::string_literal) && Left.is(tok::string_literal) &&
|
||||
Left.TokenText.ends_with("\\n\"")) {
|
||||
return true;
|
||||
}
|
||||
if (Right.is(TT_RequiresClause)) {
|
||||
switch (Style.RequiresClausePosition) {
|
||||
case FormatStyle::RCPS_OwnLine:
|
||||
|
@ -756,6 +756,65 @@ __arm_st64bv0(void *__addr, data512_t __value) {
|
||||
__builtin_arm_mops_memset_tag(__tagged_address, __value, __size)
|
||||
#endif
|
||||
|
||||
/* Coprocessor Intrinsics */
|
||||
#if defined(__ARM_FEATURE_COPROC)
|
||||
|
||||
#if (__ARM_FEATURE_COPROC & 0x1)
|
||||
|
||||
#if (__ARM_ARCH < 8)
|
||||
#define __arm_cdp(coproc, opc1, CRd, CRn, CRm, opc2) \
|
||||
__builtin_arm_cdp(coproc, opc1, CRd, CRn, CRm, opc2)
|
||||
#endif /* __ARM_ARCH < 8 */
|
||||
|
||||
#define __arm_ldc(coproc, CRd, p) __builtin_arm_ldc(coproc, CRd, p)
|
||||
#define __arm_stc(coproc, CRd, p) __builtin_arm_stc(coproc, CRd, p)
|
||||
|
||||
#define __arm_mcr(coproc, opc1, value, CRn, CRm, opc2) \
|
||||
__builtin_arm_mcr(coproc, opc1, value, CRn, CRm, opc2)
|
||||
#define __arm_mrc(coproc, opc1, CRn, CRm, opc2) \
|
||||
__builtin_arm_mrc(coproc, opc1, CRn, CRm, opc2)
|
||||
|
||||
#if (__ARM_ARCH != 4) && (__ARM_ARCH < 8)
|
||||
#define __arm_ldcl(coproc, CRd, p) __builtin_arm_ldcl(coproc, CRd, p)
|
||||
#define __arm_stcl(coproc, CRd, p) __builtin_arm_stcl(coproc, CRd, p)
|
||||
#endif /* (__ARM_ARCH != 4) && (__ARM_ARCH != 8) */
|
||||
|
||||
#if (__ARM_ARCH_8M_MAIN__) || (__ARM_ARCH_8_1M_MAIN__)
|
||||
#define __arm_cdp(coproc, opc1, CRd, CRn, CRm, opc2) \
|
||||
__builtin_arm_cdp(coproc, opc1, CRd, CRn, CRm, opc2)
|
||||
#define __arm_ldcl(coproc, CRd, p) __builtin_arm_ldcl(coproc, CRd, p)
|
||||
#define __arm_stcl(coproc, CRd, p) __builtin_arm_stcl(coproc, CRd, p)
|
||||
#endif /* ___ARM_ARCH_8M_MAIN__ */
|
||||
|
||||
#endif /* __ARM_FEATURE_COPROC & 0x1 */
|
||||
|
||||
#if (__ARM_FEATURE_COPROC & 0x2)
|
||||
#define __arm_cdp2(coproc, opc1, CRd, CRn, CRm, opc2) \
|
||||
__builtin_arm_cdp2(coproc, opc1, CRd, CRn, CRm, opc2)
|
||||
#define __arm_ldc2(coproc, CRd, p) __builtin_arm_ldc2(coproc, CRd, p)
|
||||
#define __arm_stc2(coproc, CRd, p) __builtin_arm_stc2(coproc, CRd, p)
|
||||
#define __arm_ldc2l(coproc, CRd, p) __builtin_arm_ldc2l(coproc, CRd, p)
|
||||
#define __arm_stc2l(coproc, CRd, p) __builtin_arm_stc2l(coproc, CRd, p)
|
||||
#define __arm_mcr2(coproc, opc1, value, CRn, CRm, opc2) \
|
||||
__builtin_arm_mcr2(coproc, opc1, value, CRn, CRm, opc2)
|
||||
#define __arm_mrc2(coproc, opc1, CRn, CRm, opc2) \
|
||||
__builtin_arm_mrc2(coproc, opc1, CRn, CRm, opc2)
|
||||
#endif
|
||||
|
||||
#if (__ARM_FEATURE_COPROC & 0x4)
|
||||
#define __arm_mcrr(coproc, opc1, value, CRm) \
|
||||
__builtin_arm_mcrr(coproc, opc1, value, CRm)
|
||||
#define __arm_mrrc(coproc, opc1, CRm) __builtin_arm_mrrc(coproc, opc1, CRm)
|
||||
#endif
|
||||
|
||||
#if (__ARM_FEATURE_COPROC & 0x8)
|
||||
#define __arm_mcrr2(coproc, opc1, value, CRm) \
|
||||
__builtin_arm_mcrr2(coproc, opc1, value, CRm)
|
||||
#define __arm_mrrc2(coproc, opc1, CRm) __builtin_arm_mrrc2(coproc, opc1, CRm)
|
||||
#endif
|
||||
|
||||
#endif // __ARM_FEATURE_COPROC
|
||||
|
||||
/* Transactional Memory Extension (TME) Intrinsics */
|
||||
#if defined(__ARM_FEATURE_TME) && __ARM_FEATURE_TME
|
||||
|
||||
|
@ -6,15 +6,41 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
|
||||
#define __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
|
||||
|
||||
#if !defined(_OPENMP) && !defined(__HIP__) && !defined(__CUDA__)
|
||||
#error "This file is for GPU offloading compilation only"
|
||||
#endif
|
||||
|
||||
#include_next <stdio.h>
|
||||
|
||||
// In some old versions of glibc, other standard headers sometimes define
|
||||
// special macros (e.g., __need_FILE) before including stdio.h to cause stdio.h
|
||||
// to produce special definitions. Future includes of stdio.h when those
|
||||
// special macros are undefined are expected to produce the normal definitions
|
||||
// from stdio.h.
|
||||
//
|
||||
// We do not apply our include guard (__CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__)
|
||||
// unconditionally to the above include_next. Otherwise, after an occurrence of
|
||||
// the first glibc stdio.h use case described above, the include_next would be
|
||||
// skipped for remaining includes of stdio.h, leaving required symbols
|
||||
// undefined.
|
||||
//
|
||||
// We make the following assumptions to handle all use cases:
|
||||
//
|
||||
// 1. If the above include_next produces special glibc definitions, then (a) it
|
||||
// does not produce the normal definitions that we must intercept below, (b)
|
||||
// the current file was included from a glibc header that already defined
|
||||
// __GLIBC__ (usually by including glibc's <features.h>), and (c) the above
|
||||
// include_next does not define _STDIO_H. In that case, we skip the rest of
|
||||
// the current file and don't guard against future includes.
|
||||
// 2. If the above include_next produces the normal stdio.h definitions, then
|
||||
// either (a) __GLIBC__ is not defined because C headers are from some other
|
||||
// libc implementation or (b) the above include_next defines _STDIO_H to
|
||||
// prevent the above include_next from having any effect in the future.
|
||||
#if !defined(__GLIBC__) || defined(_STDIO_H)
|
||||
|
||||
#ifndef __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
|
||||
#define __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
|
||||
|
||||
#if __has_include(<llvm-libc-decls/stdio.h>)
|
||||
|
||||
#if defined(__HIP__) || defined(__CUDA__)
|
||||
@ -50,3 +76,5 @@
|
||||
#endif
|
||||
|
||||
#endif // __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
|
||||
|
||||
#endif
|
||||
|
@ -984,7 +984,9 @@ static void inferFrameworkLink(Module *Mod) {
|
||||
assert(!Mod->isSubFramework() &&
|
||||
"Can only infer linking for top-level frameworks");
|
||||
|
||||
Mod->LinkLibraries.push_back(Module::LinkLibrary(Mod->Name,
|
||||
StringRef FrameworkName(Mod->Name);
|
||||
FrameworkName.consume_back("_Private");
|
||||
Mod->LinkLibraries.push_back(Module::LinkLibrary(FrameworkName.str(),
|
||||
/*IsFramework=*/true));
|
||||
}
|
||||
|
||||
|
@ -3483,7 +3483,8 @@ void Parser::ParseDeclarationSpecifiers(
|
||||
|
||||
case tok::coloncolon: // ::foo::bar
|
||||
// C++ scope specifier. Annotate and loop, or bail out on error.
|
||||
if (TryAnnotateCXXScopeToken(EnteringContext)) {
|
||||
if (getLangOpts().CPlusPlus &&
|
||||
TryAnnotateCXXScopeToken(EnteringContext)) {
|
||||
if (!DS.hasTypeSpecifier())
|
||||
DS.SetTypeSpecError();
|
||||
goto DoneWithDeclSpec;
|
||||
|
@ -2679,6 +2679,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
|
||||
ParsedAttributes &AccessAttrs,
|
||||
const ParsedTemplateInfo &TemplateInfo,
|
||||
ParsingDeclRAIIObject *TemplateDiags) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"ParseCXXClassMemberDeclaration should only be called in C++ mode");
|
||||
if (Tok.is(tok::at)) {
|
||||
if (getLangOpts().ObjC && NextToken().isObjCAtKeyword(tok::objc_defs))
|
||||
Diag(Tok, diag::err_at_defs_cxx);
|
||||
|
@ -76,16 +76,27 @@ OpenACCClauseKind getOpenACCClauseKind(Token Tok) {
|
||||
if (Tok.is(tok::kw_auto))
|
||||
return OpenACCClauseKind::Auto;
|
||||
|
||||
// default is a keyword, so make sure we parse it correctly.
|
||||
if (Tok.is(tok::kw_default))
|
||||
return OpenACCClauseKind::Default;
|
||||
|
||||
// if is also a keyword, make sure we parse it correctly.
|
||||
if (Tok.is(tok::kw_if))
|
||||
return OpenACCClauseKind::If;
|
||||
|
||||
if (!Tok.is(tok::identifier))
|
||||
return OpenACCClauseKind::Invalid;
|
||||
|
||||
return llvm::StringSwitch<OpenACCClauseKind>(
|
||||
Tok.getIdentifierInfo()->getName())
|
||||
.Case("auto", OpenACCClauseKind::Auto)
|
||||
.Case("default", OpenACCClauseKind::Default)
|
||||
.Case("finalize", OpenACCClauseKind::Finalize)
|
||||
.Case("if", OpenACCClauseKind::If)
|
||||
.Case("if_present", OpenACCClauseKind::IfPresent)
|
||||
.Case("independent", OpenACCClauseKind::Independent)
|
||||
.Case("nohost", OpenACCClauseKind::NoHost)
|
||||
.Case("self", OpenACCClauseKind::Self)
|
||||
.Case("seq", OpenACCClauseKind::Seq)
|
||||
.Case("vector", OpenACCClauseKind::Vector)
|
||||
.Case("worker", OpenACCClauseKind::Worker)
|
||||
@ -106,6 +117,17 @@ OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) {
|
||||
.Default(OpenACCAtomicKind::Invalid);
|
||||
}
|
||||
|
||||
OpenACCDefaultClauseKind getOpenACCDefaultClauseKind(Token Tok) {
|
||||
if (!Tok.is(tok::identifier))
|
||||
return OpenACCDefaultClauseKind::Invalid;
|
||||
|
||||
return llvm::StringSwitch<OpenACCDefaultClauseKind>(
|
||||
Tok.getIdentifierInfo()->getName())
|
||||
.Case("none", OpenACCDefaultClauseKind::None)
|
||||
.Case("present", OpenACCDefaultClauseKind::Present)
|
||||
.Default(OpenACCDefaultClauseKind::Invalid);
|
||||
}
|
||||
|
||||
enum class OpenACCSpecialTokenKind {
|
||||
ReadOnly,
|
||||
DevNum,
|
||||
@ -176,6 +198,22 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) {
|
||||
llvm_unreachable("Unknown 'Kind' Passed");
|
||||
}
|
||||
|
||||
/// Used for cases where we expect an identifier-like token, but don't want to
|
||||
/// give awkward error messages in cases where it is accidentially a keyword.
|
||||
bool expectIdentifierOrKeyword(Parser &P) {
|
||||
Token Tok = P.getCurToken();
|
||||
|
||||
if (Tok.is(tok::identifier))
|
||||
return false;
|
||||
|
||||
if (!Tok.isAnnotation() && Tok.getIdentifierInfo() &&
|
||||
Tok.getIdentifierInfo()->isKeyword(P.getLangOpts()))
|
||||
return false;
|
||||
|
||||
P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenACCDirectiveKind
|
||||
ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
|
||||
OpenACCDirectiveKindEx ExtDirKind) {
|
||||
@ -291,14 +329,94 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
|
||||
return DirKind;
|
||||
}
|
||||
|
||||
bool ClauseHasOptionalParens(OpenACCClauseKind Kind) {
|
||||
return Kind == OpenACCClauseKind::Self;
|
||||
}
|
||||
|
||||
bool ClauseHasRequiredParens(OpenACCClauseKind Kind) {
|
||||
return Kind == OpenACCClauseKind::Default || Kind == OpenACCClauseKind::If;
|
||||
}
|
||||
|
||||
ExprResult ParseOpenACCConditionalExpr(Parser &P) {
|
||||
// FIXME: It isn't clear if the spec saying 'condition' means the same as
|
||||
// it does in an if/while/etc (See ParseCXXCondition), however as it was
|
||||
// written with Fortran/C in mind, we're going to assume it just means an
|
||||
// 'expression evaluating to boolean'.
|
||||
return P.getActions().CorrectDelayedTyposInExpr(P.ParseExpression());
|
||||
}
|
||||
|
||||
bool ParseOpenACCClauseParams(Parser &P, OpenACCClauseKind Kind) {
|
||||
BalancedDelimiterTracker Parens(P, tok::l_paren,
|
||||
tok::annot_pragma_openacc_end);
|
||||
|
||||
if (ClauseHasRequiredParens(Kind)) {
|
||||
if (Parens.expectAndConsume()) {
|
||||
// We are missing a paren, so assume that the person just forgot the
|
||||
// parameter. Return 'false' so we try to continue on and parse the next
|
||||
// clause.
|
||||
P.SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end,
|
||||
Parser::StopBeforeMatch);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (Kind) {
|
||||
case OpenACCClauseKind::Default: {
|
||||
Token DefKindTok = P.getCurToken();
|
||||
|
||||
if (expectIdentifierOrKeyword(P))
|
||||
break;
|
||||
|
||||
P.ConsumeToken();
|
||||
|
||||
if (getOpenACCDefaultClauseKind(DefKindTok) ==
|
||||
OpenACCDefaultClauseKind::Invalid)
|
||||
P.Diag(DefKindTok, diag::err_acc_invalid_default_clause_kind);
|
||||
|
||||
break;
|
||||
}
|
||||
case OpenACCClauseKind::If: {
|
||||
ExprResult CondExpr = ParseOpenACCConditionalExpr(P);
|
||||
// An invalid expression can be just about anything, so just give up on
|
||||
// this clause list.
|
||||
if (CondExpr.isInvalid())
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Not a required parens type?");
|
||||
}
|
||||
|
||||
return Parens.consumeClose();
|
||||
} else if (ClauseHasOptionalParens(Kind)) {
|
||||
if (!Parens.consumeOpen()) {
|
||||
switch (Kind) {
|
||||
case OpenACCClauseKind::Self: {
|
||||
ExprResult CondExpr = ParseOpenACCConditionalExpr(P);
|
||||
// An invalid expression can be just about anything, so just give up on
|
||||
// this clause list.
|
||||
if (CondExpr.isInvalid())
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Not an optional parens type?");
|
||||
}
|
||||
Parens.consumeClose();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The OpenACC Clause List is a comma or space-delimited list of clauses (see
|
||||
// the comment on ParseOpenACCClauseList). The concept of a 'clause' doesn't
|
||||
// really have its owner grammar and each individual one has its own definition.
|
||||
// However, they all are named with a single-identifier (or auto!) token,
|
||||
// followed in some cases by either braces or parens.
|
||||
// However, they all are named with a single-identifier (or auto/default!)
|
||||
// token, followed in some cases by either braces or parens.
|
||||
bool ParseOpenACCClause(Parser &P) {
|
||||
if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto))
|
||||
return P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;
|
||||
// A number of clause names are actually keywords, so accept a keyword that
|
||||
// can be converted to a name.
|
||||
if (expectIdentifierOrKeyword(P))
|
||||
return true;
|
||||
|
||||
OpenACCClauseKind Kind = getOpenACCClauseKind(P.getCurToken());
|
||||
|
||||
@ -309,8 +427,7 @@ bool ParseOpenACCClause(Parser &P) {
|
||||
// Consume the clause name.
|
||||
P.ConsumeToken();
|
||||
|
||||
// FIXME: For future clauses, we need to handle parens/etc below.
|
||||
return false;
|
||||
return ParseOpenACCClauseParams(P, Kind);
|
||||
}
|
||||
|
||||
// Skip until we see the end of pragma token, but don't consume it. This is us
|
||||
|
@ -2226,8 +2226,8 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
|
||||
UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions)
|
||||
: S(S), SuggestSuggestions(SuggestSuggestions) {}
|
||||
|
||||
void handleUnsafeOperation(const Stmt *Operation,
|
||||
bool IsRelatedToDecl) override {
|
||||
void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl,
|
||||
ASTContext &Ctx) override {
|
||||
SourceLocation Loc;
|
||||
SourceRange Range;
|
||||
unsigned MsgParam = 0;
|
||||
@ -2261,6 +2261,18 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
|
||||
// note_unsafe_buffer_operation doesn't have this mode yet.
|
||||
assert(!IsRelatedToDecl && "Not implemented yet!");
|
||||
MsgParam = 3;
|
||||
} else if (const auto *ECE = dyn_cast<ExplicitCastExpr>(Operation)) {
|
||||
QualType destType = ECE->getType();
|
||||
const uint64_t dSize =
|
||||
Ctx.getTypeSize(destType.getTypePtr()->getPointeeType());
|
||||
if (const auto *CE = dyn_cast<CXXMemberCallExpr>(ECE->getSubExpr())) {
|
||||
QualType srcType = CE->getType();
|
||||
const uint64_t sSize =
|
||||
Ctx.getTypeSize(srcType.getTypePtr()->getPointeeType());
|
||||
if (sSize >= dSize)
|
||||
return;
|
||||
}
|
||||
MsgParam = 4;
|
||||
}
|
||||
Loc = Operation->getBeginLoc();
|
||||
Range = Operation->getSourceRange();
|
||||
|
@ -2998,7 +2998,12 @@ static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context,
|
||||
llvm_unreachable("Invalid NeonTypeFlag!");
|
||||
}
|
||||
|
||||
enum ArmStreamingType { ArmNonStreaming, ArmStreaming, ArmStreamingCompatible };
|
||||
enum ArmStreamingType {
|
||||
ArmNonStreaming,
|
||||
ArmStreaming,
|
||||
ArmStreamingCompatible,
|
||||
ArmStreamingOrSVE2p1
|
||||
};
|
||||
|
||||
bool Sema::ParseSVEImmChecks(
|
||||
CallExpr *TheCall, SmallVector<std::tuple<int, int, int>, 3> &ImmChecks) {
|
||||
@ -3156,6 +3161,16 @@ static void checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall,
|
||||
const FunctionDecl *FD,
|
||||
ArmStreamingType BuiltinType) {
|
||||
ArmStreamingType FnType = getArmStreamingFnType(FD);
|
||||
if (BuiltinType == ArmStreamingOrSVE2p1) {
|
||||
// Check intrinsics that are available in [sve2p1 or sme/sme2].
|
||||
llvm::StringMap<bool> CallerFeatureMap;
|
||||
S.Context.getFunctionFeatureMap(CallerFeatureMap, FD);
|
||||
if (Builtin::evaluateRequiredTargetFeatures("sve2p1", CallerFeatureMap))
|
||||
BuiltinType = ArmStreamingCompatible;
|
||||
else
|
||||
BuiltinType = ArmStreaming;
|
||||
}
|
||||
|
||||
if (FnType == ArmStreaming && BuiltinType == ArmNonStreaming) {
|
||||
S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin)
|
||||
<< TheCall->getSourceRange() << "streaming";
|
||||
@ -16677,7 +16692,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
|
||||
/// Have we issued a diagnostic for this object already?
|
||||
bool Diagnosed = false;
|
||||
|
||||
UsageInfo() = default;
|
||||
UsageInfo();
|
||||
};
|
||||
using UsageInfoMap = llvm::SmallDenseMap<Object, UsageInfo, 16>;
|
||||
|
||||
@ -17436,6 +17451,8 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
|
||||
}
|
||||
};
|
||||
|
||||
SequenceChecker::UsageInfo::UsageInfo() = default;
|
||||
|
||||
} // namespace
|
||||
|
||||
void Sema::CheckUnsequencedOperations(const Expr *E) {
|
||||
@ -18359,7 +18376,7 @@ static bool isSetterLikeSelector(Selector sel) {
|
||||
if (sel.isUnarySelector()) return false;
|
||||
|
||||
StringRef str = sel.getNameForSlot(0);
|
||||
while (!str.empty() && str.front() == '_') str = str.substr(1);
|
||||
str = str.ltrim('_');
|
||||
if (str.starts_with("set"))
|
||||
str = str.substr(3);
|
||||
else if (str.starts_with("add")) {
|
||||
|
@ -771,10 +771,9 @@ namespace {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static const Expr *
|
||||
SubstituteConstraintExpression(Sema &S,
|
||||
const Sema::TemplateCompareNewDeclInfo &DeclInfo,
|
||||
const Expr *ConstrExpr) {
|
||||
static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
|
||||
Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
|
||||
const Expr *ConstrExpr) {
|
||||
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
|
||||
DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false,
|
||||
/*Innermost=*/nullptr,
|
||||
@ -797,8 +796,8 @@ SubstituteConstraintExpression(Sema &S,
|
||||
std::optional<Sema::CXXThisScopeRAII> ThisScope;
|
||||
if (auto *RD = dyn_cast<CXXRecordDecl>(DeclInfo.getDeclContext()))
|
||||
ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
|
||||
ExprResult SubstConstr =
|
||||
S.SubstConstraintExpr(const_cast<clang::Expr *>(ConstrExpr), MLTAL);
|
||||
ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction(
|
||||
const_cast<clang::Expr *>(ConstrExpr), MLTAL);
|
||||
if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
|
||||
return nullptr;
|
||||
return SubstConstr.get();
|
||||
@ -814,12 +813,14 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
|
||||
if (Old && !New.isInvalid() && !New.ContainsDecl(Old) &&
|
||||
Old->getLexicalDeclContext() != New.getLexicalDeclContext()) {
|
||||
if (const Expr *SubstConstr =
|
||||
SubstituteConstraintExpression(*this, Old, OldConstr))
|
||||
SubstituteConstraintExpressionWithoutSatisfaction(*this, Old,
|
||||
OldConstr))
|
||||
OldConstr = SubstConstr;
|
||||
else
|
||||
return false;
|
||||
if (const Expr *SubstConstr =
|
||||
SubstituteConstraintExpression(*this, New, NewConstr))
|
||||
SubstituteConstraintExpressionWithoutSatisfaction(*this, New,
|
||||
NewConstr))
|
||||
NewConstr = SubstConstr;
|
||||
else
|
||||
return false;
|
||||
|
@ -9900,15 +9900,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
// Match up the template parameter lists with the scope specifier, then
|
||||
// determine whether we have a template or a template specialization.
|
||||
bool Invalid = false;
|
||||
TemplateIdAnnotation *TemplateId =
|
||||
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
|
||||
? D.getName().TemplateId
|
||||
: nullptr;
|
||||
TemplateParameterList *TemplateParams =
|
||||
MatchTemplateParametersToScopeSpecifier(
|
||||
D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
|
||||
D.getCXXScopeSpec(),
|
||||
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
|
||||
? D.getName().TemplateId
|
||||
: nullptr,
|
||||
TemplateParamLists, isFriend, isMemberSpecialization,
|
||||
Invalid);
|
||||
D.getCXXScopeSpec(), TemplateId, TemplateParamLists, isFriend,
|
||||
isMemberSpecialization, Invalid);
|
||||
if (TemplateParams) {
|
||||
// Check that we can declare a template here.
|
||||
if (CheckTemplateDeclScope(S, TemplateParams))
|
||||
@ -9921,6 +9921,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
if (Name.getNameKind() == DeclarationName::CXXDestructorName) {
|
||||
Diag(NewFD->getLocation(), diag::err_destructor_template);
|
||||
NewFD->setInvalidDecl();
|
||||
// Function template with explicit template arguments.
|
||||
} else if (TemplateId) {
|
||||
Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec)
|
||||
<< SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc);
|
||||
NewFD->setInvalidDecl();
|
||||
}
|
||||
|
||||
// If we're adding a template to a dependent context, we may need to
|
||||
@ -9973,6 +9978,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
<< FixItHint::CreateRemoval(RemoveRange)
|
||||
<< FixItHint::CreateInsertion(InsertLoc, "<>");
|
||||
Invalid = true;
|
||||
|
||||
// Recover by faking up an empty template argument list.
|
||||
HasExplicitTemplateArgs = true;
|
||||
TemplateArgs.setLAngleLoc(InsertLoc);
|
||||
TemplateArgs.setRAngleLoc(InsertLoc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -9986,6 +9996,33 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
if (TemplateParamLists.size() > 0)
|
||||
// For source fidelity, store all the template param lists.
|
||||
NewFD->setTemplateParameterListsInfo(Context, TemplateParamLists);
|
||||
|
||||
// "friend void foo<>(int);" is an implicit specialization decl.
|
||||
if (isFriend && TemplateId)
|
||||
isFunctionTemplateSpecialization = true;
|
||||
}
|
||||
|
||||
// If this is a function template specialization and the unqualified-id of
|
||||
// the declarator-id is a template-id, convert the template argument list
|
||||
// into our AST format and check for unexpanded packs.
|
||||
if (isFunctionTemplateSpecialization && TemplateId) {
|
||||
HasExplicitTemplateArgs = true;
|
||||
|
||||
TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc);
|
||||
TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc);
|
||||
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
|
||||
TemplateId->NumArgs);
|
||||
translateTemplateArguments(TemplateArgsPtr, TemplateArgs);
|
||||
|
||||
// FIXME: Should we check for unexpanded packs if this was an (invalid)
|
||||
// declaration of a function template partial specialization? Should we
|
||||
// consider the unexpanded pack context to be a partial specialization?
|
||||
for (const TemplateArgumentLoc &ArgLoc : TemplateArgs.arguments()) {
|
||||
if (DiagnoseUnexpandedParameterPack(
|
||||
ArgLoc, isFriend ? UPPC_FriendDeclaration
|
||||
: UPPC_ExplicitSpecialization))
|
||||
NewFD->setInvalidDecl();
|
||||
}
|
||||
}
|
||||
|
||||
if (Invalid) {
|
||||
@ -10438,46 +10475,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
diag::ext_operator_new_delete_declared_inline)
|
||||
<< NewFD->getDeclName();
|
||||
|
||||
// If the declarator is a template-id, translate the parser's template
|
||||
// argument list into our AST format.
|
||||
if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
|
||||
TemplateIdAnnotation *TemplateId = D.getName().TemplateId;
|
||||
TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc);
|
||||
TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc);
|
||||
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
|
||||
TemplateId->NumArgs);
|
||||
translateTemplateArguments(TemplateArgsPtr,
|
||||
TemplateArgs);
|
||||
|
||||
HasExplicitTemplateArgs = true;
|
||||
|
||||
if (NewFD->isInvalidDecl()) {
|
||||
HasExplicitTemplateArgs = false;
|
||||
} else if (FunctionTemplate) {
|
||||
// Function template with explicit template arguments.
|
||||
Diag(D.getIdentifierLoc(), diag::err_function_template_partial_spec)
|
||||
<< SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc);
|
||||
|
||||
HasExplicitTemplateArgs = false;
|
||||
} else if (isFriend) {
|
||||
// "friend void foo<>(int);" is an implicit specialization decl.
|
||||
isFunctionTemplateSpecialization = true;
|
||||
} else {
|
||||
assert(isFunctionTemplateSpecialization &&
|
||||
"should have a 'template<>' for this decl");
|
||||
}
|
||||
} else if (isFriend && isFunctionTemplateSpecialization) {
|
||||
// This combination is only possible in a recovery case; the user
|
||||
// wrote something like:
|
||||
// template <> friend void foo(int);
|
||||
// which we're recovering from as if the user had written:
|
||||
// friend void foo<>(int);
|
||||
// Go ahead and fake up a template id.
|
||||
HasExplicitTemplateArgs = true;
|
||||
TemplateArgs.setLAngleLoc(D.getIdentifierLoc());
|
||||
TemplateArgs.setRAngleLoc(D.getIdentifierLoc());
|
||||
}
|
||||
|
||||
// We do not add HD attributes to specializations here because
|
||||
// they may have different constexpr-ness compared to their
|
||||
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
|
||||
@ -15845,8 +15842,6 @@ static void diagnoseImplicitlyRetainedSelf(Sema &S) {
|
||||
}
|
||||
|
||||
void Sema::CheckCoroutineWrapper(FunctionDecl *FD) {
|
||||
if (!FD)
|
||||
return;
|
||||
RecordDecl *RD = FD->getReturnType()->getAsRecordDecl();
|
||||
if (!RD || !RD->getUnderlyingDecl()->hasAttr<CoroReturnTypeAttr>())
|
||||
return;
|
||||
@ -15869,7 +15864,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
|
||||
sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
|
||||
sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr;
|
||||
|
||||
if (getLangOpts().Coroutines) {
|
||||
// If we skip function body, we can't tell if a function is a coroutine.
|
||||
if (getLangOpts().Coroutines && FD && !FD->hasSkippedBody()) {
|
||||
if (FSI->isCoroutine())
|
||||
CheckCompletedCoroutineBody(FD, Body);
|
||||
else
|
||||
|
@ -3369,6 +3369,22 @@ static void handleSectionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
}
|
||||
}
|
||||
|
||||
static void handleCodeModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
StringRef Str;
|
||||
SourceLocation LiteralLoc;
|
||||
// Check that it is a string.
|
||||
if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &LiteralLoc))
|
||||
return;
|
||||
|
||||
llvm::CodeModel::Model CM;
|
||||
if (!CodeModelAttr::ConvertStrToModel(Str, CM)) {
|
||||
S.Diag(LiteralLoc, diag::err_attr_codemodel_arg) << Str;
|
||||
return;
|
||||
}
|
||||
|
||||
D->addAttr(::new (S.Context) CodeModelAttr(S.Context, AL, CM));
|
||||
}
|
||||
|
||||
// This is used for `__declspec(code_seg("segname"))` on a decl.
|
||||
// `#pragma code_seg("segname")` uses checkSectionName() instead.
|
||||
static bool checkCodeSegName(Sema &S, SourceLocation LiteralLoc,
|
||||
@ -9253,6 +9269,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
|
||||
case ParsedAttr::AT_Section:
|
||||
handleSectionAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_CodeModel:
|
||||
handleCodeModelAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_RandomizeLayout:
|
||||
handleRandomizeLayoutAttr(S, D, AL);
|
||||
break;
|
||||
|
@ -8691,10 +8691,10 @@ ExprResult Sema::ActOnParenListExpr(SourceLocation L,
|
||||
/// Emit a specialized diagnostic when one expression is a null pointer
|
||||
/// constant and the other is not a pointer. Returns true if a diagnostic is
|
||||
/// emitted.
|
||||
bool Sema::DiagnoseConditionalForNull(Expr *LHSExpr, Expr *RHSExpr,
|
||||
bool Sema::DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr,
|
||||
SourceLocation QuestionLoc) {
|
||||
Expr *NullExpr = LHSExpr;
|
||||
Expr *NonPointerExpr = RHSExpr;
|
||||
const Expr *NullExpr = LHSExpr;
|
||||
const Expr *NonPointerExpr = RHSExpr;
|
||||
Expr::NullPointerConstantKind NullKind =
|
||||
NullExpr->isNullPointerConstant(Context,
|
||||
Expr::NPC_ValueDependentIsNotNull);
|
||||
@ -8730,7 +8730,8 @@ bool Sema::DiagnoseConditionalForNull(Expr *LHSExpr, Expr *RHSExpr,
|
||||
}
|
||||
|
||||
/// Return false if the condition expression is valid, true otherwise.
|
||||
static bool checkCondition(Sema &S, Expr *Cond, SourceLocation QuestionLoc) {
|
||||
static bool checkCondition(Sema &S, const Expr *Cond,
|
||||
SourceLocation QuestionLoc) {
|
||||
QualType CondTy = Cond->getType();
|
||||
|
||||
// OpenCL v1.1 s6.3.i says the condition cannot be a floating point type.
|
||||
@ -9542,28 +9543,27 @@ static bool IsArithmeticOp(BinaryOperatorKind Opc) {
|
||||
/// expression, either using a built-in or overloaded operator,
|
||||
/// and sets *OpCode to the opcode and *RHSExprs to the right-hand side
|
||||
/// expression.
|
||||
static bool IsArithmeticBinaryExpr(Expr *E, BinaryOperatorKind *Opcode,
|
||||
Expr **RHSExprs) {
|
||||
static bool IsArithmeticBinaryExpr(const Expr *E, BinaryOperatorKind *Opcode,
|
||||
const Expr **RHSExprs) {
|
||||
// Don't strip parenthesis: we should not warn if E is in parenthesis.
|
||||
E = E->IgnoreImpCasts();
|
||||
E = E->IgnoreConversionOperatorSingleStep();
|
||||
E = E->IgnoreImpCasts();
|
||||
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E)) {
|
||||
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E)) {
|
||||
E = MTE->getSubExpr();
|
||||
E = E->IgnoreImpCasts();
|
||||
}
|
||||
|
||||
// Built-in binary operator.
|
||||
if (BinaryOperator *OP = dyn_cast<BinaryOperator>(E)) {
|
||||
if (IsArithmeticOp(OP->getOpcode())) {
|
||||
*Opcode = OP->getOpcode();
|
||||
*RHSExprs = OP->getRHS();
|
||||
return true;
|
||||
}
|
||||
if (const auto *OP = dyn_cast<BinaryOperator>(E);
|
||||
OP && IsArithmeticOp(OP->getOpcode())) {
|
||||
*Opcode = OP->getOpcode();
|
||||
*RHSExprs = OP->getRHS();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overloaded operator.
|
||||
if (CXXOperatorCallExpr *Call = dyn_cast<CXXOperatorCallExpr>(E)) {
|
||||
if (const auto *Call = dyn_cast<CXXOperatorCallExpr>(E)) {
|
||||
if (Call->getNumArgs() != 2)
|
||||
return false;
|
||||
|
||||
@ -9588,14 +9588,14 @@ static bool IsArithmeticBinaryExpr(Expr *E, BinaryOperatorKind *Opcode,
|
||||
/// ExprLooksBoolean - Returns true if E looks boolean, i.e. it has boolean type
|
||||
/// or is a logical expression such as (x==y) which has int type, but is
|
||||
/// commonly interpreted as boolean.
|
||||
static bool ExprLooksBoolean(Expr *E) {
|
||||
static bool ExprLooksBoolean(const Expr *E) {
|
||||
E = E->IgnoreParenImpCasts();
|
||||
|
||||
if (E->getType()->isBooleanType())
|
||||
return true;
|
||||
if (BinaryOperator *OP = dyn_cast<BinaryOperator>(E))
|
||||
if (const auto *OP = dyn_cast<BinaryOperator>(E))
|
||||
return OP->isComparisonOp() || OP->isLogicalOp();
|
||||
if (UnaryOperator *OP = dyn_cast<UnaryOperator>(E))
|
||||
if (const auto *OP = dyn_cast<UnaryOperator>(E))
|
||||
return OP->getOpcode() == UO_LNot;
|
||||
if (E->getType()->isPointerType())
|
||||
return true;
|
||||
@ -9609,13 +9609,11 @@ static bool ExprLooksBoolean(Expr *E) {
|
||||
/// and binary operator are mixed in a way that suggests the programmer assumed
|
||||
/// the conditional operator has higher precedence, for example:
|
||||
/// "int x = a + someBinaryCondition ? 1 : 2".
|
||||
static void DiagnoseConditionalPrecedence(Sema &Self,
|
||||
SourceLocation OpLoc,
|
||||
Expr *Condition,
|
||||
Expr *LHSExpr,
|
||||
Expr *RHSExpr) {
|
||||
static void DiagnoseConditionalPrecedence(Sema &Self, SourceLocation OpLoc,
|
||||
Expr *Condition, const Expr *LHSExpr,
|
||||
const Expr *RHSExpr) {
|
||||
BinaryOperatorKind CondOpcode;
|
||||
Expr *CondRHS;
|
||||
const Expr *CondRHS;
|
||||
|
||||
if (!IsArithmeticBinaryExpr(Condition, &CondOpcode, &CondRHS))
|
||||
return;
|
||||
|
@ -7589,7 +7589,8 @@ static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
|
||||
bool CheckCoroCall = false;
|
||||
if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) {
|
||||
CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&
|
||||
RD->hasAttr<CoroReturnTypeAttr>();
|
||||
RD->hasAttr<CoroReturnTypeAttr>() &&
|
||||
!Callee->hasAttr<CoroDisableLifetimeBoundAttr>();
|
||||
}
|
||||
for (unsigned I = 0,
|
||||
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
|
||||
@ -10376,11 +10377,6 @@ void InitializationSequence::dump() const {
|
||||
dump(llvm::errs());
|
||||
}
|
||||
|
||||
static bool NarrowingErrs(const LangOptions &L) {
|
||||
return L.CPlusPlus11 &&
|
||||
(!L.MicrosoftExt || L.isCompatibleWithMSVC(LangOptions::MSVC2015));
|
||||
}
|
||||
|
||||
static void DiagnoseNarrowingInInitList(Sema &S,
|
||||
const ImplicitConversionSequence &ICS,
|
||||
QualType PreNarrowingType,
|
||||
@ -10401,6 +10397,19 @@ static void DiagnoseNarrowingInInitList(Sema &S,
|
||||
return;
|
||||
}
|
||||
|
||||
auto MakeDiag = [&](bool IsConstRef, unsigned DefaultDiagID,
|
||||
unsigned ConstRefDiagID, unsigned WarnDiagID) {
|
||||
unsigned DiagID;
|
||||
auto &L = S.getLangOpts();
|
||||
if (L.CPlusPlus11 &&
|
||||
(!L.MicrosoftExt || L.isCompatibleWithMSVC(LangOptions::MSVC2015)))
|
||||
DiagID = IsConstRef ? ConstRefDiagID : DefaultDiagID;
|
||||
else
|
||||
DiagID = WarnDiagID;
|
||||
return S.Diag(PostInit->getBeginLoc(), DiagID)
|
||||
<< PostInit->getSourceRange();
|
||||
};
|
||||
|
||||
// C++11 [dcl.init.list]p7: Check whether this is a narrowing conversion.
|
||||
APValue ConstantValue;
|
||||
QualType ConstantType;
|
||||
@ -10416,13 +10425,9 @@ static void DiagnoseNarrowingInInitList(Sema &S,
|
||||
// narrowing conversion even if the value is a constant and can be
|
||||
// represented exactly as an integer.
|
||||
QualType T = EntityType.getNonReferenceType();
|
||||
S.Diag(PostInit->getBeginLoc(),
|
||||
NarrowingErrs(S.getLangOpts())
|
||||
? (T == EntityType
|
||||
? diag::ext_init_list_type_narrowing
|
||||
: diag::ext_init_list_type_narrowing_const_reference)
|
||||
: diag::warn_init_list_type_narrowing)
|
||||
<< PostInit->getSourceRange()
|
||||
MakeDiag(T != EntityType, diag::ext_init_list_type_narrowing,
|
||||
diag::ext_init_list_type_narrowing_const_reference,
|
||||
diag::warn_init_list_type_narrowing)
|
||||
<< PreNarrowingType.getLocalUnqualifiedType()
|
||||
<< T.getLocalUnqualifiedType();
|
||||
break;
|
||||
@ -10430,14 +10435,10 @@ static void DiagnoseNarrowingInInitList(Sema &S,
|
||||
|
||||
case NK_Constant_Narrowing: {
|
||||
// A constant value was narrowed.
|
||||
QualType T = EntityType.getNonReferenceType();
|
||||
S.Diag(PostInit->getBeginLoc(),
|
||||
NarrowingErrs(S.getLangOpts())
|
||||
? (T == EntityType
|
||||
? diag::ext_init_list_constant_narrowing
|
||||
: diag::ext_init_list_constant_narrowing_const_reference)
|
||||
: diag::warn_init_list_constant_narrowing)
|
||||
<< PostInit->getSourceRange()
|
||||
MakeDiag(EntityType.getNonReferenceType() != EntityType,
|
||||
diag::ext_init_list_constant_narrowing,
|
||||
diag::ext_init_list_constant_narrowing_const_reference,
|
||||
diag::warn_init_list_constant_narrowing)
|
||||
<< ConstantValue.getAsString(S.getASTContext(), ConstantType)
|
||||
<< EntityType.getNonReferenceType().getLocalUnqualifiedType();
|
||||
break;
|
||||
@ -10445,14 +10446,10 @@ static void DiagnoseNarrowingInInitList(Sema &S,
|
||||
|
||||
case NK_Variable_Narrowing: {
|
||||
// A variable's value may have been narrowed.
|
||||
QualType T = EntityType.getNonReferenceType();
|
||||
S.Diag(PostInit->getBeginLoc(),
|
||||
NarrowingErrs(S.getLangOpts())
|
||||
? (T == EntityType
|
||||
? diag::ext_init_list_variable_narrowing
|
||||
: diag::ext_init_list_variable_narrowing_const_reference)
|
||||
: diag::warn_init_list_variable_narrowing)
|
||||
<< PostInit->getSourceRange()
|
||||
MakeDiag(EntityType.getNonReferenceType() != EntityType,
|
||||
diag::ext_init_list_variable_narrowing,
|
||||
diag::ext_init_list_variable_narrowing_const_reference,
|
||||
diag::warn_init_list_variable_narrowing)
|
||||
<< PreNarrowingType.getLocalUnqualifiedType()
|
||||
<< EntityType.getNonReferenceType().getLocalUnqualifiedType();
|
||||
break;
|
||||
|
@ -5072,6 +5072,18 @@ static bool checkNestingOfRegions(Sema &SemaRef, const DSAStackTy *Stack,
|
||||
CurrentRegion != OMPD_cancellation_point &&
|
||||
CurrentRegion != OMPD_cancel && CurrentRegion != OMPD_scan)
|
||||
return false;
|
||||
// Checks needed for mapping "loop" construct. Please check mapLoopConstruct
|
||||
// for a detailed explanation
|
||||
if (SemaRef.LangOpts.OpenMP >= 50 && CurrentRegion == OMPD_loop &&
|
||||
(BindKind == OMPC_BIND_parallel || BindKind == OMPC_BIND_teams) &&
|
||||
(isOpenMPWorksharingDirective(ParentRegion) ||
|
||||
ParentRegion == OMPD_loop)) {
|
||||
int ErrorMsgNumber = (BindKind == OMPC_BIND_parallel) ? 1 : 4;
|
||||
SemaRef.Diag(StartLoc, diag::err_omp_prohibited_region)
|
||||
<< true << getOpenMPDirectiveName(ParentRegion) << ErrorMsgNumber
|
||||
<< getOpenMPDirectiveName(CurrentRegion);
|
||||
return true;
|
||||
}
|
||||
if (CurrentRegion == OMPD_cancellation_point ||
|
||||
CurrentRegion == OMPD_cancel) {
|
||||
// OpenMP [2.16, Nesting of Regions]
|
||||
@ -6124,21 +6136,25 @@ processImplicitMapsWithDefaultMappers(Sema &S, DSAStackTy *Stack,
|
||||
|
||||
bool Sema::mapLoopConstruct(llvm::SmallVector<OMPClause *> &ClausesWithoutBind,
|
||||
ArrayRef<OMPClause *> Clauses,
|
||||
OpenMPBindClauseKind BindKind,
|
||||
OpenMPBindClauseKind &BindKind,
|
||||
OpenMPDirectiveKind &Kind,
|
||||
OpenMPDirectiveKind &PrevMappedDirective) {
|
||||
OpenMPDirectiveKind &PrevMappedDirective,
|
||||
SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
const DeclarationNameInfo &DirName,
|
||||
OpenMPDirectiveKind CancelRegion) {
|
||||
|
||||
bool UseClausesWithoutBind = false;
|
||||
|
||||
// Restricting to "#pragma omp loop bind"
|
||||
if (getLangOpts().OpenMP >= 50 && Kind == OMPD_loop) {
|
||||
|
||||
const OpenMPDirectiveKind ParentDirective = DSAStack->getParentDirective();
|
||||
|
||||
if (BindKind == OMPC_BIND_unknown) {
|
||||
// Setting the enclosing teams or parallel construct for the loop
|
||||
// directive without bind clause.
|
||||
BindKind = OMPC_BIND_thread; // Default bind(thread) if binding is unknown
|
||||
|
||||
const OpenMPDirectiveKind ParentDirective =
|
||||
DSAStack->getParentDirective();
|
||||
if (ParentDirective == OMPD_unknown) {
|
||||
Diag(DSAStack->getDefaultDSALocation(),
|
||||
diag::err_omp_bind_required_on_loop);
|
||||
@ -6150,9 +6166,10 @@ bool Sema::mapLoopConstruct(llvm::SmallVector<OMPClause *> &ClausesWithoutBind,
|
||||
BindKind = OMPC_BIND_teams;
|
||||
}
|
||||
} else {
|
||||
// bind clause is present, so we should set flag indicating to only
|
||||
// use the clauses that aren't the bind clause for the new directive that
|
||||
// loop is lowered to.
|
||||
// bind clause is present in loop directive. When the loop directive is
|
||||
// changed to a new directive the bind clause is not used. So, we should
|
||||
// set flag indicating to only use the clauses that aren't the
|
||||
// bind clause.
|
||||
UseClausesWithoutBind = true;
|
||||
}
|
||||
|
||||
@ -6213,26 +6230,35 @@ StmtResult Sema::ActOnOpenMPExecutableDirective(
|
||||
OpenMPDirectiveKind PrevMappedDirective) {
|
||||
StmtResult Res = StmtError();
|
||||
OpenMPBindClauseKind BindKind = OMPC_BIND_unknown;
|
||||
llvm::SmallVector<OMPClause *> ClausesWithoutBind;
|
||||
bool UseClausesWithoutBind = false;
|
||||
|
||||
if (const OMPBindClause *BC =
|
||||
OMPExecutableDirective::getSingleClause<OMPBindClause>(Clauses))
|
||||
BindKind = BC->getBindKind();
|
||||
|
||||
// Variable used to note down the DirectiveKind because mapLoopConstruct may
|
||||
// change "Kind" variable, due to mapping of "omp loop" to other directives.
|
||||
OpenMPDirectiveKind DK = Kind;
|
||||
if (Kind == OMPD_loop || PrevMappedDirective == OMPD_loop) {
|
||||
UseClausesWithoutBind = mapLoopConstruct(
|
||||
ClausesWithoutBind, Clauses, BindKind, Kind, PrevMappedDirective,
|
||||
StartLoc, EndLoc, DirName, CancelRegion);
|
||||
DK = OMPD_loop;
|
||||
}
|
||||
|
||||
// First check CancelRegion which is then used in checkNestingOfRegions.
|
||||
if (checkCancelRegion(*this, Kind, CancelRegion, StartLoc) ||
|
||||
checkNestingOfRegions(*this, DSAStack, Kind, DirName, CancelRegion,
|
||||
BindKind, StartLoc))
|
||||
checkNestingOfRegions(*this, DSAStack, DK, DirName, CancelRegion,
|
||||
BindKind, StartLoc)) {
|
||||
return StmtError();
|
||||
}
|
||||
|
||||
// Report affected OpenMP target offloading behavior when in HIP lang-mode.
|
||||
if (getLangOpts().HIP && (isOpenMPTargetExecutionDirective(Kind) ||
|
||||
isOpenMPTargetDataManagementDirective(Kind)))
|
||||
Diag(StartLoc, diag::warn_hip_omp_target_directives);
|
||||
|
||||
llvm::SmallVector<OMPClause *> ClausesWithoutBind;
|
||||
bool UseClausesWithoutBind = false;
|
||||
|
||||
UseClausesWithoutBind = mapLoopConstruct(ClausesWithoutBind, Clauses,
|
||||
BindKind, Kind, PrevMappedDirective);
|
||||
|
||||
llvm::SmallVector<OMPClause *, 8> ClausesWithImplicit;
|
||||
VarsWithInheritedDSAType VarsWithInheritedDSA;
|
||||
bool ErrorFound = false;
|
||||
|
@ -1259,6 +1259,43 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
|
||||
if ((OldTemplate == nullptr) != (NewTemplate == nullptr))
|
||||
return true;
|
||||
|
||||
if (NewTemplate) {
|
||||
// C++ [temp.over.link]p4:
|
||||
// The signature of a function template consists of its function
|
||||
// signature, its return type and its template parameter list. The names
|
||||
// of the template parameters are significant only for establishing the
|
||||
// relationship between the template parameters and the rest of the
|
||||
// signature.
|
||||
//
|
||||
// We check the return type and template parameter lists for function
|
||||
// templates first; the remaining checks follow.
|
||||
bool SameTemplateParameterList = SemaRef.TemplateParameterListsAreEqual(
|
||||
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
|
||||
OldTemplate->getTemplateParameters(), false, Sema::TPL_TemplateMatch);
|
||||
bool SameReturnType = SemaRef.Context.hasSameType(
|
||||
Old->getDeclaredReturnType(), New->getDeclaredReturnType());
|
||||
// FIXME(GH58571): Match template parameter list even for non-constrained
|
||||
// template heads. This currently ensures that the code prior to C++20 is
|
||||
// not newly broken.
|
||||
bool ConstraintsInTemplateHead =
|
||||
NewTemplate->getTemplateParameters()->hasAssociatedConstraints() ||
|
||||
OldTemplate->getTemplateParameters()->hasAssociatedConstraints();
|
||||
// C++ [namespace.udecl]p11:
|
||||
// The set of declarations named by a using-declarator that inhabits a
|
||||
// class C does not include member functions and member function
|
||||
// templates of a base class that "correspond" to (and thus would
|
||||
// conflict with) a declaration of a function or function template in
|
||||
// C.
|
||||
// Comparing return types is not required for the "correspond" check to
|
||||
// decide whether a member introduced by a shadow declaration is hidden.
|
||||
if (UseMemberUsingDeclRules && ConstraintsInTemplateHead &&
|
||||
!SameTemplateParameterList)
|
||||
return true;
|
||||
if (!UseMemberUsingDeclRules &&
|
||||
(!SameTemplateParameterList || !SameReturnType))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is the function New an overload of the function Old?
|
||||
QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
|
||||
QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
|
||||
@ -1410,43 +1447,6 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
|
||||
}
|
||||
}
|
||||
|
||||
if (NewTemplate) {
|
||||
// C++ [temp.over.link]p4:
|
||||
// The signature of a function template consists of its function
|
||||
// signature, its return type and its template parameter list. The names
|
||||
// of the template parameters are significant only for establishing the
|
||||
// relationship between the template parameters and the rest of the
|
||||
// signature.
|
||||
//
|
||||
// We check the return type and template parameter lists for function
|
||||
// templates first; the remaining checks follow.
|
||||
bool SameTemplateParameterList = SemaRef.TemplateParameterListsAreEqual(
|
||||
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
|
||||
OldTemplate->getTemplateParameters(), false, Sema::TPL_TemplateMatch);
|
||||
bool SameReturnType = SemaRef.Context.hasSameType(
|
||||
Old->getDeclaredReturnType(), New->getDeclaredReturnType());
|
||||
// FIXME(GH58571): Match template parameter list even for non-constrained
|
||||
// template heads. This currently ensures that the code prior to C++20 is
|
||||
// not newly broken.
|
||||
bool ConstraintsInTemplateHead =
|
||||
NewTemplate->getTemplateParameters()->hasAssociatedConstraints() ||
|
||||
OldTemplate->getTemplateParameters()->hasAssociatedConstraints();
|
||||
// C++ [namespace.udecl]p11:
|
||||
// The set of declarations named by a using-declarator that inhabits a
|
||||
// class C does not include member functions and member function
|
||||
// templates of a base class that "correspond" to (and thus would
|
||||
// conflict with) a declaration of a function or function template in
|
||||
// C.
|
||||
// Comparing return types is not required for the "correspond" check to
|
||||
// decide whether a member introduced by a shadow declaration is hidden.
|
||||
if (UseMemberUsingDeclRules && ConstraintsInTemplateHead &&
|
||||
!SameTemplateParameterList)
|
||||
return true;
|
||||
if (!UseMemberUsingDeclRules &&
|
||||
(!SameTemplateParameterList || !SameReturnType))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!UseOverrideRules) {
|
||||
Expr *NewRC = New->getTrailingRequiresClause(),
|
||||
*OldRC = Old->getTrailingRequiresClause();
|
||||
@ -13995,6 +13995,22 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
|
||||
OverloadingResult OverloadResult =
|
||||
CandidateSet.BestViableFunction(*this, Fn->getBeginLoc(), Best);
|
||||
|
||||
// Model the case with a call to a templated function whose definition
|
||||
// encloses the call and whose return type contains a placeholder type as if
|
||||
// the UnresolvedLookupExpr was type-dependent.
|
||||
if (OverloadResult == OR_Success) {
|
||||
const FunctionDecl *FDecl = Best->Function;
|
||||
if (FDecl && FDecl->isTemplateInstantiation() &&
|
||||
FDecl->getReturnType()->isUndeducedType()) {
|
||||
if (const auto *TP =
|
||||
FDecl->getTemplateInstantiationPattern(/*ForDefinition=*/false);
|
||||
TP && TP->willHaveBody()) {
|
||||
return CallExpr::Create(Context, Fn, Args, Context.DependentTy,
|
||||
VK_PRValue, RParenLoc, CurFPFeatureOverrides());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args, RParenLoc,
|
||||
ExecConfig, &CandidateSet, &Best,
|
||||
OverloadResult, AllowTypoCorrection);
|
||||
|
@ -4737,6 +4737,7 @@ namespace {
|
||||
QualType Replacement;
|
||||
bool ReplacementIsPack;
|
||||
bool UseTypeSugar;
|
||||
using inherited = TreeTransform<SubstituteDeducedTypeTransform>;
|
||||
|
||||
public:
|
||||
SubstituteDeducedTypeTransform(Sema &SemaRef, DependentAuto DA)
|
||||
@ -4797,6 +4798,16 @@ namespace {
|
||||
// Lambdas never need to be transformed.
|
||||
return E;
|
||||
}
|
||||
bool TransformExceptionSpec(SourceLocation Loc,
|
||||
FunctionProtoType::ExceptionSpecInfo &ESI,
|
||||
SmallVectorImpl<QualType> &Exceptions,
|
||||
bool &Changed) {
|
||||
if (ESI.Type == EST_Uninstantiated) {
|
||||
ESI.instantiate();
|
||||
Changed = true;
|
||||
}
|
||||
return inherited::TransformExceptionSpec(Loc, ESI, Exceptions, Changed);
|
||||
}
|
||||
|
||||
QualType Apply(TypeLoc TL) {
|
||||
// Create some scratch storage for the transformed type locations.
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "clang/Sema/Template.h"
|
||||
#include "clang/Sema/TemplateDeduction.h"
|
||||
#include "clang/Sema/TemplateInstCallback.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
@ -345,15 +344,26 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
|
||||
using namespace TemplateInstArgsHelpers;
|
||||
const Decl *CurDecl = ND;
|
||||
|
||||
if (!CurDecl)
|
||||
CurDecl = Decl::castFromDeclContext(DC);
|
||||
|
||||
if (Innermost) {
|
||||
Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND),
|
||||
Innermost->asArray(), Final);
|
||||
CurDecl = Response::UseNextDecl(ND).NextDecl;
|
||||
// Populate placeholder template arguments for TemplateTemplateParmDecls.
|
||||
// This is essential for the case e.g.
|
||||
//
|
||||
// template <class> concept Concept = false;
|
||||
// template <template <Concept C> class T> void foo(T<int>)
|
||||
//
|
||||
// where parameter C has a depth of 1 but the substituting argument `int`
|
||||
// has a depth of 0.
|
||||
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
|
||||
HandleDefaultTempArgIntoTempTempParam(TTP, Result);
|
||||
CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
|
||||
}
|
||||
|
||||
if (!ND)
|
||||
CurDecl = Decl::castFromDeclContext(DC);
|
||||
|
||||
while (!CurDecl->isFileContextDecl()) {
|
||||
Response R;
|
||||
if (const auto *VarTemplSpec =
|
||||
@ -381,10 +391,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
R = Response::ChangeDecl(CTD->getLexicalDeclContext());
|
||||
} else if (!isa<DeclContext>(CurDecl)) {
|
||||
R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
|
||||
if (CurDecl->getDeclContext()->isTranslationUnit()) {
|
||||
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) {
|
||||
R = HandleDefaultTempArgIntoTempTempParam(TTP, Result);
|
||||
}
|
||||
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) {
|
||||
R = HandleDefaultTempArgIntoTempTempParam(TTP, Result);
|
||||
}
|
||||
} else {
|
||||
R = HandleGenericDeclContext(CurDecl);
|
||||
@ -1142,8 +1150,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
|
||||
case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
|
||||
// We're either substituting explicitly-specified template arguments,
|
||||
// deduced template arguments. SFINAE applies unless we are in a lambda
|
||||
// expression, see [temp.deduct]p9.
|
||||
[[fallthrough]];
|
||||
// body, see [temp.deduct]p9.
|
||||
case CodeSynthesisContext::ConstraintSubstitution:
|
||||
case CodeSynthesisContext::RequirementInstantiation:
|
||||
case CodeSynthesisContext::RequirementParameterInstantiation:
|
||||
@ -1190,6 +1197,7 @@ namespace {
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs;
|
||||
SourceLocation Loc;
|
||||
DeclarationName Entity;
|
||||
// Whether to evaluate the C++20 constraints or simply substitute into them.
|
||||
bool EvaluateConstraints = true;
|
||||
|
||||
public:
|
||||
@ -1444,13 +1452,6 @@ namespace {
|
||||
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
|
||||
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
|
||||
|
||||
Sema::CodeSynthesisContext C;
|
||||
C.Kind = clang::Sema::CodeSynthesisContext::LambdaExpressionSubstitution;
|
||||
C.PointOfInstantiation = E->getBeginLoc();
|
||||
SemaRef.pushCodeSynthesisContext(C);
|
||||
auto PopCtx =
|
||||
llvm::make_scope_exit([this] { SemaRef.popCodeSynthesisContext(); });
|
||||
|
||||
ExprResult Result = inherited::TransformLambdaExpr(E);
|
||||
if (Result.isInvalid())
|
||||
return Result;
|
||||
@ -1478,6 +1479,23 @@ namespace {
|
||||
return Result;
|
||||
}
|
||||
|
||||
StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
|
||||
// Currently, we instantiate the body when instantiating the lambda
|
||||
// expression. However, `EvaluateConstraints` is disabled during the
|
||||
// instantiation of the lambda expression, causing the instantiation
|
||||
// failure of the return type requirement in the body. If p0588r1 is fully
|
||||
// implemented, the body will be lazily instantiated, and this problem
|
||||
// will not occur. Here, `EvaluateConstraints` is temporarily set to
|
||||
// `true` to temporarily fix this issue.
|
||||
// FIXME: This temporary fix can be removed after fully implementing
|
||||
// p0588r1.
|
||||
bool Prev = EvaluateConstraints;
|
||||
EvaluateConstraints = true;
|
||||
StmtResult Stmt = inherited::TransformLambdaBody(E, Body);
|
||||
EvaluateConstraints = Prev;
|
||||
return Stmt;
|
||||
}
|
||||
|
||||
ExprResult TransformRequiresExpr(RequiresExpr *E) {
|
||||
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
|
||||
ExprResult TransReq = inherited::TransformRequiresExpr(E);
|
||||
@ -1630,9 +1648,7 @@ bool TemplateInstantiator::TransformExceptionSpec(
|
||||
SourceLocation Loc, FunctionProtoType::ExceptionSpecInfo &ESI,
|
||||
SmallVectorImpl<QualType> &Exceptions, bool &Changed) {
|
||||
if (ESI.Type == EST_Uninstantiated) {
|
||||
ESI.NoexceptExpr = cast<FunctionProtoType>(ESI.SourceTemplate->getType())
|
||||
->getNoexceptExpr();
|
||||
ESI.Type = EST_DependentNoexcept;
|
||||
ESI.instantiate();
|
||||
Changed = true;
|
||||
}
|
||||
return inherited::TransformExceptionSpec(Loc, ESI, Exceptions, Changed);
|
||||
@ -2499,6 +2515,17 @@ TemplateInstantiator::TransformNestedRequirement(
|
||||
Req->getConstraintExpr()->getBeginLoc(), Req,
|
||||
Sema::InstantiatingTemplate::ConstraintsCheck{},
|
||||
Req->getConstraintExpr()->getSourceRange());
|
||||
if (!getEvaluateConstraints()) {
|
||||
ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr());
|
||||
if (TransConstraint.isInvalid() || !TransConstraint.get())
|
||||
return nullptr;
|
||||
if (TransConstraint.get()->isInstantiationDependent())
|
||||
return new (SemaRef.Context)
|
||||
concepts::NestedRequirement(TransConstraint.get());
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
return new (SemaRef.Context) concepts::NestedRequirement(
|
||||
SemaRef.Context, TransConstraint.get(), Satisfaction);
|
||||
}
|
||||
|
||||
ExprResult TransConstraint;
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
@ -4093,13 +4120,19 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
ExprResult
|
||||
Sema::SubstConstraintExpr(Expr *E,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
// FIXME: should call SubstExpr directly if this function is equivalent or
|
||||
// should it be different?
|
||||
return SubstExpr(E, TemplateArgs);
|
||||
}
|
||||
|
||||
ExprResult Sema::SubstConstraintExprWithoutSatisfaction(
|
||||
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
if (!E)
|
||||
return E;
|
||||
|
||||
// This is where we need to make sure we 'know' constraint checking needs to
|
||||
// happen.
|
||||
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
|
||||
DeclarationName());
|
||||
Instantiator.setEvaluateConstraints(false);
|
||||
return Instantiator.TransformExpr(E);
|
||||
}
|
||||
|
||||
|
@ -674,6 +674,10 @@ class TreeTransform {
|
||||
Qualifiers ThisTypeQuals,
|
||||
Fn TransformExceptionSpec);
|
||||
|
||||
template <typename Fn>
|
||||
QualType TransformAttributedType(TypeLocBuilder &TLB, AttributedTypeLoc TL,
|
||||
Fn TransformModifiedType);
|
||||
|
||||
bool TransformExceptionSpec(SourceLocation Loc,
|
||||
FunctionProtoType::ExceptionSpecInfo &ESI,
|
||||
SmallVectorImpl<QualType> &Exceptions,
|
||||
@ -7050,12 +7054,12 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB,
|
||||
return Result;
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
template <typename Derived>
|
||||
template <typename Fn>
|
||||
QualType TreeTransform<Derived>::TransformAttributedType(
|
||||
TypeLocBuilder &TLB,
|
||||
AttributedTypeLoc TL) {
|
||||
TypeLocBuilder &TLB, AttributedTypeLoc TL, Fn TransformModifiedTypeFn) {
|
||||
const AttributedType *oldType = TL.getTypePtr();
|
||||
QualType modifiedType = getDerived().TransformType(TLB, TL.getModifiedLoc());
|
||||
QualType modifiedType = TransformModifiedTypeFn(TLB, TL.getModifiedLoc());
|
||||
if (modifiedType.isNull())
|
||||
return QualType();
|
||||
|
||||
@ -7099,6 +7103,15 @@ QualType TreeTransform<Derived>::TransformAttributedType(
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
QualType TreeTransform<Derived>::TransformAttributedType(TypeLocBuilder &TLB,
|
||||
AttributedTypeLoc TL) {
|
||||
return getDerived().TransformAttributedType(
|
||||
TLB, TL, [&](TypeLocBuilder &TLB, TypeLoc ModifiedLoc) -> QualType {
|
||||
return getDerived().TransformType(TLB, ModifiedLoc);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
QualType TreeTransform<Derived>::TransformBTFTagAttributedType(
|
||||
TypeLocBuilder &TLB, BTFTagAttributedTypeLoc TL) {
|
||||
@ -13600,32 +13613,56 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
||||
// transformed parameters.
|
||||
TypeSourceInfo *NewCallOpTSI = nullptr;
|
||||
{
|
||||
TypeSourceInfo *OldCallOpTSI = E->getCallOperator()->getTypeSourceInfo();
|
||||
auto OldCallOpFPTL =
|
||||
OldCallOpTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>();
|
||||
auto OldCallOpTypeLoc =
|
||||
E->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
|
||||
|
||||
auto TransformFunctionProtoTypeLoc =
|
||||
[this](TypeLocBuilder &TLB, FunctionProtoTypeLoc FPTL) -> QualType {
|
||||
SmallVector<QualType, 4> ExceptionStorage;
|
||||
TreeTransform *This = this; // Work around gcc.gnu.org/PR56135.
|
||||
return this->TransformFunctionProtoType(
|
||||
TLB, FPTL, nullptr, Qualifiers(),
|
||||
[&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) {
|
||||
return This->TransformExceptionSpec(FPTL.getBeginLoc(), ESI,
|
||||
ExceptionStorage, Changed);
|
||||
});
|
||||
};
|
||||
|
||||
QualType NewCallOpType;
|
||||
TypeLocBuilder NewCallOpTLBuilder;
|
||||
SmallVector<QualType, 4> ExceptionStorage;
|
||||
TreeTransform *This = this; // Work around gcc.gnu.org/PR56135.
|
||||
QualType NewCallOpType = TransformFunctionProtoType(
|
||||
NewCallOpTLBuilder, OldCallOpFPTL, nullptr, Qualifiers(),
|
||||
[&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) {
|
||||
return This->TransformExceptionSpec(OldCallOpFPTL.getBeginLoc(), ESI,
|
||||
ExceptionStorage, Changed);
|
||||
});
|
||||
|
||||
if (auto ATL = OldCallOpTypeLoc.getAs<AttributedTypeLoc>()) {
|
||||
NewCallOpType = this->TransformAttributedType(
|
||||
NewCallOpTLBuilder, ATL,
|
||||
[&](TypeLocBuilder &TLB, TypeLoc TL) -> QualType {
|
||||
return TransformFunctionProtoTypeLoc(
|
||||
TLB, TL.castAs<FunctionProtoTypeLoc>());
|
||||
});
|
||||
} else {
|
||||
auto FPTL = OldCallOpTypeLoc.castAs<FunctionProtoTypeLoc>();
|
||||
NewCallOpType = TransformFunctionProtoTypeLoc(NewCallOpTLBuilder, FPTL);
|
||||
}
|
||||
|
||||
if (NewCallOpType.isNull())
|
||||
return ExprError();
|
||||
NewCallOpTSI =
|
||||
NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);
|
||||
}
|
||||
|
||||
ArrayRef<ParmVarDecl *> Params;
|
||||
if (auto ATL = NewCallOpTSI->getTypeLoc().getAs<AttributedTypeLoc>()) {
|
||||
Params = ATL.getModifiedLoc().castAs<FunctionProtoTypeLoc>().getParams();
|
||||
} else {
|
||||
auto FPTL = NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>();
|
||||
Params = FPTL.getParams();
|
||||
}
|
||||
|
||||
getSema().CompleteLambdaCallOperator(
|
||||
NewCallOperator, E->getCallOperator()->getLocation(),
|
||||
E->getCallOperator()->getInnerLocStart(),
|
||||
E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
|
||||
E->getCallOperator()->getConstexprKind(),
|
||||
E->getCallOperator()->getStorageClass(),
|
||||
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
|
||||
E->getCallOperator()->getStorageClass(), Params,
|
||||
E->hasExplicitResultType());
|
||||
|
||||
getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
|
||||
@ -13648,10 +13685,17 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
||||
getSema().PushExpressionEvaluationContext(
|
||||
Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
|
||||
|
||||
Sema::CodeSynthesisContext C;
|
||||
C.Kind = clang::Sema::CodeSynthesisContext::LambdaExpressionSubstitution;
|
||||
C.PointOfInstantiation = E->getBody()->getBeginLoc();
|
||||
getSema().pushCodeSynthesisContext(C);
|
||||
|
||||
// Instantiate the body of the lambda expression.
|
||||
StmtResult Body =
|
||||
Invalid ? StmtError() : getDerived().TransformLambdaBody(E, E->getBody());
|
||||
|
||||
getSema().popCodeSynthesisContext();
|
||||
|
||||
// ActOnLambda* will pop the function scope for us.
|
||||
FuncScopeCleanup.disable();
|
||||
|
||||
|
@ -2507,16 +2507,30 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
|
||||
.ArgConstraint(NotNull(ArgNo(0))));
|
||||
|
||||
// char *mkdtemp(char *template);
|
||||
// FIXME: Improve for errno modeling.
|
||||
addToFunctionSummaryMap(
|
||||
"mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}),
|
||||
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
|
||||
Summary(NoEvalCall)
|
||||
.Case({ReturnValueCondition(BO_EQ, ArgNo(0))},
|
||||
ErrnoMustNotBeChecked, GenericSuccessMsg)
|
||||
.Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
|
||||
.ArgConstraint(NotNull(ArgNo(0))));
|
||||
|
||||
// char *getcwd(char *buf, size_t size);
|
||||
// FIXME: Improve for errno modeling.
|
||||
addToFunctionSummaryMap(
|
||||
"getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}),
|
||||
Summary(NoEvalCall)
|
||||
.Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)),
|
||||
ReturnValueCondition(BO_EQ, ArgNo(0))},
|
||||
ErrnoMustNotBeChecked, GenericSuccessMsg)
|
||||
.Case({ArgumentCondition(1, WithinRange, SingleValue(0)),
|
||||
IsNull(Ret)},
|
||||
ErrnoNEZeroIrrelevant, "Assuming that argument 'size' is 0")
|
||||
.Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)),
|
||||
IsNull(Ret)},
|
||||
ErrnoNEZeroIrrelevant, GenericFailureMsg)
|
||||
.ArgConstraint(NotNull(ArgNo(0)))
|
||||
.ArgConstraint(
|
||||
BufferSize(/*Buffer*/ ArgNo(0), /*BufSize*/ ArgNo(1)))
|
||||
.ArgConstraint(
|
||||
ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
|
||||
|
||||
|
@ -239,6 +239,7 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
|
||||
private:
|
||||
CallDescriptionMap<FnDescription> FnDescriptions = {
|
||||
{{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
|
||||
{{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
|
||||
{{{"freopen"}, 3},
|
||||
{&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
|
||||
{{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
|
||||
|
@ -554,6 +554,8 @@ int ClangTool::run(ToolAction *Action) {
|
||||
<< CWD.getError().message() << "\n";
|
||||
}
|
||||
|
||||
size_t NumOfTotalFiles = AbsolutePaths.size();
|
||||
unsigned ProcessedFileCounter = 0;
|
||||
for (llvm::StringRef File : AbsolutePaths) {
|
||||
// Currently implementations of CompilationDatabase::getCompileCommands can
|
||||
// change the state of the file system (e.g. prepare generated headers), so
|
||||
@ -609,7 +611,11 @@ int ClangTool::run(ToolAction *Action) {
|
||||
|
||||
// FIXME: We need a callback mechanism for the tool writer to output a
|
||||
// customized message for each file.
|
||||
LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
|
||||
if (NumOfTotalFiles > 1)
|
||||
llvm::errs() << "[" + std::to_string(++ProcessedFileCounter) + "/" +
|
||||
std::to_string(NumOfTotalFiles) +
|
||||
"] Processing file " + File
|
||||
<< ".\n";
|
||||
ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
|
||||
PCHContainerOps);
|
||||
Invocation.setDiagnosticConsumer(DiagConsumer);
|
||||
|
@ -33,6 +33,7 @@ class ClangASTNodesEmitter {
|
||||
typedef std::multimap<ASTNode, ASTNode> ChildMap;
|
||||
typedef ChildMap::const_iterator ChildIterator;
|
||||
|
||||
std::set<ASTNode> PrioritizedClasses;
|
||||
RecordKeeper &Records;
|
||||
ASTNode Root;
|
||||
const std::string &NodeClassName;
|
||||
@ -70,8 +71,16 @@ class ClangASTNodesEmitter {
|
||||
std::pair<ASTNode, ASTNode> EmitNode(raw_ostream& OS, ASTNode Base);
|
||||
public:
|
||||
explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N,
|
||||
const std::string &S)
|
||||
: Records(R), NodeClassName(N), BaseSuffix(S) {}
|
||||
const std::string &S,
|
||||
std::string_view PriorizeIfSubclassOf)
|
||||
: Records(R), NodeClassName(N), BaseSuffix(S) {
|
||||
auto vecPrioritized =
|
||||
PriorizeIfSubclassOf.empty()
|
||||
? std::vector<Record *>{}
|
||||
: R.getAllDerivedDefinitions(PriorizeIfSubclassOf);
|
||||
PrioritizedClasses =
|
||||
std::set<ASTNode>(vecPrioritized.begin(), vecPrioritized.end());
|
||||
}
|
||||
|
||||
// run - Output the .inc file contents
|
||||
void run(raw_ostream &OS);
|
||||
@ -95,8 +104,23 @@ std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
|
||||
if (!Base.isAbstract())
|
||||
First = Last = Base;
|
||||
|
||||
auto comp = [this](ASTNode LHS, ASTNode RHS) {
|
||||
auto LHSPrioritized = PrioritizedClasses.count(LHS) > 0;
|
||||
auto RHSPrioritized = PrioritizedClasses.count(RHS) > 0;
|
||||
if (LHSPrioritized && !RHSPrioritized)
|
||||
return true;
|
||||
if (!LHSPrioritized && RHSPrioritized)
|
||||
return false;
|
||||
|
||||
return LHS.getName() > RHS.getName();
|
||||
};
|
||||
auto SortedChildren = std::set<ASTNode, decltype(comp)>(comp);
|
||||
|
||||
for (; i != e; ++i) {
|
||||
ASTNode Child = i->second;
|
||||
SortedChildren.insert(i->second);
|
||||
}
|
||||
|
||||
for (const auto &Child : SortedChildren) {
|
||||
bool Abstract = Child.isAbstract();
|
||||
std::string NodeName = macroName(std::string(Child.getName()));
|
||||
|
||||
@ -148,9 +172,7 @@ void ClangASTNodesEmitter::deriveChildTree() {
|
||||
const std::vector<Record*> Stmts
|
||||
= Records.getAllDerivedDefinitions(NodeClassName);
|
||||
|
||||
for (unsigned i = 0, e = Stmts.size(); i != e; ++i) {
|
||||
Record *R = Stmts[i];
|
||||
|
||||
for (auto *R : Stmts) {
|
||||
if (auto B = R->getValueAsOptionalDef(BaseFieldName))
|
||||
Tree.insert(std::make_pair(B, R));
|
||||
else if (Root)
|
||||
@ -182,9 +204,9 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) {
|
||||
OS << "#endif\n\n";
|
||||
|
||||
OS << "#ifndef LAST_" << macroHierarchyName() << "_RANGE\n";
|
||||
OS << "# define LAST_"
|
||||
<< macroHierarchyName() << "_RANGE(Base, First, Last) "
|
||||
<< macroHierarchyName() << "_RANGE(Base, First, Last)\n";
|
||||
OS << "# define LAST_" << macroHierarchyName()
|
||||
<< "_RANGE(Base, First, Last) " << macroHierarchyName()
|
||||
<< "_RANGE(Base, First, Last)\n";
|
||||
OS << "#endif\n\n";
|
||||
|
||||
EmitNode(OS, Root);
|
||||
@ -196,8 +218,20 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) {
|
||||
}
|
||||
|
||||
void clang::EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS,
|
||||
const std::string &N, const std::string &S) {
|
||||
ClangASTNodesEmitter(RK, N, S).run(OS);
|
||||
const std::string &N, const std::string &S,
|
||||
std::string_view PriorizeIfSubclassOf) {
|
||||
ClangASTNodesEmitter(RK, N, S, PriorizeIfSubclassOf).run(OS);
|
||||
}
|
||||
|
||||
void printDeclContext(const std::multimap<Record *, Record *> &Tree,
|
||||
Record *DeclContext, raw_ostream &OS) {
|
||||
if (!DeclContext->getValueAsBit(AbstractFieldName))
|
||||
OS << "DECL_CONTEXT(" << DeclContext->getName() << ")\n";
|
||||
auto i = Tree.lower_bound(DeclContext);
|
||||
auto end = Tree.upper_bound(DeclContext);
|
||||
for (; i != end; ++i) {
|
||||
printDeclContext(Tree, i->second, OS);
|
||||
}
|
||||
}
|
||||
|
||||
// Emits and addendum to a .inc file to enumerate the clang declaration
|
||||
@ -210,38 +244,25 @@ void clang::EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) {
|
||||
OS << "#ifndef DECL_CONTEXT\n";
|
||||
OS << "# define DECL_CONTEXT(DECL)\n";
|
||||
OS << "#endif\n";
|
||||
|
||||
OS << "#ifndef DECL_CONTEXT_BASE\n";
|
||||
OS << "# define DECL_CONTEXT_BASE(DECL) DECL_CONTEXT(DECL)\n";
|
||||
OS << "#endif\n";
|
||||
|
||||
typedef std::set<Record*> RecordSet;
|
||||
typedef std::vector<Record*> RecordVector;
|
||||
|
||||
RecordVector DeclContextsVector
|
||||
= Records.getAllDerivedDefinitions(DeclContextNodeClassName);
|
||||
RecordVector Decls = Records.getAllDerivedDefinitions(DeclNodeClassName);
|
||||
RecordSet DeclContexts (DeclContextsVector.begin(), DeclContextsVector.end());
|
||||
|
||||
for (RecordVector::iterator i = Decls.begin(), e = Decls.end(); i != e; ++i) {
|
||||
Record *R = *i;
|
||||
|
||||
if (Record *B = R->getValueAsOptionalDef(BaseFieldName)) {
|
||||
if (DeclContexts.find(B) != DeclContexts.end()) {
|
||||
OS << "DECL_CONTEXT_BASE(" << B->getName() << ")\n";
|
||||
DeclContexts.erase(B);
|
||||
}
|
||||
}
|
||||
std::vector<Record *> DeclContextsVector =
|
||||
Records.getAllDerivedDefinitions(DeclContextNodeClassName);
|
||||
std::vector<Record *> Decls =
|
||||
Records.getAllDerivedDefinitions(DeclNodeClassName);
|
||||
|
||||
std::multimap<Record *, Record *> Tree;
|
||||
|
||||
const std::vector<Record *> Stmts =
|
||||
Records.getAllDerivedDefinitions(DeclNodeClassName);
|
||||
|
||||
for (auto *R : Stmts) {
|
||||
if (auto *B = R->getValueAsOptionalDef(BaseFieldName))
|
||||
Tree.insert(std::make_pair(B, R));
|
||||
}
|
||||
|
||||
// To keep identical order, RecordVector may be used
|
||||
// instead of RecordSet.
|
||||
for (RecordVector::iterator
|
||||
i = DeclContextsVector.begin(), e = DeclContextsVector.end();
|
||||
i != e; ++i)
|
||||
if (DeclContexts.find(*i) != DeclContexts.end())
|
||||
OS << "DECL_CONTEXT(" << (*i)->getName() << ")\n";
|
||||
for (auto *DeclContext : DeclContextsVector) {
|
||||
printDeclContext(Tree, DeclContext, OS);
|
||||
}
|
||||
|
||||
OS << "#undef DECL_CONTEXT\n";
|
||||
OS << "#undef DECL_CONTEXT_BASE\n";
|
||||
}
|
||||
|
@ -1773,11 +1773,14 @@ void SVEEmitter::createStreamingAttrs(raw_ostream &OS, ACLEKind Kind) {
|
||||
llvm::StringMap<std::set<std::string>> StreamingMap;
|
||||
|
||||
uint64_t IsStreamingFlag = getEnumValueForFlag("IsStreaming");
|
||||
uint64_t IsStreamingOrSVE2p1Flag = getEnumValueForFlag("IsStreamingOrSVE2p1");
|
||||
uint64_t IsStreamingCompatibleFlag =
|
||||
getEnumValueForFlag("IsStreamingCompatible");
|
||||
for (auto &Def : Defs) {
|
||||
if (Def->isFlagSet(IsStreamingFlag))
|
||||
StreamingMap["ArmStreaming"].insert(Def->getMangledName());
|
||||
else if (Def->isFlagSet(IsStreamingOrSVE2p1Flag))
|
||||
StreamingMap["ArmStreamingOrSVE2p1"].insert(Def->getMangledName());
|
||||
else if (Def->isFlagSet(IsStreamingCompatibleFlag))
|
||||
StreamingMap["ArmStreamingCompatible"].insert(Def->getMangledName());
|
||||
else
|
||||
|
@ -398,7 +398,8 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
|
||||
EmitClangASTNodes(Records, OS, CommentNodeClassName, "");
|
||||
break;
|
||||
case GenClangDeclNodes:
|
||||
EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl");
|
||||
EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl",
|
||||
DeclContextNodeClassName);
|
||||
EmitClangDeclContext(Records, OS);
|
||||
break;
|
||||
case GenClangStmtNodes:
|
||||
|
@ -25,8 +25,16 @@ class RecordKeeper;
|
||||
namespace clang {
|
||||
|
||||
void EmitClangDeclContext(llvm::RecordKeeper &RK, llvm::raw_ostream &OS);
|
||||
/**
|
||||
@param PriorizeIfSubclassOf These classes should be prioritized in the output.
|
||||
This is useful to force enum generation/jump tables/lookup tables to be more
|
||||
compact in both size and surrounding code in hot functions. An example use is
|
||||
in Decl for classes that inherit from DeclContext, for functions like
|
||||
castFromDeclContext.
|
||||
*/
|
||||
void EmitClangASTNodes(llvm::RecordKeeper &RK, llvm::raw_ostream &OS,
|
||||
const std::string &N, const std::string &S);
|
||||
const std::string &N, const std::string &S,
|
||||
std::string_view PriorizeIfSubclassOf = "");
|
||||
void EmitClangBasicReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
|
||||
void EmitClangBasicWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
|
||||
void EmitClangTypeNodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
|
||||
|
@ -81,9 +81,10 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
|
||||
}
|
||||
|
||||
if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
|
||||
SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
|
||||
SymbolizedStackHolder symbolized_stack(symbolizer->SymbolizePC(addr));
|
||||
const SymbolizedStack *frames = symbolized_stack.get();
|
||||
CHECK(frames);
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
const char *function_name = cur->info.function;
|
||||
if (!function_name) {
|
||||
continue;
|
||||
@ -91,11 +92,9 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
|
||||
// Match "interceptor_via_fun" suppressions.
|
||||
if (suppression_ctx->Match(function_name, kInterceptorViaFunction,
|
||||
&s)) {
|
||||
frames->ClearAll();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
frames->ClearAll();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -292,12 +292,14 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
|
||||
uptr pc = record & pc_mask;
|
||||
frame_desc.AppendF(" record_addr:0x%zx record:0x%zx",
|
||||
reinterpret_cast<uptr>(record_addr), record);
|
||||
if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
Symbolizer::GetOrInit()->SymbolizePC(pc));
|
||||
const SymbolizedStack *frame = symbolized_stack.get();
|
||||
if (frame) {
|
||||
StackTracePrinter::GetOrInit()->RenderFrame(
|
||||
&frame_desc, " %F %L", 0, frame->info.address, &frame->info,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
frame->ClearAll();
|
||||
}
|
||||
Printf("%s\n", frame_desc.data());
|
||||
frame_desc.clear();
|
||||
|
@ -155,14 +155,15 @@ Suppression *LeakSuppressionContext::GetSuppressionForAddr(uptr addr) {
|
||||
return s;
|
||||
|
||||
// Suppress by file or function name.
|
||||
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
Symbolizer::GetOrInit()->SymbolizePC(addr));
|
||||
const SymbolizedStack *frames = symbolized_stack.get();
|
||||
for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
if (context.Match(cur->info.function, kSuppressionLeak, &s) ||
|
||||
context.Match(cur->info.file, kSuppressionLeak, &s)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
frames->ClearAll();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -255,18 +255,19 @@ char *GetProcSelfMaps();
|
||||
void InitializeInterceptors();
|
||||
|
||||
void MsanAllocatorInit();
|
||||
void MsanDeallocate(StackTrace *stack, void *ptr);
|
||||
void MsanDeallocate(BufferedStackTrace *stack, void *ptr);
|
||||
|
||||
void *msan_malloc(uptr size, StackTrace *stack);
|
||||
void *msan_calloc(uptr nmemb, uptr size, StackTrace *stack);
|
||||
void *msan_realloc(void *ptr, uptr size, StackTrace *stack);
|
||||
void *msan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack);
|
||||
void *msan_valloc(uptr size, StackTrace *stack);
|
||||
void *msan_pvalloc(uptr size, StackTrace *stack);
|
||||
void *msan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
|
||||
void *msan_memalign(uptr alignment, uptr size, StackTrace *stack);
|
||||
void *msan_malloc(uptr size, BufferedStackTrace *stack);
|
||||
void *msan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);
|
||||
void *msan_realloc(void *ptr, uptr size, BufferedStackTrace *stack);
|
||||
void *msan_reallocarray(void *ptr, uptr nmemb, uptr size,
|
||||
BufferedStackTrace *stack);
|
||||
void *msan_valloc(uptr size, BufferedStackTrace *stack);
|
||||
void *msan_pvalloc(uptr size, BufferedStackTrace *stack);
|
||||
void *msan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack);
|
||||
void *msan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack);
|
||||
int msan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
StackTrace *stack);
|
||||
BufferedStackTrace *stack);
|
||||
|
||||
void InstallTrapHandler();
|
||||
void InstallAtExitHandler();
|
||||
@ -321,6 +322,17 @@ const int STACK_TRACE_TAG_VPTR = STACK_TRACE_TAG_FIELDS + 1;
|
||||
stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); \
|
||||
}
|
||||
|
||||
#define GET_FATAL_STACK_TRACE \
|
||||
GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
|
||||
|
||||
// Unwind the stack for fatal error, as the parameter `stack` is
|
||||
// empty without origins.
|
||||
#define GET_FATAL_STACK_TRACE_IF_EMPTY(STACK) \
|
||||
if (msan_inited && (STACK)->size == 0) { \
|
||||
(STACK)->Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, \
|
||||
common_flags()->fast_unwind_on_fatal); \
|
||||
}
|
||||
|
||||
class ScopedThreadLocalStateBackup {
|
||||
public:
|
||||
ScopedThreadLocalStateBackup() { Backup(); }
|
||||
|
@ -178,18 +178,20 @@ void MsanThreadLocalMallocStorage::CommitBack() {
|
||||
allocator.DestroyCache(GetAllocatorCache(this));
|
||||
}
|
||||
|
||||
static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
|
||||
static void *MsanAllocate(BufferedStackTrace *stack, uptr size, uptr alignment,
|
||||
bool zeroise) {
|
||||
if (size > max_malloc_size) {
|
||||
if (UNLIKELY(size > max_malloc_size)) {
|
||||
if (AllocatorMayReturnNull()) {
|
||||
Report("WARNING: MemorySanitizer failed to allocate 0x%zx bytes\n", size);
|
||||
return nullptr;
|
||||
}
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportAllocationSizeTooBig(size, max_malloc_size, stack);
|
||||
}
|
||||
if (UNLIKELY(IsRssLimitExceeded())) {
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportRssLimitExceeded(stack);
|
||||
}
|
||||
MsanThread *t = GetCurrentThread();
|
||||
@ -206,6 +208,7 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
|
||||
SetAllocatorOutOfMemory();
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportOutOfMemory(size, stack);
|
||||
}
|
||||
Metadata *meta =
|
||||
@ -229,7 +232,7 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
|
||||
return allocated;
|
||||
}
|
||||
|
||||
void MsanDeallocate(StackTrace *stack, void *p) {
|
||||
void MsanDeallocate(BufferedStackTrace *stack, void *p) {
|
||||
CHECK(p);
|
||||
UnpoisonParam(1);
|
||||
RunFreeHooks(p);
|
||||
@ -259,8 +262,8 @@ void MsanDeallocate(StackTrace *stack, void *p) {
|
||||
}
|
||||
}
|
||||
|
||||
static void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size,
|
||||
uptr alignment) {
|
||||
static void *MsanReallocate(BufferedStackTrace *stack, void *old_p,
|
||||
uptr new_size, uptr alignment) {
|
||||
Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p));
|
||||
uptr old_size = meta->requested_size;
|
||||
uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
|
||||
@ -284,10 +287,11 @@ static void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size,
|
||||
return new_p;
|
||||
}
|
||||
|
||||
static void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
|
||||
static void *MsanCalloc(BufferedStackTrace *stack, uptr nmemb, uptr size) {
|
||||
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportCallocOverflow(nmemb, size, stack);
|
||||
}
|
||||
return MsanAllocate(stack, nmemb * size, sizeof(u64), true);
|
||||
@ -320,15 +324,15 @@ static uptr AllocationSizeFast(const void *p) {
|
||||
return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;
|
||||
}
|
||||
|
||||
void *msan_malloc(uptr size, StackTrace *stack) {
|
||||
void *msan_malloc(uptr size, BufferedStackTrace *stack) {
|
||||
return SetErrnoOnNull(MsanAllocate(stack, size, sizeof(u64), false));
|
||||
}
|
||||
|
||||
void *msan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
|
||||
void *msan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
|
||||
return SetErrnoOnNull(MsanCalloc(stack, nmemb, size));
|
||||
}
|
||||
|
||||
void *msan_realloc(void *ptr, uptr size, StackTrace *stack) {
|
||||
void *msan_realloc(void *ptr, uptr size, BufferedStackTrace *stack) {
|
||||
if (!ptr)
|
||||
return SetErrnoOnNull(MsanAllocate(stack, size, sizeof(u64), false));
|
||||
if (size == 0) {
|
||||
@ -338,26 +342,29 @@ void *msan_realloc(void *ptr, uptr size, StackTrace *stack) {
|
||||
return SetErrnoOnNull(MsanReallocate(stack, ptr, size, sizeof(u64)));
|
||||
}
|
||||
|
||||
void *msan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack) {
|
||||
void *msan_reallocarray(void *ptr, uptr nmemb, uptr size,
|
||||
BufferedStackTrace *stack) {
|
||||
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
|
||||
errno = errno_ENOMEM;
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportReallocArrayOverflow(nmemb, size, stack);
|
||||
}
|
||||
return msan_realloc(ptr, nmemb * size, stack);
|
||||
}
|
||||
|
||||
void *msan_valloc(uptr size, StackTrace *stack) {
|
||||
void *msan_valloc(uptr size, BufferedStackTrace *stack) {
|
||||
return SetErrnoOnNull(MsanAllocate(stack, size, GetPageSizeCached(), false));
|
||||
}
|
||||
|
||||
void *msan_pvalloc(uptr size, StackTrace *stack) {
|
||||
void *msan_pvalloc(uptr size, BufferedStackTrace *stack) {
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
|
||||
errno = errno_ENOMEM;
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportPvallocOverflow(size, stack);
|
||||
}
|
||||
// pvalloc(0) should allocate one page.
|
||||
@ -365,31 +372,34 @@ void *msan_pvalloc(uptr size, StackTrace *stack) {
|
||||
return SetErrnoOnNull(MsanAllocate(stack, size, PageSize, false));
|
||||
}
|
||||
|
||||
void *msan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
|
||||
void *msan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) {
|
||||
if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
|
||||
errno = errno_EINVAL;
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportInvalidAlignedAllocAlignment(size, alignment, stack);
|
||||
}
|
||||
return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
|
||||
}
|
||||
|
||||
void *msan_memalign(uptr alignment, uptr size, StackTrace *stack) {
|
||||
void *msan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack) {
|
||||
if (UNLIKELY(!IsPowerOfTwo(alignment))) {
|
||||
errno = errno_EINVAL;
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportInvalidAllocationAlignment(alignment, stack);
|
||||
}
|
||||
return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
|
||||
}
|
||||
|
||||
int msan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
StackTrace *stack) {
|
||||
BufferedStackTrace *stack) {
|
||||
if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
|
||||
if (AllocatorMayReturnNull())
|
||||
return errno_EINVAL;
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(stack);
|
||||
ReportInvalidPosixMemalignAlignment(alignment, stack);
|
||||
}
|
||||
void *ptr = MsanAllocate(stack, size, alignment, false);
|
||||
|
@ -30,16 +30,22 @@ namespace std {
|
||||
|
||||
|
||||
// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
|
||||
#define OPERATOR_NEW_BODY(nothrow) \
|
||||
GET_MALLOC_STACK_TRACE; \
|
||||
void *res = msan_malloc(size, &stack);\
|
||||
if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
|
||||
return res
|
||||
#define OPERATOR_NEW_BODY_ALIGN(nothrow) \
|
||||
GET_MALLOC_STACK_TRACE;\
|
||||
void *res = msan_memalign((uptr)align, size, &stack);\
|
||||
if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
|
||||
return res;
|
||||
# define OPERATOR_NEW_BODY(nothrow) \
|
||||
GET_MALLOC_STACK_TRACE; \
|
||||
void *res = msan_malloc(size, &stack); \
|
||||
if (!nothrow && UNLIKELY(!res)) { \
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(&stack); \
|
||||
ReportOutOfMemory(size, &stack); \
|
||||
} \
|
||||
return res
|
||||
# define OPERATOR_NEW_BODY_ALIGN(nothrow) \
|
||||
GET_MALLOC_STACK_TRACE; \
|
||||
void *res = msan_memalign((uptr)align, size, &stack); \
|
||||
if (!nothrow && UNLIKELY(!res)) { \
|
||||
GET_FATAL_STACK_TRACE_IF_EMPTY(&stack); \
|
||||
ReportOutOfMemory(size, &stack); \
|
||||
} \
|
||||
return res;
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
|
||||
|
151
contrib/llvm-project/compiler-rt/lib/orc/executor_symbol_def.h
Normal file
151
contrib/llvm-project/compiler-rt/lib/orc/executor_symbol_def.h
Normal file
@ -0,0 +1,151 @@
|
||||
//===--------- ExecutorSymbolDef.h - (Addr, Flags) pair ---------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Represents a defining location for a symbol in the executing program.
|
||||
//
|
||||
// This file was derived from
|
||||
// llvm/include/llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ORC_RT_EXECUTOR_SYMBOL_DEF_H
|
||||
#define ORC_RT_EXECUTOR_SYMBOL_DEF_H
|
||||
|
||||
#include "bitmask_enum.h"
|
||||
#include "executor_address.h"
|
||||
#include "simple_packed_serialization.h"
|
||||
|
||||
namespace __orc_rt {
|
||||
|
||||
/// Flags for symbols in the JIT.
|
||||
class JITSymbolFlags {
|
||||
public:
|
||||
using UnderlyingType = uint8_t;
|
||||
using TargetFlagsType = uint8_t;
|
||||
|
||||
/// These values must be kept in sync with \c JITSymbolFlags in the JIT.
|
||||
enum FlagNames : UnderlyingType {
|
||||
None = 0,
|
||||
HasError = 1U << 0,
|
||||
Weak = 1U << 1,
|
||||
Common = 1U << 2,
|
||||
Absolute = 1U << 3,
|
||||
Exported = 1U << 4,
|
||||
Callable = 1U << 5,
|
||||
MaterializationSideEffectsOnly = 1U << 6,
|
||||
ORC_RT_MARK_AS_BITMASK_ENUM( // LargestValue =
|
||||
MaterializationSideEffectsOnly)
|
||||
};
|
||||
|
||||
/// Default-construct a JITSymbolFlags instance.
|
||||
JITSymbolFlags() = default;
|
||||
|
||||
/// Construct a JITSymbolFlags instance from the given flags and target
|
||||
/// flags.
|
||||
JITSymbolFlags(FlagNames Flags, TargetFlagsType TargetFlags)
|
||||
: TargetFlags(TargetFlags), Flags(Flags) {}
|
||||
|
||||
bool operator==(const JITSymbolFlags &RHS) const {
|
||||
return Flags == RHS.Flags && TargetFlags == RHS.TargetFlags;
|
||||
}
|
||||
|
||||
/// Get the underlying flags value as an integer.
|
||||
UnderlyingType getRawFlagsValue() const {
|
||||
return static_cast<UnderlyingType>(Flags);
|
||||
}
|
||||
|
||||
/// Return a reference to the target-specific flags.
|
||||
TargetFlagsType &getTargetFlags() { return TargetFlags; }
|
||||
|
||||
/// Return a reference to the target-specific flags.
|
||||
const TargetFlagsType &getTargetFlags() const { return TargetFlags; }
|
||||
|
||||
private:
|
||||
TargetFlagsType TargetFlags = 0;
|
||||
FlagNames Flags = None;
|
||||
};
|
||||
|
||||
/// Represents a defining location for a JIT symbol.
|
||||
class ExecutorSymbolDef {
|
||||
public:
|
||||
ExecutorSymbolDef() = default;
|
||||
ExecutorSymbolDef(ExecutorAddr Addr, JITSymbolFlags Flags)
|
||||
: Addr(Addr), Flags(Flags) {}
|
||||
|
||||
const ExecutorAddr &getAddress() const { return Addr; }
|
||||
|
||||
const JITSymbolFlags &getFlags() const { return Flags; }
|
||||
|
||||
friend bool operator==(const ExecutorSymbolDef &LHS,
|
||||
const ExecutorSymbolDef &RHS) {
|
||||
return LHS.getAddress() == RHS.getAddress() &&
|
||||
LHS.getFlags() == RHS.getFlags();
|
||||
}
|
||||
|
||||
private:
|
||||
ExecutorAddr Addr;
|
||||
JITSymbolFlags Flags;
|
||||
};
|
||||
|
||||
using SPSJITSymbolFlags =
|
||||
SPSTuple<JITSymbolFlags::UnderlyingType, JITSymbolFlags::TargetFlagsType>;
|
||||
|
||||
/// SPS serializatior for JITSymbolFlags.
|
||||
template <> class SPSSerializationTraits<SPSJITSymbolFlags, JITSymbolFlags> {
|
||||
using FlagsArgList = SPSJITSymbolFlags::AsArgList;
|
||||
|
||||
public:
|
||||
static size_t size(const JITSymbolFlags &F) {
|
||||
return FlagsArgList::size(F.getRawFlagsValue(), F.getTargetFlags());
|
||||
}
|
||||
|
||||
static bool serialize(SPSOutputBuffer &BOB, const JITSymbolFlags &F) {
|
||||
return FlagsArgList::serialize(BOB, F.getRawFlagsValue(),
|
||||
F.getTargetFlags());
|
||||
}
|
||||
|
||||
static bool deserialize(SPSInputBuffer &BIB, JITSymbolFlags &F) {
|
||||
JITSymbolFlags::UnderlyingType RawFlags;
|
||||
JITSymbolFlags::TargetFlagsType TargetFlags;
|
||||
if (!FlagsArgList::deserialize(BIB, RawFlags, TargetFlags))
|
||||
return false;
|
||||
F = JITSymbolFlags{static_cast<JITSymbolFlags::FlagNames>(RawFlags),
|
||||
TargetFlags};
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using SPSExecutorSymbolDef = SPSTuple<SPSExecutorAddr, SPSJITSymbolFlags>;
|
||||
|
||||
/// SPS serializatior for ExecutorSymbolDef.
|
||||
template <>
|
||||
class SPSSerializationTraits<SPSExecutorSymbolDef, ExecutorSymbolDef> {
|
||||
using DefArgList = SPSExecutorSymbolDef::AsArgList;
|
||||
|
||||
public:
|
||||
static size_t size(const ExecutorSymbolDef &ESD) {
|
||||
return DefArgList::size(ESD.getAddress(), ESD.getFlags());
|
||||
}
|
||||
|
||||
static bool serialize(SPSOutputBuffer &BOB, const ExecutorSymbolDef &ESD) {
|
||||
return DefArgList::serialize(BOB, ESD.getAddress(), ESD.getFlags());
|
||||
}
|
||||
|
||||
static bool deserialize(SPSInputBuffer &BIB, ExecutorSymbolDef &ESD) {
|
||||
ExecutorAddr Addr;
|
||||
JITSymbolFlags Flags;
|
||||
if (!DefArgList::deserialize(BIB, Addr, Flags))
|
||||
return false;
|
||||
ESD = ExecutorSymbolDef{Addr, Flags};
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // End namespace __orc_rt
|
||||
|
||||
#endif // ORC_RT_EXECUTOR_SYMBOL_DEF_H
|
@ -0,0 +1,19 @@
|
||||
//===-- executor_symbol_def_test.cpp --------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "executor_symbol_def.h"
|
||||
#include "simple_packed_serialization_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace __orc_rt;
|
||||
|
||||
TEST(ExecutorSymbolDefTest, Serialization) {
|
||||
blobSerializationRoundTrip<SPSExecutorSymbolDef>(ExecutorSymbolDef{});
|
||||
blobSerializationRoundTrip<SPSExecutorSymbolDef>(
|
||||
ExecutorSymbolDef{ExecutorAddr{0x70}, {JITSymbolFlags::Callable, 9}});
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "simple_packed_serialization.h"
|
||||
#include "simple_packed_serialization_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace __orc_rt;
|
||||
@ -48,25 +49,6 @@ TEST(SimplePackedSerializationTest, SPSInputBuffer) {
|
||||
EXPECT_FALSE(IB.read(&C, 1));
|
||||
}
|
||||
|
||||
template <typename SPSTagT, typename T>
|
||||
static void blobSerializationRoundTrip(const T &Value) {
|
||||
using BST = SPSSerializationTraits<SPSTagT, T>;
|
||||
|
||||
size_t Size = BST::size(Value);
|
||||
auto Buffer = std::make_unique<char[]>(Size);
|
||||
SPSOutputBuffer OB(Buffer.get(), Size);
|
||||
|
||||
EXPECT_TRUE(BST::serialize(OB, Value));
|
||||
|
||||
SPSInputBuffer IB(Buffer.get(), Size);
|
||||
|
||||
T DSValue;
|
||||
EXPECT_TRUE(BST::deserialize(IB, DSValue));
|
||||
|
||||
EXPECT_EQ(Value, DSValue)
|
||||
<< "Incorrect value after serialization/deserialization round-trip";
|
||||
}
|
||||
|
||||
template <typename T> static void testFixedIntegralTypeSerialization() {
|
||||
blobSerializationRoundTrip<T, T>(0);
|
||||
blobSerializationRoundTrip<T, T>(static_cast<T>(1));
|
||||
|
@ -0,0 +1,34 @@
|
||||
//===-- simple_packed_serialization_utils.h -------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ORC_RT_TEST_SIMPLE_PACKED_SERIALIZATION_UTILS_H
|
||||
#define ORC_RT_TEST_SIMPLE_PACKED_SERIALIZATION_UTILS_H
|
||||
|
||||
#include "simple_packed_serialization.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
template <typename SPSTagT, typename T>
|
||||
static void blobSerializationRoundTrip(const T &Value) {
|
||||
using BST = __orc_rt::SPSSerializationTraits<SPSTagT, T>;
|
||||
|
||||
size_t Size = BST::size(Value);
|
||||
auto Buffer = std::make_unique<char[]>(Size);
|
||||
__orc_rt::SPSOutputBuffer OB(Buffer.get(), Size);
|
||||
|
||||
EXPECT_TRUE(BST::serialize(OB, Value));
|
||||
|
||||
__orc_rt::SPSInputBuffer IB(Buffer.get(), Size);
|
||||
|
||||
T DSValue;
|
||||
EXPECT_TRUE(BST::deserialize(IB, DSValue));
|
||||
|
||||
EXPECT_EQ(Value, DSValue)
|
||||
<< "Incorrect value after serialization/deserialization round-trip";
|
||||
}
|
||||
|
||||
#endif // ORC_RT_TEST_SIMPLE_PACKED_SERIALIZATION_UTILS_H
|
@ -32,6 +32,7 @@ struct AddressInfo;
|
||||
struct BufferedStackTrace;
|
||||
struct SignalContext;
|
||||
struct StackTrace;
|
||||
struct SymbolizedStack;
|
||||
|
||||
// Constants.
|
||||
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
|
||||
@ -393,6 +394,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info,
|
||||
// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
|
||||
void ReportErrorSummary(const char *error_type, const StackTrace *trace,
|
||||
const char *alt_tool_name = nullptr);
|
||||
// Skips frames which we consider internal and not usefull to the users.
|
||||
const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames);
|
||||
|
||||
void ReportMmapWriteExec(int prot, int mflags);
|
||||
|
||||
|
@ -191,7 +191,8 @@
|
||||
|
||||
#define SANITIZER_INTERCEPT_PREADV \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PWRITEV \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_PREADV64 SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_PWRITEV64 SI_GLIBC
|
||||
|
||||
|
@ -33,13 +33,14 @@ class StackTraceTextPrinter {
|
||||
stack_trace_fmt)) {}
|
||||
|
||||
bool ProcessAddressFrames(uptr pc) {
|
||||
SymbolizedStack *frames = symbolize_
|
||||
? Symbolizer::GetOrInit()->SymbolizePC(pc)
|
||||
: SymbolizedStack::New(pc);
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc)
|
||||
: SymbolizedStack::New(pc));
|
||||
const SymbolizedStack *frames = symbolized_stack.get();
|
||||
if (!frames)
|
||||
return false;
|
||||
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
uptr prev_len = output_->length();
|
||||
StackTracePrinter::GetOrInit()->RenderFrame(
|
||||
output_, stack_trace_fmt_, frame_num_++, cur->info.address,
|
||||
@ -51,13 +52,12 @@ class StackTraceTextPrinter {
|
||||
|
||||
ExtendDedupToken(cur);
|
||||
}
|
||||
frames->ClearAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Extend the dedup token by appending a new frame.
|
||||
void ExtendDedupToken(SymbolizedStack *stack) {
|
||||
void ExtendDedupToken(const SymbolizedStack *stack) {
|
||||
if (!dedup_token_)
|
||||
return;
|
||||
|
||||
|
@ -64,6 +64,26 @@ struct SymbolizedStack {
|
||||
SymbolizedStack();
|
||||
};
|
||||
|
||||
class SymbolizedStackHolder {
|
||||
SymbolizedStack *Stack;
|
||||
|
||||
void clear() {
|
||||
if (Stack)
|
||||
Stack->ClearAll();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr)
|
||||
: Stack(Stack) {}
|
||||
~SymbolizedStackHolder() { clear(); }
|
||||
void reset(SymbolizedStack *S = nullptr) {
|
||||
if (Stack != S)
|
||||
clear();
|
||||
Stack = S;
|
||||
}
|
||||
const SymbolizedStack *get() const { return Stack; }
|
||||
};
|
||||
|
||||
// For now, DataInfo is used to describe global variable.
|
||||
struct DataInfo {
|
||||
// Owns all the string members. Storage for them is
|
||||
|
@ -28,6 +28,26 @@
|
||||
namespace __sanitizer {
|
||||
|
||||
#if !SANITIZER_GO
|
||||
|
||||
static bool FrameIsInternal(const SymbolizedStack *frame) {
|
||||
if (!frame)
|
||||
return true;
|
||||
const char *file = frame->info.file;
|
||||
const char *module = frame->info.module;
|
||||
if (file && (internal_strstr(file, "/compiler-rt/lib/")))
|
||||
return true;
|
||||
if (module && (internal_strstr(module, "libclang_rt.")))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) {
|
||||
for (const SymbolizedStack *f = frames; f; f = f->next)
|
||||
if (!FrameIsInternal(f))
|
||||
return f;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ReportErrorSummary(const char *error_type, const AddressInfo &info,
|
||||
const char *alt_tool_name) {
|
||||
if (!common_flags()->print_summary) return;
|
||||
@ -82,9 +102,10 @@ void ReportErrorSummary(const char *error_type, const StackTrace *stack,
|
||||
// Currently, we include the first stack frame into the report summary.
|
||||
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
||||
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
|
||||
SymbolizedStackHolder symbolized_stack(
|
||||
Symbolizer::GetOrInit()->SymbolizePC(pc));
|
||||
const SymbolizedStack *frame = symbolized_stack.get();
|
||||
ReportErrorSummary(error_type, frame->info, alt_tool_name);
|
||||
frame->ClearAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user