1
0
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:
Dimitry Andric 2024-01-09 21:00:28 +01:00
commit 1db9f3b21e
691 changed files with 13947 additions and 6424 deletions

View File

@ -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"

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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 *>;

View File

@ -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>,

View File

@ -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
}];

View File

@ -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)

View File

@ -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;

View File

@ -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">;
}

View File

@ -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<

View File

@ -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">;

View File

@ -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;

View File

@ -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

View File

@ -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>]>;
}

View File

@ -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> {

View File

@ -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> {

View File

@ -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">;

View File

@ -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,

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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();

View File

@ -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());

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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))

View File

@ -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;

View File

@ -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",

View File

@ -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)

View File

@ -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)

View File

@ -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());
}
}
}

View File

@ -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__");

View File

@ -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);

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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") {

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -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",

View File

@ -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) {

View File

@ -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);
}

View File

@ -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

View File

@ -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()) {

View File

@ -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) &&

View File

@ -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,

View File

@ -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"));

View File

@ -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();

View File

@ -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))

View File

@ -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);

View File

@ -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,

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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")) {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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();

View File

@ -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))));

View File

@ -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}},

View File

@ -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);

View File

@ -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";
}

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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(); }

View File

@ -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);

View File

@ -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*/); }

View 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

View File

@ -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}});
}

View File

@ -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));

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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