Dan Gohman
2007-Feb-05 18:08 UTC
[LLVMdev] automatically generating intrinsic declarations
LLVM knows what all the types of the intrinsic functions are; I thought, why are users (including llvm-gcc...) required to duplicate all this information in order to use them? I mean in order to call getOrInsertFunction to get declarations for them. So I wrote this patch, which allows all this code to be generated automatically. Is this a good approach? Dan -- Dan Gohman, Cray Inc. <djg at cray.com> -------------- next part -------------- Index: utils/TableGen/IntrinsicEmitter.cpp ==================================================================RCS file: /var/cvs/llvm/llvm/utils/TableGen/IntrinsicEmitter.cpp,v retrieving revision 1.22 diff -u -r1.22 IntrinsicEmitter.cpp --- utils/TableGen/IntrinsicEmitter.cpp +++ utils/TableGen/IntrinsicEmitter.cpp @@ -38,6 +38,9 @@ // Emit the intrinsic verifier. EmitVerifier(Ints, OS); + // Emit the intrinsic declaration generator. + EmitGenerator(Ints, OS); + // Emit mod/ref info for each function. EmitModRefInfo(Ints, OS); @@ -121,6 +124,19 @@ } } +static void EmitTypeGenerate(std::ostream &OS, Record *ArgType) { + if (ArgType->isSubClassOf("LLVMIntegerType")) { + OS << "IntegerType::get(" << ArgType->getValueAsInt("Width") << ")"; + } else if (ArgType->isSubClassOf("LLVMPackedType")) { + OS << "PackedType::get("; + EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy")); + OS << ", " << ArgType->getValueAsInt("NumElts") << ")"; + } else { + OS << "Type::getPrimitiveType("; + OS << ArgType->getValueAsString("TypeVal") << ")"; + } +} + /// RecordListComparator - Provide a determinstic comparator for lists of /// records. namespace { @@ -176,6 +192,43 @@ OS << "#endif\n\n"; } +void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, + std::ostream &OS) { + OS << "// Code for generating Intrinsic function declarations.\n"; + OS << "#ifdef GET_INTRINSIC_GENERATOR\n"; + OS << " switch (ID) {\n"; + OS << " default: assert(0 && \"Invalid intrinsic!\");\n"; + + // Similar to GET_INTRINSIC_VERIFIER, batch up cases that have identical + // types. + typedef std::map<std::vector<Record*>, std::vector<unsigned>, + RecordListComparator> MapTy; + MapTy UniqueArgInfos; + + // Compute the unique argument type info. + for (unsigned i = 0, e = Ints.size(); i != e; ++i) + UniqueArgInfos[Ints[i].ArgTypeDefs].push_back(i); + + // Loop through the array, emitting one generator for each batch. + for (MapTy::iterator I = UniqueArgInfos.begin(), + E = UniqueArgInfos.end(); I != E; ++I) { + for (unsigned i = 0, e = I->second.size(); i != e; ++i) { + OS << " case Intrinsic::" << Ints[I->second[i]].EnumName << ":\t\t// " + << Ints[I->second[i]].Name << "\n"; + } + + const std::vector<Record*> &ArgTypes = I->first; + OS << " return M->getOrInsertFunction(Intrinsic::getName(ID), "; + for (unsigned j = 0; j != ArgTypes.size(); ++j) { + EmitTypeGenerate(OS, ArgTypes[j]); + OS << ", "; + } + OS << "NULL);\n"; + } + OS << " }\n"; + OS << "#endif\n\n"; +} + void IntrinsicEmitter::EmitModRefInfo(const std::vector<CodeGenIntrinsic> &Ints, std::ostream &OS) { OS << "// BasicAliasAnalysis code.\n"; Index: utils/TableGen/IntrinsicEmitter.h ==================================================================RCS file: /var/cvs/llvm/llvm/utils/TableGen/IntrinsicEmitter.h,v retrieving revision 1.9 diff -u -r1.9 IntrinsicEmitter.h --- utils/TableGen/IntrinsicEmitter.h +++ utils/TableGen/IntrinsicEmitter.h @@ -35,6 +35,8 @@ std::ostream &OS); void EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints, std::ostream &OS); + void EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, + std::ostream &OS); void EmitModRefInfo(const std::vector<CodeGenIntrinsic> &Ints, std::ostream &OS); void EmitNoMemoryInfo(const std::vector<CodeGenIntrinsic> &Ints,
Chris Lattner
2007-Feb-05 20:28 UTC
[LLVMdev] automatically generating intrinsic declarations
On Mon, 5 Feb 2007, Dan Gohman wrote:> LLVM knows what all the types of the intrinsic functions are; I thought, > why are users (including llvm-gcc...) required to duplicate all this > information in order to use them? I mean in order to call > getOrInsertFunction to get declarations for them.That is an excellent question! :) In the bad old days, we used to allow intrinsics with slightly different types (e.g. http://llvm.org/PR1093). Because of this, we didn't know what type to create them as. Fortunately however, this misguided past is all behind us now, and there is no longer a reason to not have this facility.> So I wrote this patch, which allows all this code to be generated > automatically. Is this a good approach?This looks great, but appears to be missing the API that wraps the generated piece of Intrinsics.gen. If you send it as well, I'd be happy to commit it for you. Is there also a piece that switches llvm-gcc over to use this? :) Thanks! -Chris -- http://nondot.org/sabre/ http://llvm.org/
Dan Gohman
2007-Feb-06 14:41 UTC
[LLVMdev] automatically generating intrinsic declarations
On Mon, Feb 05, 2007 at 12:28:56PM -0800, Chris Lattner wrote:> On Mon, 5 Feb 2007, Dan Gohman wrote: > > > LLVM knows what all the types of the intrinsic functions are; I thought, > > why are users (including llvm-gcc...) required to duplicate all this > > information in order to use them? I mean in order to call > > getOrInsertFunction to get declarations for them. > > That is an excellent question! :) In the bad old days, we used to allow > intrinsics with slightly different types (e.g. http://llvm.org/PR1093). > Because of this, we didn't know what type to create them as. > > Fortunately however, this misguided past is all behind us now, and there > is no longer a reason to not have this facility.It appears there is at least one more. The llvm.gcroot intrinsic is documented as having generic argument types.> > So I wrote this patch, which allows all this code to be generated > > automatically. Is this a good approach? > > This looks great, but appears to be missing the API that wraps the > generated piece of Intrinsics.gen. If you send it as well, I'd be happy > to commit it for you. Is there also a piece that switches llvm-gcc over > to use this? :)Attached is a patch that includes the API bits. I also noticed that my earlier patch didn't handle pointer types, so I fixed that, and in doing so I noticed that the types for va_start, va_end, va_copy, and dbg.declare were apparently wrong, so I fixed those too. However, gcroot does currently come out as void @llvm.gcroot(i8**, i8*). Dan -- Dan Gohman, Cray Inc. <djg at cray.com> -------------- next part -------------- Index: include/llvm/Intrinsics.h ==================================================================RCS file: /var/cvs/llvm/llvm/include/llvm/Intrinsics.h,v retrieving revision 1.40 diff -u -r1.40 Intrinsics.h --- include/llvm/Intrinsics.h +++ include/llvm/Intrinsics.h @@ -18,6 +18,9 @@ namespace llvm { +class Constant; +class Module; + /// Intrinsic Namespace - This namespace contains an enum with a value for /// every intrinsic/builtin function known by LLVM. These enum values are /// returned by Function::getIntrinsicID(). @@ -36,6 +39,10 @@ /// Intrinsic::getName(ID) - Return the LLVM name for an intrinsic, such as /// "llvm.ppc.altivec.lvx". const char *getName(ID id); + + /// Intrinsic::getDeclaration(M, ID) - Create or insert an LLVM Function + /// declaration for an intrinsic, and return it. + Constant *getDeclaration(Module *M, ID id); } // End Intrinsic namespace } // End llvm namespace Index: include/llvm/Intrinsics.td ==================================================================RCS file: /var/cvs/llvm/llvm/include/llvm/Intrinsics.td,v retrieving revision 1.41 diff -u -r1.41 Intrinsics.td --- include/llvm/Intrinsics.td +++ include/llvm/Intrinsics.td @@ -68,6 +68,15 @@ LLVMType ElTy = elty; } +class LLVMPointerType<LLVMType elty> + : LLVMType<iPTR, "Type::PointerTyID">{ + LLVMType ElTy = elty; +} + +class LLVMEmptyStructType + : LLVMType<OtherVT, "Type::StructTyID">{ +} + def llvm_void_ty : LLVMType<isVoid, "Type::VoidTyID">; def llvm_bool_ty : LLVMIntegerType<i1, 1>; def llvm_i8_ty : LLVMIntegerType<i8 , 8>; @@ -76,9 +85,10 @@ def llvm_i64_ty : LLVMIntegerType<i64, 64>; def llvm_float_ty : LLVMType<f32, "Type::FloatTyID">; def llvm_double_ty : LLVMType<f64, "Type::DoubleTyID">; -def llvm_ptr_ty : LLVMType<iPTR, "Type::PointerTyID">; // i8* -def llvm_ptrptr_ty : LLVMType<iPTR, "Type::PointerTyID">; // i8** -def llvm_descriptor_ty : LLVMType<iPTR, "Type::PointerTyID">; // global* +def llvm_ptr_ty : LLVMPointerType<llvm_i8_ty>; // i8* +def llvm_ptrptr_ty : LLVMPointerType<llvm_ptr_ty>; // i8** +def llvm_empty_ty : LLVMEmptyStructType; // { } +def llvm_descriptor_ty : LLVMPointerType<llvm_empty_ty>; // { }* def llvm_v16i8_ty : LLVMPackedType<v16i8,16, llvm_i8_ty>; // 16 x i8 def llvm_v8i16_ty : LLVMPackedType<v8i16, 8, llvm_i16_ty>; // 8 x i16 @@ -121,10 +131,10 @@ //===--------------- Variable Argument Handling Intrinsics ----------------===// // -def int_vastart : Intrinsic<[llvm_void_ty, llvm_ptrptr_ty], [], "llvm.va_start">; -def int_vacopy : Intrinsic<[llvm_void_ty, llvm_ptrptr_ty, llvm_ptrptr_ty], [], +def int_vastart : Intrinsic<[llvm_void_ty, llvm_ptr_ty], [], "llvm.va_start">; +def int_vacopy : Intrinsic<[llvm_void_ty, llvm_ptr_ty, llvm_ptr_ty], [], "llvm.va_copy">; -def int_vaend : Intrinsic<[llvm_void_ty, llvm_ptrptr_ty], [], "llvm.va_end">; +def int_vaend : Intrinsic<[llvm_void_ty, llvm_ptr_ty], [], "llvm.va_end">; //===------------------- Garbage Collection Intrinsics --------------------===// // @@ -214,7 +224,7 @@ def int_dbg_region_start : Intrinsic<[llvm_void_ty, llvm_descriptor_ty]>; def int_dbg_region_end : Intrinsic<[llvm_void_ty, llvm_descriptor_ty]>; def int_dbg_func_start : Intrinsic<[llvm_void_ty, llvm_descriptor_ty]>; -def int_dbg_declare : Intrinsic<[llvm_void_ty, llvm_ptr_ty, +def int_dbg_declare : Intrinsic<[llvm_void_ty, llvm_descriptor_ty, llvm_descriptor_ty]>; //===----------------------------------------------------------------------===// Index: lib/VMCore/Function.cpp ==================================================================RCS file: /var/cvs/llvm/llvm/lib/VMCore/Function.cpp,v retrieving revision 1.111 diff -u -r1.111 Function.cpp --- lib/VMCore/Function.cpp +++ lib/VMCore/Function.cpp @@ -183,6 +183,12 @@ return Table[id]; } +Constant *Intrinsic::getDeclaration(Module *M, ID id) { +#define GET_INTRINSIC_GENERATOR +#include "llvm/Intrinsics.gen" +#undef GET_INTRINSIC_GENERATOR +} + Value *IntrinsicInst::StripPointerCasts(Value *Ptr) { if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Ptr)) { if (CE->getOpcode() == Instruction::BitCast) { Index: utils/TableGen/IntrinsicEmitter.cpp ==================================================================RCS file: /var/cvs/llvm/llvm/utils/TableGen/IntrinsicEmitter.cpp,v retrieving revision 1.22 diff -u -r1.22 IntrinsicEmitter.cpp --- utils/TableGen/IntrinsicEmitter.cpp +++ utils/TableGen/IntrinsicEmitter.cpp @@ -38,6 +38,9 @@ // Emit the intrinsic verifier. EmitVerifier(Ints, OS); + // Emit the intrinsic declaration generator. + EmitGenerator(Ints, OS); + // Emit mod/ref info for each function. EmitModRefInfo(Ints, OS); @@ -121,6 +124,25 @@ } } +static void EmitTypeGenerate(std::ostream &OS, Record *ArgType) { + if (ArgType->isSubClassOf("LLVMIntegerType")) { + OS << "IntegerType::get(" << ArgType->getValueAsInt("Width") << ")"; + } else if (ArgType->isSubClassOf("LLVMPackedType")) { + OS << "PackedType::get("; + EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy")); + OS << ", " << ArgType->getValueAsInt("NumElts") << ")"; + } else if (ArgType->isSubClassOf("LLVMPointerType")) { + OS << "PointerType::get("; + EmitTypeGenerate(OS, ArgType->getValueAsDef("ElTy")); + OS << ")"; + } else if (ArgType->isSubClassOf("LLVMEmptyStructType")) { + OS << "StructType::get(std::vector<const Type *>())"; + } else { + OS << "Type::getPrimitiveType("; + OS << ArgType->getValueAsString("TypeVal") << ")"; + } +} + /// RecordListComparator - Provide a determinstic comparator for lists of /// records. namespace { @@ -176,6 +198,43 @@ OS << "#endif\n\n"; } +void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, + std::ostream &OS) { + OS << "// Code for generating Intrinsic function declarations.\n"; + OS << "#ifdef GET_INTRINSIC_GENERATOR\n"; + OS << " switch (id) {\n"; + OS << " default: assert(0 && \"Invalid intrinsic!\");\n"; + + // Similar to GET_INTRINSIC_VERIFIER, batch up cases that have identical + // types. + typedef std::map<std::vector<Record*>, std::vector<unsigned>, + RecordListComparator> MapTy; + MapTy UniqueArgInfos; + + // Compute the unique argument type info. + for (unsigned i = 0, e = Ints.size(); i != e; ++i) + UniqueArgInfos[Ints[i].ArgTypeDefs].push_back(i); + + // Loop through the array, emitting one generator for each batch. + for (MapTy::iterator I = UniqueArgInfos.begin(), + E = UniqueArgInfos.end(); I != E; ++I) { + for (unsigned i = 0, e = I->second.size(); i != e; ++i) { + OS << " case Intrinsic::" << Ints[I->second[i]].EnumName << ":\t\t// " + << Ints[I->second[i]].Name << "\n"; + } + + const std::vector<Record*> &ArgTypes = I->first; + OS << " return M->getOrInsertFunction(Intrinsic::getName(id), "; + for (unsigned j = 0; j != ArgTypes.size(); ++j) { + EmitTypeGenerate(OS, ArgTypes[j]); + OS << ", "; + } + OS << "NULL);\n"; + } + OS << " }\n"; + OS << "#endif\n\n"; +} + void IntrinsicEmitter::EmitModRefInfo(const std::vector<CodeGenIntrinsic> &Ints, std::ostream &OS) { OS << "// BasicAliasAnalysis code.\n"; Index: utils/TableGen/IntrinsicEmitter.h ==================================================================RCS file: /var/cvs/llvm/llvm/utils/TableGen/IntrinsicEmitter.h,v retrieving revision 1.9 diff -u -r1.9 IntrinsicEmitter.h --- utils/TableGen/IntrinsicEmitter.h +++ utils/TableGen/IntrinsicEmitter.h @@ -35,6 +35,8 @@ std::ostream &OS); void EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints, std::ostream &OS); + void EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, + std::ostream &OS); void EmitModRefInfo(const std::vector<CodeGenIntrinsic> &Ints, std::ostream &OS); void EmitNoMemoryInfo(const std::vector<CodeGenIntrinsic> &Ints,