diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -240,7 +240,7 @@ LANGOPT(CUDADeviceApproxTranscendentals, 1, 0, "using approximate transcendental functions") LANGOPT(GPURelocatableDeviceCode, 1, 0, "generate relocatable device code") LANGOPT(GPUAllowDeviceInit, 1, 0, "allowing device side global init functions for HIP") -LANGOPT(GPUMaxThreadsPerBlock, 32, 1024, "default max threads per block for kernel launch bounds for HIP") +LANGOPT(GPUMaxThreadsPerBlock, 32, 256, "default max threads per block for kernel launch bounds for HIP") LANGOPT(SYCL , 1, 0, "SYCL") LANGOPT(SYCLIsDevice , 1, 0, "Generate code for SYCL device") diff --git a/clang/include/clang/Tooling/Transformer/RewriteRule.h b/clang/include/clang/Tooling/Transformer/RewriteRule.h --- a/clang/include/clang/Tooling/Transformer/RewriteRule.h +++ b/clang/include/clang/Tooling/Transformer/RewriteRule.h @@ -380,6 +380,38 @@ // RewriteRule API. Recast them as such. Or, just declare these functions // public and well-supported and move them out of `detail`. namespace detail { +/// The following overload set is a version of `rewriteDescendants` that +/// operates directly on the AST, rather than generating a Transformer +/// combinator. It applies `Rule` to all descendants of `Node`, although not +/// `Node` itself. `Rule` can refer to nodes bound in `Result`. +/// +/// For example, assuming that "body" is bound to a function body in MatchResult +/// `Results`, this will produce edits to change all appearances of `x` in that +/// body to `3`. +/// ``` +/// auto InlineX = +/// makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); +/// const auto *Node = Results.Nodes.getNodeAs("body"); +/// auto Edits = rewriteDescendants(*Node, InlineX, Results); +/// ``` +/// @{ +llvm::Expected> +rewriteDescendants(const Decl &Node, RewriteRule Rule, + const ast_matchers::MatchFinder::MatchResult &Result); + +llvm::Expected> +rewriteDescendants(const Stmt &Node, RewriteRule Rule, + const ast_matchers::MatchFinder::MatchResult &Result); + +llvm::Expected> +rewriteDescendants(const TypeLoc &Node, RewriteRule Rule, + const ast_matchers::MatchFinder::MatchResult &Result); + +llvm::Expected> +rewriteDescendants(const DynTypedNode &Node, RewriteRule Rule, + const ast_matchers::MatchFinder::MatchResult &Result); +/// @} + /// Builds a single matcher for the rule, covering all of the rule's cases. /// Only supports Rules whose cases' matchers share the same base "kind" /// (`Stmt`, `Decl`, etc.) Deprecated: use `buildMatchers` instead, which diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -52,10 +52,8 @@ textual header "Basic/BuiltinsX86_64.def" textual header "Basic/BuiltinsXCore.def" textual header "Basic/CodeGenOptions.def" - textual header "Basic/CommentOptions.def" textual header "Basic/DiagnosticOptions.def" textual header "Basic/Features.def" - textual header "Basic/FileSystemOptions.def" textual header "Basic/FPOptions.def" textual header "Basic/MSP430Target.def" textual header "Basic/LangOptions.def" @@ -65,7 +63,6 @@ textual header "Basic/OpenMPKinds.def" textual header "Basic/OperatorKinds.def" textual header "Basic/Sanitizers.def" - textual header "Basic/TargetOptions.def" textual header "Basic/TokenKinds.def" textual header "Basic/X86Target.def" @@ -110,35 +107,17 @@ umbrella "Frontend" textual header "Basic/LangStandards.def" - textual header "Frontend/DependencyOutputOptions.def" - textual header "Frontend/FrontendOptions.def" - textual header "Frontend/MigratorOptions.def" - textual header "Frontend/PreprocessorOutputOptions.def" module * { export * } } module Clang_FrontendTool { requires cplusplus umbrella "FrontendTool" module * { export * } } module Clang_Index { requires cplusplus umbrella "Index" module * { export * } } -module Clang_Lex { - requires cplusplus - umbrella "Lex" - textual header "Lex/HeaderSearchOptions.def" - textual header "Lex/PreprocessorOptions.def" - - module * { export * } -} +module Clang_Lex { requires cplusplus umbrella "Lex" module * { export * } } module Clang_Parse { requires cplusplus umbrella "Parse" module * { export * } } module Clang_Rewrite { requires cplusplus umbrella "Rewrite/Core" module * { export * } } module Clang_RewriteFrontend { requires cplusplus umbrella "Rewrite/Frontend" module * { export * } } -module Clang_Sema { - requires cplusplus - umbrella "Sema" - - textual header "Sema/CodeCompleteOptions.def" - - module * { export * } -} +module Clang_Sema { requires cplusplus umbrella "Sema" module * { export * } } module Clang_Serialization { requires cplusplus diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -9066,13 +9066,9 @@ assert(Max == 0 && "Max must be zero"); } else if (IsOpenCLKernel || IsHIPKernel) { // By default, restrict the maximum size to a value specified by - // --gpu-max-threads-per-block=n or its default value for HIP. - const unsigned OpenCLDefaultMaxWorkGroupSize = 256; - const unsigned DefaultMaxWorkGroupSize = - IsOpenCLKernel ? OpenCLDefaultMaxWorkGroupSize - : M.getLangOpts().GPUMaxThreadsPerBlock; + // --gpu-max-threads-per-block=n or its default value. std::string AttrVal = - std::string("1,") + llvm::utostr(DefaultMaxWorkGroupSize); + std::string("1,") + llvm::utostr(M.getLangOpts().GPUMaxThreadsPerBlock); F->addFnAttr("amdgpu-flat-work-group-size", AttrVal); } diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -132,15 +132,38 @@ return Missing; } + // The ModuleManager's use of FileEntry nodes as the keys for its map of + // loaded modules is less than ideal. Uniqueness for FileEntry nodes is + // maintained by FileManager, which in turn uses inode numbers on hosts + // that support that. When coupled with the module cache's proclivity for + // turning over and deleting stale PCMs, this means entries for different + // module files can wind up reusing the same underlying inode. When this + // happens, subsequent accesses to the Modules map will disagree on the + // ModuleFile associated with a given file. In general, it is not sufficient + // to resolve this conundrum with a type like FileEntryRef that stores the + // name of the FileEntry node on first access because of path canonicalization + // issues. However, the paths constructed for implicit module builds are + // fully under Clang's control. We *can*, therefore, rely on their structure + // being consistent across operating systems and across subsequent accesses + // to the Modules map. + auto implicitModuleNamesMatch = [](ModuleKind Kind, const ModuleFile *MF, + const FileEntry *Entry) -> bool { + if (Kind != MK_ImplicitModule) + return true; + return Entry->getName() == MF->FileName; + }; + // Check whether we already loaded this module, before if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) { - // Check the stored signature. - if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) - return OutOfDate; - - Module = ModuleEntry; - updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc); - return AlreadyLoaded; + if (implicitModuleNamesMatch(Type, ModuleEntry, Entry)) { + // Check the stored signature. + if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) + return OutOfDate; + + Module = ModuleEntry; + updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc); + return AlreadyLoaded; + } } // Allocate a new module. diff --git a/clang/lib/Tooling/Transformer/RewriteRule.cpp b/clang/lib/Tooling/Transformer/RewriteRule.cpp --- a/clang/lib/Tooling/Transformer/RewriteRule.cpp +++ b/clang/lib/Tooling/Transformer/RewriteRule.cpp @@ -242,7 +242,7 @@ } // namespace template -static llvm::Expected> +llvm::Expected> rewriteDescendantsImpl(const T &Node, RewriteRule Rule, const MatchResult &Result) { ApplyRuleCallback Callback(std::move(Rule)); @@ -252,10 +252,43 @@ return std::move(Callback.Edits); } +llvm::Expected> +transformer::detail::rewriteDescendants(const Decl &Node, RewriteRule Rule, + const MatchResult &Result) { + return rewriteDescendantsImpl(Node, std::move(Rule), Result); +} + +llvm::Expected> +transformer::detail::rewriteDescendants(const Stmt &Node, RewriteRule Rule, + const MatchResult &Result) { + return rewriteDescendantsImpl(Node, std::move(Rule), Result); +} + +llvm::Expected> +transformer::detail::rewriteDescendants(const TypeLoc &Node, RewriteRule Rule, + const MatchResult &Result) { + return rewriteDescendantsImpl(Node, std::move(Rule), Result); +} + +llvm::Expected> +transformer::detail::rewriteDescendants(const DynTypedNode &DNode, + RewriteRule Rule, + const MatchResult &Result) { + if (auto *Node = DNode.get()) + return rewriteDescendantsImpl(*Node, std::move(Rule), Result); + if (auto *Node = DNode.get()) + return rewriteDescendantsImpl(*Node, std::move(Rule), Result); + if (auto *Node = DNode.get()) + return rewriteDescendantsImpl(*Node, std::move(Rule), Result); + + return llvm::make_error( + llvm::errc::invalid_argument, + "type unsupported for recursive rewriting, Kind=" + + DNode.getNodeKind().asStringRef()); +} + EditGenerator transformer::rewriteDescendants(std::string NodeId, RewriteRule Rule) { - // FIXME: warn or return error if `Rule` contains any `AddedIncludes`, since - // these will be dropped. return [NodeId = std::move(NodeId), Rule = std::move(Rule)](const MatchResult &Result) -> llvm::Expected> { @@ -265,17 +298,7 @@ if (It == NodesMap.end()) return llvm::make_error(llvm::errc::invalid_argument, "ID not bound: " + NodeId); - if (auto *Node = It->second.get()) - return rewriteDescendantsImpl(*Node, std::move(Rule), Result); - if (auto *Node = It->second.get()) - return rewriteDescendantsImpl(*Node, std::move(Rule), Result); - if (auto *Node = It->second.get()) - return rewriteDescendantsImpl(*Node, std::move(Rule), Result); - - return llvm::make_error( - llvm::errc::invalid_argument, - "type unsupported for recursive rewriting, ID=\"" + NodeId + - "\", Kind=" + It->second.getNodeKind().asStringRef()); + return detail::rewriteDescendants(It->second, std::move(Rule), Result); }; } diff --git a/clang/test/CodeGenCUDA/amdgpu-kernel-attrs.cu b/clang/test/CodeGenCUDA/amdgpu-kernel-attrs.cu --- a/clang/test/CodeGenCUDA/amdgpu-kernel-attrs.cu +++ b/clang/test/CodeGenCUDA/amdgpu-kernel-attrs.cu @@ -39,7 +39,7 @@ // NAMD-NOT: "amdgpu-num-vgpr" // NAMD-NOT: "amdgpu-num-sgpr" -// DEFAULT-DAG: attributes [[FLAT_WORK_GROUP_SIZE_DEFAULT]] = {{.*}}"amdgpu-flat-work-group-size"="1,1024"{{.*}}"uniform-work-group-size"="true" +// DEFAULT-DAG: attributes [[FLAT_WORK_GROUP_SIZE_DEFAULT]] = {{.*}}"amdgpu-flat-work-group-size"="1,256"{{.*}}"uniform-work-group-size"="true" // MAX1024-DAG: attributes [[FLAT_WORK_GROUP_SIZE_DEFAULT]] = {{.*}}"amdgpu-flat-work-group-size"="1,1024" // CHECK-DAG: attributes [[FLAT_WORK_GROUP_SIZE_32_64]] = {{.*}}"amdgpu-flat-work-group-size"="32,64" // CHECK-DAG: attributes [[WAVES_PER_EU_2]] = {{.*}}"amdgpu-waves-per-eu"="2" diff --git a/clang/test/CodeGenCUDA/kernel-amdgcn.cu b/clang/test/CodeGenCUDA/kernel-amdgcn.cu --- a/clang/test/CodeGenCUDA/kernel-amdgcn.cu +++ b/clang/test/CodeGenCUDA/kernel-amdgcn.cu @@ -39,4 +39,4 @@ launch((void*)D.Empty()); return 0; } -// CHECK: attributes #[[ATTR]] = {{.*}}"amdgpu-flat-work-group-size"="1,1024" +// CHECK: attributes #[[ATTR]] = {{.*}}"amdgpu-flat-work-group-size"="1,256" diff --git a/clang/unittests/Tooling/TransformerTest.cpp b/clang/unittests/Tooling/TransformerTest.cpp --- a/clang/unittests/Tooling/TransformerTest.cpp +++ b/clang/unittests/Tooling/TransformerTest.cpp @@ -25,6 +25,7 @@ using ::testing::IsEmpty; using transformer::cat; using transformer::changeTo; +using transformer::rewriteDescendants; using transformer::RewriteRule; constexpr char KHeaderContents[] = R"cc( @@ -568,6 +569,88 @@ EXPECT_EQ(ErrorCount, 1); } +// +// We include one test per typed overload. We don't test extensively since that +// is already covered by the tests above. +// + +TEST_F(TransformerTest, RewriteDescendantsTypedStmt) { + // Add an unrelated definition to the header that also has a variable named + // "x", to test that the rewrite is limited to the scope we intend. + appendToHeader(R"cc(int g(int x) { return x; })cc"); + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))), + [&InlineX](const MatchFinder::MatchResult &R) { + const auto *Node = R.Nodes.getNodeAs("body"); + assert(Node != nullptr && "body must be bound"); + return transformer::detail::rewriteDescendants( + *Node, InlineX, R); + }), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypedDecl) { + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f")).bind("fun"), + [&InlineX](const MatchFinder::MatchResult &R) { + const auto *Node = R.Nodes.getNodeAs("fun"); + assert(Node != nullptr && "fun must be bound"); + return transformer::detail::rewriteDescendants( + *Node, InlineX, R); + }), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypedTypeLoc) { + std::string Input = "int f(int *x) { return *x; }"; + std::string Expected = "int f(char *x) { return *x; }"; + auto IntToChar = + makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))).bind("loc"), + changeTo(cat("char"))); + testRule( + makeRule( + functionDecl( + hasName("f"), + hasParameter(0, varDecl(hasTypeLoc(typeLoc().bind("parmType"))))), + [&IntToChar](const MatchFinder::MatchResult &R) { + const auto *Node = R.Nodes.getNodeAs("parmType"); + assert(Node != nullptr && "parmType must be bound"); + return transformer::detail::rewriteDescendants(*Node, IntToChar, R); + }), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypedDynTyped) { + // Add an unrelated definition to the header that also has a variable named + // "x", to test that the rewrite is limited to the scope we intend. + appendToHeader(R"cc(int g(int x) { return x; })cc"); + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + testRule( + makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))), + [&InlineX](const MatchFinder::MatchResult &R) { + auto It = R.Nodes.getMap().find("body"); + assert(It != R.Nodes.getMap().end() && "body must be bound"); + return transformer::detail::rewriteDescendants(It->second, + InlineX, R); + }), + Input, Expected); +} + TEST_F(TransformerTest, InsertBeforeEdit) { std::string Input = R"cc( int f() { diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt --- a/compiler-rt/CMakeLists.txt +++ b/compiler-rt/CMakeLists.txt @@ -544,7 +544,8 @@ if(COMPILER_RT_INCLUDE_TESTS) add_subdirectory(unittests) add_subdirectory(test) - if (COMPILER_RT_STANDALONE_BUILD) + # Don't build llvm-lit for runtimes-build, it will clean up map_config. + if (COMPILER_RT_STANDALONE_BUILD AND NOT RUNTIMES_BUILD) # If we have a valid source tree, generate llvm-lit into the bin directory. # The user can still choose to have the check targets *use* a different lit # by specifying -DLLVM_EXTERNAL_LIT, but we generate it regardless. diff --git a/compiler-rt/lib/asan/scripts/asan_symbolize.py b/compiler-rt/lib/asan/scripts/asan_symbolize.py --- a/compiler-rt/lib/asan/scripts/asan_symbolize.py +++ b/compiler-rt/lib/asan/scripts/asan_symbolize.py @@ -89,7 +89,6 @@ def open_llvm_symbolizer(self): cmd = [self.symbolizer_path, - '--use-symbol-table=true', '--demangle=%s' % demangle, '--functions=linkage', '--inlines', diff --git a/compiler-rt/lib/msan/tests/msan_test.cpp b/compiler-rt/lib/msan/tests/msan_test.cpp --- a/compiler-rt/lib/msan/tests/msan_test.cpp +++ b/compiler-rt/lib/msan/tests/msan_test.cpp @@ -3229,9 +3229,19 @@ #if !defined(__FreeBSD__) && !defined(__NetBSD__) TEST(MemorySanitizer, sched_getaffinity) { cpu_set_t mask; - int res = sched_getaffinity(getpid(), sizeof(mask), &mask); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(mask); + if (sched_getaffinity(getpid(), sizeof(mask), &mask) == 0) + EXPECT_NOT_POISONED(mask); + else { + // The call to sched_getaffinity() may have failed because the Affinity + // mask is too small for the number of CPUs on the system (i.e. the + // system has more than 1024 CPUs). Allocate a mask large enough for + // twice as many CPUs. + cpu_set_t *DynAffinity; + DynAffinity = CPU_ALLOC(2048); + int res = sched_getaffinity(getpid(), CPU_ALLOC_SIZE(2048), DynAffinity); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(*DynAffinity); + } } #endif diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -306,7 +306,7 @@ void *Block = nullptr; uptr ClassId = 0; - uptr SecondaryBlockEnd; + uptr SecondaryBlockEnd = 0; if (LIKELY(PrimaryT::canAllocate(NeededSize))) { ClassId = SizeClassMap::getClassIdBySize(NeededSize); DCHECK_NE(ClassId, 0U); diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h --- a/compiler-rt/lib/scudo/standalone/secondary.h +++ b/compiler-rt/lib/scudo/standalone/secondary.h @@ -75,11 +75,6 @@ s32 MaxReleaseToOsIntervalMs = INT32_MAX> class MapAllocatorCache { public: - // Fuchsia doesn't allow releasing Secondary blocks yet. Note that 0 length - // arrays are an extension for some compilers. - // FIXME(kostyak): support (partially) the cache on Fuchsia. - static_assert(!SCUDO_FUCHSIA || EntriesArraySize == 0U, ""); - // Ensure the default maximum specified fits the array. static_assert(DefaultMaxEntriesCount <= EntriesArraySize, ""); @@ -392,9 +387,9 @@ } const uptr CommitSize = MapEnd - PageSize - CommitBase; - const uptr Ptr = - reinterpret_cast(map(reinterpret_cast(CommitBase), - CommitSize, "scudo:secondary", 0, &Data)); + const uptr Ptr = reinterpret_cast( + map(reinterpret_cast(CommitBase), CommitSize, "scudo:secondary", + MAP_RESIZABLE, &Data)); LargeBlock::Header *H = reinterpret_cast(Ptr); H->MapBase = MapBase; H->MapSize = MapEnd - MapBase; diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,8 @@ static constexpr scudo::Chunk::Origin Origin = scudo::Chunk::Origin::Malloc; -static void disableDebuggerdMaybe() { +// Fuchsia complains that the function is not used. +UNUSED static void disableDebuggerdMaybe() { #if SCUDO_ANDROID // Disable the debuggerd signal handler on Android, without this we can end // up spending a significant amount of time creating tombstones. diff --git a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp @@ -56,18 +56,12 @@ TEST(ScudoSecondaryTest, SecondaryBasic) { testSecondaryBasic>(); -#if !SCUDO_FUCHSIA testSecondaryBasic>>(); testSecondaryBasic< scudo::MapAllocator>>(); -#endif } -#if SCUDO_FUCHSIA -using LargeAllocator = scudo::MapAllocator; -#else using LargeAllocator = scudo::MapAllocator>; -#endif // This exercises a variety of combinations of size and alignment for the // MapAllocator. The size computation done here mimic the ones done by the diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -56,6 +56,7 @@ * `NAME=` as synonym for `FILE=` * Data edit descriptors without width or other details * `D` lines in fixed form as comments or debug code +* `CARRIAGECONTROL=` on the OPEN and INQUIRE statements * `CONVERT=` on the OPEN and INQUIRE statements * `DISPOSE=` on the OPEN and INQUIRE statements * Leading semicolons are ignored before any statement that diff --git a/flang/docs/f2018-grammar.txt b/flang/docs/f2018-grammar.txt --- a/flang/docs/f2018-grammar.txt +++ b/flang/docs/f2018-grammar.txt @@ -577,7 +577,8 @@ POSITION = scalar-default-char-expr | RECL = scalar-int-expr | ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr | STATUS = scalar-default-char-expr - @ | CONVERT = scalar-default-char-expr + @ | CARRIAGECONTROL = scalar-default-char-expr + | CONVERT = scalar-default-char-expr | DISPOSE = scalar-default-char-expr R1206 file-name-expr -> scalar-default-char-expr R1207 iomsg-variable -> scalar-default-char-variable @@ -657,7 +658,8 @@ STREAM = scalar-default-char-variable | STATUS = scalar-default-char-variable | WRITE = scalar-default-char-variable - @ | CONVERT = scalar-default-char-expr + @ | CARRIAGECONTROL = scalar-default-char-expr + | CONVERT = scalar-default-char-expr | DISPOSE = scalar-default-char-expr R1301 format-stmt -> FORMAT format-specification diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h --- a/flang/include/flang/Common/Fortran-features.h +++ b/flang/include/flang/Common/Fortran-features.h @@ -22,14 +22,14 @@ DoubleComplex, Byte, StarKind, QuadPrecision, SlashInitialization, TripletInArrayConstructor, MissingColons, SignedComplexLiteral, OldStyleParameter, ComplexConstructor, PercentLOC, SignedPrimary, FileName, - Convert, Dispose, IOListLeadingComma, AbbreviatedEditDescriptor, - ProgramParentheses, PercentRefAndVal, OmitFunctionDummies, CrayPointer, - Hollerith, ArithmeticIF, Assign, AssignedGOTO, Pause, OpenACC, OpenMP, - CruftAfterAmpersand, ClassicCComments, AdditionalFormats, BigIntLiterals, - RealDoControls, EquivalenceNumericWithCharacter, AdditionalIntrinsics, - AnonymousParents, OldLabelDoEndStatements, LogicalIntegerAssignment, - EmptySourceFile, ProgramReturn, ImplicitNoneTypeNever, - ImplicitNoneTypeAlways) + Carriagecontrol, Convert, Dispose, IOListLeadingComma, + AbbreviatedEditDescriptor, ProgramParentheses, PercentRefAndVal, + OmitFunctionDummies, CrayPointer, Hollerith, ArithmeticIF, Assign, + AssignedGOTO, Pause, OpenACC, OpenMP, CruftAfterAmpersand, ClassicCComments, + AdditionalFormats, BigIntLiterals, RealDoControls, + EquivalenceNumericWithCharacter, AdditionalIntrinsics, AnonymousParents, + OldLabelDoEndStatements, LogicalIntegerAssignment, EmptySourceFile, + ProgramReturn, ImplicitNoneTypeNever, ImplicitNoneTypeAlways) using LanguageFeatures = EnumSet; diff --git a/flang/include/flang/Common/Fortran.h b/flang/include/flang/Common/Fortran.h --- a/flang/include/flang/Common/Fortran.h +++ b/flang/include/flang/Common/Fortran.h @@ -52,6 +52,7 @@ Id, Iomsg, Iostat, Name, Named, Newunit, Nextrec, Nml, Number, Opened, Pad, Pending, Pos, Position, Read, Readwrite, Rec, Recl, Round, Sequential, Sign, Size, Status, Stream, Unformatted, Unit, Write, + Carriagecontrol, // nonstandard Convert, // nonstandard Dispose, // nonstandard ) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -2549,7 +2549,8 @@ // POSITION = scalar-default-char-expr | RECL = scalar-int-expr | // ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr | // STATUS = scalar-default-char-expr -// @ | CONVERT = scalar-default-char-variable +// @ | CARRIAGECONTROL = scalar-default-char-variable +// | CONVERT = scalar-default-char-variable // | DISPOSE = scalar-default-char-variable WRAPPER_CLASS(StatusExpr, ScalarDefaultCharExpr); WRAPPER_CLASS(ErrLabel, Label); @@ -2559,7 +2560,7 @@ struct CharExpr { ENUM_CLASS(Kind, Access, Action, Asynchronous, Blank, Decimal, Delim, Encoding, Form, Pad, Position, Round, Sign, - /* extensions: */ Convert, Dispose) + /* extensions: */ Carriagecontrol, Convert, Dispose) TUPLE_CLASS_BOILERPLATE(CharExpr); std::tuple t; }; @@ -2767,7 +2768,8 @@ // STATUS = scalar-default-char-variable | // UNFORMATTED = scalar-default-char-variable | // WRITE = scalar-default-char-variable -// @ | CONVERT = scalar-default-char-variable +// @ | CARRIAGECONTROL = scalar-default-char-variable +// | CONVERT = scalar-default-char-variable // | DISPOSE = scalar-default-char-variable struct InquireSpec { UNION_CLASS_BOILERPLATE(InquireSpec); @@ -2775,7 +2777,7 @@ ENUM_CLASS(Kind, Access, Action, Asynchronous, Blank, Decimal, Delim, Direct, Encoding, Form, Formatted, Iomsg, Name, Pad, Position, Read, Readwrite, Round, Sequential, Sign, Stream, Status, Unformatted, Write, - /* extensions: */ Convert, Dispose) + /* extensions: */ Carriagecontrol, Convert, Dispose) TUPLE_CLASS_BOILERPLATE(CharVar); std::tuple t; }; diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp --- a/flang/lib/Lower/IO.cpp +++ b/flang/lib/Lower/IO.cpp @@ -60,12 +60,12 @@ mkIOKey(OutputComplex64), mkIOKey(OutputComplex32), mkIOKey(OutputAscii), mkIOKey(InputAscii), mkIOKey(OutputLogical), mkIOKey(InputLogical), mkIOKey(SetAccess), mkIOKey(SetAction), mkIOKey(SetAsynchronous), - mkIOKey(SetEncoding), mkIOKey(SetForm), mkIOKey(SetPosition), - mkIOKey(SetRecl), mkIOKey(SetStatus), mkIOKey(SetFile), mkIOKey(GetNewUnit), - mkIOKey(GetSize), mkIOKey(GetIoLength), mkIOKey(GetIoMsg), - mkIOKey(InquireCharacter), mkIOKey(InquireLogical), - mkIOKey(InquirePendingId), mkIOKey(InquireInteger64), - mkIOKey(EndIoStatement)> + mkIOKey(SetCarriagecontrol), mkIOKey(SetEncoding), mkIOKey(SetForm), + mkIOKey(SetPosition), mkIOKey(SetRecl), mkIOKey(SetStatus), + mkIOKey(SetFile), mkIOKey(GetNewUnit), mkIOKey(GetSize), + mkIOKey(GetIoLength), mkIOKey(GetIoMsg), mkIOKey(InquireCharacter), + mkIOKey(InquireLogical), mkIOKey(InquirePendingId), + mkIOKey(InquireInteger64), mkIOKey(EndIoStatement)> newIOTable; } // namespace Fortran::lower @@ -599,6 +599,9 @@ case Fortran::parser::ConnectSpec::CharExpr::Kind::Sign: ioFunc = getIORuntimeFunc(loc, builder); break; + case Fortran::parser::ConnectSpec::CharExpr::Kind::Carriagecontrol: + ioFunc = getIORuntimeFunc(loc, builder); + break; case Fortran::parser::ConnectSpec::CharExpr::Kind::Convert: llvm_unreachable("CONVERT not part of the runtime::io interface"); case Fortran::parser::ConnectSpec::CharExpr::Kind::Dispose: diff --git a/flang/lib/Parser/io-parsers.cpp b/flang/lib/Parser/io-parsers.cpp --- a/flang/lib/Parser/io-parsers.cpp +++ b/flang/lib/Parser/io-parsers.cpp @@ -54,8 +54,9 @@ // POSITION = scalar-default-char-expr | RECL = scalar-int-expr | // ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr | // STATUS = scalar-default-char-expr -// @ | CONVERT = scalar-default-char-variable -// @ | DISPOSE = scalar-default-char-variable +// @ | CARRIAGECONTROL = scalar-default-char-variable +// | CONVERT = scalar-default-char-variable +// | DISPOSE = scalar-default-char-variable constexpr auto statusExpr{construct(scalarDefaultCharExpr)}; constexpr auto errLabel{construct(label)}; @@ -107,6 +108,10 @@ "SIGN =" >> pure(ConnectSpec::CharExpr::Kind::Sign), scalarDefaultCharExpr)), construct("STATUS =" >> statusExpr), + extension(construct( + construct("CARRIAGECONTROL =" >> + pure(ConnectSpec::CharExpr::Kind::Carriagecontrol), + scalarDefaultCharExpr))), extension( construct(construct( "CONVERT =" >> pure(ConnectSpec::CharExpr::Kind::Convert), @@ -357,7 +362,8 @@ // STREAM = scalar-default-char-variable | // STATUS = scalar-default-char-variable | // WRITE = scalar-default-char-variable -// @ | CONVERT = scalar-default-char-variable +// @ | CARRIAGECONTROL = scalar-default-char-variable +// | CONVERT = scalar-default-char-variable // | DISPOSE = scalar-default-char-variable TYPE_PARSER(first(construct(maybe("UNIT ="_tok) >> fileUnitNumber), construct("FILE =" >> fileNameExpr), @@ -475,6 +481,11 @@ construct("WRITE =" >> construct(pure(InquireSpec::CharVar::Kind::Write), scalarDefaultCharVariable)), + extension( + construct("CARRIAGECONTROL =" >> + construct( + pure(InquireSpec::CharVar::Kind::Carriagecontrol), + scalarDefaultCharVariable))), extension(construct( "CONVERT =" >> construct( pure(InquireSpec::CharVar::Kind::Convert), diff --git a/flang/lib/Semantics/check-io.cpp b/flang/lib/Semantics/check-io.cpp --- a/flang/lib/Semantics/check-io.cpp +++ b/flang/lib/Semantics/check-io.cpp @@ -135,6 +135,9 @@ case ParseKind::Sign: specKind = IoSpecKind::Sign; break; + case ParseKind::Carriagecontrol: + specKind = IoSpecKind::Carriagecontrol; + break; case ParseKind::Convert: specKind = IoSpecKind::Convert; break; @@ -152,6 +155,13 @@ flags_.set(Flag::AccessStream, s == "STREAM"); } CheckStringValue(specKind, *charConst, parser::FindSourceLocation(spec)); + if (specKind == IoSpecKind::Carriagecontrol && + (s == "FORTRAN" || s == "NONE")) { + context_.Say(parser::FindSourceLocation(spec), + "Unimplemented %s value '%s'"_err_en_US, + parser::ToUpperCaseLetters(common::EnumToString(specKind)), + *charConst); + } } } @@ -378,6 +388,9 @@ case ParseKind::Write: specKind = IoSpecKind::Write; break; + case ParseKind::Carriagecontrol: + specKind = IoSpecKind::Carriagecontrol; + break; case ParseKind::Convert: specKind = IoSpecKind::Convert; break; @@ -821,6 +834,7 @@ {IoSpecKind::Status, // Open values; Close values are {"DELETE", "KEEP"}. {"NEW", "OLD", "REPLACE", "SCRATCH", "UNKNOWN"}}, + {IoSpecKind::Carriagecontrol, {"LIST", "FORTRAN", "NONE"}}, {IoSpecKind::Convert, {"BIG_ENDIAN", "LITTLE_ENDIAN", "NATIVE"}}, {IoSpecKind::Dispose, {"DELETE", "KEEP"}}, }; diff --git a/flang/runtime/io-api.h b/flang/runtime/io-api.h --- a/flang/runtime/io-api.h +++ b/flang/runtime/io-api.h @@ -260,6 +260,8 @@ bool IONAME(SetAction)(Cookie, const char *, std::size_t); // ASYNCHRONOUS=YES, NO bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t); +// CARRIAGECONTROL=LIST, FORTRAN, NONE +bool IONAME(SetCarriagecontrol)(Cookie, const char *, std::size_t); // CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP bool IONAME(SetConvert)(Cookie, const char *, std::size_t); // ENCODING=UTF-8, DEFAULT diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -655,6 +655,31 @@ } } +bool IONAME(SetCarriagecontrol)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetCarriageControl() called when not in an OPEN statement"); + } + static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: + return true; + case 1: + case 2: + open->SignalError(IostatErrorInKeyword, + "Unimplemented CARRIAGECONTROL='%.*s'", static_cast(length), + keyword); + return false; + default: + open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'", + static_cast(length), keyword); + return false; + } +} + bool IONAME(SetConvert)( Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; @@ -708,7 +733,7 @@ auto *open{io.get_if()}; if (!open) { io.GetIoErrorHandler().Crash( - "SetEncoding() called when not in an OPEN statement"); + "SetForm() called when not in an OPEN statement"); } static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr}; switch (IdentifyValue(keyword, length, keywords)) { diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -779,6 +779,9 @@ : unit().modes.editingFlags & blankZero ? "ZERO" : "NULL"; break; + case HashInquiryKeyword("CARRIAGECONTROL"): + str = "LIST"; + break; case HashInquiryKeyword("CONVERT"): str = unit().swapEndianness() ? "SWAP" : "NATIVE"; break; @@ -976,6 +979,7 @@ case HashInquiryKeyword("ACTION"): case HashInquiryKeyword("ASYNCHRONOUS"): case HashInquiryKeyword("BLANK"): + case HashInquiryKeyword("CARRIAGECONTROL"): case HashInquiryKeyword("CONVERT"): case HashInquiryKeyword("DECIMAL"): case HashInquiryKeyword("DELIM"): @@ -1061,6 +1065,7 @@ case HashInquiryKeyword("ACTION"): case HashInquiryKeyword("ASYNCHRONOUS"): case HashInquiryKeyword("BLANK"): + case HashInquiryKeyword("CARRIAGECONTROL"): case HashInquiryKeyword("CONVERT"): case HashInquiryKeyword("DECIMAL"): case HashInquiryKeyword("DELIM"): diff --git a/flang/test/Semantics/io01.f90 b/flang/test/Semantics/io01.f90 --- a/flang/test/Semantics/io01.f90 +++ b/flang/test/Semantics/io01.f90 @@ -62,6 +62,7 @@ open(81, convert=convert_(2), dispose=dispose_(2)) open(access='STREAM', 90) ! nonstandard + open (unit=91, file='xfile', carriagecontrol='list') ! nonstandard !ERROR: OPEN statement must have a UNIT or NEWUNIT specifier !ERROR: If ACCESS='DIRECT' appears, RECL must also appear @@ -127,4 +128,10 @@ !ERROR: If NEWUNIT appears, FILE or STATUS='SCRATCH' must also appear open(newunit=nn, status='old') + + !ERROR: Unimplemented CARRIAGECONTROL value 'fortran' + open (unit=116, file='xfile', carriagecontrol='fortran') ! nonstandard + + !ERROR: Invalid CARRIAGECONTROL value 'nonsense' + open (unit=116, file='xfile', carriagecontrol='nonsense') ! nonstandard end diff --git a/flang/test/Semantics/io05.f90 b/flang/test/Semantics/io05.f90 --- a/flang/test/Semantics/io05.f90 +++ b/flang/test/Semantics/io05.f90 @@ -25,6 +25,7 @@ inquire(pending=v(5), file='abc') inquire(10, id=id, pending=v(5)) inquire(10, id=const_id, pending=v(5)) + inquire(10, carriagecontrol=c(1)) ! nonstandard ! using variable 'cv' multiple times seems to be allowed inquire(file='abc', & diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -184,7 +184,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_swap_algorithms`` *unimplemented* ------------------------------------------------- ----------------- - ``__cpp_lib_constexpr_utility `` ``201811L`` + ``__cpp_lib_constexpr_utility`` ``201811L`` ------------------------------------------------- ----------------- ``__cpp_lib_destroying_delete`` ``201806L`` ------------------------------------------------- ----------------- diff --git a/libcxx/include/ctime b/libcxx/include/ctime --- a/libcxx/include/ctime +++ b/libcxx/include/ctime @@ -59,9 +59,11 @@ // we're detecting this here instead of in <__config> because we can't include // system headers from <__config>, since it leads to circular module dependencies. // This is also meant to be a very temporary workaround until the SDKs are fixed. -#include -#if defined(__APPLE__) && defined(_LIBCPP_HAS_TIMESPEC_GET) && (__DARWIN_C_LEVEL < __DARWIN_C_FULL) -# define _LIBCPP_HAS_TIMESPEC_GET_NOT_ACTUALLY_PROVIDED +#if defined(__APPLE__) +# include +# if defined(_LIBCPP_HAS_TIMESPEC_GET) && (__DARWIN_C_LEVEL < __DARWIN_C_FULL) +# define _LIBCPP_HAS_TIMESPEC_GET_NOT_ACTUALLY_PROVIDED +# endif #endif _LIBCPP_BEGIN_NAMESPACE_STD diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp @@ -13,14 +13,14 @@ // Test the feature test macros defined by -/* Constant Value - __cpp_lib_as_const 201510L [C++17] - __cpp_lib_constexpr_misc 201811L [C++2a] - __cpp_lib_constexpr_utility 201811L [C++2a] - __cpp_lib_exchange_function 201304L [C++14] - __cpp_lib_integer_sequence 201304L [C++14] - __cpp_lib_to_chars 201611L [C++17] - __cpp_lib_tuples_by_type 201304L [C++14] +/* Constant Value + __cpp_lib_as_const 201510L [C++17] + __cpp_lib_constexpr_misc 201811L [C++2a] + __cpp_lib_constexpr_utility 201811L [C++2a] + __cpp_lib_exchange_function 201304L [C++14] + __cpp_lib_integer_sequence 201304L [C++14] + __cpp_lib_to_chars 201611L [C++17] + __cpp_lib_tuples_by_type 201304L [C++14] */ #include @@ -36,8 +36,8 @@ # error "__cpp_lib_constexpr_misc should not be defined before c++2a" # endif -# ifdef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should not be defined before c++2a" +# ifdef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should not be defined before c++2a" # endif # ifdef __cpp_lib_exchange_function @@ -66,8 +66,8 @@ # error "__cpp_lib_constexpr_misc should not be defined before c++2a" # endif -# ifdef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should not be defined before c++2a" +# ifdef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should not be defined before c++2a" # endif # ifndef __cpp_lib_exchange_function @@ -108,8 +108,8 @@ # error "__cpp_lib_constexpr_misc should not be defined before c++2a" # endif -# ifdef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should not be defined before c++2a" +# ifdef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should not be defined before c++2a" # endif # ifndef __cpp_lib_exchange_function @@ -168,11 +168,11 @@ # endif # endif -# ifndef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should be defined in c++2a" +# ifndef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should be defined in c++2a" # endif -# if __cpp_lib_constexpr_utility != 201811L -# error "__cpp_lib_constexpr_utility should have the value 201811L in c++2a" +# if __cpp_lib_constexpr_utility != 201811L +# error "__cpp_lib_constexpr_utility should have the value 201811L in c++2a" # endif # ifndef __cpp_lib_exchange_function diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -195,8 +195,8 @@ # error "__cpp_lib_constexpr_swap_algorithms should not be defined before c++2a" # endif -# ifdef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should not be defined before c++2a" +# ifdef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should not be defined before c++2a" # endif # ifdef __cpp_lib_destroying_delete @@ -555,8 +555,8 @@ # error "__cpp_lib_constexpr_swap_algorithms should not be defined before c++2a" # endif -# ifdef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should not be defined before c++2a" +# ifdef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should not be defined before c++2a" # endif # ifdef __cpp_lib_destroying_delete @@ -1029,8 +1029,8 @@ # error "__cpp_lib_constexpr_swap_algorithms should not be defined before c++2a" # endif -# ifdef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should not be defined before c++2a" +# ifdef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should not be defined before c++2a" # endif # ifdef __cpp_lib_destroying_delete @@ -1734,11 +1734,11 @@ # endif # endif -# ifndef __cpp_lib_constexpr_utility -# error "__cpp_lib_constexpr_utility should be defined in c++2a" +# ifndef __cpp_lib_constexpr_utility +# error "__cpp_lib_constexpr_utility should be defined in c++2a" # endif -# if __cpp_lib_constexpr_utility != 201811L -# error "__cpp_lib_constexpr_utility should have the value 201811L in c++2a" +# if __cpp_lib_constexpr_utility != 201811L +# error "__cpp_lib_constexpr_utility should have the value 201811L in c++2a" # endif # if TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -607,7 +607,7 @@ "depends": "defined(__cpp_concepts) && __cpp_concepts >= 201811L", "internal_depends": "defined(__cpp_concepts) && __cpp_concepts >= 201811L", }, - {"name": "__cpp_lib_constexpr_utility ", + {"name": "__cpp_lib_constexpr_utility", "values": { "c++2a": int(201811), }, diff --git a/lldb/test/API/commands/platform/basic/myshell.c b/lldb/test/API/commands/platform/basic/myshell.c --- a/lldb/test/API/commands/platform/basic/myshell.c +++ b/lldb/test/API/commands/platform/basic/myshell.c @@ -8,7 +8,7 @@ exit(1); } -#ifdef WIN32 +#if defined(_WIN32) || defined(_WIN64) char *cmd_opt = "/C"; #else char *cmd_opt = "-c"; diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -1450,6 +1450,14 @@ setBit(BitWidth - 1); } + /// Set a given bit to a given value. + void setBitVal(unsigned BitPosition, bool BitValue) { + if (BitValue) + setBit(BitPosition); + else + clearBit(BitPosition); + } + /// Set the bits from loBit (inclusive) to hiBit (exclusive) to 1. /// This function handles "wrap" case when \p loBit >= \p hiBit, and calls /// setBits when \p loBit < \p hiBit. diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h --- a/llvm/include/llvm/ADT/SmallVector.h +++ b/llvm/include/llvm/ADT/SmallVector.h @@ -60,7 +60,7 @@ /// This is an implementation of the grow() method which only works /// on POD-like data types and is out of line to reduce code duplication. /// This function will report a fatal error if it cannot increase capacity. - void grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize); + void grow_pod(void *FirstEl, size_t MinSize, size_t TSize); public: size_t size() const { return Size; } @@ -115,8 +115,8 @@ protected: SmallVectorTemplateCommon(size_t Size) : Base(getFirstEl(), Size) {} - void grow_pod(size_t MinCapacity, size_t TSize) { - Base::grow_pod(getFirstEl(), MinCapacity, TSize); + void grow_pod(size_t MinSize, size_t TSize) { + Base::grow_pod(getFirstEl(), MinSize, TSize); } /// Return true if this is a smallvector which has not had dynamic @@ -268,16 +268,32 @@ void SmallVectorTemplateBase::grow(size_t MinSize) { // Ensure we can fit the new capacity. // This is only going to be applicable when the capacity is 32 bit. - if (MinSize > this->SizeTypeMax()) - report_bad_alloc_error("SmallVector capacity overflow during allocation"); + if (MinSize > this->SizeTypeMax()) { + std::string Reason = "SmallVector unable to grow. Requested capacity (" + + std::to_string(MinSize) + + ") is larger than maximum value for size type (" + + std::to_string(this->SizeTypeMax()) + ")"; +#ifdef LLVM_ENABLE_EXCEPTIONS + throw std::length_error(Reason); +#else + report_fatal_error(Reason); +#endif + } // Ensure we can meet the guarantee of space for at least one more element. // The above check alone will not catch the case where grow is called with a - // default MinCapacity of 0, but the current capacity cannot be increased. + // default MinSize of 0, but the current capacity cannot be increased. // This is only going to be applicable when the capacity is 32 bit. - if (this->capacity() == this->SizeTypeMax()) - report_bad_alloc_error("SmallVector capacity unable to grow"); - + if (this->capacity() == this->SizeTypeMax()) { + std::string Reason = + "SmallVector capacity unable to grow. Already at maximum size " + + std::to_string(this->SizeTypeMax()); +#ifdef LLVM_ENABLE_EXCEPTIONS + throw std::length_error(Reason); +#else + report_fatal_error(Reason); +#endif + } // Always grow, even from zero. size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2)); NewCapacity = std::min(std::max(NewCapacity, MinSize), this->SizeTypeMax()); diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h --- a/llvm/include/llvm/Support/ErrorHandling.h +++ b/llvm/include/llvm/Support/ErrorHandling.h @@ -110,9 +110,9 @@ /// the following unwind succeeds, e.g. do not trigger additional allocations /// in the unwind chain. /// -/// If no error handler is installed (default), then a bad_alloc exception -/// is thrown, if LLVM is compiled with exception support, otherwise an -/// assertion is called. +/// If no error handler is installed (default), throws a bad_alloc exception +/// if LLVM is compiled with exception support. Otherwise prints the error +/// to standard error and calls abort(). LLVM_ATTRIBUTE_NORETURN void report_bad_alloc_error(const char *Reason, bool GenCrashDiag = true); diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -2568,11 +2568,13 @@ const Value *Vec = EEI->getVectorOperand(); const Value *Idx = EEI->getIndexOperand(); auto *CIdx = dyn_cast(Idx); - unsigned NumElts = cast(Vec->getType())->getNumElements(); - APInt DemandedVecElts = APInt::getAllOnesValue(NumElts); - if (CIdx && CIdx->getValue().ult(NumElts)) - DemandedVecElts = APInt::getOneBitSet(NumElts, CIdx->getZExtValue()); - return isKnownNonZero(Vec, DemandedVecElts, Depth, Q); + if (auto *VecTy = dyn_cast(Vec->getType())) { + unsigned NumElts = VecTy->getNumElements(); + APInt DemandedVecElts = APInt::getAllOnesValue(NumElts); + if (CIdx && CIdx->getValue().ult(NumElts)) + DemandedVecElts = APInt::getOneBitSet(NumElts, CIdx->getZExtValue()); + return isKnownNonZero(Vec, DemandedVecElts, Depth, Q); + } } KnownBits Known(BitWidth); diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -2603,13 +2603,9 @@ KnownZero, TLO, Depth + 1)) return true; - KnownUndef.clearBit(Idx); - if (Scl.isUndef()) - KnownUndef.setBit(Idx); + KnownUndef.setBitVal(Idx, Scl.isUndef()); - KnownZero.clearBit(Idx); - if (isNullConstant(Scl) || isNullFPConstant(Scl)) - KnownZero.setBit(Idx); + KnownZero.setBitVal(Idx, isNullConstant(Scl) || isNullFPConstant(Scl)); break; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -530,7 +530,13 @@ DataExtractor StrData(Section, isLittleEndian(), 0); uint64_t Offset = 0; uint64_t StrOffset = 0; - while (const char *CStr = StrData.getCStr(&Offset)) { + while (StrData.isValidOffset(Offset)) { + Error Err = Error::success(); + const char *CStr = StrData.getCStr(&Offset, &Err); + if (Err) { + DumpOpts.WarningHandler(std::move(Err)); + return; + } OS << format("0x%8.8" PRIx64 ": \"", StrOffset); OS.write_escaped(CStr); OS << "\"\n"; diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -589,7 +589,8 @@ Assert(!GV.isDSOLocal(), "GlobalValue with DLLImport Storage is dso_local!", &GV); - Assert((GV.isDeclaration() && GV.hasExternalLinkage()) || + Assert((GV.isDeclaration() && + (GV.hasExternalLinkage() || GV.hasExternalWeakLinkage())) || GV.hasAvailableExternallyLinkage(), "Global is marked as dllimport, but not external", &GV); } diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -1126,14 +1126,13 @@ assert(CU && "Expected valid compile unit"); // Enums, macros, and retained types don't need to be listed on the // imported DICompileUnit. This means they will only be imported - // if reached from the mapped IR. Do this by setting their value map - // entries to nullptr, which will automatically prevent their importing - // when reached from the DICompileUnit during metadata mapping. - ValueMap.MD()[CU->getRawEnumTypes()].reset(nullptr); - ValueMap.MD()[CU->getRawMacros()].reset(nullptr); - ValueMap.MD()[CU->getRawRetainedTypes()].reset(nullptr); + // if reached from the mapped IR. + CU->replaceEnumTypes(nullptr); + CU->replaceMacros(nullptr); + CU->replaceRetainedTypes(nullptr); + // The original definition (or at least its debug info - if the variable is - // internalized an optimized away) will remain in the source module, so + // internalized and optimized away) will remain in the source module, so // there's no need to import them. // If LLVM ever does more advanced optimizations on global variables // (removing/localizing write operations, for instance) that can track @@ -1141,7 +1140,7 @@ // with care when it comes to debug info size. Emitting small CUs containing // only a few imported entities into every destination module may be very // size inefficient. - ValueMap.MD()[CU->getRawGlobalVariables()].reset(nullptr); + CU->replaceGlobalVariables(nullptr); // Imported entities only need to be mapped in if they have local // scope, as those might correspond to an imported entity inside a @@ -1174,7 +1173,7 @@ else // If there were no local scope imported entities, we can map // the whole list to nullptr. - ValueMap.MD()[CU->getRawImportedEntities()].reset(nullptr); + CU->replaceImportedEntities(nullptr); } } } diff --git a/llvm/lib/MCA/HardwareUnits/RegisterFile.cpp b/llvm/lib/MCA/HardwareUnits/RegisterFile.cpp --- a/llvm/lib/MCA/HardwareUnits/RegisterFile.cpp +++ b/llvm/lib/MCA/HardwareUnits/RegisterFile.cpp @@ -196,15 +196,9 @@ // Update zero registers. MCPhysReg ZeroRegisterID = WS.clearsSuperRegisters() ? RegID : WS.getRegisterID(); - if (IsWriteZero) { - ZeroRegisters.setBit(ZeroRegisterID); - for (MCSubRegIterator I(ZeroRegisterID, &MRI); I.isValid(); ++I) - ZeroRegisters.setBit(*I); - } else { - ZeroRegisters.clearBit(ZeroRegisterID); - for (MCSubRegIterator I(ZeroRegisterID, &MRI); I.isValid(); ++I) - ZeroRegisters.clearBit(*I); - } + ZeroRegisters.setBitVal(ZeroRegisterID, IsWriteZero); + for (MCSubRegIterator I(ZeroRegisterID, &MRI); I.isValid(); ++I) + ZeroRegisters.setBitVal(*I, IsWriteZero); // If this is move has been eliminated, then the call to tryEliminateMove // should have already updated all the register mappings. @@ -233,10 +227,7 @@ RegisterMappings[*I].second.AliasRegID = 0U; } - if (IsWriteZero) - ZeroRegisters.setBit(*I); - else - ZeroRegisters.clearBit(*I); + ZeroRegisters.setBitVal(*I, IsWriteZero); } } diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp --- a/llvm/lib/Support/APInt.cpp +++ b/llvm/lib/Support/APInt.cpp @@ -338,8 +338,7 @@ /// Toggles a given bit to its opposite value. void APInt::flipBit(unsigned bitPosition) { assert(bitPosition < BitWidth && "Out of the bit-width range!"); - if ((*this)[bitPosition]) clearBit(bitPosition); - else setBit(bitPosition); + setBitVal(bitPosition, !(*this)[bitPosition]); } void APInt::insertBits(const APInt &subBits, unsigned bitPosition) { @@ -393,12 +392,8 @@ // General case - set/clear individual bits in dst based on src. // TODO - there is scope for optimization here, but at the moment this code // path is barely used so prefer readability over performance. - for (unsigned i = 0; i != subBitWidth; ++i) { - if (subBits[i]) - setBit(bitPosition + i); - else - clearBit(bitPosition + i); - } + for (unsigned i = 0; i != subBitWidth; ++i) + setBitVal(bitPosition + i, subBits[i]); } void APInt::insertBits(uint64_t subBits, unsigned bitPosition, unsigned numBits) { diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp --- a/llvm/lib/Support/ErrorHandling.cpp +++ b/llvm/lib/Support/ErrorHandling.cpp @@ -168,9 +168,11 @@ #else // Don't call the normal error handler. It may allocate memory. Directly write // an OOM to stderr and abort. - char OOMMessage[] = "LLVM ERROR: out of memory\n"; - ssize_t written = ::write(2, OOMMessage, strlen(OOMMessage)); - (void)written; + const char *OOMMessage = "LLVM ERROR: out of memory\n"; + const char *Newline = "\n"; + (void)::write(2, OOMMessage, strlen(OOMMessage)); + (void)::write(2, Reason, strlen(Reason)); + (void)::write(2, Newline, strlen(Newline)); abort(); #endif } diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp --- a/llvm/lib/Support/SmallVector.cpp +++ b/llvm/lib/Support/SmallVector.cpp @@ -44,24 +44,40 @@ // Note: Moving this function into the header may cause performance regression. template -void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity, +void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize, size_t TSize) { // Ensure we can fit the new capacity. // This is only going to be applicable when the capacity is 32 bit. - if (MinCapacity > SizeTypeMax()) - report_bad_alloc_error("SmallVector capacity overflow during allocation"); + if (MinSize > SizeTypeMax()) { + std::string Reason = "SmallVector unable to grow. Requested capacity (" + + std::to_string(MinSize) + + ") is larger than maximum value for size type (" + + std::to_string(SizeTypeMax()) + ")"; +#ifdef LLVM_ENABLE_EXCEPTIONS + throw std::length_error(Reason); +#else + report_fatal_error(Reason); +#endif + } // Ensure we can meet the guarantee of space for at least one more element. // The above check alone will not catch the case where grow is called with a - // default MinCapacity of 0, but the current capacity cannot be increased. + // default MinSize of 0, but the current capacity cannot be increased. // This is only going to be applicable when the capacity is 32 bit. - if (capacity() == SizeTypeMax()) - report_bad_alloc_error("SmallVector capacity unable to grow"); + if (capacity() == SizeTypeMax()) { + std::string Reason = + "SmallVector capacity unable to grow. Already at maximum size " + + std::to_string(SizeTypeMax()); +#ifdef LLVM_ENABLE_EXCEPTIONS + throw std::length_error(Reason); +#endif + report_fatal_error(Reason); + } // In theory 2*capacity can overflow if the capacity is 64 bit, but the // original capacity would never be large enough for this to be a problem. size_t NewCapacity = 2 * capacity() + 1; // Always grow. - NewCapacity = std::min(std::max(NewCapacity, MinCapacity), SizeTypeMax()); + NewCapacity = std::min(std::max(NewCapacity, MinSize), SizeTypeMax()); void *NewElts; if (BeginX == FirstEl) { diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -860,7 +860,7 @@ setOperationAction(ISD::MUL, MVT::v4f32, Legal); setOperationAction(ISD::FMA, MVT::v4f32, Legal); - if (TM.Options.UnsafeFPMath || Subtarget.hasVSX()) { + if (Subtarget.hasVSX()) { setOperationAction(ISD::FDIV, MVT::v4f32, Legal); setOperationAction(ISD::FSQRT, MVT::v4f32, Legal); } @@ -1234,12 +1234,6 @@ setTargetDAGCombine(ISD::SELECT_CC); } - // Use reciprocal estimates. - if (TM.Options.UnsafeFPMath) { - setTargetDAGCombine(ISD::FDIV); - setTargetDAGCombine(ISD::FSQRT); - } - if (Subtarget.hasP9Altivec()) { setTargetDAGCombine(ISD::ABS); setTargetDAGCombine(ISD::VSELECT); diff --git a/llvm/lib/Target/PowerPC/PPCMIPeephole.cpp b/llvm/lib/Target/PowerPC/PPCMIPeephole.cpp --- a/llvm/lib/Target/PowerPC/PPCMIPeephole.cpp +++ b/llvm/lib/Target/PowerPC/PPCMIPeephole.cpp @@ -1555,6 +1555,8 @@ MI.getOperand(1).setReg(SrcMI->getOperand(1).getReg()); MI.getOperand(2).setImm(NewSH); MI.getOperand(3).setImm(NewMB); + MI.getOperand(1).setIsKill(SrcMI->getOperand(1).isKill()); + SrcMI->getOperand(1).setIsKill(false); LLVM_DEBUG(dbgs() << "To: "); LLVM_DEBUG(MI.dump()); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -2224,9 +2224,7 @@ if (!VectorType::isValidElementType(DestType)) return nullptr; - unsigned NumElts = - cast(ExtElt->getVectorOperandType())->getNumElements(); - auto *NewVecType = FixedVectorType::get(DestType, NumElts); + auto *NewVecType = VectorType::get(DestType, ExtElt->getVectorOperandType()); auto *NewBC = IC.Builder.CreateBitCast(ExtElt->getVectorOperand(), NewVecType, "bc"); return ExtractElementInst::Create(NewBC, ExtElt->getIndexOperand()); diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp --- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp +++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp @@ -434,11 +434,14 @@ m_OneUse(m_Shuffle(m_Value(V), m_Undef(), m_Mask(Mask)))))) return false; - // Disallow non-vector casts and length-changing shuffles. + // 1) Do not fold bitcast shuffle for scalable type. First, shuffle cost for + // scalable type is unknown; Second, we cannot reason if the narrowed shuffle + // mask for scalable type is a splat or not. + // 2) Disallow non-vector casts and length-changing shuffles. // TODO: We could allow any shuffle. - auto *DestTy = dyn_cast(I.getType()); - auto *SrcTy = cast(V->getType()); - if (!DestTy || I.getOperand(0)->getType() != SrcTy) + auto *DestTy = dyn_cast(I.getType()); + auto *SrcTy = dyn_cast(V->getType()); + if (!SrcTy || !DestTy || I.getOperand(0)->getType() != SrcTy) return false; // The new shuffle must not cost more than the old shuffle. The bitcast is @@ -447,10 +450,8 @@ TTI.getShuffleCost(TargetTransformInfo::SK_PermuteSingleSrc, SrcTy)) return false; - // FIXME: it should be possible to implement the computation of the widened - // shuffle mask in terms of ElementCount to work with scalable shuffles. - unsigned DestNumElts = cast(DestTy)->getNumElements(); - unsigned SrcNumElts = cast(SrcTy)->getNumElements(); + unsigned DestNumElts = DestTy->getNumElements(); + unsigned SrcNumElts = SrcTy->getNumElements(); SmallVector NewMask; if (SrcNumElts <= DestNumElts) { // The bitcast is from wide to narrow/equal elements. The shuffle mask can diff --git a/llvm/test/CodeGen/PowerPC/jump-tables-collapse-rotate-remove-SrcMI.mir b/llvm/test/CodeGen/PowerPC/jump-tables-collapse-rotate-remove-SrcMI.mir --- a/llvm/test/CodeGen/PowerPC/jump-tables-collapse-rotate-remove-SrcMI.mir +++ b/llvm/test/CodeGen/PowerPC/jump-tables-collapse-rotate-remove-SrcMI.mir @@ -51,4 +51,4 @@ # # CHECK-PASS-NOT: %2:g8rc = RLDICL killed %1, 0, 32 # CHECK-PASS-NOT: %3:g8rc = RLDICR %2, 2, 61 -# CHECK-PASS: %3:g8rc = RLDIC %1, 2, 30 +# CHECK-PASS: %3:g8rc = RLDIC killed %1, 2, 30 diff --git a/llvm/test/CodeGen/PowerPC/mi-peephole.mir b/llvm/test/CodeGen/PowerPC/mi-peephole.mir --- a/llvm/test/CodeGen/PowerPC/mi-peephole.mir +++ b/llvm/test/CodeGen/PowerPC/mi-peephole.mir @@ -31,7 +31,7 @@ ; CHECK: bb.0.entry: ; CHECK: %1:g8rc = COPY $x4 ; CHECK: %0:g8rc = COPY $x3 - ; CHECK: %3:g8rc = RLDIC %1, 2, 30 + ; CHECK: %3:g8rc = RLDIC killed %1, 2, 30 ; CHECK: $x3 = COPY %3 ; CHECK: BLR8 implicit $lr8, implicit $rm, implicit $x3 ... diff --git a/llvm/test/CodeGen/PowerPC/pr47373.ll b/llvm/test/CodeGen/PowerPC/pr47373.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/pr47373.ll @@ -0,0 +1,180 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=powerpc64-unknown-freebsd13.0 -verify-machineinstrs \ +; RUN: -mcpu=ppc64 -ppc-asm-full-reg-names < %s | FileCheck %s +@a = local_unnamed_addr global float* null, align 8 + +; Function Attrs: nounwind +define void @d() local_unnamed_addr #0 { +; CHECK-LABEL: d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mflr r0 +; CHECK-NEXT: std r0, 16(r1) +; CHECK-NEXT: stdu r1, -208(r1) +; CHECK-NEXT: addis r3, r2, .LC0@toc@ha +; CHECK-NEXT: std r29, 184(r1) # 8-byte Folded Spill +; CHECK-NEXT: ld r3, .LC0@toc@l(r3) +; CHECK-NEXT: std r30, 192(r1) # 8-byte Folded Spill +; CHECK-NEXT: ld r29, 0(r3) +; CHECK-NEXT: bl c +; CHECK-NEXT: nop +; CHECK-NEXT: mr r30, r3 +; CHECK-NEXT: bl b +; CHECK-NEXT: nop +; CHECK-NEXT: cmpwi r30, 1 +; CHECK-NEXT: blt cr0, .LBB0_9 +; CHECK-NEXT: # %bb.1: # %for.body.preheader +; CHECK-NEXT: cmplwi r30, 4 +; CHECK-NEXT: clrldi r4, r30, 32 +; CHECK-NEXT: li r5, 0 +; CHECK-NEXT: blt cr0, .LBB0_7 +; CHECK-NEXT: # %bb.2: # %vector.memcheck +; CHECK-NEXT: rldic r6, r30, 2, 30 +; CHECK-NEXT: add r7, r3, r6 +; CHECK-NEXT: cmpld r29, r7 +; CHECK-NEXT: add r6, r29, r6 +; CHECK-NEXT: bc 4, lt, .LBB0_4 +; CHECK-NEXT: # %bb.3: # %vector.memcheck +; CHECK-NEXT: cmpld r3, r6 +; CHECK-NEXT: bc 12, lt, .LBB0_7 +; CHECK-NEXT: .LBB0_4: # %vector.ph +; CHECK-NEXT: rlwinm r5, r4, 0, 0, 29 +; CHECK-NEXT: li r7, 15 +; CHECK-NEXT: addi r6, r5, -4 +; CHECK-NEXT: addi r8, r1, 144 +; CHECK-NEXT: rldicl r6, r6, 62, 2 +; CHECK-NEXT: addi r9, r1, 128 +; CHECK-NEXT: addi r6, r6, 1 +; CHECK-NEXT: addi r10, r1, 160 +; CHECK-NEXT: mtctr r6 +; CHECK-NEXT: li r6, 0 +; CHECK-NEXT: addi r11, r1, 112 +; CHECK-NEXT: .LBB0_5: # %vector.body +; CHECK-NEXT: # +; CHECK-NEXT: add r12, r3, r6 +; CHECK-NEXT: lvx v3, r3, r6 +; CHECK-NEXT: lvx v5, r12, r7 +; CHECK-NEXT: add r12, r29, r6 +; CHECK-NEXT: lvsl v2, r3, r6 +; CHECK-NEXT: vperm v2, v3, v5, v2 +; CHECK-NEXT: lvx v3, r29, r6 +; CHECK-NEXT: lvx v5, r12, r7 +; CHECK-NEXT: lvsl v4, r29, r6 +; CHECK-NEXT: stvx v2, 0, r8 +; CHECK-NEXT: vperm v2, v3, v5, v4 +; CHECK-NEXT: stvx v2, 0, r9 +; CHECK-NEXT: lfs f0, 156(r1) +; CHECK-NEXT: lfs f1, 140(r1) +; CHECK-NEXT: fdivs f0, f1, f0 +; CHECK-NEXT: lfs f1, 136(r1) +; CHECK-NEXT: stfs f0, 172(r1) +; CHECK-NEXT: lfs f0, 152(r1) +; CHECK-NEXT: fdivs f0, f1, f0 +; CHECK-NEXT: lfs f1, 132(r1) +; CHECK-NEXT: stfs f0, 168(r1) +; CHECK-NEXT: lfs f0, 148(r1) +; CHECK-NEXT: fdivs f0, f1, f0 +; CHECK-NEXT: lfs f1, 128(r1) +; CHECK-NEXT: stfs f0, 164(r1) +; CHECK-NEXT: lfs f0, 144(r1) +; CHECK-NEXT: fdivs f0, f1, f0 +; CHECK-NEXT: stfs f0, 160(r1) +; CHECK-NEXT: lvx v2, 0, r10 +; CHECK-NEXT: stvx v2, 0, r11 +; CHECK-NEXT: ld r0, 112(r1) +; CHECK-NEXT: stdx r0, r29, r6 +; CHECK-NEXT: addi r6, r6, 16 +; CHECK-NEXT: ld r0, 120(r1) +; CHECK-NEXT: std r0, 8(r12) +; CHECK-NEXT: bdnz .LBB0_5 +; CHECK-NEXT: # %bb.6: # %middle.block +; CHECK-NEXT: cmpld r5, r4 +; CHECK-NEXT: beq cr0, .LBB0_9 +; CHECK-NEXT: .LBB0_7: # %for.body.preheader18 +; CHECK-NEXT: sldi r6, r5, 2 +; CHECK-NEXT: sub r5, r4, r5 +; CHECK-NEXT: addi r6, r6, -4 +; CHECK-NEXT: add r3, r3, r6 +; CHECK-NEXT: add r4, r29, r6 +; CHECK-NEXT: mtctr r5 +; CHECK-NEXT: .LBB0_8: # %for.body +; CHECK-NEXT: # +; CHECK-NEXT: lfsu f0, 4(r4) +; CHECK-NEXT: lfsu f1, 4(r3) +; CHECK-NEXT: fdivs f0, f0, f1 +; CHECK-NEXT: stfs f0, 0(r4) +; CHECK-NEXT: bdnz .LBB0_8 +; CHECK-NEXT: .LBB0_9: # %for.end +; CHECK-NEXT: ld r30, 192(r1) # 8-byte Folded Reload +; CHECK-NEXT: ld r29, 184(r1) # 8-byte Folded Reload +; CHECK-NEXT: addi r1, r1, 208 +; CHECK-NEXT: ld r0, 16(r1) +; CHECK-NEXT: mtlr r0 +; CHECK-NEXT: blr +entry: + %0 = load float*, float** @a, align 8 + %call = call signext i32 bitcast (i32 (...)* @c to i32 ()*)() #2 + %call1 = call float* bitcast (float* (...)* @b to float* ()*)() #2 + %cmp11 = icmp sgt i32 %call, 0 + br i1 %cmp11, label %for.body.preheader, label %for.end + +for.body.preheader: ; preds = %entry + %wide.trip.count = zext i32 %call to i64 + %min.iters.check = icmp ult i32 %call, 4 + br i1 %min.iters.check, label %for.body.preheader18, label %vector.memcheck + +vector.memcheck: ; preds = %for.body.preheader + %scevgep = getelementptr float, float* %0, i64 %wide.trip.count + %scevgep15 = getelementptr float, float* %call1, i64 %wide.trip.count + %bound0 = icmp ult float* %0, %scevgep15 + %bound1 = icmp ult float* %call1, %scevgep + %found.conflict = and i1 %bound0, %bound1 + br i1 %found.conflict, label %for.body.preheader18, label %vector.ph + +vector.ph: ; preds = %vector.memcheck + %n.vec = and i64 %wide.trip.count, 4294967292 + br label %vector.body + +vector.body: ; preds = %vector.body, %vector.ph + %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] + %1 = getelementptr inbounds float, float* %call1, i64 %index + %2 = bitcast float* %1 to <4 x float>* + %wide.load = load <4 x float>, <4 x float>* %2, align 4 + %3 = getelementptr inbounds float, float* %0, i64 %index + %4 = bitcast float* %3 to <4 x float>* + %wide.load17 = load <4 x float>, <4 x float>* %4, align 4 + %5 = fdiv reassoc nsz arcp afn <4 x float> %wide.load17, %wide.load + %6 = bitcast float* %3 to <4 x float>* + store <4 x float> %5, <4 x float>* %6, align 4 + %index.next = add i64 %index, 4 + %7 = icmp eq i64 %index.next, %n.vec + br i1 %7, label %middle.block, label %vector.body + +middle.block: ; preds = %vector.body + %cmp.n = icmp eq i64 %n.vec, %wide.trip.count + br i1 %cmp.n, label %for.end, label %for.body.preheader18 + +for.body.preheader18: ; preds = %middle.block, %vector.memcheck, %for.body.preheader + %indvars.iv.ph = phi i64 [ 0, %vector.memcheck ], [ 0, %for.body.preheader ], [ %n.vec, %middle.block ] + br label %for.body + +for.body: ; preds = %for.body.preheader18, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ %indvars.iv.ph, %for.body.preheader18 ] + %arrayidx = getelementptr inbounds float, float* %call1, i64 %indvars.iv + %8 = load float, float* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds float, float* %0, i64 %indvars.iv + %9 = load float, float* %arrayidx3, align 4 + %div = fdiv reassoc nsz arcp afn float %9, %8 + store float %div, float* %arrayidx3, align 4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count + br i1 %exitcond.not, label %for.end, label %for.body + +for.end: ; preds = %for.body, %middle.block, %entry + ret void +} + +declare signext i32 @c(...) local_unnamed_addr #1 + +declare float* @b(...) local_unnamed_addr #1 + +attributes #0 = { nounwind } diff --git a/llvm/test/ThinLTO/X86/Inputs/import-metadata.ll b/llvm/test/ThinLTO/X86/Inputs/import-metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/ThinLTO/X86/Inputs/import-metadata.ll @@ -0,0 +1,23 @@ +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-scei-ps4" + +define i32 @foo(i32 %goo) { +entry: + %goo.addr = alloca i32, align 4 + store i32 %goo, i32* %goo.addr, align 4 + %0 = load i32, i32* %goo.addr, align 4 + %1 = load i32, i32* %goo.addr, align 4 + %mul = mul nsw i32 %0, %1 + ret i32 %mul +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} +!llvm.md = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, enums: !4) +!1 = !DIFile(filename: "foo.cpp", directory: "tmp") +!2 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{} +!5 = !{!4} diff --git a/llvm/test/ThinLTO/X86/import-metadata.ll b/llvm/test/ThinLTO/X86/import-metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/ThinLTO/X86/import-metadata.ll @@ -0,0 +1,40 @@ +; RUN: opt -thinlto-bc %s -o %t1.bc +; RUN: opt -thinlto-bc %p/Inputs/import-metadata.ll -o %t2.bc +; RUN: llvm-lto2 run -save-temps %t1.bc %t2.bc -o %t-out \ +; RUN: -r=%t1.bc,main,plx \ +; RUN: -r=%t1.bc,foo,l \ +; RUN: -r=%t2.bc,foo,pl +; RUN: llvm-dis %t-out.1.3.import.bc -o - | FileCheck %s + +;; Check the imported DICompileUnit doesn't have the enums operand. +;; Also check the imported md metadata that shares a node with the +;; enums operand originally is not null. + +; CHECK: !llvm.dbg.cu = !{![[#CU1:]], ![[#CU2:]]} +;; Note that MD1 comes from the current module. MD2 is from the imported module. +;; We are checking if the imported MD2 doesn't end up having a null operand. +; CHECK: !llvm.md = !{![[#MD1:]], ![[#MD2:]]} +; CHECK: ![[#MD3:]] = !{} +; CHECK: ![[#CU2]] = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: ![[#FILE2:]], isOptimized: false, runtimeVersion: 0, emissionKind: NoDebug) +; CHECK: ![[#MD2]] = !{![[#MD3]]} + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-scei-ps4" + +declare i32 @foo(i32 %goo) + +define i32 @main() { + call i32 @foo(i32 0) + ret i32 0 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} +!llvm.md = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, enums: !4) +!1 = !DIFile(filename: "main.cpp", directory: "tmp") +!2 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{} +!5 = !{!4} diff --git a/llvm/test/Transforms/InstCombine/vscale_extractelement.ll b/llvm/test/Transforms/InstCombine/vscale_extractelement.ll --- a/llvm/test/Transforms/InstCombine/vscale_extractelement.ll +++ b/llvm/test/Transforms/InstCombine/vscale_extractelement.ll @@ -146,3 +146,25 @@ %4 = insertelement %3, i32 %vec.e3, i32 3 ret %4 } + +define i32 @bitcast_of_extractelement( %d) { +; CHECK-LABEL: @bitcast_of_extractelement( +; CHECK-NEXT: [[BC:%.*]] = bitcast [[D:%.*]] to +; CHECK-NEXT: [[CAST:%.*]] = extractelement [[BC]], i32 0 +; CHECK-NEXT: ret i32 [[CAST]] +; + %ext = extractelement %d, i32 0 + %cast = bitcast float %ext to i32 + ret i32 %cast +} + +define i1 @extractelement_is_zero( %d, i1 %b, i32 %z) { +; CHECK-LABEL: @extractelement_is_zero( +; CHECK-NEXT: [[EXT:%.*]] = extractelement [[D:%.*]], i32 0 +; CHECK-NEXT: [[BB:%.*]] = icmp eq i32 [[EXT]], 0 +; CHECK-NEXT: ret i1 [[BB]] +; + %ext = extractelement %d, i32 0 + %bb = icmp eq i32 %ext, 0 + ret i1 %bb +} diff --git a/llvm/test/Transforms/VectorCombine/AArch64/lit.local.cfg b/llvm/test/Transforms/VectorCombine/AArch64/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/VectorCombine/AArch64/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'AArch64' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/Transforms/VectorCombine/AArch64/vscale-bitcast-shuffle.ll b/llvm/test/Transforms/VectorCombine/AArch64/vscale-bitcast-shuffle.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/VectorCombine/AArch64/vscale-bitcast-shuffle.ll @@ -0,0 +1,21 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -vector-combine -S -mtriple=aarch64-- | FileCheck %s --check-prefixes=CHECK + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +; This test checks we are not crashing with TTI when trying to get shuffle cost. +; This test also check that shuffle mask zeroinitializer is +; not narrowed into <0, 1, 0, 1, ...>, which we cannot reason if it's a valid +; splat or not. + +define @bitcast_shuffle( %a) { +; CHECK-LABEL: @bitcast_shuffle( +; CHECK-NEXT: [[I:%.*]] = shufflevector [[A:%.*]], undef, zeroinitializer +; CHECK-NEXT: [[R:%.*]] = bitcast [[I]] to +; CHECK-NEXT: ret [[R]] +; + %i = shufflevector %a, undef, zeroinitializer + %r = bitcast %i to + ret %r +} diff --git a/llvm/test/Verifier/weak-dllimport.ll b/llvm/test/Verifier/weak-dllimport.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/weak-dllimport.ll @@ -0,0 +1,28 @@ +; RUN: opt -verify < %s 2>&1 | FileCheck %s +; CHECK-NOT: Global is marked as dllimport, but not external + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.11.0" + +@"?var_hook@@3HA" = extern_weak dllimport global i32, align 4 + +; Function Attrs: noinline optnone uwtable +define dso_local zeroext i1 @"?foo@@YA_NPEAHH@Z"(i32* %0, i32 %1) #0 { + ret i1 0 +} + +declare extern_weak dllimport void @func_hook(i32) #1 + +attributes #0 = { noinline optnone uwtable } +attributes #1 = { uwtable } + +; Compiled from the following C++ example with --target=x86_64-pc-win32, +; using the non-checking configuration +;__declspec(dllimport) __attribute__((weak)) extern "C" void func_hook(int); +;extern __declspec(dllimport) __attribute__((weak)) int var_hook; +;bool foo(int *q, int p) +;{ +; if (func_hook) +; func_hook(p); +; return &var_hook == q; +;} diff --git a/llvm/test/tools/llvm-dwarfdump/debug-str.yaml b/llvm/test/tools/llvm-dwarfdump/debug-str.yaml --- a/llvm/test/tools/llvm-dwarfdump/debug-str.yaml +++ b/llvm/test/tools/llvm-dwarfdump/debug-str.yaml @@ -44,3 +44,16 @@ # ESCAPED-NEXT: 0x00000002: "\001" # ESCAPED-NEXT: 0x00000004: "\\001" # ESCAPED-EMPTY: + +## c) Test that llvm-dwarfdump emits a warning when it encounters a string without a null terminator. + +## "abc\0" "abc" +# RUN: yaml2obj -DCONTENT="61626300616263" %s -o %t3.o +# RUN: llvm-dwarfdump --debug-str %t3.o 2>&1 | FileCheck %s --check-prefix=WARN + +# WARN: .debug_str contents: +# WARN-NEXT: 0x00000000: "abc" +# WARN-NEXT: warning: no null terminated string at offset 0x4 +# WARN: .debug_str.dwo contents: +# WARN-NEXT: 0x00000000: "abc" +# WARN-NEXT: warning: no null terminated string at offset 0x4 diff --git a/llvm/tools/llvm-ml/llvm-ml.cpp b/llvm/tools/llvm-ml/llvm-ml.cpp --- a/llvm/tools/llvm-ml/llvm-ml.cpp +++ b/llvm/tools/llvm-ml/llvm-ml.cpp @@ -175,6 +175,7 @@ static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) { AsmLexer Lexer(MAI); Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer()); + Lexer.setLexMasmIntegers(true); bool Error = false; while (Lexer.Lex().isNot(AsmToken::Eof)) { diff --git a/mlir/include/mlir/IR/Builders.h b/mlir/include/mlir/IR/Builders.h --- a/mlir/include/mlir/IR/Builders.h +++ b/mlir/include/mlir/IR/Builders.h @@ -328,6 +328,20 @@ setInsertionPoint(op->getBlock(), ++Block::iterator(op)); } + /// Sets the insertion point to the node after the specified value. If value + /// has a defining operation, sets the insertion point to the node after such + /// defining operation. This will cause subsequent insertions to go right + /// after it. Otherwise, value is a BlockArgumen. Sets the insertion point to + /// the start of its block. + void setInsertionPointAfter(Value val) { + if (Operation *op = val.getDefiningOp()) { + setInsertionPointAfter(op); + } else { + auto blockArg = val.cast(); + setInsertionPointToStart(blockArg.getOwner()); + } + } + /// Sets the insertion point to the start of the specified block. void setInsertionPointToStart(Block *block) { setInsertionPoint(block, block->begin()); diff --git a/mlir/include/mlir/Pass/PassInstrumentation.h b/mlir/include/mlir/Pass/PassInstrumentation.h --- a/mlir/include/mlir/Pass/PassInstrumentation.h +++ b/mlir/include/mlir/Pass/PassInstrumentation.h @@ -9,12 +9,12 @@ #ifndef MLIR_PASS_PASSINSTRUMENTATION_H_ #define MLIR_PASS_PASSINSTRUMENTATION_H_ +#include "mlir/IR/Identifier.h" #include "mlir/Support/LLVM.h" #include "mlir/Support/TypeID.h" namespace mlir { class Operation; -class OperationName; class Pass; namespace detail { @@ -43,13 +43,13 @@ /// A callback to run before a pass pipeline is executed. This function takes /// the name of the operation type being operated on, and information related /// to the parent that spawned this pipeline. - virtual void runBeforePipeline(const OperationName &name, + virtual void runBeforePipeline(Identifier name, const PipelineParentInfo &parentInfo) {} /// A callback to run after a pass pipeline has executed. This function takes /// the name of the operation type being operated on, and information related /// to the parent that spawned this pipeline. - virtual void runAfterPipeline(const OperationName &name, + virtual void runAfterPipeline(Identifier name, const PipelineParentInfo &parentInfo) {} /// A callback to run before a pass is executed. This function takes a pointer @@ -90,12 +90,12 @@ /// See PassInstrumentation::runBeforePipeline for details. void - runBeforePipeline(const OperationName &name, + runBeforePipeline(Identifier name, const PassInstrumentation::PipelineParentInfo &parentInfo); /// See PassInstrumentation::runAfterPipeline for details. void - runAfterPipeline(const OperationName &name, + runAfterPipeline(Identifier name, const PassInstrumentation::PipelineParentInfo &parentInfo); /// See PassInstrumentation::runBeforePass for details. diff --git a/mlir/include/mlir/Pass/PassManager.h b/mlir/include/mlir/Pass/PassManager.h --- a/mlir/include/mlir/Pass/PassManager.h +++ b/mlir/include/mlir/Pass/PassManager.h @@ -26,9 +26,9 @@ namespace mlir { class AnalysisManager; +class Identifier; class MLIRContext; class ModuleOp; -class OperationName; class Operation; class Pass; class PassInstrumentation; @@ -47,7 +47,7 @@ /// other OpPassManagers or the top-level PassManager. class OpPassManager { public: - OpPassManager(OperationName name, bool verifyPasses); + OpPassManager(Identifier name, MLIRContext *context, bool verifyPasses); OpPassManager(OpPassManager &&rhs); OpPassManager(const OpPassManager &rhs); ~OpPassManager(); @@ -70,10 +70,10 @@ /// Nest a new operation pass manager for the given operation kind under this /// pass manager. - OpPassManager &nest(const OperationName &nestedName); + OpPassManager &nest(Identifier nestedName); OpPassManager &nest(StringRef nestedName); template OpPassManager &nest() { - return nest(OpT::getOperationName()); + return nest(Identifier::get(OpT::getOperationName(), getContext())); } /// Add the given pass to this pass manager. If this pass has a concrete @@ -93,7 +93,7 @@ MLIRContext *getContext() const; /// Return the operation name that this pass manager operates on. - const OperationName &getOpName() const; + Identifier getOpName() const; /// Returns the internal implementation instance. detail::OpPassManagerImpl &getImpl(); diff --git a/mlir/lib/Dialect/Affine/Transforms/AffineLoopInvariantCodeMotion.cpp b/mlir/lib/Dialect/Affine/Transforms/AffineLoopInvariantCodeMotion.cpp --- a/mlir/lib/Dialect/Affine/Transforms/AffineLoopInvariantCodeMotion.cpp +++ b/mlir/lib/Dialect/Affine/Transforms/AffineLoopInvariantCodeMotion.cpp @@ -63,7 +63,7 @@ static bool isMemRefDereferencingOp(Operation &op) { // TODO: Support DMA Ops. - return isa(op); + return isa(op); } // Returns true if the individual op is loop invariant. @@ -84,10 +84,15 @@ // TODO: Support DMA ops. return false; } else if (!isa(op)) { + // Register op in the set of ops defined inside the loop. This set is used + // to prevent hoisting ops that depend on other ops defined inside the loop + // which are themselves not being hoisted. + definedOps.insert(&op); + if (isMemRefDereferencingOp(op)) { - Value memref = isa(op) - ? cast(op).getMemRef() - : cast(op).getMemRef(); + Value memref = isa(op) + ? cast(op).getMemRef() + : cast(op).getMemRef(); for (auto *user : memref.getUsers()) { // If this memref has a user that is a DMA, give up because these // operations write to this memref. @@ -97,8 +102,9 @@ // If the memref used by the load/store is used in a store elsewhere in // the loop nest, we do not hoist. Similarly, if the memref used in a // load is also being stored too, we do not hoist the load. - if (isa(user) || - (isa(user) && isa(op))) { + if (isa(user) || + (isa(user) && + isa(op))) { if (&op != user) { SmallVector userIVs; getLoopIVs(*user, &userIVs); @@ -111,9 +117,6 @@ } } - // Insert this op in the defined ops list. - definedOps.insert(&op); - if (op.getNumOperands() == 0 && !isa(op)) { LLVM_DEBUG(llvm::dbgs() << "\nNon-constant op with 0 operands\n"); return false; diff --git a/mlir/lib/Dialect/Affine/Transforms/SuperVectorize.cpp b/mlir/lib/Dialect/Affine/Transforms/SuperVectorize.cpp --- a/mlir/lib/Dialect/Affine/Transforms/SuperVectorize.cpp +++ b/mlir/lib/Dialect/Affine/Transforms/SuperVectorize.cpp @@ -38,6 +38,7 @@ #include "llvm/Support/Debug.h" using namespace mlir; +using namespace vector; /// /// Implements a high-level vectorization strategy on a Function. @@ -918,6 +919,42 @@ return b.createOperation(state)->getResult(0); } +/// Returns the vector type resulting from applying the provided vectorization +/// strategy on the scalar type. +static VectorType getVectorType(Type scalarTy, + const VectorizationStrategy *strategy) { + assert(!scalarTy.isa() && "Expected scalar type"); + return VectorType::get(strategy->vectorSizes, scalarTy); +} + +/// Returns true if the provided value is vector uniform given the vectorization +/// strategy. +// TODO: For now, only values that are invariants to all the loops in the +// vectorization strategy are considered vector uniforms. +static bool isUniformDefinition(Value value, + const VectorizationStrategy *strategy) { + for (auto loopToDim : strategy->loopToVectorDim) { + auto loop = cast(loopToDim.first); + if (!loop.isDefinedOutsideOfLoop(value)) + return false; + } + return true; +} + +/// Generates a broadcast op for the provided uniform value using the +/// vectorization strategy in 'state'. +static Value vectorizeUniform(Value value, VectorizationState *state) { + OpBuilder builder(value.getContext()); + builder.setInsertionPointAfter(value); + + auto vectorTy = getVectorType(value.getType(), state->strategy); + auto bcast = builder.create(value.getLoc(), vectorTy, value); + + // Add broadcast to the replacement map to reuse it for other uses. + state->replacementMap[value] = bcast; + return bcast; +} + /// Tries to vectorize a given operand `op` of Operation `op` during /// def-chain propagation or during terminal vectorization, by applying the /// following logic: @@ -927,7 +964,8 @@ /// vectorize atm (i.e. broadcasting required), returns nullptr to indicate /// failure; /// 3. if the `op` is a constant, returns the vectorized form of the constant; -/// 4. non-constant scalars are currently non-vectorizable, in particular to +/// 4. if the `op` is uniform, returns a vector broadcast of the `op`; +/// 5. non-constant scalars are currently non-vectorizable, in particular to /// guard against vectorizing an index which may be loop-variant and needs /// special handling. /// @@ -963,12 +1001,15 @@ return nullptr; } // 3. vectorize constant. - if (auto constant = operand.getDefiningOp()) { - return vectorizeConstant( - op, constant, - VectorType::get(state->strategy->vectorSizes, operand.getType())); - } - // 4. currently non-vectorizable. + if (auto constant = operand.getDefiningOp()) + return vectorizeConstant(op, constant, + getVectorType(operand.getType(), state->strategy)); + + // 4. Uniform values. + if (isUniformDefinition(operand, state->strategy)) + return vectorizeUniform(operand, state); + + // 5. currently non-vectorizable. LLVM_DEBUG(dbgs() << "-> non-vectorizable: " << operand); return nullptr; } diff --git a/mlir/lib/Pass/Pass.cpp b/mlir/lib/Pass/Pass.cpp --- a/mlir/lib/Pass/Pass.cpp +++ b/mlir/lib/Pass/Pass.cpp @@ -92,17 +92,17 @@ namespace mlir { namespace detail { struct OpPassManagerImpl { - OpPassManagerImpl(OperationName name, bool verifyPasses) - : name(name), verifyPasses(verifyPasses) {} + OpPassManagerImpl(Identifier name, MLIRContext *ctx, bool verifyPasses) + : name(name), context(ctx), verifyPasses(verifyPasses) {} /// Merge the passes of this pass manager into the one provided. void mergeInto(OpPassManagerImpl &rhs); /// Nest a new operation pass manager for the given operation kind under this /// pass manager. - OpPassManager &nest(const OperationName &nestedName); + OpPassManager &nest(Identifier nestedName); OpPassManager &nest(StringRef nestedName) { - return nest(OperationName(nestedName, getContext())); + return nest(Identifier::get(nestedName, getContext())); } /// Add the given pass to this pass manager. If this pass has a concrete @@ -118,12 +118,13 @@ void splitAdaptorPasses(); /// Return an instance of the context. - MLIRContext *getContext() const { - return name.getAbstractOperation()->dialect.getContext(); - } + MLIRContext *getContext() const { return context; } /// The name of the operation that passes of this pass manager operate on. - OperationName name; + Identifier name; + + /// The current context for this pass manager + MLIRContext *context; /// Flag that specifies if the IR should be verified after each pass has run. bool verifyPasses : 1; @@ -141,8 +142,8 @@ passes.clear(); } -OpPassManager &OpPassManagerImpl::nest(const OperationName &nestedName) { - OpPassManager nested(nestedName, verifyPasses); +OpPassManager &OpPassManagerImpl::nest(Identifier nestedName) { + OpPassManager nested(nestedName, getContext(), verifyPasses); auto *adaptor = new OpToOpPassAdaptor(std::move(nested)); addPass(std::unique_ptr(adaptor)); return adaptor->getPassManagers().front(); @@ -152,7 +153,7 @@ // If this pass runs on a different operation than this pass manager, then // implicitly nest a pass manager for this operation. auto passOpName = pass->getOpName(); - if (passOpName && passOpName != name.getStringRef()) + if (passOpName && passOpName != name.strref()) return nest(*passOpName).addPass(std::move(pass)); passes.emplace_back(std::move(pass)); @@ -239,19 +240,14 @@ // OpPassManager //===----------------------------------------------------------------------===// -OpPassManager::OpPassManager(OperationName name, bool verifyPasses) - : impl(new OpPassManagerImpl(name, verifyPasses)) { - assert(name.getAbstractOperation() && - "OpPassManager can only operate on registered operations"); - assert(name.getAbstractOperation()->hasProperty( - OperationProperty::IsolatedFromAbove) && - "OpPassManager only supports operating on operations marked as " - "'IsolatedFromAbove'"); -} +OpPassManager::OpPassManager(Identifier name, MLIRContext *context, + bool verifyPasses) + : impl(new OpPassManagerImpl(name, context, verifyPasses)) {} OpPassManager::OpPassManager(OpPassManager &&rhs) : impl(std::move(rhs.impl)) {} OpPassManager::OpPassManager(const OpPassManager &rhs) { *this = rhs; } OpPassManager &OpPassManager::operator=(const OpPassManager &rhs) { - impl.reset(new OpPassManagerImpl(rhs.impl->name, rhs.impl->verifyPasses)); + impl.reset(new OpPassManagerImpl(rhs.impl->name, rhs.impl->getContext(), + rhs.impl->verifyPasses)); for (auto &pass : rhs.impl->passes) impl->passes.emplace_back(pass->clone()); return *this; @@ -275,7 +271,7 @@ /// Nest a new operation pass manager for the given operation kind under this /// pass manager. -OpPassManager &OpPassManager::nest(const OperationName &nestedName) { +OpPassManager &OpPassManager::nest(Identifier nestedName) { return impl->nest(nestedName); } OpPassManager &OpPassManager::nest(StringRef nestedName) { @@ -298,7 +294,7 @@ MLIRContext *OpPassManager::getContext() const { return impl->getContext(); } /// Return the operation name that this pass manager operates on. -const OperationName &OpPassManager::getOpName() const { return impl->name; } +Identifier OpPassManager::getOpName() const { return impl->name; } /// Prints out the given passes as the textual representation of a pipeline. static void printAsTextualPipeline(ArrayRef> passes, @@ -336,6 +332,14 @@ LogicalResult OpToOpPassAdaptor::run(Pass *pass, Operation *op, AnalysisManager am) { + if (!op->getName().getAbstractOperation()) + return op->emitOpError() + << "trying to schedule a pass on an unregistered operation"; + if (!op->getName().getAbstractOperation()->hasProperty( + OperationProperty::IsolatedFromAbove)) + return op->emitOpError() << "trying to schedule a pass on an operation not " + "marked as 'IsolatedFromAbove'"; + pass->passState.emplace(op, am); // Instrument before the pass has run. @@ -385,7 +389,7 @@ /// Find an operation pass manager that can operate on an operation of the given /// type, or nullptr if one does not exist. static OpPassManager *findPassManagerFor(MutableArrayRef mgrs, - const OperationName &name) { + Identifier name) { auto it = llvm::find_if( mgrs, [&](OpPassManager &mgr) { return mgr.getOpName() == name; }); return it == mgrs.end() ? nullptr : &*it; @@ -417,8 +421,8 @@ // After coalescing, sort the pass managers within rhs by name. llvm::array_pod_sort(rhs.mgrs.begin(), rhs.mgrs.end(), [](const OpPassManager *lhs, const OpPassManager *rhs) { - return lhs->getOpName().getStringRef().compare( - rhs->getOpName().getStringRef()); + return lhs->getOpName().strref().compare( + rhs->getOpName().strref()); }); } @@ -450,7 +454,7 @@ for (auto ®ion : getOperation()->getRegions()) { for (auto &block : region) { for (auto &op : block) { - auto *mgr = findPassManagerFor(mgrs, op.getName()); + auto *mgr = findPassManagerFor(mgrs, op.getName().getIdentifier()); if (!mgr) continue; @@ -494,8 +498,8 @@ for (auto ®ion : getOperation()->getRegions()) { for (auto &block : region) { for (auto &op : block) { - // Add this operation iff the name matches the any of the pass managers. - if (findPassManagerFor(mgrs, op.getName())) + // Add this operation iff the name matches any of the pass managers. + if (findPassManagerFor(mgrs, op.getName().getIdentifier())) opAMPairs.emplace_back(&op, am.nest(&op)); } } @@ -531,7 +535,8 @@ // Get the pass manager for this operation and execute it. auto &it = opAMPairs[nextID]; - auto *pm = findPassManagerFor(pms, it.first->getName()); + auto *pm = + findPassManagerFor(pms, it.first->getName().getIdentifier()); assert(pm && "expected valid pass manager for operation"); if (instrumentor) @@ -732,7 +737,7 @@ //===----------------------------------------------------------------------===// PassManager::PassManager(MLIRContext *ctx, bool verifyPasses) - : OpPassManager(OperationName(ModuleOp::getOperationName(), ctx), + : OpPassManager(Identifier::get(ModuleOp::getOperationName(), ctx), ctx, verifyPasses), passTiming(false), localReproducer(false) {} @@ -870,7 +875,7 @@ /// See PassInstrumentation::runBeforePipeline for details. void PassInstrumentor::runBeforePipeline( - const OperationName &name, + Identifier name, const PassInstrumentation::PipelineParentInfo &parentInfo) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : impl->instrumentations) @@ -879,7 +884,7 @@ /// See PassInstrumentation::runAfterPipeline for details. void PassInstrumentor::runAfterPipeline( - const OperationName &name, + Identifier name, const PassInstrumentation::PipelineParentInfo &parentInfo) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : llvm::reverse(impl->instrumentations)) diff --git a/mlir/lib/Pass/PassStatistics.cpp b/mlir/lib/Pass/PassStatistics.cpp --- a/mlir/lib/Pass/PassStatistics.cpp +++ b/mlir/lib/Pass/PassStatistics.cpp @@ -116,7 +116,7 @@ // Print each of the children passes. for (OpPassManager &mgr : mgrs) { - auto name = ("'" + mgr.getOpName().getStringRef() + "' Pipeline").str(); + auto name = ("'" + mgr.getOpName().strref() + "' Pipeline").str(); printPassEntry(os, indent, name); for (Pass &pass : mgr.getPasses()) printPass(indent + 2, &pass); diff --git a/mlir/lib/Pass/PassTiming.cpp b/mlir/lib/Pass/PassTiming.cpp --- a/mlir/lib/Pass/PassTiming.cpp +++ b/mlir/lib/Pass/PassTiming.cpp @@ -165,9 +165,9 @@ ~PassTiming() override { print(); } /// Setup the instrumentation hooks. - void runBeforePipeline(const OperationName &name, + void runBeforePipeline(Identifier name, const PipelineParentInfo &parentInfo) override; - void runAfterPipeline(const OperationName &name, + void runAfterPipeline(Identifier name, const PipelineParentInfo &parentInfo) override; void runBeforePass(Pass *pass, Operation *) override { startPassTimer(pass); } void runAfterPass(Pass *pass, Operation *) override; @@ -242,15 +242,15 @@ }; } // end anonymous namespace -void PassTiming::runBeforePipeline(const OperationName &name, +void PassTiming::runBeforePipeline(Identifier name, const PipelineParentInfo &parentInfo) { // We don't actually want to time the pipelines, they gather their total // from their held passes. getTimer(name.getAsOpaquePointer(), TimerKind::Pipeline, - [&] { return ("'" + name.getStringRef() + "' Pipeline").str(); }); + [&] { return ("'" + name.strref() + "' Pipeline").str(); }); } -void PassTiming::runAfterPipeline(const OperationName &name, +void PassTiming::runAfterPipeline(Identifier name, const PipelineParentInfo &parentInfo) { // Pop the timer for the pipeline. auto tid = llvm::get_threadid(); diff --git a/mlir/test/Dialect/Affine/SuperVectorize/uniform_divergent.mlir b/mlir/test/Dialect/Affine/SuperVectorize/uniform_divergent.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/Affine/SuperVectorize/uniform_divergent.mlir @@ -0,0 +1,60 @@ +// RUN: mlir-opt %s -affine-super-vectorize="virtual-vector-size=128" -split-input-file | FileCheck %s + +// Specific tests to check vectorization of uniform/divergent values. + +// CHECK-LABEL: @uniform_arg +// CHECK-SAME: %[[in:.*]]: memref<512xf32>, +// CHECK-SAME: %[[uniform:.*]]: f32 +func @uniform_arg(%in : memref<512xf32>, %uniform : f32) { + affine.for %i = 0 to 512 { + %ld = affine.load %in[%i] : memref<512xf32> + %add = addf %ld, %uniform : f32 + } + return +} + +// CHECK-NEXT: %[[bcast:.*]] = vector.broadcast %[[uniform]] : f32 to vector<128xf32> +// CHECK-NEXT: affine.for +// CHECK: addf %{{.*}}, %[[bcast]] : vector<128xf32> + +// ----- + +// CHECK-LABEL: @multi_use_uniform_arg +// CHECK-SAME: %[[in:.*]]: memref<512xf32> +// CHECK-SAME: %[[uniform:.*]]: f32 +func @multi_use_uniform_arg(%in : memref<512xf32>, %uniform : f32) { + affine.for %i = 0 to 512 { + %ld = affine.load %in[%i] : memref<512xf32> + %user0 = addf %ld, %uniform : f32 + %user1 = addf %ld, %uniform : f32 + } + return +} + +// CHECK-NEXT: %[[bcast:.*]] = vector.broadcast %[[uniform]] : f32 to vector<128xf32> +// CHECK-NOT: vector.broadcast +// CHECK-NEXT: affine.for +// CHECK: addf %{{.*}}, %[[bcast]] : vector<128xf32> +// CHECK: addf %{{.*}}, %[[bcast]] : vector<128xf32> + +// ----- + +// CHECK-LABEL: @uniform_load +func @uniform_load(%A : memref, %C : memref) { + %c0 = constant 0 : index + %N = dim %A, %c0 : memref + affine.for %i = 0 to %N { + %uniform_ld = affine.load %A[%i, %i] : memref + affine.for %j = 0 to %N { + %b = affine.load %A[%i, %j] : memref + %c = addf %uniform_ld, %b : f32 + } + } + return +} + +// CHECK: affine.for +// CHECK-NEXT: %[[uniform_ld:.*]] = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref +// CHECK-NEXT: %[[bcast:.*]] = vector.broadcast %[[uniform_ld]] : f32 to vector<128xf32> +// CHECK-NEXT: affine.for +// CHECK: addf %[[bcast]], %{{.*}} : vector<128xf32> diff --git a/mlir/test/Dialect/Affine/SuperVectorize/vectorize_1d.mlir b/mlir/test/Dialect/Affine/SuperVectorize/vectorize_1d.mlir --- a/mlir/test/Dialect/Affine/SuperVectorize/vectorize_1d.mlir +++ b/mlir/test/Dialect/Affine/SuperVectorize/vectorize_1d.mlir @@ -396,25 +396,6 @@ return } -// This should not vectorize and should not crash. -// CHECK-LABEL: @vec_rejected_11 -func @vec_rejected_11(%A : memref, %C : memref) { - %c0 = constant 0 : index - %N = dim %A, %c0 : memref - affine.for %i = 0 to %N { -// CHECK-NOT: vector - %a = affine.load %A[%i, %i] : memref // not vectorized - affine.for %j = 0 to %N { - %b = affine.load %A[%i, %j] : memref // may be vectorized -// CHECK-NOT: vector - %c = addf %a, %b : f32 // not vectorized because %a wasn't -// CHECK-NOT: vector - affine.store %c, %C[%i, %j] : memref // not vectorized because %c wasn't - } - } - return -} - // This should not vectorize due to the sequential dependence in the scf. // CHECK-LABEL: @vec_rejected_sequential func @vec_rejected_sequential(%A : memref) { diff --git a/mlir/test/Dialect/Affine/affine-loop-invariant-code-motion.mlir b/mlir/test/Dialect/Affine/affine-loop-invariant-code-motion.mlir --- a/mlir/test/Dialect/Affine/affine-loop-invariant-code-motion.mlir +++ b/mlir/test/Dialect/Affine/affine-loop-invariant-code-motion.mlir @@ -22,6 +22,8 @@ return } +// ----- + // The store-load forwarding can see through affine apply's since it relies on // dependence information. // CHECK-LABEL: func @store_affine_apply @@ -36,12 +38,14 @@ // CHECK: %cst = constant 7.000000e+00 : f32 // CHECK-NEXT: %0 = alloc() : memref<10xf32> // CHECK-NEXT: affine.for %arg0 = 0 to 10 { -// CHECK-NEXT: %1 = affine.apply #map3(%arg0) +// CHECK-NEXT: %1 = affine.apply #map{{[0-9]+}}(%arg0) // CHECK-NEXT: affine.store %cst, %0[%1] : memref<10xf32> // CHECK-NEXT: } // CHECK-NEXT: return %0 : memref<10xf32> } +// ----- + func @nested_loops_code_invariant_to_both() { %m = alloc() : memref<10xf32> %cf7 = constant 7.0 : f32 @@ -61,6 +65,8 @@ return } +// ----- + func @single_loop_nothing_invariant() { %m1 = alloc() : memref<10xf32> %m2 = alloc() : memref<10xf32> @@ -82,6 +88,8 @@ return } +// ----- + func @invariant_code_inside_affine_if() { %m = alloc() : memref<10xf32> %cf8 = constant 8.0 : f32 @@ -98,7 +106,7 @@ // CHECK: %0 = alloc() : memref<10xf32> // CHECK-NEXT: %cst = constant 8.000000e+00 : f32 // CHECK-NEXT: affine.for %arg0 = 0 to 10 { - // CHECK-NEXT: %1 = affine.apply #map3(%arg0) + // CHECK-NEXT: %1 = affine.apply #map{{[0-9]+}}(%arg0) // CHECK-NEXT: affine.if #set0(%arg0, %1) { // CHECK-NEXT: %2 = addf %cst, %cst : f32 // CHECK-NEXT: affine.store %2, %0[%arg0] : memref<10xf32> @@ -108,6 +116,7 @@ return } +// ----- func @dependent_stores() { %m = alloc() : memref<10xf32> @@ -137,6 +146,8 @@ return } +// ----- + func @independent_stores() { %m = alloc() : memref<10xf32> %cf7 = constant 7.0 : f32 @@ -165,6 +176,8 @@ return } +// ----- + func @load_dependent_store() { %m = alloc() : memref<10xf32> %cf7 = constant 7.0 : f32 @@ -192,6 +205,8 @@ return } +// ----- + func @load_after_load() { %m = alloc() : memref<10xf32> %cf7 = constant 7.0 : f32 @@ -219,6 +234,8 @@ return } +// ----- + func @invariant_affine_if() { %m = alloc() : memref<10xf32> %cf8 = constant 8.0 : f32 @@ -244,6 +261,8 @@ return } +// ----- + func @invariant_affine_if2() { %m = alloc() : memref<10xf32> %cf8 = constant 8.0 : f32 @@ -271,6 +290,8 @@ return } +// ----- + func @invariant_affine_nested_if() { %m = alloc() : memref<10xf32> %cf8 = constant 8.0 : f32 @@ -303,6 +324,8 @@ return } +// ----- + func @invariant_affine_nested_if_else() { %m = alloc() : memref<10xf32> %cf8 = constant 8.0 : f32 @@ -339,6 +362,8 @@ return } +// ----- + func @invariant_affine_nested_if_else2() { %m = alloc() : memref<10xf32> %m2 = alloc() : memref<10xf32> @@ -375,6 +400,7 @@ return } +// ----- func @invariant_affine_nested_if2() { %m = alloc() : memref<10xf32> @@ -406,6 +432,8 @@ return } +// ----- + func @invariant_affine_for_inside_affine_if() { %m = alloc() : memref<10xf32> %cf8 = constant 8.0 : f32 @@ -438,6 +466,7 @@ return } +// ----- func @invariant_constant_and_load() { %m = alloc() : memref<100xf32> @@ -459,6 +488,7 @@ return } +// ----- func @nested_load_store_same_memref() { %m = alloc() : memref<10xf32> @@ -483,6 +513,7 @@ return } +// ----- func @nested_load_store_same_memref2() { %m = alloc() : memref<10xf32> @@ -505,3 +536,80 @@ return } + +// ----- + +// CHECK-LABEL: func @do_not_hoist_dependent_side_effect_free_op +func @do_not_hoist_dependent_side_effect_free_op(%arg0: memref<10x512xf32>) { + %0 = alloca() : memref<1xf32> + %cst = constant 8.0 : f32 + affine.for %i = 0 to 512 { + affine.for %j = 0 to 10 { + %5 = affine.load %arg0[%i, %j] : memref<10x512xf32> + %6 = affine.load %0[0] : memref<1xf32> + %add = addf %5, %6 : f32 + affine.store %add, %0[0] : memref<1xf32> + } + %3 = affine.load %0[0] : memref<1xf32> + %4 = mulf %3, %cst : f32 // It shouldn't be hoisted. + } + return +} + +// CHECK: affine.for +// CHECK-NEXT: affine.for +// CHECK-NEXT: affine.load +// CHECK-NEXT: affine.load +// CHECK-NEXT: addf +// CHECK-NEXT: affine.store +// CHECK-NEXT: } +// CHECK-NEXT: affine.load +// CHECK-NEXT: mulf +// CHECK-NEXT: } + +// ----- + +// CHECK-LABEL: func @vector_loop_nothing_invariant +func @vector_loop_nothing_invariant() { + %m1 = alloc() : memref<40xf32> + %m2 = alloc() : memref<40xf32> + affine.for %arg0 = 0 to 10 { + %v0 = affine.vector_load %m1[%arg0*4] : memref<40xf32>, vector<4xf32> + %v1 = affine.vector_load %m2[%arg0*4] : memref<40xf32>, vector<4xf32> + %v2 = addf %v0, %v1 : vector<4xf32> + affine.vector_store %v2, %m1[%arg0*4] : memref<40xf32>, vector<4xf32> + } + return +} + +// CHECK: affine.for +// CHECK-NEXT: affine.vector_load +// CHECK-NEXT: affine.vector_load +// CHECK-NEXT: addf +// CHECK-NEXT: affine.vector_store +// CHECK-NEXT: } + +// ----- + +// CHECK-LABEL: func @vector_loop_all_invariant +func @vector_loop_all_invariant() { + %m1 = alloc() : memref<4xf32> + %m2 = alloc() : memref<4xf32> + %m3 = alloc() : memref<4xf32> + affine.for %arg0 = 0 to 10 { + %v0 = affine.vector_load %m1[0] : memref<4xf32>, vector<4xf32> + %v1 = affine.vector_load %m2[0] : memref<4xf32>, vector<4xf32> + %v2 = addf %v0, %v1 : vector<4xf32> + affine.vector_store %v2, %m3[0] : memref<4xf32>, vector<4xf32> + } + return +} + +// CHECK: alloc() +// CHECK-NEXT: alloc() +// CHECK-NEXT: alloc() +// CHECK-NEXT: affine.vector_load +// CHECK-NEXT: affine.vector_load +// CHECK-NEXT: addf +// CHECK-NEXT: affine.vector_store +// CHECK-NEXT: affine.for diff --git a/mlir/tools/mlir-linalg-ods-gen/CMakeLists.txt b/mlir/tools/mlir-linalg-ods-gen/CMakeLists.txt --- a/mlir/tools/mlir-linalg-ods-gen/CMakeLists.txt +++ b/mlir/tools/mlir-linalg-ods-gen/CMakeLists.txt @@ -7,6 +7,6 @@ ) llvm_update_compile_flags(mlir-linalg-ods-gen) target_link_libraries(mlir-linalg-ods-gen PRIVATE - MLIRParser MLIRSupport + MLIRIR ) diff --git a/mlir/unittests/Pass/PassManagerTest.cpp b/mlir/unittests/Pass/PassManagerTest.cpp --- a/mlir/unittests/Pass/PassManagerTest.cpp +++ b/mlir/unittests/Pass/PassManagerTest.cpp @@ -74,4 +74,47 @@ } } +namespace { +struct InvalidPass : Pass { + InvalidPass() : Pass(TypeID::get(), StringRef("invalid_op")) {} + StringRef getName() const override { return "Invalid Pass"; } + void runOnOperation() override {} + + /// A clone method to create a copy of this pass. + std::unique_ptr clonePass() const override { + return std::make_unique( + *static_cast(this)); + } +}; +} // anonymous namespace + +TEST(PassManagerTest, InvalidPass) { + MLIRContext context; + + // Create a module + OwningModuleRef module(ModuleOp::create(UnknownLoc::get(&context))); + + // Add a single "invalid_op" operation + OpBuilder builder(&module->getBodyRegion()); + OperationState state(UnknownLoc::get(&context), "invalid_op"); + builder.insert(Operation::create(state)); + + // Register a diagnostic handler to capture the diagnostic so that we can + // check it later. + std::unique_ptr diagnostic; + context.getDiagEngine().registerHandler([&](Diagnostic &diag) { + diagnostic.reset(new Diagnostic(std::move(diag))); + }); + + // Instantiate and run our pass. + PassManager pm(&context); + pm.addPass(std::make_unique()); + LogicalResult result = pm.run(module.get()); + EXPECT_TRUE(failed(result)); + ASSERT_TRUE(diagnostic.get() != nullptr); + EXPECT_EQ( + diagnostic->str(), + "'invalid_op' op trying to schedule a pass on an unregistered operation"); +} + } // end namespace