Skip to content

Commit 7a5cb15

Browse files
committed
[RISCV] Lazily add RVV C intrinsics.
Leverage the method OpenCL uses that adds C intrinsics when the lookup failed. There is no need to define C intrinsics in the header file any more. It could help to avoid the large header file to speed up the compilation of RVV source code. Besides that, only the C intrinsics used by the users will be added into the declaration table. This patch is based on https://reviews.llvm.org/D103228 and inspired by OpenCL implementation. ### Experimental Results #### TL;DR: - Binary size of clang increase ~200k, which is +0.07% for debug build and +0.13% for release build. - Single file compilation speed up ~33x for debug build and ~8.5x for release build - Regression time reduce ~10% (`ninja check-all`, enable all targets) #### Header size change ``` | size | LoC | ------------------------------ Before | 4,434,725 | 69,749 | After | 6,140 | 162 | ``` #### Single File Compilation Time Testcase: ``` #include <riscv_vector.h> vint32m1_t test_vadd_vv_vfloat32m1_t(vint32m1_t op1, vint32m1_t op2, size_t vl) { return vadd(op1, op2, vl); } ``` ##### Debug build: Before: ``` real 0m19.352s user 0m19.252s sys 0m0.092s ``` After: ``` real 0m0.576s user 0m0.552s sys 0m0.024s ``` ~33x speed up for debug build ##### Release build: Before: ``` real 0m0.773s user 0m0.741s sys 0m0.032s ``` After: ``` real 0m0.092s user 0m0.080s sys 0m0.012s ``` ~8.5x speed up for release build #### Regression time Note: the failed case is `tools/llvm-debuginfod-find/debuginfod.test` which is unrelated to this patch. ##### Debug build Before: ``` Testing Time: 1358.38s Skipped : 11 Unsupported : 446 Passed : 75767 Expectedly Failed: 190 Failed : 1 ``` After ``` Testing Time: 1220.29s Skipped : 11 Unsupported : 446 Passed : 75767 Expectedly Failed: 190 Failed : 1 ``` ##### Release build Before: ``` Testing Time: 381.98s Skipped : 12 Unsupported : 1407 Passed : 74765 Expectedly Failed: 176 Failed : 1 ``` After: ``` Testing Time: 346.25s Skipped : 12 Unsupported : 1407 Passed : 74765 Expectedly Failed: 176 Failed : 1 ``` #### Binary size of clang ##### Debug build Before ``` text data bss dec hex filename 335261851 12726004 552812 348540667 14c64efb bin/clang ``` After ``` text data bss dec hex filename 335442803 12798708 552940 348794451 14ca2e53 bin/clang ``` +253K, +0.07% code size ##### Release build Before ``` text data bss dec hex filename 144123975 8374648 483140 152981763 91e5103 bin/clang ``` After ``` text data bss dec hex filename 144255762 8447296 483268 153186326 9217016 bin/clang ``` +204K, +0.13% Authored-by: Kito Cheng <[email protected]> Co-Authored-by: Hsiangkai Wang <[email protected]> Reviewed By: khchen, aaron.ballman Differential Revision: https://reviews.llvm.org/D111617
1 parent 2f9fa9e commit 7a5cb15

17 files changed

+883
-152
lines changed

clang/include/clang/Basic/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,6 @@ clang_tablegen(riscv_vector_builtins.inc -gen-riscv-vector-builtins
9090
clang_tablegen(riscv_vector_builtin_cg.inc -gen-riscv-vector-builtin-codegen
9191
SOURCE riscv_vector.td
9292
TARGET ClangRISCVVectorBuiltinCG)
93+
clang_tablegen(riscv_vector_builtin_sema.inc -gen-riscv-vector-builtin-sema
94+
SOURCE riscv_vector.td
95+
TARGET ClangRISCVVectorBuiltinSema)

clang/include/clang/Basic/TokenKinds.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,9 @@ PRAGMA_ANNOTATION(pragma_fp)
908908
// Annotation for the attribute pragma directives - #pragma clang attribute ...
909909
PRAGMA_ANNOTATION(pragma_attribute)
910910

911+
// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
912+
PRAGMA_ANNOTATION(pragma_riscv)
913+
911914
// Annotations for module import translated from #include etc.
912915
ANNOTATION(module_include)
913916
ANNOTATION(module_begin)

clang/include/clang/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class Parser : public CodeCompletionHandler {
215215
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
216216
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
217217
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
218+
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
218219

219220
std::unique_ptr<CommentHandler> CommentSemaHandler;
220221

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===- RISCVIntrinsicManager.h - RISC-V Intrinsic Handler -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines the RISCVIntrinsicManager, which handles RISC-V vector
10+
// intrinsic functions.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_CLANG_SEMA_RISCVINTRINSICMANAGER_H
15+
#define LLVM_CLANG_SEMA_RISCVINTRINSICMANAGER_H
16+
17+
namespace clang {
18+
class Sema;
19+
class LookupResult;
20+
class IdentifierInfo;
21+
class Preprocessor;
22+
23+
namespace sema {
24+
class RISCVIntrinsicManager {
25+
public:
26+
virtual ~RISCVIntrinsicManager() = default;
27+
28+
// Create RISC-V intrinsic and insert into symbol table and return true if
29+
// found, otherwise return false.
30+
virtual bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II,
31+
Preprocessor &PP) = 0;
32+
};
33+
} // end namespace sema
34+
} // end namespace clang
35+
36+
#endif

clang/include/clang/Sema/Sema.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ namespace sema {
226226
class FunctionScopeInfo;
227227
class LambdaScopeInfo;
228228
class PossiblyUnreachableDiag;
229+
class RISCVIntrinsicManager;
229230
class SemaPPCallbacks;
230231
class TemplateDeductionInfo;
231232
}
@@ -1587,7 +1588,12 @@ class Sema final {
15871588
/// assignment.
15881589
llvm::DenseMap<const VarDecl *, int> RefsMinusAssignments;
15891590

1591+
/// Indicate RISC-V vector builtin functions enabled or not.
1592+
bool DeclareRISCVVBuiltins = false;
1593+
15901594
private:
1595+
std::unique_ptr<sema::RISCVIntrinsicManager> RVIntrinsicManager;
1596+
15911597
Optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
15921598

15931599
bool WarnedDarwinSDKInfoMissing = false;
@@ -13590,6 +13596,8 @@ void Sema::PragmaStack<Sema::AlignPackInfo>::Act(SourceLocation PragmaLocation,
1359013596
llvm::StringRef StackSlotLabel,
1359113597
AlignPackInfo Value);
1359213598

13599+
std::unique_ptr<sema::RISCVIntrinsicManager>
13600+
CreateRISCVIntrinsicManager(Sema &S);
1359313601
} // end namespace clang
1359413602

1359513603
namespace llvm {

clang/include/clang/Support/RISCVVIntrinsicUtils.h

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#include <string>
1919
#include <vector>
2020

21+
namespace llvm {
22+
class raw_ostream;
23+
} // end namespace llvm
24+
2125
namespace clang {
2226
namespace RISCV {
2327

@@ -104,12 +108,14 @@ struct PrototypeDescriptor {
104108
uint8_t TM = static_cast<uint8_t>(TypeModifier::NoModifier);
105109

106110
bool operator!=(const PrototypeDescriptor &PD) const {
107-
return PD.PT != PT || PD.VTM != VTM || PD.TM != TM;
111+
return !(*this == PD);
108112
}
109-
bool operator>(const PrototypeDescriptor &PD) const {
110-
return !(PD.PT <= PT && PD.VTM <= VTM && PD.TM <= TM);
113+
bool operator==(const PrototypeDescriptor &PD) const {
114+
return PD.PT == PT && PD.VTM == VTM && PD.TM == TM;
115+
}
116+
bool operator<(const PrototypeDescriptor &PD) const {
117+
return std::tie(PT, VTM, TM) < std::tie(PD.PT, PD.VTM, PD.TM);
111118
}
112-
113119
static const PrototypeDescriptor Mask;
114120
static const PrototypeDescriptor Vector;
115121
static const PrototypeDescriptor VL;
@@ -224,8 +230,12 @@ class RVVType {
224230
bool isFloat(unsigned Width) const {
225231
return isFloat() && ElementBitwidth == Width;
226232
}
227-
233+
bool isConstant() const { return IsConstant; }
228234
bool isPointer() const { return IsPointer; }
235+
unsigned getElementBitwidth() const { return ElementBitwidth; }
236+
237+
ScalarTypeKind getScalarType() const { return ScalarType; }
238+
VScaleVal getScale() const { return Scale; }
229239

230240
private:
231241
// Verify RVV vector type and set Valid.
@@ -263,18 +273,6 @@ class RVVType {
263273
PrototypeDescriptor Proto);
264274
};
265275

266-
using RISCVPredefinedMacroT = uint8_t;
267-
268-
enum RISCVPredefinedMacro : RISCVPredefinedMacroT {
269-
Basic = 0,
270-
V = 1 << 1,
271-
Zvfh = 1 << 2,
272-
RV64 = 1 << 3,
273-
VectorMaxELen64 = 1 << 4,
274-
VectorMaxELenFp32 = 1 << 5,
275-
VectorMaxELenFp64 = 1 << 6,
276-
};
277-
278276
enum PolicyScheme : uint8_t {
279277
SchemeNone,
280278
HasPassthruOperand,
@@ -302,7 +300,6 @@ class RVVIntrinsic {
302300
// The types we use to obtain the specific LLVM intrinsic. They are index of
303301
// InputTypes. -1 means the return type.
304302
std::vector<int64_t> IntrinsicTypes;
305-
RISCVPredefinedMacroT RISCVPredefinedMacros = 0;
306303
unsigned NF = 1;
307304

308305
public:
@@ -333,9 +330,6 @@ class RVVIntrinsic {
333330
llvm::StringRef getIRName() const { return IRName; }
334331
llvm::StringRef getManualCodegen() const { return ManualCodegen; }
335332
PolicyScheme getPolicyScheme() const { return Scheme; }
336-
RISCVPredefinedMacroT getRISCVPredefinedMacros() const {
337-
return RISCVPredefinedMacros;
338-
}
339333
unsigned getNF() const { return NF; }
340334
const std::vector<int64_t> &getIntrinsicTypes() const {
341335
return IntrinsicTypes;
@@ -349,6 +343,67 @@ class RVVIntrinsic {
349343
llvm::ArrayRef<PrototypeDescriptor> PrototypeDescriptors);
350344
};
351345

346+
// RVVRequire should be sync'ed with target features, but only
347+
// required features used in riscv_vector.td.
348+
enum RVVRequire : uint8_t {
349+
RVV_REQ_None = 0,
350+
RVV_REQ_RV64 = 1 << 0,
351+
RVV_REQ_FullMultiply = 1 << 1,
352+
353+
LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_FullMultiply)
354+
};
355+
356+
// Raw RVV intrinsic info, used to expand later.
357+
// This struct is highly compact for minimized code size.
358+
struct RVVIntrinsicRecord {
359+
// Intrinsic name, e.g. vadd_vv
360+
const char *Name;
361+
362+
// Overloaded intrinsic name, could be empty if it can be computed from Name.
363+
// e.g. vadd
364+
const char *OverloadedName;
365+
366+
// Prototype for this intrinsic, index of RVVSignatureTable.
367+
uint16_t PrototypeIndex;
368+
369+
// Prototype for masked intrinsic, index of RVVSignatureTable.
370+
uint16_t MaskedPrototypeIndex;
371+
372+
// Suffix of intrinsic name, index of RVVSignatureTable.
373+
uint16_t SuffixIndex;
374+
375+
// Suffix of overloaded intrinsic name, index of RVVSignatureTable.
376+
uint16_t OverloadedSuffixIndex;
377+
378+
// Length of the prototype.
379+
uint8_t PrototypeLength;
380+
381+
// Length of prototype of masked intrinsic.
382+
uint8_t MaskedPrototypeLength;
383+
384+
// Length of intrinsic name suffix.
385+
uint8_t SuffixLength;
386+
387+
// Length of overloaded intrinsic suffix.
388+
uint8_t OverloadedSuffixSize;
389+
390+
// Required target features for this intrinsic.
391+
uint8_t RequiredExtensions;
392+
393+
// Supported type, mask of BasicType.
394+
uint8_t TypeRangeMask;
395+
396+
// Supported LMUL.
397+
uint8_t Log2LMULMask;
398+
399+
// Number of fields, greater than 1 if it's segment load/store.
400+
uint8_t NF;
401+
};
402+
403+
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
404+
const RVVIntrinsicRecord &RVVInstrRecord);
405+
406+
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
352407
} // end namespace RISCV
353408

354409
} // end namespace clang

clang/lib/Parse/ParsePragma.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,16 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
350350
Token &FirstToken) override;
351351
};
352352

353+
struct PragmaRISCVHandler : public PragmaHandler {
354+
PragmaRISCVHandler(Sema &Actions)
355+
: PragmaHandler("riscv"), Actions(Actions) {}
356+
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
357+
Token &FirstToken) override;
358+
359+
private:
360+
Sema &Actions;
361+
};
362+
353363
void markAsReinjectedForRelexing(llvm::MutableArrayRef<clang::Token> Toks) {
354364
for (auto &T : Toks)
355365
T.setFlag(clang::Token::IsReinjected);
@@ -493,6 +503,11 @@ void Parser::initializePragmaHandlers() {
493503

494504
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
495505
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
506+
507+
if (getTargetInfo().getTriple().isRISCV()) {
508+
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
509+
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
510+
}
496511
}
497512

498513
void Parser::resetPragmaHandlers() {
@@ -617,6 +632,11 @@ void Parser::resetPragmaHandlers() {
617632

618633
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
619634
MaxTokensTotalPragmaHandler.reset();
635+
636+
if (getTargetInfo().getTriple().isRISCV()) {
637+
PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
638+
RISCVPragmaHandler.reset();
639+
}
620640
}
621641

622642
/// Handle the annotation token produced for #pragma unused(...)
@@ -3929,3 +3949,35 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
39293949

39303950
PP.overrideMaxTokens(MaxTokens, Loc);
39313951
}
3952+
3953+
// Handle '#pragma clang riscv intrinsic vector'.
3954+
void PragmaRISCVHandler::HandlePragma(Preprocessor &PP,
3955+
PragmaIntroducer Introducer,
3956+
Token &FirstToken) {
3957+
Token Tok;
3958+
PP.Lex(Tok);
3959+
IdentifierInfo *II = Tok.getIdentifierInfo();
3960+
3961+
if (!II || !II->isStr("intrinsic")) {
3962+
PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
3963+
<< PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'intrinsic'";
3964+
return;
3965+
}
3966+
3967+
PP.Lex(Tok);
3968+
II = Tok.getIdentifierInfo();
3969+
if (!II || !II->isStr("vector")) {
3970+
PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
3971+
<< PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'vector'";
3972+
return;
3973+
}
3974+
3975+
PP.Lex(Tok);
3976+
if (Tok.isNot(tok::eod)) {
3977+
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
3978+
<< "clang riscv intrinsic";
3979+
return;
3980+
}
3981+
3982+
Actions.DeclareRISCVVBuiltins = true;
3983+
}

clang/lib/Sema/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ add_clang_library(clangSema
5252
SemaOpenMP.cpp
5353
SemaOverload.cpp
5454
SemaPseudoObject.cpp
55+
SemaRISCVVectorLookup.cpp
5556
SemaStmt.cpp
5657
SemaStmtAsm.cpp
5758
SemaStmtAttr.cpp
@@ -74,4 +75,5 @@ add_clang_library(clangSema
7475
clangBasic
7576
clangEdit
7677
clangLex
78+
clangSupport
7779
)

clang/lib/Sema/Sema.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "clang/Sema/Initialization.h"
3838
#include "clang/Sema/MultiplexExternalSemaSource.h"
3939
#include "clang/Sema/ObjCMethodList.h"
40+
#include "clang/Sema/RISCVIntrinsicManager.h"
4041
#include "clang/Sema/Scope.h"
4142
#include "clang/Sema/ScopeInfo.h"
4243
#include "clang/Sema/SemaConsumer.h"

clang/lib/Sema/SemaLookup.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "clang/Sema/DeclSpec.h"
3030
#include "clang/Sema/Lookup.h"
3131
#include "clang/Sema/Overload.h"
32+
#include "clang/Sema/RISCVIntrinsicManager.h"
3233
#include "clang/Sema/Scope.h"
3334
#include "clang/Sema/ScopeInfo.h"
3435
#include "clang/Sema/Sema.h"
@@ -928,6 +929,14 @@ bool Sema::LookupBuiltin(LookupResult &R) {
928929
}
929930
}
930931

932+
if (DeclareRISCVVBuiltins) {
933+
if (!RVIntrinsicManager)
934+
RVIntrinsicManager = CreateRISCVIntrinsicManager(*this);
935+
936+
if (RVIntrinsicManager->CreateIntrinsicIfFound(R, II, PP))
937+
return true;
938+
}
939+
931940
// If this is a builtin on this (or all) targets, create the decl.
932941
if (unsigned BuiltinID = II->getBuiltinID()) {
933942
// In C++, C2x, and OpenCL (spec v1.2 s6.9.f), we don't have any

0 commit comments

Comments
 (0)