1

rG7a5cb15ea6fa

 1 year ago
source link: https://reviews.llvm.org/rG7a5cb15ea6fa
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[RISCV] Lazily add RVV C intrinsics.
Authored by kito-cheng on Jul 13 2022, 12:52 AM.
  • Restricted Project
  • Restricted Project
Subscribers

Description

[RISCV] Lazily add RVV C intrinsics.

Leverage the method OpenCL uses that adds C intrinsics when the lookup
failed. There is no need to define C intrinsics in the header file any
more. It could help to avoid the large header file to speed up the
compilation of RVV source code. Besides that, only the C intrinsics used
by the users will be added into the declaration table.

This patch is based on https://reviews.llvm.org/D103228 and inspired by
OpenCL implementation.

Experimental Results

TL;DR:
  • Binary size of clang increase ~200k, which is +0.07% for debug build and +0.13% for release build.
  • Single file compilation speed up ~33x for debug build and ~8.5x for release build
  • Regression time reduce ~10% (ninja check-all, enable all targets)
Header size change
       |      size |     LoC |
------------------------------
Before | 4,434,725 |  69,749 |
After  |     6,140 |     162 |
Single File Compilation Time

Testcase:

#include <riscv_vector.h>

vint32m1_t test_vadd_vv_vfloat32m1_t(vint32m1_t op1, vint32m1_t op2, size_t vl) {
  return vadd(op1, op2, vl);
}
Debug build:

Before:

real    0m19.352s
user    0m19.252s
sys     0m0.092s

After:

real    0m0.576s
user    0m0.552s
sys     0m0.024s

~33x speed up for debug build

Release build:

Before:

real    0m0.773s
user    0m0.741s
sys     0m0.032s

After:

real    0m0.092s
user    0m0.080s
sys     0m0.012s

~8.5x speed up for release build

Regression time

Note: the failed case is tools/llvm-debuginfod-find/debuginfod.test which is unrelated to this patch.

Debug build

Before:

Testing Time: 1358.38s
  Skipped          :    11
  Unsupported      :   446
  Passed           : 75767
  Expectedly Failed:   190
  Failed           :     1

After

Testing Time: 1220.29s
  Skipped          :    11
  Unsupported      :   446
  Passed           : 75767
  Expectedly Failed:   190
  Failed           :     1
Release build

Before:

Testing Time: 381.98s
  Skipped          :    12
  Unsupported      :  1407
  Passed           : 74765
  Expectedly Failed:   176
  Failed           :     1

After:

Testing Time: 346.25s
  Skipped          :    12
  Unsupported      :  1407
  Passed           : 74765
  Expectedly Failed:   176
  Failed           :     1
Binary size of clang
Debug build

Before

   text    data     bss     dec     hex filename
335261851       12726004         552812 348540667       14c64efb        bin/clang

After

   text    data     bss     dec     hex filename
335442803       12798708         552940 348794451       14ca2e53        bin/clang

+253K, +0.07% code size

Release build

Before

   text    data     bss     dec     hex filename
144123975       8374648  483140 152981763       91e5103 bin/clang

After

   text    data     bss     dec     hex filename
144255762       8447296  483268 153186326       9217016 bin/clang

+204K, +0.13%

Authored-by: Kito Cheng <[email protected]>
Co-Authored-by: Hsiangkai Wang <[email protected]>

Reviewed By: khchen, aaron.ballman

Differential Revision: https://reviews.llvm.org/D111617

rG7a5cb15ea6fa

clang/include/clang/Basic/CMakeLists.txt

Show First 20 LinesShow All 84 Lines▼ Show 20 Lines
SOURCE arm_cde.td
TARGET ClangARMCdeBuiltinAliases)
clang_tablegen(riscv_vector_builtins.inc -gen-riscv-vector-builtins
SOURCE riscv_vector.td
TARGET ClangRISCVVectorBuiltins)
clang_tablegen(riscv_vector_builtin_cg.inc -gen-riscv-vector-builtin-codegen
SOURCE riscv_vector.td
TARGET ClangRISCVVectorBuiltinCG)
+ clang_tablegen(riscv_vector_builtin_sema.inc -gen-riscv-vector-builtin-sema
+ SOURCE riscv_vector.td
+ TARGET ClangRISCVVectorBuiltinSema)

clang/include/clang/Basic/TokenKinds.def

Show First 20 LinesShow All 902 Lines▼ Show 20 Lines
// handles #pragma loop ... directives.
PRAGMA_ANNOTATION(pragma_loop_hint)
PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)
+ // Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
+ PRAGMA_ANNOTATION(pragma_riscv)
+
// Annotations for module import translated from #include etc.
ANNOTATION(module_include)
ANNOTATION(module_begin)
ANNOTATION(module_end)
// Annotation for a header_name token that has been looked up and transformed
// into the name of a header unit.
ANNOTATION(header_unit)
Show All 22 Lines

clang/include/clang/Parse/Parser.h

Show First 20 LinesShow All 209 Lines▼ Show 20 Lines
std::unique_ptr<PragmaHandler> FPHandler;
std::unique_ptr<PragmaHandler> STDCFenvAccessHandler;
std::unique_ptr<PragmaHandler> STDCFenvRoundHandler;
std::unique_ptr<PragmaHandler> STDCCXLIMITHandler;
std::unique_ptr<PragmaHandler> STDCUnknownHandler;
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+ std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
std::unique_ptr<CommentHandler> CommentSemaHandler;
/// Whether the '>' token acts as an operator or not. This will be
/// true except when we are parsing an expression within a C++
/// template argument list, where the '>' closes the template
/// argument list.
bool GreaterThanIsOperator;
▲ Show 20 LinesShow All 3,280 LinesShow Last 20 Lines

clang/include/clang/Sema/RISCVIntrinsicManager.h

  • This file was added.
+ //===- RISCVIntrinsicManager.h - RISC-V Intrinsic Handler -------*- 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
+ //
+ //===----------------------------------------------------------------------===//
+ //
+ // This file defines the RISCVIntrinsicManager, which handles RISC-V vector
+ // intrinsic functions.
+ //
+ //===----------------------------------------------------------------------===//
+
+ #ifndef LLVM_CLANG_SEMA_RISCVINTRINSICMANAGER_H
+ #define LLVM_CLANG_SEMA_RISCVINTRINSICMANAGER_H
+
+ namespace clang {
+ class Sema;
+ class LookupResult;
+ class IdentifierInfo;
+ class Preprocessor;
+
+ namespace sema {
+ class RISCVIntrinsicManager {
+ public:
+ virtual ~RISCVIntrinsicManager() = default;
+
+ // Create RISC-V intrinsic and insert into symbol table and return true if
+ // found, otherwise return false.
+ virtual bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II,
+ Preprocessor &PP) = 0;
+ };
+ } // end namespace sema
+ } // end namespace clang
+
+ #endif

clang/include/clang/Sema/Sema.h

  • This file is larger than 256 KB, so syntax highlighting is disabled by default.
Show First 20 LinesShow All 220 Lines▼ Show 20 Lines
class CapturedRegionScopeInfo;
class CapturingScopeInfo;
class CompoundScopeInfo;
class DelayedDiagnostic;
class DelayedDiagnosticPool;
class FunctionScopeInfo;
class LambdaScopeInfo;
class PossiblyUnreachableDiag;
+ class RISCVIntrinsicManager;
class SemaPPCallbacks;
class TemplateDeductionInfo;
}
namespace threadSafety {
class BeforeSet;
void threadSafetyCleanup(BeforeSet* Cache);
}
▲ Show 20 LinesShow All 1,345 Lines▼ Show 20 Lines
bool WarnedStackExhausted = false;
/// Increment when we find a reference; decrement when we find an ignored
/// assignment. Ultimately the value is 0 if every reference is an ignored
/// assignment.
llvm::DenseMap<const VarDecl *, int> RefsMinusAssignments;
+ /// Indicate RISC-V vector builtin functions enabled or not.
+ bool DeclareRISCVVBuiltins = false;
+
private:
+ std::unique_ptr<sema::RISCVIntrinsicManager> RVIntrinsicManager;
+
Optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
bool WarnedDarwinSDKInfoMissing = false;
public:
Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TranslationUnitKind TUKind = TU_Complete,
CodeCompleteConsumer *CompletionConsumer = nullptr);
▲ Show 20 LinesShow All 11,986 Lines▼ Show 20 Lines
};
template <>
void Sema::PragmaStack<Sema::AlignPackInfo>::Act(SourceLocation PragmaLocation,
PragmaMsStackAction Action,
llvm::StringRef StackSlotLabel,
AlignPackInfo Value);
+ std::unique_ptr<sema::RISCVIntrinsicManager>
+ CreateRISCVIntrinsicManager(Sema &S);
} // end namespace clang
namespace llvm {
// Hash a FunctionDeclAndLoc by looking at both its FunctionDecl and its
// SourceLocation.
template <> struct DenseMapInfo<clang::Sema::FunctionDeclAndLoc> {
using FunctionDeclAndLoc = clang::Sema::FunctionDeclAndLoc;
using FDBaseInfo = DenseMapInfo<clang::CanonicalDeclPtr<clang::FunctionDecl>>;
Show All 22 Lines

clang/include/clang/Support/RISCVVIntrinsicUtils.h

Show All 12 Lines
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <cstdint>
#include <string>
#include <vector>
+ namespace llvm {
+ class raw_ostream;
+ } // end namespace llvm
+
namespace clang {
namespace RISCV {
using VScaleVal = llvm::Optional<unsigned>;
// Modifier for vector type.
enum class VectorTypeModifier : uint8_t {
NoModifier,
▲ Show 20 LinesShow All 70 Lines▼ Show 20 Lines
constexpr PrototypeDescriptor(uint8_t PT, uint8_t VTM, uint8_t TM)
: PT(PT), VTM(VTM), TM(TM) {}
uint8_t PT = static_cast<uint8_t>(BaseTypeModifier::Invalid);
uint8_t VTM = static_cast<uint8_t>(VectorTypeModifier::NoModifier);
uint8_t TM = static_cast<uint8_t>(TypeModifier::NoModifier);
bool operator!=(const PrototypeDescriptor &PD) const {
- return PD.PT != PT || PD.VTM != VTM || PD.TM != TM;
+ return !(*this == PD);
}
- bool operator>(const PrototypeDescriptor &PD) const {
- return !(PD.PT <= PT && PD.VTM <= VTM && PD.TM <= TM);
+ bool operator==(const PrototypeDescriptor &PD) const {
+ return PD.PT == PT && PD.VTM == VTM && PD.TM == TM;
+ }
+ bool operator<(const PrototypeDescriptor &PD) const {
+ return std::tie(PT, VTM, TM) < std::tie(PD.PT, PD.VTM, PD.TM);
}
-
static const PrototypeDescriptor Mask;
static const PrototypeDescriptor Vector;
static const PrototypeDescriptor VL;
static llvm::Optional<PrototypeDescriptor>
parsePrototypeDescriptor(llvm::StringRef PrototypeStr);
};
llvm::SmallVector<PrototypeDescriptor>
▲ Show 20 LinesShow All 98 Lines▼ Show 20 Lines
return ScalarType == ScalarTypeKind::SignedInteger;
}
bool isFloatVector(unsigned Width) const {
return isVector() && isFloat() && ElementBitwidth == Width;
}
bool isFloat(unsigned Width) const {
return isFloat() && ElementBitwidth == Width;
}
-
+ bool isConstant() const { return IsConstant; }
bool isPointer() const { return IsPointer; }
+ unsigned getElementBitwidth() const { return ElementBitwidth; }
+
+ ScalarTypeKind getScalarType() const { return ScalarType; }
+ VScaleVal getScale() const { return Scale; }
private:
// Verify RVV vector type and set Valid.
bool verifyType() const;
// Creates a type based on basic types of TypeRange
void applyBasicType();
Show All 21 Lines
/// have illegal RVVType.
static llvm::Optional<RVVTypes>
computeTypes(BasicType BT, int Log2LMUL, unsigned NF,
llvm::ArrayRef<PrototypeDescriptor> Prototype);
static llvm::Optional<RVVTypePtr> computeType(BasicType BT, int Log2LMUL,
PrototypeDescriptor Proto);
};
- using RISCVPredefinedMacroT = uint8_t;
-
- enum RISCVPredefinedMacro : RISCVPredefinedMacroT {
- Basic = 0,
- V = 1 << 1,
- Zvfh = 1 << 2,
- RV64 = 1 << 3,
- VectorMaxELen64 = 1 << 4,
- VectorMaxELenFp32 = 1 << 5,
- VectorMaxELenFp64 = 1 << 6,
- };
-
enum PolicyScheme : uint8_t {
SchemeNone,
HasPassthruOperand,
HasPolicyOperand,
};
// TODO refactor RVVIntrinsic class design after support all intrinsic
// combination. This represents an instantiation of an intrinsic with a
Show All 11 Lines
bool HasUnMaskedOverloaded;
bool HasBuiltinAlias;
std::string ManualCodegen;
RVVTypePtr OutputType; // Builtin output type
RVVTypes InputTypes; // Builtin input types
// The types we use to obtain the specific LLVM intrinsic. They are index of
// InputTypes. -1 means the return type.
std::vector<int64_t> IntrinsicTypes;
- RISCVPredefinedMacroT RISCVPredefinedMacros = 0;
unsigned NF = 1;
public:
RVVIntrinsic(llvm::StringRef Name, llvm::StringRef Suffix,
llvm::StringRef OverloadedName, llvm::StringRef OverloadedSuffix,
llvm::StringRef IRName, bool IsMasked, bool HasMaskedOffOperand,
bool HasVL, PolicyScheme Scheme, bool HasUnMaskedOverloaded,
bool HasBuiltinAlias, llvm::StringRef ManualCodegen,
Show All 14 Lines
bool hasPolicyOperand() const { return Scheme == HasPolicyOperand; }
bool hasUnMaskedOverloaded() const { return HasUnMaskedOverloaded; }
bool hasBuiltinAlias() const { return HasBuiltinAlias; }
bool hasManualCodegen() const { return !ManualCodegen.empty(); }
bool isMasked() const { return IsMasked; }
llvm::StringRef getIRName() const { return IRName; }
llvm::StringRef getManualCodegen() const { return ManualCodegen; }
PolicyScheme getPolicyScheme() const { return Scheme; }
- RISCVPredefinedMacroT getRISCVPredefinedMacros() const {
- return RISCVPredefinedMacros;
- }
unsigned getNF() const { return NF; }
const std::vector<int64_t> &getIntrinsicTypes() const {
return IntrinsicTypes;
}
// Return the type string for a BUILTIN() macro in Builtins.def.
std::string getBuiltinTypeStr() const;
static std::string
getSuffixStr(BasicType Type, int Log2LMUL,
llvm::ArrayRef<PrototypeDescriptor> PrototypeDescriptors);
};
+ // RVVRequire should be sync'ed with target features, but only
+ // required features used in riscv_vector.td.
+ enum RVVRequire : uint8_t {
+ RVV_REQ_None = 0,
+ RVV_REQ_RV64 = 1 << 0,
+ RVV_REQ_FullMultiply = 1 << 1,
+
+ LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_FullMultiply)
+ };
+
+ // Raw RVV intrinsic info, used to expand later.
+ // This struct is highly compact for minimized code size.
+ struct RVVIntrinsicRecord {
+ // Intrinsic name, e.g. vadd_vv
+ const char *Name;
+
+ // Overloaded intrinsic name, could be empty if it can be computed from Name.
+ // e.g. vadd
+ const char *OverloadedName;
+
+ // Prototype for this intrinsic, index of RVVSignatureTable.
+ uint16_t PrototypeIndex;
+
+ // Prototype for masked intrinsic, index of RVVSignatureTable.
+ uint16_t MaskedPrototypeIndex;
+
+ // Suffix of intrinsic name, index of RVVSignatureTable.
+ uint16_t SuffixIndex;
+
+ // Suffix of overloaded intrinsic name, index of RVVSignatureTable.
+ uint16_t OverloadedSuffixIndex;
+
+ // Length of the prototype.
+ uint8_t PrototypeLength;
+
+ // Length of prototype of masked intrinsic.
+ uint8_t MaskedPrototypeLength;
+
+ // Length of intrinsic name suffix.
+ uint8_t SuffixLength;
+
+ // Length of overloaded intrinsic suffix.
+ uint8_t OverloadedSuffixSize;
+
+ // Required target features for this intrinsic.
+ uint8_t RequiredExtensions;
+
+ // Supported type, mask of BasicType.
+ uint8_t TypeRangeMask;
+
+ // Supported LMUL.
+ uint8_t Log2LMULMask;
+
+ // Number of fields, greater than 1 if it's segment load/store.
+ uint8_t NF;
+ };
+
+ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const RVVIntrinsicRecord &RVVInstrRecord);
+
+ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
} // end namespace RISCV
} // end namespace clang
#endif // CLANG_SUPPORT_RISCVVINTRINSICUTILS_H

clang/lib/Parse/ParsePragma.cpp

Show First 20 LinesShow All 344 Lines▼ Show 20 Lines
};
struct PragmaMaxTokensTotalHandler : public PragmaHandler {
PragmaMaxTokensTotalHandler() : PragmaHandler("max_tokens_total") {}
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
Token &FirstToken) override;
};
+ struct PragmaRISCVHandler : public PragmaHandler {
+ PragmaRISCVHandler(Sema &Actions)
+ : PragmaHandler("riscv"), Actions(Actions) {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+
+ private:
+ Sema &Actions;
+ };
+
void markAsReinjectedForRelexing(llvm::MutableArrayRef<clang::Token> Toks) {
for (auto &T : Toks)
T.setFlag(clang::Token::IsReinjected);
}
} // end namespace
void Parser::initializePragmaHandlers() {
AlignHandler = std::make_unique<PragmaAlignHandler>();
▲ Show 20 LinesShow All 127 Lines▼ Show 20 Lines
std::make_unique<PragmaAttributeHandler>(AttrFactory);
PP.AddPragmaHandler("clang", AttributePragmaHandler.get());
MaxTokensHerePragmaHandler = std::make_unique<PragmaMaxTokensHereHandler>();
PP.AddPragmaHandler("clang", MaxTokensHerePragmaHandler.get());
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+
+ if (getTargetInfo().getTriple().isRISCV()) {
+ RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
+ PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
+ }
}
void Parser::resetPragmaHandlers() {
// Remove the pragma handlers we installed.
PP.RemovePragmaHandler(AlignHandler.get());
AlignHandler.reset();
PP.RemovePragmaHandler("GCC", GCCVisibilityHandler.get());
GCCVisibilityHandler.reset();
▲ Show 20 LinesShow All 108 Lines▼ Show 20 Lines
PP.RemovePragmaHandler("clang", AttributePragmaHandler.get());
AttributePragmaHandler.reset();
PP.RemovePragmaHandler("clang", MaxTokensHerePragmaHandler.get());
MaxTokensHerePragmaHandler.reset();
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
MaxTokensTotalPragmaHandler.reset();
+
+ if (getTargetInfo().getTriple().isRISCV()) {
+ PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
+ RISCVPragmaHandler.reset();
+ }
}
/// Handle the annotation token produced for #pragma unused(...)
///
/// Each annot_pragma_unused is followed by the argument token so e.g.
/// "#pragma unused(x,y)" becomes:
/// annot_pragma_unused 'x' annot_pragma_unused 'y'
void Parser::HandlePragmaUnused() {
▲ Show 20 LinesShow All 3,296 Lines▼ Show 20 Lines
if (Tok.isNot(tok::eod)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
<< "clang max_tokens_total";
return;
}
PP.overrideMaxTokens(MaxTokens, Loc);
}
+
+ // Handle '#pragma clang riscv intrinsic vector'.
+ void PragmaRISCVHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstToken) {
+ Token Tok;
+ PP.Lex(Tok);
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+
+ if (!II || !II->isStr("intrinsic")) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
+ << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'intrinsic'";
+ return;
+ }
+
+ PP.Lex(Tok);
+ II = Tok.getIdentifierInfo();
+ if (!II || !II->isStr("vector")) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
+ << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'vector'";
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang riscv intrinsic";
+ return;
+ }
+
+ Actions.DeclareRISCVVBuiltins = true;
+ }

clang/lib/Sema/CMakeLists.txt

Show First 20 LinesShow All 46 Lines▼ Show 20 Lines
SemaInit.cpp
SemaLambda.cpp
SemaLookup.cpp
SemaModule.cpp
SemaObjCProperty.cpp
SemaOpenMP.cpp
SemaOverload.cpp
SemaPseudoObject.cpp
+ SemaRISCVVectorLookup.cpp
SemaStmt.cpp
SemaStmtAsm.cpp
SemaStmtAttr.cpp
SemaSYCL.cpp
SemaTemplate.cpp
SemaTemplateDeduction.cpp
SemaTemplateInstantiate.cpp
SemaTemplateInstantiateDecl.cpp
SemaTemplateVariadic.cpp
SemaType.cpp
TypeLocBuilder.cpp
DEPENDS
ClangOpenCLBuiltinsImpl
omp_gen
LINK_LIBS
clangAST
clangAnalysis
clangBasic
clangEdit
clangLex
+ clangSupport
)

clang/lib/Sema/Sema.cpp

Show All 31 Lines
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/CXXFieldCollector.h"
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/MultiplexExternalSemaSource.h"
#include "clang/Sema/ObjCMethodList.h"
+ #include "clang/Sema/RISCVIntrinsicManager.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaConsumer.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/TemplateInstCallback.h"
#include "clang/Sema/TypoCorrection.h"
#include "llvm/ADT/DenseMap.h"
▲ Show 20 LinesShow All 2,613 LinesShow Last 20 Lines

clang/lib/Sema/SemaLookup.cpp

Show All 23 Lines
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
+ #include "clang/Sema/RISCVIntrinsicManager.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/TypoCorrection.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
▲ Show 20 LinesShow All 883 Lines▼ Show 20 Lines
auto Index = isOpenCLBuiltin(II->getName());
if (Index.first) {
InsertOCLBuiltinDeclarationsFromTable(*this, R, II, Index.first - 1,
Index.second);
return true;
}
}
+ if (DeclareRISCVVBuiltins) {
+ if (!RVIntrinsicManager)
+ RVIntrinsicManager = CreateRISCVIntrinsicManager(*this);
+
+ if (RVIntrinsicManager->CreateIntrinsicIfFound(R, II, PP))
+ return true;
+ }
+
// If this is a builtin on this (or all) targets, create the decl.
if (unsigned BuiltinID = II->getBuiltinID()) {
// In C++, C2x, and OpenCL (spec v1.2 s6.9.f), we don't have any
// predefined library functions like 'malloc'. Instead, we'll just
// error.
if ((getLangOpts().CPlusPlus || getLangOpts().OpenCL ||
getLangOpts().C2x) &&
Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID))
▲ Show 20 LinesShow All 4,891 LinesShow Last 20 Lines

clang/lib/Sema/SemaRISCVVectorLookup.cpp

  • This file was added.
+ //==- SemaRISCVVectorLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -==//
+ //
+ // 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
+ //
+ //===----------------------------------------------------------------------===//
+ //
+ // This file implements name lookup for RISC-V vector intrinsic.
+ //
+ //===----------------------------------------------------------------------===//
+
+ #include "clang/AST/ASTContext.h"
+ #include "clang/AST/Decl.h"
+ #include "clang/Basic/Builtins.h"
+ #include "clang/Basic/TargetInfo.h"
+ #include "clang/Lex/Preprocessor.h"
+ #include "clang/Sema/Lookup.h"
+ #include "clang/Sema/RISCVIntrinsicManager.h"
+ #include "clang/Sema/Sema.h"
+ #include "clang/Support/RISCVVIntrinsicUtils.h"
+ #include "llvm/ADT/SmallVector.h"
+ #include <string>
+ #include <vector>
+
+ using namespace llvm;
+ using namespace clang;
+ using namespace clang::RISCV;
+
+ namespace {
+
+ // Function definition of a RVV intrinsic.
+ struct RVVIntrinsicDef {
+ /// Full function name with suffix, e.g. vadd_vv_i32m1.
+ std::string Name;
+
+ /// Overloaded function name, e.g. vadd.
+ std::string OverloadName;
+
+ /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd.
+ std::string BuiltinName;
+
+ /// Function signature, first element is return type.
+ RVVTypes Signature;
+ };
+
+ struct RVVOverloadIntrinsicDef {
+ // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList.
+ SmallVector<size_t, 8> Indexes;
+ };
+
+ } // namespace
+
+ static const PrototypeDescriptor RVVSignatureTable[] = {
+ #define DECL_SIGNATURE_TABLE
+ #include "clang/Basic/riscv_vector_builtin_sema.inc"
+ #undef DECL_SIGNATURE_TABLE
+ };
+
+ static const RVVIntrinsicRecord RVVIntrinsicRecords[] = {
+ #define DECL_INTRINSIC_RECORDS
+ #include "clang/Basic/riscv_vector_builtin_sema.inc"
+ #undef DECL_INTRINSIC_RECORDS
+ };
+
+ // Get subsequence of signature table.
+ static ArrayRef<PrototypeDescriptor> ProtoSeq2ArrayRef(uint16_t Index,
+ uint8_t Length) {
+ return makeArrayRef(&RVVSignatureTable[Index], Length);
+ }
+
+ static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type) {
+ QualType QT;
+ switch (Type->getScalarType()) {
+ case ScalarTypeKind::Void:
+ QT = Context.VoidTy;
+ break;
+ case ScalarTypeKind::Size_t:
+ QT = Context.getSizeType();
+ break;
+ case ScalarTypeKind::Ptrdiff_t:
+ QT = Context.getPointerDiffType();
+ break;
+ case ScalarTypeKind::UnsignedLong:
+ QT = Context.UnsignedLongTy;
+ break;
+ case ScalarTypeKind::SignedLong:
+ QT = Context.LongTy;
+ break;
+ case ScalarTypeKind::Boolean:
+ QT = Context.BoolTy;
+ break;
+ case ScalarTypeKind::SignedInteger:
+ QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), true);
+ break;
+ case ScalarTypeKind::UnsignedInteger:
+ QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), false);
+ break;
+ case ScalarTypeKind::Float:
+ switch (Type->getElementBitwidth()) {
+ case 64:
+ QT = Context.DoubleTy;
+ break;
+ case 32:
+ QT = Context.FloatTy;
+ break;
+ case 16:
+ QT = Context.Float16Ty;
+ break;
+ default:
+ llvm_unreachable("Unsupported floating point width.");
+ }
+ break;
+ case Invalid:
+ llvm_unreachable("Unhandled type.");
+ }
+ if (Type->isVector())
+ QT = Context.getScalableVectorType(QT, Type->getScale().getValue());
+
+ if (Type->isConstant())
+ QT = Context.getConstType(QT);
+
+ // Transform the type to a pointer as the last step, if necessary.
+ if (Type->isPointer())
+ QT = Context.getPointerType(QT);
+
+ return QT;
+ }
+
+ namespace {
+ class RISCVIntrinsicManagerImpl : public sema::RISCVIntrinsicManager {
+ private:
+ Sema &S;
+ ASTContext &Context;
+
+ // List of all RVV intrinsic.
+ std::vector<RVVIntrinsicDef> IntrinsicList;
+ // Mapping function name to index of IntrinsicList.
+ StringMap<size_t> Intrinsics;
+ // Mapping function name to RVVOverloadIntrinsicDef.
+ StringMap<RVVOverloadIntrinsicDef> OverloadIntrinsics;
+
+ // Create IntrinsicList
+ void InitIntrinsicList();
+
+ // Create RVVIntrinsicDef.
+ void InitRVVIntrinsic(const RVVIntrinsicRecord &Record, StringRef SuffixStr,
+ StringRef OverloadedSuffixStr, bool IsMask,
+ RVVTypes &Types);
+
+ // Create FunctionDecl for a vector intrinsic.
+ void CreateRVVIntrinsicDecl(LookupResult &LR, IdentifierInfo *II,
+ Preprocessor &PP, unsigned Index,
+ bool IsOverload);
+
+ public:
+ RISCVIntrinsicManagerImpl(clang::Sema &S) : S(S), Context(S.Context) {
+ InitIntrinsicList();
+ }
+
+ // Create RISC-V vector intrinsic and insert into symbol table if found, and
+ // return true, otherwise return false.
+ bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II,
+ Preprocessor &PP) override;
+ };
+ } // namespace
+
+ void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
+ const TargetInfo &TI = Context.getTargetInfo();
+ bool HasVectorFloat32 = TI.hasFeature("zve32f");
+ bool HasVectorFloat64 = TI.hasFeature("zve64d");
+ bool HasZvfh = TI.hasFeature("experimental-zvfh");
+ bool HasRV64 = TI.hasFeature("64bit");
+ bool HasFullMultiply = TI.hasFeature("v");
+
+ // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
+ // in RISCVVEmitter.cpp.
+ for (auto &Record : RVVIntrinsicRecords) {
+ // Create Intrinsics for each type and LMUL.
+ BasicType BaseType = BasicType::Unknown;
+ ArrayRef<PrototypeDescriptor> ProtoSeq =
+ ProtoSeq2ArrayRef(Record.PrototypeIndex, Record.PrototypeLength);
+ ArrayRef<PrototypeDescriptor> ProtoMaskSeq = ProtoSeq2ArrayRef(
+ Record.MaskedPrototypeIndex, Record.MaskedPrototypeLength);
+ ArrayRef<PrototypeDescriptor> SuffixProto =
+ ProtoSeq2ArrayRef(Record.SuffixIndex, Record.SuffixLength);
+ ArrayRef<PrototypeDescriptor> OverloadedSuffixProto = ProtoSeq2ArrayRef(
+ Record.OverloadedSuffixIndex, Record.OverloadedSuffixSize);
+ for (unsigned int TypeRangeMaskShift = 0;
+ TypeRangeMaskShift <= static_cast<unsigned int>(BasicType::MaxOffset);
+ ++TypeRangeMaskShift) {
+ unsigned int BaseTypeI = 1 << TypeRangeMaskShift;
+ BaseType = static_cast<BasicType>(BaseTypeI);
+
+ if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI)
+ continue;
+
+ // Check requirement.
+ if (BaseType == BasicType::Float16 && !HasZvfh)
+ continue;
+
+ if (BaseType == BasicType::Float32 && !HasVectorFloat32)
+ continue;
+
+ if (BaseType == BasicType::Float64 && !HasVectorFloat64)
+ continue;
+
+ if (((Record.RequiredExtensions & RVV_REQ_RV64) == RVV_REQ_RV64) &&
+ !HasRV64)
+ continue;
+
+ if ((BaseType == BasicType::Int64) &&
+ ((Record.RequiredExtensions & RVV_REQ_FullMultiply) ==
+ RVV_REQ_FullMultiply) &&
+ !HasFullMultiply)
+ continue;
+
+ // Expanded with different LMUL.
+ for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) {
+ if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3))))
+ continue;
+
+ Optional<RVVTypes> Types =
+ RVVType::computeTypes(BaseType, Log2LMUL, Record.NF, ProtoSeq);
+
+ // Ignored to create new intrinsic if there are any illegal types.
+ if (!Types.hasValue())
+ continue;
+
+ std::string SuffixStr =
+ RVVIntrinsic::getSuffixStr(BaseType, Log2LMUL, SuffixProto);
+ std::string OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
+ BaseType, Log2LMUL, OverloadedSuffixProto);
+
+ // Create non-masked intrinsic.
+ InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, false, *Types);
+
+ if (Record.MaskedPrototypeLength != 0) {
+ // Create masked intrinsic.
+ Optional<RVVTypes> MaskTypes = RVVType::computeTypes(
+ BaseType, Log2LMUL, Record.NF, ProtoMaskSeq);
+
+ InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, true,
+ *MaskTypes);
+ }
+ }
+ }
+ }
+ }
+
+ // Compute name and signatures for intrinsic with practical types.
+ void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
+ const RVVIntrinsicRecord &Record, StringRef SuffixStr,
+ StringRef OverloadedSuffixStr, bool IsMask, RVVTypes &Signature) {
+ // Function name, e.g. vadd_vv_i32m1.
+ std::string Name = Record.Name;
+ if (!SuffixStr.empty())
+ Name += "_" + SuffixStr.str();
+
+ if (IsMask)
+ Name += "_m";
+
+ // Overloaded function name, e.g. vadd.
+ std::string OverloadedName;
+ if (!Record.OverloadedName)
+ OverloadedName = StringRef(Record.Name).split("_").first.str();
+ else
+ OverloadedName = Record.OverloadedName;
+ if (!OverloadedSuffixStr.empty())
+ OverloadedName += "_" + OverloadedSuffixStr.str();
+
+ // clang built-in function name, e.g. __builtin_rvv_vadd.
+ std::string BuiltinName = "__builtin_rvv_" + std::string(Record.Name);
+ if (IsMask)
+ BuiltinName += "_m";
+
+ // Put into IntrinsicList.
+ size_t Index = IntrinsicList.size();
+ IntrinsicList.push_back({Name, OverloadedName, BuiltinName, Signature});
+
+ // Creating mapping to Intrinsics.
+ Intrinsics.insert({Name, Index});
+
+ // Get the RVVOverloadIntrinsicDef.
+ RVVOverloadIntrinsicDef &OverloadIntrinsicDef =
+ OverloadIntrinsics[OverloadedName];
+
+ // And added the index.
+ OverloadIntrinsicDef.Indexes.push_back(Index);
+ }
+
+ void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult &LR,
+ IdentifierInfo *II,
+ Preprocessor &PP,
+ unsigned Index,
+ bool IsOverload) {
+ ASTContext &Context = S.Context;
+ RVVIntrinsicDef &IDef = IntrinsicList[Index];
+ RVVTypes Sigs = IDef.Signature;
+ size_t SigLength = Sigs.size();
+ RVVType *ReturnType = Sigs[0];
+ QualType RetType = RVVType2Qual(Context, ReturnType);
+ SmallVector<QualType, 8> ArgTypes;
+ QualType BuiltinFuncType;
+
+ // Skip return type, and convert RVVType to QualType for arguments.
+ for (size_t i = 1; i < SigLength; ++i)
+ ArgTypes.push_back(RVVType2Qual(Context, Sigs[i]));
+
+ FunctionProtoType::ExtProtoInfo PI(
+ Context.getDefaultCallingConvention(false, false, true));
+
+ PI.Variadic = false;
+
+ SourceLocation Loc = LR.getNameLoc();
+ BuiltinFuncType = Context.getFunctionType(RetType, ArgTypes, PI);
+ DeclContext *Parent = Context.getTranslationUnitDecl();
+
+ FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create(
+ Context, Parent, Loc, Loc, II, BuiltinFuncType, /*TInfo=*/nullptr,
+ SC_Extern, S.getCurFPFeatures().isFPConstrained(),
+ /*isInlineSpecified*/ false,
+ /*hasWrittenPrototype*/ true);
+
+ // Create Decl objects for each parameter, adding them to the
+ // FunctionDecl.
+ const auto *FP = cast<FunctionProtoType>(BuiltinFuncType);
+ SmallVector<ParmVarDecl *, 8> ParmList;
+ for (unsigned IParm = 0, E = FP->getNumParams(); IParm != E; ++IParm) {
+ ParmVarDecl *Parm =
+ ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr,
+ FP->getParamType(IParm), nullptr, SC_None, nullptr);
+ Parm->setScopeInfo(0, IParm);
+ ParmList.push_back(Parm);
+ }
+ RVVIntrinsicDecl->setParams(ParmList);
+
+ // Add function attributes.
+ if (IsOverload)
+ RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context));
+
+ // Setup alias to __builtin_rvv_*
+ IdentifierInfo &IntrinsicII = PP.getIdentifierTable().get(IDef.BuiltinName);
+ RVVIntrinsicDecl->addAttr(
+ BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII));
+
+ // Add to symbol table.
+ LR.addDecl(RVVIntrinsicDecl);
+ }
+
+ bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult &LR,
+ IdentifierInfo *II,
+ Preprocessor &PP) {
+ StringRef Name = II->getName();
+
+ // Lookup the function name from the overload intrinsics first.
+ auto OvIItr = OverloadIntrinsics.find(Name);
+ if (OvIItr != OverloadIntrinsics.end()) {
+ const RVVOverloadIntrinsicDef &OvIntrinsicDef = OvIItr->second;
+ for (auto Index : OvIntrinsicDef.Indexes)
+ CreateRVVIntrinsicDecl(LR, II, PP, Index,
+ /*IsOverload*/ true);
+
+ // If we added overloads, need to resolve the lookup result.
+ LR.resolveKind();
+ return true;
+ }
+
+ // Lookup the function name from the intrinsics.
+ auto Itr = Intrinsics.find(Name);
+ if (Itr != Intrinsics.end()) {
+ CreateRVVIntrinsicDecl(LR, II, PP, Itr->second,
+ /*IsOverload*/ false);
+ return true;
+ }
+
+ // It's not an RVV intrinsics.
+ return false;
+ }
+
+ namespace clang {
+ std::unique_ptr<clang::sema::RISCVIntrinsicManager>
+ CreateRISCVIntrinsicManager(Sema &S) {
+ return std::make_unique<RISCVIntrinsicManagerImpl>(S);
+ }
+ } // namespace clang

clang/lib/Support/RISCVVIntrinsicUtils.cpp

Show First 20 LinesShow All 867 Lines▼ Show 20 Lines
Name += "_" + Suffix.str();
if (!OverloadedSuffix.empty())
OverloadedName += "_" + OverloadedSuffix.str();
if (IsMasked) {
BuiltinName += "_m";
Name += "_m";
}
- // Init RISC-V extensions
- for (const auto &T : OutInTypes) {
- if (T->isFloatVector(16) || T->isFloat(16))
- RISCVPredefinedMacros |= RISCVPredefinedMacro::Zvfh;
- if (T->isFloatVector(32))
- RISCVPredefinedMacros |= RISCVPredefinedMacro::VectorMaxELenFp32;
- if (T->isFloatVector(64))
- RISCVPredefinedMacros |= RISCVPredefinedMacro::VectorMaxELenFp64;
- if (T->isVector(64))
- RISCVPredefinedMacros |= RISCVPredefinedMacro::VectorMaxELen64;
- }
- for (auto Feature : RequiredFeatures) {
- if (Feature == "RV64")
- RISCVPredefinedMacros |= RISCVPredefinedMacro::RV64;
- // Note: Full multiply instruction (mulh, mulhu, mulhsu, smul) for EEW=64
- // require V.
- if (Feature == "FullMultiply" &&
- (RISCVPredefinedMacros & RISCVPredefinedMacro::VectorMaxELen64))
- RISCVPredefinedMacros |= RISCVPredefinedMacro::V;
- }
-
// Init OutputType and InputTypes
OutputType = OutInTypes[0];
InputTypes.assign(OutInTypes.begin() + 1, OutInTypes.end());
// IntrinsicTypes is unmasked TA version index. Need to update it
// if there is merge operand (It is always in first operand).
IntrinsicTypes = NewIntrinsicTypes;
if ((IsMasked && HasMaskedOffOperand) ||
▲ Show 20 LinesShow All 41 Lines▼ Show 20 Lines
if (!PD)
llvm_unreachable("Error during parsing prototype.");
PrototypeDescriptors.push_back(*PD);
Prototypes = Prototypes.drop_front(Idx + 1);
}
return PrototypeDescriptors;
}
+ raw_ostream &operator<<(raw_ostream &OS, const RVVIntrinsicRecord &Record) {
+ OS << "{";
+ OS << "\"" << Record.Name << "\",";
+ if (Record.OverloadedName == nullptr ||
+ StringRef(Record.OverloadedName).empty())
+ OS << "nullptr,";
+ else
+ OS << "\"" << Record.OverloadedName << "\",";
+ OS << Record.PrototypeIndex << ",";
+ OS << Record.MaskedPrototypeIndex << ",";
+ OS << Record.SuffixIndex << ",";
+ OS << Record.OverloadedSuffixIndex << ",";
+ OS << (int)Record.PrototypeLength << ",";
+ OS << (int)Record.MaskedPrototypeLength << ",";
+ OS << (int)Record.SuffixLength << ",";
+ OS << (int)Record.OverloadedSuffixSize << ",";
+ OS << (int)Record.RequiredExtensions << ",";
+ OS << (int)Record.TypeRangeMask << ",";
+ OS << (int)Record.Log2LMULMask << ",";
+ OS << (int)Record.NF << ",";
+ OS << "},\n";
+ return OS;
+ }
+
} // end namespace RISCV
} // end namespace clang

clang/test/Sema/riscv-bad-intrinsic-pragma.c

  • This file was added.
+ // RUN: %clang_cc1 -triple riscv64 -target-feature +v %s -emit-llvm -o - \
+ // RUN: 2>&1 | FileCheck %s
+
+ #pragma clang riscv intrinsic vvvv
+ // CHECK: warning: unexpected argument 'vvvv' to '#pragma riscv'; expected 'vector' [-Wignored-pragmas]
+
+ #pragma clang riscv what + 3241
+ // CHECK: warning: unexpected argument 'what' to '#pragma riscv'; expected 'intrinsic' [-Wignored-pragmas]
+ #pragma clang riscv int i = 12;
+ // CHECK: warning: unexpected argument 'int' to '#pragma riscv'; expected 'intrinsic' [-Wignored-pragmas]
+ #pragma clang riscv intrinsic vector bar
+ // CHECK: warning: extra tokens at end of '#pragma clang riscv intrinsic' - ignored [-Wignored-pragmas]
+
+ #define FOO 0
+
+ int main()
+ {
+ return FOO;
+ }
+
+ // Make sure no more warnings
+ // CHECK-NOT: warning:

clang/test/Sema/riscv-intrinsic-pragma.c

  • This file was added.
+ // RUN: %clang_cc1 -triple riscv64 -target-feature +v -emit-llvm -o - -verify %s
+
+ #pragma clang riscv intrinsic vector
+ // expected-no-diagnostics

clang/utils/TableGen/RISCVVEmitter.cpp

Show All 14 Lines
//===----------------------------------------------------------------------===//
#include "clang/Support/RISCVVIntrinsicUtils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
+ #include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include <numeric>
using namespace llvm;
using namespace clang::RISCV;
namespace {
+ struct SemaRecord {
+ // Intrinsic name, e.g. vadd_vv
+ std::string Name;
+
+ // Overloaded intrinsic name, could be empty if can be computed from Name
+ // e.g. vadd
+ std::string OverloadedName;
+
+ // Supported type, mask of BasicType.
+ unsigned TypeRangeMask;
+
+ // Supported LMUL.
+ unsigned Log2LMULMask;
+
+ // Required extensions for this intrinsic.
+ unsigned RequiredExtensions;
+
+ // Prototype for this intrinsic.
+ SmallVector<PrototypeDescriptor> Prototype;
+
+ // Prototype for masked intrinsic.
+ SmallVector<PrototypeDescriptor> MaskedPrototype;
+
+ // Suffix of intrinsic name.
+ SmallVector<PrototypeDescriptor> Suffix;
+
+ // Suffix of overloaded intrinsic name.
+ SmallVector<PrototypeDescriptor> OverloadedSuffix;
+
+ // Number of field, large than 1 if it's segment load/store.
+ unsigned NF;
+ };
+
+ // Compressed function signature table.
+ class SemaSignatureTable {
+ private:
+ std::vector<PrototypeDescriptor> SignatureTable;
+
+ void insert(ArrayRef<PrototypeDescriptor> Signature);
+
+ public:
+ static constexpr unsigned INVALID_INDEX = ~0U;
+
+ // Create compressed signature table from SemaRecords.
+ void init(ArrayRef<SemaRecord> SemaRecords);
+
+ // Query the Signature, return INVALID_INDEX if not found.
+ unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature);
+
+ /// Print signature table in RVVHeader Record to \p OS
+ void print(raw_ostream &OS);
+ };
+
class RVVEmitter {
private:
RecordKeeper &Records;
public:
RVVEmitter(RecordKeeper &R) : Records(R) {}
/// Emit riscv_vector.h
void createHeader(raw_ostream &o);
/// Emit all the __builtin prototypes and code needed by Sema.
void createBuiltins(raw_ostream &o);
/// Emit all the information needed to map builtin -> LLVM IR intrinsic.
void createCodeGen(raw_ostream &o);
+ /// Emit all the information needed by SemaRISCVVectorLookup.cpp.
+ /// We've large number of intrinsic function for RVV, creating a customized
+ /// could speed up the compilation time.
+ void createSema(raw_ostream &o);
+
private:
- /// Create all intrinsics and add them to \p Out
- void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out);
+ /// Create all intrinsics and add them to \p Out and SemaRecords.
+ void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
+ std::vector<SemaRecord> *SemaRecords = nullptr);
+ /// Create all intrinsic records and SemaSignatureTable from SemaRecords.
+ void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
+ SemaSignatureTable &SST,
+ ArrayRef<SemaRecord> SemaRecords);
+
/// Print HeaderCode in RVVHeader Record to \p Out
void printHeaderCode(raw_ostream &OS);
-
- /// Emit Acrh predecessor definitions and body, assume the element of Defs are
- /// sorted by extension.
- void emitArchMacroAndBody(
- std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &o,
- std::function<void(raw_ostream &, const RVVIntrinsic &)>);
-
- // Emit the architecture preprocessor definitions. Return true when emits
- // non-empty string.
- bool emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros,
- raw_ostream &o);
};
} // namespace
static BasicType ParseBasicType(char c) {
switch (c) {
case 'c':
return BasicType::Int8;
▲ Show 20 LinesShow All 74 Lines▼ Show 20 Lines
// VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is
// always last operand.
if (RVVI->hasVL())
OS << ", Ops.back()->getType()";
OS << "};\n";
OS << " break;\n";
}
- void emitIntrinsicFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) {
- OS << "__attribute__((__clang_builtin_alias__(";
- OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n";
- OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getName() << "(";
- // Emit function arguments
- const RVVTypes &InputTypes = RVVI.getInputTypes();
- if (!InputTypes.empty()) {
- ListSeparator LS;
- for (unsigned i = 0; i < InputTypes.size(); ++i)
- OS << LS << InputTypes[i]->getTypeStr();
- }
- OS << ");\n";
- }
+ //===----------------------------------------------------------------------===//
+ // SemaSignatureTable implementation
+ //===----------------------------------------------------------------------===//
+ void SemaSignatureTable::init(ArrayRef<SemaRecord> SemaRecords) {
+ // Sort signature entries by length, let longer signature insert first, to
+ // make it more possible to reuse table entries, that can reduce ~10% table
+ // size.
+ struct Compare {
+ bool operator()(const SmallVector<PrototypeDescriptor> &A,
+ const SmallVector<PrototypeDescriptor> &B) const {
+ if (A.size() != B.size())
+ return A.size() > B.size();
+
+ size_t Len = A.size();
+ for (size_t i = 0; i < Len; ++i) {
+ if (A[i] != B[i])
+ return A[i] < B[i];
+ }
+
+ return false;
+ }
+ };
- void emitOverloadedFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) {
- OS << "__attribute__((__clang_builtin_alias__(";
- OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n";
- OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getOverloadedName()
- << "(";
- // Emit function arguments
- const RVVTypes &InputTypes = RVVI.getInputTypes();
- if (!InputTypes.empty()) {
- ListSeparator LS;
- for (unsigned i = 0; i < InputTypes.size(); ++i)
- OS << LS << InputTypes[i]->getTypeStr();
+ std::set<SmallVector<PrototypeDescriptor>, Compare> Signatures;
+ auto InsertToSignatureSet =
+ [&](const SmallVector<PrototypeDescriptor> &Signature) {
+ if (Signature.empty())
+ return;
+
+ Signatures.insert(Signature);
+ };
+
+ assert(!SemaRecords.empty());
+
+ llvm::for_each(SemaRecords, [&](const SemaRecord &SR) {
+ InsertToSignatureSet(SR.Prototype);
+ InsertToSignatureSet(SR.MaskedPrototype);
+ InsertToSignatureSet(SR.Suffix);
+ InsertToSignatureSet(SR.OverloadedSuffix);
+ });
+
+ llvm::for_each(Signatures, [this](auto &Sig) { insert(Sig); });
+ }
+
+ void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> Signature) {
+ if (getIndex(Signature) != INVALID_INDEX)
+ return;
+
+ // Insert Signature into SignatureTable if not found in the table.
+ SignatureTable.insert(SignatureTable.begin(), Signature.begin(),
+ Signature.end());
+ }
+
+ unsigned SemaSignatureTable::getIndex(ArrayRef<PrototypeDescriptor> Signature) {
+ // Empty signature could be point into any index since there is length
+ // field when we use, so just always point it to 0.
+ if (Signature.empty())
+ return 0;
+
+ // Checking Signature already in table or not.
+ if (Signature.size() < SignatureTable.size()) {
+ size_t Bound = SignatureTable.size() - Signature.size() + 1;
+ for (size_t Index = 0; Index < Bound; ++Index) {
+ if (equal(Signature.begin(), Signature.end(),
+ SignatureTable.begin() + Index))
+ return Index;
+ }
}
- OS << ");\n";
+
+ return INVALID_INDEX;
+ }
+
+ void SemaSignatureTable::print(raw_ostream &OS) {
+ for (const auto &Sig : SignatureTable)
+ OS << "PrototypeDescriptor(" << static_cast<int>(Sig.PT) << ", "
+ << static_cast<int>(Sig.VTM) << ", " << static_cast<int>(Sig.TM)
+ << "),\n";
}
//===----------------------------------------------------------------------===//
// RVVEmitter implementation
//===----------------------------------------------------------------------===//
void RVVEmitter::createHeader(raw_ostream &OS) {
OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics "
Show All 18 Lines
OS << "#ifndef __riscv_vector\n";
OS << "#error \"Vector intrinsics require the vector extension.\"\n";
OS << "#endif\n\n";
OS << "#ifdef __cplusplus\n";
OS << "extern \"C\" {\n";
OS << "#endif\n\n";
- printHeaderCode(OS);
+ OS << "#pragma clang riscv intrinsic vector\n\n";
- std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
- createRVVIntrinsics(Defs);
+ printHeaderCode(OS);
auto printType = [&](auto T) {
OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr()
<< ";\n";
};
constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3};
// Print RVV boolean types.
Show All 23 Lines
for (int Log2LMUL : Log2LMULs) {
auto T = RVVType::computeType(BasicType::Float16, Log2LMUL,
PrototypeDescriptor::Vector);
if (T)
printType(T.value());
}
OS << "#endif\n";
- OS << "#if defined(__riscv_f)\n";
+ OS << "#if (__riscv_v_elen_fp >= 32)\n";
for (int Log2LMUL : Log2LMULs) {
auto T = RVVType::computeType(BasicType::Float32, Log2LMUL,
PrototypeDescriptor::Vector);
if (T)
printType(T.value());
}
OS << "#endif\n";
- OS << "#if defined(__riscv_d)\n";
+ OS << "#if (__riscv_v_elen_fp >= 64)\n";
for (int Log2LMUL : Log2LMULs) {
auto T = RVVType::computeType(BasicType::Float64, Log2LMUL,
PrototypeDescriptor::Vector);
if (T)
printType(T.value());
}
OS << "#endif\n\n";
- // The same extension include in the same arch guard marco.
- llvm::stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A,
- const std::unique_ptr<RVVIntrinsic> &B) {
- return A->getRISCVPredefinedMacros() < B->getRISCVPredefinedMacros();
- });
-
- OS << "#define __rvv_ai static __inline__\n";
-
- // Print intrinsic functions with macro
- emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) {
- OS << "__rvv_ai ";
- emitIntrinsicFuncDef(Inst, OS);
- });
-
- OS << "#undef __rvv_ai\n\n";
-
OS << "#define __riscv_v_intrinsic_overloading 1\n";
- // Print Overloaded APIs
- OS << "#define __rvv_aio static __inline__ "
- "__attribute__((__overloadable__))\n";
-
- emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) {
- if (!Inst.isMasked() && !Inst.hasUnMaskedOverloaded())
- return;
- OS << "__rvv_aio ";
- emitOverloadedFuncDef(Inst, OS);
- });
-
- OS << "#undef __rvv_aio\n";
-
OS << "\n#ifdef __cplusplus\n";
OS << "}\n";
OS << "#endif // __cplusplus\n";
OS << "#endif // __RISCV_VECTOR_H\n";
}
void RVVEmitter::createBuiltins(raw_ostream &OS) {
std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
▲ Show 20 LinesShow All 72 Lines▼ Show 20 Lines
else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes())
PrintFatalError("Builtin with same name has different IntrinsicTypes");
}
emitCodeGenSwitchBody(Defs.back().get(), OS);
OS << "\n";
}
void RVVEmitter::createRVVIntrinsics(
- std::vector<std::unique_ptr<RVVIntrinsic>> &Out) {
+ std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
+ std::vector<SemaRecord> *SemaRecords) {
std::vector<Record *> RV = Records.getAllDerivedDefinitions("RVVBuiltin");
for (auto *R : RV) {
StringRef Name = R->getValueAsString("Name");
StringRef SuffixProto = R->getValueAsString("Suffix");
StringRef OverloadedName = R->getValueAsString("OverloadedName");
StringRef OverloadedSuffixProto = R->getValueAsString("OverloadedSuffix");
StringRef Prototypes = R->getValueAsString("Prototype");
StringRef TypeRange = R->getValueAsString("TypeRange");
▲ Show 20 LinesShow All 93 Lines▼ Show 20 Lines
Name, SuffixStr, OverloadedName, OverloadedSuffixStr,
MaskedIRName,
/*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicy,
HasUnMaskedOverloaded, HasBuiltinAlias, MaskedManualCodegen,
*MaskTypes, IntrinsicTypes, RequiredFeatures, NF));
}
} // end for Log2LMULList
} // end for TypeRange
+
+ // We don't emit vsetvli and vsetvlimax for SemaRecord.
+ // They are written in riscv_vector.td and will emit those marco define in
+ // riscv_vector.h
+ if (Name == "vsetvli" || Name == "vsetvlimax")
+ continue;
+
+ if (!SemaRecords)
+ continue;
+
+ // Create SemaRecord
+ SemaRecord SR;
+ SR.Name = Name.str();
+ SR.OverloadedName = OverloadedName.str();
+ BasicType TypeRangeMask = BasicType::Unknown;
+ for (char I : TypeRange)
+ TypeRangeMask |= ParseBasicType(I);
+
+ SR.TypeRangeMask = static_cast<unsigned>(TypeRangeMask);
+
+ unsigned Log2LMULMask = 0;
+ for (int Log2LMUL : Log2LMULList)
+ Log2LMULMask |= 1 << (Log2LMUL + 3);
+
+ SR.Log2LMULMask = Log2LMULMask;
+
+ SR.RequiredExtensions = 0;
+ for (auto RequiredFeature : RequiredFeatures) {
+ RVVRequire RequireExt = StringSwitch<RVVRequire>(RequiredFeature)
+ .Case("RV64", RVV_REQ_RV64)
+ .Case("FullMultiply", RVV_REQ_FullMultiply)
+ .Default(RVV_REQ_None);
+ assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?");
+ SR.RequiredExtensions |= RequireExt;
+ }
+
+ SR.NF = NF;
+
+ SR.Prototype = std::move(Prototype);
+
+ if (HasMasked)
+ SR.MaskedPrototype = std::move(MaskedPrototype);
+
+ SR.Suffix = parsePrototypes(SuffixProto);
+ SR.OverloadedSuffix = parsePrototypes(OverloadedSuffixProto);
+
+ SemaRecords->push_back(SR);
}
}
void RVVEmitter::printHeaderCode(raw_ostream &OS) {
std::vector<Record *> RVVHeaders =
Records.getAllDerivedDefinitions("RVVHeader");
for (auto *R : RVVHeaders) {
StringRef HeaderCodeStr = R->getValueAsString("HeaderCode");
OS << HeaderCodeStr.str();
}
}
- void RVVEmitter::emitArchMacroAndBody(
- std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &OS,
- std::function<void(raw_ostream &, const RVVIntrinsic &)> PrintBody) {
- RISCVPredefinedMacroT PrevMacros =
- (*Defs.begin())->getRISCVPredefinedMacros();
- bool NeedEndif = emitMacroRestrictionStr(PrevMacros, OS);
- for (auto &Def : Defs) {
- RISCVPredefinedMacroT CurMacros = Def->getRISCVPredefinedMacros();
- if (CurMacros != PrevMacros) {
- if (NeedEndif)
- OS << "#endif\n\n";
- NeedEndif = emitMacroRestrictionStr(CurMacros, OS);
- PrevMacros = CurMacros;
- }
- if (Def->hasBuiltinAlias())
- PrintBody(OS, *Def);
+ void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
+ SemaSignatureTable &SST,
+ ArrayRef<SemaRecord> SemaRecords) {
+ SST.init(SemaRecords);
+
+ for (const auto &SR : SemaRecords) {
+ Out.emplace_back(RVVIntrinsicRecord());
+ RVVIntrinsicRecord &R = Out.back();
+ R.Name = SR.Name.c_str();
+ R.OverloadedName = SR.OverloadedName.c_str();
+ R.PrototypeIndex = SST.getIndex(SR.Prototype);
+ R.MaskedPrototypeIndex = SST.getIndex(SR.MaskedPrototype);
+ R.SuffixIndex = SST.getIndex(SR.Suffix);
+ R.OverloadedSuffixIndex = SST.getIndex(SR.OverloadedSuffix);
+ R.PrototypeLength = SR.Prototype.size();
+ R.MaskedPrototypeLength = SR.MaskedPrototype.size();
+ R.SuffixLength = SR.Suffix.size();
+ R.OverloadedSuffixSize = SR.OverloadedSuffix.size();
+ R.RequiredExtensions = SR.RequiredExtensions;
+ R.TypeRangeMask = SR.TypeRangeMask;
+ R.Log2LMULMask = SR.Log2LMULMask;
+ R.NF = SR.NF;
+
+ assert(R.PrototypeIndex !=
+ static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
+ assert(R.MaskedPrototypeIndex !=
+ static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
+ assert(R.SuffixIndex !=
+ static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
+ assert(R.OverloadedSuffixIndex !=
+ static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
}
- if (NeedEndif)
- OS << "#endif\n\n";
}
- bool RVVEmitter::emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros,
- raw_ostream &OS) {
- if (PredefinedMacros == RISCVPredefinedMacro::Basic)
- return false;
- OS << "#if ";
- ListSeparator LS(" && ");
- if (PredefinedMacros & RISCVPredefinedMacro::V)
- OS << LS << "defined(__riscv_v)";
- if (PredefinedMacros & RISCVPredefinedMacro::Zvfh)
- OS << LS << "defined(__riscv_zvfh)";
- if (PredefinedMacros & RISCVPredefinedMacro::RV64)
- OS << LS << "(__riscv_xlen == 64)";
- if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELen64)
- OS << LS << "(__riscv_v_elen >= 64)";
- if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp32)
- OS << LS << "(__riscv_v_elen_fp >= 32)";
- if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp64)
- OS << LS << "(__riscv_v_elen_fp >= 64)";
- OS << "\n";
- return true;
+ void RVVEmitter::createSema(raw_ostream &OS) {
+ std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
+ std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords;
+ SemaSignatureTable SST;
+ std::vector<SemaRecord> SemaRecords;
+
+ createRVVIntrinsics(Defs, &SemaRecords);
+
+ createRVVIntrinsicRecords(RVVIntrinsicRecords, SST, SemaRecords);
+
+ // Emit signature table for SemaRISCVVectorLookup.cpp.
+ OS << "#ifdef DECL_SIGNATURE_TABLE\n";
+ SST.print(OS);
+ OS << "#endif\n";
+
+ // Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp.
+ OS << "#ifdef DECL_INTRINSIC_RECORDS\n";
+ for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords)
+ OS << Record;
+ OS << "#endif\n";
}
namespace clang {
void EmitRVVHeader(RecordKeeper &Records, raw_ostream &OS) {
RVVEmitter(Records).createHeader(OS);
}
void EmitRVVBuiltins(RecordKeeper &Records, raw_ostream &OS) {
RVVEmitter(Records).createBuiltins(OS);
}
void EmitRVVBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {
RVVEmitter(Records).createCodeGen(OS);
}
+ void EmitRVVBuiltinSema(RecordKeeper &Records, raw_ostream &OS) {
+ RVVEmitter(Records).createSema(OS);
+ }
+
} // End namespace clang

clang/utils/TableGen/TableGen.cpp

Show First 20 LinesShow All 82 Lines▼ Show 20 Lines
GenArmCdeHeader,
GenArmCdeBuiltinDef,
GenArmCdeBuiltinSema,
GenArmCdeBuiltinCG,
GenArmCdeBuiltinAliases,
GenRISCVVectorHeader,
GenRISCVVectorBuiltins,
GenRISCVVectorBuiltinCG,
+ GenRISCVVectorBuiltinSema,
GenAttrDocs,
GenDiagDocs,
GenOptDocs,
GenDataCollectors,
GenTestPragmaAttributeSupportedAttributes
};
namespace {
▲ Show 20 LinesShow All 139 Lines▼ Show 20 Lines
clEnumValN(GenArmCdeBuiltinAliases, "gen-arm-cde-builtin-aliases",
"Generate list of valid ARM CDE builtin aliases for clang"),
clEnumValN(GenRISCVVectorHeader, "gen-riscv-vector-header",
"Generate riscv_vector.h for clang"),
clEnumValN(GenRISCVVectorBuiltins, "gen-riscv-vector-builtins",
"Generate riscv_vector_builtins.inc for clang"),
clEnumValN(GenRISCVVectorBuiltinCG, "gen-riscv-vector-builtin-codegen",
"Generate riscv_vector_builtin_cg.inc for clang"),
+ clEnumValN(GenRISCVVectorBuiltinSema, "gen-riscv-vector-builtin-sema",
+ "Generate riscv_vector_builtin_sema.inc for clang"),
clEnumValN(GenAttrDocs, "gen-attr-docs",
"Generate attribute documentation"),
clEnumValN(GenDiagDocs, "gen-diag-docs",
"Generate diagnostic documentation"),
clEnumValN(GenOptDocs, "gen-opt-docs", "Generate option documentation"),
clEnumValN(GenDataCollectors, "gen-clang-data-collectors",
"Generate data collectors for AST nodes"),
clEnumValN(GenTestPragmaAttributeSupportedAttributes,
▲ Show 20 LinesShow All 199 Lines▼ Show 20 Lines
EmitRVVHeader(Records, OS);
break;
case GenRISCVVectorBuiltins:
EmitRVVBuiltins(Records, OS);
break;
case GenRISCVVectorBuiltinCG:
EmitRVVBuiltinCG(Records, OS);
break;
+ case GenRISCVVectorBuiltinSema:
+ EmitRVVBuiltinSema(Records, OS);
+ break;
case GenAttrDocs:
EmitClangAttrDocs(Records, OS);
break;
case GenDiagDocs:
EmitClangDiagDocs(Records, OS);
break;
case GenOptDocs:
EmitClangOptDocs(Records, OS);
Show All 31 Lines

clang/utils/TableGen/TableGenBackends.h

Show First 20 LinesShow All 104 Lines▼ Show 20 Lines
void EmitMveBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitRVVHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitRVVBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitRVVBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+ void EmitRVVBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCdeHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCdeBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCdeBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCdeBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCdeBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitClangAttrDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
Show All 17 Lines

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK