[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:
Kito Cheng 2022-07-13 15:52:17 +08:00
parent 2f9fa9ef53
commit 7a5cb15ea6
17 changed files with 883 additions and 152 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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;
}

View File

@ -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
)

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);