forked from OSchip/llvm-project
[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 <kito.cheng@sifive.com> Co-Authored-by: Hsiangkai Wang <kai.wang@sifive.com> Reviewed By: khchen, aaron.ballman Differential Revision: https://reviews.llvm.org/D111617
This commit is contained in:
parent
2f9fa9ef53
commit
7a5cb15ea6
|
@ -90,3 +90,6 @@ clang_tablegen(riscv_vector_builtins.inc -gen-riscv-vector-builtins
|
|||
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)
|
||||
|
|
|
@ -908,6 +908,9 @@ 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)
|
||||
|
|
|
@ -215,6 +215,7 @@ class Parser : public CodeCompletionHandler {
|
|||
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;
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//===- 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
|
|
@ -226,6 +226,7 @@ namespace sema {
|
|||
class FunctionScopeInfo;
|
||||
class LambdaScopeInfo;
|
||||
class PossiblyUnreachableDiag;
|
||||
class RISCVIntrinsicManager;
|
||||
class SemaPPCallbacks;
|
||||
class TemplateDeductionInfo;
|
||||
}
|
||||
|
@ -1587,7 +1588,12 @@ public:
|
|||
/// 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;
|
||||
|
@ -13590,6 +13596,8 @@ void Sema::PragmaStack<Sema::AlignPackInfo>::Act(SourceLocation PragmaLocation,
|
|||
llvm::StringRef StackSlotLabel,
|
||||
AlignPackInfo Value);
|
||||
|
||||
std::unique_ptr<sema::RISCVIntrinsicManager>
|
||||
CreateRISCVIntrinsicManager(Sema &S);
|
||||
} // end namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
} // end namespace llvm
|
||||
|
||||
namespace clang {
|
||||
namespace RISCV {
|
||||
|
||||
|
@ -104,12 +108,14 @@ struct PrototypeDescriptor {
|
|||
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;
|
||||
|
@ -224,8 +230,12 @@ public:
|
|||
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.
|
||||
|
@ -263,18 +273,6 @@ public:
|
|||
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,
|
||||
|
@ -302,7 +300,6 @@ private:
|
|||
// 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:
|
||||
|
@ -333,9 +330,6 @@ public:
|
|||
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;
|
||||
|
@ -349,6 +343,67 @@ public:
|
|||
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
|
||||
|
|
|
@ -350,6 +350,16 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
|
|||
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);
|
||||
|
@ -493,6 +503,11 @@ void Parser::initializePragmaHandlers() {
|
|||
|
||||
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() {
|
||||
|
@ -617,6 +632,11 @@ void Parser::resetPragmaHandlers() {
|
|||
|
||||
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(...)
|
||||
|
@ -3929,3 +3949,35 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ add_clang_library(clangSema
|
|||
SemaOpenMP.cpp
|
||||
SemaOverload.cpp
|
||||
SemaPseudoObject.cpp
|
||||
SemaRISCVVectorLookup.cpp
|
||||
SemaStmt.cpp
|
||||
SemaStmtAsm.cpp
|
||||
SemaStmtAttr.cpp
|
||||
|
@ -74,4 +75,5 @@ add_clang_library(clangSema
|
|||
clangBasic
|
||||
clangEdit
|
||||
clangLex
|
||||
clangSupport
|
||||
)
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#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"
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#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"
|
||||
|
@ -928,6 +929,14 @@ bool Sema::LookupBuiltin(LookupResult &R) {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -0,0 +1,386 @@
|
|||
//==- 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
|
|
@ -873,27 +873,6 @@ RVVIntrinsic::RVVIntrinsic(
|
|||
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());
|
||||
|
@ -951,5 +930,29 @@ SmallVector<PrototypeDescriptor> parsePrototypes(StringRef Prototypes) {
|
|||
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
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// 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:
|
|
@ -0,0 +1,4 @@
|
|||
// RUN: %clang_cc1 -triple riscv64 -target-feature +v -emit-llvm -o - -verify %s
|
||||
|
||||
#pragma clang riscv intrinsic vector
|
||||
// expected-no-diagnostics
|
|
@ -20,6 +20,7 @@
|
|||
#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"
|
||||
|
@ -29,6 +30,59 @@ 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;
|
||||
|
@ -45,22 +99,22 @@ public:
|
|||
/// 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
|
||||
|
@ -151,33 +205,83 @@ void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) {
|
|||
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;
|
||||
}
|
||||
};
|
||||
|
||||
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 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();
|
||||
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";
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -212,10 +316,9 @@ void RVVEmitter::createHeader(raw_ostream &OS) {
|
|||
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()
|
||||
|
@ -255,7 +358,7 @@ void RVVEmitter::createHeader(raw_ostream &OS) {
|
|||
}
|
||||
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);
|
||||
|
@ -264,7 +367,7 @@ void RVVEmitter::createHeader(raw_ostream &OS) {
|
|||
}
|
||||
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);
|
||||
|
@ -273,37 +376,8 @@ void RVVEmitter::createHeader(raw_ostream &OS) {
|
|||
}
|
||||
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";
|
||||
|
@ -392,7 +466,8 @@ void RVVEmitter::createCodeGen(raw_ostream &OS) {
|
|||
}
|
||||
|
||||
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");
|
||||
|
@ -502,6 +577,53 @@ void RVVEmitter::createRVVIntrinsics(
|
|||
}
|
||||
} // 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,47 +636,60 @@ void RVVEmitter::printHeaderCode(raw_ostream &OS) {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -570,4 +705,8 @@ 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
|
||||
|
|
|
@ -88,6 +88,7 @@ enum ActionType {
|
|||
GenRISCVVectorHeader,
|
||||
GenRISCVVectorBuiltins,
|
||||
GenRISCVVectorBuiltinCG,
|
||||
GenRISCVVectorBuiltinSema,
|
||||
GenAttrDocs,
|
||||
GenDiagDocs,
|
||||
GenOptDocs,
|
||||
|
@ -243,6 +244,8 @@ cl::opt<ActionType> Action(
|
|||
"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",
|
||||
|
@ -458,6 +461,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
|
|||
case GenRISCVVectorBuiltinCG:
|
||||
EmitRVVBuiltinCG(Records, OS);
|
||||
break;
|
||||
case GenRISCVVectorBuiltinSema:
|
||||
EmitRVVBuiltinSema(Records, OS);
|
||||
break;
|
||||
case GenAttrDocs:
|
||||
EmitClangAttrDocs(Records, OS);
|
||||
break;
|
||||
|
|
|
@ -110,6 +110,7 @@ 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);
|
||||
|
|
Loading…
Reference in New Issue