| /* |
| * Copyright 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. |
| */ |
| |
| //===----------------------------------------------------------------------===// |
| // This file implements RSInfo::ExtractFromSource() |
| //===----------------------------------------------------------------------===// |
| #include "bcc/Renderscript/RSInfo.h" |
| |
| #include <llvm/Constants.h> |
| #include <llvm/Metadata.h> |
| #include <llvm/Module.h> |
| |
| #include "bcc/Source.h" |
| #include "bcc/Support/Log.h" |
| |
| using namespace bcc; |
| |
| namespace { |
| |
| // Name of metadata node where pragma info resides (should be synced with |
| // slang.cpp) |
| const llvm::StringRef pragma_metadata_name("#pragma"); |
| |
| /* |
| * The following names should be synced with the one appeared in |
| * slang_rs_metadata.h. |
| */ |
| // Name of metadata node where exported variable names reside |
| const llvm::StringRef export_var_metadata_name("#rs_export_var"); |
| |
| // Name of metadata node where exported function names reside |
| const llvm::StringRef export_func_metadata_name("#rs_export_func"); |
| |
| // Name of metadata node where exported ForEach name information resides |
| const llvm::StringRef export_foreach_name_metadata_name("#rs_export_foreach_name"); |
| |
| // Name of metadata node where exported ForEach signature information resides |
| const llvm::StringRef export_foreach_metadata_name("#rs_export_foreach"); |
| |
| // Name of metadata node where RS object slot info resides (should be |
| const llvm::StringRef object_slot_metadata_name("#rs_object_slots"); |
| |
| inline llvm::StringRef getStringFromOperand(const llvm::Value *pString) { |
| if ((pString != NULL) && (pString->getValueID() == llvm::Value::MDStringVal)) { |
| return static_cast<const llvm::MDString *>(pString)->getString(); |
| } |
| return llvm::StringRef(); |
| } |
| |
| template<size_t NumOperands> |
| inline size_t getMetadataStringLength(const llvm::NamedMDNode *pMetadata) { |
| if (pMetadata == NULL) { |
| return 0; |
| } |
| |
| size_t string_size = 0; |
| for (unsigned i = 0, e = pMetadata->getNumOperands(); i < e; i++) { |
| llvm::MDNode *node = pMetadata->getOperand(i); |
| if ((node != NULL) && (node->getNumOperands() >= NumOperands)) { |
| // Compiler try its best to unroll this loop since NumOperands is a |
| // template parameter (therefore the number of iteration can be determined |
| // at compile-time and it's usually small.) |
| for (unsigned j = 0; j < NumOperands; j++) { |
| llvm::StringRef s = getStringFromOperand(node->getOperand(j)); |
| if (s.size() > 0) { |
| // +1 is for the null-terminator at the end of string. |
| string_size += (s.size() + 1); |
| } |
| } |
| } |
| } |
| |
| return string_size; |
| } |
| |
| // Write a string pString to the string pool pStringPool at offset pWriteStart. |
| // Return the pointer the pString resides within the string pool. |
| const char *writeString(const llvm::StringRef &pString, char *pStringPool, |
| off_t *pWriteStart) { |
| if (pString.empty()) { |
| return pStringPool; |
| } |
| |
| char *pStringWriteStart = pStringPool + *pWriteStart; |
| // Copy the string. |
| ::memcpy(pStringWriteStart, pString.data(), pString.size()); |
| // Write null-terminator at the end of the string. |
| pStringWriteStart[ pString.size() ] = '\0'; |
| // Update pWriteStart. |
| *pWriteStart += (pString.size() + 1); |
| |
| return pStringWriteStart; |
| } |
| |
| bool writeDependency(const std::string &pSourceName, const uint8_t *pSHA1, |
| char *pStringPool, off_t *pWriteStart, |
| RSInfo::DependencyTableTy &pDepTable) { |
| const char *source_name = writeString(pSourceName, pStringPool, pWriteStart); |
| |
| uint8_t *sha1 = reinterpret_cast<uint8_t *>(pStringPool + *pWriteStart); |
| |
| // SHA-1 is special. It's size of SHA1_DIGEST_LENGTH (=20) bytes long without |
| // null-terminator. |
| ::memcpy(sha1, pSHA1, SHA1_DIGEST_LENGTH); |
| // Record in the result RSInfo object. |
| pDepTable.push(std::make_pair(source_name, sha1)); |
| // Update the string pool pointer. |
| *pWriteStart += SHA1_DIGEST_LENGTH; |
| |
| return true; |
| } |
| |
| } // end anonymous namespace |
| |
| RSInfo *RSInfo::ExtractFromSource(const Source &pSource, |
| const DependencyTableTy &pDeps) |
| { |
| const llvm::Module &module = pSource.getModule(); |
| const char *module_name = module.getModuleIdentifier().c_str(); |
| |
| const llvm::NamedMDNode *pragma = |
| module.getNamedMetadata(pragma_metadata_name); |
| const llvm::NamedMDNode *export_var = |
| module.getNamedMetadata(export_var_metadata_name); |
| const llvm::NamedMDNode *export_func = |
| module.getNamedMetadata(export_func_metadata_name); |
| const llvm::NamedMDNode *export_foreach_name = |
| module.getNamedMetadata(export_foreach_name_metadata_name); |
| const llvm::NamedMDNode *export_foreach_signature = |
| module.getNamedMetadata(export_foreach_metadata_name); |
| const llvm::NamedMDNode *object_slots = |
| module.getNamedMetadata(object_slot_metadata_name); |
| |
| // Always write a byte 0x0 at the beginning of the string pool. |
| size_t string_pool_size = 1; |
| off_t cur_string_pool_offset = 0; |
| |
| RSInfo *result = NULL; |
| |
| // 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. |
| if ((export_foreach_name == NULL) || (export_foreach_signature == NULL)) { |
| export_foreach_name = NULL; |
| export_foreach_signature = NULL; |
| string_pool_size += 5; // insert "root\0" for #rs_export_foreach_name |
| } |
| |
| string_pool_size += getMetadataStringLength<2>(pragma); |
| string_pool_size += getMetadataStringLength<1>(export_var); |
| string_pool_size += getMetadataStringLength<1>(export_func); |
| string_pool_size += getMetadataStringLength<1>(export_foreach_name); |
| |
| // Don't forget to reserve the space for the dependency informationin string |
| // pool. |
| string_pool_size += ::strlen(LibBCCPath) + 1 + SHA1_DIGEST_LENGTH; |
| string_pool_size += ::strlen(LibRSPath) + 1 + SHA1_DIGEST_LENGTH; |
| string_pool_size += ::strlen(LibCLCorePath) + 1 + SHA1_DIGEST_LENGTH; |
| #if defined(ARCH_ARM_HAVE_NEON) |
| string_pool_size += ::strlen(LibCLCoreNEONPath) + 1 + SHA1_DIGEST_LENGTH; |
| #endif |
| for (unsigned i = 0, e = pDeps.size(); i != e; i++) { |
| // +1 for null-terminator |
| string_pool_size += ::strlen(/* name */pDeps[i].first) + 1; |
| // +SHA1_DIGEST_LENGTH for SHA-1 checksum |
| string_pool_size += SHA1_DIGEST_LENGTH; |
| } |
| |
| // Allocate result object |
| result = new (std::nothrow) RSInfo(string_pool_size); |
| if (result == NULL) { |
| ALOGE("Out of memory when create RSInfo object for %s!", module_name); |
| goto bail; |
| } |
| |
| // Check string pool. |
| if (result->mStringPool == NULL) { |
| ALOGE("Out of memory when allocate string pool in RSInfo object for %s!", |
| module_name); |
| goto bail; |
| } |
| |
| // First byte of string pool should be an empty string |
| result->mStringPool[ cur_string_pool_offset++ ] = '\0'; |
| |
| // Populate all the strings and data. |
| #define FOR_EACH_NODE_IN(_metadata, _node) \ |
| for (unsigned i = 0, e = (_metadata)->getNumOperands(); i != e; i++) \ |
| if (((_node) = (_metadata)->getOperand(i)) != NULL) |
| //===--------------------------------------------------------------------===// |
| // #pragma |
| //===--------------------------------------------------------------------===// |
| // Pragma is actually a key-value pair. The value can be an empty string while |
| // the key cannot. |
| if (pragma != NULL) { |
| llvm::MDNode *node; |
| FOR_EACH_NODE_IN(pragma, node) { |
| llvm::StringRef key = getStringFromOperand(node->getOperand(0)); |
| llvm::StringRef val = getStringFromOperand(node->getOperand(1)); |
| if (key.empty()) { |
| ALOGW("%s contains pragma metadata with empty key (skip)!", |
| module_name); |
| } else { |
| result->mPragmas.push(std::make_pair( |
| writeString(key, result->mStringPool, &cur_string_pool_offset), |
| writeString(val, result->mStringPool, &cur_string_pool_offset))); |
| } // key.empty() |
| } // FOR_EACH_NODE_IN |
| } // pragma != NULL |
| |
| //===--------------------------------------------------------------------===// |
| // #rs_export_var |
| //===--------------------------------------------------------------------===// |
| if (export_var != NULL) { |
| llvm::MDNode *node; |
| FOR_EACH_NODE_IN(export_var, node) { |
| llvm::StringRef name = getStringFromOperand(node->getOperand(0)); |
| if (name.empty()) { |
| ALOGW("%s contains empty entry in #rs_export_var metadata (skip)!", |
| module_name); |
| } else { |
| result->mExportVarNames.push( |
| writeString(name, result->mStringPool, &cur_string_pool_offset)); |
| } |
| } |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // #rs_export_func |
| //===--------------------------------------------------------------------===// |
| if (export_func != NULL) { |
| llvm::MDNode *node; |
| FOR_EACH_NODE_IN(export_func, node) { |
| llvm::StringRef name = getStringFromOperand(node->getOperand(0)); |
| if (name.empty()) { |
| ALOGW("%s contains empty entry in #rs_export_func metadata (skip)!", |
| module_name); |
| } else { |
| result->mExportFuncNames.push( |
| writeString(name, result->mStringPool, &cur_string_pool_offset)); |
| } |
| } |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // #rs_export_foreach and #rs_export_foreach_name |
| //===--------------------------------------------------------------------===// |
| // It's a little bit complicated to deal with #rs_export_foreach (the |
| // signature of foreach-able function) and #rs_export_foreach_name (the name |
| // of function which is foreach-able). We have to maintain a legacy case: |
| // |
| // In pre-ICS bitcode, forEach feature only supports non-graphic root() |
| // function and only one signature corresponded to that non-graphic root() |
| // was written to the #rs_export_foreach metadata section. There's no |
| // #rs_export_foreach_name metadata section. |
| // |
| // Currently, not only non-graphic root() is supported but also other |
| // functions that are exportable. Therefore, a new metadata section |
| // #rs_export_foreach_name is added to specify which functions are |
| // for-eachable. In this case, #rs_export_foreach (the function name) and |
| // #rs_export_foreach metadata (the signature) is one-to-one mapping among |
| // their entries. |
| if ((export_foreach_name != NULL) && (export_foreach_signature != NULL)) { |
| unsigned num_foreach_function; |
| |
| // Should be one-to-one mapping. |
| if (export_foreach_name->getNumOperands() != |
| export_foreach_signature->getNumOperands()) { |
| ALOGE("Mismatch number of foreach-able function names (%u) in " |
| "#rs_export_foreach_name and number of signatures (%u) " |
| "in %s!", export_foreach_name->getNumOperands(), |
| export_foreach_signature->getNumOperands(), module_name); |
| goto bail; |
| } |
| |
| num_foreach_function = export_foreach_name->getNumOperands(); |
| for (unsigned i = 0; i < num_foreach_function; i++) { |
| llvm::MDNode *name_node = export_foreach_name->getOperand(i); |
| llvm::MDNode *signature_node = export_foreach_signature->getOperand(i); |
| |
| llvm::StringRef name, signature_string; |
| if (name_node != NULL) { |
| name = getStringFromOperand(name_node->getOperand(0)); |
| } |
| if (signature_node != NULL) { |
| signature_string = getStringFromOperand(signature_node->getOperand(0)); |
| } |
| |
| if (!name.empty() && !signature_string.empty()) { |
| // Both name_node and signature_node are not NULL nodes. |
| uint32_t signature; |
| if (signature_string.getAsInteger(10, signature)) { |
| ALOGE("Non-integer signature value '%s' for function %s found in %s!", |
| signature_string.str().c_str(), name.str().c_str(), module_name); |
| goto bail; |
| } |
| result->mExportForeachFuncs.push(std::make_pair( |
| writeString(name, result->mStringPool, &cur_string_pool_offset), |
| signature)); |
| } else { |
| // One or both of the name and signature value are empty. It's safe only |
| // if both of them are empty. |
| if (name.empty() && signature_string.empty()) { |
| ALOGW("Entries #%u at #rs_export_foreach_name and #rs_export_foreach" |
| " are both NULL in %s! (skip)", i, module_name); |
| continue; |
| } else { |
| ALOGE("Entries #%u at %s is NULL in %s! (skip)", i, |
| (name.empty() ? "#rs_export_foreach_name" : |
| "#rs_export_foreach"), module_name); |
| goto bail; |
| } |
| } |
| } // end for |
| } else { |
| // To handle the legacy case, we generate a full signature for a "root" |
| // function which means that we need to set the bottom 5 bits (0x1f) in the |
| // mask. |
| result->mExportForeachFuncs.push(std::make_pair( |
| writeString(llvm::StringRef("root"), result->mStringPool, |
| &cur_string_pool_offset), 0x1f)); |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // #rs_object_slots |
| //===--------------------------------------------------------------------===// |
| if (object_slots != NULL) { |
| llvm::MDNode *node; |
| for (unsigned int i = 0; i <= export_var->getNumOperands(); i++) { |
| result->mObjectSlots.push(0); |
| } |
| FOR_EACH_NODE_IN(object_slots, node) { |
| llvm::StringRef val = getStringFromOperand(node->getOperand(0)); |
| if (val.empty()) { |
| ALOGW("%s contains empty entry in #rs_object_slots (skip)!", |
| module.getModuleIdentifier().c_str()); |
| } else { |
| uint32_t slot; |
| if (val.getAsInteger(10, slot)) { |
| ALOGE("Non-integer object slot value '%s' in %s!", val.str().c_str(), |
| module.getModuleIdentifier().c_str()); |
| goto bail; |
| } else { |
| result->mObjectSlots.editItemAt(slot) = 1; |
| } |
| } |
| } |
| } |
| #undef FOR_EACH_NODE_IN |
| |
| //===--------------------------------------------------------------------===// |
| // Record built-in dependency information. |
| //===--------------------------------------------------------------------===// |
| LoadBuiltInSHA1Information(); |
| |
| if (!writeDependency(LibBCCPath, LibBCCSHA1, |
| result->mStringPool, &cur_string_pool_offset, |
| result->mDependencyTable)) { |
| goto bail; |
| } |
| |
| if (!writeDependency(LibRSPath, LibRSSHA1, |
| result->mStringPool, &cur_string_pool_offset, |
| result->mDependencyTable)) { |
| goto bail; |
| } |
| |
| if (!writeDependency(LibCLCorePath, LibCLCoreSHA1, |
| result->mStringPool, &cur_string_pool_offset, |
| result->mDependencyTable)) { |
| goto bail; |
| } |
| |
| #if defined(ARCH_ARM_HAVE_NEON) |
| if (!writeDependency(LibCLCoreNEONPath, LibCLCoreNEONSHA1, |
| result->mStringPool, &cur_string_pool_offset, |
| result->mDependencyTable)) { |
| goto bail; |
| } |
| #endif |
| |
| //===--------------------------------------------------------------------===// |
| // Record dependency information. |
| //===--------------------------------------------------------------------===// |
| for (unsigned i = 0, e = pDeps.size(); i != e; i++) { |
| if (!writeDependency(/* name */pDeps[i].first, /* SHA-1 */pDeps[i].second, |
| result->mStringPool, &cur_string_pool_offset, |
| result->mDependencyTable)) { |
| goto bail; |
| } |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // Determine whether the bitcode contains debug information |
| //===--------------------------------------------------------------------===// |
| // The root context of the debug information in the bitcode is put under |
| // the metadata named "llvm.dbg.cu". |
| result->mHeader.hasDebugInformation = |
| static_cast<uint8_t>(module.getNamedMetadata("llvm.dbg.cu") != NULL); |
| |
| assert((cur_string_pool_offset == string_pool_size) && |
| "Unexpected string pool size!"); |
| |
| return result; |
| |
| bail: |
| delete result; |
| return NULL; |
| } |