diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -655,6 +655,10 @@ "-fjmc works only for ELF; option ignored">, InGroup; +def warn_target_override_arm64ec : Warning< + "/arm64EC has been overridden by specified target: %0; option ignored">, + InGroup; + def err_drv_target_variant_invalid : Error< "unsupported '%0' value '%1'; use 'ios-macabi' instead">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6695,6 +6695,8 @@ def _SLASH_QIntel_jcc_erratum : CLFlag<"QIntel-jcc-erratum">, HelpText<"Align branches within 32-byte boundaries to mitigate the performance impact of the Intel JCC erratum.">, Alias; +def _SLASH_arm64EC : CLFlag<"arm64EC">, + HelpText<"Set build target to arm64ec">; // Non-aliases: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -582,7 +582,12 @@ SymbolMapTy TheLiving; SymbolSetTy MetadataInUse; - RegionSetTy RegionRoots; + RegionSetTy LiveRegionRoots; + // The lazily copied regions are locations for which a program + // can access the value stored at that location, but not its address. + // These regions are constructed as a set of regions referred to by + // lazyCompoundVal. + RegionSetTy LazilyCopiedRegionRoots; const StackFrameContext *LCtx; const Stmt *Loc; @@ -628,8 +633,8 @@ using region_iterator = RegionSetTy::const_iterator; - region_iterator region_begin() const { return RegionRoots.begin(); } - region_iterator region_end() const { return RegionRoots.end(); } + region_iterator region_begin() const { return LiveRegionRoots.begin(); } + region_iterator region_end() const { return LiveRegionRoots.end(); } /// Returns whether or not a symbol has been confirmed dead. /// @@ -640,6 +645,7 @@ } void markLive(const MemRegion *region); + void markLazilyCopied(const MemRegion *region); void markElementIndicesLive(const MemRegion *region); /// Set to the value of the symbolic store after @@ -647,6 +653,12 @@ void setReapedStore(StoreRef st) { reapedStore = st; } private: + bool isLazilyCopiedRegion(const MemRegion *region) const; + // A readable region is a region that live or lazily copied. + // Any symbols that refer to values in regions are alive if the region + // is readable. + bool isReadableRegion(const MemRegion *region); + /// Mark the symbols dependent on the input symbol as live. void markDependentsLive(SymbolRef sym); }; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1256,6 +1256,8 @@ T.setVendor(llvm::Triple::PC); T.setEnvironment(llvm::Triple::MSVC); T.setObjectFormat(llvm::Triple::COFF); + if (Args.hasArg(options::OPT__SLASH_arm64EC)) + T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec); TargetTriple = T.str(); } else if (IsDXCMode()) { // Build TargetTriple from target_profile option for clang-dxc. @@ -1380,6 +1382,14 @@ const ToolChain &TC = getToolChain( *UArgs, computeTargetTriple(*this, TargetTriple, *UArgs)); + // Report warning when arm64EC option is overridden by specified target + if ((TC.getTriple().getArch() != llvm::Triple::aarch64 || + TC.getTriple().getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) && + UArgs->hasArg(options::OPT__SLASH_arm64EC)) { + getDiags().Report(clang::diag::warn_target_override_arm64ec) + << TC.getTriple().str(); + } + // The compilation takes ownership of Args. Compilation *C = new Compilation(*this, TC, UArgs.release(), TranslatedArgs, ContainsError); diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -2838,6 +2838,10 @@ // Is it a LazyCompoundVal? All referenced regions are live as well. if (Optional LCS = V.getAs()) { + // TODO: Make regions referred to by `lazyCompoundVals` that are bound to + // subregions of the `LCS.getRegion()` also lazily copied. + if (const MemRegion *R = LCS->getRegion()) + SymReaper.markLazilyCopied(R); const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp --- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -411,10 +411,14 @@ } void SymbolReaper::markLive(const MemRegion *region) { - RegionRoots.insert(region->getBaseRegion()); + LiveRegionRoots.insert(region->getBaseRegion()); markElementIndicesLive(region); } +void SymbolReaper::markLazilyCopied(const clang::ento::MemRegion *region) { + LazilyCopiedRegionRoots.insert(region->getBaseRegion()); +} + void SymbolReaper::markElementIndicesLive(const MemRegion *region) { for (auto SR = dyn_cast(region); SR; SR = dyn_cast(SR->getSuperRegion())) { @@ -437,8 +441,7 @@ // is not used later in the path, we can diagnose a leak of a value within // that field earlier than, say, the variable that contains the field dies. MR = MR->getBaseRegion(); - - if (RegionRoots.count(MR)) + if (LiveRegionRoots.count(MR)) return true; if (const auto *SR = dyn_cast(MR)) @@ -454,6 +457,15 @@ return isa(MR); } +bool SymbolReaper::isLazilyCopiedRegion(const MemRegion *MR) const { + // TODO: See comment in isLiveRegion. + return LazilyCopiedRegionRoots.count(MR->getBaseRegion()); +} + +bool SymbolReaper::isReadableRegion(const MemRegion *MR) { + return isLiveRegion(MR) || isLazilyCopiedRegion(MR); +} + bool SymbolReaper::isLive(SymbolRef sym) { if (TheLiving.count(sym)) { markDependentsLive(sym); @@ -464,7 +476,7 @@ switch (sym->getKind()) { case SymExpr::SymbolRegionValueKind: - KnownLive = isLiveRegion(cast(sym)->getRegion()); + KnownLive = isReadableRegion(cast(sym)->getRegion()); break; case SymExpr::SymbolConjuredKind: KnownLive = false; diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp --- a/clang/test/Analysis/NewDeleteLeaks.cpp +++ b/clang/test/Analysis/NewDeleteLeaks.cpp @@ -192,3 +192,29 @@ // expected-note@-1 {{Potential leak of memory pointed to by 'v'}} } // namespace refkind_from_unoallocated_to_allocated + +// Check that memory leak is reported against a symbol if the last place it's +// mentioned is a base region of a lazy compound value, as the program cannot +// possibly free that memory. +namespace symbol_reaper_lifetime { +struct Nested { + int buf[2]; +}; +struct Wrapping { + Nested data; +}; + +Nested allocateWrappingAndReturnNested() { + // expected-note@+1 {{Memory is allocated}} + Wrapping const* p = new Wrapping(); + // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}} + // expected-note@+1 {{Potential leak of memory pointed to by 'p'}} + return p->data; +} + +void caller() { + // expected-note@+1 {{Calling 'allocateWrappingAndReturnNested'}} + Nested n = allocateWrappingAndReturnNested(); + (void)n; +} // no-warning: No potential memory leak here, because that's been already reported. +} // namespace symbol_reaper_lifetime diff --git a/clang/test/Analysis/symbol-reaper-lambda.cpp b/clang/test/Analysis/symbol-reaper-lambda.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/symbol-reaper-lambda.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s +// expected-no-diagnostics + +template +void escape(Ts&...); +struct Dummy {}; + +int strange(Dummy param) { + Dummy local_pre_lambda; + int ref_captured = 0; + + // LambdaExpr is modeled as lazyCompoundVal of tempRegion, that contains + // all captures. In this instance, this region contains a pointer/reference + // to ref_captured variable. + auto fn = [&] { + escape(param, local_pre_lambda); + return ref_captured; // no-warning: The value is not garbage. + }; + + int local_defined_after_lambda; // Unused, but necessary! Important that it's before the call. + + // The ref_captured binding should not be pruned at this point, as it is still + // accessed via reference captured in operator() of fn. + return fn(); +} + diff --git a/clang/test/Analysis/trivial-copy-struct.cpp b/clang/test/Analysis/trivial-copy-struct.cpp --- a/clang/test/Analysis/trivial-copy-struct.cpp +++ b/clang/test/Analysis/trivial-copy-struct.cpp @@ -1,6 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s template void clang_analyzer_dump(T); +template void clang_analyzer_value(T); void clang_analyzer_warnIfReached(); struct Node { int* ptr; }; @@ -34,3 +35,67 @@ (void)(n1->ptr); (void)(n2->ptr); } + +struct List { + List* next; + int value; + int padding; +}; + +void deadCode(List orig) { + List c = orig; + clang_analyzer_dump(c.value); + // expected-warning-re@-1 {{reg_${{[0-9]+}}}} + if (c.value == 42) + return; + clang_analyzer_value(c.value); + // expected-warning@-1 {{32s:{ [-2147483648, 41], [43, 2147483647] }}} + // Before symbol was garbage collected too early, and we lost the constraints. + if (c.value != 42) + return; + + clang_analyzer_warnIfReached(); // no-warning: Dead code. +}; + +void ptr1(List* n) { + List* n2 = new List(*n); // cctor + if (!n->next) { + if (n2->next) { + clang_analyzer_warnIfReached(); // unreachable + } + } + delete n2; +} + +void ptr2(List* n) { + List* n2 = new List(); // ctor + *n2 = *n; // assignment + if (!n->next) { + if (n2->next) { + clang_analyzer_warnIfReached(); // unreachable + } + } + delete n2; +} + +struct Wrapper { + List head; + int count; +}; + +void nestedLazyCompoundVal(List* n) { + Wrapper* w = 0; + { + Wrapper lw; + lw.head = *n; + w = new Wrapper(lw); + } + if (!n->next) { + if (w->head.next) { + // Unreachable, w->head is a copy of *n, therefore + // w->head.next and n->next are equal + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } + } + delete w; +} diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -782,4 +782,8 @@ // EXTERNAL_W0: "-Wno-system-headers" // EXTERNAL_Wn: "-Wsystem-headers" +// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -### -- %s 2>&1 | FileCheck %s --check-prefix ARM64EC +// ARM64EC: "-triple" "arm64ec-pc-windows-msvc19.20.0" +// ARM64EC-NOT: /arm64EC has been overridden by specified target + void f(void) { } diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -392,53 +392,53 @@ } } -void macho::reportPendingUndefinedSymbols() { - for (const auto &undef : undefs) { - const UndefinedDiag &locations = undef.second; - - std::string message = "undefined symbol"; - if (config->archMultiple) - message += (" for arch " + getArchitectureName(config->arch())).str(); - message += ": " + toString(*undef.first); - - const size_t maxUndefinedReferences = 3; - size_t i = 0; - for (const std::string &loc : locations.otherReferences) { - if (i >= maxUndefinedReferences) - break; - message += "\n>>> referenced by " + loc; - ++i; - } - - for (const UndefinedDiag::SectionAndOffset &loc : - locations.codeReferences) { - if (i >= maxUndefinedReferences) - break; - message += "\n>>> referenced by "; - std::string src = loc.isec->getSourceLocation(loc.offset); - if (!src.empty()) - message += src + "\n>>> "; - message += loc.isec->getLocation(loc.offset); - ++i; - } +static void reportUndefinedSymbol(const Undefined &sym, + const UndefinedDiag &locations) { + std::string message = "undefined symbol"; + if (config->archMultiple) + message += (" for arch " + getArchitectureName(config->arch())).str(); + message += ": " + toString(sym); + + const size_t maxUndefinedReferences = 3; + size_t i = 0; + for (const std::string &loc : locations.otherReferences) { + if (i >= maxUndefinedReferences) + break; + message += "\n>>> referenced by " + loc; + ++i; + } - size_t totalReferences = - locations.otherReferences.size() + locations.codeReferences.size(); - if (totalReferences > i) - message += - ("\n>>> referenced " + Twine(totalReferences - i) + " more times") - .str(); - - if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error) - error(message); - else if (config->undefinedSymbolTreatment == - UndefinedSymbolTreatment::warning) - warn(message); - else - assert(false && - "diagnostics make sense for -undefined error|warning only"); + for (const UndefinedDiag::SectionAndOffset &loc : locations.codeReferences) { + if (i >= maxUndefinedReferences) + break; + message += "\n>>> referenced by "; + std::string src = loc.isec->getSourceLocation(loc.offset); + if (!src.empty()) + message += src + "\n>>> "; + message += loc.isec->getLocation(loc.offset); + ++i; } + size_t totalReferences = + locations.otherReferences.size() + locations.codeReferences.size(); + if (totalReferences > i) + message += + ("\n>>> referenced " + Twine(totalReferences - i) + " more times") + .str(); + + if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error) + error(message); + else if (config->undefinedSymbolTreatment == + UndefinedSymbolTreatment::warning) + warn(message); + else + assert(false && "diagnostics make sense for -undefined error|warning only"); +} + +void macho::reportPendingUndefinedSymbols() { + for (const auto &undef : undefs) + reportUndefinedSymbol(*undef.first, undef.second); + // This function is called multiple times during execution. Clear the printed // diagnostics to avoid printing the same things again the next time. undefs.clear(); diff --git a/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td b/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td --- a/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td +++ b/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td @@ -705,14 +705,14 @@ - the new scf.foreach_thread op, - the tiled op that implements TilingInterface. - ### Example using `num_threads` + #### Example using `num_threads` ``` %0 = pdl_match @match_matmul in %arg1 %3:2 = transform.structured.tile_to_foreach_thread_op %0 num_threads [10, 20] ``` - ### Example using `tile_sizes` + #### Example using `tile_sizes` ``` %0 = pdl_match @match_matmul in %arg1 diff --git a/openmp/runtime/src/kmp_sched.cpp b/openmp/runtime/src/kmp_sched.cpp --- a/openmp/runtime/src/kmp_sched.cpp +++ b/openmp/runtime/src/kmp_sched.cpp @@ -83,6 +83,9 @@ KMP_PUSH_PARTITIONED_TIMER(OMP_loop_static); KMP_PUSH_PARTITIONED_TIMER(OMP_loop_static_scheduling); + // Clear monotonic/nonmonotonic bits (ignore it) + schedtype = SCHEDULE_WITHOUT_MODIFIERS(schedtype); + typedef typename traits_t::unsigned_t UT; typedef typename traits_t::signed_t ST; /* this all has to be changed back to TID and such.. */