From a98aa6ad1e8e53600b2e322e2ab869e03d436849 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 16 May 2012 06:34:44 +0000 Subject: [PATCH] Significantly reduce the compiled size of Functions.cpp by turning a big blob of tblgen generated code (for Intrinsic::getType) into a table. This handles common cases right now, but I plan to extend it to handle all cases and merge in type verification logic as well in follow-on patches. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@156905 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/VMCore/Function.cpp | 50 +++- utils/TableGen/IntrinsicEmitter.cpp | 424 ++++++++++++++++++++-------- 2 files changed, 351 insertions(+), 123 deletions(-) diff --git a/lib/VMCore/Function.cpp b/lib/VMCore/Function.cpp index 9b098a25250..eb5ffb38c81 100644 --- a/lib/VMCore/Function.cpp +++ b/lib/VMCore/Function.cpp @@ -29,7 +29,6 @@ #include "llvm/ADT/StringExtras.h" using namespace llvm; - // Explicit instantiations of SymbolTableListTraits since some of the methods // are not in the public header file... template class llvm::SymbolTableListTraits; @@ -358,17 +357,57 @@ std::string Intrinsic::getName(ID id, ArrayRef Tys) { return Result; } +#define GET_INTRINSTIC_GENERATOR_GLOBAL +#include "llvm/Intrinsics.gen" +#undef GET_INTRINSTIC_GENERATOR_GLOBAL + +static Type *DecodeFixedType(unsigned &TableVal, LLVMContext &Context) { + unsigned Nibble = TableVal & 0xF; + TableVal >>= 4; + + switch ((IIT_Info)Nibble) { + case IIT_Done: return Type::getVoidTy(Context); + case IIT_I1: return Type::getInt1Ty(Context); + case IIT_I8: return Type::getInt8Ty(Context); + case IIT_I16: return Type::getInt16Ty(Context); + case IIT_I32: return Type::getInt32Ty(Context); + case IIT_I64: return Type::getInt64Ty(Context); + case IIT_F32: return Type::getFloatTy(Context); + case IIT_F64: return Type::getDoubleTy(Context); + case IIT_V2: return VectorType::get(DecodeFixedType(TableVal, Context), 2); + case IIT_V4: return VectorType::get(DecodeFixedType(TableVal, Context), 4); + case IIT_V8: return VectorType::get(DecodeFixedType(TableVal, Context), 8); + case IIT_V16: return VectorType::get(DecodeFixedType(TableVal, Context), 16); + case IIT_MMX: return Type::getX86_MMXTy(Context); + case IIT_PTR: return PointerType::get(DecodeFixedType(TableVal, Context),0); + case IIT_ARG: assert(0 && "Unimp!"); + } + llvm_unreachable("unhandled"); +} + + FunctionType *Intrinsic::getType(LLVMContext &Context, - ID id, ArrayRef Tys) { - Type *ResultTy = NULL; + ID id, ArrayRef Tys) { + Type *ResultTy = 0; SmallVector ArgTys; - bool IsVarArg = false; + + // Check to see if the intrinsic's type was expressible by the table. + unsigned TableVal = IIT_Table[id-1]; + if (TableVal != ~0U) { + ResultTy = DecodeFixedType(TableVal, Context); + + while (TableVal) + ArgTys.push_back(DecodeFixedType(TableVal, Context)); + + return FunctionType::get(ResultTy, ArgTys, false); + } + #define GET_INTRINSIC_GENERATOR #include "llvm/Intrinsics.gen" #undef GET_INTRINSIC_GENERATOR - return FunctionType::get(ResultTy, ArgTys, IsVarArg); + return FunctionType::get(ResultTy, ArgTys, false); } bool Intrinsic::isOverloaded(ID id) { @@ -440,4 +479,3 @@ bool Function::callsFunctionThatReturnsTwice() const { return false; } -// vim: sw=2 ai diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index be1fd639f7b..d487c276080 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -174,109 +174,6 @@ EmitIntrinsicToOverloadTable(const std::vector &Ints, OS << "#endif\n\n"; } -static void EmitTypeForValueType(raw_ostream &OS, MVT::SimpleValueType VT) { - if (EVT(VT).isInteger()) { - unsigned BitWidth = EVT(VT).getSizeInBits(); - OS << "IntegerType::get(Context, " << BitWidth << ")"; - } else if (VT == MVT::Other) { - // MVT::OtherVT is used to mean the empty struct type here. - OS << "StructType::get(Context)"; - } else if (VT == MVT::f16) { - OS << "Type::getHalfTy(Context)"; - } else if (VT == MVT::f32) { - OS << "Type::getFloatTy(Context)"; - } else if (VT == MVT::f64) { - OS << "Type::getDoubleTy(Context)"; - } else if (VT == MVT::f80) { - OS << "Type::getX86_FP80Ty(Context)"; - } else if (VT == MVT::f128) { - OS << "Type::getFP128Ty(Context)"; - } else if (VT == MVT::ppcf128) { - OS << "Type::getPPC_FP128Ty(Context)"; - } else if (VT == MVT::isVoid) { - OS << "Type::getVoidTy(Context)"; - } else if (VT == MVT::Metadata) { - OS << "Type::getMetadataTy(Context)"; - } else if (VT == MVT::x86mmx) { - OS << "Type::getX86_MMXTy(Context)"; - } else { - assert(false && "Unsupported ValueType!"); - } -} - -static void EmitTypeGenerate(raw_ostream &OS, const Record *ArgType, - unsigned &ArgNo); - -static void EmitTypeGenerate(raw_ostream &OS, - const std::vector &ArgTypes, - unsigned &ArgNo) { - if (ArgTypes.empty()) - return EmitTypeForValueType(OS, MVT::isVoid); - - if (ArgTypes.size() == 1) - return EmitTypeGenerate(OS, ArgTypes.front(), ArgNo); - - OS << "StructType::get("; - - for (std::vector::const_iterator - I = ArgTypes.begin(), E = ArgTypes.end(); I != E; ++I) { - EmitTypeGenerate(OS, *I, ArgNo); - OS << ", "; - } - - OS << " NULL)"; -} - -static void EmitTypeGenerate(raw_ostream &OS, const Record *ArgType, - unsigned &ArgNo) { - MVT::SimpleValueType VT = getValueType(ArgType->getValueAsDef("VT")); - - if (ArgType->isSubClassOf("LLVMMatchType")) { - unsigned Number = ArgType->getValueAsInt("Number"); - assert(Number < ArgNo && "Invalid matching number!"); - if (ArgType->isSubClassOf("LLVMExtendedElementVectorType")) - OS << "VectorType::getExtendedElementVectorType" - << "(cast(Tys[" << Number << "]))"; - else if (ArgType->isSubClassOf("LLVMTruncatedElementVectorType")) - OS << "VectorType::getTruncatedElementVectorType" - << "(cast(Tys[" << Number << "]))"; - else - OS << "Tys[" << Number << "]"; - } else if (VT == MVT::iAny || VT == MVT::fAny || VT == MVT::vAny) { - // NOTE: The ArgNo variable here is not the absolute argument number, it is - // the index of the "arbitrary" type in the Tys array passed to the - // Intrinsic::getDeclaration function. Consequently, we only want to - // increment it when we actually hit an overloaded type. Getting this wrong - // leads to very subtle bugs! - OS << "Tys[" << ArgNo++ << "]"; - } else if (EVT(VT).isVector()) { - EVT VVT = VT; - OS << "VectorType::get("; - EmitTypeForValueType(OS, VVT.getVectorElementType().getSimpleVT().SimpleTy); - OS << ", " << VVT.getVectorNumElements() << ")"; - } else if (VT == MVT::iPTR) { - OS << "PointerType::getUnqual("; - EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy"), ArgNo); - OS << ")"; - } else if (VT == MVT::iPTRAny) { - // Make sure the user has passed us an argument type to overload. If not, - // treat it as an ordinary (not overloaded) intrinsic. - OS << "(" << ArgNo << " < Tys.size()) ? Tys[" << ArgNo - << "] : PointerType::getUnqual("; - EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy"), ArgNo); - OS << ")"; - ++ArgNo; - } else if (VT == MVT::isVoid) { - if (ArgNo == 0) - OS << "Type::getVoidTy(Context)"; - else - // MVT::isVoid is used to mean varargs here. - OS << "..."; - } else { - EmitTypeForValueType(OS, VT); - } -} - /// RecordListComparator - Provide a deterministic comparator for lists of /// records. namespace { @@ -411,22 +308,322 @@ void IntrinsicEmitter::EmitVerifier(const std::vector &Ints, OS << "#endif\n\n"; } +static void EmitTypeForValueType(raw_ostream &OS, MVT::SimpleValueType VT) { + if (EVT(VT).isInteger()) { + unsigned BitWidth = EVT(VT).getSizeInBits(); + OS << "IntegerType::get(Context, " << BitWidth << ")"; + } else if (VT == MVT::Other) { + // MVT::OtherVT is used to mean the empty struct type here. + OS << "StructType::get(Context)"; + } else if (VT == MVT::f16) { + OS << "Type::getHalfTy(Context)"; + } else if (VT == MVT::f32) { + OS << "Type::getFloatTy(Context)"; + } else if (VT == MVT::f64) { + OS << "Type::getDoubleTy(Context)"; + } else if (VT == MVT::f80) { + OS << "Type::getX86_FP80Ty(Context)"; + } else if (VT == MVT::f128) { + OS << "Type::getFP128Ty(Context)"; + } else if (VT == MVT::ppcf128) { + OS << "Type::getPPC_FP128Ty(Context)"; + } else if (VT == MVT::isVoid) { + OS << "Type::getVoidTy(Context)"; + } else if (VT == MVT::Metadata) { + OS << "Type::getMetadataTy(Context)"; + } else if (VT == MVT::x86mmx) { + OS << "Type::getX86_MMXTy(Context)"; + } else { + assert(false && "Unsupported ValueType!"); + } +} + +static void EmitTypeGenerate(raw_ostream &OS, const Record *ArgType, + unsigned &ArgNo); + +static void EmitTypeGenerate(raw_ostream &OS, + const std::vector &ArgTypes, + unsigned &ArgNo) { + if (ArgTypes.empty()) + return EmitTypeForValueType(OS, MVT::isVoid); + + if (ArgTypes.size() == 1) + return EmitTypeGenerate(OS, ArgTypes.front(), ArgNo); + + OS << "StructType::get("; + + for (std::vector::const_iterator + I = ArgTypes.begin(), E = ArgTypes.end(); I != E; ++I) { + EmitTypeGenerate(OS, *I, ArgNo); + OS << ", "; + } + + OS << " NULL)"; +} + +static void EmitTypeGenerate(raw_ostream &OS, const Record *ArgType, + unsigned &ArgNo) { + MVT::SimpleValueType VT = getValueType(ArgType->getValueAsDef("VT")); + + if (ArgType->isSubClassOf("LLVMMatchType")) { + unsigned Number = ArgType->getValueAsInt("Number"); + assert(Number < ArgNo && "Invalid matching number!"); + if (ArgType->isSubClassOf("LLVMExtendedElementVectorType")) + OS << "VectorType::getExtendedElementVectorType" + << "(cast(Tys[" << Number << "]))"; + else if (ArgType->isSubClassOf("LLVMTruncatedElementVectorType")) + OS << "VectorType::getTruncatedElementVectorType" + << "(cast(Tys[" << Number << "]))"; + else + OS << "Tys[" << Number << "]"; + } else if (VT == MVT::iAny || VT == MVT::fAny || VT == MVT::vAny) { + // NOTE: The ArgNo variable here is not the absolute argument number, it is + // the index of the "arbitrary" type in the Tys array passed to the + // Intrinsic::getDeclaration function. Consequently, we only want to + // increment it when we actually hit an overloaded type. Getting this wrong + // leads to very subtle bugs! + OS << "Tys[" << ArgNo++ << "]"; + } else if (EVT(VT).isVector()) { + EVT VVT = VT; + OS << "VectorType::get("; + EmitTypeForValueType(OS, VVT.getVectorElementType().getSimpleVT().SimpleTy); + OS << ", " << VVT.getVectorNumElements() << ")"; + } else if (VT == MVT::iPTR) { + OS << "PointerType::getUnqual("; + EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy"), ArgNo); + OS << ")"; + } else if (VT == MVT::iPTRAny) { + // Make sure the user has passed us an argument type to overload. If not, + // treat it as an ordinary (not overloaded) intrinsic. + OS << "(" << ArgNo << " < Tys.size()) ? Tys[" << ArgNo + << "] : PointerType::getUnqual("; + EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy"), ArgNo); + OS << ")"; + ++ArgNo; + } else if (VT == MVT::isVoid) { + assert(ArgNo == 0); + OS << "Type::getVoidTy(Context)"; + } else { + EmitTypeForValueType(OS, VT); + } +} + + +// NOTE: This must be kept in synch with the version emitted to the .gen file! +enum IIT_Info { + IIT_Done = 0, + IIT_I1 = 1, + IIT_I8 = 2, + IIT_I16 = 3, + IIT_I32 = 4, + IIT_I64 = 5, + IIT_F32 = 6, + IIT_F64 = 7, + IIT_V2 = 8, + IIT_V4 = 9, + IIT_V8 = 10, + IIT_V16 = 11, + IIT_MMX = 12, + IIT_PTR = 13, + IIT_ARG = 14 +}; + +static void EncodeFixedValueType(MVT::SimpleValueType VT, + SmallVectorImpl &Sig) { + if (EVT(VT).isInteger()) { + unsigned BitWidth = EVT(VT).getSizeInBits(); + switch (BitWidth) { + default: return Sig.push_back(~0U); + case 1: return Sig.push_back(IIT_I1); + case 8: return Sig.push_back(IIT_I8); + case 16: return Sig.push_back(IIT_I16); + case 32: return Sig.push_back(IIT_I32); + case 64: return Sig.push_back(IIT_I64); + } + } + +/* } else if (VT == MVT::Other) { + // MVT::OtherVT is used to mean the empty struct type here. + OS << "StructType::get(Context)"; + } else if (VT == MVT::f16) { + OS << "Type::getHalfTy(Context)";*/ + if (VT == MVT::f32) + return Sig.push_back(IIT_F32); + if (VT == MVT::f64) + return Sig.push_back(IIT_F64); + //if (VT == MVT::f80) { + // OS << "Type::getX86_FP80Ty(Context)"; + //if (VT == MVT::f128) { + // OS << "Type::getFP128Ty(Context)"; + // if (VT == MVT::ppcf128) { + // OS << "Type::getPPC_FP128Ty(Context)"; + //if (VT == MVT::Metadata) { + // OS << "Type::getMetadataTy(Context)"; + if (VT == MVT::x86mmx) + return Sig.push_back(IIT_MMX); + + assert(VT != MVT::isVoid); + Sig.push_back(~0U); +} + + +static void EncodeFixedType(Record *R, SmallVectorImpl &Sig) { + + if (R->isSubClassOf("LLVMMatchType")) { + return Sig.push_back(~0U); +/* + unsigned Number = ArgType->getValueAsInt("Number"); + assert(Number < ArgNo && "Invalid matching number!"); + if (ArgType->isSubClassOf("LLVMExtendedElementVectorType")) + OS << "VectorType::getExtendedElementVectorType" + << "(cast(Tys[" << Number << "]))"; + else if (ArgType->isSubClassOf("LLVMTruncatedElementVectorType")) + OS << "VectorType::getTruncatedElementVectorType" + << "(cast(Tys[" << Number << "]))"; + else + OS << "Tys[" << Number << "]"; + */ + } + + MVT::SimpleValueType VT = getValueType(R->getValueAsDef("VT")); + + if (VT == MVT::iAny || VT == MVT::fAny || VT == MVT::vAny) { + return Sig.push_back(~0U); + /* + // NOTE: The ArgNo variable here is not the absolute argument number, it is + // the index of the "arbitrary" type in the Tys array passed to the + // Intrinsic::getDeclaration function. Consequently, we only want to + // increment it when we actually hit an overloaded type. Getting this wrong + // leads to very subtle bugs! + OS << "Tys[" << ArgNo++ << "]"; + */ + } + + if (EVT(VT).isVector()) { + EVT VVT = VT; + switch (VVT.getVectorNumElements()) { + default: Sig.push_back(~0U); return; + case 2: Sig.push_back(IIT_V2); break; + case 4: Sig.push_back(IIT_V4); break; + case 8: Sig.push_back(IIT_V8); break; + case 16: Sig.push_back(IIT_V16); break; + } + + return EncodeFixedValueType(VVT.getVectorElementType(). + getSimpleVT().SimpleTy, Sig); + } + + if (VT == MVT::iPTR) { + Sig.push_back(IIT_PTR); + return EncodeFixedType(R->getValueAsDef("ElTy"), Sig); + } + + /*if (VT == MVT::iPTRAny) { + // Make sure the user has passed us an argument type to overload. If not, + // treat it as an ordinary (not overloaded) intrinsic. + OS << "(" << ArgNo << " < Tys.size()) ? Tys[" << ArgNo + << "] : PointerType::getUnqual("; + EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy"), ArgNo); + OS << ")"; + ++ArgNo; + }*/ + + assert(VT != MVT::isVoid); + EncodeFixedValueType(VT, Sig); +} + +/// ComputeFixedEncoding - If we can encode the type signature for this +/// intrinsic into 32 bits, return it. If not, return ~0U. +static unsigned ComputeFixedEncoding(const CodeGenIntrinsic &Int) { + if (Int.IS.RetVTs.size() >= 2) return ~0U; + + SmallVector TypeSig; + if (Int.IS.RetVTs.empty()) + TypeSig.push_back(IIT_Done); + else if (Int.IS.RetVTs.size() == 1 && + Int.IS.RetVTs[0] == MVT::isVoid) + TypeSig.push_back(IIT_Done); + else + EncodeFixedType(Int.IS.RetTypeDefs[0], TypeSig); + + for (unsigned i = 0, e = Int.IS.ParamTypeDefs.size(); i != e; ++i) + EncodeFixedType(Int.IS.ParamTypeDefs[i], TypeSig); + + // Can only encode 8 nibbles into a 32-bit word. + if (TypeSig.size() > 8) return ~0U; + + unsigned Result = 0; + for (unsigned i = 0, e = TypeSig.size(); i != e; ++i) { + // If we had an unencodable argument, bail out. + if (TypeSig[i] == ~0U) + return ~0U; + Result = (Result << 4) | TypeSig[e-i-1]; + } + + return Result; +} + void IntrinsicEmitter::EmitGenerator(const std::vector &Ints, raw_ostream &OS) { - OS << "// Code for generating Intrinsic function declarations.\n"; - OS << "#ifdef GET_INTRINSIC_GENERATOR\n"; - OS << " switch (id) {\n"; - OS << " default: llvm_unreachable(\"Invalid intrinsic!\");\n"; + OS << "// Global intrinsic function declaration type table.\n"; + OS << "#ifdef GET_INTRINSTIC_GENERATOR_GLOBAL\n"; + // NOTE: These enums must be kept in sync with the ones above! + OS << "enum IIT_Info {\n"; + OS << " IIT_Done = 0,\n"; + OS << " IIT_I1 = 1,\n"; + OS << " IIT_I8 = 2,\n"; + OS << " IIT_I16 = 3,\n"; + OS << " IIT_I32 = 4,\n"; + OS << " IIT_I64 = 5,\n"; + OS << " IIT_F32 = 6,\n"; + OS << " IIT_F64 = 7,\n"; + OS << " IIT_V2 = 8,\n"; + OS << " IIT_V4 = 9,\n"; + OS << " IIT_V8 = 10,\n"; + OS << " IIT_V16 = 11,\n"; + OS << " IIT_MMX = 12,\n"; + OS << " IIT_PTR = 13,\n"; + OS << " IIT_ARG = 14\n"; + // 15 is unassigned so far. + OS << "};\n\n"; + // Similar to GET_INTRINSIC_VERIFIER, batch up cases that have identical // types. typedef std::map, RecordListComparator> MapTy; MapTy UniqueArgInfos; + + // If we can compute a 32-bit fixed encoding for this intrinsic, do so and + // capture it in this vector, otherwise store a ~0U. + std::vector FixedEncodings; // Compute the unique argument type info. - for (unsigned i = 0, e = Ints.size(); i != e; ++i) - UniqueArgInfos[make_pair(Ints[i].IS.RetTypeDefs, - Ints[i].IS.ParamTypeDefs)].push_back(i); + for (unsigned i = 0, e = Ints.size(); i != e; ++i) { + FixedEncodings.push_back(ComputeFixedEncoding(Ints[i])); + + // If we didn't compute a compact encoding, emit a long-form variant. + if (FixedEncodings.back() == ~0U) + UniqueArgInfos[make_pair(Ints[i].IS.RetTypeDefs, + Ints[i].IS.ParamTypeDefs)].push_back(i); + } + + OS << "static const unsigned IIT_Table[] = {\n "; + + for (unsigned i = 0, e = FixedEncodings.size(); i != e; ++i) { + if ((i & 7) == 7) + OS << "\n "; + if (FixedEncodings[i] == ~0U) + OS << "~0U, "; + else + OS << "0x" << utohexstr(FixedEncodings[i]) << ", "; + } + + OS << "0\n};\n\n#endif\n\n"; // End of GET_INTRINSTIC_GENERATOR_GLOBAL + + OS << "// Code for generating Intrinsic function declarations.\n"; + OS << "#ifdef GET_INTRINSIC_GENERATOR\n"; + OS << " switch (id) {\n"; + OS << " default: llvm_unreachable(\"Invalid intrinsic!\");\n"; // Loop through the array, emitting one generator for each batch. std::string IntrinsicStr = TargetPrefix + "Intrinsic::"; @@ -442,13 +639,6 @@ void IntrinsicEmitter::EmitGenerator(const std::vector &Ints, const std::vector &ParamTys = ArgTypes.second; unsigned N = ParamTys.size(); - - if (N > 1 && - getValueType(ParamTys[N - 1]->getValueAsDef("VT")) == MVT::isVoid) { - OS << " IsVarArg = true;\n"; - --N; - } - unsigned ArgNo = 0; OS << " ResultTy = "; EmitTypeGenerate(OS, RetTys, ArgNo);