diff --git a/clang/include/clang/Tooling/Refactoring/Stencil.h b/clang/include/clang/Tooling/Refactoring/Stencil.h --- a/clang/include/clang/Tooling/Refactoring/Stencil.h +++ b/clang/include/clang/Tooling/Refactoring/Stencil.h @@ -23,6 +23,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/RangeSelector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include @@ -121,6 +122,7 @@ private: friend bool operator==(const Stencil &A, const Stencil &B); static StencilPart wrap(llvm::StringRef Text); + static StencilPart wrap(RangeSelector Selector); static StencilPart wrap(StencilPart Part) { return Part; } std::vector Parts; @@ -142,14 +144,24 @@ /// \returns exactly the text provided. StencilPart text(llvm::StringRef Text); +/// \returns the source corresponding to the selected range. +StencilPart selection(RangeSelector Selector); + /// \returns the source corresponding to the identified node. -StencilPart node(llvm::StringRef Id); +/// FIXME: Deprecated. Write `selection(node(Id))` instead. +inline StencilPart node(llvm::StringRef Id) { + return selection(tooling::node(Id)); +} + /// Variant of \c node() that identifies the node as a statement, for purposes /// of deciding whether to include any trailing semicolon. Only relevant for /// Expr nodes, which, by default, are *not* considered as statements. /// \returns the source corresponding to the identified node, considered as a /// statement. -StencilPart sNode(llvm::StringRef Id); +/// FIXME: Deprecated. Write `selection(statement(Id))` instead. +inline StencilPart sNode(llvm::StringRef Id) { + return selection(tooling::statement(Id)); +} /// For debug use only; semantics are not guaranteed. /// diff --git a/clang/include/clang/Tooling/Refactoring/Transformer.h b/clang/include/clang/Tooling/Refactoring/Transformer.h --- a/clang/include/clang/Tooling/Refactoring/Transformer.h +++ b/clang/include/clang/Tooling/Refactoring/Transformer.h @@ -19,6 +19,7 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/Tooling/Refactoring/AtomicChange.h" +#include "clang/Tooling/Refactoring/RangeSelector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" @@ -30,19 +31,6 @@ namespace clang { namespace tooling { -/// Determines the part of the AST node to replace. We support this to work -/// around the fact that the AST does not differentiate various syntactic -/// elements into their own nodes, so users can specify them relative to a node, -/// instead. -enum class NodePart { - /// The node itself. - Node, - /// Given a \c MemberExpr, selects the member's token. - Member, - /// Given a \c NamedDecl or \c CxxCtorInitializer, selects that token of the - /// relevant name, not including qualifiers. - Name, -}; // Note that \p TextGenerator is allowed to fail, e.g. when trying to access a // matched node that was not bound. Allowing this to fail simplifies error @@ -76,73 +64,28 @@ // (`RewriteRule::Explanation`) instead. Notes serve the rare cases wherein // edit-specific diagnostics are required. // -// `ASTEdit` should be built using the `change` convenience fucntions. For +// `ASTEdit` should be built using the `change` convenience functions. For // example, // \code -// change(fun, NodePart::Name, "Frodo") +// change(name(fun), text("Frodo")) // \endcode // Or, if we use Stencil for the TextGenerator: // \code -// change(thenNode, stencil::cat("{", thenNode, "}")) -// change(call, NodePart::Args, stencil::cat(x, ",", y)) -// .note("argument order changed.") +// using stencil::cat; +// change(statement(thenNode), cat("{", thenNode, "}")) +// change(callArgs(call), cat(x, ",", y)) // \endcode // Or, if you are changing the node corresponding to the rule's matcher, you can // use the single-argument override of \c change: // \code -// change("different_expr") +// change(cat("different_expr")) // \endcode struct ASTEdit { - // The (bound) id of the node whose source will be replaced. This id should - // never be the empty string. - std::string Target; - ast_type_traits::ASTNodeKind Kind; - NodePart Part; + RangeSelector TargetRange; TextGenerator Replacement; TextGenerator Note; }; -// Convenience functions for creating \c ASTEdits. They all must be explicitly -// instantiated with the desired AST type. Each overload includes both \c -// std::string and \c TextGenerator versions. - -// FIXME: For overloads taking a \c NodePart, constrain the valid values of \c -// Part based on the type \c T. -template -ASTEdit change(StringRef Target, NodePart Part, TextGenerator Replacement) { - ASTEdit E; - E.Target = Target.str(); - E.Kind = ast_type_traits::ASTNodeKind::getFromNodeKind(); - E.Part = Part; - E.Replacement = std::move(Replacement); - return E; -} - -template -ASTEdit change(StringRef Target, NodePart Part, std::string Replacement) { - return change(Target, Part, text(std::move(Replacement))); -} - -/// Variant of \c change for which the NodePart defaults to the whole node. -template -ASTEdit change(StringRef Target, TextGenerator Replacement) { - return change(Target, NodePart::Node, std::move(Replacement)); -} - -/// Variant of \c change for which the NodePart defaults to the whole node. -template -ASTEdit change(StringRef Target, std::string Replacement) { - return change(Target, text(std::move(Replacement))); -} - -/// Variant of \c change that selects the node of the entire match. -template ASTEdit change(TextGenerator Replacement); - -/// Variant of \c change that selects the node of the entire match. -template ASTEdit change(std::string Replacement) { - return change(text(std::move(Replacement))); -} - /// Description of a source-code transformation. // // A *rewrite rule* describes a transformation of source code. A simple rule @@ -175,9 +118,9 @@ // We expect RewriteRules will most commonly include only one case. SmallVector Cases; - // Id used as the default target of each match. The node described by the + // ID used as the default target of each match. The node described by the // matcher is should always be bound to this id. - static constexpr llvm::StringLiteral RootId = "___root___"; + static constexpr llvm::StringLiteral RootID = "___root___"; }; /// Convenience function for constructing a simple \c RewriteRule. @@ -235,10 +178,17 @@ // ``` RewriteRule applyFirst(ArrayRef Rules); -// Define this overload of `change` here because RewriteRule::RootId is not in -// scope at the declaration point above. -template ASTEdit change(TextGenerator Replacement) { - return change(RewriteRule::RootId, NodePart::Node, std::move(Replacement)); +/// Replaces a portion of the source text with \p Replacement. +ASTEdit change(RangeSelector Target, TextGenerator Replacement); + +/// Replaces the entirety of a RewriteRule's match with \p Replacement. For +/// example, to replace a function call, one could write: +/// \code +/// makeRule(callExpr(callee(functionDecl(hasName("foo")))), +/// change(text("bar()"))) +/// \endcode +inline ASTEdit change(TextGenerator Replacement) { + return change(node(RewriteRule::RootID), std::move(Replacement)); } /// The following three functions are a low-level part of the RewriteRule diff --git a/clang/include/clang/Tooling/Syntax/Tokens.h b/clang/include/clang/Tooling/Syntax/Tokens.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Tooling/Syntax/Tokens.h @@ -0,0 +1,302 @@ +//===- Tokens.h - collect tokens from preprocessing --------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Record tokens that a preprocessor emits and define operations to map between +// the tokens written in a file and tokens produced by the preprocessor. +// +// When running the compiler, there are two token streams we are interested in: +// - "spelled" tokens directly correspond to a substring written in some +// source file. +// - "expanded" tokens represent the result of preprocessing, parses consumes +// this token stream to produce the AST. +// +// Expanded tokens correspond directly to locations found in the AST, allowing +// to find subranges of the token stream covered by various AST nodes. Spelled +// tokens correspond directly to the source code written by the user. +// +// To allow composing these two use-cases, we also define operations that map +// between expanded and spelled tokens that produced them (macro calls, +// directives, etc). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_SYNTAX_TOKENS_H +#define LLVM_CLANG_TOOLING_SYNTAX_TOKENS_H + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Lex/Token.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace clang { +class Preprocessor; + +namespace syntax { + +/// A half-open character range inside a particular file, the start offset is +/// included and the end offset is excluded from the range. +struct FileRange { + /// EXPECTS: File.isValid() && Begin <= End. + FileRange(FileID File, unsigned BeginOffset, unsigned EndOffset); + /// EXPECTS: BeginLoc.isValid() && BeginLoc.isFileID(). + FileRange(const SourceManager &SM, SourceLocation BeginLoc, unsigned Length); + /// EXPECTS: BeginLoc.isValid() && BeginLoc.isFileID(), Begin <= End and files + /// are the same. + FileRange(const SourceManager &SM, SourceLocation BeginLoc, + SourceLocation EndLoc); + + FileID file() const { return File; } + /// Start is a start offset (inclusive) in the corresponding file. + unsigned beginOffset() const { return Begin; } + /// End offset (exclusive) in the corresponding file. + unsigned endOffset() const { return End; } + + unsigned length() const { return End - Begin; } + + /// Gets the substring that this FileRange refers to. + llvm::StringRef text(const SourceManager &SM) const; + + friend bool operator==(const FileRange &L, const FileRange &R) { + return std::tie(L.File, L.Begin, L.End) == std::tie(R.File, R.Begin, R.End); + } + friend bool operator!=(const FileRange &L, const FileRange &R) { + return !(L == R); + } + +private: + FileID File; + unsigned Begin; + unsigned End; +}; + +/// For debugging purposes. +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FileRange &R); + +/// A token coming directly from a file or from a macro invocation. Has just +/// enough information to locate the token in the source code. +/// Can represent both expanded and spelled tokens. +class Token { +public: + Token(SourceLocation Location, unsigned Length, tok::TokenKind Kind) + : Location(Location), Length(Length), Kind(Kind) {} + /// EXPECTS: clang::Token is not an annotation token. + explicit Token(const clang::Token &T); + + tok::TokenKind kind() const { return Kind; } + /// Location of the first character of a token. + SourceLocation location() const { return Location; } + /// Location right after the last character of a token. + SourceLocation endLocation() const { + return Location.getLocWithOffset(Length); + } + unsigned length() const { return Length; } + + /// Get the substring covered by the token. Note that will include all + /// digraphs, newline continuations, etc. E.g. tokens for 'int' and + /// in\ + /// t + /// both have the same kind tok::kw_int, but results of text() are different. + llvm::StringRef text(const SourceManager &SM) const; + + /// Gets a range of this token. + /// EXPECTS: token comes from a file, not from a macro expansion. + FileRange range(const SourceManager &SM) const; + + /// Given two tokens inside the same file, returns a file range that starts at + /// \p First and ends at \p Last. + /// EXPECTS: First and Last are file tokens from the same file, Last starts + /// after First. + static FileRange range(const SourceManager &SM, const syntax::Token &First, + const syntax::Token &Last); + + std::string dumpForTests(const SourceManager &SM) const; + /// For debugging purposes. + std::string str() const; + +private: + SourceLocation Location; + unsigned Length; + tok::TokenKind Kind; +}; +/// For debugging purposes. Equivalent to a call to Token::str(). +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Token &T); + +/// A list of tokens obtained by preprocessing a text buffer and operations to +/// map between the expanded and spelled tokens, i.e. TokenBuffer has +/// information about two token streams: +/// 1. Expanded tokens: tokens produced by the preprocessor after all macro +/// replacements, +/// 2. Spelled tokens: corresponding directly to the source code of a file +/// before any macro replacements occurred. +/// Here's an example to illustrate a difference between those two: +/// #define FOO 10 +/// int a = FOO; +/// +/// Spelled tokens are {'#','define','FOO','10','int','a','=','FOO',';'}. +/// Expanded tokens are {'int','a','=','10',';','eof'}. +/// +/// Note that the expanded token stream has a tok::eof token at the end, the +/// spelled tokens never store a 'eof' token. +/// +/// The full list expanded tokens can be obtained with expandedTokens(). Spelled +/// tokens for each of the files can be obtained via spelledTokens(FileID). +/// +/// To map between the expanded and spelled tokens use findSpelledByExpanded(). +/// +/// To build a token buffer use the TokenCollector class. You can also compute +/// the spelled tokens of a file using the tokenize() helper. +/// +/// FIXME: allow to map from spelled to expanded tokens when use-case shows up. +class TokenBuffer { +public: + TokenBuffer(const SourceManager &SourceMgr) : SourceMgr(&SourceMgr) {} + /// All tokens produced by the preprocessor after all macro replacements, + /// directives, etc. Source locations found in the clang AST will always + /// point to one of these tokens. + /// FIXME: figure out how to handle token splitting, e.g. '>>' can be split + /// into two '>' tokens by the parser. However, TokenBuffer currently + /// keeps it as a single '>>' token. + llvm::ArrayRef expandedTokens() const { + return ExpandedTokens; + } + + /// Find the subrange of spelled tokens that produced the corresponding \p + /// Expanded tokens. + /// + /// EXPECTS: \p Expanded is a subrange of expandedTokens(). + /// + /// Will fail if the expanded tokens do not correspond to a + /// sequence of spelled tokens. E.g. for the following example: + /// + /// #define FIRST f1 f2 f3 + /// #define SECOND s1 s2 s3 + /// + /// a FIRST b SECOND c // expanded tokens are: a f1 f2 f3 b s1 s2 s3 c + /// + /// the results would be: + /// expanded => spelled + /// ------------------------ + /// a => a + /// s1 s2 s3 => SECOND + /// a f1 f2 f3 => a FIRST + /// a f1 => can't map + /// s1 s2 => can't map + /// + /// If \p Expanded is empty, the returned value is llvm::None. + /// Complexity is logarithmic. + llvm::Optional> + spelledForExpanded(llvm::ArrayRef Expanded) const; + + /// Lexed tokens of a file before preprocessing. E.g. for the following input + /// #define DECL(name) int name = 10 + /// DECL(a); + /// spelledTokens() returns {"#", "define", "DECL", "(", "name", ")", "eof"}. + /// FIXME: we do not yet store tokens of directives, like #include, #define, + /// #pragma, etc. + llvm::ArrayRef spelledTokens(FileID FID) const; + + std::string dumpForTests() const; + +private: + /// Describes a mapping between a continuous subrange of spelled tokens and + /// expanded tokens. Represents macro expansions, preprocessor directives, + /// conditionally disabled pp regions, etc. + /// #define FOO 1+2 + /// #define BAR(a) a + 1 + /// FOO // invocation #1, tokens = {'1','+','2'}, macroTokens = {'FOO'}. + /// BAR(1) // invocation #2, tokens = {'a', '+', '1'}, + /// macroTokens = {'BAR', '(', '1', ')'}. + struct Mapping { + // Positions in the corresponding spelled token stream. The corresponding + // range is never empty. + unsigned BeginSpelled = 0; + unsigned EndSpelled = 0; + // Positions in the expanded token stream. The corresponding range can be + // empty. + unsigned BeginExpanded = 0; + unsigned EndExpanded = 0; + + /// For debugging purposes. + std::string str() const; + }; + /// Spelled tokens of the file with information about the subranges. + struct MarkedFile { + /// Lexed, but not preprocessed, tokens of the file. These map directly to + /// text in the corresponding files and include tokens of all preprocessor + /// directives. + /// FIXME: spelled tokens don't change across FileID that map to the same + /// FileEntry. We could consider deduplicating them to save memory. + std::vector SpelledTokens; + /// A sorted list to convert between the spelled and expanded token streams. + std::vector Mappings; + /// The first expanded token produced for this FileID. + unsigned BeginExpanded = 0; + unsigned EndExpanded = 0; + }; + + friend class TokenCollector; + + /// Maps a single expanded token to its spelled counterpart or a mapping that + /// produced it. + std::pair + spelledForExpandedToken(const syntax::Token *Expanded) const; + + /// Token stream produced after preprocessing, conceputally this captures the + /// same stream as 'clang -E' (excluding the preprocessor directives like + /// #file, etc.). + std::vector ExpandedTokens; + llvm::DenseMap Files; + // The value is never null, pointer instead of reference to avoid disabling + // implicit assignment operator. + const SourceManager *SourceMgr; +}; + +/// Lex the text buffer, corresponding to \p FID, in raw mode and record the +/// resulting spelled tokens. Does minimal post-processing on raw identifiers, +/// setting the appropriate token kind (instead of the raw_identifier reported +/// by lexer in raw mode). This is a very low-level function, most users should +/// prefer to use TokenCollector. Lexing in raw mode produces wildly different +/// results from what one might expect when running a C++ frontend, e.g. +/// preprocessor does not run at all. +/// The result will *not* have a 'eof' token at the end. +std::vector tokenize(FileID FID, const SourceManager &SM, + const LangOptions &LO); + +/// Collects tokens for the main file while running the frontend action. An +/// instance of this object should be created on +/// FrontendAction::BeginSourceFile() and the results should be consumed after +/// FrontendAction::Execute() finishes. +class TokenCollector { +public: + /// Adds the hooks to collect the tokens. Should be called before the + /// preprocessing starts, i.e. as a part of BeginSourceFile() or + /// CreateASTConsumer(). + TokenCollector(Preprocessor &P); + + /// Finalizes token collection. Should be called after preprocessing is + /// finished, i.e. after running Execute(). + LLVM_NODISCARD TokenBuffer consume() &&; + +private: + class Builder; + std::vector Expanded; + Preprocessor &PP; +}; + +} // namespace syntax +} // namespace clang + +#endif diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -4352,16 +4352,14 @@ llvm::DIFile *Unit = getOrCreateFile(VD->getLocation()); StringRef Name = VD->getName(); llvm::DIType *Ty = getOrCreateType(VD->getType(), Unit); + + // Do not use global variables for enums. if (const auto *ECD = dyn_cast(VD)) { const auto *ED = cast(ECD->getDeclContext()); assert(isa(ED->getTypeForDecl()) && "Enum without EnumType?"); - Ty = getOrCreateType(QualType(ED->getTypeForDecl(), 0), Unit); - } - // Do not use global variables for enums. - // - // FIXME: why not? - if (Ty->getTag() == llvm::dwarf::DW_TAG_enumeration_type) return; + } + // Do not emit separate definitions for function local const/statics. if (isa(VD->getDeclContext())) return; diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt --- a/clang/lib/Tooling/CMakeLists.txt +++ b/clang/lib/Tooling/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(Inclusions) add_subdirectory(Refactoring) add_subdirectory(ASTDiff) +add_subdirectory(Syntax) add_clang_library(clangTooling AllTUsExecution.cpp diff --git a/clang/lib/Tooling/Refactoring/Stencil.cpp b/clang/lib/Tooling/Refactoring/Stencil.cpp --- a/clang/lib/Tooling/Refactoring/Stencil.cpp +++ b/clang/lib/Tooling/Refactoring/Stencil.cpp @@ -55,22 +55,11 @@ explicit DebugPrintNodeOpData(std::string S) : Id(std::move(S)) {} std::string Id; }; -// Whether to associate a trailing semicolon with a node when identifying it's -// text. This flag is needed for expressions (clang::Expr), because their role -// is ambiguous when they are also complete statements. When this flag is -// `Always`, an expression node will be treated like a statement, and will -// therefore be associated with any trailing semicolon. -enum class SemiAssociation : bool { - Always, - Inferred, -}; -// A reference to a particular (bound) AST node. -struct NodeRefData { - explicit NodeRefData(std::string S, SemiAssociation SA) - : Id(std::move(S)), SemiAssoc(SA) {} - std::string Id; - SemiAssociation SemiAssoc; +// The fragment of code corresponding to the selected range. +struct SelectorOpData { + explicit SelectorOpData(RangeSelector S) : Selector(std::move(S)) {} + RangeSelector Selector; }; } // namespace @@ -82,9 +71,8 @@ return A.Id == B.Id; } -bool isEqualData(const NodeRefData &A, const NodeRefData &B) { - return A.Id == B.Id && A.SemiAssoc == B.SemiAssoc; -} +// Equality is not (yet) defined for \c RangeSelector. +bool isEqualData(const SelectorOpData &, const SelectorOpData &) { return false; } // The `evalData()` overloads evaluate the given stencil data to a string, given // the match result, and append it to `Result`. We define an overload for each @@ -108,25 +96,12 @@ return Error::success(); } -Error evalData(const NodeRefData &Data, const MatchFinder::MatchResult &Match, +Error evalData(const SelectorOpData &Data, const MatchFinder::MatchResult &Match, std::string *Result) { - auto NodeOrErr = getNode(Match.Nodes, Data.Id); - if (auto Err = NodeOrErr.takeError()) - return Err; - auto &Node = *NodeOrErr; - switch (Data.SemiAssoc) { - case SemiAssociation::Inferred: - // Include the semicolon for non-expression statements: - *Result += Node.get() != nullptr && Node.get() == nullptr - ? getExtendedText(NodeOrErr.get(), tok::TokenKind::semi, - *Match.Context) - : getText(NodeOrErr.get(), *Match.Context); - break; - case SemiAssociation::Always: - *Result += - getExtendedText(NodeOrErr.get(), tok::TokenKind::semi, *Match.Context); - break; - } + auto Range = Data.Selector(Match); + if (!Range) + return Range.takeError(); + *Result += getText(*Range, *Match.Context); return Error::success(); } @@ -162,13 +137,17 @@ namespace { using RawText = StencilPartImpl; using DebugPrintNodeOp = StencilPartImpl; -using NodeRef = StencilPartImpl; +using SelectorOp = StencilPartImpl; } // namespace StencilPart Stencil::wrap(StringRef Text) { return stencil::text(Text); } +StencilPart Stencil::wrap(RangeSelector Selector) { + return stencil::selection(std::move(Selector)); +} + void Stencil::append(Stencil OtherStencil) { for (auto &Part : OtherStencil.Parts) Parts.push_back(std::move(Part)); @@ -187,12 +166,8 @@ return StencilPart(std::make_shared(Text)); } -StencilPart stencil::node(StringRef Id) { - return StencilPart(std::make_shared(Id, SemiAssociation::Inferred)); -} - -StencilPart stencil::sNode(StringRef Id) { - return StencilPart(std::make_shared(Id, SemiAssociation::Always)); +StencilPart stencil::selection(RangeSelector Selector) { + return StencilPart(std::make_shared(std::move(Selector))); } StencilPart stencil::dPrint(StringRef Id) { diff --git a/clang/lib/Tooling/Refactoring/Transformer.cpp b/clang/lib/Tooling/Refactoring/Transformer.cpp --- a/clang/lib/Tooling/Refactoring/Transformer.cpp +++ b/clang/lib/Tooling/Refactoring/Transformer.cpp @@ -32,11 +32,7 @@ using ast_type_traits::ASTNodeKind; using ast_type_traits::DynTypedNode; using llvm::Error; -using llvm::Expected; -using llvm::Optional; using llvm::StringError; -using llvm::StringRef; -using llvm::Twine; using MatchResult = MatchFinder::MatchResult; @@ -71,91 +67,12 @@ return false; } -static llvm::Error invalidArgumentError(Twine Message) { - return llvm::make_error(llvm::errc::invalid_argument, Message); -} - -static llvm::Error typeError(StringRef Id, const ASTNodeKind &Kind, - Twine Message) { - return invalidArgumentError( - Message + " (node id=" + Id + " kind=" + Kind.asStringRef() + ")"); -} - -static llvm::Error missingPropertyError(StringRef Id, Twine Description, - StringRef Property) { - return invalidArgumentError(Description + " requires property '" + Property + - "' (node id=" + Id + ")"); -} - -static Expected -getTargetRange(StringRef Target, const DynTypedNode &Node, ASTNodeKind Kind, - NodePart TargetPart, ASTContext &Context) { - switch (TargetPart) { - case NodePart::Node: { - // For non-expression statements, associate any trailing semicolon with the - // statement text. However, if the target was intended as an expression (as - // indicated by its kind) then we do not associate any trailing semicolon - // with it. We only associate the exact expression text. - if (Node.get() != nullptr) { - auto ExprKind = ASTNodeKind::getFromNodeKind(); - if (!ExprKind.isBaseOf(Kind)) - return getExtendedRange(Node, tok::TokenKind::semi, Context); - } - return CharSourceRange::getTokenRange(Node.getSourceRange()); - } - case NodePart::Member: - if (auto *M = Node.get()) - return CharSourceRange::getTokenRange( - M->getMemberNameInfo().getSourceRange()); - return typeError(Target, Node.getNodeKind(), - "NodePart::Member applied to non-MemberExpr"); - case NodePart::Name: - if (const auto *D = Node.get()) { - if (!D->getDeclName().isIdentifier()) - return missingPropertyError(Target, "NodePart::Name", "identifier"); - SourceLocation L = D->getLocation(); - auto R = CharSourceRange::getTokenRange(L, L); - // Verify that the range covers exactly the name. - // FIXME: extend this code to support cases like `operator +` or - // `foo` for which this range will be too short. Doing so will - // require subcasing `NamedDecl`, because it doesn't provide virtual - // access to the \c DeclarationNameInfo. - if (getText(R, Context) != D->getName()) - return CharSourceRange(); - return R; - } - if (const auto *E = Node.get()) { - if (!E->getNameInfo().getName().isIdentifier()) - return missingPropertyError(Target, "NodePart::Name", "identifier"); - SourceLocation L = E->getLocation(); - return CharSourceRange::getTokenRange(L, L); - } - if (const auto *I = Node.get()) { - if (!I->isMemberInitializer() && I->isWritten()) - return missingPropertyError(Target, "NodePart::Name", - "explicit member initializer"); - SourceLocation L = I->getMemberLocation(); - return CharSourceRange::getTokenRange(L, L); - } - return typeError( - Target, Node.getNodeKind(), - "NodePart::Name applied to neither DeclRefExpr, NamedDecl nor " - "CXXCtorInitializer"); - } - llvm_unreachable("Unexpected case in NodePart type."); -} - Expected> tooling::detail::translateEdits(const MatchResult &Result, llvm::ArrayRef Edits) { - SmallVector Transformations; - auto &NodesMap = Result.Nodes.getMap(); + SmallVector Transformations; for (const auto &Edit : Edits) { - auto It = NodesMap.find(Edit.Target); - assert(It != NodesMap.end() && "Edit target must be bound in the match."); - - Expected Range = getTargetRange( - Edit.Target, It->second, Edit.Kind, Edit.Part, *Result.Context); + Expected Range = Edit.TargetRange(Result); if (!Range) return Range.takeError(); if (Range->isInvalid() || @@ -164,7 +81,7 @@ auto Replacement = Edit.Replacement(Result); if (!Replacement) return Replacement.takeError(); - Transformation T; + tooling::detail::Transformation T; T.Range = *Range; T.Replacement = std::move(*Replacement); Transformations.push_back(std::move(T)); @@ -172,6 +89,13 @@ return Transformations; } +ASTEdit tooling::change(RangeSelector S, TextGenerator Replacement) { + ASTEdit E; + E.TargetRange = std::move(S); + E.Replacement = std::move(Replacement); + return E; +} + RewriteRule tooling::makeRule(DynTypedMatcher M, SmallVector Edits) { return RewriteRule{ @@ -255,7 +179,7 @@ DynTypedMatcher M = joinCaseMatchers(Rule); M.setAllowBind(true); // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true. - return *M.tryBind(RewriteRule::RootId); + return *M.tryBind(RewriteRule::RootID); } // Finds the case that was "selected" -- that is, whose matcher triggered the @@ -275,7 +199,7 @@ llvm_unreachable("No tag found for this rule."); } -constexpr llvm::StringLiteral RewriteRule::RootId; +constexpr llvm::StringLiteral RewriteRule::RootID; void Transformer::registerMatchers(MatchFinder *MatchFinder) { MatchFinder->addDynamicMatcher(tooling::detail::buildMatcher(Rule), this); @@ -287,7 +211,7 @@ // Verify the existence and validity of the AST node that roots this rule. auto &NodesMap = Result.Nodes.getMap(); - auto Root = NodesMap.find(RewriteRule::RootId); + auto Root = NodesMap.find(RewriteRule::RootID); assert(Root != NodesMap.end() && "Transformation failed: missing root node."); SourceLocation RootLoc = Result.SourceManager->getExpansionLoc( Root->second.getSourceRange().getBegin()); diff --git a/clang/lib/Tooling/Syntax/CMakeLists.txt b/clang/lib/Tooling/Syntax/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/clang/lib/Tooling/Syntax/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS Support) + +add_clang_library(clangToolingSyntax + Tokens.cpp + + LINK_LIBS + clangBasic + clangFrontend + clangLex + ) diff --git a/clang/lib/Tooling/Syntax/Tokens.cpp b/clang/lib/Tooling/Syntax/Tokens.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Tooling/Syntax/Tokens.cpp @@ -0,0 +1,509 @@ +//===- Tokens.cpp - collect tokens from preprocessing ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "clang/Tooling/Syntax/Tokens.h" + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Token.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include + +using namespace clang; +using namespace clang::syntax; + +syntax::Token::Token(const clang::Token &T) + : Token(T.getLocation(), T.getLength(), T.getKind()) { + assert(!T.isAnnotation()); +} + +llvm::StringRef syntax::Token::text(const SourceManager &SM) const { + bool Invalid = false; + const char *Start = SM.getCharacterData(location(), &Invalid); + assert(!Invalid); + return llvm::StringRef(Start, length()); +} + +FileRange syntax::Token::range(const SourceManager &SM) const { + assert(location().isFileID() && "must be a spelled token"); + FileID File; + unsigned StartOffset; + std::tie(File, StartOffset) = SM.getDecomposedLoc(location()); + return FileRange(File, StartOffset, StartOffset + length()); +} + +FileRange syntax::Token::range(const SourceManager &SM, + const syntax::Token &First, + const syntax::Token &Last) { + auto F = First.range(SM); + auto L = Last.range(SM); + assert(F.file() == L.file() && "tokens from different files"); + assert(F.endOffset() <= L.beginOffset() && "wrong order of tokens"); + return FileRange(F.file(), F.beginOffset(), L.endOffset()); +} + +llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, const Token &T) { + return OS << T.str(); +} + +FileRange::FileRange(FileID File, unsigned BeginOffset, unsigned EndOffset) + : File(File), Begin(BeginOffset), End(EndOffset) { + assert(File.isValid()); + assert(BeginOffset <= EndOffset); +} + +FileRange::FileRange(const SourceManager &SM, SourceLocation BeginLoc, + unsigned Length) { + assert(BeginLoc.isValid()); + assert(BeginLoc.isFileID()); + + std::tie(File, Begin) = SM.getDecomposedLoc(BeginLoc); + End = Begin + Length; +} +FileRange::FileRange(const SourceManager &SM, SourceLocation BeginLoc, + SourceLocation EndLoc) { + assert(BeginLoc.isValid()); + assert(BeginLoc.isFileID()); + assert(EndLoc.isValid()); + assert(EndLoc.isFileID()); + assert(SM.getFileID(BeginLoc) == SM.getFileID(EndLoc)); + assert(SM.getFileOffset(BeginLoc) <= SM.getFileOffset(EndLoc)); + + std::tie(File, Begin) = SM.getDecomposedLoc(BeginLoc); + End = SM.getFileOffset(EndLoc); +} + +llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, + const FileRange &R) { + return OS << llvm::formatv("FileRange(file = {0}, offsets = {1}-{2})", + R.file().getHashValue(), R.beginOffset(), + R.endOffset()); +} + +llvm::StringRef FileRange::text(const SourceManager &SM) const { + bool Invalid = false; + StringRef Text = SM.getBufferData(File, &Invalid); + if (Invalid) + return ""; + assert(Begin <= Text.size()); + assert(End <= Text.size()); + return Text.substr(Begin, length()); +} + +std::pair +TokenBuffer::spelledForExpandedToken(const syntax::Token *Expanded) const { + assert(Expanded); + assert(ExpandedTokens.data() <= Expanded && + Expanded < ExpandedTokens.data() + ExpandedTokens.size()); + + auto FileIt = Files.find( + SourceMgr->getFileID(SourceMgr->getExpansionLoc(Expanded->location()))); + assert(FileIt != Files.end() && "no file for an expanded token"); + + const MarkedFile &File = FileIt->second; + + unsigned ExpandedIndex = Expanded - ExpandedTokens.data(); + // Find the first mapping that produced tokens after \p Expanded. + auto It = llvm::bsearch(File.Mappings, [&](const Mapping &M) { + return ExpandedIndex < M.BeginExpanded; + }); + // Our token could only be produced by the previous mapping. + if (It == File.Mappings.begin()) { + // No previous mapping, no need to modify offsets. + return {&File.SpelledTokens[ExpandedIndex - File.BeginExpanded], nullptr}; + } + --It; // 'It' now points to last mapping that started before our token. + + // Check if the token is part of the mapping. + if (ExpandedIndex < It->EndExpanded) + return {&File.SpelledTokens[It->BeginSpelled], /*Mapping*/ &*It}; + + // Not part of the mapping, use the index from previous mapping to compute the + // corresponding spelled token. + return { + &File.SpelledTokens[It->EndSpelled + (ExpandedIndex - It->EndExpanded)], + /*Mapping*/ nullptr}; +} + +llvm::ArrayRef TokenBuffer::spelledTokens(FileID FID) const { + auto It = Files.find(FID); + assert(It != Files.end()); + return It->second.SpelledTokens; +} + +std::string TokenBuffer::Mapping::str() const { + return llvm::formatv("spelled tokens: [{0},{1}), expanded tokens: [{2},{3})", + BeginSpelled, EndSpelled, BeginExpanded, EndExpanded); +} + +llvm::Optional> +TokenBuffer::spelledForExpanded(llvm::ArrayRef Expanded) const { + // Mapping an empty range is ambiguous in case of empty mappings at either end + // of the range, bail out in that case. + if (Expanded.empty()) + return llvm::None; + + // FIXME: also allow changes uniquely mapping to macro arguments. + + const syntax::Token *BeginSpelled; + const Mapping *BeginMapping; + std::tie(BeginSpelled, BeginMapping) = + spelledForExpandedToken(&Expanded.front()); + + const syntax::Token *LastSpelled; + const Mapping *LastMapping; + std::tie(LastSpelled, LastMapping) = + spelledForExpandedToken(&Expanded.back()); + + FileID FID = SourceMgr->getFileID(BeginSpelled->location()); + // FIXME: Handle multi-file changes by trying to map onto a common root. + if (FID != SourceMgr->getFileID(LastSpelled->location())) + return llvm::None; + + const MarkedFile &File = Files.find(FID)->second; + + // Do not allow changes that cross macro expansion boundaries. + unsigned BeginExpanded = Expanded.begin() - ExpandedTokens.data(); + unsigned EndExpanded = Expanded.end() - ExpandedTokens.data(); + if (BeginMapping && BeginMapping->BeginExpanded < BeginExpanded) + return llvm::None; + if (LastMapping && EndExpanded < LastMapping->EndExpanded) + return llvm::None; + // All is good, return the result. + return llvm::makeArrayRef( + BeginMapping ? File.SpelledTokens.data() + BeginMapping->BeginSpelled + : BeginSpelled, + LastMapping ? File.SpelledTokens.data() + LastMapping->EndSpelled + : LastSpelled + 1); +} + +std::vector syntax::tokenize(FileID FID, const SourceManager &SM, + const LangOptions &LO) { + std::vector Tokens; + IdentifierTable Identifiers(LO); + auto AddToken = [&](clang::Token T) { + // Fill the proper token kind for keywords, etc. + if (T.getKind() == tok::raw_identifier && !T.needsCleaning() && + !T.hasUCN()) { // FIXME: support needsCleaning and hasUCN cases. + clang::IdentifierInfo &II = Identifiers.get(T.getRawIdentifier()); + T.setIdentifierInfo(&II); + T.setKind(II.getTokenID()); + } + Tokens.push_back(syntax::Token(T)); + }; + + Lexer L(FID, SM.getBuffer(FID), SM, LO); + + clang::Token T; + while (!L.LexFromRawLexer(T)) + AddToken(T); + // 'eof' is only the last token if the input is null-terminated. Never store + // it, for consistency. + if (T.getKind() != tok::eof) + AddToken(T); + return Tokens; +} + +/// Fills in the TokenBuffer by tracing the run of a preprocessor. The +/// implementation tracks the tokens, macro expansions and directives coming +/// from the preprocessor and: +/// - for each token, figures out if it is a part of an expanded token stream, +/// spelled token stream or both. Stores the tokens appropriately. +/// - records mappings from the spelled to expanded token ranges, e.g. for macro +/// expansions. +/// FIXME: also properly record: +/// - #include directives, +/// - #pragma, #line and other PP directives, +/// - skipped pp regions, +/// - ... + +TokenCollector::TokenCollector(Preprocessor &PP) : PP(PP) { + // Collect the expanded token stream during preprocessing. + PP.setTokenWatcher([this](const clang::Token &T) { + if (T.isAnnotation()) + return; + DEBUG_WITH_TYPE("collect-tokens", llvm::dbgs() + << "Token: " + << syntax::Token(T).dumpForTests( + this->PP.getSourceManager()) + << "\n" + + ); + Expanded.push_back(syntax::Token(T)); + }); +} + +/// Builds mappings and spelled tokens in the TokenBuffer based on the expanded +/// token stream. +class TokenCollector::Builder { +public: + Builder(std::vector Expanded, const SourceManager &SM, + const LangOptions &LangOpts) + : Result(SM), SM(SM), LangOpts(LangOpts) { + Result.ExpandedTokens = std::move(Expanded); + } + + TokenBuffer build() && { + buildSpelledTokens(); + + // Walk over expanded tokens and spelled tokens in parallel, building the + // mappings between those using source locations. + + // The 'eof' token is special, it is not part of spelled token stream. We + // handle it separately at the end. + assert(!Result.ExpandedTokens.empty()); + assert(Result.ExpandedTokens.back().kind() == tok::eof); + for (unsigned I = 0; I < Result.ExpandedTokens.size() - 1; ++I) { + // (!) I might be updated by the following call. + processExpandedToken(I); + } + + // 'eof' not handled in the loop, do it here. + assert(SM.getMainFileID() == + SM.getFileID(Result.ExpandedTokens.back().location())); + fillGapUntil(Result.Files[SM.getMainFileID()], + Result.ExpandedTokens.back().location(), + Result.ExpandedTokens.size() - 1); + Result.Files[SM.getMainFileID()].EndExpanded = Result.ExpandedTokens.size(); + + // Some files might have unaccounted spelled tokens at the end, add an empty + // mapping for those as they did not have expanded counterparts. + fillGapsAtEndOfFiles(); + + return std::move(Result); + } + +private: + /// Process the next token in an expanded stream and move corresponding + /// spelled tokens, record any mapping if needed. + /// (!) \p I will be updated if this had to skip tokens, e.g. for macros. + void processExpandedToken(unsigned &I) { + auto L = Result.ExpandedTokens[I].location(); + if (L.isMacroID()) { + processMacroExpansion(SM.getExpansionRange(L), I); + return; + } + if (L.isFileID()) { + auto FID = SM.getFileID(L); + TokenBuffer::MarkedFile &File = Result.Files[FID]; + + fillGapUntil(File, L, I); + + // Skip the token. + assert(File.SpelledTokens[NextSpelled[FID]].location() == L && + "no corresponding token in the spelled stream"); + ++NextSpelled[FID]; + return; + } + } + + /// Skipped expanded and spelled tokens of a macro expansion that covers \p + /// SpelledRange. Add a corresponding mapping. + /// (!) \p I will be the index of the last token in an expansion after this + /// function returns. + void processMacroExpansion(CharSourceRange SpelledRange, unsigned &I) { + auto FID = SM.getFileID(SpelledRange.getBegin()); + assert(FID == SM.getFileID(SpelledRange.getEnd())); + TokenBuffer::MarkedFile &File = Result.Files[FID]; + + fillGapUntil(File, SpelledRange.getBegin(), I); + + TokenBuffer::Mapping M; + // Skip the spelled macro tokens. + std::tie(M.BeginSpelled, M.EndSpelled) = + consumeSpelledUntil(File, SpelledRange.getEnd().getLocWithOffset(1)); + // Skip all expanded tokens from the same macro expansion. + M.BeginExpanded = I; + for (; I + 1 < Result.ExpandedTokens.size(); ++I) { + auto NextL = Result.ExpandedTokens[I + 1].location(); + if (!NextL.isMacroID() || + SM.getExpansionLoc(NextL) != SpelledRange.getBegin()) + break; + } + M.EndExpanded = I + 1; + + // Add a resulting mapping. + File.Mappings.push_back(M); + } + + /// Initializes TokenBuffer::Files and fills spelled tokens and expanded + /// ranges for each of the files. + void buildSpelledTokens() { + for (unsigned I = 0; I < Result.ExpandedTokens.size(); ++I) { + auto FID = + SM.getFileID(SM.getExpansionLoc(Result.ExpandedTokens[I].location())); + auto It = Result.Files.try_emplace(FID); + TokenBuffer::MarkedFile &File = It.first->second; + + File.EndExpanded = I + 1; + if (!It.second) + continue; // we have seen this file before. + + // This is the first time we see this file. + File.BeginExpanded = I; + File.SpelledTokens = tokenize(FID, SM, LangOpts); + } + } + + /// Consumed spelled tokens until location L is reached (token starting at L + /// is not included). Returns the indicies of the consumed range. + std::pair + consumeSpelledUntil(TokenBuffer::MarkedFile &File, SourceLocation L) { + assert(L.isFileID()); + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(L); + + // (!) we update the index in-place. + unsigned &SpelledI = NextSpelled[FID]; + unsigned Before = SpelledI; + for (; SpelledI < File.SpelledTokens.size() && + SM.getFileOffset(File.SpelledTokens[SpelledI].location()) < Offset; + ++SpelledI) { + } + return std::make_pair(Before, /*After*/ SpelledI); + }; + + /// Consumes spelled tokens until location \p L is reached and adds a mapping + /// covering the consumed tokens. The mapping will point to an empty expanded + /// range at position \p ExpandedIndex. + void fillGapUntil(TokenBuffer::MarkedFile &File, SourceLocation L, + unsigned ExpandedIndex) { + unsigned BeginSpelledGap, EndSpelledGap; + std::tie(BeginSpelledGap, EndSpelledGap) = consumeSpelledUntil(File, L); + if (BeginSpelledGap == EndSpelledGap) + return; // No gap. + TokenBuffer::Mapping M; + M.BeginSpelled = BeginSpelledGap; + M.EndSpelled = EndSpelledGap; + M.BeginExpanded = M.EndExpanded = ExpandedIndex; + File.Mappings.push_back(M); + }; + + /// Adds empty mappings for unconsumed spelled tokens at the end of each file. + void fillGapsAtEndOfFiles() { + for (auto &F : Result.Files) { + unsigned Next = NextSpelled[F.first]; + if (F.second.SpelledTokens.size() == Next) + continue; // All spelled tokens are accounted for. + + // Record a mapping for the gap at the end of the spelled tokens. + TokenBuffer::Mapping M; + M.BeginSpelled = Next; + M.EndSpelled = F.second.SpelledTokens.size(); + M.BeginExpanded = F.second.EndExpanded; + M.EndExpanded = F.second.EndExpanded; + + F.second.Mappings.push_back(M); + } + } + + TokenBuffer Result; + /// For each file, a position of the next spelled token we will consume. + llvm::DenseMap NextSpelled; + const SourceManager &SM; + const LangOptions &LangOpts; +}; + +TokenBuffer TokenCollector::consume() && { + PP.setTokenWatcher(nullptr); + return Builder(std::move(Expanded), PP.getSourceManager(), PP.getLangOpts()) + .build(); +} + +std::string syntax::Token::str() const { + return llvm::formatv("Token({0}, length = {1})", tok::getTokenName(kind()), + length()); +} + +std::string syntax::Token::dumpForTests(const SourceManager &SM) const { + return llvm::formatv("{0} {1}", tok::getTokenName(kind()), text(SM)); +} + +std::string TokenBuffer::dumpForTests() const { + auto PrintToken = [this](const syntax::Token &T) -> std::string { + if (T.kind() == tok::eof) + return ""; + return T.text(*SourceMgr); + }; + + auto DumpTokens = [this, &PrintToken](llvm::raw_ostream &OS, + llvm::ArrayRef Tokens) { + if (Tokens.size() == 1) { + assert(Tokens[0].kind() == tok::eof); + OS << ""; + return; + } + OS << Tokens[0].text(*SourceMgr); + for (unsigned I = 1; I < Tokens.size(); ++I) { + if (Tokens[I].kind() == tok::eof) + continue; + OS << " " << PrintToken(Tokens[I]); + } + }; + + std::string Dump; + llvm::raw_string_ostream OS(Dump); + + OS << "expanded tokens:\n" + << " "; + DumpTokens(OS, ExpandedTokens); + OS << "\n"; + + std::vector Keys; + for (auto F : Files) + Keys.push_back(F.first); + llvm::sort(Keys); + + for (FileID ID : Keys) { + const MarkedFile &File = Files.find(ID)->second; + auto *Entry = SourceMgr->getFileEntryForID(ID); + if (!Entry) + continue; // Skip builtin files. + OS << llvm::formatv("file '{0}'\n", Entry->getName()) + << " spelled tokens:\n" + << " "; + DumpTokens(OS, File.SpelledTokens); + OS << "\n"; + + if (File.Mappings.empty()) { + OS << " no mappings.\n"; + continue; + } + OS << " mappings:\n"; + for (auto &M : File.Mappings) { + OS << llvm::formatv( + " ['{0}'_{1}, '{2}'_{3}) => ['{4}'_{5}, '{6}'_{7})\n", + PrintToken(File.SpelledTokens[M.BeginSpelled]), M.BeginSpelled, + M.EndSpelled == File.SpelledTokens.size() + ? "" + : PrintToken(File.SpelledTokens[M.EndSpelled]), + M.EndSpelled, PrintToken(ExpandedTokens[M.BeginExpanded]), + M.BeginExpanded, PrintToken(ExpandedTokens[M.EndExpanded]), + M.EndExpanded); + } + } + return OS.str(); +} diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -72,3 +72,6 @@ clangToolingInclusions clangToolingRefactor ) + + +add_subdirectory(Syntax) diff --git a/clang/unittests/Tooling/StencilTest.cpp b/clang/unittests/Tooling/StencilTest.cpp --- a/clang/unittests/Tooling/StencilTest.cpp +++ b/clang/unittests/Tooling/StencilTest.cpp @@ -22,9 +22,9 @@ using ::testing::Eq; using ::testing::HasSubstr; using MatchResult = MatchFinder::MatchResult; -using tooling::stencil::node; -using tooling::stencil::sNode; -using tooling::stencil::text; +using stencil::cat; +using stencil::dPrint; +using stencil::text; // In tests, we can't directly match on llvm::Expected since its accessors // mutate the object. So, we collapse it to an Optional. @@ -141,8 +141,8 @@ hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); ASSERT_TRUE(StmtMatch); // Invert the if-then-else. - auto Stencil = Stencil::cat("if (!", node(Condition), ") ", sNode(Else), - " else ", sNode(Then)); + auto Stencil = cat("if (!", node(Condition), ") ", statement(Else), " else ", + statement(Then)); EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), IsSomething(Eq("if (!true) return 0; else return 1;"))); } @@ -160,8 +160,8 @@ hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); ASSERT_TRUE(StmtMatch); // Invert the if-then-else. - Stencil S = Stencil::cat("if (!", node(Condition), ") ", sNode(Else), - " else ", sNode(Then)); + Stencil S = cat("if (!", node(Condition), ") ", statement(Else), " else ", + statement(Then)); EXPECT_THAT(toOptional(S(StmtMatch->Result)), IsSomething(Eq("if (!true) return 0; else return 1;"))); } @@ -176,7 +176,7 @@ auto StmtMatch = matchStmt(Snippet, ifStmt(hasCondition(stmt().bind("a1")), hasThen(stmt().bind("a2")))); ASSERT_TRUE(StmtMatch); - auto Stencil = Stencil::cat("if(!", sNode("a1"), ") ", node("UNBOUND"), ";"); + auto Stencil = cat("if(!", node("a1"), ") ", node("UNBOUND"), ";"); auto ResultOrErr = Stencil.eval(StmtMatch->Result); EXPECT_TRUE(llvm::errorToBool(ResultOrErr.takeError())) << "Expected unbound node, got " << *ResultOrErr; @@ -192,32 +192,33 @@ IsSomething(Expected)); } -TEST_F(StencilTest, NodeOp) { +TEST_F(StencilTest, SelectionOp) { StringRef Id = "id"; - testExpr(Id, "3;", Stencil::cat(node(Id)), "3"); -} - -TEST_F(StencilTest, SNodeOp) { - StringRef Id = "id"; - testExpr(Id, "3;", Stencil::cat(sNode(Id)), "3;"); + testExpr(Id, "3;", cat(node(Id)), "3"); } TEST(StencilEqualityTest, Equality) { - using stencil::dPrint; - auto Lhs = Stencil::cat("foo", node("node"), dPrint("dprint_id")); - auto Rhs = Lhs; + auto Lhs = cat("foo", dPrint("dprint_id")); + auto Rhs = cat("foo", dPrint("dprint_id")); EXPECT_EQ(Lhs, Rhs); } TEST(StencilEqualityTest, InEqualityDifferentOrdering) { - auto Lhs = Stencil::cat("foo", node("node")); - auto Rhs = Stencil::cat(node("node"), "foo"); + auto Lhs = cat("foo", dPrint("node")); + auto Rhs = cat(dPrint("node"), "foo"); EXPECT_NE(Lhs, Rhs); } TEST(StencilEqualityTest, InEqualityDifferentSizes) { - auto Lhs = Stencil::cat("foo", node("node"), "bar", "baz"); - auto Rhs = Stencil::cat("foo", node("node"), "bar"); + auto Lhs = cat("foo", dPrint("node"), "bar", "baz"); + auto Rhs = cat("foo", dPrint("node"), "bar"); EXPECT_NE(Lhs, Rhs); } + +// node is opaque and therefore cannot be examined for equality. +TEST(StencilEqualityTest, InEqualitySelection) { + auto S1 = cat(node("node")); + auto S2 = cat(node("node")); + EXPECT_NE(S1, S2); +} } // namespace diff --git a/clang/unittests/Tooling/Syntax/CMakeLists.txt b/clang/unittests/Tooling/Syntax/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/clang/unittests/Tooling/Syntax/CMakeLists.txt @@ -0,0 +1,20 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Support + ) + +add_clang_unittest(SyntaxTests + TokensTest.cpp +) + +target_link_libraries(SyntaxTests + PRIVATE + clangAST + clangBasic + clangFrontend + clangLex + clangSerialization + clangTooling + clangToolingSyntax + LLVMTestingSupport + ) diff --git a/clang/unittests/Tooling/Syntax/TokensTest.cpp b/clang/unittests/Tooling/Syntax/TokensTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Tooling/Syntax/TokensTest.cpp @@ -0,0 +1,654 @@ +//===- TokensTest.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Syntax/Tokens.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Expr.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.def" +#include "clang/Basic/TokenKinds.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Lex/Token.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_os_ostream.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Testing/Support/Annotations.h" +#include "llvm/Testing/Support/SupportHelpers.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace clang; +using namespace clang::syntax; + +using llvm::ValueIs; +using ::testing::AllOf; +using ::testing::Contains; +using ::testing::ElementsAre; +using ::testing::Matcher; +using ::testing::Not; +using ::testing::StartsWith; + +namespace { +// Checks the passed ArrayRef has the same begin() and end() iterators as the +// argument. +MATCHER_P(SameRange, A, "") { + return A.begin() == arg.begin() && A.end() == arg.end(); +} +// Matchers for syntax::Token. +MATCHER_P(Kind, K, "") { return arg.kind() == K; } +MATCHER_P2(HasText, Text, SourceMgr, "") { + return arg.text(*SourceMgr) == Text; +} +/// Checks the start and end location of a token are equal to SourceRng. +MATCHER_P(RangeIs, SourceRng, "") { + return arg.location() == SourceRng.first && + arg.endLocation() == SourceRng.second; +} + +class TokenCollectorTest : public ::testing::Test { +public: + /// Run the clang frontend, collect the preprocessed tokens from the frontend + /// invocation and store them in this->Buffer. + /// This also clears SourceManager before running the compiler. + void recordTokens(llvm::StringRef Code) { + class RecordTokens : public ASTFrontendAction { + public: + explicit RecordTokens(TokenBuffer &Result) : Result(Result) {} + + bool BeginSourceFileAction(CompilerInstance &CI) override { + assert(!Collector && "expected only a single call to BeginSourceFile"); + Collector.emplace(CI.getPreprocessor()); + return true; + } + void EndSourceFileAction() override { + assert(Collector && "BeginSourceFileAction was never called"); + Result = std::move(*Collector).consume(); + } + + std::unique_ptr + CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { + return llvm::make_unique(); + } + + private: + TokenBuffer &Result; + llvm::Optional Collector; + }; + + constexpr const char *FileName = "./input.cpp"; + FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); + // Prepare to run a compiler. + if (!Diags->getClient()) + Diags->setClient(new IgnoringDiagConsumer); + std::vector Args = {"tok-test", "-std=c++03", "-fsyntax-only", + FileName}; + auto CI = createInvocationFromCommandLine(Args, Diags, FS); + assert(CI); + CI->getFrontendOpts().DisableFree = false; + CI->getPreprocessorOpts().addRemappedFile( + FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); + CompilerInstance Compiler; + Compiler.setInvocation(std::move(CI)); + Compiler.setDiagnostics(Diags.get()); + Compiler.setFileManager(FileMgr.get()); + Compiler.setSourceManager(SourceMgr.get()); + + this->Buffer = TokenBuffer(*SourceMgr); + RecordTokens Recorder(this->Buffer); + ASSERT_TRUE(Compiler.ExecuteAction(Recorder)) + << "failed to run the frontend"; + } + + /// Record the tokens and return a test dump of the resulting buffer. + std::string collectAndDump(llvm::StringRef Code) { + recordTokens(Code); + return Buffer.dumpForTests(); + } + + // Adds a file to the test VFS. + void addFile(llvm::StringRef Path, llvm::StringRef Contents) { + if (!FS->addFile(Path, time_t(), + llvm::MemoryBuffer::getMemBufferCopy(Contents))) { + ADD_FAILURE() << "could not add a file to VFS: " << Path; + } + } + + /// Add a new file, run syntax::tokenize() on it and return the results. + std::vector tokenize(llvm::StringRef Text) { + // FIXME: pass proper LangOptions. + return syntax::tokenize( + SourceMgr->createFileID(llvm::MemoryBuffer::getMemBufferCopy(Text)), + *SourceMgr, LangOptions()); + } + + // Specialized versions of matchers that hide the SourceManager from clients. + Matcher HasText(std::string Text) const { + return ::HasText(Text, SourceMgr.get()); + } + Matcher RangeIs(llvm::Annotations::Range R) const { + std::pair Ls; + Ls.first = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()) + .getLocWithOffset(R.Begin); + Ls.second = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()) + .getLocWithOffset(R.End); + return ::RangeIs(Ls); + } + + /// Finds a subrange in O(n * m). + template + llvm::ArrayRef findSubrange(llvm::ArrayRef Subrange, + llvm::ArrayRef Range, Eq F) { + for (auto Begin = Range.begin(); Begin < Range.end(); ++Begin) { + auto It = Begin; + for (auto ItSub = Subrange.begin(); + ItSub != Subrange.end() && It != Range.end(); ++ItSub, ++It) { + if (!F(*ItSub, *It)) + goto continue_outer; + } + return llvm::makeArrayRef(Begin, It); + continue_outer:; + } + return llvm::makeArrayRef(Range.end(), Range.end()); + } + + /// Finds a subrange in \p Tokens that match the tokens specified in \p Query. + /// The match should be unique. \p Query is a whitespace-separated list of + /// tokens to search for. + llvm::ArrayRef + findTokenRange(llvm::StringRef Query, llvm::ArrayRef Tokens) { + llvm::SmallVector QueryTokens; + Query.split(QueryTokens, ' ', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + if (QueryTokens.empty()) { + ADD_FAILURE() << "will not look for an empty list of tokens"; + std::abort(); + } + // An equality test for search. + auto TextMatches = [this](llvm::StringRef Q, const syntax::Token &T) { + return Q == T.text(*SourceMgr); + }; + // Find a match. + auto Found = + findSubrange(llvm::makeArrayRef(QueryTokens), Tokens, TextMatches); + if (Found.begin() == Tokens.end()) { + ADD_FAILURE() << "could not find the subrange for " << Query; + std::abort(); + } + // Check that the match is unique. + if (findSubrange(llvm::makeArrayRef(QueryTokens), + llvm::makeArrayRef(Found.end(), Tokens.end()), TextMatches) + .begin() != Tokens.end()) { + ADD_FAILURE() << "match is not unique for " << Query; + std::abort(); + } + return Found; + }; + + // Specialized versions of findTokenRange for expanded and spelled tokens. + llvm::ArrayRef findExpanded(llvm::StringRef Query) { + return findTokenRange(Query, Buffer.expandedTokens()); + } + llvm::ArrayRef findSpelled(llvm::StringRef Query, + FileID File = FileID()) { + if (!File.isValid()) + File = SourceMgr->getMainFileID(); + return findTokenRange(Query, Buffer.spelledTokens(File)); + } + + // Data fields. + llvm::IntrusiveRefCntPtr Diags = + new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions); + IntrusiveRefCntPtr FS = + new llvm::vfs::InMemoryFileSystem; + llvm::IntrusiveRefCntPtr FileMgr = + new FileManager(FileSystemOptions(), FS); + llvm::IntrusiveRefCntPtr SourceMgr = + new SourceManager(*Diags, *FileMgr); + /// Contains last result of calling recordTokens(). + TokenBuffer Buffer = TokenBuffer(*SourceMgr); +}; + +TEST_F(TokenCollectorTest, RawMode) { + EXPECT_THAT(tokenize("int main() {}"), + ElementsAre(Kind(tok::kw_int), + AllOf(HasText("main"), Kind(tok::identifier)), + Kind(tok::l_paren), Kind(tok::r_paren), + Kind(tok::l_brace), Kind(tok::r_brace))); + // Comments are ignored for now. + EXPECT_THAT(tokenize("/* foo */int a; // more comments"), + ElementsAre(Kind(tok::kw_int), + AllOf(HasText("a"), Kind(tok::identifier)), + Kind(tok::semi))); +} + +TEST_F(TokenCollectorTest, Basic) { + std::pair TestCases[] = { + {"int main() {}", + R"(expanded tokens: + int main ( ) { } +file './input.cpp' + spelled tokens: + int main ( ) { } + no mappings. +)"}, + // All kinds of whitespace are ignored. + {"\t\n int\t\n main\t\n (\t\n )\t\n{\t\n }\t\n", + R"(expanded tokens: + int main ( ) { } +file './input.cpp' + spelled tokens: + int main ( ) { } + no mappings. +)"}, + // Annotation tokens are ignored. + {R"cpp( + #pragma GCC visibility push (public) + #pragma GCC visibility pop + )cpp", + R"(expanded tokens: + +file './input.cpp' + spelled tokens: + # pragma GCC visibility push ( public ) # pragma GCC visibility pop + mappings: + ['#'_0, ''_13) => [''_0, ''_0) +)"}}; + for (auto &Test : TestCases) + EXPECT_EQ(collectAndDump(Test.first), Test.second) + << collectAndDump(Test.first); +} + +TEST_F(TokenCollectorTest, Locations) { + // Check locations of the tokens. + llvm::Annotations Code(R"cpp( + $r1[[int]] $r2[[a]] $r3[[=]] $r4[["foo bar baz"]] $r5[[;]] + )cpp"); + recordTokens(Code.code()); + // Check expanded tokens. + EXPECT_THAT( + Buffer.expandedTokens(), + ElementsAre(AllOf(Kind(tok::kw_int), RangeIs(Code.range("r1"))), + AllOf(Kind(tok::identifier), RangeIs(Code.range("r2"))), + AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), + AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), + AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))), + Kind(tok::eof))); + // Check spelled tokens. + EXPECT_THAT( + Buffer.spelledTokens(SourceMgr->getMainFileID()), + ElementsAre(AllOf(Kind(tok::kw_int), RangeIs(Code.range("r1"))), + AllOf(Kind(tok::identifier), RangeIs(Code.range("r2"))), + AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), + AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), + AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))))); +} + +TEST_F(TokenCollectorTest, MacroDirectives) { + // Macro directives are not stored anywhere at the moment. + std::string Code = R"cpp( + #define FOO a + #include "unresolved_file.h" + #undef FOO + #ifdef X + #else + #endif + #ifndef Y + #endif + #if 1 + #elif 2 + #else + #endif + #pragma once + #pragma something lalala + + int a; + )cpp"; + std::string Expected = + "expanded tokens:\n" + " int a ;\n" + "file './input.cpp'\n" + " spelled tokens:\n" + " # define FOO a # include \"unresolved_file.h\" # undef FOO " + "# ifdef X # else # endif # ifndef Y # endif # if 1 # elif 2 # else " + "# endif # pragma once # pragma something lalala int a ;\n" + " mappings:\n" + " ['#'_0, 'int'_39) => ['int'_0, 'int'_0)\n"; + EXPECT_EQ(collectAndDump(Code), Expected); +} + +TEST_F(TokenCollectorTest, MacroReplacements) { + std::pair TestCases[] = { + // A simple object-like macro. + {R"cpp( + #define INT int const + INT a; + )cpp", + R"(expanded tokens: + int const a ; +file './input.cpp' + spelled tokens: + # define INT int const INT a ; + mappings: + ['#'_0, 'INT'_5) => ['int'_0, 'int'_0) + ['INT'_5, 'a'_6) => ['int'_0, 'a'_2) +)"}, + // A simple function-like macro. + {R"cpp( + #define INT(a) const int + INT(10+10) a; + )cpp", + R"(expanded tokens: + const int a ; +file './input.cpp' + spelled tokens: + # define INT ( a ) const int INT ( 10 + 10 ) a ; + mappings: + ['#'_0, 'INT'_8) => ['const'_0, 'const'_0) + ['INT'_8, 'a'_14) => ['const'_0, 'a'_2) +)"}, + // Recursive macro replacements. + {R"cpp( + #define ID(X) X + #define INT int const + ID(ID(INT)) a; + )cpp", + R"(expanded tokens: + int const a ; +file './input.cpp' + spelled tokens: + # define ID ( X ) X # define INT int const ID ( ID ( INT ) ) a ; + mappings: + ['#'_0, 'ID'_12) => ['int'_0, 'int'_0) + ['ID'_12, 'a'_19) => ['int'_0, 'a'_2) +)"}, + // A little more complicated recursive macro replacements. + {R"cpp( + #define ADD(X, Y) X+Y + #define MULT(X, Y) X*Y + + int a = ADD(MULT(1,2), MULT(3,ADD(4,5))); + )cpp", + "expanded tokens:\n" + " int a = 1 * 2 + 3 * 4 + 5 ;\n" + "file './input.cpp'\n" + " spelled tokens:\n" + " # define ADD ( X , Y ) X + Y # define MULT ( X , Y ) X * Y int " + "a = ADD ( MULT ( 1 , 2 ) , MULT ( 3 , ADD ( 4 , 5 ) ) ) ;\n" + " mappings:\n" + " ['#'_0, 'int'_22) => ['int'_0, 'int'_0)\n" + " ['ADD'_25, ';'_46) => ['1'_3, ';'_12)\n"}, + // Empty macro replacement. + {R"cpp( + #define EMPTY + #define EMPTY_FUNC(X) + EMPTY + EMPTY_FUNC(1+2+3) + )cpp", + R"(expanded tokens: + +file './input.cpp' + spelled tokens: + # define EMPTY # define EMPTY_FUNC ( X ) EMPTY EMPTY_FUNC ( 1 + 2 + 3 ) + mappings: + ['#'_0, ''_18) => [''_0, ''_0) +)"}, + // File ends with a macro replacement. + {R"cpp( + #define FOO 10+10; + int a = FOO + )cpp", + R"(expanded tokens: + int a = 10 + 10 ; +file './input.cpp' + spelled tokens: + # define FOO 10 + 10 ; int a = FOO + mappings: + ['#'_0, 'int'_7) => ['int'_0, 'int'_0) + ['FOO'_10, ''_11) => ['10'_3, ''_7) +)"}}; + + for (auto &Test : TestCases) + EXPECT_EQ(Test.second, collectAndDump(Test.first)) + << collectAndDump(Test.first); +} + +TEST_F(TokenCollectorTest, SpecialTokens) { + // Tokens coming from concatenations. + recordTokens(R"cpp( + #define CONCAT(a, b) a ## b + int a = CONCAT(1, 2); + )cpp"); + EXPECT_THAT(std::vector(Buffer.expandedTokens()), + Contains(HasText("12"))); + // Multi-line tokens with slashes at the end. + recordTokens("i\\\nn\\\nt"); + EXPECT_THAT(Buffer.expandedTokens(), + ElementsAre(AllOf(Kind(tok::kw_int), HasText("i\\\nn\\\nt")), + Kind(tok::eof))); + // FIXME: test tokens with digraphs and UCN identifiers. +} + +TEST_F(TokenCollectorTest, LateBoundTokens) { + // The parser eventually breaks the first '>>' into two tokens ('>' and '>'), + // but we choose to record them as a single token (for now). + llvm::Annotations Code(R"cpp( + template + struct foo { int a; }; + int bar = foo>]]().a; + int baz = 10 $op[[>>]] 2; + )cpp"); + recordTokens(Code.code()); + EXPECT_THAT(std::vector(Buffer.expandedTokens()), + AllOf(Contains(AllOf(Kind(tok::greatergreater), + RangeIs(Code.range("br")))), + Contains(AllOf(Kind(tok::greatergreater), + RangeIs(Code.range("op")))))); +} + +TEST_F(TokenCollectorTest, DelayedParsing) { + llvm::StringLiteral Code = R"cpp( + struct Foo { + int method() { + // Parser will visit method bodies and initializers multiple times, but + // TokenBuffer should only record the first walk over the tokens; + return 100; + } + int a = 10; + + struct Subclass { + void foo() { + Foo().method(); + } + }; + }; + )cpp"; + std::string ExpectedTokens = + "expanded tokens:\n" + " struct Foo { int method ( ) { return 100 ; } int a = 10 ; struct " + "Subclass { void foo ( ) { Foo ( ) . method ( ) ; } } ; } ;\n"; + EXPECT_THAT(collectAndDump(Code), StartsWith(ExpectedTokens)); +} + +TEST_F(TokenCollectorTest, MultiFile) { + addFile("./foo.h", R"cpp( + #define ADD(X, Y) X+Y + int a = 100; + #include "bar.h" + )cpp"); + addFile("./bar.h", R"cpp( + int b = ADD(1, 2); + #define MULT(X, Y) X*Y + )cpp"); + llvm::StringLiteral Code = R"cpp( + #include "foo.h" + int c = ADD(1, MULT(2,3)); + )cpp"; + + std::string Expected = R"(expanded tokens: + int a = 100 ; int b = 1 + 2 ; int c = 1 + 2 * 3 ; +file './input.cpp' + spelled tokens: + # include "foo.h" int c = ADD ( 1 , MULT ( 2 , 3 ) ) ; + mappings: + ['#'_0, 'int'_3) => ['int'_12, 'int'_12) + ['ADD'_6, ';'_17) => ['1'_15, ';'_20) +file './foo.h' + spelled tokens: + # define ADD ( X , Y ) X + Y int a = 100 ; # include "bar.h" + mappings: + ['#'_0, 'int'_11) => ['int'_0, 'int'_0) + ['#'_16, ''_19) => ['int'_5, 'int'_5) +file './bar.h' + spelled tokens: + int b = ADD ( 1 , 2 ) ; # define MULT ( X , Y ) X * Y + mappings: + ['ADD'_3, ';'_9) => ['1'_8, ';'_11) + ['#'_10, ''_21) => ['int'_12, 'int'_12) +)"; + + EXPECT_EQ(Expected, collectAndDump(Code)) + << "input: " << Code << "\nresults: " << collectAndDump(Code); +} + +class TokenBufferTest : public TokenCollectorTest {}; + +TEST_F(TokenBufferTest, SpelledByExpanded) { + recordTokens(R"cpp( + a1 a2 a3 b1 b2 + )cpp"); + + // Sanity check: expanded and spelled tokens are stored separately. + EXPECT_THAT(findExpanded("a1 a2"), Not(SameRange(findSpelled("a1 a2")))); + // Searching for subranges of expanded tokens should give the corresponding + // spelled ones. + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 b1 b2")), + ValueIs(SameRange(findSpelled("a1 a2 a3 b1 b2")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), + ValueIs(SameRange(findSpelled("a1 a2 a3")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), + ValueIs(SameRange(findSpelled("b1 b2")))); + + // Test search on simple macro expansions. + recordTokens(R"cpp( + #define A a1 a2 a3 + #define B b1 b2 + + A split B + )cpp"); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1 b2")), + ValueIs(SameRange(findSpelled("A split B")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), + ValueIs(SameRange(findSpelled("A split").drop_back()))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), + ValueIs(SameRange(findSpelled("split B").drop_front()))); + // Ranges not fully covering macro invocations should fail. + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2")), llvm::None); + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("b2")), llvm::None); + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2 a3 split b1 b2")), + llvm::None); + + // Recursive macro invocations. + recordTokens(R"cpp( + #define ID(x) x + #define B b1 b2 + + ID(ID(ID(a1) a2 a3)) split ID(B) + )cpp"); + + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), + ValueIs(SameRange(findSpelled("ID ( ID ( ID ( a1 ) a2 a3 ) )")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), + ValueIs(SameRange(findSpelled("ID ( B )")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1 b2")), + ValueIs(SameRange(findSpelled( + "ID ( ID ( ID ( a1 ) a2 a3 ) ) split ID ( B )")))); + // Ranges crossing macro call boundaries. + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1")), + llvm::None); + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2 a3 split b1")), + llvm::None); + // FIXME: next two examples should map to macro arguments, but currently they + // fail. + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2")), llvm::None); + EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2")), llvm::None); + + // Empty macro expansions. + recordTokens(R"cpp( + #define EMPTY + #define ID(X) X + + EMPTY EMPTY ID(1 2 3) EMPTY EMPTY split1 + EMPTY EMPTY ID(4 5 6) split2 + ID(7 8 9) EMPTY EMPTY + )cpp"); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("1 2 3")), + ValueIs(SameRange(findSpelled("ID ( 1 2 3 )")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("4 5 6")), + ValueIs(SameRange(findSpelled("ID ( 4 5 6 )")))); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("7 8 9")), + ValueIs(SameRange(findSpelled("ID ( 7 8 9 )")))); + + // Empty mappings coming from various directives. + recordTokens(R"cpp( + #define ID(X) X + ID(1) + #pragma lalala + not_mapped + )cpp"); + EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("not_mapped")), + ValueIs(SameRange(findSpelled("not_mapped")))); +} + +TEST_F(TokenBufferTest, TokensToFileRange) { + addFile("./foo.h", "token_from_header"); + llvm::Annotations Code(R"cpp( + #define FOO token_from_expansion + #include "./foo.h" + $all[[$i[[int]] a = FOO;]] + )cpp"); + recordTokens(Code.code()); + + auto &SM = *SourceMgr; + + // Two simple examples. + auto Int = findExpanded("int").front(); + auto Semi = findExpanded(";").front(); + EXPECT_EQ(Int.range(SM), FileRange(SM.getMainFileID(), Code.range("i").Begin, + Code.range("i").End)); + EXPECT_EQ(syntax::Token::range(SM, Int, Semi), + FileRange(SM.getMainFileID(), Code.range("all").Begin, + Code.range("all").End)); + // We don't test assertion failures because death tests are slow. +} + +} // namespace \ No newline at end of file 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 @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/Refactoring/Transformer.h" - #include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Refactoring/RangeSelector.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" @@ -147,7 +147,7 @@ on(expr(hasType(isOrPointsTo(StringType))) .bind(StringExpr)), callee(cxxMethodDecl(hasName("c_str")))))), - change("REPLACED")); + change(text("REPLACED"))); R.Cases[0].Explanation = text("Use size() method directly on string."); return R; } @@ -183,7 +183,7 @@ hasName("proto::ProtoCommandLineFlag")))) .bind(Flag)), unless(callee(cxxMethodDecl(hasName("GetProto"))))), - change(Flag, "EXPR")); + change(node(Flag), text("EXPR"))); std::string Input = R"cc( proto::ProtoCommandLineFlag flag; @@ -201,9 +201,8 @@ TEST_F(TransformerTest, NodePartNameNamedDecl) { StringRef Fun = "fun"; - RewriteRule Rule = - makeRule(functionDecl(hasName("bad")).bind(Fun), - change(Fun, NodePart::Name, "good")); + RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun), + change(name(Fun), text("good"))); std::string Input = R"cc( int bad(int x); @@ -235,7 +234,7 @@ StringRef Ref = "ref"; testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref), - change(Ref, NodePart::Name, "good")), + change(name(Ref), text("good"))), Input, Expected); } @@ -253,7 +252,7 @@ StringRef Ref = "ref"; Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref), - change(Ref, NodePart::Name, "good")), + change(name(Ref), text("good"))), consumer()); T.registerMatchers(&MatchFinder); EXPECT_FALSE(rewrite(Input)); @@ -262,7 +261,7 @@ TEST_F(TransformerTest, NodePartMember) { StringRef E = "expr"; RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E), - change(E, NodePart::Member, "good")); + change(member(E), text("good"))); std::string Input = R"cc( struct S { @@ -315,8 +314,7 @@ )cc"; StringRef E = "expr"; - testRule(makeRule(memberExpr().bind(E), - change(E, NodePart::Member, "good")), + testRule(makeRule(memberExpr().bind(E), change(member(E), text("good"))), Input, Expected); } @@ -348,7 +346,7 @@ StringRef MemExpr = "member"; testRule(makeRule(memberExpr().bind(MemExpr), - change(MemExpr, NodePart::Member, "good")), + change(member(MemExpr), text("good"))), Input, Expected); } @@ -371,8 +369,9 @@ StringRef C = "C", T = "T", E = "E"; testRule(makeRule(ifStmt(hasCondition(expr().bind(C)), hasThen(stmt().bind(T)), hasElse(stmt().bind(E))), - {change(C, "true"), change(T, "{ /* then */ }"), - change(E, "{ /* else */ }")}), + {change(node(C), text("true")), + change(statement(T), text("{ /* then */ }")), + change(statement(E), text("{ /* else */ }"))}), Input, Expected); } @@ -383,7 +382,7 @@ hasName("proto::ProtoCommandLineFlag")))) .bind(Flag)), unless(callee(cxxMethodDecl(hasName("GetProto"))))), - change(Flag, "PROTO")); + change(node(Flag), text("PROTO"))); std::string Input = R"cc( proto::ProtoCommandLineFlag flag; @@ -410,7 +409,7 @@ hasArgument(0, cxxMemberCallExpr( on(expr().bind(S)), callee(cxxMethodDecl(hasName("c_str")))))), - change("DISTINCT")); + change(text("DISTINCT"))); } TEST_F(TransformerTest, OrderedRuleRelated) { @@ -475,7 +474,7 @@ -> llvm::Expected { return llvm::createStringError(llvm::errc::invalid_argument, "ERROR"); }; - Transformer T(makeRule(binaryOperator().bind(O), change(O, AlwaysFail)), + Transformer T(makeRule(binaryOperator().bind(O), change(node(O), AlwaysFail)), consumer()); T.registerMatchers(&MatchFinder); EXPECT_FALSE(rewrite(Input)); @@ -488,10 +487,10 @@ std::string Input = "int conflictOneRule() { return 3 + 7; }"; // Try to change the whole binary-operator expression AND one its operands: StringRef O = "O", L = "L"; - Transformer T( - makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O), - {change(O, "DELETE_OP"), change(L, "DELETE_LHS")}), - consumer()); + Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O), + {change(node(O), text("DELETE_OP")), + change(node(L), text("DELETE_LHS"))}), + consumer()); T.registerMatchers(&MatchFinder); EXPECT_FALSE(rewrite(Input)); EXPECT_THAT(Changes, IsEmpty()); @@ -503,7 +502,7 @@ std::string Input = "int conflictOneRule() { return -7; }"; // Try to change the whole binary-operator expression AND one its operands: StringRef E = "E"; - Transformer T(makeRule(expr().bind(E), change(E, "DELETE_EXPR")), + Transformer T(makeRule(expr().bind(E), change(node(E), text("DELETE_EXPR"))), consumer()); T.registerMatchers(&MatchFinder); // The rewrite process fails because the changes conflict with each other... @@ -517,7 +516,7 @@ // Syntax error in the function body: std::string Input = "void errorOccurred() { 3 }"; Transformer T(makeRule(functionDecl(hasName("errorOccurred")), - change("DELETED;")), + change(text("DELETED;"))), consumer()); T.registerMatchers(&MatchFinder); // The rewrite process itself fails... diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp --- a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -13,6 +13,7 @@ #include "FuzzerInternal.h" #include "FuzzerUtil.h" +#include #include #include #include @@ -232,57 +233,72 @@ ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), "_zx_object_signal"); - zx_port_packet_t Packet; - ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE, &Packet), - "_zx_port_wait"); - - // At this point, we want to get the state of the crashing thread, but - // libFuzzer and the sanitizers assume this will happen from that same thread - // via a POSIX signal handler. "Resurrecting" the thread in the middle of the - // appropriate callback is as simple as forcibly setting the instruction - // pointer/program counter, provided we NEVER EVER return from that function - // (since otherwise our stack will not be valid). - ScopedHandle Thread; - ExitOnErr(_zx_object_get_child(Self, Packet.exception.tid, - ZX_RIGHT_SAME_RIGHTS, &Thread.Handle), - "_zx_object_get_child"); - - zx_thread_state_general_regs_t GeneralRegisters; - ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, - &GeneralRegisters, sizeof(GeneralRegisters)), - "_zx_thread_read_state"); - - // To unwind properly, we need to push the crashing thread's register state - // onto the stack and jump into a trampoline with CFI instructions on how - // to restore it. + // This thread lives as long as the process in order to keep handling + // crashes. In practice, the first crashed thread to reach the end of the + // StaticCrashHandler will end the process. + while (true) { + zx_port_packet_t Packet; + ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE, &Packet), + "_zx_port_wait"); + + // Ignore informational synthetic exceptions. + assert(ZX_PKT_IS_EXCEPTION(Packet.type)); + if (ZX_EXCP_THREAD_STARTING == Packet.type || + ZX_EXCP_THREAD_EXITING == Packet.type || + ZX_EXCP_PROCESS_STARTING == Packet.type) { + continue; + } + + // At this point, we want to get the state of the crashing thread, but + // libFuzzer and the sanitizers assume this will happen from that same + // thread via a POSIX signal handler. "Resurrecting" the thread in the + // middle of the appropriate callback is as simple as forcibly setting the + // instruction pointer/program counter, provided we NEVER EVER return from + // that function (since otherwise our stack will not be valid). + ScopedHandle Thread; + ExitOnErr(_zx_object_get_child(Self, Packet.exception.tid, + ZX_RIGHT_SAME_RIGHTS, &Thread.Handle), + "_zx_object_get_child"); + + zx_thread_state_general_regs_t GeneralRegisters; + ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, + sizeof(GeneralRegisters)), + "_zx_thread_read_state"); + + // To unwind properly, we need to push the crashing thread's register state + // onto the stack and jump into a trampoline with CFI instructions on how + // to restore it. #if defined(__x86_64__) - uintptr_t StackPtr = - (GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) & - -(uintptr_t)16; - __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - GeneralRegisters.rsp = StackPtr; - GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); + uintptr_t StackPtr = + (GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) & + -(uintptr_t)16; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.rsp = StackPtr; + GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); #elif defined(__aarch64__) - uintptr_t StackPtr = - (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16; - __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - GeneralRegisters.sp = StackPtr; - GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); + uintptr_t StackPtr = + (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.sp = StackPtr; + GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); #else #error "Unsupported architecture for fuzzing on Fuchsia" #endif - // Now force the crashing thread's state. - ExitOnErr(_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, - &GeneralRegisters, sizeof(GeneralRegisters)), - "_zx_thread_write_state"); + // Now force the crashing thread's state. + ExitOnErr( + _zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_write_state"); - ExitOnErr(_zx_task_resume_from_exception(Thread.Handle, Port.Handle, 0), - "_zx_task_resume_from_exception"); + ExitOnErr(_zx_task_resume_from_exception(Thread.Handle, Port.Handle, 0), + "_zx_task_resume_from_exception"); + } } } // namespace diff --git a/lld/test/ELF/mips-32.s b/lld/test/ELF/mips-32.s --- a/lld/test/ELF/mips-32.s +++ b/lld/test/ELF/mips-32.s @@ -3,15 +3,13 @@ # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t-be.o # RUN: ld.lld -shared %t-be.o -o %t-be.so -# RUN: llvm-objdump -t -s %t-be.so \ -# RUN: | FileCheck -check-prefix=SYM -check-prefix=BE %s +# RUN: llvm-objdump -t -s %t-be.so | FileCheck -check-prefixes=SYM,BE %s # RUN: llvm-readobj -r --dynamic-table --mips-plt-got %t-be.so \ # RUN: | FileCheck -check-prefix=REL %s # RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux %s -o %t-el.o # RUN: ld.lld -shared %t-el.o -o %t-el.so -# RUN: llvm-objdump -t -s %t-el.so \ -# RUN: | FileCheck -check-prefix=SYM -check-prefix=EL %s +# RUN: llvm-objdump -t -s %t-el.so | FileCheck -check-prefixes=SYM,EL %s # RUN: llvm-readobj -r --dynamic-table --mips-plt-got %t-el.so \ # RUN: | FileCheck -check-prefix=REL %s diff --git a/lld/test/ELF/mips-64-disp.s b/lld/test/ELF/mips-64-disp.s --- a/lld/test/ELF/mips-64-disp.s +++ b/lld/test/ELF/mips-64-disp.s @@ -7,7 +7,7 @@ # RUN: ld.lld %t.so.o -shared -o %t.so # RUN: ld.lld %t.exe.o %t.so -o %t.exe # RUN: llvm-objdump -d -t %t.exe | FileCheck %s -# RUN: llvm-readobj -r --mips-plt-got %t.exe | FileCheck -check-prefix=GOT %s +# RUN: llvm-readelf -r --mips-plt-got %t.exe | FileCheck -check-prefix=GOT %s # CHECK: __start: # CHECK-NEXT: 20000: 24 42 80 40 addiu $2, $2, -32704 @@ -20,59 +20,24 @@ # CHECK: 0000000000020000 .text 00000000 __start # CHECK: 0000000000000000 g F *UND* 00000000 foo1a -# GOT: Relocations [ -# GOT-NEXT: ] -# GOT-NEXT: Primary GOT { -# GOT-NEXT: Canonical gp value: -# GOT-NEXT: Reserved entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32752 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Purpose: Lazy resolver -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32744 -# GOT-NEXT: Initial: 0x8000000000000000 -# GOT-NEXT: Purpose: Module pointer (GNU extension) -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Local entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32736 -# GOT-NEXT: Initial: 0x20014 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32728 -# GOT-NEXT: Initial: 0x20004 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32720 -# GOT-NEXT: Initial: 0x20008 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32712 -# GOT-NEXT: Initial: 0x2000C -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Global entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32704 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Value: 0x0 -# GOT-NEXT: Type: Function -# GOT-NEXT: Section: Undefined -# GOT-NEXT: Name: foo1a -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Number of TLS and multi-GOT entries: 0 -# GOT-NEXT: } +# GOT: Primary GOT: +# GOT-NEXT: Canonical gp value: 0000000000038000 +# GOT-EMPTY: +# GOT-NEXT: Reserved entries: +# GOT-NEXT: Address Access Initial Purpose +# GOT-NEXT: 0000000000030010 -32752(gp) 0000000000000000 Lazy resolver +# GOT-NEXT: 0000000000030018 -32744(gp) 8000000000000000 Module pointer (GNU extension) +# GOT-EMPTY: +# GOT-NEXT: Local entries: +# GOT-NEXT: Address Access Initial +# GOT-NEXT: 0000000000030020 -32736(gp) 0000000000020014 +# GOT-NEXT: 0000000000030028 -32728(gp) 0000000000020004 +# GOT-NEXT: 0000000000030030 -32720(gp) 0000000000020008 +# GOT-NEXT: 0000000000030038 -32712(gp) 000000000002000c +# GOT-EMPTY: +# GOT-NEXT: Global entries: +# GOT-NEXT: Address Access Initial Sym.Val. Type Ndx Name +# GOT-NEXT: 0000000000030040 -32704(gp) 0000000000000000 0000000000000000 FUNC UND foo1a .text .global __start diff --git a/lld/test/ELF/mips-64-got-overflow.s b/lld/test/ELF/mips-64-got-overflow.s --- a/lld/test/ELF/mips-64-got-overflow.s +++ b/lld/test/ELF/mips-64-got-overflow.s @@ -6,69 +6,35 @@ # RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t2.so.o # RUN: ld.lld -shared -mips-got-size 32 %t1.so.o %t2.so.o -o %t-sgot.so # RUN: ld.lld -shared -mips-got-size 24 %t1.so.o %t2.so.o -o %t-mgot.so -# RUN: llvm-readobj -r --dyn-syms --mips-plt-got %t-sgot.so | FileCheck -check-prefix=SGOT %s -# RUN: llvm-readobj -r --dyn-syms --mips-plt-got %t-mgot.so | FileCheck -check-prefix=MGOT %s +# RUN: llvm-readelf --mips-plt-got %t-sgot.so | FileCheck -check-prefix=SGOT %s +# RUN: llvm-readelf --mips-plt-got %t-mgot.so | FileCheck -check-prefix=MGOT %s -# SGOT: Primary GOT { -# SGOT-NEXT: Canonical gp value: 0x27FF0 -# SGOT-NEXT: Reserved entries [ -# SGOT-NEXT: Entry { -# SGOT-NEXT: Address: -# SGOT-NEXT: Access: -32752 -# SGOT-NEXT: Initial: 0x0 -# SGOT-NEXT: Purpose: Lazy resolver -# SGOT-NEXT: } -# SGOT-NEXT: Entry { -# SGOT-NEXT: Address: -# SGOT-NEXT: Access: -32744 -# SGOT-NEXT: Initial: 0x80000000 -# SGOT-NEXT: Purpose: Module pointer (GNU extension) -# SGOT-NEXT: } -# SGOT-NEXT: ] -# SGOT-NEXT: Local entries [ -# SGOT-NEXT: Entry { -# SGOT-NEXT: Address: -# SGOT-NEXT: Access: -32736 -# SGOT-NEXT: Initial: 0x20020 -# SGOT-NEXT: } -# SGOT-NEXT: Entry { -# SGOT-NEXT: Address: -# SGOT-NEXT: Access: -32728 -# SGOT-NEXT: Initial: 0x20030 -# SGOT-NEXT: } -# SGOT-NEXT: ] -# SGOT-NEXT: Global entries [ -# SGOT-NEXT: ] -# SGOT-NEXT: Number of TLS and multi-GOT entries: 0 -# SGOT-NEXT: } +# SGOT: Primary GOT: +# SGOT-NEXT: Canonical gp value: 0000000000027ff0 +# SGOT-EMPTY: +# SGOT-NEXT: Reserved entries: +# SGOT-NEXT: Address Access Initial Purpose +# SGOT-NEXT: 0000000000020000 -32752(gp) 0000000000000000 Lazy resolver +# SGOT-NEXT: 0000000000020008 -32744(gp) 8000000000000000 Module pointer (GNU extension) +# SGOT-EMPTY: +# SGOT-NEXT: Local entries: +# SGOT-NEXT: Address Access Initial +# SGOT-NEXT: 0000000000020010 -32736(gp) 0000000000020020 +# SGOT-NEXT: 0000000000020018 -32728(gp) 0000000000020030 -# MGOT: Primary GOT { -# MGOT-NEXT: Canonical gp value: 0x27FF0 -# MGOT-NEXT: Reserved entries [ -# MGOT-NEXT: Entry { -# MGOT-NEXT: Address: -# MGOT-NEXT: Access: -32752 -# MGOT-NEXT: Initial: 0x0 -# MGOT-NEXT: Purpose: Lazy resolver -# MGOT-NEXT: } -# MGOT-NEXT: Entry { -# MGOT-NEXT: Address: -# MGOT-NEXT: Access: -32744 -# MGOT-NEXT: Initial: 0x80000000 -# MGOT-NEXT: Purpose: Module pointer (GNU extension) -# MGOT-NEXT: } -# MGOT-NEXT: ] -# MGOT-NEXT: Local entries [ -# MGOT-NEXT: Entry { -# MGOT-NEXT: Address: -# MGOT-NEXT: Access: -32736 -# MGOT-NEXT: Initial: 0x20020 -# MGOT-NEXT: } -# MGOT-NEXT: ] -# MGOT-NEXT: Global entries [ -# MGOT-NEXT: ] -# MGOT-NEXT: Number of TLS and multi-GOT entries: 1 -# MGOT-NEXT: } +# MGOT: Primary GOT: +# MGOT-NEXT: Canonical gp value: 0000000000027ff0 +# MGOT-EMPTY: +# MGOT-NEXT: Reserved entries: +# MGOT-NEXT: Address Access Initial Purpose +# MGOT-NEXT: 0000000000020000 -32752(gp) 0000000000000000 Lazy resolver +# MGOT-NEXT: 0000000000020008 -32744(gp) 8000000000000000 Module pointer (GNU extension) +# MGOT-EMPTY: +# MGOT-NEXT: Local entries: +# MGOT-NEXT: Address Access Initial +# MGOT-NEXT: 0000000000020010 -32736(gp) 0000000000020020 +# MGOT-EMPTY: +# MGOT-NEXT: Number of TLS and multi-GOT entries 1 .text .global foo2 diff --git a/lld/test/ELF/mips-64-got.s b/lld/test/ELF/mips-64-got.s --- a/lld/test/ELF/mips-64-got.s +++ b/lld/test/ELF/mips-64-got.s @@ -7,7 +7,7 @@ # RUN: ld.lld %t.so.o -shared -o %t.so # RUN: ld.lld %t.exe.o %t.so -o %t.exe # RUN: llvm-objdump -d -t %t.exe | FileCheck %s -# RUN: llvm-readobj -r --mips-plt-got %t.exe | FileCheck -check-prefix=GOT %s +# RUN: llvm-readelf -r --mips-plt-got %t.exe | FileCheck -check-prefix=GOT %s # CHECK: __start: @@ -21,59 +21,25 @@ # CHECK: 0000000000020000 .text 00000000 __start # CHECK: 0000000000020014 .text 00000000 bar -# GOT: Relocations [ -# GOT-NEXT: ] -# GOT-NEXT: Primary GOT { -# GOT-NEXT: Canonical gp value: -# GOT-NEXT: Reserved entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32752 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Purpose: Lazy resolver -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32744 -# GOT-NEXT: Initial: 0x8000000000000000 -# GOT-NEXT: Purpose: Module pointer (GNU extension) -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Local entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32736 -# GOT-NEXT: Initial: 0x20000 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32728 -# GOT-NEXT: Initial: 0x30000 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32720 -# GOT-NEXT: Initial: 0x20014 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32712 -# GOT-NEXT: Initial: 0x20018 -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Global entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32704 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Value: 0x0 -# GOT-NEXT: Type: Function -# GOT-NEXT: Section: Undefined -# GOT-NEXT: Name: foo1a -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Number of TLS and multi-GOT entries: 0 -# GOT-NEXT: } +# GOT: There are no relocations in this file. +# GOT-NEXT: Primary GOT: +# GOT-NEXT: Canonical gp value: 0000000000038000 +# GOT-EMPTY: +# GOT-NEXT: Reserved entries: +# GOT-NEXT: Address Access Initial Purpose +# GOT-NEXT: 0000000000030010 -32752(gp) 0000000000000000 Lazy resolver +# GOT-NEXT: 0000000000030018 -32744(gp) 8000000000000000 Module pointer (GNU extension) +# GOT-EMPTY: +# GOT-NEXT: Local entries: +# GOT-NEXT: Address Access Initial +# GOT-NEXT: 0000000000030020 -32736(gp) 0000000000020000 +# GOT-NEXT: 0000000000030028 -32728(gp) 0000000000030000 +# GOT-NEXT: 0000000000030030 -32720(gp) 0000000000020014 +# GOT-NEXT: 0000000000030038 -32712(gp) 0000000000020018 +# GOT-EMPTY: +# GOT-NEXT: Global entries: +# GOT-NEXT: Address Access Initial Sym.Val. Type Ndx Name +# GOT-NEXT: 0000000000030040 -32704(gp) 0000000000000000 0000000000000000 FUNC UND foo1a .text .global __start, bar diff --git a/lld/test/ELF/mips-call-hilo.s b/lld/test/ELF/mips-call-hilo.s --- a/lld/test/ELF/mips-call-hilo.s +++ b/lld/test/ELF/mips-call-hilo.s @@ -4,7 +4,7 @@ # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -shared -o %t.so # RUN: llvm-objdump -d %t.so | FileCheck %s -# RUN: llvm-readobj -r --mips-plt-got %t.so | FileCheck -check-prefix=GOT %s +# RUN: llvm-readelf -r --mips-plt-got %t.so | FileCheck -check-prefix=GOT %s # CHECK: Disassembly of section .text: # CHECK-EMPTY: @@ -16,36 +16,23 @@ # CHECK-NEXT: 10010: 3c 02 00 00 lui $2, 0 # CHECK-NEXT: 10014: 8c 42 80 1c lw $2, -32740($2) -# GOT: Relocations [ -# GOT-NEXT: ] - -# GOT: Primary GOT { -# GOT-NEXT: Canonical gp value: 0x27FF0 -# GOT: Local entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: 0x20008 -# GOT-NEXT: Access: -32744 -# GOT-NEXT: Initial: 0x10018 -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: 0x2000C -# GOT-NEXT: Access: -32740 -# GOT-NEXT: Initial: 0x1001C -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Global entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: 0x20010 -# GOT-NEXT: Access: -32736 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Value: 0x0 -# GOT-NEXT: Type: None -# GOT-NEXT: Section: Undefined -# GOT-NEXT: Name: bar -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Number of TLS and multi-GOT entries: 0 -# GOT-NEXT: } +# GOT: There are no relocations in this file. +# GOT: Primary GOT: +# GOT-NEXT: Canonical gp value: 00027ff0 +# GOT-EMPTY: +# GOT-NEXT: Reserved entries: +# GOT-NEXT: Address Access Initial Purpose +# GOT-NEXT: 00020000 -32752(gp) 00000000 Lazy resolver +# GOT-NEXT: 00020004 -32748(gp) 80000000 Module pointer (GNU extension) +# GOT-EMPTY: +# GOT-NEXT: Local entries: +# GOT-NEXT: Address Access Initial +# GOT-NEXT: 00020008 -32744(gp) 00010018 +# GOT-NEXT: 0002000c -32740(gp) 0001001c +# GOT-EMPTY: +# GOT-NEXT: Global entries: +# GOT-NEXT: Address Access Initial Sym.Val. Type Ndx Name +# GOT-NEXT: 00020010 -32736(gp) 00000000 00000000 NOTYPE UND bar .text .global foo diff --git a/lld/test/ELF/mips-got16.s b/lld/test/ELF/mips-got16.s --- a/lld/test/ELF/mips-got16.s +++ b/lld/test/ELF/mips-got16.s @@ -4,7 +4,7 @@ # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -shared -o %t.so # RUN: llvm-objdump -d -t %t.so | FileCheck %s -# RUN: llvm-readobj -r --mips-plt-got %t.so | FileCheck -check-prefix=GOT %s +# RUN: llvm-readelf -r --mips-plt-got %t.so | FileCheck -check-prefix=GOT %s # CHECK: Disassembly of section .text: # CHECK-EMPTY: @@ -25,83 +25,29 @@ # CHECK: 00041008 .data 00000000 .hidden bar # CHECK: 00000000 *UND* 00000000 foo -# GOT: Relocations [ -# GOT-NEXT: ] +# GOT: There are no relocations in this file. -# GOT: Primary GOT { -# GOT-NEXT: Canonical gp value: -# GOT-NEXT: Reserved entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32752 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Purpose: Lazy resolver -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32748 -# GOT-NEXT: Initial: 0x80000000 -# GOT-NEXT: Purpose: Module pointer (GNU extension) -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Local entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32744 -# GOT-NEXT: Initial: 0x10000 -# ^-- (0x1002c + 0x8000) & ~0xffff -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32740 -# GOT-NEXT: Initial: 0x20000 -# ^-- redundant unused entry -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32736 -# GOT-NEXT: Initial: 0x20000 -# ^-- redundant unused entry -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32732 -# GOT-NEXT: Initial: 0x30000 -# ^-- (0x29000 + 0x8000) & ~0xffff -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32728 -# GOT-NEXT: Initial: 0x40000 -# ^-- (0x29000 + 0x10004 + 0x8000) & ~0xffff -# ^-- (0x29000 + 0x18004 + 0x8000) & ~0xffff -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32724 -# GOT-NEXT: Initial: 0x50000 -# ^-- redundant unused entry -# GOT-NEXT: } -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32720 -# GOT-NEXT: Initial: 0x41008 -# ^-- 'bar' address -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Global entries [ -# GOT-NEXT: Entry { -# GOT-NEXT: Address: -# GOT-NEXT: Access: -32716 -# GOT-NEXT: Initial: 0x0 -# GOT-NEXT: Value: 0x0 -# GOT-NEXT: Type: None -# GOT-NEXT: Section: Undefined -# GOT-NEXT: Name: foo -# GOT-NEXT: } -# GOT-NEXT: ] -# GOT-NEXT: Number of TLS and multi-GOT entries: 0 -# GOT-NEXT: } +# GOT: Local entries: +# GOT-NEXT: Address Access Initial +# GOT-NEXT: 00041018 -32744(gp) 00010000 +# ^-- (0x1002c + 0x8000) & ~0xffff +# GOT-NEXT: 0004101c -32740(gp) 00020000 +# ^-- redundant unused entry +# GOT-NEXT: 00041020 -32736(gp) 00020000 +# ^-- redundant unused entry +# GOT-NEXT: 00041024 -32732(gp) 00030000 +# ^-- (0x29000 + 0x8000) & ~0xffff +# GOT-NEXT: 00041028 -32728(gp) 00040000 +# ^-- (0x29000 + 0x10004 + 0x8000) & ~0xffff +# ^-- (0x29000 + 0x18004 + 0x8000) & ~0xffff +# GOT-NEXT: 0004102c -32724(gp) 00050000 +# ^-- redundant unused entry +# GOT-NEXT: 00041030 -32720(gp) 00041008 +# ^-- 'bar' address +# GOT-EMPTY: +# GOT-NEXT: Global entries: +# GOT-NEXT: Address Access Initial Sym.Val. Type Ndx Name +# GOT-NEXT: 00041034 -32716(gp) 00000000 00000000 NOTYPE UND foo .text .globl __start diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -921,7 +921,7 @@ set(LLVM_ENABLE_PLUGINS OFF) endif() else() - set(LLVM_ENABLE_PLUGINS ON) + set(LLVM_ENABLE_PLUGINS ${LLVM_ENABLE_PIC}) endif() # By default we should enable LLVM_ENABLE_IDE only for multi-configuration diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -9931,7 +9931,7 @@ :: - = select selty , , ; yields ty + = select [fast-math flags] selty , , ; yields ty selty is either i1 or {} @@ -9948,6 +9948,11 @@ values indicating the condition, and two values of the same :ref:`first class ` type. +#. The optional ``fast-math flags`` marker indicates that the select has one or more + :ref:`fast-math flags `. These are optimization hints to enable + otherwise unsafe floating-point optimizations. Fast-math flags are only valid + for selects that return a floating-point scalar or vector type. + Semantics: """""""""" diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -1260,8 +1260,9 @@ /// Measure the specified inline asm to determine an approximation of its /// length. - virtual unsigned getInlineAsmLength(const char *Str, - const MCAsmInfo &MAI) const; + virtual unsigned getInlineAsmLength( + const char *Str, const MCAsmInfo &MAI, + const TargetSubtargetInfo *STI = nullptr) const; /// Allocate and return a hazard recognizer to use for this target when /// scheduling the machine instructions before register allocation. diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -2067,6 +2067,8 @@ MDNode *Unpred = MDFrom->getMetadata(LLVMContext::MD_unpredictable); Sel = addBranchMetadata(Sel, Prof, Unpred); } + if (isa(Sel)) + Sel = cast(setFPAttrs(Sel, nullptr /* MDNode* */, FMF)); return Insert(Sel, Name); } diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -27,6 +27,7 @@ class MCExpr; class MCSection; class MCStreamer; +class MCSubtargetInfo; class MCSymbol; namespace WinEH { @@ -473,7 +474,13 @@ bool hasMachoTBSSDirective() const { return HasMachoTBSSDirective; } bool hasCOFFAssociativeComdats() const { return HasCOFFAssociativeComdats; } bool hasCOFFComdatConstants() const { return HasCOFFComdatConstants; } - unsigned getMaxInstLength() const { return MaxInstLength; } + + /// Returns the maximum possible encoded instruction size in bytes. If \p STI + /// is null, this should be the maximum size for any subtarget. + virtual unsigned getMaxInstLength(const MCSubtargetInfo *STI = nullptr) const { + return MaxInstLength; + } + unsigned getMinInstAlignment() const { return MinInstAlignment; } bool getDollarIsPC() const { return DollarIsPC; } const char *getSeparatorString() const { return SeparatorString; } diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -5701,7 +5701,19 @@ case lltok::kw_inttoptr: case lltok::kw_ptrtoint: return ParseCast(Inst, PFS, KeywordVal); // Other. - case lltok::kw_select: return ParseSelect(Inst, PFS); + case lltok::kw_select: { + FastMathFlags FMF = EatFastMathFlagsIfPresent(); + int Res = ParseSelect(Inst, PFS); + if (Res != 0) + return Res; + if (FMF.any()) { + if (!Inst->getType()->isFPOrFPVectorTy()) + return Error(Loc, "fast-math-flags specified for select without " + "floating-point scalar or vector return type"); + Inst->setFastMathFlags(FMF); + } + return 0; + } case lltok::kw_va_arg: return ParseVA_Arg(Inst, PFS); case lltok::kw_extractelement: return ParseExtractElement(Inst, PFS); case lltok::kw_insertelement: return ParseInsertElement(Inst, PFS); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -3835,6 +3835,11 @@ I = SelectInst::Create(Cond, TrueVal, FalseVal); InstructionList.push_back(I); + if (OpNum < Record.size() && isa(I)) { + FastMathFlags FMF = getDecodedFastMathFlags(Record[OpNum]); + if (FMF.any()) + I->setFastMathFlags(FMF); + } break; } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -2636,12 +2636,16 @@ Vals.append(IVI->idx_begin(), IVI->idx_end()); break; } - case Instruction::Select: + case Instruction::Select: { Code = bitc::FUNC_CODE_INST_VSELECT; pushValueAndType(I.getOperand(1), InstID, Vals); pushValue(I.getOperand(2), InstID, Vals); pushValueAndType(I.getOperand(0), InstID, Vals); + uint64_t Flags = getOptimizationFlags(&I); + if (Flags != 0) + Vals.push_back(Flags); break; + } case Instruction::ExtractElement: Code = bitc::FUNC_CODE_INST_EXTRACTELT; pushValueAndType(I.getOperand(0), InstID, Vals); 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 @@ -1739,6 +1739,7 @@ DCI.AddToWorklist(Op.getNode()); DCI.CommitTargetLoweringOpt(TLO); } + return Simplified; } @@ -3561,7 +3562,16 @@ return; } else if ((C = dyn_cast(Op)) && ConstraintLetter != 's') { - Ops.push_back(DAG.getTargetConstant(Offset + C->getSExtValue(), + // gcc prints these as sign extended. Sign extend value to 64 bits + // now; without this it would get ZExt'd later in + // ScheduleDAGSDNodes::EmitNode, which is very generic. + bool IsBool = C->getConstantIntValue()->getBitWidth() == 1; + BooleanContent BCont = getBooleanContents(MVT::i64); + ISD::NodeType ExtOpc = IsBool ? getExtendForContent(BCont) + : ISD::SIGN_EXTEND; + int64_t ExtVal = ExtOpc == ISD::ZERO_EXTEND ? C->getZExtValue() + : C->getSExtValue(); + Ops.push_back(DAG.getTargetConstant(Offset + ExtVal, SDLoc(C), MVT::i64)); return; } else { diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp --- a/llvm/lib/CodeGen/TargetInstrInfo.cpp +++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp @@ -85,11 +85,13 @@ /// simple--i.e. not a logical or arithmetic expression--size values without /// the optional fill value. This is primarily used for creating arbitrary /// sized inline asm blocks for testing purposes. -unsigned TargetInstrInfo::getInlineAsmLength(const char *Str, - const MCAsmInfo &MAI) const { +unsigned TargetInstrInfo::getInlineAsmLength( + const char *Str, + const MCAsmInfo &MAI, const TargetSubtargetInfo *STI) const { // Count the number of instructions in the asm. bool AtInsnStart = true; unsigned Length = 0; + const unsigned MaxInstLength = MAI.getMaxInstLength(STI); for (; *Str; ++Str) { if (*Str == '\n' || strncmp(Str, MAI.getSeparatorString(), strlen(MAI.getSeparatorString())) == 0) { @@ -101,7 +103,7 @@ } if (AtInsnStart && !std::isspace(static_cast(*Str))) { - unsigned AddLength = MAI.getMaxInstLength(); + unsigned AddLength = MaxInstLength; if (strncmp(Str, ".space", 6) == 0) { char *EStr; int SpaceSize; diff --git a/llvm/lib/Demangle/MicrosoftDemangle.cpp b/llvm/lib/Demangle/MicrosoftDemangle.cpp --- a/llvm/lib/Demangle/MicrosoftDemangle.cpp +++ b/llvm/lib/Demangle/MicrosoftDemangle.cpp @@ -2175,8 +2175,10 @@ SymbolNode *S = nullptr; if (MangledName.startsWith('?')) { S = parse(MangledName); - if (Error) + if (Error || !S->Name) { + Error = true; return nullptr; + } memorizeIdentifier(S->Name->getUnqualifiedIdentifier()); } diff --git a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.h b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.h --- a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.h +++ b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.h @@ -41,15 +41,14 @@ private: std::unique_ptr const MCII; const MCRegisterInfo &MRI; + const unsigned TargetMaxInstBytes; mutable ArrayRef Bytes; mutable uint32_t Literal; mutable bool HasLiteral; public: AMDGPUDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, - MCInstrInfo const *MCII) : - MCDisassembler(STI, Ctx), MCII(MCII), MRI(*Ctx.getRegisterInfo()) {} - + MCInstrInfo const *MCII); ~AMDGPUDisassembler() override = default; DecodeStatus getInstruction(MCInst &MI, uint64_t &Size, diff --git a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp --- a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp +++ b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCExpr.h" @@ -56,6 +57,17 @@ using DecodeStatus = llvm::MCDisassembler::DecodeStatus; +AMDGPUDisassembler::AMDGPUDisassembler(const MCSubtargetInfo &STI, + MCContext &Ctx, + MCInstrInfo const *MCII) : + MCDisassembler(STI, Ctx), MCII(MCII), MRI(*Ctx.getRegisterInfo()), + TargetMaxInstBytes(Ctx.getAsmInfo()->getMaxInstLength(&STI)) { + + // ToDo: AMDGPUDisassembler supports only VI ISA. + if (!STI.getFeatureBits()[AMDGPU::FeatureGCN3Encoding] && !isGFX10()) + report_fatal_error("Disassembly not yet supported for subtarget"); +} + inline static MCDisassembler::DecodeStatus addOperand(MCInst &Inst, const MCOperand& Opnd) { Inst.addOperand(Opnd); @@ -182,14 +194,7 @@ CommentStream = &CS; bool IsSDWA = false; - // ToDo: AMDGPUDisassembler supports only VI ISA. - if (!STI.getFeatureBits()[AMDGPU::FeatureGCN3Encoding] && !isGFX10()) - report_fatal_error("Disassembly not yet supported for subtarget"); - - unsigned MaxInstBytesNum = (std::min)( - STI.getFeatureBits()[AMDGPU::FeatureGFX10] ? (size_t) 20 : - STI.getFeatureBits()[AMDGPU::FeatureVOP3Literal] ? (size_t) 12 : (size_t)8, - Bytes_.size()); + unsigned MaxInstBytesNum = std::min((size_t)TargetMaxInstBytes, Bytes_.size()); Bytes = Bytes_.slice(0, MaxInstBytesNum); DecodeStatus Res = MCDisassembler::Fail; diff --git a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.h b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.h --- a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.h +++ b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.h @@ -27,6 +27,7 @@ public: explicit AMDGPUMCAsmInfo(const Triple &TT); bool shouldOmitSectionDirective(StringRef SectionName) const override; + unsigned getMaxInstLength(const MCSubtargetInfo *STI) const override; }; } // namespace llvm #endif diff --git a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.cpp b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.cpp --- a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.cpp +++ b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCAsmInfo.cpp @@ -9,6 +9,8 @@ #include "AMDGPUMCAsmInfo.h" #include "llvm/ADT/Triple.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "MCTargetDesc/AMDGPUMCTargetDesc.h" using namespace llvm; @@ -18,7 +20,10 @@ HasSingleParameterDotFile = false; //===------------------------------------------------------------------===// MinInstAlignment = 4; - MaxInstLength = (TT.getArch() == Triple::amdgcn) ? 8 : 16; + + // This is the maximum instruction encoded size for gfx10. With a known + // subtarget, it can be reduced to 8 bytes. + MaxInstLength = (TT.getArch() == Triple::amdgcn) ? 20 : 16; SeparatorString = "\n"; CommentString = ";"; PrivateLabelPrefix = ""; @@ -44,3 +49,18 @@ SectionName == ".hsarodata_readonly_agent" || MCAsmInfo::shouldOmitSectionDirective(SectionName); } + +unsigned AMDGPUMCAsmInfo::getMaxInstLength(const MCSubtargetInfo *STI) const { + if (!STI || STI->getTargetTriple().getArch() == Triple::r600) + return MaxInstLength; + + // Maximum for NSA encoded images + if (STI->getFeatureBits()[AMDGPU::FeatureNSAEncoding]) + return 20; + + // 64-bit instruction with 32-bit literal. + if (STI->getFeatureBits()[AMDGPU::FeatureVOP3Literal]) + return 12; + + return 8; +} diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp @@ -5578,7 +5578,8 @@ case TargetOpcode::INLINEASM_BR: { const MachineFunction *MF = MI.getParent()->getParent(); const char *AsmStr = MI.getOperand(0).getSymbolName(); - return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo()); + return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo(), + &MF->getSubtarget()); } default: return DescSize; diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h --- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h +++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h @@ -264,8 +264,10 @@ /// Measure the specified inline asm to determine an approximation of its /// length. - unsigned getInlineAsmLength(const char *Str, - const MCAsmInfo &MAI) const override; + unsigned getInlineAsmLength( + const char *Str, + const MCAsmInfo &MAI, + const TargetSubtargetInfo *STI = nullptr) const override; /// Allocate and return a hazard recognizer to use for this target when /// scheduling the machine instructions after register allocation. diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp --- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp +++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp @@ -1712,17 +1712,19 @@ /// Hexagon counts the number of ##'s and adjust for that many /// constant exenders. unsigned HexagonInstrInfo::getInlineAsmLength(const char *Str, - const MCAsmInfo &MAI) const { + const MCAsmInfo &MAI, + const TargetSubtargetInfo *STI) const { StringRef AStr(Str); // Count the number of instructions in the asm. bool atInsnStart = true; unsigned Length = 0; + const unsigned MaxInstLength = MAI.getMaxInstLength(STI); for (; *Str; ++Str) { if (*Str == '\n' || strncmp(Str, MAI.getSeparatorString(), strlen(MAI.getSeparatorString())) == 0) atInsnStart = true; if (atInsnStart && !std::isspace(static_cast(*Str))) { - Length += MAI.getMaxInstLength(); + Length += MaxInstLength; atInsnStart = false; } if (atInsnStart && strncmp(Str, MAI.getCommentString().data(), diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -815,6 +815,34 @@ ret void } +define void @fastmathflags_select(i1 %cond, float %op1, float %op2) { + %f.nnan = select nnan i1 %cond, float %op1, float %op2 + ; CHECK: %f.nnan = select nnan i1 %cond, float %op1, float %op2 + %f.ninf = select ninf i1 %cond, float %op1, float %op2 + ; CHECK: %f.ninf = select ninf i1 %cond, float %op1, float %op2 + %f.nsz = select nsz i1 %cond, float %op1, float %op2 + ; CHECK: %f.nsz = select nsz i1 %cond, float %op1, float %op2 + %f.arcp = select arcp i1 %cond, float %op1, float %op2 + ; CHECK: %f.arcp = select arcp i1 %cond, float %op1, float %op2 + %f.contract = select contract i1 %cond, float %op1, float %op2 + ; CHECK: %f.contract = select contract i1 %cond, float %op1, float %op2 + %f.afn = select afn i1 %cond, float %op1, float %op2 + ; CHECK: %f.afn = select afn i1 %cond, float %op1, float %op2 + %f.reassoc = select reassoc i1 %cond, float %op1, float %op2 + ; CHECK: %f.reassoc = select reassoc i1 %cond, float %op1, float %op2 + %f.fast = select fast i1 %cond, float %op1, float %op2 + ; CHECK: %f.fast = select fast i1 %cond, float %op1, float %op2 + ret void +} + +define void @fastmathflags_vector_select(<2 x i1> %cond, <2 x double> %op1, <2 x double> %op2) { + %f.nnan.nsz = select nnan nsz <2 x i1> %cond, <2 x double> %op1, <2 x double> %op2 + ; CHECK: %f.nnan.nsz = select nnan nsz <2 x i1> %cond, <2 x double> %op1, <2 x double> %op2 + %f.fast = select fast <2 x i1> %cond, <2 x double> %op1, <2 x double> %op2 + ; CHECK: %f.fast = select fast <2 x i1> %cond, <2 x double> %op1, <2 x double> %op2 + ret void +} + ; Check various fast math flags and floating-point types on calls. declare float @fmf1() diff --git a/llvm/test/CodeGen/AArch64/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/AArch64/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "aarch64-unknown-linux-gnu" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/AArch64/sink-addsub-of-const.ll b/llvm/test/CodeGen/AArch64/sink-addsub-of-const.ll --- a/llvm/test/CodeGen/AArch64/sink-addsub-of-const.ll +++ b/llvm/test/CodeGen/AArch64/sink-addsub-of-const.ll @@ -1,22 +1,41 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=aarch64-unknown-unknown | FileCheck %s -; Scalar tests. Trying to avoid LEA here, so the output is actually readable.. +; Scalar tests. -define i32 @sink_add_of_const_to_add(i32 %a, i32 %b, i32 %c) { -; CHECK-LABEL: sink_add_of_const_to_add: +; add (add %x, C), %y +; Outer 'add' is commutative - 2 variants. + +define i32 @sink_add_of_const_to_add0(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_add_of_const_to_add0: ; CHECK: // %bb.0: ; CHECK-NEXT: add w8, w0, w1 ; CHECK-NEXT: add w8, w8, w2 ; CHECK-NEXT: add w0, w8, #32 // =32 ; CHECK-NEXT: ret %t0 = add i32 %a, %b - %t1 = add i32 %t0, 32 + %t1 = add i32 %t0, 32 ; constant always on RHS %r = add i32 %t1, %c ret i32 %r } -define i32 @sink_sub_of_const_to_add(i32 %a, i32 %b, i32 %c) { -; CHECK-LABEL: sink_sub_of_const_to_add: +define i32 @sink_add_of_const_to_add1(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_add_of_const_to_add1: +; CHECK: // %bb.0: +; CHECK-NEXT: add w8, w0, w1 +; CHECK-NEXT: add w8, w8, w2 +; CHECK-NEXT: add w0, w8, #32 // =32 +; CHECK-NEXT: ret + %t0 = add i32 %a, %b + %t1 = add i32 %t0, 32 ; constant always on RHS + %r = add i32 %c, %t1 + ret i32 %r +} + +; add (sub %x, C), %y +; Outer 'add' is commutative - 2 variants. + +define i32 @sink_sub_of_const_to_add0(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_of_const_to_add0: ; CHECK: // %bb.0: ; CHECK-NEXT: add w8, w0, w1 ; CHECK-NEXT: add w8, w8, w2 @@ -27,21 +46,37 @@ %r = add i32 %t1, %c ret i32 %r } +define i32 @sink_sub_of_const_to_add1(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_of_const_to_add1: +; CHECK: // %bb.0: +; CHECK-NEXT: add w8, w0, w1 +; CHECK-NEXT: add w8, w8, w2 +; CHECK-NEXT: sub w0, w8, #32 // =32 +; CHECK-NEXT: ret + %t0 = add i32 %a, %b + %t1 = sub i32 %t0, 32 + %r = add i32 %c, %t1 + ret i32 %r +} -define i32 @sink_add_of_const_to_sub(i32 %a, i32 %b, i32 %c) { -; CHECK-LABEL: sink_add_of_const_to_sub: +; add (sub C, %x), %y +; Outer 'add' is commutative - 2 variants. + +define i32 @sink_sub_from_const_to_add0(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_from_const_to_add0: ; CHECK: // %bb.0: ; CHECK-NEXT: add w8, w0, w1 -; CHECK-NEXT: add w8, w8, #32 // =32 -; CHECK-NEXT: sub w0, w8, w2 +; CHECK-NEXT: mov w9, #32 +; CHECK-NEXT: sub w8, w9, w8 +; CHECK-NEXT: add w0, w8, w2 ; CHECK-NEXT: ret %t0 = add i32 %a, %b - %t1 = add i32 %t0, 32 - %r = sub i32 %t1, %c + %t1 = sub i32 32, %t0 + %r = add i32 %t1, %c ret i32 %r } -define i32 @sink_sub_of_const_to_sub2(i32 %a, i32 %b, i32 %c) { -; CHECK-LABEL: sink_sub_of_const_to_sub2: +define i32 @sink_sub_from_const_to_add1(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_from_const_to_add1: ; CHECK: // %bb.0: ; CHECK-NEXT: add w8, w0, w1 ; CHECK-NEXT: mov w9, #32 @@ -49,129 +84,287 @@ ; CHECK-NEXT: add w0, w2, w8 ; CHECK-NEXT: ret %t0 = add i32 %a, %b - %t1 = sub i32 %t0, 32 - %r = sub i32 %c, %t1 + %t1 = sub i32 32, %t0 + %r = add i32 %c, %t1 ret i32 %r } +; sub (add %x, C), %y +; sub %y, (add %x, C) + +define i32 @sink_add_of_const_to_sub(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_add_of_const_to_sub: +; CHECK: // %bb.0: +; CHECK-NEXT: sub w8, w0, w1 +; CHECK-NEXT: add w8, w8, #32 // =32 +; CHECK-NEXT: sub w0, w8, w2 +; CHECK-NEXT: ret + %t0 = sub i32 %a, %b + %t1 = add i32 %t0, 32 ; constant always on RHS + %r = sub i32 %t1, %c + ret i32 %r +} define i32 @sink_add_of_const_to_sub2(i32 %a, i32 %b, i32 %c) { ; CHECK-LABEL: sink_add_of_const_to_sub2: ; CHECK: // %bb.0: -; CHECK-NEXT: add w8, w0, w1 +; CHECK-NEXT: sub w8, w0, w1 ; CHECK-NEXT: add w8, w8, #32 // =32 ; CHECK-NEXT: sub w0, w2, w8 ; CHECK-NEXT: ret - %t0 = add i32 %a, %b - %t1 = add i32 %t0, 32 + %t0 = sub i32 %a, %b + %t1 = add i32 %t0, 32 ; constant always on RHS %r = sub i32 %c, %t1 ret i32 %r } + +; sub (sub %x, C), %y +; sub %y, (sub %x, C) + define i32 @sink_sub_of_const_to_sub(i32 %a, i32 %b, i32 %c) { ; CHECK-LABEL: sink_sub_of_const_to_sub: ; CHECK: // %bb.0: -; CHECK-NEXT: add w8, w0, w1 +; CHECK-NEXT: sub w8, w0, w1 ; CHECK-NEXT: sub w8, w8, #32 // =32 ; CHECK-NEXT: sub w0, w8, w2 ; CHECK-NEXT: ret - %t0 = add i32 %a, %b + %t0 = sub i32 %a, %b %t1 = sub i32 %t0, 32 %r = sub i32 %t1, %c ret i32 %r } +define i32 @sink_sub_of_const_to_sub2(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_of_const_to_sub2: +; CHECK: // %bb.0: +; CHECK-NEXT: sub w8, w1, w0 +; CHECK-NEXT: add w8, w8, w2 +; CHECK-NEXT: add w0, w8, #32 // =32 +; CHECK-NEXT: ret + %t0 = sub i32 %a, %b + %t1 = sub i32 %t0, 32 + %r = sub i32 %c, %t1 + ret i32 %r +} +; sub (sub C, %x), %y +; sub %y, (sub C, %x) + +define i32 @sink_sub_from_const_to_sub(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_from_const_to_sub: +; CHECK: // %bb.0: +; CHECK-NEXT: sub w8, w1, w0 +; CHECK-NEXT: add w8, w8, #32 // =32 +; CHECK-NEXT: sub w0, w8, w2 +; CHECK-NEXT: ret + %t0 = sub i32 %a, %b + %t1 = sub i32 32, %t0 + %r = sub i32 %t1, %c + ret i32 %r +} +define i32 @sink_sub_from_const_to_sub2(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: sink_sub_from_const_to_sub2: +; CHECK: // %bb.0: +; CHECK-NEXT: sub w8, w0, w1 +; CHECK-NEXT: add w8, w8, w2 +; CHECK-NEXT: sub w0, w8, #32 // =32 +; CHECK-NEXT: ret + %t0 = sub i32 %a, %b + %t1 = sub i32 32, %t0 + %r = sub i32 %c, %t1 + ret i32 %r +} + +;------------------------------------------------------------------------------; ; Basic vector tests. Here it is easier to see where the constant operand is. +;------------------------------------------------------------------------------; + +; add (add %x, C), %y +; Outer 'add' is commutative - 2 variants. -define <4 x i32> @vec_sink_add_of_const_to_add(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; CHECK-LABEL: vec_sink_add_of_const_to_add: +define <4 x i32> @vec_sink_add_of_const_to_add0(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_add_of_const_to_add0: ; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, .LCPI6_0 -; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI6_0] +; CHECK-NEXT: adrp x8, .LCPI12_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI12_0] ; CHECK-NEXT: add v0.4s, v0.4s, v1.4s ; CHECK-NEXT: add v0.4s, v0.4s, v2.4s ; CHECK-NEXT: add v0.4s, v0.4s, v3.4s ; CHECK-NEXT: ret %t0 = add <4 x i32> %a, %b - %t1 = add <4 x i32> %t0, + %t1 = add <4 x i32> %t0, ; constant always on RHS %r = add <4 x i32> %t1, %c ret <4 x i32> %r } -define <4 x i32> @vec_sink_sub_of_const_to_add(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; CHECK-LABEL: vec_sink_sub_of_const_to_add: +define <4 x i32> @vec_sink_add_of_const_to_add1(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_add_of_const_to_add1: ; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, .LCPI7_0 -; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI7_0] +; CHECK-NEXT: adrp x8, .LCPI13_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI13_0] +; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: add v0.4s, v0.4s, v2.4s +; CHECK-NEXT: add v0.4s, v0.4s, v3.4s +; CHECK-NEXT: ret + %t0 = add <4 x i32> %a, %b + %t1 = add <4 x i32> %t0, ; constant always on RHS + %r = add <4 x i32> %c, %t1 + ret <4 x i32> %r +} + +; add (sub %x, C), %y +; Outer 'add' is commutative - 2 variants. + +define <4 x i32> @vec_sink_sub_of_const_to_add0(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_of_const_to_add0: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x8, .LCPI14_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI14_0] ; CHECK-NEXT: add v0.4s, v0.4s, v1.4s ; CHECK-NEXT: sub v0.4s, v0.4s, v3.4s ; CHECK-NEXT: add v0.4s, v0.4s, v2.4s ; CHECK-NEXT: ret %t0 = add <4 x i32> %a, %b - %t1 = sub <4 x i32> %t0, + %t1 = sub <4 x i32> %t0, %r = add <4 x i32> %t1, %c ret <4 x i32> %r } +define <4 x i32> @vec_sink_sub_of_const_to_add1(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_of_const_to_add1: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x8, .LCPI15_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI15_0] +; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: sub v0.4s, v0.4s, v3.4s +; CHECK-NEXT: add v0.4s, v2.4s, v0.4s +; CHECK-NEXT: ret + %t0 = add <4 x i32> %a, %b + %t1 = sub <4 x i32> %t0, + %r = add <4 x i32> %c, %t1 + ret <4 x i32> %r +} -define <4 x i32> @vec_sink_add_of_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; CHECK-LABEL: vec_sink_add_of_const_to_sub: +; add (sub C, %x), %y +; Outer 'add' is commutative - 2 variants. + +define <4 x i32> @vec_sink_sub_from_const_to_add0(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_from_const_to_add0: ; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, .LCPI8_0 -; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI8_0] +; CHECK-NEXT: adrp x8, .LCPI16_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI16_0] ; CHECK-NEXT: add v0.4s, v0.4s, v1.4s -; CHECK-NEXT: add v0.4s, v0.4s, v3.4s -; CHECK-NEXT: sub v0.4s, v0.4s, v2.4s +; CHECK-NEXT: sub v0.4s, v3.4s, v0.4s +; CHECK-NEXT: add v0.4s, v0.4s, v2.4s ; CHECK-NEXT: ret %t0 = add <4 x i32> %a, %b - %t1 = add <4 x i32> %t0, - %r = sub <4 x i32> %t1, %c + %t1 = sub <4 x i32> , %t0 + %r = add <4 x i32> %t1, %c ret <4 x i32> %r } -define <4 x i32> @vec_sink_sub_of_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; ALL-LABEL: vec_sink_sub_of_const_to_sub2: -; ALL: # %bb.0: -; ALL-NEXT: paddd %xmm1, %xmm0 -; ALL-NEXT: movdqa {{.*#+}} xmm1 = <93,u,45,81> -; ALL-NEXT: psubd %xmm0, %xmm1 -; ALL-NEXT: paddd %xmm2, %xmm1 -; ALL-NEXT: movdqa %xmm1, %xmm0 -; ALL-NEXT: ret{{[l|q]}} -; CHECK-LABEL: vec_sink_sub_of_const_to_sub2: +define <4 x i32> @vec_sink_sub_from_const_to_add1(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_from_const_to_add1: ; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, .LCPI9_0 -; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI9_0] +; CHECK-NEXT: adrp x8, .LCPI17_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI17_0] ; CHECK-NEXT: add v0.4s, v0.4s, v1.4s ; CHECK-NEXT: sub v0.4s, v3.4s, v0.4s ; CHECK-NEXT: add v0.4s, v2.4s, v0.4s ; CHECK-NEXT: ret %t0 = add <4 x i32> %a, %b - %t1 = sub <4 x i32> %t0, - %r = sub <4 x i32> %c, %t1 + %t1 = sub <4 x i32> , %t0 + %r = add <4 x i32> %c, %t1 ret <4 x i32> %r } +; sub (add %x, C), %y +; sub %y, (add %x, C) + +define <4 x i32> @vec_sink_add_of_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_add_of_const_to_sub: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x8, .LCPI18_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI18_0] +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: add v0.4s, v0.4s, v3.4s +; CHECK-NEXT: sub v0.4s, v0.4s, v2.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %a, %b + %t1 = add <4 x i32> %t0, ; constant always on RHS + %r = sub <4 x i32> %t1, %c + ret <4 x i32> %r +} define <4 x i32> @vec_sink_add_of_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { ; CHECK-LABEL: vec_sink_add_of_const_to_sub2: ; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, .LCPI10_0 -; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI10_0] -; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: adrp x8, .LCPI19_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI19_0] +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s ; CHECK-NEXT: add v0.4s, v0.4s, v3.4s ; CHECK-NEXT: sub v0.4s, v2.4s, v0.4s ; CHECK-NEXT: ret - %t0 = add <4 x i32> %a, %b - %t1 = add <4 x i32> %t0, + %t0 = sub <4 x i32> %a, %b + %t1 = add <4 x i32> %t0, ; constant always on RHS %r = sub <4 x i32> %c, %t1 ret <4 x i32> %r } + +; sub (sub %x, C), %y +; sub %y, (sub %x, C) + define <4 x i32> @vec_sink_sub_of_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { ; CHECK-LABEL: vec_sink_sub_of_const_to_sub: ; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, .LCPI11_0 -; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI11_0] -; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: adrp x8, .LCPI20_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI20_0] +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s ; CHECK-NEXT: sub v0.4s, v0.4s, v3.4s ; CHECK-NEXT: sub v0.4s, v0.4s, v2.4s ; CHECK-NEXT: ret - %t0 = add <4 x i32> %a, %b - %t1 = sub <4 x i32> %t0, + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> %t0, + %r = sub <4 x i32> %t1, %c + ret <4 x i32> %r +} +define <4 x i32> @vec_sink_sub_of_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_of_const_to_sub2: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x8, .LCPI21_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI21_0] +; CHECK-NEXT: sub v0.4s, v1.4s, v0.4s +; CHECK-NEXT: add v0.4s, v0.4s, v2.4s +; CHECK-NEXT: add v0.4s, v0.4s, v3.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> %t0, + %r = sub <4 x i32> %c, %t1 + ret <4 x i32> %r +} + +; sub (sub C, %x), %y +; sub %y, (sub C, %x) + +define <4 x i32> @vec_sink_sub_from_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_from_const_to_sub: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x8, .LCPI22_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI22_0] +; CHECK-NEXT: sub v0.4s, v1.4s, v0.4s +; CHECK-NEXT: add v0.4s, v0.4s, v3.4s +; CHECK-NEXT: sub v0.4s, v0.4s, v2.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> , %t0 %r = sub <4 x i32> %t1, %c ret <4 x i32> %r } +define <4 x i32> @vec_sink_sub_from_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; CHECK-LABEL: vec_sink_sub_from_const_to_sub2: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x8, .LCPI23_0 +; CHECK-NEXT: ldr q3, [x8, :lo12:.LCPI23_0] +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: sub v0.4s, v0.4s, v3.4s +; CHECK-NEXT: add v0.4s, v2.4s, v0.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> , %t0 + %r = sub <4 x i32> %c, %t1 + ret <4 x i32> %r +} diff --git a/llvm/test/CodeGen/AArch64/xor.ll b/llvm/test/CodeGen/AArch64/xor.ll --- a/llvm/test/CodeGen/AArch64/xor.ll +++ b/llvm/test/CodeGen/AArch64/xor.ll @@ -15,3 +15,72 @@ ret i32 %load } +define i32 @add_of_not(i32 %x, i32 %y) { +; CHECK-LABEL: add_of_not: +; CHECK: // %bb.0: +; CHECK-NEXT: sub w8, w0, w1 +; CHECK-NEXT: sub w0, w8, #1 // =1 +; CHECK-NEXT: ret + %t0 = sub i32 %x, %y + %r = add i32 %t0, -1 + ret i32 %r +} + +define i32 @add_of_not_decrement(i32 %x, i32 %y) { +; CHECK-LABEL: add_of_not_decrement: +; CHECK: // %bb.0: +; CHECK-NEXT: sub w8, w0, w1 +; CHECK-NEXT: sub w0, w8, #1 // =1 +; CHECK-NEXT: ret + %t0 = sub i32 %x, %y + %r = sub i32 %t0, 1 + ret i32 %r +} + +define <4 x i32> @vec_add_of_not(<4 x i32> %x, <4 x i32> %y) { +; CHECK-LABEL: vec_add_of_not: +; CHECK: // %bb.0: +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: movi v1.2d, #0xffffffffffffffff +; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %x, %y + %r = add <4 x i32> %t0, + ret <4 x i32> %r +} + +define <4 x i32> @vec_add_of_not_decrement(<4 x i32> %x, <4 x i32> %y) { +; CHECK-LABEL: vec_add_of_not_decrement: +; CHECK: // %bb.0: +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: movi v1.4s, #1 +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %x, %y + %r = sub <4 x i32> %t0, + ret <4 x i32> %r +} + +define <4 x i32> @vec_add_of_not_with_undef(<4 x i32> %x, <4 x i32> %y) { +; CHECK-LABEL: vec_add_of_not_with_undef: +; CHECK: // %bb.0: +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: movi v1.2d, #0xffffffffffffffff +; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %x, %y + %r = add <4 x i32> %t0, + ret <4 x i32> %r +} + +define <4 x i32> @vec_add_of_not_with_undef_decrement(<4 x i32> %x, <4 x i32> %y) { +; CHECK-LABEL: vec_add_of_not_with_undef_decrement: +; CHECK: // %bb.0: +; CHECK-NEXT: sub v0.4s, v0.4s, v1.4s +; CHECK-NEXT: movi v1.4s, #1 +; CHECK-NEXT: add v0.4s, v0.4s, v1.4s +; CHECK-NEXT: ret + %t0 = sub <4 x i32> %x, %y + %r = add <4 x i32> %t0, + ret <4 x i32> %r +} diff --git a/llvm/test/CodeGen/AMDGPU/branch-relaxation-inst-size-gfx10.ll b/llvm/test/CodeGen/AMDGPU/branch-relaxation-inst-size-gfx10.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AMDGPU/branch-relaxation-inst-size-gfx10.ll @@ -0,0 +1,33 @@ +; RUN: llc -march=amdgcn -mcpu=gfx1010 -verify-machineinstrs -amdgpu-s-branch-bits=4 < %s | FileCheck -enable-var-scope -check-prefixes=GCN,GFX10 %s +; RUN: llc -march=amdgcn -mcpu=gfx900 -verify-machineinstrs -amdgpu-s-branch-bits=4 < %s | FileCheck -enable-var-scope -check-prefixes=GCN,GFX9 %s + +; Make sure the code size estimate for inline asm is 12-bytes per +; instruction, rather than 8 in previous generations. + +; GCN-LABEL: {{^}}long_forward_branch_gfx10only: +; GFX9: s_cmp_eq_u32 +; GFX9-NEXT: s_cbranch_scc1 + +; GFX10: s_cmp_eq_u32 +; GFX10-NEXT: s_cbranch_scc0 +; GFX10: s_getpc_b64 +; GFX10: s_add_u32 +; GFX10: s_addc_u32 +; GFX10: s_setpc_b64 +define amdgpu_kernel void @long_forward_branch_gfx10only(i32 addrspace(1)* %arg, i32 %cnd) #0 { +bb0: + %cmp = icmp eq i32 %cnd, 0 + br i1 %cmp, label %bb3, label %bb2 ; +9 dword branch + +bb2: + ; Estimated as 40-bytes on gfx10 (requiring a long branch), but + ; 16-bytes on gfx9 (allowing a short branch) + call void asm sideeffect + "v_nop_e64 + v_nop_e64", ""() #0 + br label %bb3 + +bb3: + store volatile i32 %cnd, i32 addrspace(1)* %arg + ret void +} diff --git a/llvm/test/CodeGen/ARM/addsubcarry-promotion.ll b/llvm/test/CodeGen/ARM/addsubcarry-promotion.ll --- a/llvm/test/CodeGen/ARM/addsubcarry-promotion.ll +++ b/llvm/test/CodeGen/ARM/addsubcarry-promotion.ll @@ -1,45 +1,55 @@ -; RUN: llc -O2 -mtriple armv7a < %s | FileCheck --check-prefix=ARM %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -O2 -mtriple armv7a < %s | FileCheck --check-prefixes=ARM,ARMV7A %s -; RUN: llc -O2 -mtriple thumbv6m < %s | FileCheck --check-prefix=THUMB1 %s -; RUN: llc -O2 -mtriple thumbv8m.base < %s | FileCheck --check-prefix=THUMB1 %s +; RUN: llc -O2 -mtriple thumbv6m < %s | FileCheck --check-prefixes=THUMB1,THUMBV6M %s +; RUN: llc -O2 -mtriple thumbv8m.base < %s | FileCheck --check-prefixes=THUMB1,THUMBV8M-BASE %s -; RUN: llc -O2 -mtriple thumbv7a < %s | FileCheck --check-prefix=THUMB %s -; RUN: llc -O2 -mtriple thumbv8m.main < %s | FileCheck --check-prefix=THUMB %s +; RUN: llc -O2 -mtriple thumbv7a < %s | FileCheck --check-prefixes=THUMB,THUMBV7A %s +; RUN: llc -O2 -mtriple thumbv8m.main < %s | FileCheck --check-prefixes=THUMB,THUMBV8M-MAIN %s define void @fn1(i32 %a, i32 %b, i32 %c) local_unnamed_addr #0 { +; ARM-LABEL: fn1: +; ARM: @ %bb.0: @ %entry +; ARM-NEXT: rsb r2, r2, #1 +; ARM-NEXT: adds r0, r1, r0 +; ARM-NEXT: movw r1, #65535 +; ARM-NEXT: sxth r2, r2 +; ARM-NEXT: adc r0, r2, #0 +; ARM-NEXT: tst r0, r1 +; ARM-NEXT: bxeq lr +; ARM-NEXT: .LBB0_1: @ %for.cond +; ARM-NEXT: @ =>This Inner Loop Header: Depth=1 +; ARM-NEXT: b .LBB0_1 +; +; THUMB1-LABEL: fn1: +; THUMB1: @ %bb.0: @ %entry +; THUMB1-NEXT: movs r3, #1 +; THUMB1-NEXT: subs r2, r3, r2 +; THUMB1-NEXT: sxth r2, r2 +; THUMB1-NEXT: movs r3, #0 +; THUMB1-NEXT: adds r0, r1, r0 +; THUMB1-NEXT: adcs r3, r2 +; THUMB1-NEXT: lsls r0, r3, #16 +; THUMB1-NEXT: beq .LBB0_2 +; THUMB1-NEXT: .LBB0_1: @ %for.cond +; THUMB1-NEXT: @ =>This Inner Loop Header: Depth=1 +; THUMB1-NEXT: b .LBB0_1 +; THUMB1-NEXT: .LBB0_2: @ %if.end +; THUMB1-NEXT: bx lr +; +; THUMB-LABEL: fn1: +; THUMB: @ %bb.0: @ %entry +; THUMB-NEXT: rsb.w r2, r2, #1 +; THUMB-NEXT: adds r0, r0, r1 +; THUMB-NEXT: sxth r2, r2 +; THUMB-NEXT: adc r0, r2, #0 +; THUMB-NEXT: lsls r0, r0, #16 +; THUMB-NEXT: it eq +; THUMB-NEXT: bxeq lr +; THUMB-NEXT: .LBB0_1: @ %for.cond +; THUMB-NEXT: @ =>This Inner Loop Header: Depth=1 +; THUMB-NEXT: b .LBB0_1 entry: - -; ARM: rsb r2, r2, #1 -; ARM: adds r0, r1, r0 -; ARM: movw r1, #65535 -; ARM: sxth r2, r2 -; ARM: adc r0, r2, #0 -; ARM: tst r0, r1 -; ARM: bxeq lr -; ARM: .LBB0_1: -; ARM: b .LBB0_1 - -; THUMB1: movs r3, #1 -; THUMB1: subs r2, r3, r2 -; THUMB1: sxth r2, r2 -; THUMB1: movs r3, #0 -; THUMB1: adds r0, r1, r0 -; THUMB1: adcs r3, r2 -; THUMB1: lsls r0, r3, #16 -; THUMB1: beq .LBB0_2 -; THUMB1: .LBB0_1: -; THUMB1: b .LBB0_1 - -; THUMB: rsb.w r2, r2, #1 -; THUMB: adds r0, r0, r1 -; THUMB: sxth r2, r2 -; THUMB: adc r0, r2, #0 -; THUMB: lsls r0, r0, #16 -; THUMB: it eq -; THUMB: bxeq lr -; THUMB: .LBB0_1: -; THUMB: b .LBB0_1 - %add = add i32 %b, %a %cmp = icmp ult i32 %add, %b %conv = zext i1 %cmp to i32 diff --git a/llvm/test/CodeGen/ARM/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/ARM/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=armv7-unknown-linux-gnueabi < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "armv7-unknown-linux-gnueabi" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/Generic/expand-experimental-reductions.ll b/llvm/test/CodeGen/Generic/expand-experimental-reductions.ll --- a/llvm/test/CodeGen/Generic/expand-experimental-reductions.ll +++ b/llvm/test/CodeGen/Generic/expand-experimental-reductions.ll @@ -277,7 +277,7 @@ ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RDX_SHUF:%.*]] = shufflevector <2 x double> [[VEC:%.*]], <2 x double> undef, <2 x i32> ; CHECK-NEXT: [[RDX_MINMAX_CMP:%.*]] = fcmp fast ogt <2 x double> [[VEC]], [[RDX_SHUF]] -; CHECK-NEXT: [[RDX_MINMAX_SELECT:%.*]] = select <2 x i1> [[RDX_MINMAX_CMP]], <2 x double> [[VEC]], <2 x double> [[RDX_SHUF]] +; CHECK-NEXT: [[RDX_MINMAX_SELECT:%.*]] = select fast <2 x i1> [[RDX_MINMAX_CMP]], <2 x double> [[VEC]], <2 x double> [[RDX_SHUF]] ; CHECK-NEXT: [[TMP0:%.*]] = extractelement <2 x double> [[RDX_MINMAX_SELECT]], i32 0 ; CHECK-NEXT: ret double [[TMP0]] ; @@ -291,7 +291,7 @@ ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RDX_SHUF:%.*]] = shufflevector <2 x double> [[VEC:%.*]], <2 x double> undef, <2 x i32> ; CHECK-NEXT: [[RDX_MINMAX_CMP:%.*]] = fcmp fast olt <2 x double> [[VEC]], [[RDX_SHUF]] -; CHECK-NEXT: [[RDX_MINMAX_SELECT:%.*]] = select <2 x i1> [[RDX_MINMAX_CMP]], <2 x double> [[VEC]], <2 x double> [[RDX_SHUF]] +; CHECK-NEXT: [[RDX_MINMAX_SELECT:%.*]] = select fast <2 x i1> [[RDX_MINMAX_CMP]], <2 x double> [[VEC]], <2 x double> [[RDX_SHUF]] ; CHECK-NEXT: [[TMP0:%.*]] = extractelement <2 x double> [[RDX_MINMAX_SELECT]], i32 0 ; CHECK-NEXT: ret double [[TMP0]] ; diff --git a/llvm/test/CodeGen/Mips/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/Mips/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Mips/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=mips64el-unknown-linux-gnu < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "mips64el-unknown-linux-gnu" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/PowerPC/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/PowerPC/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "powerpc64le-unknown-linux-gnu" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/RISCV/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/RISCV/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=riscv64-unknown-linux-gnu < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "riscv64-unknown-linux-gnu" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/SPARC/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/SPARC/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPARC/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=sparc64-unknown-linux-gnu < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "sparc64-unknown-linux-gnu" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/SystemZ/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/SystemZ/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "s390x-linux-gnu" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/Thumb/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/Thumb/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Thumb/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=thumbv7-linux-gnueabi < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "thumbv7-linux-gnueabi" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/Thumb2/inline-asm-i-constraint-i1.ll b/llvm/test/CodeGen/Thumb2/inline-asm-i-constraint-i1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Thumb2/inline-asm-i-constraint-i1.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=thumbv8-none-linux-gnueabi < %s | FileCheck %s + +; Make sure that boolean immediates are properly (zero) extended. +; CHECK: TEST 42 + 1 - . + +target triple = "thumbv8-none-linux-gnueabi" + +define i32 @foo() #0 { +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i,~{dirflag},~{fpsr},~{flags}"(i1 true) #0 + ret i32 1 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/X86/negative-offset.ll b/llvm/test/CodeGen/X86/negative-offset.ll --- a/llvm/test/CodeGen/X86/negative-offset.ll +++ b/llvm/test/CodeGen/X86/negative-offset.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -6,13 +7,14 @@ ; Test that a constant consisting of a global symbol with a negative offset ; is properly folded and isel'd. -; CHECK-LABEL: negative_offset: -; CHECK: movl $G, %eax -; CHECK: notq %rax -; CHECK: addq %rdi, %rax -; CHECK: retq @G = external global [8 x i32] define i8* @negative_offset(i8* %a) { +; CHECK-LABEL: negative_offset: +; CHECK: # %bb.0: +; CHECK-NEXT: movl $G, %eax +; CHECK-NEXT: notq %rax +; CHECK-NEXT: addq %rdi, %rax +; CHECK-NEXT: retq %t = getelementptr i8, i8* %a, i64 sub (i64 -1, i64 ptrtoint ([8 x i32]* @G to i64)) ret i8* %t } diff --git a/llvm/test/CodeGen/X86/sink-addsub-of-const.ll b/llvm/test/CodeGen/X86/sink-addsub-of-const.ll --- a/llvm/test/CodeGen/X86/sink-addsub-of-const.ll +++ b/llvm/test/CodeGen/X86/sink-addsub-of-const.ll @@ -4,8 +4,11 @@ ; Scalar tests. Trying to avoid LEA here, so the output is actually readable.. -define i32 @sink_add_of_const_to_add(i32 %a, i32 %b, i32 %c) { -; X32-LABEL: sink_add_of_const_to_add: +; add (add %x, C), %y +; Outer 'add' is commutative - 2 variants. + +define i32 @sink_add_of_const_to_add0(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_add_of_const_to_add0: ; X32: # %bb.0: ; X32-NEXT: movl {{[0-9]+}}(%esp), %eax ; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx @@ -14,7 +17,7 @@ ; X32-NEXT: addl $32, %eax ; X32-NEXT: retl ; -; X64-LABEL: sink_add_of_const_to_add: +; X64-LABEL: sink_add_of_const_to_add0: ; X64: # %bb.0: ; X64-NEXT: # kill: def $edx killed $edx def $rdx ; X64-NEXT: # kill: def $edi killed $edi def $rdi @@ -22,12 +25,38 @@ ; X64-NEXT: leal 32(%rdx,%rdi), %eax ; X64-NEXT: retq %t0 = add i32 %a, %b - %t1 = add i32 %t0, 32 + %t1 = add i32 %t0, 32 ; constant always on RHS %r = add i32 %t1, %c ret i32 %r } -define i32 @sink_sub_of_const_to_add(i32 %a, i32 %b, i32 %c) { -; X32-LABEL: sink_sub_of_const_to_add: +define i32 @sink_add_of_const_to_add1(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_add_of_const_to_add1: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: addl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: addl %ecx, %eax +; X32-NEXT: addl $32, %eax +; X32-NEXT: retl +; +; X64-LABEL: sink_add_of_const_to_add1: +; X64: # %bb.0: +; X64-NEXT: # kill: def $edx killed $edx def $rdx +; X64-NEXT: # kill: def $edi killed $edi def $rdi +; X64-NEXT: addl %esi, %edi +; X64-NEXT: leal 32(%rdx,%rdi), %eax +; X64-NEXT: retq + %t0 = add i32 %a, %b + %t1 = add i32 %t0, 32 ; constant always on RHS + %r = add i32 %c, %t1 + ret i32 %r +} + +; add (sub %x, C), %y +; Outer 'add' is commutative - 2 variants. + +define i32 @sink_sub_of_const_to_add0(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_of_const_to_add0: ; X32: # %bb.0: ; X32-NEXT: movl {{[0-9]+}}(%esp), %eax ; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx @@ -36,7 +65,7 @@ ; X32-NEXT: addl $-32, %eax ; X32-NEXT: retl ; -; X64-LABEL: sink_sub_of_const_to_add: +; X64-LABEL: sink_sub_of_const_to_add0: ; X64: # %bb.0: ; X64-NEXT: # kill: def $edx killed $edx def $rdx ; X64-NEXT: # kill: def $edi killed $edi def $rdi @@ -48,31 +77,34 @@ %r = add i32 %t1, %c ret i32 %r } - -define i32 @sink_add_of_const_to_sub(i32 %a, i32 %b, i32 %c) { -; X32-LABEL: sink_add_of_const_to_sub: +define i32 @sink_sub_of_const_to_add1(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_of_const_to_add1: ; X32: # %bb.0: ; X32-NEXT: movl {{[0-9]+}}(%esp), %eax ; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: addl {{[0-9]+}}(%esp), %ecx ; X32-NEXT: addl %ecx, %eax -; X32-NEXT: addl $32, %eax -; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: addl $-32, %eax ; X32-NEXT: retl ; -; X64-LABEL: sink_add_of_const_to_sub: +; X64-LABEL: sink_sub_of_const_to_add1: ; X64: # %bb.0: -; X64-NEXT: # kill: def $esi killed $esi def $rsi +; X64-NEXT: # kill: def $edx killed $edx def $rdx ; X64-NEXT: # kill: def $edi killed $edi def $rdi -; X64-NEXT: leal 32(%rdi,%rsi), %eax -; X64-NEXT: subl %edx, %eax +; X64-NEXT: addl %esi, %edi +; X64-NEXT: leal -32(%rdx,%rdi), %eax ; X64-NEXT: retq %t0 = add i32 %a, %b - %t1 = add i32 %t0, 32 - %r = sub i32 %t1, %c + %t1 = sub i32 %t0, 32 + %r = add i32 %c, %t1 ret i32 %r } -define i32 @sink_sub_of_const_to_sub2(i32 %a, i32 %b, i32 %c) { -; X32-LABEL: sink_sub_of_const_to_sub2: + +; add (sub C, %x), %y +; Outer 'add' is commutative - 2 variants. + +define i32 @sink_sub_from_const_to_add0(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_from_const_to_add0: ; X32: # %bb.0: ; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx ; X32-NEXT: addl {{[0-9]+}}(%esp), %ecx @@ -81,7 +113,7 @@ ; X32-NEXT: addl {{[0-9]+}}(%esp), %eax ; X32-NEXT: retl ; -; X64-LABEL: sink_sub_of_const_to_sub2: +; X64-LABEL: sink_sub_from_const_to_add0: ; X64: # %bb.0: ; X64-NEXT: addl %esi, %edi ; X64-NEXT: movl $32, %eax @@ -89,18 +121,63 @@ ; X64-NEXT: addl %edx, %eax ; X64-NEXT: retq %t0 = add i32 %a, %b - %t1 = sub i32 %t0, 32 - %r = sub i32 %c, %t1 + %t1 = sub i32 32, %t0 + %r = add i32 %t1, %c + ret i32 %r +} +define i32 @sink_sub_from_const_to_add1(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_from_const_to_add1: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: addl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: movl $32, %eax +; X32-NEXT: subl %ecx, %eax +; X32-NEXT: addl {{[0-9]+}}(%esp), %eax +; X32-NEXT: retl +; +; X64-LABEL: sink_sub_from_const_to_add1: +; X64: # %bb.0: +; X64-NEXT: addl %esi, %edi +; X64-NEXT: movl $32, %eax +; X64-NEXT: subl %edi, %eax +; X64-NEXT: addl %edx, %eax +; X64-NEXT: retq + %t0 = add i32 %a, %b + %t1 = sub i32 32, %t0 + %r = add i32 %c, %t1 ret i32 %r } +; sub (add %x, C), %y +; sub %y, (add %x, C) + +define i32 @sink_add_of_const_to_sub(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_add_of_const_to_sub: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: addl $32, %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: retl +; +; X64-LABEL: sink_add_of_const_to_sub: +; X64: # %bb.0: +; X64-NEXT: # kill: def $edi killed $edi def $rdi +; X64-NEXT: subl %esi, %edi +; X64-NEXT: leal 32(%rdi), %eax +; X64-NEXT: subl %edx, %eax +; X64-NEXT: retq + %t0 = sub i32 %a, %b + %t1 = add i32 %t0, 32 ; constant always on RHS + %r = sub i32 %t1, %c + ret i32 %r +} define i32 @sink_add_of_const_to_sub2(i32 %a, i32 %b, i32 %c) { ; X32-LABEL: sink_add_of_const_to_sub2: ; X32: # %bb.0: ; X32-NEXT: movl {{[0-9]+}}(%esp), %eax ; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx -; X32-NEXT: movl {{[0-9]+}}(%esp), %edx -; X32-NEXT: addl %edx, %ecx +; X32-NEXT: subl {{[0-9]+}}(%esp), %ecx ; X32-NEXT: addl $32, %ecx ; X32-NEXT: subl %ecx, %eax ; X32-NEXT: retl @@ -108,118 +185,256 @@ ; X64-LABEL: sink_add_of_const_to_sub2: ; X64: # %bb.0: ; X64-NEXT: movl %edx, %eax -; X64-NEXT: # kill: def $esi killed $esi def $rsi -; X64-NEXT: # kill: def $edi killed $edi def $rdi -; X64-NEXT: leal 32(%rdi,%rsi), %ecx -; X64-NEXT: subl %ecx, %eax +; X64-NEXT: subl %esi, %edi +; X64-NEXT: addl $32, %edi +; X64-NEXT: subl %edi, %eax ; X64-NEXT: retq - %t0 = add i32 %a, %b - %t1 = add i32 %t0, 32 + %t0 = sub i32 %a, %b + %t1 = add i32 %t0, 32 ; constant always on RHS %r = sub i32 %c, %t1 ret i32 %r } + +; sub (sub %x, C), %y +; sub %y, (sub %x, C) + define i32 @sink_sub_of_const_to_sub(i32 %a, i32 %b, i32 %c) { ; X32-LABEL: sink_sub_of_const_to_sub: ; X32: # %bb.0: ; X32-NEXT: movl {{[0-9]+}}(%esp), %eax -; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx -; X32-NEXT: addl %ecx, %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax ; X32-NEXT: addl $-32, %eax ; X32-NEXT: subl {{[0-9]+}}(%esp), %eax ; X32-NEXT: retl ; ; X64-LABEL: sink_sub_of_const_to_sub: ; X64: # %bb.0: -; X64-NEXT: # kill: def $esi killed $esi def $rsi ; X64-NEXT: # kill: def $edi killed $edi def $rdi -; X64-NEXT: leal -32(%rdi,%rsi), %eax +; X64-NEXT: subl %esi, %edi +; X64-NEXT: leal -32(%rdi), %eax ; X64-NEXT: subl %edx, %eax ; X64-NEXT: retq - %t0 = add i32 %a, %b + %t0 = sub i32 %a, %b %t1 = sub i32 %t0, 32 %r = sub i32 %t1, %c ret i32 %r } +define i32 @sink_sub_of_const_to_sub2(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_of_const_to_sub2: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: subl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: addl %ecx, %eax +; X32-NEXT: addl $32, %eax +; X32-NEXT: retl +; +; X64-LABEL: sink_sub_of_const_to_sub2: +; X64: # %bb.0: +; X64-NEXT: # kill: def $edx killed $edx def $rdx +; X64-NEXT: # kill: def $esi killed $esi def $rsi +; X64-NEXT: subl %edi, %esi +; X64-NEXT: leal 32(%rsi,%rdx), %eax +; X64-NEXT: retq + %t0 = sub i32 %a, %b + %t1 = sub i32 %t0, 32 + %r = sub i32 %c, %t1 + ret i32 %r +} + +; sub (sub C, %x), %y +; sub %y, (sub C, %x) + +define i32 @sink_sub_from_const_to_sub(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_from_const_to_sub: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: addl $32, %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: retl +; +; X64-LABEL: sink_sub_from_const_to_sub: +; X64: # %bb.0: +; X64-NEXT: # kill: def $esi killed $esi def $rsi +; X64-NEXT: subl %edi, %esi +; X64-NEXT: leal 32(%rsi), %eax +; X64-NEXT: subl %edx, %eax +; X64-NEXT: retq + %t0 = sub i32 %a, %b + %t1 = sub i32 32, %t0 + %r = sub i32 %t1, %c + ret i32 %r +} +define i32 @sink_sub_from_const_to_sub2(i32 %a, i32 %b, i32 %c) { +; X32-LABEL: sink_sub_from_const_to_sub2: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: subl {{[0-9]+}}(%esp), %ecx +; X32-NEXT: addl %ecx, %eax +; X32-NEXT: addl $-32, %eax +; X32-NEXT: retl +; +; X64-LABEL: sink_sub_from_const_to_sub2: +; X64: # %bb.0: +; X64-NEXT: # kill: def $edx killed $edx def $rdx +; X64-NEXT: # kill: def $edi killed $edi def $rdi +; X64-NEXT: subl %esi, %edi +; X64-NEXT: leal -32(%rdi,%rdx), %eax +; X64-NEXT: retq + %t0 = sub i32 %a, %b + %t1 = sub i32 32, %t0 + %r = sub i32 %c, %t1 + ret i32 %r +} +;------------------------------------------------------------------------------; ; Basic vector tests. Here it is easier to see where the constant operand is. +;------------------------------------------------------------------------------; + +; add (add %x, C), %y +; Outer 'add' is commutative - 2 variants. -define <4 x i32> @vec_sink_add_of_const_to_add(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; X32-LABEL: vec_sink_add_of_const_to_add: +define <4 x i32> @vec_sink_add_of_const_to_add0(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_add_of_const_to_add0: ; X32: # %bb.0: ; X32-NEXT: paddd %xmm2, %xmm1 ; X32-NEXT: paddd %xmm1, %xmm0 ; X32-NEXT: paddd {{\.LCPI.*}}, %xmm0 ; X32-NEXT: retl ; -; X64-LABEL: vec_sink_add_of_const_to_add: +; X64-LABEL: vec_sink_add_of_const_to_add0: ; X64: # %bb.0: ; X64-NEXT: paddd %xmm2, %xmm1 ; X64-NEXT: paddd %xmm1, %xmm0 ; X64-NEXT: paddd {{.*}}(%rip), %xmm0 ; X64-NEXT: retq %t0 = add <4 x i32> %a, %b - %t1 = add <4 x i32> %t0, + %t1 = add <4 x i32> %t0, ; constant always on RHS %r = add <4 x i32> %t1, %c ret <4 x i32> %r } -define <4 x i32> @vec_sink_sub_of_const_to_add(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; X32-LABEL: vec_sink_sub_of_const_to_add: +define <4 x i32> @vec_sink_add_of_const_to_add1(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_add_of_const_to_add1: +; X32: # %bb.0: +; X32-NEXT: paddd %xmm2, %xmm1 +; X32-NEXT: paddd %xmm1, %xmm0 +; X32-NEXT: paddd {{\.LCPI.*}}, %xmm0 +; X32-NEXT: retl +; +; X64-LABEL: vec_sink_add_of_const_to_add1: +; X64: # %bb.0: +; X64-NEXT: paddd %xmm2, %xmm1 +; X64-NEXT: paddd %xmm1, %xmm0 +; X64-NEXT: paddd {{.*}}(%rip), %xmm0 +; X64-NEXT: retq + %t0 = add <4 x i32> %a, %b + %t1 = add <4 x i32> %t0, ; constant always on RHS + %r = add <4 x i32> %c, %t1 + ret <4 x i32> %r +} + +; add (sub %x, C), %y +; Outer 'add' is commutative - 2 variants. + +define <4 x i32> @vec_sink_sub_of_const_to_add0(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_sub_of_const_to_add0: ; X32: # %bb.0: ; X32-NEXT: paddd %xmm1, %xmm0 ; X32-NEXT: psubd {{\.LCPI.*}}, %xmm0 ; X32-NEXT: paddd %xmm2, %xmm0 ; X32-NEXT: retl ; -; X64-LABEL: vec_sink_sub_of_const_to_add: +; X64-LABEL: vec_sink_sub_of_const_to_add0: ; X64: # %bb.0: ; X64-NEXT: paddd %xmm1, %xmm0 ; X64-NEXT: psubd {{.*}}(%rip), %xmm0 ; X64-NEXT: paddd %xmm2, %xmm0 ; X64-NEXT: retq %t0 = add <4 x i32> %a, %b - %t1 = sub <4 x i32> %t0, + %t1 = sub <4 x i32> %t0, %r = add <4 x i32> %t1, %c ret <4 x i32> %r } - -define <4 x i32> @vec_sink_add_of_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; X32-LABEL: vec_sink_add_of_const_to_sub: +define <4 x i32> @vec_sink_sub_of_const_to_add1(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_sub_of_const_to_add1: ; X32: # %bb.0: ; X32-NEXT: paddd %xmm1, %xmm0 -; X32-NEXT: paddd {{\.LCPI.*}}, %xmm0 -; X32-NEXT: psubd %xmm2, %xmm0 +; X32-NEXT: psubd {{\.LCPI.*}}, %xmm0 +; X32-NEXT: paddd %xmm2, %xmm0 ; X32-NEXT: retl ; -; X64-LABEL: vec_sink_add_of_const_to_sub: +; X64-LABEL: vec_sink_sub_of_const_to_add1: ; X64: # %bb.0: ; X64-NEXT: paddd %xmm1, %xmm0 -; X64-NEXT: paddd {{.*}}(%rip), %xmm0 -; X64-NEXT: psubd %xmm2, %xmm0 +; X64-NEXT: psubd {{.*}}(%rip), %xmm0 +; X64-NEXT: paddd %xmm2, %xmm0 ; X64-NEXT: retq %t0 = add <4 x i32> %a, %b - %t1 = add <4 x i32> %t0, - %r = sub <4 x i32> %t1, %c + %t1 = sub <4 x i32> %t0, + %r = add <4 x i32> %c, %t1 ret <4 x i32> %r } -define <4 x i32> @vec_sink_sub_of_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { -; ALL-LABEL: vec_sink_sub_of_const_to_sub2: + +; add (sub C, %x), %y +; Outer 'add' is commutative - 2 variants. + +define <4 x i32> @vec_sink_sub_from_const_to_add0(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; ALL-LABEL: vec_sink_sub_from_const_to_add0: ; ALL: # %bb.0: ; ALL-NEXT: paddd %xmm1, %xmm0 -; ALL-NEXT: movdqa {{.*#+}} xmm1 = <93,u,45,81> +; ALL-NEXT: movdqa {{.*#+}} xmm1 = <42,24,u,46> ; ALL-NEXT: psubd %xmm0, %xmm1 ; ALL-NEXT: paddd %xmm2, %xmm1 ; ALL-NEXT: movdqa %xmm1, %xmm0 ; ALL-NEXT: ret{{[l|q]}} %t0 = add <4 x i32> %a, %b - %t1 = sub <4 x i32> %t0, - %r = sub <4 x i32> %c, %t1 + %t1 = sub <4 x i32> , %t0 + %r = add <4 x i32> %t1, %c + ret <4 x i32> %r +} +define <4 x i32> @vec_sink_sub_from_const_to_add1(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; ALL-LABEL: vec_sink_sub_from_const_to_add1: +; ALL: # %bb.0: +; ALL-NEXT: paddd %xmm1, %xmm0 +; ALL-NEXT: movdqa {{.*#+}} xmm1 = <42,24,u,46> +; ALL-NEXT: psubd %xmm0, %xmm1 +; ALL-NEXT: paddd %xmm2, %xmm1 +; ALL-NEXT: movdqa %xmm1, %xmm0 +; ALL-NEXT: ret{{[l|q]}} + %t0 = add <4 x i32> %a, %b + %t1 = sub <4 x i32> , %t0 + %r = add <4 x i32> %c, %t1 ret <4 x i32> %r } +; sub (add %x, C), %y +; sub %y, (add %x, C) + +define <4 x i32> @vec_sink_add_of_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_add_of_const_to_sub: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: paddd {{\.LCPI.*}}, %xmm0 +; X32-NEXT: psubd %xmm2, %xmm0 +; X32-NEXT: retl +; +; X64-LABEL: vec_sink_add_of_const_to_sub: +; X64: # %bb.0: +; X64-NEXT: psubd %xmm1, %xmm0 +; X64-NEXT: paddd {{.*}}(%rip), %xmm0 +; X64-NEXT: psubd %xmm2, %xmm0 +; X64-NEXT: retq + %t0 = sub <4 x i32> %a, %b + %t1 = add <4 x i32> %t0, ; constant always on RHS + %r = sub <4 x i32> %t1, %c + ret <4 x i32> %r +} define <4 x i32> @vec_sink_add_of_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { ; X32-LABEL: vec_sink_add_of_const_to_sub2: ; X32: # %bb.0: -; X32-NEXT: paddd %xmm1, %xmm0 +; X32-NEXT: psubd %xmm1, %xmm0 ; X32-NEXT: paddd {{\.LCPI.*}}, %xmm0 ; X32-NEXT: psubd %xmm0, %xmm2 ; X32-NEXT: movdqa %xmm2, %xmm0 @@ -227,32 +442,101 @@ ; ; X64-LABEL: vec_sink_add_of_const_to_sub2: ; X64: # %bb.0: -; X64-NEXT: paddd %xmm1, %xmm0 +; X64-NEXT: psubd %xmm1, %xmm0 ; X64-NEXT: paddd {{.*}}(%rip), %xmm0 ; X64-NEXT: psubd %xmm0, %xmm2 ; X64-NEXT: movdqa %xmm2, %xmm0 ; X64-NEXT: retq - %t0 = add <4 x i32> %a, %b - %t1 = add <4 x i32> %t0, + %t0 = sub <4 x i32> %a, %b + %t1 = add <4 x i32> %t0, ; constant always on RHS %r = sub <4 x i32> %c, %t1 ret <4 x i32> %r } + +; sub (sub %x, C), %y +; sub %y, (sub %x, C) + define <4 x i32> @vec_sink_sub_of_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { ; X32-LABEL: vec_sink_sub_of_const_to_sub: ; X32: # %bb.0: -; X32-NEXT: paddd %xmm1, %xmm0 +; X32-NEXT: psubd %xmm1, %xmm0 ; X32-NEXT: psubd {{\.LCPI.*}}, %xmm0 ; X32-NEXT: psubd %xmm2, %xmm0 ; X32-NEXT: retl ; ; X64-LABEL: vec_sink_sub_of_const_to_sub: ; X64: # %bb.0: -; X64-NEXT: paddd %xmm1, %xmm0 +; X64-NEXT: psubd %xmm1, %xmm0 ; X64-NEXT: psubd {{.*}}(%rip), %xmm0 ; X64-NEXT: psubd %xmm2, %xmm0 ; X64-NEXT: retq - %t0 = add <4 x i32> %a, %b - %t1 = sub <4 x i32> %t0, + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> %t0, %r = sub <4 x i32> %t1, %c ret <4 x i32> %r } +define <4 x i32> @vec_sink_sub_of_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_sub_of_const_to_sub2: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm0, %xmm1 +; X32-NEXT: paddd %xmm2, %xmm1 +; X32-NEXT: paddd {{\.LCPI.*}}, %xmm1 +; X32-NEXT: movdqa %xmm1, %xmm0 +; X32-NEXT: retl +; +; X64-LABEL: vec_sink_sub_of_const_to_sub2: +; X64: # %bb.0: +; X64-NEXT: psubd %xmm0, %xmm1 +; X64-NEXT: paddd %xmm2, %xmm1 +; X64-NEXT: paddd {{.*}}(%rip), %xmm1 +; X64-NEXT: movdqa %xmm1, %xmm0 +; X64-NEXT: retq + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> %t0, + %r = sub <4 x i32> %c, %t1 + ret <4 x i32> %r +} + +; sub (sub C, %x), %y +; sub %y, (sub C, %x) + +define <4 x i32> @vec_sink_sub_from_const_to_sub(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_sub_from_const_to_sub: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm0, %xmm1 +; X32-NEXT: paddd {{\.LCPI.*}}, %xmm1 +; X32-NEXT: psubd %xmm2, %xmm1 +; X32-NEXT: movdqa %xmm1, %xmm0 +; X32-NEXT: retl +; +; X64-LABEL: vec_sink_sub_from_const_to_sub: +; X64: # %bb.0: +; X64-NEXT: psubd %xmm0, %xmm1 +; X64-NEXT: paddd {{.*}}(%rip), %xmm1 +; X64-NEXT: psubd %xmm2, %xmm1 +; X64-NEXT: movdqa %xmm1, %xmm0 +; X64-NEXT: retq + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> , %t0 + %r = sub <4 x i32> %t1, %c + ret <4 x i32> %r +} +define <4 x i32> @vec_sink_sub_from_const_to_sub2(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c) { +; X32-LABEL: vec_sink_sub_from_const_to_sub2: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: psubd {{\.LCPI.*}}, %xmm0 +; X32-NEXT: paddd %xmm2, %xmm0 +; X32-NEXT: retl +; +; X64-LABEL: vec_sink_sub_from_const_to_sub2: +; X64: # %bb.0: +; X64-NEXT: psubd %xmm1, %xmm0 +; X64-NEXT: psubd {{.*}}(%rip), %xmm0 +; X64-NEXT: paddd %xmm2, %xmm0 +; X64-NEXT: retq + %t0 = sub <4 x i32> %a, %b + %t1 = sub <4 x i32> , %t0 + %r = sub <4 x i32> %c, %t1 + ret <4 x i32> %r +} diff --git a/llvm/test/CodeGen/X86/xor.ll b/llvm/test/CodeGen/X86/xor.ll --- a/llvm/test/CodeGen/X86/xor.ll +++ b/llvm/test/CodeGen/X86/xor.ll @@ -528,3 +528,162 @@ ret i32 %load } +define i32 @add_of_not(i32 %x, i32 %y) { +; X32-LABEL: add_of_not: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: decl %eax +; X32-NEXT: retl +; +; X64-LIN-LABEL: add_of_not: +; X64-LIN: # %bb.0: +; X64-LIN-NEXT: # kill: def $edi killed $edi def $rdi +; X64-LIN-NEXT: subl %esi, %edi +; X64-LIN-NEXT: leal -1(%rdi), %eax +; X64-LIN-NEXT: retq +; +; X64-WIN-LABEL: add_of_not: +; X64-WIN: # %bb.0: +; X64-WIN-NEXT: # kill: def $ecx killed $ecx def $rcx +; X64-WIN-NEXT: subl %edx, %ecx +; X64-WIN-NEXT: leal -1(%rcx), %eax +; X64-WIN-NEXT: retq + %t0 = sub i32 %x, %y + %r = add i32 %t0, -1 + ret i32 %r +} + +define i32 @add_of_not_decrement(i32 %x, i32 %y) { +; X32-LABEL: add_of_not_decrement: +; X32: # %bb.0: +; X32-NEXT: movl {{[0-9]+}}(%esp), %eax +; X32-NEXT: subl {{[0-9]+}}(%esp), %eax +; X32-NEXT: decl %eax +; X32-NEXT: retl +; +; X64-LIN-LABEL: add_of_not_decrement: +; X64-LIN: # %bb.0: +; X64-LIN-NEXT: # kill: def $edi killed $edi def $rdi +; X64-LIN-NEXT: subl %esi, %edi +; X64-LIN-NEXT: leal -1(%rdi), %eax +; X64-LIN-NEXT: retq +; +; X64-WIN-LABEL: add_of_not_decrement: +; X64-WIN: # %bb.0: +; X64-WIN-NEXT: # kill: def $ecx killed $ecx def $rcx +; X64-WIN-NEXT: subl %edx, %ecx +; X64-WIN-NEXT: leal -1(%rcx), %eax +; X64-WIN-NEXT: retq + %t0 = sub i32 %x, %y + %r = sub i32 %t0, 1 + ret i32 %r +} + +define <4 x i32> @vec_add_of_not(<4 x i32> %x, <4 x i32> %y) { +; X32-LABEL: vec_add_of_not: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: pcmpeqd %xmm1, %xmm1 +; X32-NEXT: paddd %xmm1, %xmm0 +; X32-NEXT: retl +; +; X64-LIN-LABEL: vec_add_of_not: +; X64-LIN: # %bb.0: +; X64-LIN-NEXT: psubd %xmm1, %xmm0 +; X64-LIN-NEXT: pcmpeqd %xmm1, %xmm1 +; X64-LIN-NEXT: paddd %xmm1, %xmm0 +; X64-LIN-NEXT: retq +; +; X64-WIN-LABEL: vec_add_of_not: +; X64-WIN: # %bb.0: +; X64-WIN-NEXT: movdqa (%rcx), %xmm1 +; X64-WIN-NEXT: psubd (%rdx), %xmm1 +; X64-WIN-NEXT: pcmpeqd %xmm0, %xmm0 +; X64-WIN-NEXT: paddd %xmm1, %xmm0 +; X64-WIN-NEXT: retq + %t0 = sub <4 x i32> %x, %y + %r = add <4 x i32> %t0, + ret <4 x i32> %r +} + +define <4 x i32> @vec_add_of_not_decrement(<4 x i32> %x, <4 x i32> %y) { +; X32-LABEL: vec_add_of_not_decrement: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: pcmpeqd %xmm1, %xmm1 +; X32-NEXT: paddd %xmm1, %xmm0 +; X32-NEXT: retl +; +; X64-LIN-LABEL: vec_add_of_not_decrement: +; X64-LIN: # %bb.0: +; X64-LIN-NEXT: psubd %xmm1, %xmm0 +; X64-LIN-NEXT: pcmpeqd %xmm1, %xmm1 +; X64-LIN-NEXT: paddd %xmm1, %xmm0 +; X64-LIN-NEXT: retq +; +; X64-WIN-LABEL: vec_add_of_not_decrement: +; X64-WIN: # %bb.0: +; X64-WIN-NEXT: movdqa (%rcx), %xmm1 +; X64-WIN-NEXT: psubd (%rdx), %xmm1 +; X64-WIN-NEXT: pcmpeqd %xmm0, %xmm0 +; X64-WIN-NEXT: paddd %xmm1, %xmm0 +; X64-WIN-NEXT: retq + %t0 = sub <4 x i32> %x, %y + %r = sub <4 x i32> %t0, + ret <4 x i32> %r +} + +define <4 x i32> @vec_add_of_not_with_undef(<4 x i32> %x, <4 x i32> %y) { +; X32-LABEL: vec_add_of_not_with_undef: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: pcmpeqd %xmm1, %xmm1 +; X32-NEXT: paddd %xmm1, %xmm0 +; X32-NEXT: retl +; +; X64-LIN-LABEL: vec_add_of_not_with_undef: +; X64-LIN: # %bb.0: +; X64-LIN-NEXT: psubd %xmm1, %xmm0 +; X64-LIN-NEXT: pcmpeqd %xmm1, %xmm1 +; X64-LIN-NEXT: paddd %xmm1, %xmm0 +; X64-LIN-NEXT: retq +; +; X64-WIN-LABEL: vec_add_of_not_with_undef: +; X64-WIN: # %bb.0: +; X64-WIN-NEXT: movdqa (%rcx), %xmm1 +; X64-WIN-NEXT: psubd (%rdx), %xmm1 +; X64-WIN-NEXT: pcmpeqd %xmm0, %xmm0 +; X64-WIN-NEXT: paddd %xmm1, %xmm0 +; X64-WIN-NEXT: retq + %t0 = sub <4 x i32> %x, %y + %r = add <4 x i32> %t0, + ret <4 x i32> %r +} + +define <4 x i32> @vec_add_of_not_with_undef_decrement(<4 x i32> %x, <4 x i32> %y) { +; X32-LABEL: vec_add_of_not_with_undef_decrement: +; X32: # %bb.0: +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: pcmpeqd %xmm1, %xmm1 +; X32-NEXT: psubd %xmm1, %xmm0 +; X32-NEXT: retl +; +; X64-LIN-LABEL: vec_add_of_not_with_undef_decrement: +; X64-LIN: # %bb.0: +; X64-LIN-NEXT: psubd %xmm1, %xmm0 +; X64-LIN-NEXT: pcmpeqd %xmm1, %xmm1 +; X64-LIN-NEXT: psubd %xmm1, %xmm0 +; X64-LIN-NEXT: retq +; +; X64-WIN-LABEL: vec_add_of_not_with_undef_decrement: +; X64-WIN: # %bb.0: +; X64-WIN-NEXT: movdqa (%rcx), %xmm0 +; X64-WIN-NEXT: psubd (%rdx), %xmm0 +; X64-WIN-NEXT: pcmpeqd %xmm1, %xmm1 +; X64-WIN-NEXT: psubd %xmm1, %xmm0 +; X64-WIN-NEXT: retq + %t0 = sub <4 x i32> %x, %y + %r = add <4 x i32> %t0, + ret <4 x i32> %r +} diff --git a/llvm/test/Demangle/invalid-manglings.test b/llvm/test/Demangle/invalid-manglings.test --- a/llvm/test/Demangle/invalid-manglings.test +++ b/llvm/test/Demangle/invalid-manglings.test @@ -169,3 +169,8 @@ ; CHECK-EMPTY: ; CHECK-NEXT: ??_C@_12@?z ; CHECK-NEXT: error: Invalid mangled name + +??$foo@$1??_C@_02PCEFGMJL@hi?$AA@@ +; CHECK-EMPTY: +; CHECK-NEXT: ??$foo@$1??_C@_02PCEFGMJL@hi?$AA@@ +; CHECK-NEXT: error: Invalid mangled name diff --git a/llvm/test/Transforms/InstCombine/fast-math.ll b/llvm/test/Transforms/InstCombine/fast-math.ll --- a/llvm/test/Transforms/InstCombine/fast-math.ll +++ b/llvm/test/Transforms/InstCombine/fast-math.ll @@ -820,7 +820,7 @@ define float @max1(float %a, float %b) { ; CHECK-LABEL: @max1( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp fast ogt float [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], float [[A]], float [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select fast i1 [[TMP1]], float [[A]], float [[B]] ; CHECK-NEXT: ret float [[TMP2]] ; %c = fpext float %a to double @@ -833,7 +833,7 @@ define float @max2(float %a, float %b) { ; CHECK-LABEL: @max2( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp nnan nsz ogt float [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], float [[A]], float [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select nnan nsz i1 [[TMP1]], float [[A]], float [[B]] ; CHECK-NEXT: ret float [[TMP2]] ; %c = call nnan float @fmaxf(float %a, float %b) @@ -844,7 +844,7 @@ define double @max3(double %a, double %b) { ; CHECK-LABEL: @max3( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp fast ogt double [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], double [[A]], double [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select fast i1 [[TMP1]], double [[A]], double [[B]] ; CHECK-NEXT: ret double [[TMP2]] ; %c = call fast double @fmax(double %a, double %b) @@ -854,7 +854,7 @@ define fp128 @max4(fp128 %a, fp128 %b) { ; CHECK-LABEL: @max4( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp nnan nsz ogt fp128 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], fp128 [[A]], fp128 [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select nnan nsz i1 [[TMP1]], fp128 [[A]], fp128 [[B]] ; CHECK-NEXT: ret fp128 [[TMP2]] ; %c = call nnan fp128 @fmaxl(fp128 %a, fp128 %b) @@ -865,7 +865,7 @@ define float @min1(float %a, float %b) { ; CHECK-LABEL: @min1( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp nnan nsz olt float [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], float [[A]], float [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select nnan nsz i1 [[TMP1]], float [[A]], float [[B]] ; CHECK-NEXT: ret float [[TMP2]] ; %c = fpext float %a to double @@ -878,7 +878,7 @@ define float @min2(float %a, float %b) { ; CHECK-LABEL: @min2( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp fast olt float [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], float [[A]], float [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select fast i1 [[TMP1]], float [[A]], float [[B]] ; CHECK-NEXT: ret float [[TMP2]] ; %c = call fast float @fminf(float %a, float %b) @@ -888,7 +888,7 @@ define double @min3(double %a, double %b) { ; CHECK-LABEL: @min3( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp nnan nsz olt double [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], double [[A]], double [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select nnan nsz i1 [[TMP1]], double [[A]], double [[B]] ; CHECK-NEXT: ret double [[TMP2]] ; %c = call nnan double @fmin(double %a, double %b) @@ -898,7 +898,7 @@ define fp128 @min4(fp128 %a, fp128 %b) { ; CHECK-LABEL: @min4( ; CHECK-NEXT: [[TMP1:%.*]] = fcmp fast olt fp128 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], fp128 [[A]], fp128 [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = select fast i1 [[TMP1]], fp128 [[A]], fp128 [[B]] ; CHECK-NEXT: ret fp128 [[TMP2]] ; %c = call fast fp128 @fminl(fp128 %a, fp128 %b) diff --git a/llvm/test/Transforms/InstCombine/pow-sqrt.ll b/llvm/test/Transforms/InstCombine/pow-sqrt.ll --- a/llvm/test/Transforms/InstCombine/pow-sqrt.ll +++ b/llvm/test/Transforms/InstCombine/pow-sqrt.ll @@ -36,7 +36,7 @@ ; CHECK-NEXT: [[SQRT:%.*]] = call afn double @sqrt(double [[X:%.*]]) ; CHECK-NEXT: [[ABS:%.*]] = call afn double @llvm.fabs.f64(double [[SQRT]]) ; CHECK-NEXT: [[ISINF:%.*]] = fcmp afn oeq double [[X]], 0xFFF0000000000000 -; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[ISINF]], double 0x7FF0000000000000, double [[ABS]] +; CHECK-NEXT: [[TMP1:%.*]] = select afn i1 [[ISINF]], double 0x7FF0000000000000, double [[ABS]] ; CHECK-NEXT: ret double [[TMP1]] ; %pow = call afn double @pow(double %x, double 5.0e-01) @@ -48,7 +48,7 @@ ; CHECK-NEXT: [[SQRT:%.*]] = call afn <2 x double> @llvm.sqrt.v2f64(<2 x double> [[X:%.*]]) ; CHECK-NEXT: [[ABS:%.*]] = call afn <2 x double> @llvm.fabs.v2f64(<2 x double> [[SQRT]]) ; CHECK-NEXT: [[ISINF:%.*]] = fcmp afn oeq <2 x double> [[X]], -; CHECK-NEXT: [[TMP1:%.*]] = select <2 x i1> [[ISINF]], <2 x double> , <2 x double> [[ABS]] +; CHECK-NEXT: [[TMP1:%.*]] = select afn <2 x i1> [[ISINF]], <2 x double> , <2 x double> [[ABS]] ; CHECK-NEXT: ret <2 x double> [[TMP1]] ; %pow = call afn <2 x double> @llvm.pow.v2f64(<2 x double> %x, <2 x double> ) @@ -92,7 +92,7 @@ ; CHECK-LABEL: @pow_libcall_half_nsz( ; CHECK-NEXT: [[SQRT:%.*]] = call nsz double @sqrt(double [[X:%.*]]) ; CHECK-NEXT: [[ISINF:%.*]] = fcmp nsz oeq double [[X]], 0xFFF0000000000000 -; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[ISINF]], double 0x7FF0000000000000, double [[SQRT]] +; CHECK-NEXT: [[TMP1:%.*]] = select nsz i1 [[ISINF]], double 0x7FF0000000000000, double [[SQRT]] ; CHECK-NEXT: ret double [[TMP1]] ; %pow = call nsz double @pow(double %x, double 5.0e-01) @@ -103,7 +103,7 @@ ; CHECK-LABEL: @pow_intrinsic_half_nsz( ; CHECK-NEXT: [[SQRT:%.*]] = call nsz double @llvm.sqrt.f64(double [[X:%.*]]) ; CHECK-NEXT: [[ISINF:%.*]] = fcmp nsz oeq double [[X]], 0xFFF0000000000000 -; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[ISINF]], double 0x7FF0000000000000, double [[SQRT]] +; CHECK-NEXT: [[TMP1:%.*]] = select nsz i1 [[ISINF]], double 0x7FF0000000000000, double [[SQRT]] ; CHECK-NEXT: ret double [[TMP1]] ; %pow = call nsz double @llvm.pow.f64(double %x, double 5.0e-01) diff --git a/llvm/test/Transforms/LoopVectorize/float-minmax-instruction-flag.ll b/llvm/test/Transforms/LoopVectorize/float-minmax-instruction-flag.ll --- a/llvm/test/Transforms/LoopVectorize/float-minmax-instruction-flag.ll +++ b/llvm/test/Transforms/LoopVectorize/float-minmax-instruction-flag.ll @@ -74,10 +74,10 @@ ; CHECK: middle.block: ; CHECK-NEXT: [[RDX_SHUF:%.*]] = shufflevector <4 x float> [[TMP6]], <4 x float> undef, <4 x i32> ; CHECK-NEXT: [[RDX_MINMAX_CMP:%.*]] = fcmp fast olt <4 x float> [[TMP6]], [[RDX_SHUF]] -; CHECK-NEXT: [[RDX_MINMAX_SELECT:%.*]] = select <4 x i1> [[RDX_MINMAX_CMP]], <4 x float> [[TMP6]], <4 x float> [[RDX_SHUF]] +; CHECK-NEXT: [[RDX_MINMAX_SELECT:%.*]] = select fast <4 x i1> [[RDX_MINMAX_CMP]], <4 x float> [[TMP6]], <4 x float> [[RDX_SHUF]] ; CHECK-NEXT: [[RDX_SHUF1:%.*]] = shufflevector <4 x float> [[RDX_MINMAX_SELECT]], <4 x float> undef, <4 x i32> ; CHECK-NEXT: [[RDX_MINMAX_CMP2:%.*]] = fcmp fast olt <4 x float> [[RDX_MINMAX_SELECT]], [[RDX_SHUF1]] -; CHECK-NEXT: [[RDX_MINMAX_SELECT3:%.*]] = select <4 x i1> [[RDX_MINMAX_CMP2]], <4 x float> [[RDX_MINMAX_SELECT]], <4 x float> [[RDX_SHUF1]] +; CHECK-NEXT: [[RDX_MINMAX_SELECT3:%.*]] = select fast <4 x i1> [[RDX_MINMAX_CMP2]], <4 x float> [[RDX_MINMAX_SELECT]], <4 x float> [[RDX_SHUF1]] ; CHECK-NEXT: [[TMP8:%.*]] = extractelement <4 x float> [[RDX_MINMAX_SELECT3]], i32 0 ; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 65536, 65536 ; CHECK-NEXT: br i1 [[CMP_N]], label [[OUT:%.*]], label [[SCALAR_PH]] diff --git a/llvm/test/Transforms/LoopVectorize/minmax_reduction.ll b/llvm/test/Transforms/LoopVectorize/minmax_reduction.ll --- a/llvm/test/Transforms/LoopVectorize/minmax_reduction.ll +++ b/llvm/test/Transforms/LoopVectorize/minmax_reduction.ll @@ -416,7 +416,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @max_red_float(float %max) #0 { entry: @@ -442,7 +442,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @max_red_float_ge(float %max) #0 { entry: @@ -468,7 +468,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_max_red_float(float %max) #0 { entry: @@ -494,7 +494,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_max_red_float_le(float %max) #0 { entry: @@ -520,7 +520,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @unordered_max_red_float(float %max) #0 { entry: @@ -546,7 +546,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @unordered_max_red_float_ge(float %max) #0 { entry: @@ -572,7 +572,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_unordered_max_red_float(float %max) #0 { entry: @@ -598,7 +598,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast ogt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_unordered_max_red_float_le(float %max) #0 { entry: @@ -627,7 +627,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @min_red_float(float %min) #0 { entry: @@ -653,7 +653,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @min_red_float_le(float %min) #0 { entry: @@ -679,7 +679,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_min_red_float(float %min) #0 { entry: @@ -705,7 +705,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_min_red_float_ge(float %min) #0 { entry: @@ -731,7 +731,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @unordered_min_red_float(float %min) #0 { entry: @@ -757,7 +757,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @unordered_min_red_float_le(float %min) #0 { entry: @@ -783,7 +783,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_unordered_min_red_float(float %min) #0 { entry: @@ -809,7 +809,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x float> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define float @inverted_unordered_min_red_float_ge(float %min) #0 { entry: @@ -836,7 +836,7 @@ ; CHECK: select <2 x i1> ; CHECK: middle.block ; CHECK: fcmp fast olt <2 x double> -; CHECK: select <2 x i1> +; CHECK: select fast <2 x i1> define double @min_red_double(double %min) #0 { entry: diff --git a/llvm/test/tools/llvm-objdump/X86/section-filter-relocs.test b/llvm/test/tools/llvm-objdump/X86/section-filter-relocs.test --- a/llvm/test/tools/llvm-objdump/X86/section-filter-relocs.test +++ b/llvm/test/tools/llvm-objdump/X86/section-filter-relocs.test @@ -1,7 +1,4 @@ ## Test that --section works correctly for -d with -r. -## FIXME: Inline relocations are only printed if the relocation section itself is -## specified with --section. This test just characterizes the existing behavior. -## See https://bugs.llvm.org/show_bug.cgi?id=41886 # RUN: yaml2obj %s -o %t.o ## Show non-executable sections are not disassembled even if specified, @@ -12,21 +9,15 @@ ## executable sections if requested explicitly. ## See https://bugs.llvm.org/show_bug.cgi?id=41897. # RUN: llvm-objdump -d -r %t.o --section=.text --section=.rodata \ -# RUN: | FileCheck %s --check-prefix=DISASM --implicit-check-not=.text2 \ -# RUN: --implicit-check-not=.rodata --implicit-check-not=R_X86_64 - -## Show that only the specified relocation sections that patch the -## disassembled sections are dumped. -# RUN: llvm-objdump -d -r %t.o --section=.text \ -# RUN: --section=.rela.text --section=.rela.text2 \ # RUN: | FileCheck %s --check-prefixes=DISASM,RELOC --implicit-check-not=.text2 \ -# RUN: --implicit-check-not=R_X86_64 +# RUN: --implicit-check-not=.rodata # DISASM: Disassembly of section .text: # DISASM-EMPTY: # DISASM-NEXT: 0000000000000400 .text: # DISASM-NEXT: 400: e8 00 00 00 00 callq 0 <.text+0x5> # RELOC-NEXT: 00000401: R_X86_64_PC32 foo+1 +# RELOC-NEXT: 00000401: R_X86_64_GOT32 foo --- !ELF FileHeader: diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -334,18 +334,18 @@ typedef std::vector> SectionSymbolsTy; +static bool shouldKeep(object::SectionRef S) { + if (FilterSections.empty()) + return true; + StringRef String; + std::error_code error = S.getName(String); + if (error) + return false; + return is_contained(FilterSections, String); +} + SectionFilter ToolSectionFilter(object::ObjectFile const &O) { - return SectionFilter( - [](object::SectionRef const &S) { - if (FilterSections.empty()) - return true; - StringRef String; - std::error_code error = S.getName(String); - if (error) - return false; - return is_contained(FilterSections, String); - }, - O); + return SectionFilter([](object::SectionRef S) { return shouldKeep(S); }, O); } void error(std::error_code EC) { @@ -922,15 +922,15 @@ static std::map> getRelocsMap(object::ObjectFile const &Obj) { std::map> Ret; - for (const SectionRef &Section : ToolSectionFilter(Obj)) { - section_iterator RelSec = Section.getRelocatedSection(); - if (RelSec == Obj.section_end()) + for (SectionRef Sec : Obj.sections()) { + section_iterator Relocated = Sec.getRelocatedSection(); + if (Relocated == Obj.section_end() || !shouldKeep(*Relocated)) continue; - std::vector &V = Ret[*RelSec]; - for (const RelocationRef &R : Section.relocations()) + std::vector &V = Ret[*Relocated]; + for (const RelocationRef &R : Sec.relocations()) V.push_back(R); // Sort relocations by address. - llvm::sort(V, isRelocAddressLess); + llvm::stable_sort(V, isRelocAddressLess); } return Ret; } diff --git a/openmp/runtime/src/kmp_runtime.cpp b/openmp/runtime/src/kmp_runtime.cpp --- a/openmp/runtime/src/kmp_runtime.cpp +++ b/openmp/runtime/src/kmp_runtime.cpp @@ -4302,11 +4302,6 @@ KMP_ATOMIC_DEC(&__kmp_thread_pool_active_nth); new_thr->th.th_active_in_pool = FALSE; } -#if KMP_DEBUG - else { - KMP_DEBUG_ASSERT(new_thr->th.th_active == FALSE); - } -#endif __kmp_unlock_suspend_mx(new_thr); KA_TRACE(20, ("__kmp_allocate_thread: T#%d using thread T#%d\n",