/* * Copyright 2011-2012, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bcinfo/MetadataExtractor.h" #include "bcinfo/BitcodeWrapper.h" #include "rsDefines.h" #define LOG_TAG "bcinfo" #include <log/log.h> #include "Assert.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/Constants.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Function.h" #include "llvm/Support/MemoryBuffer.h" #ifdef __ANDROID__ #include "Properties.h" #endif #include <cstdlib> namespace bcinfo { namespace { llvm::StringRef getStringOperand(const llvm::Metadata *node) { if (auto *mds = llvm::dyn_cast_or_null<const llvm::MDString>(node)) { return mds->getString(); } return llvm::StringRef(); } bool extractUIntFromMetadataString(uint32_t *value, const llvm::Metadata *m) { llvm::StringRef SigString = getStringOperand(m); if (SigString != "") { if (!SigString.getAsInteger(10, *value)) { return true; } } return false; } const char *createStringFromValue(llvm::Metadata *m) { auto ref = getStringOperand(m); char *c = new char[ref.size() + 1]; memcpy(c, ref.data(), ref.size()); c[ref.size()] = '\0'; return c; } const char *createStringFromOptionalValue(llvm::MDNode *n, unsigned opndNum) { llvm::Metadata *opnd; if (opndNum >= n->getNumOperands() || !(opnd = n->getOperand(opndNum))) return nullptr; return createStringFromValue(opnd); } // Collect metadata from NamedMDNodes that contain a list of names // (strings). // // Inputs: // // NamedMetadata - An LLVM metadata node, each of whose operands have // a string as their first entry // // NameList - A reference that will hold an allocated array of strings // // Count - A reference that will hold the length of the allocated // array of strings // // Return value: // // Return true on success, false on error. // // Upon success, the function sets NameList to an array of strings // corresponding the names found in the metadata. The function sets // Count to the number of entries in NameList. // // An error occurs if one of the metadata operands doesn't have a // first entry. bool populateNameMetadata(const llvm::NamedMDNode *NameMetadata, const char **&NameList, size_t &Count) { if (!NameMetadata) { NameList = nullptr; Count = 0; return true; } Count = NameMetadata->getNumOperands(); if (!Count) { NameList = nullptr; return true; } NameList = new const char *[Count]; for (size_t i = 0; i < Count; i++) { llvm::MDNode *Name = NameMetadata->getOperand(i); if (Name && Name->getNumOperands() > 0) { NameList[i] = createStringFromValue(Name->getOperand(0)); } else { ALOGE("Metadata operand does not contain a name string"); for (size_t AllocatedIndex = 0; AllocatedIndex < i; AllocatedIndex++) { delete [] NameList[AllocatedIndex]; } delete [] NameList; NameList = nullptr; Count = 0; return false; } } return true; } } // end anonymous namespace // Name of metadata node where pragma info resides (should be synced with // slang.cpp) static const llvm::StringRef PragmaMetadataName = "#pragma"; // Name of metadata node where exported variable names reside (should be // synced with slang_rs_metadata.h) static const llvm::StringRef ExportVarMetadataName = "#rs_export_var"; // Name of metadata node where exported function names reside (should be // synced with slang_rs_metadata.h) static const llvm::StringRef ExportFuncMetadataName = "#rs_export_func"; // Name of metadata node where exported ForEach name information resides // (should be synced with slang_rs_metadata.h) static const llvm::StringRef ExportForEachNameMetadataName = "#rs_export_foreach_name"; // Name of metadata node where exported ForEach signature information resides // (should be synced with slang_rs_metadata.h) static const llvm::StringRef ExportForEachMetadataName = "#rs_export_foreach"; // Name of metadata node where exported general reduce information resides // (should be synced with slang_rs_metadata.h) static const llvm::StringRef ExportReduceMetadataName = "#rs_export_reduce"; // Name of metadata node where RS object slot info resides (should be // synced with slang_rs_metadata.h) static const llvm::StringRef ObjectSlotMetadataName = "#rs_object_slots"; static const llvm::StringRef ThreadableMetadataName = "#rs_is_threadable"; // Name of metadata node where the checksum for this build is stored. (should // be synced with libbcc/lib/Core/Source.cpp) static const llvm::StringRef ChecksumMetadataName = "#rs_build_checksum"; // Name of metadata node which contains a list of compile units that have debug // metadata. If this is null then there is no debug metadata in the compile // unit. static const llvm::StringRef DebugInfoMetadataName = "llvm.dbg.cu"; const char MetadataExtractor::kWrapperMetadataName[] = "#rs_wrapper"; MetadataExtractor::MetadataExtractor(const char *bitcode, size_t bitcodeSize) : mModule(nullptr), mBitcode(bitcode), mBitcodeSize(bitcodeSize), mExportVarCount(0), mExportFuncCount(0), mExportForEachSignatureCount(0), mExportReduceCount(0), mExportVarNameList(nullptr), mExportFuncNameList(nullptr), mExportForEachNameList(nullptr), mExportForEachSignatureList(nullptr), mExportForEachInputCountList(nullptr), mExportReduceList(nullptr), mPragmaCount(0), mPragmaKeyList(nullptr), mPragmaValueList(nullptr), mObjectSlotCount(0), mObjectSlotList(nullptr), mRSFloatPrecision(RS_FP_Full), mIsThreadable(true), mBuildChecksum(nullptr), mHasDebugInfo(false) { BitcodeWrapper wrapper(bitcode, bitcodeSize); mCompilerVersion = wrapper.getCompilerVersion(); mOptimizationLevel = wrapper.getOptimizationLevel(); } MetadataExtractor::MetadataExtractor(const llvm::Module *module) : mModule(module), mBitcode(nullptr), mBitcodeSize(0), mExportVarCount(0), mExportFuncCount(0), mExportForEachSignatureCount(0), mExportReduceCount(0), mExportVarNameList(nullptr), mExportFuncNameList(nullptr), mExportForEachNameList(nullptr), mExportForEachSignatureList(nullptr), mExportForEachInputCountList(nullptr), mExportReduceList(nullptr), mPragmaCount(0), mPragmaKeyList(nullptr), mPragmaValueList(nullptr), mObjectSlotCount(0), mObjectSlotList(nullptr), mRSFloatPrecision(RS_FP_Full), mIsThreadable(true), mBuildChecksum(nullptr) { const llvm::NamedMDNode *const wrapperMDNode = module->getNamedMetadata(kWrapperMetadataName); bccAssert((wrapperMDNode != nullptr) && (wrapperMDNode->getNumOperands() == 1)); const llvm::MDNode *const wrapperMDTuple = wrapperMDNode->getOperand(0); bool success = true; success &= extractUIntFromMetadataString(&mCompilerVersion, wrapperMDTuple->getOperand(0)); success &= extractUIntFromMetadataString(&mOptimizationLevel, wrapperMDTuple->getOperand(1)); bccAssert(success); } MetadataExtractor::~MetadataExtractor() { if (mExportVarNameList) { for (size_t i = 0; i < mExportVarCount; i++) { delete [] mExportVarNameList[i]; mExportVarNameList[i] = nullptr; } } delete [] mExportVarNameList; mExportVarNameList = nullptr; if (mExportFuncNameList) { for (size_t i = 0; i < mExportFuncCount; i++) { delete [] mExportFuncNameList[i]; mExportFuncNameList[i] = nullptr; } } delete [] mExportFuncNameList; mExportFuncNameList = nullptr; if (mExportForEachNameList) { for (size_t i = 0; i < mExportForEachSignatureCount; i++) { delete [] mExportForEachNameList[i]; mExportForEachNameList[i] = nullptr; } } delete [] mExportForEachNameList; mExportForEachNameList = nullptr; delete [] mExportForEachSignatureList; mExportForEachSignatureList = nullptr; delete [] mExportForEachInputCountList; mExportForEachInputCountList = nullptr; delete [] mExportReduceList; mExportReduceList = nullptr; for (size_t i = 0; i < mPragmaCount; i++) { if (mPragmaKeyList) { delete [] mPragmaKeyList[i]; mPragmaKeyList[i] = nullptr; } if (mPragmaValueList) { delete [] mPragmaValueList[i]; mPragmaValueList[i] = nullptr; } } delete [] mPragmaKeyList; mPragmaKeyList = nullptr; delete [] mPragmaValueList; mPragmaValueList = nullptr; delete [] mObjectSlotList; mObjectSlotList = nullptr; delete [] mBuildChecksum; return; } bool MetadataExtractor::populateObjectSlotMetadata( const llvm::NamedMDNode *ObjectSlotMetadata) { if (!ObjectSlotMetadata) { return true; } mObjectSlotCount = ObjectSlotMetadata->getNumOperands(); if (!mObjectSlotCount) { return true; } std::unique_ptr<uint32_t[]> TmpSlotList(new uint32_t[mObjectSlotCount]()); for (size_t i = 0; i < mObjectSlotCount; i++) { llvm::MDNode *ObjectSlot = ObjectSlotMetadata->getOperand(i); if (ObjectSlot != nullptr && ObjectSlot->getNumOperands() == 1) { if (!extractUIntFromMetadataString(&TmpSlotList[i], ObjectSlot->getOperand(0))) { ALOGE("Non-integer object slot value"); return false; } } else { ALOGE("Corrupt object slot information"); return false; } } delete [] mObjectSlotList; mObjectSlotList = TmpSlotList.release(); return true; } void MetadataExtractor::populatePragmaMetadata( const llvm::NamedMDNode *PragmaMetadata) { if (!PragmaMetadata) { return; } mPragmaCount = PragmaMetadata->getNumOperands(); if (!mPragmaCount) { return; } const char **TmpKeyList = new const char*[mPragmaCount]; const char **TmpValueList = new const char*[mPragmaCount]; for (size_t i = 0; i < mPragmaCount; i++) { llvm::MDNode *Pragma = PragmaMetadata->getOperand(i); if (Pragma != nullptr && Pragma->getNumOperands() == 2) { llvm::Metadata *PragmaKeyMDS = Pragma->getOperand(0); TmpKeyList[i] = createStringFromValue(PragmaKeyMDS); llvm::Metadata *PragmaValueMDS = Pragma->getOperand(1); TmpValueList[i] = createStringFromValue(PragmaValueMDS); } } mPragmaKeyList = TmpKeyList; mPragmaValueList = TmpValueList; // Check to see if we have any FP precision-related pragmas. std::string Relaxed("rs_fp_relaxed"); std::string Imprecise("rs_fp_imprecise"); std::string Full("rs_fp_full"); bool RelaxedPragmaSeen = false; bool FullPragmaSeen = false; for (size_t i = 0; i < mPragmaCount; i++) { if (!Relaxed.compare(mPragmaKeyList[i])) { RelaxedPragmaSeen = true; } else if (!Imprecise.compare(mPragmaKeyList[i])) { ALOGW("rs_fp_imprecise is deprecated. Assuming rs_fp_relaxed instead."); RelaxedPragmaSeen = true; } else if (!Full.compare(mPragmaKeyList[i])) { FullPragmaSeen = true; } } if (RelaxedPragmaSeen && FullPragmaSeen) { ALOGE("Full and relaxed precision specified at the same time!"); } mRSFloatPrecision = RelaxedPragmaSeen ? RS_FP_Relaxed : RS_FP_Full; #ifdef __ANDROID__ // Provide an override for precsiion via adb shell setprop // adb shell setprop debug.rs.precision rs_fp_full // adb shell setprop debug.rs.precision rs_fp_relaxed // adb shell setprop debug.rs.precision rs_fp_imprecise char PrecisionPropBuf[PROP_VALUE_MAX]; const std::string PrecisionPropName("debug.rs.precision"); property_get("debug.rs.precision", PrecisionPropBuf, ""); if (PrecisionPropBuf[0]) { if (!Relaxed.compare(PrecisionPropBuf)) { ALOGI("Switching to RS FP relaxed mode via setprop"); mRSFloatPrecision = RS_FP_Relaxed; } else if (!Imprecise.compare(PrecisionPropBuf)) { ALOGW("Switching to RS FP relaxed mode via setprop. rs_fp_imprecise was " "specified but is deprecated "); mRSFloatPrecision = RS_FP_Relaxed; } else if (!Full.compare(PrecisionPropBuf)) { ALOGI("Switching to RS FP full mode via setprop"); mRSFloatPrecision = RS_FP_Full; } else { ALOGE("Unrecognized debug.rs.precision %s", PrecisionPropBuf); } } #endif } uint32_t MetadataExtractor::calculateNumInputs(const llvm::Function *Function, uint32_t Signature) { if (hasForEachSignatureIn(Signature)) { uint32_t OtherCount = 0; OtherCount += hasForEachSignatureUsrData(Signature); OtherCount += hasForEachSignatureX(Signature); OtherCount += hasForEachSignatureY(Signature); OtherCount += hasForEachSignatureZ(Signature); OtherCount += hasForEachSignatureCtxt(Signature); OtherCount += hasForEachSignatureOut(Signature) && Function->getReturnType()->isVoidTy(); return Function->arg_size() - OtherCount; } else { return 0; } } bool MetadataExtractor::populateForEachMetadata( const llvm::NamedMDNode *Names, const llvm::NamedMDNode *Signatures) { if (!Names && !Signatures && mCompilerVersion == 0) { // Handle legacy case for pre-ICS bitcode that doesn't contain a metadata // section for ForEach. We generate a full signature for a "root" function // which means that we need to set the bottom 5 bits in the mask. mExportForEachSignatureCount = 1; char **TmpNameList = new char*[mExportForEachSignatureCount]; size_t RootLen = strlen(kRoot) + 1; TmpNameList[0] = new char[RootLen]; strncpy(TmpNameList[0], kRoot, RootLen); uint32_t *TmpSigList = new uint32_t[mExportForEachSignatureCount]; TmpSigList[0] = 0x1f; mExportForEachNameList = (const char**)TmpNameList; mExportForEachSignatureList = TmpSigList; return true; } if (Signatures) { mExportForEachSignatureCount = Signatures->getNumOperands(); if (!mExportForEachSignatureCount) { return true; } } else { mExportForEachSignatureCount = 0; mExportForEachSignatureList = nullptr; return true; } std::unique_ptr<uint32_t[]> TmpSigList(new uint32_t[mExportForEachSignatureCount]); std::unique_ptr<const char *[]> TmpNameList(new const char*[mExportForEachSignatureCount]); std::unique_ptr<uint32_t[]> TmpInputCountList(new uint32_t[mExportForEachSignatureCount]); for (size_t i = 0; i < mExportForEachSignatureCount; i++) { llvm::MDNode *SigNode = Signatures->getOperand(i); if (SigNode != nullptr && SigNode->getNumOperands() == 1) { if (!extractUIntFromMetadataString(&TmpSigList[i], SigNode->getOperand(0))) { ALOGE("Non-integer signature value"); return false; } } else { ALOGE("Corrupt signature information"); return false; } } if (Names) { for (size_t i = 0; i < mExportForEachSignatureCount; i++) { llvm::MDNode *Name = Names->getOperand(i); if (Name != nullptr && Name->getNumOperands() == 1) { TmpNameList[i] = createStringFromValue(Name->getOperand(0)); // Note that looking up the function by name can fail: One of // the uses of MetadataExtractor is as part of the // RSEmbedInfoPass, which bcc_compat runs sufficiently late in // the phase order that RSKernelExpandPass has already run and // the original (UNexpanded) kernel function (TmpNameList[i]) // may have been deleted as having no references (if it has // been inlined into the expanded kernel function and is // otherwise unreferenced). llvm::Function *Func = mModule->getFunction(llvm::StringRef(TmpNameList[i])); TmpInputCountList[i] = (Func != nullptr) ? calculateNumInputs(Func, TmpSigList[i]) : 0; } } } else { if (mExportForEachSignatureCount != 1) { ALOGE("mExportForEachSignatureCount = %zu, but should be 1", mExportForEachSignatureCount); } char *RootName = new char[5]; strncpy(RootName, "root", 5); TmpNameList[0] = RootName; } delete [] mExportForEachNameList; mExportForEachNameList = TmpNameList.release(); delete [] mExportForEachSignatureList; mExportForEachSignatureList = TmpSigList.release(); delete [] mExportForEachInputCountList; mExportForEachInputCountList = TmpInputCountList.release(); return true; } bool MetadataExtractor::populateReduceMetadata(const llvm::NamedMDNode *ReduceMetadata) { mExportReduceCount = 0; mExportReduceList = nullptr; if (!ReduceMetadata || !(mExportReduceCount = ReduceMetadata->getNumOperands())) return true; std::unique_ptr<Reduce[]> TmpReduceList(new Reduce[mExportReduceCount]); for (size_t i = 0; i < mExportReduceCount; i++) { llvm::MDNode *Node = ReduceMetadata->getOperand(i); if (!Node || Node->getNumOperands() < 3) { ALOGE("Missing reduce metadata"); return false; } TmpReduceList[i].mReduceName = createStringFromValue(Node->getOperand(0)); if (!extractUIntFromMetadataString(&TmpReduceList[i].mAccumulatorDataSize, Node->getOperand(1))) { ALOGE("Non-integer accumulator data size value in reduce metadata"); return false; } llvm::MDNode *AccumulatorNode = llvm::dyn_cast<llvm::MDNode>(Node->getOperand(2)); if (!AccumulatorNode || AccumulatorNode->getNumOperands() != 2) { ALOGE("Malformed accumulator node in reduce metadata"); return false; } TmpReduceList[i].mAccumulatorName = createStringFromValue(AccumulatorNode->getOperand(0)); if (!extractUIntFromMetadataString(&TmpReduceList[i].mSignature, AccumulatorNode->getOperand(1))) { ALOGE("Non-integer signature value in reduce metadata"); return false; } // Note that looking up the function by name can fail: One of the // uses of MetadataExtractor is as part of the RSEmbedInfoPass, // which bcc_compat runs sufficiently late in the phase order that // RSKernelExpandPass has already run and the original // (UNexpanded) accumulator function (mAccumulatorName) may have // been deleted as having no references (if it has been inlined // into the expanded accumulator function and is otherwise // unreferenced). llvm::Function *Func = mModule->getFunction(llvm::StringRef(TmpReduceList[i].mAccumulatorName)); // Why calculateNumInputs() - 1? The "-1" is because we don't // want to treat the accumulator argument as an input. TmpReduceList[i].mInputCount = (Func ? calculateNumInputs(Func, TmpReduceList[i].mSignature) - 1 : 0); TmpReduceList[i].mInitializerName = createStringFromOptionalValue(Node, 3); TmpReduceList[i].mCombinerName = createStringFromOptionalValue(Node, 4); TmpReduceList[i].mOutConverterName = createStringFromOptionalValue(Node, 5); TmpReduceList[i].mHalterName = createStringFromOptionalValue(Node, 6); } mExportReduceList = TmpReduceList.release(); return true; } void MetadataExtractor::readThreadableFlag( const llvm::NamedMDNode *ThreadableMetadata) { // Scripts are threadable by default. If we read a valid metadata value for // 'ThreadableMetadataName' and it is set to 'no', we mark script as non // threadable. All other exception paths retain the default value. mIsThreadable = true; if (ThreadableMetadata == nullptr) return; llvm::MDNode *mdNode = ThreadableMetadata->getOperand(0); if (mdNode == nullptr) return; llvm::Metadata *mdValue = mdNode->getOperand(0); if (mdValue == nullptr) return; if (getStringOperand(mdValue) == "no") mIsThreadable = false; } void MetadataExtractor::readBuildChecksumMetadata( const llvm::NamedMDNode *ChecksumMetadata) { if (ChecksumMetadata == nullptr) return; llvm::MDNode *mdNode = ChecksumMetadata->getOperand(0); if (mdNode == nullptr) return; llvm::Metadata *mdValue = mdNode->getOperand(0); if (mdValue == nullptr) return; mBuildChecksum = createStringFromValue(mdValue); } bool MetadataExtractor::extract() { if (!(mBitcode && mBitcodeSize) && !mModule) { ALOGE("Invalid/empty bitcode/module"); return false; } std::unique_ptr<llvm::LLVMContext> mContext; bool shouldNullModule = false; if (!mModule) { mContext.reset(new llvm::LLVMContext()); std::unique_ptr<llvm::MemoryBuffer> MEM( llvm::MemoryBuffer::getMemBuffer( llvm::StringRef(mBitcode, mBitcodeSize), "", false)); std::string error; llvm::ErrorOr<std::unique_ptr<llvm::Module> > errval = llvm::parseBitcodeFile(MEM.get()->getMemBufferRef(), *mContext); if (std::error_code ec = errval.getError()) { ALOGE("Could not parse bitcode file"); ALOGE("%s", ec.message().c_str()); return false; } mModule = errval.get().release(); shouldNullModule = true; } const llvm::NamedMDNode *ExportVarMetadata = mModule->getNamedMetadata(ExportVarMetadataName); const llvm::NamedMDNode *ExportFuncMetadata = mModule->getNamedMetadata(ExportFuncMetadataName); const llvm::NamedMDNode *ExportForEachNameMetadata = mModule->getNamedMetadata(ExportForEachNameMetadataName); const llvm::NamedMDNode *ExportForEachMetadata = mModule->getNamedMetadata(ExportForEachMetadataName); const llvm::NamedMDNode *ExportReduceMetadata = mModule->getNamedMetadata(ExportReduceMetadataName); const llvm::NamedMDNode *PragmaMetadata = mModule->getNamedMetadata(PragmaMetadataName); const llvm::NamedMDNode *ObjectSlotMetadata = mModule->getNamedMetadata(ObjectSlotMetadataName); const llvm::NamedMDNode *ThreadableMetadata = mModule->getNamedMetadata(ThreadableMetadataName); const llvm::NamedMDNode *ChecksumMetadata = mModule->getNamedMetadata(ChecksumMetadataName); const llvm::NamedMDNode *DebugInfoMetadata = mModule->getNamedMetadata(DebugInfoMetadataName); if (!populateNameMetadata(ExportVarMetadata, mExportVarNameList, mExportVarCount)) { ALOGE("Could not populate export variable metadata"); goto err; } if (!populateNameMetadata(ExportFuncMetadata, mExportFuncNameList, mExportFuncCount)) { ALOGE("Could not populate export function metadata"); goto err; } if (!populateForEachMetadata(ExportForEachNameMetadata, ExportForEachMetadata)) { ALOGE("Could not populate ForEach signature metadata"); goto err; } if (!populateReduceMetadata(ExportReduceMetadata)) { ALOGE("Could not populate export general reduction metadata"); goto err; } populatePragmaMetadata(PragmaMetadata); if (!populateObjectSlotMetadata(ObjectSlotMetadata)) { ALOGE("Could not populate object slot metadata"); goto err; } readThreadableFlag(ThreadableMetadata); readBuildChecksumMetadata(ChecksumMetadata); mHasDebugInfo = DebugInfoMetadata != nullptr; if (shouldNullModule) { mModule = nullptr; } return true; err: if (shouldNullModule) { mModule = nullptr; } return false; } } // namespace bcinfo