//===------- CGObjCEtoile.cpp - Emit LLVM Code from ASTs for a Module --------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This provides Objective-C code generation targetting the Etoile runtime. // //===----------------------------------------------------------------------===// #include "CGObjCRuntime.h" #include "llvm/Module.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/IRBuilder.h" #include "llvm/ADT/SmallVector.h" namespace { class CGObjCEtoile : public clang::CodeGen::CGObjCRuntime { private: llvm::Module &TheModule; const llvm::Type *SelectorTy; const llvm::PointerType *PtrToInt8Ty; const llvm::Type *IMPTy; const llvm::Type *IntTy; const llvm::Type *PtrTy; const llvm::Type *LongTy; const llvm::Type *PtrToIntTy; const llvm::Type *IdTy; const llvm::Type *CallTy; const llvm::Type *SlotTy; const llvm::Type *LookupFunctionTy; public: CGObjCEtoile(llvm::Module &Mp, const llvm::Type *LLVMIntType, const llvm::Type *LLVMLongType); virtual llvm::Value *generateMessageSend(llvm::IRBuilder &Builder, const llvm::Type *ReturnTy, llvm::Value *Sender, llvm::Value *Receiver, llvm::Value *Selector, llvm::Value** ArgV, unsigned ArgC); llvm::Value *getSelector(llvm::IRBuilder &Builder, llvm::Value *SelName, llvm::Value *SelTypes); virtual llvm::Function *MethodPreamble(const llvm::Type *ReturnTy, const llvm::Type *SelfTy, const llvm::Type **ArgTy, unsigned ArgC, bool isVarArg); }; } // end anonymous namespace CGObjCEtoile::CGObjCEtoile(llvm::Module &M, const llvm::Type *LLVMIntType, const llvm::Type *LLVMLongType) : TheModule(M), IntTy(LLVMIntType), LongTy(LLVMLongType) { // C string type. Used in lots of places. PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // Get the selector Type. SelectorTy = llvm::Type::Int32Ty; PtrToIntTy = llvm::PointerType::getUnqual(IntTy); PtrTy = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // Object type llvm::PATypeHolder OpaqueObjTy = llvm::OpaqueType::get(); llvm::Type *OpaqueIdTy = llvm::PointerType::getUnqual(OpaqueObjTy); IdTy = llvm::StructType::get(OpaqueIdTy, NULL); llvm::cast(OpaqueObjTy.get())->refineAbstractTypeTo(IdTy); IdTy = llvm::cast(OpaqueObjTy.get()); IdTy = llvm::PointerType::getUnqual(IdTy); // Call structure type. llvm::PATypeHolder OpaqueSlotTy = llvm::OpaqueType::get(); CallTy = llvm::StructType::get( llvm::PointerType::getUnqual(OpaqueSlotTy), SelectorTy, IdTy, NULL); //CallTy = llvm::PointerType::getUnqual(CallTy); // IMP type std::vector IMPArgs; IMPArgs.push_back(IdTy); IMPArgs.push_back(llvm::PointerType::getUnqual(CallTy)); IMPTy = llvm::FunctionType::get(IdTy, IMPArgs, true); // Slot type SlotTy = llvm::StructType::get(IntTy, IMPTy, PtrToInt8Ty, PtrToInt8Ty, llvm::Type::Int32Ty, NULL); llvm::cast( OpaqueSlotTy.get())->refineAbstractTypeTo(SlotTy); SlotTy = llvm::PointerType::getUnqual( llvm::cast(OpaqueSlotTy.get())); // Lookup function type std::vector LookupFunctionArgs; LookupFunctionArgs.push_back(llvm::PointerType::getUnqual(IdTy)); LookupFunctionArgs.push_back(IdTy); LookupFunctionArgs.push_back(SelectorTy); LookupFunctionArgs.push_back(IdTy); LookupFunctionTy = llvm::FunctionType::get(SlotTy, LookupFunctionArgs, false); LookupFunctionTy = llvm::PointerType::getUnqual(LookupFunctionTy); } /// Looks up the selector for the specified name / type pair. llvm::Value *CGObjCEtoile::getSelector(llvm::IRBuilder &Builder, llvm::Value *SelName, llvm::Value *SelTypes) { // Look up the selector. if(SelTypes == 0) { SelTypes = llvm::ConstantPointerNull::get(PtrToInt8Ty); } llvm::Constant *SelFunction = TheModule.getOrInsertFunction("lookup_typed_selector", SelectorTy, PtrToInt8Ty, PtrToInt8Ty, NULL); return Builder.CreateCall2(SelFunction, SelName, SelTypes); } static void SetField(llvm::IRBuilder &Builder, llvm::Value *Structure, unsigned Index, llvm::Value *Value) { llvm::Value *element_ptr = Builder.CreateStructGEP(Structure, Index); Builder.CreateStore(Value, element_ptr); } // Generate code for a message send expression on the Etoile runtime. // BIG FAT WARNING: Much of this code will need factoring out later. llvm::Value *CGObjCEtoile::generateMessageSend(llvm::IRBuilder &Builder, const llvm::Type *ReturnTy, llvm::Value *Sender, llvm::Value *Receiver, llvm::Value *Selector, llvm::Value** ArgV, unsigned ArgC) { // FIXME: Selectors should be statically cached, not looked up on every call. llvm::Value *cmd = getSelector(Builder, Selector, 0); // TODO: [Polymorphic] inline caching // Get the lookup function for this object: llvm::Value *ObjAddr = Builder.CreateBitCast(Receiver, PtrToInt8Ty); llvm::Value *FunctionOffset = new llvm::GlobalVariable(llvm::Type::Int32Ty, false, llvm::GlobalValue::ExternalLinkage, 0, "lookup_offset", &TheModule); FunctionOffset = Builder.CreateLoad(FunctionOffset); llvm::Value *Tag = Builder.CreateGEP(ObjAddr, FunctionOffset); llvm::Value *Lookup = Builder.CreateBitCast(Tag, LookupFunctionTy); // TODO: Remove this when the caller is providing sensible sender info if(Sender == 0) { Sender = llvm::ConstantPointerNull::get((llvm::PointerType*)IdTy); } Receiver = Builder.CreateBitCast(Receiver, IdTy); llvm::Value *ReceiverAddr = Builder.CreateAlloca(IdTy); Builder.CreateStore(Receiver, ReceiverAddr); // Look up the method implementation. llvm::SmallVector LookupArgs; LookupArgs.push_back(ReceiverAddr); LookupArgs.push_back(Receiver); LookupArgs.push_back(cmd); LookupArgs.push_back(Sender); llvm::Value *Slot = Builder.CreateCall(Lookup, LookupArgs.begin(), LookupArgs.end()); // Create the call structure llvm::Value *Call = Builder.CreateAlloca(CallTy); SetField(Builder, Call, 0, Slot); SetField(Builder, Call, 1, cmd); SetField(Builder, Call, 2, Sender); // Get the IMP from the slot and call it // TODO: Property load / store optimisations llvm::Value *IMP = Builder.CreateStructGEP(Slot, 1); // If the return type of the IMP is wrong, cast it so it isn't. if(ReturnTy != IdTy) { std::vector IMPArgs; IMPArgs.push_back(IdTy); IMPArgs.push_back(llvm::PointerType::getUnqual(CallTy)); llvm::Type *NewIMPTy = llvm::FunctionType::get(ReturnTy, IMPArgs, true); IMP = Builder.CreateBitCast(IMP, llvm::PointerType::getUnqual(NewIMPTy)); } llvm::SmallVector Args; Args.push_back(Receiver); Args.push_back(Call); Args.insert(Args.end(), ArgV, ArgV+ArgC); return Builder.CreateCall(IMP, Args.begin(), Args.end()); } /// Generates an LLVM Function object corresponding to the Objective-C method, /// including the implicit arguments. llvm::Function *CGObjCEtoile::MethodPreamble( const llvm::Type *ReturnTy, const llvm::Type *SelfTy, const llvm::Type **ArgTy, unsigned ArgC, bool isVarArg) { std::vector Args; //Args.push_back(SelfTy); Args.push_back(IdTy); Args.push_back(llvm::PointerType::getUnqual(CallTy)); for (unsigned i=0; i