Skip to content

Commit

Permalink
[RISCV] Lazily add RVV C intrinsics.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kito-cheng committed Jul 26, 2022
1 parent 2f9fa9e commit 7a5cb15
Show file tree
Hide file tree
Showing 17 changed files with 883 additions and 152 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
36 changes: 36 additions & 0 deletions clang/include/clang/Sema/RISCVIntrinsicManager.h
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ namespace sema {
class FunctionScopeInfo;
class LambdaScopeInfo;
class PossiblyUnreachableDiag;
class RISCVIntrinsicManager;
class SemaPPCallbacks;
class TemplateDeductionInfo;
}
Expand Down Expand Up @@ -1587,7 +1588,12 @@ class Sema final {
/// 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;
Expand Down Expand Up @@ -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 {
Expand Down
97 changes: 76 additions & 21 deletions clang/include/clang/Support/RISCVVIntrinsicUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include <string>
#include <vector>

namespace llvm {
class raw_ostream;
} // end namespace llvm

namespace clang {
namespace RISCV {

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -224,8 +230,12 @@ class RVVType {
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.
Expand Down Expand Up @@ -263,18 +273,6 @@ class RVVType {
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,
Expand Down Expand Up @@ -302,7 +300,6 @@ class RVVIntrinsic {
// 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:
Expand Down Expand Up @@ -333,9 +330,6 @@ class RVVIntrinsic {
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;
Expand All @@ -349,6 +343,67 @@ class RVVIntrinsic {
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
Expand Down
52 changes: 52 additions & 0 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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(...)
Expand Down Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ add_clang_library(clangSema
SemaOpenMP.cpp
SemaOverload.cpp
SemaPseudoObject.cpp
SemaRISCVVectorLookup.cpp
SemaStmt.cpp
SemaStmtAsm.cpp
SemaStmtAttr.cpp
Expand All @@ -74,4 +75,5 @@ add_clang_library(clangSema
clangBasic
clangEdit
clangLex
clangSupport
)
1 change: 1 addition & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 7a5cb15

Please sign in to comment.