1 //==- SemaRISCVVectorLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -==//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements name lookup for RISC-V vector intrinsic.
11 //===----------------------------------------------------------------------===//
13 #include "clang/AST/ASTContext.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/Basic/Builtins.h"
16 #include "clang/Basic/TargetInfo.h"
17 #include "clang/Lex/Preprocessor.h"
18 #include "clang/Sema/Lookup.h"
19 #include "clang/Sema/RISCVIntrinsicManager.h"
20 #include "clang/Sema/Sema.h"
21 #include "clang/Support/RISCVVIntrinsicUtils.h"
22 #include "llvm/ADT/SmallVector.h"
28 using namespace clang
;
29 using namespace clang::RISCV
;
31 using IntrinsicKind
= sema::RISCVIntrinsicManager::IntrinsicKind
;
35 // Function definition of a RVV intrinsic.
36 struct RVVIntrinsicDef
{
37 /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd.
38 std::string BuiltinName
;
40 /// Function signature, first element is return type.
44 struct RVVOverloadIntrinsicDef
{
45 // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList.
46 SmallVector
<uint32_t, 8> Indexes
;
51 static const PrototypeDescriptor RVVSignatureTable
[] = {
52 #define DECL_SIGNATURE_TABLE
53 #include "clang/Basic/riscv_vector_builtin_sema.inc"
54 #undef DECL_SIGNATURE_TABLE
57 static const PrototypeDescriptor RVSiFiveVectorSignatureTable
[] = {
58 #define DECL_SIGNATURE_TABLE
59 #include "clang/Basic/riscv_sifive_vector_builtin_sema.inc"
60 #undef DECL_SIGNATURE_TABLE
63 static const RVVIntrinsicRecord RVVIntrinsicRecords
[] = {
64 #define DECL_INTRINSIC_RECORDS
65 #include "clang/Basic/riscv_vector_builtin_sema.inc"
66 #undef DECL_INTRINSIC_RECORDS
69 static const RVVIntrinsicRecord RVSiFiveVectorIntrinsicRecords
[] = {
70 #define DECL_INTRINSIC_RECORDS
71 #include "clang/Basic/riscv_sifive_vector_builtin_sema.inc"
72 #undef DECL_INTRINSIC_RECORDS
75 // Get subsequence of signature table.
76 static ArrayRef
<PrototypeDescriptor
>
77 ProtoSeq2ArrayRef(IntrinsicKind K
, uint16_t Index
, uint8_t Length
) {
79 case IntrinsicKind::RVV
:
80 return ArrayRef(&RVVSignatureTable
[Index
], Length
);
81 case IntrinsicKind::SIFIVE_VECTOR
:
82 return ArrayRef(&RVSiFiveVectorSignatureTable
[Index
], Length
);
84 llvm_unreachable("Unhandled IntrinsicKind");
87 static QualType
RVVType2Qual(ASTContext
&Context
, const RVVType
*Type
) {
89 switch (Type
->getScalarType()) {
90 case ScalarTypeKind::Void
:
93 case ScalarTypeKind::Size_t
:
94 QT
= Context
.getSizeType();
96 case ScalarTypeKind::Ptrdiff_t
:
97 QT
= Context
.getPointerDiffType();
99 case ScalarTypeKind::UnsignedLong
:
100 QT
= Context
.UnsignedLongTy
;
102 case ScalarTypeKind::SignedLong
:
105 case ScalarTypeKind::Boolean
:
108 case ScalarTypeKind::SignedInteger
:
109 QT
= Context
.getIntTypeForBitwidth(Type
->getElementBitwidth(), true);
111 case ScalarTypeKind::UnsignedInteger
:
112 QT
= Context
.getIntTypeForBitwidth(Type
->getElementBitwidth(), false);
114 case ScalarTypeKind::BFloat
:
115 QT
= Context
.BFloat16Ty
;
117 case ScalarTypeKind::Float
:
118 switch (Type
->getElementBitwidth()) {
120 QT
= Context
.DoubleTy
;
123 QT
= Context
.FloatTy
;
126 QT
= Context
.Float16Ty
;
129 llvm_unreachable("Unsupported floating point width.");
134 llvm_unreachable("Unhandled type.");
136 if (Type
->isVector()) {
138 QT
= Context
.getScalableVectorType(QT
, *Type
->getScale(), Type
->getNF());
140 QT
= Context
.getScalableVectorType(QT
, *Type
->getScale());
143 if (Type
->isConstant())
144 QT
= Context
.getConstType(QT
);
146 // Transform the type to a pointer as the last step, if necessary.
147 if (Type
->isPointer())
148 QT
= Context
.getPointerType(QT
);
154 class RISCVIntrinsicManagerImpl
: public sema::RISCVIntrinsicManager
{
158 RVVTypeCache TypeCache
;
159 bool ConstructedRISCVVBuiltins
;
160 bool ConstructedRISCVSiFiveVectorBuiltins
;
162 // List of all RVV intrinsic.
163 std::vector
<RVVIntrinsicDef
> IntrinsicList
;
164 // Mapping function name to index of IntrinsicList.
165 StringMap
<uint32_t> Intrinsics
;
166 // Mapping function name to RVVOverloadIntrinsicDef.
167 StringMap
<RVVOverloadIntrinsicDef
> OverloadIntrinsics
;
170 // Create RVVIntrinsicDef.
171 void InitRVVIntrinsic(const RVVIntrinsicRecord
&Record
, StringRef SuffixStr
,
172 StringRef OverloadedSuffixStr
, bool IsMask
,
173 RVVTypes
&Types
, bool HasPolicy
, Policy PolicyAttrs
);
175 // Create FunctionDecl for a vector intrinsic.
176 void CreateRVVIntrinsicDecl(LookupResult
&LR
, IdentifierInfo
*II
,
177 Preprocessor
&PP
, uint32_t Index
,
180 void ConstructRVVIntrinsics(ArrayRef
<RVVIntrinsicRecord
> Recs
,
184 RISCVIntrinsicManagerImpl(clang::Sema
&S
) : S(S
), Context(S
.Context
) {
185 ConstructedRISCVVBuiltins
= false;
186 ConstructedRISCVSiFiveVectorBuiltins
= false;
189 // Initialize IntrinsicList
190 void InitIntrinsicList() override
;
192 // Create RISC-V vector intrinsic and insert into symbol table if found, and
193 // return true, otherwise return false.
194 bool CreateIntrinsicIfFound(LookupResult
&LR
, IdentifierInfo
*II
,
195 Preprocessor
&PP
) override
;
199 void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
200 ArrayRef
<RVVIntrinsicRecord
> Recs
, IntrinsicKind K
) {
201 const TargetInfo
&TI
= Context
.getTargetInfo();
202 static const std::pair
<const char *, RVVRequire
> FeatureCheckList
[] = {
203 {"64bit", RVV_REQ_RV64
},
204 {"xsfvcp", RVV_REQ_Xsfvcp
},
205 {"xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf
},
206 {"xsfvfwmaccqqq", RVV_REQ_Xsfvfwmaccqqq
},
207 {"xsfvqmaccdod", RVV_REQ_Xsfvqmaccdod
},
208 {"xsfvqmaccqoq", RVV_REQ_Xsfvqmaccqoq
},
209 {"zvbb", RVV_REQ_Zvbb
},
210 {"zvbc", RVV_REQ_Zvbc
},
211 {"zvkb", RVV_REQ_Zvkb
},
212 {"zvkg", RVV_REQ_Zvkg
},
213 {"zvkned", RVV_REQ_Zvkned
},
214 {"zvknha", RVV_REQ_Zvknha
},
215 {"zvknhb", RVV_REQ_Zvknhb
},
216 {"zvksed", RVV_REQ_Zvksed
},
217 {"zvksh", RVV_REQ_Zvksh
},
218 {"experimental", RVV_REQ_Experimental
}};
220 // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
221 // in RISCVVEmitter.cpp.
222 for (auto &Record
: Recs
) {
223 // Check requirements.
224 if (llvm::any_of(FeatureCheckList
, [&](const auto &Item
) {
225 return (Record
.RequiredExtensions
& Item
.second
) == Item
.second
&&
226 !TI
.hasFeature(Item
.first
);
230 // Create Intrinsics for each type and LMUL.
231 BasicType BaseType
= BasicType::Unknown
;
232 ArrayRef
<PrototypeDescriptor
> BasicProtoSeq
=
233 ProtoSeq2ArrayRef(K
, Record
.PrototypeIndex
, Record
.PrototypeLength
);
234 ArrayRef
<PrototypeDescriptor
> SuffixProto
=
235 ProtoSeq2ArrayRef(K
, Record
.SuffixIndex
, Record
.SuffixLength
);
236 ArrayRef
<PrototypeDescriptor
> OverloadedSuffixProto
= ProtoSeq2ArrayRef(
237 K
, Record
.OverloadedSuffixIndex
, Record
.OverloadedSuffixSize
);
239 PolicyScheme UnMaskedPolicyScheme
=
240 static_cast<PolicyScheme
>(Record
.UnMaskedPolicyScheme
);
241 PolicyScheme MaskedPolicyScheme
=
242 static_cast<PolicyScheme
>(Record
.MaskedPolicyScheme
);
244 const Policy DefaultPolicy
;
246 llvm::SmallVector
<PrototypeDescriptor
> ProtoSeq
=
247 RVVIntrinsic::computeBuiltinTypes(
248 BasicProtoSeq
, /*IsMasked=*/false,
249 /*HasMaskedOffOperand=*/false, Record
.HasVL
, Record
.NF
,
250 UnMaskedPolicyScheme
, DefaultPolicy
, Record
.IsTuple
);
252 llvm::SmallVector
<PrototypeDescriptor
> ProtoMaskSeq
;
253 if (Record
.HasMasked
)
254 ProtoMaskSeq
= RVVIntrinsic::computeBuiltinTypes(
255 BasicProtoSeq
, /*IsMasked=*/true, Record
.HasMaskedOffOperand
,
256 Record
.HasVL
, Record
.NF
, MaskedPolicyScheme
, DefaultPolicy
,
259 bool UnMaskedHasPolicy
= UnMaskedPolicyScheme
!= PolicyScheme::SchemeNone
;
260 bool MaskedHasPolicy
= MaskedPolicyScheme
!= PolicyScheme::SchemeNone
;
261 SmallVector
<Policy
> SupportedUnMaskedPolicies
=
262 RVVIntrinsic::getSupportedUnMaskedPolicies();
263 SmallVector
<Policy
> SupportedMaskedPolicies
=
264 RVVIntrinsic::getSupportedMaskedPolicies(Record
.HasTailPolicy
,
265 Record
.HasMaskPolicy
);
267 for (unsigned int TypeRangeMaskShift
= 0;
268 TypeRangeMaskShift
<= static_cast<unsigned int>(BasicType::MaxOffset
);
269 ++TypeRangeMaskShift
) {
270 unsigned int BaseTypeI
= 1 << TypeRangeMaskShift
;
271 BaseType
= static_cast<BasicType
>(BaseTypeI
);
273 if ((BaseTypeI
& Record
.TypeRangeMask
) != BaseTypeI
)
276 if (BaseType
== BasicType::Float16
) {
277 if ((Record
.RequiredExtensions
& RVV_REQ_ZvfhminOrZvfh
) ==
278 RVV_REQ_ZvfhminOrZvfh
) {
279 if (!TI
.hasFeature("zvfh") && !TI
.hasFeature("zvfhmin"))
281 } else if (!TI
.hasFeature("zvfh")) {
286 // Expanded with different LMUL.
287 for (int Log2LMUL
= -3; Log2LMUL
<= 3; Log2LMUL
++) {
288 if (!(Record
.Log2LMULMask
& (1 << (Log2LMUL
+ 3))))
291 std::optional
<RVVTypes
> Types
=
292 TypeCache
.computeTypes(BaseType
, Log2LMUL
, Record
.NF
, ProtoSeq
);
294 // Ignored to create new intrinsic if there are any illegal types.
295 if (!Types
.has_value())
298 std::string SuffixStr
= RVVIntrinsic::getSuffixStr(
299 TypeCache
, BaseType
, Log2LMUL
, SuffixProto
);
300 std::string OverloadedSuffixStr
= RVVIntrinsic::getSuffixStr(
301 TypeCache
, BaseType
, Log2LMUL
, OverloadedSuffixProto
);
303 // Create non-masked intrinsic.
304 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
, false, *Types
,
305 UnMaskedHasPolicy
, DefaultPolicy
);
307 // Create non-masked policy intrinsic.
308 if (Record
.UnMaskedPolicyScheme
!= PolicyScheme::SchemeNone
) {
309 for (auto P
: SupportedUnMaskedPolicies
) {
310 llvm::SmallVector
<PrototypeDescriptor
> PolicyPrototype
=
311 RVVIntrinsic::computeBuiltinTypes(
312 BasicProtoSeq
, /*IsMasked=*/false,
313 /*HasMaskedOffOperand=*/false, Record
.HasVL
, Record
.NF
,
314 UnMaskedPolicyScheme
, P
, Record
.IsTuple
);
315 std::optional
<RVVTypes
> PolicyTypes
= TypeCache
.computeTypes(
316 BaseType
, Log2LMUL
, Record
.NF
, PolicyPrototype
);
317 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
,
318 /*IsMask=*/false, *PolicyTypes
, UnMaskedHasPolicy
,
322 if (!Record
.HasMasked
)
324 // Create masked intrinsic.
325 std::optional
<RVVTypes
> MaskTypes
=
326 TypeCache
.computeTypes(BaseType
, Log2LMUL
, Record
.NF
, ProtoMaskSeq
);
327 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
, true,
328 *MaskTypes
, MaskedHasPolicy
, DefaultPolicy
);
329 if (Record
.MaskedPolicyScheme
== PolicyScheme::SchemeNone
)
331 // Create masked policy intrinsic.
332 for (auto P
: SupportedMaskedPolicies
) {
333 llvm::SmallVector
<PrototypeDescriptor
> PolicyPrototype
=
334 RVVIntrinsic::computeBuiltinTypes(
335 BasicProtoSeq
, /*IsMasked=*/true, Record
.HasMaskedOffOperand
,
336 Record
.HasVL
, Record
.NF
, MaskedPolicyScheme
, P
,
338 std::optional
<RVVTypes
> PolicyTypes
= TypeCache
.computeTypes(
339 BaseType
, Log2LMUL
, Record
.NF
, PolicyPrototype
);
340 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
,
341 /*IsMask=*/true, *PolicyTypes
, MaskedHasPolicy
, P
);
343 } // End for different LMUL
344 } // End for different TypeRange
348 void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
350 if (S
.DeclareRISCVVBuiltins
&& !ConstructedRISCVVBuiltins
) {
351 ConstructedRISCVVBuiltins
= true;
352 ConstructRVVIntrinsics(RVVIntrinsicRecords
,
355 if (S
.DeclareRISCVSiFiveVectorBuiltins
&&
356 !ConstructedRISCVSiFiveVectorBuiltins
) {
357 ConstructedRISCVSiFiveVectorBuiltins
= true;
358 ConstructRVVIntrinsics(RVSiFiveVectorIntrinsicRecords
,
359 IntrinsicKind::SIFIVE_VECTOR
);
363 // Compute name and signatures for intrinsic with practical types.
364 void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
365 const RVVIntrinsicRecord
&Record
, StringRef SuffixStr
,
366 StringRef OverloadedSuffixStr
, bool IsMasked
, RVVTypes
&Signature
,
367 bool HasPolicy
, Policy PolicyAttrs
) {
368 // Function name, e.g. vadd_vv_i32m1.
369 std::string Name
= Record
.Name
;
370 if (!SuffixStr
.empty())
371 Name
+= "_" + SuffixStr
.str();
373 // Overloaded function name, e.g. vadd.
374 std::string OverloadedName
;
375 if (!Record
.OverloadedName
)
376 OverloadedName
= StringRef(Record
.Name
).split("_").first
.str();
378 OverloadedName
= Record
.OverloadedName
;
379 if (!OverloadedSuffixStr
.empty())
380 OverloadedName
+= "_" + OverloadedSuffixStr
.str();
382 // clang built-in function name, e.g. __builtin_rvv_vadd.
383 std::string BuiltinName
= "__builtin_rvv_" + std::string(Record
.Name
);
385 RVVIntrinsic::updateNamesAndPolicy(IsMasked
, HasPolicy
, Name
, BuiltinName
,
386 OverloadedName
, PolicyAttrs
,
387 Record
.HasFRMRoundModeOp
);
389 // Put into IntrinsicList.
390 uint32_t Index
= IntrinsicList
.size();
391 IntrinsicList
.push_back({BuiltinName
, Signature
});
393 // Creating mapping to Intrinsics.
394 Intrinsics
.insert({Name
, Index
});
396 // Get the RVVOverloadIntrinsicDef.
397 RVVOverloadIntrinsicDef
&OverloadIntrinsicDef
=
398 OverloadIntrinsics
[OverloadedName
];
400 // And added the index.
401 OverloadIntrinsicDef
.Indexes
.push_back(Index
);
404 void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult
&LR
,
409 ASTContext
&Context
= S
.Context
;
410 RVVIntrinsicDef
&IDef
= IntrinsicList
[Index
];
411 RVVTypes Sigs
= IDef
.Signature
;
412 size_t SigLength
= Sigs
.size();
413 RVVType
*ReturnType
= Sigs
[0];
414 QualType RetType
= RVVType2Qual(Context
, ReturnType
);
415 SmallVector
<QualType
, 8> ArgTypes
;
416 QualType BuiltinFuncType
;
418 // Skip return type, and convert RVVType to QualType for arguments.
419 for (size_t i
= 1; i
< SigLength
; ++i
)
420 ArgTypes
.push_back(RVVType2Qual(Context
, Sigs
[i
]));
422 FunctionProtoType::ExtProtoInfo
PI(
423 Context
.getDefaultCallingConvention(false, false, true));
427 SourceLocation Loc
= LR
.getNameLoc();
428 BuiltinFuncType
= Context
.getFunctionType(RetType
, ArgTypes
, PI
);
429 DeclContext
*Parent
= Context
.getTranslationUnitDecl();
431 FunctionDecl
*RVVIntrinsicDecl
= FunctionDecl::Create(
432 Context
, Parent
, Loc
, Loc
, II
, BuiltinFuncType
, /*TInfo=*/nullptr,
433 SC_Extern
, S
.getCurFPFeatures().isFPConstrained(),
434 /*isInlineSpecified*/ false,
435 /*hasWrittenPrototype*/ true);
437 // Create Decl objects for each parameter, adding them to the
439 const auto *FP
= cast
<FunctionProtoType
>(BuiltinFuncType
);
440 SmallVector
<ParmVarDecl
*, 8> ParmList
;
441 for (unsigned IParm
= 0, E
= FP
->getNumParams(); IParm
!= E
; ++IParm
) {
443 ParmVarDecl::Create(Context
, RVVIntrinsicDecl
, Loc
, Loc
, nullptr,
444 FP
->getParamType(IParm
), nullptr, SC_None
, nullptr);
445 Parm
->setScopeInfo(0, IParm
);
446 ParmList
.push_back(Parm
);
448 RVVIntrinsicDecl
->setParams(ParmList
);
450 // Add function attributes.
452 RVVIntrinsicDecl
->addAttr(OverloadableAttr::CreateImplicit(Context
));
454 // Setup alias to __builtin_rvv_*
455 IdentifierInfo
&IntrinsicII
= PP
.getIdentifierTable().get(IDef
.BuiltinName
);
456 RVVIntrinsicDecl
->addAttr(
457 BuiltinAliasAttr::CreateImplicit(S
.Context
, &IntrinsicII
));
459 // Add to symbol table.
460 LR
.addDecl(RVVIntrinsicDecl
);
463 bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult
&LR
,
466 StringRef Name
= II
->getName();
468 // Lookup the function name from the overload intrinsics first.
469 auto OvIItr
= OverloadIntrinsics
.find(Name
);
470 if (OvIItr
!= OverloadIntrinsics
.end()) {
471 const RVVOverloadIntrinsicDef
&OvIntrinsicDef
= OvIItr
->second
;
472 for (auto Index
: OvIntrinsicDef
.Indexes
)
473 CreateRVVIntrinsicDecl(LR
, II
, PP
, Index
,
474 /*IsOverload*/ true);
476 // If we added overloads, need to resolve the lookup result.
481 // Lookup the function name from the intrinsics.
482 auto Itr
= Intrinsics
.find(Name
);
483 if (Itr
!= Intrinsics
.end()) {
484 CreateRVVIntrinsicDecl(LR
, II
, PP
, Itr
->second
,
485 /*IsOverload*/ false);
489 // It's not an RVV intrinsics.
494 std::unique_ptr
<clang::sema::RISCVIntrinsicManager
>
495 CreateRISCVIntrinsicManager(Sema
&S
) {
496 return std::make_unique
<RISCVIntrinsicManagerImpl
>(S
);