[OpenCL] Group builtin functions by prototype

The TableGen-generated file containing the function definitions can be
reorganized to save some memory in the Clang binary.  Functions having
the same prototype(s) will point to a shared list of prototype(s).

Patch by Pierre Gondois and Sven van Haastregt.

Differential Revision: https://reviews.llvm.org/D63557
This commit is contained in:
Sven van Haastregt 2019-11-05 10:16:45 +00:00
parent 9a8d477a0e
commit 0e56b0f94b
1 changed files with 135 additions and 12 deletions

View File

@ -69,6 +69,13 @@
using namespace llvm;
namespace {
// A list of signatures that are shared by one or more builtin functions.
struct BuiltinTableEntries {
SmallVector<StringRef, 4> Names;
std::vector<std::pair<const Record *, unsigned>> Signatures;
};
class BuiltinNameEmitter {
public:
BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS)
@ -79,6 +86,9 @@ public:
void Emit();
private:
// A list of indices into the builtin function table.
using BuiltinIndexListTy = SmallVector<unsigned, 11>;
// Contains OpenCL builtin functions and related information, stored as
// Record instances. They are coming from the associated TableGen file.
RecordKeeper &Records;
@ -106,6 +116,23 @@ private:
// FctOverloadMap and TypeMap.
void GetOverloads();
// Compare two lists of signatures and check that e.g. the OpenCL version,
// function attributes, and extension are equal for each signature.
// \param Candidate (in) Entry in the SignatureListMap to check.
// \param SignatureList (in) List of signatures of the considered function.
// \returns true if the two lists of signatures are identical.
bool CanReuseSignature(
BuiltinIndexListTy *Candidate,
std::vector<std::pair<const Record *, unsigned>> &SignatureList);
// Group functions with the same list of signatures by populating the
// SignatureListMap.
// Some builtin functions have the same list of signatures, for example the
// "sin" and "cos" functions. To save space in the BuiltinTable, the
// "isOpenCLBuiltin" function will have the same output for these two
// function names.
void GroupBySignature();
// Emit the TypeTable containing all types used by OpenCL builtins.
void EmitTypeTable();
@ -170,6 +197,24 @@ private:
// Same as TypeList, but for generic types only.
std::vector<const Record *> GenTypeList;
// Map an ordered vector of signatures to their original Record instances,
// and to a list of function names that share these signatures.
//
// For example, suppose the "cos" and "sin" functions have only three
// signatures, and these signatures are at index Ix in the SignatureTable:
// cos | sin | Signature | Index
// float cos(float) | float sin(float) | Signature1 | I1
// double cos(double) | double sin(double) | Signature2 | I2
// half cos(half) | half sin(half) | Signature3 | I3
//
// Then we will create a mapping of the vector of signatures:
// SignatureListMap[<I1, I2, I3>] = <
// <"cos", "sin">,
// <Signature1, Signature2, Signature3>>
// The function "tan", having the same signatures, would be mapped to the
// same entry (<I1, I2, I3>).
MapVector<BuiltinIndexListTy *, BuiltinTableEntries> SignatureListMap;
};
} // namespace
@ -183,6 +228,7 @@ void BuiltinNameEmitter::Emit() {
EmitDeclarations();
GetOverloads();
GroupBySignature();
// Emit tables.
EmitTypeTable();
@ -408,11 +454,15 @@ void BuiltinNameEmitter::EmitBuiltinTable() {
unsigned Index = 0;
OS << "static const OpenCLBuiltinStruct BuiltinTable[] = {\n";
for (const auto &FOM : FctOverloadMap) {
for (const auto &SLM : SignatureListMap) {
OS << " // " << (Index + 1) << ": " << FOM.first << "\n";
OS << " // " << (Index + 1) << ": ";
for (const auto &Name : SLM.second.Names) {
OS << Name << ", ";
}
OS << "\n";
for (const auto &Overload : FOM.second) {
for (const auto &Overload : SLM.second.Signatures) {
OS << " { " << Overload.second << ", "
<< Overload.first->getValueAsListOfDefs("Signature").size() << ", "
<< (Overload.first->getValueAsBit("IsPure")) << ", "
@ -428,19 +478,92 @@ void BuiltinNameEmitter::EmitBuiltinTable() {
OS << "};\n\n";
}
bool BuiltinNameEmitter::CanReuseSignature(
BuiltinIndexListTy *Candidate,
std::vector<std::pair<const Record *, unsigned>> &SignatureList) {
assert(Candidate->size() == SignatureList.size() &&
"signature lists should have the same size");
auto &CandidateSigs =
SignatureListMap.find(Candidate)->second.Signatures;
for (unsigned Index = 0; Index < Candidate->size(); Index++) {
const Record *Rec = SignatureList[Index].first;
const Record *Rec2 = CandidateSigs[Index].first;
if (Rec->getValueAsBit("IsPure") == Rec2->getValueAsBit("IsPure") &&
Rec->getValueAsBit("IsConst") == Rec2->getValueAsBit("IsConst") &&
Rec->getValueAsBit("IsConv") == Rec2->getValueAsBit("IsConv") &&
Rec->getValueAsDef("MinVersion")->getValueAsInt("ID") ==
Rec2->getValueAsDef("MinVersion")->getValueAsInt("ID") &&
Rec->getValueAsDef("MaxVersion")->getValueAsInt("ID") ==
Rec2->getValueAsDef("MaxVersion")->getValueAsInt("ID") &&
Rec->getValueAsString("Extension") ==
Rec2->getValueAsString("Extension")) {
return true;
}
}
return false;
}
void BuiltinNameEmitter::GroupBySignature() {
// List of signatures known to be emitted.
std::vector<BuiltinIndexListTy *> KnownSignatures;
for (auto &Fct : FctOverloadMap) {
bool FoundReusableSig = false;
// Gather all signatures for the current function.
auto *CurSignatureList = new BuiltinIndexListTy();
for (const auto &Signature : Fct.second) {
CurSignatureList->push_back(Signature.second);
}
// Sort the list to facilitate future comparisons.
std::sort(CurSignatureList->begin(), CurSignatureList->end());
// Check if we have already seen another function with the same list of
// signatures. If so, just add the name of the function.
for (auto *Candidate : KnownSignatures) {
if (Candidate->size() == CurSignatureList->size() &&
*Candidate == *CurSignatureList) {
if (CanReuseSignature(Candidate, Fct.second)) {
SignatureListMap.find(Candidate)->second.Names.push_back(Fct.first);
FoundReusableSig = true;
}
}
}
if (FoundReusableSig) {
delete CurSignatureList;
} else {
// Add a new entry.
SignatureListMap[CurSignatureList] = {
SmallVector<StringRef, 4>(1, Fct.first), Fct.second};
KnownSignatures.push_back(CurSignatureList);
}
}
for (auto *I : KnownSignatures) {
delete I;
}
}
void BuiltinNameEmitter::EmitStringMatcher() {
std::vector<StringMatcher::StringPair> ValidBuiltins;
unsigned CumulativeIndex = 1;
for (auto &i : FctOverloadMap) {
auto &Ov = i.second;
std::string RetStmt;
raw_string_ostream SS(RetStmt);
SS << "return std::make_pair(" << CumulativeIndex << ", " << Ov.size()
<< ");";
SS.flush();
CumulativeIndex += Ov.size();
ValidBuiltins.push_back(StringMatcher::StringPair(i.first, RetStmt));
for (const auto &SLM : SignatureListMap) {
const auto &Ovl = SLM.second.Signatures;
// A single signature list may be used by different builtins. Return the
// same <index, length> pair for each of those builtins.
for (const auto &FctName : SLM.second.Names) {
std::string RetStmt;
raw_string_ostream SS(RetStmt);
SS << "return std::make_pair(" << CumulativeIndex << ", " << Ovl.size()
<< ");";
SS.flush();
ValidBuiltins.push_back(StringMatcher::StringPair(FctName, RetStmt));
}
CumulativeIndex += Ovl.size();
}
OS << R"(