1 //==- SemaRISCVVectorLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -==//
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 implements name lookup for RISC-V vector intrinsic.
10 //
11 //===----------------------------------------------------------------------===//
12
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"
23 #include <string>
24 #include <vector>
25
26 using namespace llvm;
27 using namespace clang;
28 using namespace clang::RISCV;
29
30 namespace {
31
32 // Function definition of a RVV intrinsic.
33 struct RVVIntrinsicDef {
34 /// Full function name with suffix, e.g. vadd_vv_i32m1.
35 std::string Name;
36
37 /// Overloaded function name, e.g. vadd.
38 std::string OverloadName;
39
40 /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd.
41 std::string BuiltinName;
42
43 /// Function signature, first element is return type.
44 RVVTypes Signature;
45 };
46
47 struct RVVOverloadIntrinsicDef {
48 // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList.
49 SmallVector<size_t, 8> Indexes;
50 };
51
52 } // namespace
53
54 static const PrototypeDescriptor RVVSignatureTable[] = {
55 #define DECL_SIGNATURE_TABLE
56 #include "clang/Basic/riscv_vector_builtin_sema.inc"
57 #undef DECL_SIGNATURE_TABLE
58 };
59
60 static const RVVIntrinsicRecord RVVIntrinsicRecords[] = {
61 #define DECL_INTRINSIC_RECORDS
62 #include "clang/Basic/riscv_vector_builtin_sema.inc"
63 #undef DECL_INTRINSIC_RECORDS
64 };
65
66 // Get subsequence of signature table.
ProtoSeq2ArrayRef(uint16_t Index,uint8_t Length)67 static ArrayRef<PrototypeDescriptor> ProtoSeq2ArrayRef(uint16_t Index,
68 uint8_t Length) {
69 return makeArrayRef(&RVVSignatureTable[Index], Length);
70 }
71
RVVType2Qual(ASTContext & Context,const RVVType * Type)72 static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type) {
73 QualType QT;
74 switch (Type->getScalarType()) {
75 case ScalarTypeKind::Void:
76 QT = Context.VoidTy;
77 break;
78 case ScalarTypeKind::Size_t:
79 QT = Context.getSizeType();
80 break;
81 case ScalarTypeKind::Ptrdiff_t:
82 QT = Context.getPointerDiffType();
83 break;
84 case ScalarTypeKind::UnsignedLong:
85 QT = Context.UnsignedLongTy;
86 break;
87 case ScalarTypeKind::SignedLong:
88 QT = Context.LongTy;
89 break;
90 case ScalarTypeKind::Boolean:
91 QT = Context.BoolTy;
92 break;
93 case ScalarTypeKind::SignedInteger:
94 QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), true);
95 break;
96 case ScalarTypeKind::UnsignedInteger:
97 QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), false);
98 break;
99 case ScalarTypeKind::Float:
100 switch (Type->getElementBitwidth()) {
101 case 64:
102 QT = Context.DoubleTy;
103 break;
104 case 32:
105 QT = Context.FloatTy;
106 break;
107 case 16:
108 QT = Context.Float16Ty;
109 break;
110 default:
111 llvm_unreachable("Unsupported floating point width.");
112 }
113 break;
114 case Invalid:
115 llvm_unreachable("Unhandled type.");
116 }
117 if (Type->isVector())
118 QT = Context.getScalableVectorType(QT, Type->getScale().getValue());
119
120 if (Type->isConstant())
121 QT = Context.getConstType(QT);
122
123 // Transform the type to a pointer as the last step, if necessary.
124 if (Type->isPointer())
125 QT = Context.getPointerType(QT);
126
127 return QT;
128 }
129
130 namespace {
131 class RISCVIntrinsicManagerImpl : public sema::RISCVIntrinsicManager {
132 private:
133 Sema &S;
134 ASTContext &Context;
135
136 // List of all RVV intrinsic.
137 std::vector<RVVIntrinsicDef> IntrinsicList;
138 // Mapping function name to index of IntrinsicList.
139 StringMap<size_t> Intrinsics;
140 // Mapping function name to RVVOverloadIntrinsicDef.
141 StringMap<RVVOverloadIntrinsicDef> OverloadIntrinsics;
142
143 // Create IntrinsicList
144 void InitIntrinsicList();
145
146 // Create RVVIntrinsicDef.
147 void InitRVVIntrinsic(const RVVIntrinsicRecord &Record, StringRef SuffixStr,
148 StringRef OverloadedSuffixStr, bool IsMask,
149 RVVTypes &Types);
150
151 // Create FunctionDecl for a vector intrinsic.
152 void CreateRVVIntrinsicDecl(LookupResult &LR, IdentifierInfo *II,
153 Preprocessor &PP, unsigned Index,
154 bool IsOverload);
155
156 public:
RISCVIntrinsicManagerImpl(clang::Sema & S)157 RISCVIntrinsicManagerImpl(clang::Sema &S) : S(S), Context(S.Context) {
158 InitIntrinsicList();
159 }
160
161 // Create RISC-V vector intrinsic and insert into symbol table if found, and
162 // return true, otherwise return false.
163 bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II,
164 Preprocessor &PP) override;
165 };
166 } // namespace
167
InitIntrinsicList()168 void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
169 const TargetInfo &TI = Context.getTargetInfo();
170 bool HasVectorFloat32 = TI.hasFeature("zve32f");
171 bool HasVectorFloat64 = TI.hasFeature("zve64d");
172 bool HasZvfh = TI.hasFeature("experimental-zvfh");
173 bool HasRV64 = TI.hasFeature("64bit");
174 bool HasFullMultiply = TI.hasFeature("v");
175
176 // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
177 // in RISCVVEmitter.cpp.
178 for (auto &Record : RVVIntrinsicRecords) {
179 // Create Intrinsics for each type and LMUL.
180 BasicType BaseType = BasicType::Unknown;
181 ArrayRef<PrototypeDescriptor> BasicProtoSeq =
182 ProtoSeq2ArrayRef(Record.PrototypeIndex, Record.PrototypeLength);
183 ArrayRef<PrototypeDescriptor> SuffixProto =
184 ProtoSeq2ArrayRef(Record.SuffixIndex, Record.SuffixLength);
185 ArrayRef<PrototypeDescriptor> OverloadedSuffixProto = ProtoSeq2ArrayRef(
186 Record.OverloadedSuffixIndex, Record.OverloadedSuffixSize);
187
188 llvm::SmallVector<PrototypeDescriptor> ProtoSeq =
189 RVVIntrinsic::computeBuiltinTypes(BasicProtoSeq, /*IsMasked=*/false,
190 /*HasMaskedOffOperand=*/false,
191 Record.HasVL, Record.NF);
192
193 llvm::SmallVector<PrototypeDescriptor> ProtoMaskSeq =
194 RVVIntrinsic::computeBuiltinTypes(BasicProtoSeq, /*IsMasked=*/true,
195 Record.HasMaskedOffOperand,
196 Record.HasVL, Record.NF);
197
198 for (unsigned int TypeRangeMaskShift = 0;
199 TypeRangeMaskShift <= static_cast<unsigned int>(BasicType::MaxOffset);
200 ++TypeRangeMaskShift) {
201 unsigned int BaseTypeI = 1 << TypeRangeMaskShift;
202 BaseType = static_cast<BasicType>(BaseTypeI);
203
204 if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI)
205 continue;
206
207 // Check requirement.
208 if (BaseType == BasicType::Float16 && !HasZvfh)
209 continue;
210
211 if (BaseType == BasicType::Float32 && !HasVectorFloat32)
212 continue;
213
214 if (BaseType == BasicType::Float64 && !HasVectorFloat64)
215 continue;
216
217 if (((Record.RequiredExtensions & RVV_REQ_RV64) == RVV_REQ_RV64) &&
218 !HasRV64)
219 continue;
220
221 if ((BaseType == BasicType::Int64) &&
222 ((Record.RequiredExtensions & RVV_REQ_FullMultiply) ==
223 RVV_REQ_FullMultiply) &&
224 !HasFullMultiply)
225 continue;
226
227 // Expanded with different LMUL.
228 for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) {
229 if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3))))
230 continue;
231
232 Optional<RVVTypes> Types =
233 RVVType::computeTypes(BaseType, Log2LMUL, Record.NF, ProtoSeq);
234
235 // Ignored to create new intrinsic if there are any illegal types.
236 if (!Types.hasValue())
237 continue;
238
239 std::string SuffixStr =
240 RVVIntrinsic::getSuffixStr(BaseType, Log2LMUL, SuffixProto);
241 std::string OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
242 BaseType, Log2LMUL, OverloadedSuffixProto);
243
244 // Create non-masked intrinsic.
245 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, false, *Types);
246
247 if (Record.HasMasked) {
248 // Create masked intrinsic.
249 Optional<RVVTypes> MaskTypes = RVVType::computeTypes(
250 BaseType, Log2LMUL, Record.NF, ProtoMaskSeq);
251
252 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, true,
253 *MaskTypes);
254 }
255 }
256 }
257 }
258 }
259
260 // Compute name and signatures for intrinsic with practical types.
InitRVVIntrinsic(const RVVIntrinsicRecord & Record,StringRef SuffixStr,StringRef OverloadedSuffixStr,bool IsMask,RVVTypes & Signature)261 void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
262 const RVVIntrinsicRecord &Record, StringRef SuffixStr,
263 StringRef OverloadedSuffixStr, bool IsMask, RVVTypes &Signature) {
264 // Function name, e.g. vadd_vv_i32m1.
265 std::string Name = Record.Name;
266 if (!SuffixStr.empty())
267 Name += "_" + SuffixStr.str();
268
269 if (IsMask)
270 Name += "_m";
271
272 // Overloaded function name, e.g. vadd.
273 std::string OverloadedName;
274 if (!Record.OverloadedName)
275 OverloadedName = StringRef(Record.Name).split("_").first.str();
276 else
277 OverloadedName = Record.OverloadedName;
278 if (!OverloadedSuffixStr.empty())
279 OverloadedName += "_" + OverloadedSuffixStr.str();
280
281 // clang built-in function name, e.g. __builtin_rvv_vadd.
282 std::string BuiltinName = "__builtin_rvv_" + std::string(Record.Name);
283 if (IsMask)
284 BuiltinName += "_m";
285
286 // Put into IntrinsicList.
287 size_t Index = IntrinsicList.size();
288 IntrinsicList.push_back({Name, OverloadedName, BuiltinName, Signature});
289
290 // Creating mapping to Intrinsics.
291 Intrinsics.insert({Name, Index});
292
293 // Get the RVVOverloadIntrinsicDef.
294 RVVOverloadIntrinsicDef &OverloadIntrinsicDef =
295 OverloadIntrinsics[OverloadedName];
296
297 // And added the index.
298 OverloadIntrinsicDef.Indexes.push_back(Index);
299 }
300
CreateRVVIntrinsicDecl(LookupResult & LR,IdentifierInfo * II,Preprocessor & PP,unsigned Index,bool IsOverload)301 void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult &LR,
302 IdentifierInfo *II,
303 Preprocessor &PP,
304 unsigned Index,
305 bool IsOverload) {
306 ASTContext &Context = S.Context;
307 RVVIntrinsicDef &IDef = IntrinsicList[Index];
308 RVVTypes Sigs = IDef.Signature;
309 size_t SigLength = Sigs.size();
310 RVVType *ReturnType = Sigs[0];
311 QualType RetType = RVVType2Qual(Context, ReturnType);
312 SmallVector<QualType, 8> ArgTypes;
313 QualType BuiltinFuncType;
314
315 // Skip return type, and convert RVVType to QualType for arguments.
316 for (size_t i = 1; i < SigLength; ++i)
317 ArgTypes.push_back(RVVType2Qual(Context, Sigs[i]));
318
319 FunctionProtoType::ExtProtoInfo PI(
320 Context.getDefaultCallingConvention(false, false, true));
321
322 PI.Variadic = false;
323
324 SourceLocation Loc = LR.getNameLoc();
325 BuiltinFuncType = Context.getFunctionType(RetType, ArgTypes, PI);
326 DeclContext *Parent = Context.getTranslationUnitDecl();
327
328 FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create(
329 Context, Parent, Loc, Loc, II, BuiltinFuncType, /*TInfo=*/nullptr,
330 SC_Extern, S.getCurFPFeatures().isFPConstrained(),
331 /*isInlineSpecified*/ false,
332 /*hasWrittenPrototype*/ true);
333
334 // Create Decl objects for each parameter, adding them to the
335 // FunctionDecl.
336 const auto *FP = cast<FunctionProtoType>(BuiltinFuncType);
337 SmallVector<ParmVarDecl *, 8> ParmList;
338 for (unsigned IParm = 0, E = FP->getNumParams(); IParm != E; ++IParm) {
339 ParmVarDecl *Parm =
340 ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr,
341 FP->getParamType(IParm), nullptr, SC_None, nullptr);
342 Parm->setScopeInfo(0, IParm);
343 ParmList.push_back(Parm);
344 }
345 RVVIntrinsicDecl->setParams(ParmList);
346
347 // Add function attributes.
348 if (IsOverload)
349 RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context));
350
351 // Setup alias to __builtin_rvv_*
352 IdentifierInfo &IntrinsicII = PP.getIdentifierTable().get(IDef.BuiltinName);
353 RVVIntrinsicDecl->addAttr(
354 BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII));
355
356 // Add to symbol table.
357 LR.addDecl(RVVIntrinsicDecl);
358 }
359
CreateIntrinsicIfFound(LookupResult & LR,IdentifierInfo * II,Preprocessor & PP)360 bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult &LR,
361 IdentifierInfo *II,
362 Preprocessor &PP) {
363 StringRef Name = II->getName();
364
365 // Lookup the function name from the overload intrinsics first.
366 auto OvIItr = OverloadIntrinsics.find(Name);
367 if (OvIItr != OverloadIntrinsics.end()) {
368 const RVVOverloadIntrinsicDef &OvIntrinsicDef = OvIItr->second;
369 for (auto Index : OvIntrinsicDef.Indexes)
370 CreateRVVIntrinsicDecl(LR, II, PP, Index,
371 /*IsOverload*/ true);
372
373 // If we added overloads, need to resolve the lookup result.
374 LR.resolveKind();
375 return true;
376 }
377
378 // Lookup the function name from the intrinsics.
379 auto Itr = Intrinsics.find(Name);
380 if (Itr != Intrinsics.end()) {
381 CreateRVVIntrinsicDecl(LR, II, PP, Itr->second,
382 /*IsOverload*/ false);
383 return true;
384 }
385
386 // It's not an RVV intrinsics.
387 return false;
388 }
389
390 namespace clang {
391 std::unique_ptr<clang::sema::RISCVIntrinsicManager>
CreateRISCVIntrinsicManager(Sema & S)392 CreateRISCVIntrinsicManager(Sema &S) {
393 return std::make_unique<RISCVIntrinsicManagerImpl>(S);
394 }
395 } // namespace clang
396