Skip to content

Commit 8866793

Browse files
committed
[OpenCL] Add OpenCL builtin test generator
Add a new clang-tblgen flag `-gen-clang-opencl-builtin-tests` that generates a .cl file containing calls to every builtin function defined in the .td input. This patch does not add any use of the new flag yet, so the only way to obtain a generated test file is through a manual invocation of clang-tblgen. A test making use of this emitter will be added in a followup commit. Differential Revision: https://reviews.llvm.org/D97869
1 parent 630820b commit 8866793

File tree

3 files changed

+289
-0
lines changed

3 files changed

+289
-0
lines changed

clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,64 @@ class BuiltinNameEmitter {
228228
// same entry (<I1, I2, I3>).
229229
MapVector<BuiltinIndexListTy *, BuiltinTableEntries> SignatureListMap;
230230
};
231+
232+
// OpenCL builtin test generator. This class processes the same TableGen input
233+
// as BuiltinNameEmitter, but generates a .cl file that contains a call to each
234+
// builtin function described in the .td input.
235+
class OpenCLBuiltinTestEmitter {
236+
public:
237+
OpenCLBuiltinTestEmitter(RecordKeeper &Records, raw_ostream &OS)
238+
: Records(Records), OS(OS) {}
239+
240+
// Entrypoint to generate the functions for testing all OpenCL builtin
241+
// functions.
242+
void emit();
243+
244+
private:
245+
struct TypeFlags {
246+
TypeFlags() : IsConst(false), IsVolatile(false), IsPointer(false) {}
247+
bool IsConst : 1;
248+
bool IsVolatile : 1;
249+
bool IsPointer : 1;
250+
StringRef AddrSpace;
251+
};
252+
253+
// Return a string representation of the given type, such that it can be
254+
// used as a type in OpenCL C code.
255+
std::string getTypeString(const Record *Type, TypeFlags Flags,
256+
int VectorSize) const;
257+
258+
// Return the type(s) and vector size(s) for the given type. For
259+
// non-GenericTypes, the resulting vectors will contain 1 element. For
260+
// GenericTypes, the resulting vectors typically contain multiple elements.
261+
void getTypeLists(Record *Type, TypeFlags &Flags,
262+
std::vector<Record *> &TypeList,
263+
std::vector<int64_t> &VectorList) const;
264+
265+
// Expand the TableGen Records representing a builtin function signature into
266+
// one or more function signatures. Return them as a vector of a vector of
267+
// strings, with each string containing an OpenCL C type and optional
268+
// qualifiers.
269+
//
270+
// The Records may contain GenericTypes, which expand into multiple
271+
// signatures. Repeated occurrences of GenericType in a signature expand to
272+
// the same types. For example [char, FGenType, FGenType] expands to:
273+
// [char, float, float]
274+
// [char, float2, float2]
275+
// [char, float3, float3]
276+
// ...
277+
void
278+
expandTypesInSignature(const std::vector<Record *> &Signature,
279+
SmallVectorImpl<SmallVector<std::string, 2>> &Types);
280+
281+
// Contains OpenCL builtin functions and related information, stored as
282+
// Record instances. They are coming from the associated TableGen file.
283+
RecordKeeper &Records;
284+
285+
// The output file.
286+
raw_ostream &OS;
287+
};
288+
231289
} // namespace
232290

233291
void BuiltinNameEmitter::Emit() {
@@ -861,7 +919,230 @@ static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty,
861919
OS << "\n} // OCL2Qual\n";
862920
}
863921

922+
std::string OpenCLBuiltinTestEmitter::getTypeString(const Record *Type,
923+
TypeFlags Flags,
924+
int VectorSize) const {
925+
std::string S;
926+
if (Type->getValueAsBit("IsConst") || Flags.IsConst) {
927+
S += "const ";
928+
}
929+
if (Type->getValueAsBit("IsVolatile") || Flags.IsVolatile) {
930+
S += "volatile ";
931+
}
932+
933+
auto PrintAddrSpace = [&S](StringRef AddrSpace) {
934+
S += StringSwitch<const char *>(AddrSpace)
935+
.Case("clang::LangAS::opencl_private", "__private")
936+
.Case("clang::LangAS::opencl_global", "__global")
937+
.Case("clang::LangAS::opencl_constant", "__constant")
938+
.Case("clang::LangAS::opencl_local", "__local")
939+
.Case("clang::LangAS::opencl_generic", "__generic")
940+
.Default("__private");
941+
S += " ";
942+
};
943+
if (Flags.IsPointer) {
944+
PrintAddrSpace(Flags.AddrSpace);
945+
} else if (Type->getValueAsBit("IsPointer")) {
946+
PrintAddrSpace(Type->getValueAsString("AddrSpace"));
947+
}
948+
949+
StringRef Acc = Type->getValueAsString("AccessQualifier");
950+
if (Acc != "") {
951+
S += StringSwitch<const char *>(Acc)
952+
.Case("RO", "__read_only ")
953+
.Case("WO", "__write_only ")
954+
.Case("RW", "__read_write ");
955+
}
956+
957+
S += Type->getValueAsString("Name").str();
958+
if (VectorSize > 1) {
959+
S += std::to_string(VectorSize);
960+
}
961+
962+
if (Type->getValueAsBit("IsPointer") || Flags.IsPointer) {
963+
S += " *";
964+
}
965+
966+
return S;
967+
}
968+
969+
void OpenCLBuiltinTestEmitter::getTypeLists(
970+
Record *Type, TypeFlags &Flags, std::vector<Record *> &TypeList,
971+
std::vector<int64_t> &VectorList) const {
972+
bool isGenType = Type->isSubClassOf("GenericType");
973+
if (isGenType) {
974+
TypeList = Type->getValueAsDef("TypeList")->getValueAsListOfDefs("List");
975+
VectorList =
976+
Type->getValueAsDef("VectorList")->getValueAsListOfInts("List");
977+
return;
978+
}
979+
980+
if (Type->isSubClassOf("PointerType") || Type->isSubClassOf("ConstType") ||
981+
Type->isSubClassOf("VolatileType")) {
982+
StringRef SubTypeName = Type->getValueAsString("Name");
983+
Record *PossibleGenType = Records.getDef(SubTypeName);
984+
if (PossibleGenType && PossibleGenType->isSubClassOf("GenericType")) {
985+
// When PointerType, ConstType, or VolatileType is applied to a
986+
// GenericType, the flags need to be taken from the subtype, not from the
987+
// GenericType.
988+
Flags.IsPointer = Type->getValueAsBit("IsPointer");
989+
Flags.IsConst = Type->getValueAsBit("IsConst");
990+
Flags.IsVolatile = Type->getValueAsBit("IsVolatile");
991+
Flags.AddrSpace = Type->getValueAsString("AddrSpace");
992+
getTypeLists(PossibleGenType, Flags, TypeList, VectorList);
993+
return;
994+
}
995+
}
996+
997+
// Not a GenericType, so just insert the single type.
998+
TypeList.push_back(Type);
999+
VectorList.push_back(Type->getValueAsInt("VecWidth"));
1000+
}
1001+
1002+
void OpenCLBuiltinTestEmitter::expandTypesInSignature(
1003+
const std::vector<Record *> &Signature,
1004+
SmallVectorImpl<SmallVector<std::string, 2>> &Types) {
1005+
// Find out if there are any GenTypes in this signature, and if so, calculate
1006+
// into how many signatures they will expand.
1007+
unsigned NumSignatures = 1;
1008+
SmallVector<SmallVector<std::string, 4>, 4> ExpandedGenTypes;
1009+
for (const auto &Arg : Signature) {
1010+
SmallVector<std::string, 4> ExpandedArg;
1011+
std::vector<Record *> TypeList;
1012+
std::vector<int64_t> VectorList;
1013+
TypeFlags Flags;
1014+
1015+
getTypeLists(Arg, Flags, TypeList, VectorList);
1016+
1017+
// Insert the Cartesian product of the types and vector sizes.
1018+
for (const auto &Vector : VectorList) {
1019+
for (const auto &Type : TypeList) {
1020+
ExpandedArg.push_back(getTypeString(Type, Flags, Vector));
1021+
}
1022+
}
1023+
NumSignatures = std::max<unsigned>(NumSignatures, ExpandedArg.size());
1024+
ExpandedGenTypes.push_back(ExpandedArg);
1025+
}
1026+
1027+
// Now the total number of signatures is known. Populate the return list with
1028+
// all signatures.
1029+
for (unsigned I = 0; I < NumSignatures; I++) {
1030+
SmallVector<std::string, 2> Args;
1031+
1032+
// Process a single signature.
1033+
for (unsigned ArgNum = 0; ArgNum < Signature.size(); ArgNum++) {
1034+
// For differently-sized GenTypes in a parameter list, the smaller
1035+
// GenTypes just repeat, so index modulo the number of expanded types.
1036+
size_t TypeIndex = I % ExpandedGenTypes[ArgNum].size();
1037+
Args.push_back(ExpandedGenTypes[ArgNum][TypeIndex]);
1038+
}
1039+
Types.push_back(Args);
1040+
}
1041+
}
1042+
1043+
void OpenCLBuiltinTestEmitter::emit() {
1044+
emitSourceFileHeader("OpenCL Builtin exhaustive testing", OS);
1045+
1046+
// Enable some extensions for testing.
1047+
OS << R"(
1048+
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
1049+
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
1050+
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
1051+
#pragma OPENCL EXTENSION cl_khr_int64_extended_atomics : enable
1052+
#pragma OPENCL EXTENSION cl_khr_gl_msaa_sharing : enable
1053+
#pragma OPENCL EXTENSION cl_khr_mipmap_image_writes : enable
1054+
#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable
1055+
1056+
)";
1057+
1058+
// Ensure each test has a unique name by numbering them.
1059+
unsigned TestID = 0;
1060+
1061+
// Iterate over all builtins.
1062+
std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin");
1063+
for (const auto *B : Builtins) {
1064+
StringRef Name = B->getValueAsString("Name");
1065+
1066+
SmallVector<SmallVector<std::string, 2>, 4> FTypes;
1067+
expandTypesInSignature(B->getValueAsListOfDefs("Signature"), FTypes);
1068+
1069+
OS << "// Test " << Name << "\n";
1070+
std::string OptionalEndif;
1071+
StringRef Extensions =
1072+
B->getValueAsDef("Extension")->getValueAsString("ExtName");
1073+
if (!Extensions.empty()) {
1074+
OS << "#if";
1075+
OptionalEndif = "#endif // Extension\n";
1076+
1077+
SmallVector<StringRef, 2> ExtVec;
1078+
Extensions.split(ExtVec, " ");
1079+
bool isFirst = true;
1080+
for (StringRef Ext : ExtVec) {
1081+
if (!isFirst) {
1082+
OS << " &&";
1083+
}
1084+
OS << " defined(" << Ext << ")";
1085+
isFirst = false;
1086+
}
1087+
OS << "\n";
1088+
}
1089+
auto PrintOpenCLVersion = [this](int Version) {
1090+
OS << "CL_VERSION_" << (Version / 100) << "_" << ((Version % 100) / 10);
1091+
};
1092+
int MinVersion = B->getValueAsDef("MinVersion")->getValueAsInt("ID");
1093+
if (MinVersion != 100) {
1094+
// OpenCL 1.0 is the default minimum version.
1095+
OS << "#if __OPENCL_C_VERSION__ >= ";
1096+
PrintOpenCLVersion(MinVersion);
1097+
OS << "\n";
1098+
OptionalEndif = "#endif // MinVersion\n" + OptionalEndif;
1099+
}
1100+
int MaxVersion = B->getValueAsDef("MaxVersion")->getValueAsInt("ID");
1101+
if (MaxVersion) {
1102+
OS << "#if __OPENCL_C_VERSION__ < ";
1103+
PrintOpenCLVersion(MaxVersion);
1104+
OS << "\n";
1105+
OptionalEndif = "#endif // MaxVersion\n" + OptionalEndif;
1106+
}
1107+
for (const auto &Signature : FTypes) {
1108+
// Emit function declaration.
1109+
OS << Signature[0] << " test" << TestID++ << "_" << Name << "(";
1110+
if (Signature.size() > 1) {
1111+
for (unsigned I = 1; I < Signature.size(); I++) {
1112+
if (I != 1)
1113+
OS << ", ";
1114+
OS << Signature[I] << " arg" << I;
1115+
}
1116+
}
1117+
OS << ") {\n";
1118+
1119+
// Emit function body.
1120+
OS << " ";
1121+
if (Signature[0] != "void") {
1122+
OS << "return ";
1123+
}
1124+
OS << Name << "(";
1125+
for (unsigned I = 1; I < Signature.size(); I++) {
1126+
if (I != 1)
1127+
OS << ", ";
1128+
OS << "arg" << I;
1129+
}
1130+
OS << ");\n";
1131+
1132+
// End of function body.
1133+
OS << "}\n";
1134+
}
1135+
OS << OptionalEndif << "\n";
1136+
}
1137+
}
1138+
8641139
void clang::EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) {
8651140
BuiltinNameEmitter NameChecker(Records, OS);
8661141
NameChecker.Emit();
8671142
}
1143+
1144+
void clang::EmitClangOpenCLBuiltinTests(RecordKeeper &Records,
1145+
raw_ostream &OS) {
1146+
OpenCLBuiltinTestEmitter TestFileGenerator(Records, OS);
1147+
TestFileGenerator.emit();
1148+
}

clang/utils/TableGen/TableGen.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum ActionType {
6363
GenClangCommentCommandInfo,
6464
GenClangCommentCommandList,
6565
GenClangOpenCLBuiltins,
66+
GenClangOpenCLBuiltinTests,
6667
GenArmNeon,
6768
GenArmFP16,
6869
GenArmBF16,
@@ -194,6 +195,8 @@ cl::opt<ActionType> Action(
194195
"documentation comments"),
195196
clEnumValN(GenClangOpenCLBuiltins, "gen-clang-opencl-builtins",
196197
"Generate OpenCL builtin declaration handlers"),
198+
clEnumValN(GenClangOpenCLBuiltinTests, "gen-clang-opencl-builtin-tests",
199+
"Generate OpenCL builtin declaration tests"),
197200
clEnumValN(GenArmNeon, "gen-arm-neon", "Generate arm_neon.h for clang"),
198201
clEnumValN(GenArmFP16, "gen-arm-fp16", "Generate arm_fp16.h for clang"),
199202
clEnumValN(GenArmBF16, "gen-arm-bf16", "Generate arm_bf16.h for clang"),
@@ -371,6 +374,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
371374
case GenClangOpenCLBuiltins:
372375
EmitClangOpenCLBuiltins(Records, OS);
373376
break;
377+
case GenClangOpenCLBuiltinTests:
378+
EmitClangOpenCLBuiltinTests(Records, OS);
379+
break;
374380
case GenClangSyntaxNodeList:
375381
EmitClangSyntaxNodeList(Records, OS);
376382
break;

clang/utils/TableGen/TableGenBackends.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ void EmitClangOptDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
122122

123123
void EmitClangOpenCLBuiltins(llvm::RecordKeeper &Records,
124124
llvm::raw_ostream &OS);
125+
void EmitClangOpenCLBuiltinTests(llvm::RecordKeeper &Records,
126+
llvm::raw_ostream &OS);
125127

126128
void EmitClangDataCollectors(llvm::RecordKeeper &Records,
127129
llvm::raw_ostream &OS);

0 commit comments

Comments
 (0)