#include "LLVMCompat.h" #include "llvm/Analysis/Verifier.h" #include "llvm/ADT/DenseSet.h" #include "ObjectiveCOpts.h" #include using namespace llvm; using std::string; typedef std::pair Replacement; namespace llvm { template<> struct DenseMapInfo { static inline Replacement getEmptyKey() { return Replacement(0,0); } static inline Replacement getTombstoneKey() { return Replacement(0, (Value*)-1); } static unsigned getHashValue(const Replacement& Val) { return ((uintptr_t)Val.first) * 37U; } static bool isEqual(const Replacement& LHS, const Replacement& RHS) { return LHS.first == RHS.first; } }; } namespace { class GNUNonfragileIvarPass : public FunctionPass { public: static char ID; GNUNonfragileIvarPass() : FunctionPass(ID) {} Module *M; size_t PointerSize; virtual bool doInitialization(Module &Mod) { M = &Mod; PointerSize = 8; if (M->getPointerSize() == Module::Pointer32) PointerSize = 4; return false; } std::string getSuperName(Constant *ClsStruct) { User *super = cast(ClsStruct->getOperand(1)); if (isa(super)) return ""; GlobalVariable *name = cast(super->getOperand(0)); #if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0)) return cast(name->getInitializer())->getAsString(); #else return cast(name->getInitializer())->getAsString(); #endif } size_t sizeOfClass(const std::string &className) { // This is a root class if ("" == className) { return 0; } // These root classes are assumed to only have one ivar: isa if (className.compare(0, 8, "NSObject") == 0 || className.compare(0, 6, "Object") == 0) { return PointerSize; } GlobalVariable *Cls = M->getGlobalVariable("_OBJC_CLASS_" + className); if (!Cls) return 0; Constant *ClsStruct = Cls->getInitializer(); // Size is initialized to be negative for the non-fragile ABI. ConstantInt *Size = cast(ClsStruct->getOperand(5)); int s = Size->getSExtValue(); // If we find a fragile class in the hierarchy, don't perform the // simplification. This means that we're the mixed ABI, so we need the // extra indirection. if (s > 0) return 0; return sizeOfClass(getSuperName(ClsStruct)) - Size->getSExtValue(); } size_t hardCodedOffset(const StringRef &className, const StringRef &ivarName) { GlobalVariable *Cls = M->getGlobalVariable(("_OBJC_CLASS_" + className).str(), true); if (!Cls) return 0; Constant *ClsStruct = Cls->getInitializer(); size_t superSize = sizeOfClass(getSuperName(ClsStruct)); if (!superSize) return 0; ConstantStruct *IvarStruct = cast( cast(ClsStruct->getOperand(6))->getInitializer()); int ivarCount = cast(IvarStruct->getOperand(0))->getSExtValue(); Constant *ivars = IvarStruct->getOperand(1); for (int i=0 ; i(ivars->getOperand(i)); GlobalVariable *name = cast( cast(ivar->getOperand(0))->getOperand(0)); std::string ivarNameStr = #if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0)) cast(name->getInitializer())->getAsString(); #else cast(name->getInitializer())->getAsString(); #endif // Remove the NULL terminator from the metadata string ivarNameStr.resize(ivarNameStr.size() - 1); if (ivarNameStr == ivarName.str()) return superSize + cast(ivar->getOperand(2))->getSExtValue(); } return 0; } virtual bool runOnFunction(Function &F) { bool modified = false; llvm::DenseSet replacements; //llvm::cerr << "IvarPass: " << F.getName() << "\n"; for (Function::iterator i=F.begin(), end=F.end() ; i != end ; ++i) { for (BasicBlock::iterator b=i->begin(), last=i->end() ; b != last ; ++b) { if (LoadInst *indirectload = dyn_cast(b)) { if (LoadInst *load = dyn_cast(indirectload->getOperand(0))) { if (GlobalVariable *ivar = dyn_cast(load->getOperand(0))) { StringRef variableName = ivar->getName(); if (!variableName.startswith("__objc_ivar_offset_")) break; static size_t prefixLength = strlen("__objc_ivar_offset_"); StringRef suffix = variableName.substr(prefixLength, variableName.size()-prefixLength); std::pair parts = suffix.split('.'); StringRef className = parts.first; StringRef ivarName = parts.second; // If the class, and all superclasses, are visible in this module // then we can hard-code the ivar offset if (size_t offset = hardCodedOffset(className, ivarName)) { replacements.insert(Replacement(indirectload, ConstantInt::get(indirectload->getType(), offset))); replacements.insert(Replacement(load, 0)); modified = true; } else { // If the class was compiled with the new ABI, then we have a // direct offset variable that we can use if (Value *offset = M->getGlobalVariable( ("__objc_ivar_offset_value_" + suffix).str())) { replacements.insert(Replacement(load, offset)); modified = true; } } } } } } } for (DenseSet::iterator i=replacements.begin(), end=replacements.end() ; i != end ; ++i) { if (i->second) i->first->replaceAllUsesWith(i->second); } verifyFunction(F); return modified; } }; char GNUNonfragileIvarPass::ID = 0; RegisterPass X("gnu-nonfragile-ivar", "Ivar fragility pass"); } FunctionPass *createGNUNonfragileIvarPass(void) { return new GNUNonfragileIvarPass(); }