Michael Kruse 27f3002974 [llvm-(min-)tblgen] Avoid redundant source compilation (#114494)
All the sources of `llvm-min-tblgen` are also used for `llvm-tblgen`,
with identical compilation flags. Reuse the object files of
`llvm-min-tblgen` for `llvm-tblgen` by applying the usual source
structure of an executable: One file per executable which named after
the executable name containing the (in this case trivial) main function,
which just calls the tblgen_main in TableGen.cpp. This should also clear
up any confusion (including mine) of where each executable's main
function is.

While this slightly reduces build time, the main motivation is ccache.
Using the hard_link
option, building the object files for `llvm-tblgen` will result in a
hard link to the same object file already used for `llvm-min-tblgen`. To
signal the build system that the file is new, ccache will update the
file's time stamp. Unfortunately, time stamps are shared between all
hard-linked files s.t. this will indirectly also update the time stamps
for the object files used for `llvm-tblgen`. At the next run, Ninja will
recognize this time stamp discrepancy to the expected stamp recorded in
`.ninja_log` and rebuild those object files for `llvm-min-tblgen`, which
again will also update the stamp for the `llvm-tblgen`... . This is
especially annoying for tablegen because it means Ninja will re-run all
tablegenning in every build.

I am using the hard_link option because it reduces the cost of having
multiple build-trees of the LLVM sources and reduces the wear to the SSD
they are stored on.
2025-01-03 09:41:57 +01:00

217 lines
7.1 KiB
C++

//===- VTEmitter.cpp - Generate properties from ValueTypes.td -------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <cassert>
#include <map>
using namespace llvm;
namespace {
class VTEmitter {
private:
const RecordKeeper &Records;
public:
VTEmitter(const RecordKeeper &R) : Records(R) {}
void run(raw_ostream &OS);
};
} // End anonymous namespace.
static void vTtoGetLlvmTyString(raw_ostream &OS, const Record *VT) {
bool IsVector = VT->getValueAsBit("isVector");
bool IsRISCVVecTuple = VT->getValueAsBit("isRISCVVecTuple");
if (IsRISCVVecTuple) {
unsigned NElem = VT->getValueAsInt("nElem");
unsigned Sz = VT->getValueAsInt("Size");
OS << "TargetExtType::get(Context, \"riscv.vector.tuple\", "
"ScalableVectorType::get(Type::getInt8Ty(Context), "
<< (Sz / (NElem * 8)) << "), " << NElem << ")";
return;
}
if (IsVector)
OS << (VT->getValueAsBit("isScalable") ? "Scalable" : "Fixed")
<< "VectorType::get(";
auto OutputVT = IsVector ? VT->getValueAsDef("ElementType") : VT;
int64_t OutputVTSize = OutputVT->getValueAsInt("Size");
if (OutputVT->getValueAsBit("isFP")) {
StringRef FloatTy;
auto OutputVTName = OutputVT->getValueAsString("LLVMName");
switch (OutputVTSize) {
default:
llvm_unreachable("Unhandled case");
case 16:
FloatTy = (OutputVTName == "bf16") ? "BFloatTy" : "HalfTy";
break;
case 32:
FloatTy = "FloatTy";
break;
case 64:
FloatTy = "DoubleTy";
break;
case 80:
FloatTy = "X86_FP80Ty";
break;
case 128:
FloatTy = (OutputVTName == "ppcf128") ? "PPC_FP128Ty" : "FP128Ty";
break;
}
OS << "Type::get" << FloatTy << "(Context)";
} else if (OutputVT->getValueAsBit("isInteger")) {
// We only have Type::getInt1Ty, Int8, Int16, Int32, Int64, and Int128
if ((isPowerOf2_64(OutputVTSize) && OutputVTSize >= 8 &&
OutputVTSize <= 128) ||
OutputVTSize == 1)
OS << "Type::getInt" << OutputVTSize << "Ty(Context)";
else
OS << "Type::getIntNTy(Context, " << OutputVTSize << ")";
} else
llvm_unreachable("Unhandled case");
if (IsVector)
OS << ", " << VT->getValueAsInt("nElem") << ")";
}
void VTEmitter::run(raw_ostream &OS) {
emitSourceFileHeader("ValueTypes Source Fragment", OS, Records);
std::vector<const Record *> VTsByNumber{512};
for (auto *VT : Records.getAllDerivedDefinitions("ValueType")) {
auto Number = VT->getValueAsInt("Value");
assert(0 <= Number && Number < (int)VTsByNumber.size() &&
"ValueType should be uint16_t");
assert(!VTsByNumber[Number] && "Duplicate ValueType");
VTsByNumber[Number] = VT;
}
struct VTRange {
StringRef First;
StringRef Last;
bool Closed;
};
std::map<StringRef, VTRange> VTRanges;
auto UpdateVTRange = [&VTRanges](const char *Key, StringRef Name,
bool Valid) {
if (Valid) {
if (!VTRanges.count(Key))
VTRanges[Key].First = Name;
assert(!VTRanges[Key].Closed && "Gap detected!");
VTRanges[Key].Last = Name;
} else if (VTRanges.count(Key)) {
VTRanges[Key].Closed = true;
}
};
OS << "#ifdef GET_VT_ATTR // (Ty, n, sz, Any, Int, FP, Vec, Sc, Tup, NF, "
"NElem, EltTy)\n";
for (const auto *VT : VTsByNumber) {
if (!VT)
continue;
auto Name = VT->getValueAsString("LLVMName");
auto Value = VT->getValueAsInt("Value");
bool IsInteger = VT->getValueAsBit("isInteger");
bool IsFP = VT->getValueAsBit("isFP");
bool IsVector = VT->getValueAsBit("isVector");
bool IsScalable = VT->getValueAsBit("isScalable");
bool IsRISCVVecTuple = VT->getValueAsBit("isRISCVVecTuple");
int64_t NF = VT->getValueAsInt("NF");
bool IsNormalValueType = VT->getValueAsBit("isNormalValueType");
int64_t NElem = IsVector ? VT->getValueAsInt("nElem") : 0;
StringRef EltName = IsVector ? VT->getValueAsDef("ElementType")->getName()
: "INVALID_SIMPLE_VALUE_TYPE";
UpdateVTRange("INTEGER_FIXEDLEN_VECTOR_VALUETYPE", Name,
IsInteger && IsVector && !IsScalable);
UpdateVTRange("INTEGER_SCALABLE_VECTOR_VALUETYPE", Name,
IsInteger && IsScalable);
UpdateVTRange("FP_FIXEDLEN_VECTOR_VALUETYPE", Name,
IsFP && IsVector && !IsScalable);
UpdateVTRange("FP_SCALABLE_VECTOR_VALUETYPE", Name, IsFP && IsScalable);
UpdateVTRange("FIXEDLEN_VECTOR_VALUETYPE", Name, IsVector && !IsScalable);
UpdateVTRange("SCALABLE_VECTOR_VALUETYPE", Name, IsScalable);
UpdateVTRange("RISCV_VECTOR_TUPLE_VALUETYPE", Name, IsRISCVVecTuple);
UpdateVTRange("VECTOR_VALUETYPE", Name, IsVector);
UpdateVTRange("INTEGER_VALUETYPE", Name, IsInteger && !IsVector);
UpdateVTRange("FP_VALUETYPE", Name, IsFP && !IsVector);
UpdateVTRange("VALUETYPE", Name, IsNormalValueType);
// clang-format off
OS << " GET_VT_ATTR("
<< Name << ", "
<< Value << ", "
<< VT->getValueAsInt("Size") << ", "
<< VT->getValueAsBit("isOverloaded") << ", "
<< (IsInteger ? Name[0] == 'i' ? 3 : 1 : 0) << ", "
<< (IsFP ? Name[0] == 'f' ? 3 : 1 : 0) << ", "
<< IsVector << ", "
<< IsScalable << ", "
<< IsRISCVVecTuple << ", "
<< NF << ", "
<< NElem << ", "
<< EltName << ")\n";
// clang-format on
}
OS << "#endif\n\n";
OS << "#ifdef GET_VT_RANGES\n";
for (const auto &KV : VTRanges) {
assert(KV.second.Closed);
OS << " FIRST_" << KV.first << " = " << KV.second.First << ",\n"
<< " LAST_" << KV.first << " = " << KV.second.Last << ",\n";
}
OS << "#endif\n\n";
OS << "#ifdef GET_VT_VECATTR // (Ty, Sc, Tup, nElem, ElTy)\n";
for (const auto *VT : VTsByNumber) {
if (!VT || !VT->getValueAsBit("isVector"))
continue;
const auto *ElTy = VT->getValueAsDef("ElementType");
assert(ElTy);
// clang-format off
OS << " GET_VT_VECATTR("
<< VT->getValueAsString("LLVMName") << ", "
<< VT->getValueAsBit("isScalable") << ", "
<< VT->getValueAsBit("isRISCVVecTuple") << ", "
<< VT->getValueAsInt("nElem") << ", "
<< ElTy->getName() << ")\n";
// clang-format on
}
OS << "#endif\n\n";
OS << "#ifdef GET_VT_EVT\n";
for (const auto *VT : VTsByNumber) {
if (!VT)
continue;
bool IsInteger = VT->getValueAsBit("isInteger");
bool IsVector = VT->getValueAsBit("isVector");
bool IsFP = VT->getValueAsBit("isFP");
bool IsRISCVVecTuple = VT->getValueAsBit("isRISCVVecTuple");
if (!IsInteger && !IsVector && !IsFP && !IsRISCVVecTuple)
continue;
OS << " GET_VT_EVT(" << VT->getValueAsString("LLVMName") << ", ";
vTtoGetLlvmTyString(OS, VT);
OS << ")\n";
}
OS << "#endif\n\n";
}
static TableGen::Emitter::OptClass<VTEmitter> X("gen-vt", "Generate ValueType");