Rename the file name (looks more similar to LLVM).
diff --git a/lib/bcc/Compiler.cpp b/lib/bcc/Compiler.cpp
new file mode 100644
index 0000000..50aeaa2
--- /dev/null
+++ b/lib/bcc/Compiler.cpp
@@ -0,0 +1,1776 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#define LOG_TAG "bcc"
+#include <cutils/log.h>
+
+#if defined(__arm__)
+# define DEFAULT_ARM_CODEGEN
+# define PROVIDE_ARM_CODEGEN
+#elif defined(__i386__)
+# define DEFAULT_X86_CODEGEN
+# define PROVIDE_X86_CODEGEN
+#elif defined(__x86_64__)
+# define DEFAULT_X64_CODEGEN
+# define PROVIDE_X64_CODEGEN
+#endif
+
+#if defined(FORCE_ARM_CODEGEN)
+# define DEFAULT_ARM_CODEGEN
+# undef DEFAULT_X86_CODEGEN
+# undef DEFAULT_X64_CODEGEN
+# define PROVIDE_ARM_CODEGEN
+# undef PROVIDE_X86_CODEGEN
+# undef PROVIDE_X64_CODEGEN
+#elif defined(FORCE_X86_CODEGEN)
+# undef DEFAULT_ARM_CODEGEN
+# define DEFAULT_X86_CODEGEN
+# undef DEFAULT_X64_CODEGEN
+# undef PROVIDE_ARM_CODEGEN
+# define PROVIDE_X86_CODEGEN
+# undef PROVIDE_X64_CODEGEN
+#elif defined(FORCE_X64_CODEGEN)
+# undef DEFAULT_ARM_CODEGEN
+# undef DEFAULT_X86_CODEGEN
+# define DEFAULT_X64_CODEGEN
+# undef PROVIDE_ARM_CODEGEN
+# undef PROVIDE_X86_CODEGEN
+# define PROVIDE_X64_CODEGEN
+#endif
+
+#if defined(DEFAULT_ARM_CODEGEN)
+# define TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi"
+#elif defined(DEFAULT_X86_CODEGEN)
+# define TARGET_TRIPLE_STRING "i686-unknown-linux"
+#elif defined(DEFAULT_X64_CODEGEN)
+# define TARGET_TRIPLE_STRING "x86_64-unknown-linux"
+#endif
+
+#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
+# define ARM_USE_VFP
+#endif
+
+#include "Compiler.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include "llvm/Analysis/Passes.h"
+
+#include "llvm/Bitcode/ReaderWriter.h"
+
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/RegAllocRegistry.h"
+#include "llvm/CodeGen/SchedulerRegistry.h"
+
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Scalar.h"
+
+#include "llvm/Target/SubtargetFeature.h"
+#include "llvm/Target/TargetData.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/Target/TargetRegistry.h"
+#include "llvm/Target/TargetSelect.h"
+
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "llvm/GlobalValue.h"
+#include "llvm/Linker.h"
+#include "llvm/LLVMContext.h"
+#include "llvm/Metadata.h"
+#include "llvm/Module.h"
+#include "llvm/PassManager.h"
+#include "llvm/Value.h"
+
+#include <errno.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+
+namespace {
+
+#define TEMP_FAILURE_RETRY1(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+
+
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
+ while (count != 0) {
+ ssize_t actual = TEMP_FAILURE_RETRY1(write(fd, buf, count));
+ if (actual < 0) {
+ int err = errno;
+ LOGE("%s: write failed: %s\n", logMsg, strerror(err));
+ return err;
+ } else if (actual != (ssize_t) count) {
+ LOGD("%s: partial write (will retry): (%d of %zd)\n",
+ logMsg, (int) actual, count);
+ buf = (const void*) (((const uint8_t*) buf) + actual);
+ }
+ count -= actual;
+ }
+
+ return 0;
+}
+
+} // namespace anonymous
+
+
+namespace bcc {
+
+//////////////////////////////////////////////////////////////////////////////
+// BCC Compiler Static Variables
+//////////////////////////////////////////////////////////////////////////////
+
+bool Compiler::GlobalInitialized = false;
+
+bool Compiler::BccMmapImgAddrTaken[BCC_MMAP_IMG_COUNT];
+
+// Code generation optimization level for the compiler
+llvm::CodeGenOpt::Level Compiler::CodeGenOptLevel;
+
+std::string Compiler::Triple;
+
+std::string Compiler::CPU;
+
+std::vector<std::string> Compiler::Features;
+
+// The named of metadata node that pragma resides (should be synced with
+// slang.cpp)
+const llvm::StringRef Compiler::PragmaMetadataName = "#pragma";
+
+// The named of metadata node that export variable name resides (should be
+// synced with slang_rs_metadata.h)
+const llvm::StringRef Compiler::ExportVarMetadataName = "#rs_export_var";
+
+// The named of metadata node that export function name resides (should be
+// synced with slang_rs_metadata.h)
+const llvm::StringRef Compiler::ExportFuncMetadataName = "#rs_export_func";
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Compiler
+//////////////////////////////////////////////////////////////////////////////
+
+void Compiler::GlobalInitialization() {
+ if (GlobalInitialized)
+ return;
+
+ // if (!llvm::llvm_is_multithreaded())
+ // llvm::llvm_start_multithreaded();
+
+ // Set Triple, CPU and Features here
+ Triple = TARGET_TRIPLE_STRING;
+
+ // TODO(sliao): NEON for JIT
+ // Features.push_back("+neon");
+ // Features.push_back("+vmlx");
+ // Features.push_back("+neonfp");
+ Features.push_back("+vfp3");
+ Features.push_back("+d16");
+
+#if defined(DEFAULT_ARM_CODEGEN) || defined(PROVIDE_ARM_CODEGEN)
+ LLVMInitializeARMTargetInfo();
+ LLVMInitializeARMTarget();
+#if defined(USE_DISASSEMBLER)
+ LLVMInitializeARMDisassembler();
+ LLVMInitializeARMAsmPrinter();
+#endif
+#endif
+
+#if defined(DEFAULT_X86_CODEGEN) || defined(PROVIDE_X86_CODEGEN)
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+#if defined(USE_DISASSEMBLER)
+ LLVMInitializeX86Disassembler();
+ LLVMInitializeX86AsmPrinter();
+#endif
+#endif
+
+#if defined(DEFAULT_X64_CODEGEN) || defined(PROVIDE_X64_CODEGEN)
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+#if defined(USE_DISASSEMBLER)
+ LLVMInitializeX86Disassembler();
+ LLVMInitializeX86AsmPrinter();
+#endif
+#endif
+
+ // -O0: llvm::CodeGenOpt::None
+ // -O1: llvm::CodeGenOpt::Less
+ // -O2: llvm::CodeGenOpt::Default
+ // -O3: llvm::CodeGenOpt::Aggressive
+ CodeGenOptLevel = llvm::CodeGenOpt::None;
+
+ // Below are the global settings to LLVM
+
+ // Disable frame pointer elimination optimization
+ llvm::NoFramePointerElim = false;
+
+ // Use hardfloat ABI
+ //
+ // TODO(all): Need to detect the CPU capability and decide whether to use
+ // softfp. To use softfp, change following 2 lines to
+ //
+ // llvm::FloatABIType = llvm::FloatABI::Soft;
+ // llvm::UseSoftFloat = true;
+ //
+ llvm::FloatABIType = llvm::FloatABI::Soft;
+ llvm::UseSoftFloat = false;
+
+ // BCC needs all unknown symbols resolved at JIT/compilation time.
+ // So we don't need any dynamic relocation model.
+ llvm::TargetMachine::setRelocationModel(llvm::Reloc::Static);
+
+#if defined(DEFAULT_X64_CODEGEN)
+ // Data address in X86_64 architecture may reside in a far-away place
+ llvm::TargetMachine::setCodeModel(llvm::CodeModel::Medium);
+#else
+ // This is set for the linker (specify how large of the virtual addresses
+ // we can access for all unknown symbols.)
+ llvm::TargetMachine::setCodeModel(llvm::CodeModel::Small);
+#endif
+
+ // Register the scheduler
+ llvm::RegisterScheduler::setDefault(llvm::createDefaultScheduler);
+
+ // Register allocation policy:
+ // createFastRegisterAllocator: fast but bad quality
+ // createLinearScanRegisterAllocator: not so fast but good quality
+ llvm::RegisterRegAlloc::setDefault
+ ((CodeGenOptLevel == llvm::CodeGenOpt::None) ?
+ llvm::createFastRegisterAllocator :
+ llvm::createLinearScanRegisterAllocator);
+
+ GlobalInitialized = true;
+}
+
+
+void Compiler::LLVMErrorHandler(void *UserData, const std::string &Message) {
+ std::string *Error = static_cast<std::string*>(UserData);
+ Error->assign(Message);
+ LOGE("%s", Message.c_str());
+ exit(1);
+}
+
+
+CodeMemoryManager *Compiler::createCodeMemoryManager() {
+ mCodeMemMgr.reset(new CodeMemoryManager());
+ return mCodeMemMgr.get();
+}
+
+
+CodeEmitter *Compiler::createCodeEmitter() {
+ mCodeEmitter.reset(new CodeEmitter(mCodeMemMgr.take()));
+ return mCodeEmitter.get();
+}
+
+
+Compiler::Compiler()
+ : mUseCache(false),
+ mCacheNew(false),
+ mCacheFd(-1),
+ mCacheMapAddr(NULL),
+ mCacheHdr(NULL),
+ mCacheSize(0),
+ mCacheDiff(0),
+ mCodeDataAddr(NULL),
+ mpSymbolLookupFn(NULL),
+ mpSymbolLookupContext(NULL),
+ mContext(NULL),
+ mModule(NULL),
+ mHasLinked(false) /* Turn off linker */ {
+ llvm::remove_fatal_error_handler();
+ llvm::install_fatal_error_handler(LLVMErrorHandler, &mError);
+ mContext = new llvm::LLVMContext();
+ return;
+}
+
+
+int Compiler::readBC(const char *bitcode,
+ size_t bitcodeSize,
+ const BCCchar *resName) {
+ GlobalInitialization();
+
+ if (resName) {
+ // Turn on mUseCache mode iff
+ // 1. Has resName
+ // and, assuming USE_RELOCATE is false:
+ // 2. Later running code doesn't violate the following condition:
+ // mCodeDataAddr (set in loadCacheFile()) ==
+ // mCacheHdr->cachedCodeDataAddr
+ //
+ // BTW, this condition is achievable only when in the earlier
+ // cache-generating run,
+ // mpCodeMem == BccCodeAddr - MaxCodeSize - MaxGlobalVarSize,
+ // which means the mmap'ed is in the reserved area,
+ //
+ // Note: Upon violation, mUseCache will be set back to false.
+ mUseCache = true;
+
+ mCacheFd = openCacheFile(resName, true /* createIfMissing */);
+ if (mCacheFd >= 0 && !mCacheNew) { // Just use cache file
+ return -mCacheFd;
+ }
+ }
+
+ llvm::OwningPtr<llvm::MemoryBuffer> MEM;
+
+ if (bitcode == NULL || bitcodeSize <= 0)
+ return 0;
+
+ // Package input to object MemoryBuffer
+ MEM.reset(llvm::MemoryBuffer::getMemBuffer(
+ llvm::StringRef(bitcode, bitcodeSize)));
+
+ if (MEM.get() == NULL) {
+ setError("Error reading input program bitcode into memory");
+ return hasError();
+ }
+
+ // Read the input Bitcode as a Module
+ mModule = llvm::ParseBitcodeFile(MEM.get(), *mContext, &mError);
+ MEM.reset();
+ return hasError();
+}
+
+
+int Compiler::linkBC(const char *bitcode, size_t bitcodeSize) {
+ llvm::OwningPtr<llvm::MemoryBuffer> MEM;
+
+ if (bitcode == NULL || bitcodeSize <= 0)
+ return 0;
+
+ if (mModule == NULL) {
+ setError("No module presents for linking");
+ return hasError();
+ }
+
+ MEM.reset(llvm::MemoryBuffer::getMemBuffer(
+ llvm::StringRef(bitcode, bitcodeSize)));
+
+ if (MEM.get() == NULL) {
+ setError("Error reading input library bitcode into memory");
+ return hasError();
+ }
+
+ llvm::OwningPtr<llvm::Module> Lib(llvm::ParseBitcodeFile(MEM.get(),
+ *mContext,
+ &mError));
+ if (Lib.get() == NULL)
+ return hasError();
+
+ if (llvm::Linker::LinkModules(mModule, Lib.take(), &mError))
+ return hasError();
+
+ // Everything for linking should be settled down here with no error occurs
+ mHasLinked = true;
+ return hasError();
+}
+
+
+// interface for bccLoadBinary()
+int Compiler::loadCacheFile() {
+ // Check File Descriptor
+ if (mCacheFd < 0) {
+ LOGE("loading cache from invalid mCacheFd = %d\n", (int)mCacheFd);
+ goto giveup;
+ }
+
+ // Check File Size
+ struct stat statCacheFd;
+ if (fstat(mCacheFd, &statCacheFd) < 0) {
+ LOGE("unable to stat mCacheFd = %d\n", (int)mCacheFd);
+ goto giveup;
+ }
+
+ mCacheSize = statCacheFd.st_size;
+
+ if (mCacheSize < sizeof(oBCCHeader) ||
+ mCacheSize <= MaxCodeSize + MaxGlobalVarSize) {
+ LOGE("mCacheFd %d is too small to be correct\n", (int)mCacheFd);
+ goto giveup;
+ }
+
+ if (lseek(mCacheFd, 0, SEEK_SET) != 0) {
+ LOGE("Unable to seek to 0: %s\n", strerror(errno));
+ goto giveup;
+ }
+
+ // Part 1. Deal with the non-codedata section first
+ {
+ // Read cached file and perform quick integrity check
+
+ off_t heuristicCodeOffset = mCacheSize - MaxCodeSize - MaxGlobalVarSize;
+ LOGW("TODO(sliao)@loadCacheFile: mCacheSize=%x, heuristicCodeOffset=%llx",
+ (unsigned int)mCacheSize,
+ (unsigned long long int)heuristicCodeOffset);
+
+ mCacheMapAddr = (char *)malloc(heuristicCodeOffset);
+ if (!mCacheMapAddr) {
+ flock(mCacheFd, LOCK_UN);
+ LOGE("allocation failed.\n");
+ goto bail;
+ }
+
+ size_t nread = TEMP_FAILURE_RETRY1(read(mCacheFd, mCacheMapAddr,
+ heuristicCodeOffset));
+ if (nread != (size_t)heuristicCodeOffset) {
+ LOGE("read(mCacheFd) failed\n");
+ goto bail;
+ }
+
+ mCacheHdr = reinterpret_cast<oBCCHeader *>(mCacheMapAddr);
+ // Sanity check
+ if (mCacheHdr->codeOffset != (uint32_t)heuristicCodeOffset) {
+ LOGE("assertion failed: heuristic code offset is not correct.\n");
+ goto bail;
+ }
+ LOGW("TODO(sliao): mCacheHdr->cachedCodeDataAddr=%x", mCacheHdr->cachedCodeDataAddr);
+ LOGW("mCacheHdr->rootAddr=%x", mCacheHdr->rootAddr);
+ LOGW("mCacheHdr->initAddr=%x", mCacheHdr->initAddr);
+ LOGW("mCacheHdr->codeOffset=%x", mCacheHdr->codeOffset);
+ LOGW("mCacheHdr->codeSize=%x", mCacheHdr->codeSize);
+
+ // Verify the Cache File
+ if (memcmp(mCacheHdr->magic, OBCC_MAGIC, 4) != 0) {
+ LOGE("bad magic word\n");
+ goto bail;
+ }
+
+ if (memcmp(mCacheHdr->magicVersion, OBCC_MAGIC_VERS, 4) != 0) {
+ LOGE("bad oBCC version 0x%08x\n",
+ *reinterpret_cast<uint32_t *>(mCacheHdr->magicVersion));
+ goto bail;
+ }
+
+ if (mCacheSize < mCacheHdr->relocOffset +
+ mCacheHdr->relocCount * sizeof(oBCCRelocEntry)) {
+ LOGE("relocate table overflow\n");
+ goto bail;
+ }
+
+ if (mCacheSize < mCacheHdr->exportVarsOffset +
+ mCacheHdr->exportVarsCount * sizeof(uint32_t)) {
+ LOGE("export variables table overflow\n");
+ goto bail;
+ }
+
+ if (mCacheSize < mCacheHdr->exportFuncsOffset +
+ mCacheHdr->exportFuncsCount * sizeof(uint32_t)) {
+ LOGE("export functions table overflow\n");
+ goto bail;
+ }
+
+ if (mCacheSize < mCacheHdr->exportPragmasOffset +
+ mCacheHdr->exportPragmasCount * sizeof(uint32_t)) {
+ LOGE("export pragmas table overflow\n");
+ goto bail;
+ }
+
+ if (mCacheSize < mCacheHdr->codeOffset + mCacheHdr->codeSize) {
+ LOGE("code cache overflow\n");
+ goto bail;
+ }
+
+ if (mCacheSize < mCacheHdr->dataOffset + mCacheHdr->dataSize) {
+ LOGE("data (global variable) cache overflow\n");
+ goto bail;
+ }
+
+ long pagesize = sysconf(_SC_PAGESIZE);
+ if (mCacheHdr->codeOffset % pagesize != 0) {
+ LOGE("code offset must aligned to pagesize\n");
+ goto bail;
+ }
+ }
+
+ // Part 2. Deal with the codedata section
+ {
+ long pagesize = sysconf(_SC_PAGESIZE);
+
+ if (mCacheHdr->cachedCodeDataAddr % pagesize == 0) {
+ void *addr = reinterpret_cast<char *>(mCacheHdr->cachedCodeDataAddr);
+
+ // Try to mmap at cached address directly.
+ mCodeDataAddr = (char *) mmap(addr,
+ BCC_MMAP_IMG_SIZE,
+ PROT_READ | PROT_EXEC | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED,
+ mCacheFd,
+ mCacheHdr->codeOffset);
+
+ if (mCodeDataAddr && mCodeDataAddr != MAP_FAILED) {
+ // Cheers! Mapped at the cached address successfully.
+
+ // Update the BccMmapImgAddrTaken table (if required)
+ if (mCacheHdr->cachedCodeDataAddr >= BCC_MMAP_IMG_BEGIN) {
+ size_t offset = mCacheHdr->cachedCodeDataAddr - BCC_MMAP_IMG_BEGIN;
+
+ if ((offset % BCC_MMAP_IMG_SIZE) == 0 &&
+ (offset / BCC_MMAP_IMG_SIZE) < BCC_MMAP_IMG_COUNT) {
+ Compiler::BccMmapImgAddrTaken[offset / BCC_MMAP_IMG_SIZE] = true;
+ }
+ }
+
+#if 1
+ // Check the checksum of code and data
+ {
+ uint32_t sum = mCacheHdr->checksum;
+ uint32_t *ptr = (uint32_t *)mCodeDataAddr;
+
+ for (size_t i = 0; i < BCC_MMAP_IMG_SIZE / sizeof(uint32_t); ++i) {
+ sum ^= *ptr++;
+ }
+
+ if (sum != 0) {
+ LOGE("Checksum check failed\n");
+ goto bail;
+ }
+
+ LOGE("Passed checksum even parity verification.\n");
+ }
+#endif
+
+ flock(mCacheFd, LOCK_UN);
+ return 0; // loadCacheFile succeed!
+ }
+ }
+ }
+
+#if !USE_RELOCATE
+ // Note: Since this build does not support relocation, we have no
+ // choose but give up to load the cached file, and recompile the
+ // code.
+
+ flock(mCacheFd, LOCK_UN);
+ goto bail;
+#else
+
+ // Note: Currently, relocation code is not working. Give up now.
+ flock(mCacheFd, LOCK_UN);
+ goto bail;
+
+ // TODO(logan): Following code is not working. Don't use them.
+ // And rewrite them asap.
+#if 0
+ {
+ // Try to allocate at arbitary address. And perform relocation.
+ mCacheMapAddr = (char *) mmap(0,
+ mCacheSize,
+ PROT_READ | PROT_EXEC | PROT_WRITE,
+ MAP_PRIVATE,
+ mCacheFd,
+ 0);
+
+ if (mCacheMapAddr == MAP_FAILED) {
+ LOGE("unable to mmap .oBBC cache: %s\n", strerror(errno));
+ flock(mCacheFd, LOCK_UN);
+ goto giveup;
+ }
+
+ flock(mCacheFd, LOCK_UN);
+ mCodeDataAddr = mCacheMapAddr + mCacheHdr->codeOffset;
+
+ // Relocate
+ mCacheDiff = mCodeDataAddr -
+ reinterpret_cast<char *>(mCacheHdr->cachedCodeDataAddr);
+
+ if (mCacheDiff) { // To relocate
+ if (mCacheHdr->rootAddr) {
+ mCacheHdr->rootAddr += mCacheDiff;
+ }
+
+ if (mCacheHdr->initAddr) {
+ mCacheHdr->initAddr += mCacheDiff;
+ }
+
+ oBCCRelocEntry *cachedRelocTable =
+ reinterpret_cast<oBCCRelocEntry *>(mCacheMapAddr +
+ mCacheHdr->relocOffset);
+
+ std::vector<llvm::MachineRelocation> relocations;
+
+ // Read in the relocs
+ for (size_t i = 0; i < mCacheHdr->relocCount; i++) {
+ oBCCRelocEntry *entry = &cachedRelocTable[i];
+
+ llvm::MachineRelocation reloc =
+ llvm::MachineRelocation::getGV((uintptr_t)entry->relocOffset,
+ (unsigned)entry->relocType, 0, 0);
+
+ reloc.setResultPointer(
+ reinterpret_cast<char *>(entry->cachedResultAddr) + mCacheDiff);
+
+ relocations.push_back(reloc);
+ }
+
+ // Rewrite machine code using llvm::TargetJITInfo relocate
+ {
+ llvm::TargetMachine *TM = NULL;
+ const llvm::Target *Target;
+ std::string FeaturesStr;
+
+ // Create TargetMachine
+ Target = llvm::TargetRegistry::lookupTarget(Triple, mError);
+ if (hasError())
+ goto bail;
+
+ if (!CPU.empty() || !Features.empty()) {
+ llvm::SubtargetFeatures F;
+ F.setCPU(CPU);
+ for (std::vector<std::string>::const_iterator I = Features.begin(),
+ E = Features.end(); I != E; I++)
+ F.AddFeature(*I);
+ FeaturesStr = F.getString();
+ }
+
+ TM = Target->createTargetMachine(Triple, FeaturesStr);
+ if (TM == NULL) {
+ setError("Failed to create target machine implementation for the"
+ " specified triple '" + Triple + "'");
+ goto bail;
+ }
+
+ TM->getJITInfo()->relocate(mCodeDataAddr,
+ &relocations[0], relocations.size(),
+ (unsigned char *)mCodeDataAddr+MaxCodeSize);
+
+ if (mCodeEmitter.get()) {
+ mCodeEmitter->Disassemble(llvm::StringRef("cache"),
+ reinterpret_cast<uint8_t*>(mCodeDataAddr),
+ 2 * 1024 /*MaxCodeSize*/,
+ false);
+ }
+
+ delete TM;
+ }
+ } // End of if (mCacheDiff)
+
+ return 0; // Success!
+ }
+#endif
+#endif
+
+bail:
+ if (mCacheMapAddr) {
+ free(mCacheMapAddr);
+ }
+
+ if (mCodeDataAddr && mCodeDataAddr != MAP_FAILED) {
+ if (munmap(mCodeDataAddr, BCC_MMAP_IMG_SIZE) != 0) {
+ LOGE("munmap failed: %s\n", strerror(errno));
+ }
+ }
+
+ mCacheMapAddr = NULL;
+ mCacheHdr = NULL;
+ mCodeDataAddr = NULL;
+
+giveup:
+ return 1;
+}
+
+// interace for bccCompileBC()
+int Compiler::compile() {
+ llvm::TargetData *TD = NULL;
+
+ llvm::TargetMachine *TM = NULL;
+ const llvm::Target *Target;
+ std::string FeaturesStr;
+
+ llvm::FunctionPassManager *CodeGenPasses = NULL;
+
+ const llvm::NamedMDNode *PragmaMetadata;
+ const llvm::NamedMDNode *ExportVarMetadata;
+ const llvm::NamedMDNode *ExportFuncMetadata;
+
+ if (mModule == NULL) // No module was loaded
+ return 0;
+
+ // Create TargetMachine
+ Target = llvm::TargetRegistry::lookupTarget(Triple, mError);
+ if (hasError())
+ goto on_bcc_compile_error;
+
+ if (!CPU.empty() || !Features.empty()) {
+ llvm::SubtargetFeatures F;
+ F.setCPU(CPU);
+
+ for (std::vector<std::string>::const_iterator
+ I = Features.begin(), E = Features.end(); I != E; I++) {
+ F.AddFeature(*I);
+ }
+
+ FeaturesStr = F.getString();
+ }
+
+ TM = Target->createTargetMachine(Triple, FeaturesStr);
+ if (TM == NULL) {
+ setError("Failed to create target machine implementation for the"
+ " specified triple '" + Triple + "'");
+ goto on_bcc_compile_error;
+ }
+
+ // Create memory manager for creation of code emitter later.
+ if (!mCodeMemMgr.get() && !createCodeMemoryManager()) {
+ setError("Failed to startup memory management for further compilation");
+ goto on_bcc_compile_error;
+ }
+ mCodeDataAddr = (char *) (mCodeMemMgr.get()->getCodeMemBase());
+
+ // Create code emitter
+ if (!mCodeEmitter.get()) {
+ if (!createCodeEmitter()) {
+ setError("Failed to create machine code emitter to complete"
+ " the compilation");
+ goto on_bcc_compile_error;
+ }
+ } else {
+ // Reuse the code emitter
+ mCodeEmitter->reset();
+ }
+
+ mCodeEmitter->setTargetMachine(*TM);
+ mCodeEmitter->registerSymbolCallback(mpSymbolLookupFn,
+ mpSymbolLookupContext);
+
+ // Get target data from Module
+ TD = new llvm::TargetData(mModule);
+
+ // Load named metadata
+ ExportVarMetadata = mModule->getNamedMetadata(ExportVarMetadataName);
+ ExportFuncMetadata = mModule->getNamedMetadata(ExportFuncMetadataName);
+ PragmaMetadata = mModule->getNamedMetadata(PragmaMetadataName);
+
+ // Create LTO passes and run them on the mModule
+ if (mHasLinked) {
+ llvm::TimePassesIsEnabled = true; // TODO(all)
+ llvm::PassManager LTOPasses;
+ LTOPasses.add(new llvm::TargetData(*TD));
+
+ std::vector<const char*> ExportSymbols;
+
+ // A workaround for getting export variable and function name. Will refine
+ // it soon.
+ if (ExportVarMetadata) {
+ for (int i = 0, e = ExportVarMetadata->getNumOperands(); i != e; i++) {
+ llvm::MDNode *ExportVar = ExportVarMetadata->getOperand(i);
+ if (ExportVar != NULL && ExportVar->getNumOperands() > 1) {
+ llvm::Value *ExportVarNameMDS = ExportVar->getOperand(0);
+ if (ExportVarNameMDS->getValueID() == llvm::Value::MDStringVal) {
+ llvm::StringRef ExportVarName =
+ static_cast<llvm::MDString*>(ExportVarNameMDS)->getString();
+ ExportSymbols.push_back(ExportVarName.data());
+ }
+ }
+ }
+ }
+
+ if (ExportFuncMetadata) {
+ for (int i = 0, e = ExportFuncMetadata->getNumOperands(); i != e; i++) {
+ llvm::MDNode *ExportFunc = ExportFuncMetadata->getOperand(i);
+ if (ExportFunc != NULL && ExportFunc->getNumOperands() > 0) {
+ llvm::Value *ExportFuncNameMDS = ExportFunc->getOperand(0);
+ if (ExportFuncNameMDS->getValueID() == llvm::Value::MDStringVal) {
+ llvm::StringRef ExportFuncName =
+ static_cast<llvm::MDString*>(ExportFuncNameMDS)->getString();
+ ExportSymbols.push_back(ExportFuncName.data());
+ }
+ }
+ }
+ }
+ // root() and init() are born to be exported
+ ExportSymbols.push_back("root");
+ ExportSymbols.push_back("init");
+
+ // We now create passes list performing LTO. These are copied from
+ // (including comments) llvm::createStandardLTOPasses().
+
+ // Internalize all other symbols not listed in ExportSymbols
+ LTOPasses.add(llvm::createInternalizePass(ExportSymbols));
+
+ // Propagate constants at call sites into the functions they call. This
+ // opens opportunities for globalopt (and inlining) by substituting
+ // function pointers passed as arguments to direct uses of functions.
+ LTOPasses.add(llvm::createIPSCCPPass());
+
+ // Now that we internalized some globals, see if we can hack on them!
+ LTOPasses.add(llvm::createGlobalOptimizerPass());
+
+ // Linking modules together can lead to duplicated global constants, only
+ // keep one copy of each constant...
+ LTOPasses.add(llvm::createConstantMergePass());
+
+ // Remove unused arguments from functions...
+ LTOPasses.add(llvm::createDeadArgEliminationPass());
+
+ // Reduce the code after globalopt and ipsccp. Both can open up
+ // significant simplification opportunities, and both can propagate
+ // functions through function pointers. When this happens, we often have
+ // to resolve varargs calls, etc, so let instcombine do this.
+ LTOPasses.add(llvm::createInstructionCombiningPass());
+
+ // Inline small functions
+ LTOPasses.add(llvm::createFunctionInliningPass());
+
+ // Remove dead EH info.
+ LTOPasses.add(llvm::createPruneEHPass());
+
+ // Internalize the globals again after inlining
+ LTOPasses.add(llvm::createGlobalOptimizerPass());
+
+ // Remove dead functions.
+ LTOPasses.add(llvm::createGlobalDCEPass());
+
+ // If we didn't decide to inline a function, check to see if we can
+ // transform it to pass arguments by value instead of by reference.
+ LTOPasses.add(llvm::createArgumentPromotionPass());
+
+ // The IPO passes may leave cruft around. Clean up after them.
+ LTOPasses.add(llvm::createInstructionCombiningPass());
+ LTOPasses.add(llvm::createJumpThreadingPass());
+
+ // Break up allocas
+ LTOPasses.add(llvm::createScalarReplAggregatesPass());
+
+ // Run a few AA driven optimizations here and now, to cleanup the code.
+ LTOPasses.add(llvm::createFunctionAttrsPass()); // Add nocapture.
+ LTOPasses.add(llvm::createGlobalsModRefPass()); // IP alias analysis.
+
+ // Hoist loop invariants.
+ LTOPasses.add(llvm::createLICMPass());
+
+ // Remove redundancies.
+ LTOPasses.add(llvm::createGVNPass());
+
+ // Remove dead memcpys.
+ LTOPasses.add(llvm::createMemCpyOptPass());
+
+ // Nuke dead stores.
+ LTOPasses.add(llvm::createDeadStoreEliminationPass());
+
+ // Cleanup and simplify the code after the scalar optimizations.
+ LTOPasses.add(llvm::createInstructionCombiningPass());
+
+ LTOPasses.add(llvm::createJumpThreadingPass());
+
+ // Delete basic blocks, which optimization passes may have killed.
+ LTOPasses.add(llvm::createCFGSimplificationPass());
+
+ // Now that we have optimized the program, discard unreachable functions.
+ LTOPasses.add(llvm::createGlobalDCEPass());
+
+ LTOPasses.run(*mModule);
+ }
+
+ // Create code-gen pass to run the code emitter
+ CodeGenPasses = new llvm::FunctionPassManager(mModule);
+ CodeGenPasses->add(TD); // Will take the ownership of TD
+
+ if (TM->addPassesToEmitMachineCode(*CodeGenPasses,
+ *mCodeEmitter,
+ CodeGenOptLevel)) {
+ setError("The machine code emission is not supported by BCC on target '"
+ + Triple + "'");
+ goto on_bcc_compile_error;
+ }
+
+ // Run the pass (the code emitter) on every non-declaration function in the
+ // module
+ CodeGenPasses->doInitialization();
+ for (llvm::Module::iterator I = mModule->begin(), E = mModule->end();
+ I != E; I++) {
+ if (!I->isDeclaration()) {
+ CodeGenPasses->run(*I);
+ }
+ }
+
+ CodeGenPasses->doFinalization();
+
+ // Copy the global address mapping from code emitter and remapping
+ if (ExportVarMetadata) {
+ for (int i = 0, e = ExportVarMetadata->getNumOperands(); i != e; i++) {
+ llvm::MDNode *ExportVar = ExportVarMetadata->getOperand(i);
+ if (ExportVar != NULL && ExportVar->getNumOperands() > 1) {
+ llvm::Value *ExportVarNameMDS = ExportVar->getOperand(0);
+ if (ExportVarNameMDS->getValueID() == llvm::Value::MDStringVal) {
+ llvm::StringRef ExportVarName =
+ static_cast<llvm::MDString*>(ExportVarNameMDS)->getString();
+
+ CodeEmitter::global_addresses_const_iterator I, E;
+ for (I = mCodeEmitter->global_address_begin(),
+ E = mCodeEmitter->global_address_end();
+ I != E; I++) {
+ if (I->first->getValueID() != llvm::Value::GlobalVariableVal)
+ continue;
+ if (ExportVarName == I->first->getName()) {
+ mExportVars.push_back(I->second);
+ break;
+ }
+ }
+ if (I != mCodeEmitter->global_address_end())
+ continue; // found
+ }
+ }
+ // if reaching here, we know the global variable record in metadata is
+ // not found. So we make an empty slot
+ mExportVars.push_back(NULL);
+ }
+ assert((mExportVars.size() == ExportVarMetadata->getNumOperands()) &&
+ "Number of slots doesn't match the number of export variables!");
+ }
+
+ if (ExportFuncMetadata) {
+ for (int i = 0, e = ExportFuncMetadata->getNumOperands(); i != e; i++) {
+ llvm::MDNode *ExportFunc = ExportFuncMetadata->getOperand(i);
+ if (ExportFunc != NULL && ExportFunc->getNumOperands() > 0) {
+ llvm::Value *ExportFuncNameMDS = ExportFunc->getOperand(0);
+ if (ExportFuncNameMDS->getValueID() == llvm::Value::MDStringVal) {
+ llvm::StringRef ExportFuncName =
+ static_cast<llvm::MDString*>(ExportFuncNameMDS)->getString();
+ mExportFuncs.push_back(mCodeEmitter->lookup(ExportFuncName));
+ }
+ }
+ }
+ }
+
+ // Tell code emitter now can release the memory using during the JIT since
+ // we have done the code emission
+ mCodeEmitter->releaseUnnecessary();
+
+ // Finally, read pragma information from the metadata node of the @Module if
+ // any.
+ if (PragmaMetadata)
+ for (int i = 0, e = PragmaMetadata->getNumOperands(); i != e; i++) {
+ llvm::MDNode *Pragma = PragmaMetadata->getOperand(i);
+ if (Pragma != NULL &&
+ Pragma->getNumOperands() == 2 /* should have exactly 2 operands */) {
+ llvm::Value *PragmaNameMDS = Pragma->getOperand(0);
+ llvm::Value *PragmaValueMDS = Pragma->getOperand(1);
+
+ if ((PragmaNameMDS->getValueID() == llvm::Value::MDStringVal) &&
+ (PragmaValueMDS->getValueID() == llvm::Value::MDStringVal)) {
+ llvm::StringRef PragmaName =
+ static_cast<llvm::MDString*>(PragmaNameMDS)->getString();
+ llvm::StringRef PragmaValue =
+ static_cast<llvm::MDString*>(PragmaValueMDS)->getString();
+
+ mPragmas.push_back(
+ std::make_pair(std::string(PragmaName.data(),
+ PragmaName.size()),
+ std::string(PragmaValue.data(),
+ PragmaValue.size())));
+ }
+ }
+ }
+
+on_bcc_compile_error:
+ // LOGE("on_bcc_compiler_error");
+ if (CodeGenPasses) {
+ delete CodeGenPasses;
+ } else if (TD) {
+ delete TD;
+ }
+ if (TM)
+ delete TM;
+
+ if (mError.empty()) {
+ if (mUseCache && mCacheFd >= 0 && mCacheNew) {
+ genCacheFile();
+ flock(mCacheFd, LOCK_UN);
+ }
+
+ return false;
+ }
+
+ // LOGE(getErrorMessage());
+ return true;
+}
+
+
+// interface for bccGetScriptLabel()
+void *Compiler::lookup(const char *name) {
+ void *addr = NULL;
+ if (mUseCache && mCacheFd >= 0 && !mCacheNew) {
+ if (!strcmp(name, "root")) {
+ addr = reinterpret_cast<void *>(mCacheHdr->rootAddr);
+ } else if (!strcmp(name, "init")) {
+ addr = reinterpret_cast<void *>(mCacheHdr->initAddr);
+ }
+ return addr;
+ }
+
+ if (mCodeEmitter.get())
+ // Find function pointer
+ addr = mCodeEmitter->lookup(name);
+ return addr;
+}
+
+
+// Interface for bccGetExportVars()
+void Compiler::getExportVars(BCCsizei *actualVarCount,
+ BCCsizei maxVarCount,
+ BCCvoid **vars) {
+ int varCount;
+
+ if (mUseCache && mCacheFd >= 0 && !mCacheNew) {
+ varCount = static_cast<int>(mCacheHdr->exportVarsCount);
+ if (actualVarCount)
+ *actualVarCount = varCount;
+ if (varCount > maxVarCount)
+ varCount = maxVarCount;
+ if (vars) {
+ uint32_t *cachedVars = (uint32_t *)(mCacheMapAddr +
+ mCacheHdr->exportVarsOffset);
+
+ for (int i = 0; i < varCount; i++) {
+ *vars++ = (BCCvoid *)(reinterpret_cast<char *>(*cachedVars) +
+ mCacheDiff);
+ cachedVars++;
+ }
+ }
+ return;
+ }
+
+ varCount = mExportVars.size();
+ if (actualVarCount)
+ *actualVarCount = varCount;
+ if (varCount > maxVarCount)
+ varCount = maxVarCount;
+ if (vars) {
+ for (ExportVarList::const_iterator
+ I = mExportVars.begin(), E = mExportVars.end(); I != E; I++) {
+ *vars++ = *I;
+ }
+ }
+
+ return;
+}
+
+
+// Interface for bccGetExportFuncs()
+void Compiler::getExportFuncs(BCCsizei *actualFuncCount,
+ BCCsizei maxFuncCount,
+ BCCvoid **funcs) {
+ int funcCount;
+
+ if (mUseCache && mCacheFd >= 0 && !mCacheNew) {
+ funcCount = static_cast<int>(mCacheHdr->exportFuncsCount);
+ if (actualFuncCount)
+ *actualFuncCount = funcCount;
+ if (funcCount > maxFuncCount)
+ funcCount = maxFuncCount;
+ if (funcs) {
+ uint32_t *cachedFuncs = (uint32_t *)(mCacheMapAddr +
+ mCacheHdr->exportFuncsOffset);
+
+ for (int i = 0; i < funcCount; i++) {
+ *funcs++ = (BCCvoid *)(reinterpret_cast<char *>(*cachedFuncs) +
+ mCacheDiff);
+ cachedFuncs++;
+ }
+ }
+ return;
+ }
+
+ funcCount = mExportFuncs.size();
+ if (actualFuncCount)
+ *actualFuncCount = funcCount;
+ if (funcCount > maxFuncCount)
+ funcCount = maxFuncCount;
+ if (funcs) {
+ for (ExportFuncList::const_iterator I = mExportFuncs.begin(),
+ E = mExportFuncs.end();
+ I != E;
+ I++) {
+ *funcs++ = *I;
+ }
+ }
+
+ return;
+}
+
+
+// Interface for bccGetPragmas()
+void Compiler::getPragmas(BCCsizei *actualStringCount,
+ BCCsizei maxStringCount,
+ BCCchar **strings) {
+ int stringCount;
+ if (mUseCache && mCacheFd >= 0 && !mCacheNew) {
+ if (actualStringCount)
+ *actualStringCount = 0; // XXX
+ return;
+ }
+
+ stringCount = mPragmas.size() * 2;
+
+ if (actualStringCount)
+ *actualStringCount = stringCount;
+ if (stringCount > maxStringCount)
+ stringCount = maxStringCount;
+ if (strings) {
+ for (PragmaList::const_iterator it = mPragmas.begin();
+ stringCount > 0;
+ stringCount -= 2, it++) {
+ *strings++ = const_cast<BCCchar*>(it->first.c_str());
+ *strings++ = const_cast<BCCchar*>(it->second.c_str());
+ }
+ }
+
+ return;
+}
+
+
+// Interface for bccGetFunctions()
+void Compiler::getFunctions(BCCsizei *actualFunctionCount,
+ BCCsizei maxFunctionCount,
+ BCCchar **functions) {
+ if (mCodeEmitter.get())
+ mCodeEmitter->getFunctionNames(actualFunctionCount,
+ maxFunctionCount,
+ functions);
+ else
+ *actualFunctionCount = 0;
+
+ return;
+}
+
+
+// Interface for bccGetFunctionBinary()
+void Compiler::getFunctionBinary(BCCchar *function,
+ BCCvoid **base,
+ BCCsizei *length) {
+ if (mCodeEmitter.get()) {
+ mCodeEmitter->getFunctionBinary(function, base, length);
+ } else {
+ *base = NULL;
+ *length = 0;
+ }
+ return;
+}
+
+
+Compiler::~Compiler() {
+ if (!mCodeMemMgr.get()) {
+ // mCodeDataAddr and mCacheMapAddr are from loadCacheFile and not
+ // managed by CodeMemoryManager.
+
+ if (mCodeDataAddr != 0 && mCodeDataAddr != MAP_FAILED) {
+ if (munmap(mCodeDataAddr, BCC_MMAP_IMG_SIZE) < 0) {
+ LOGE("munmap failed while releasing mCodeDataAddr\n");
+ }
+ }
+
+ if (mCacheMapAddr) {
+ free(mCacheMapAddr);
+ }
+
+ mCodeDataAddr = 0;
+ mCacheMapAddr = 0;
+ }
+
+ delete mModule;
+ delete mContext;
+
+ // llvm::llvm_shutdown();
+}
+
+
+// Design of caching EXE:
+// ======================
+// 1. Each process will have virtual address available starting at 0x7e00000.
+// E.g., Books and Youtube all have its own 0x7e00000. Next, we should
+// minimize the chance of needing to do relocation INSIDE an app too.
+//
+// 2. Each process will have ONE class static variable called BccCodeAddr.
+// I.e., even though the Compiler class will have multiple Compiler objects,
+// e.g, one object for carousel.rs and the other for pageturn.rs,
+// both Compiler objects will share 1 static variable called BccCodeAddr.
+//
+// Key observation: Every app (process) initiates, say 3, scripts (which
+// correspond to 3 Compiler objects) in the same order, usually.
+//
+// So, we should mmap to, e.g., 0x7e00000, 0x7e40000, 0x7e80000 for the 3
+// scripts, respectively. Each time, BccCodeAddr should be updated after
+// JITTing a script. BTW, in ~Compiler(), BccCodeAddr should NOT be
+// decremented back by CodeDataSize. I.e., for 3 scripts: A, B, C,
+// even if it's A -> B -> ~B -> C -> ~C -> B -> C ... no relocation will
+// ever be needed.)
+//
+// If we are lucky, then we don't need relocation ever, since next time the
+// application gets run, the 3 scripts are likely created in the SAME order.
+//
+//
+// End-to-end algorithm on when to caching and when to JIT:
+// ========================================================
+// Prologue:
+// ---------
+// Assertion: bccReadBC() is always called and is before bccCompileBC(),
+// bccLoadBinary(), ...
+//
+// Key variable definitions: Normally,
+// Compiler::BccCodeAddr: non-zero if (USE_CACHE)
+// | (Stricter, because currently relocation doesn't work. So mUseCache only
+// | when BccCodeAddr is nonzero.)
+// V
+// mUseCache: In addition to (USE_CACHE), resName is non-zero
+// Note: mUseCache will be set to false later on whenever we find that caching
+// won't work. E.g., when mCodeDataAddr != mCacheHdr->cachedCodeDataAddr.
+// This is because currently relocation doesn't work.
+// | (Stricter, initially)
+// V
+// mCacheFd: In addition, >= 0 if openCacheFile() returns >= 0
+// | (Stricter)
+// V
+// mCacheNew: In addition, mCacheFd's size is 0, so need to call genCacheFile()
+// at the end of compile()
+//
+//
+// Main algorithm:
+// ---------------
+// #if !USE_RELOCATE
+// Case 1. ReadBC() doesn't detect a cache file:
+// compile(), which calls genCacheFile() at the end.
+// Note: mCacheNew will guard the invocation of genCacheFile()
+// Case 2. ReadBC() find a cache file
+// loadCacheFile(). But if loadCacheFile() failed, should go to Case 1.
+// #endif
+
+// Note: loadCacheFile() and genCacheFile() go hand in hand
+void Compiler::genCacheFile() {
+ if (lseek(mCacheFd, 0, SEEK_SET) != 0) {
+ LOGE("Unable to seek to 0: %s\n", strerror(errno));
+ return;
+ }
+
+ bool codeOffsetNeedPadding = false;
+
+ uint32_t offset = sizeof(oBCCHeader);
+
+ // BCC Cache File Header
+ oBCCHeader *hdr = (oBCCHeader *)malloc(sizeof(oBCCHeader));
+
+ if (!hdr) {
+ LOGE("Unable to allocate oBCCHeader.\n");
+ return;
+ }
+
+ // Magic Words
+ memcpy(hdr->magic, OBCC_MAGIC, 4);
+ memcpy(hdr->magicVersion, OBCC_MAGIC_VERS, 4);
+
+ // Timestamp
+ hdr->sourceWhen = 0; // TODO(all)
+ hdr->rslibWhen = 0; // TODO(all)
+ hdr->libRSWhen = 0; // TODO(all)
+ hdr->libbccWhen = 0; // TODO(all)
+
+ // Current Memory Address (Saved for Recalculation)
+ hdr->cachedCodeDataAddr = reinterpret_cast<uint32_t>(mCodeDataAddr);
+ hdr->rootAddr = reinterpret_cast<uint32_t>(lookup("root"));
+ hdr->initAddr = reinterpret_cast<uint32_t>(lookup("init"));
+
+ // Relocation Table Offset and Entry Count
+ hdr->relocOffset = sizeof(oBCCHeader);
+ hdr->relocCount = mCodeEmitter->getCachingRelocations().size();
+
+ offset += hdr->relocCount * (sizeof(oBCCRelocEntry));
+
+ // Export Variable Table Offset and Entry Count
+ hdr->exportVarsOffset = offset;
+ hdr->exportVarsCount = mExportVars.size();
+
+ offset += hdr->exportVarsCount * sizeof(uint32_t);
+
+ // Export Function Table Offset and Entry Count
+ hdr->exportFuncsOffset = offset;
+ hdr->exportFuncsCount = mExportFuncs.size();
+
+ offset += hdr->exportFuncsCount * sizeof(uint32_t);
+
+ // Export Pragmas Table Offset and Entry Count
+ hdr->exportPragmasOffset = offset;
+ hdr->exportPragmasCount = 0; // TODO(all): mPragmas.size();
+
+ offset += hdr->exportPragmasCount * sizeof(uint32_t);
+
+ // Code Offset and Size
+
+ { // Always pad to the page boundary for now
+ long pagesize = sysconf(_SC_PAGESIZE);
+
+ if (offset % pagesize > 0) {
+ codeOffsetNeedPadding = true;
+ offset += pagesize - (offset % pagesize);
+ }
+ }
+
+ hdr->codeOffset = offset;
+ hdr->codeSize = MaxCodeSize;
+
+ offset += hdr->codeSize;
+
+ // Data (Global Variable) Offset and Size
+ hdr->dataOffset = offset;
+ hdr->dataSize = MaxGlobalVarSize;
+
+ offset += hdr->dataSize;
+
+ // Checksum
+#if 1
+ {
+ // Note: This is an simple checksum implementation that are using xor
+ // to calculate even parity (for code and data only).
+
+ uint32_t sum = 0;
+ uint32_t *ptr = (uint32_t *)mCodeDataAddr;
+
+ for (size_t i = 0; i < BCC_MMAP_IMG_SIZE / sizeof(uint32_t); ++i) {
+ sum ^= *ptr++;
+ }
+
+ hdr->checksum = sum;
+ }
+#else
+ hdr->checksum = 0; // Set Field checksum. TODO(all)
+#endif
+
+ // Write Header
+ sysWriteFully(mCacheFd, reinterpret_cast<char const *>(hdr),
+ sizeof(oBCCHeader), "Write oBCC header");
+
+ // Write Relocation Entry Table
+ {
+ size_t allocSize = hdr->relocCount * sizeof(oBCCRelocEntry);
+
+ oBCCRelocEntry const*records = &mCodeEmitter->getCachingRelocations()[0];
+
+ sysWriteFully(mCacheFd, reinterpret_cast<char const *>(records),
+ allocSize, "Write Relocation Entries");
+ }
+
+ // Write Export Variables Table
+ {
+ uint32_t *record, *ptr;
+
+ record = (uint32_t *)calloc(hdr->exportVarsCount, sizeof(uint32_t));
+ ptr = record;
+
+ if (!record) {
+ goto bail;
+ }
+
+ for (ExportVarList::const_iterator I = mExportVars.begin(),
+ E = mExportVars.end(); I != E; I++) {
+ *ptr++ = reinterpret_cast<uint32_t>(*I);
+ }
+
+ sysWriteFully(mCacheFd, reinterpret_cast<char const *>(record),
+ hdr->exportVarsCount * sizeof(uint32_t),
+ "Write ExportVars");
+
+ free(record);
+ }
+
+ // Write Export Functions Table
+ {
+ uint32_t *record, *ptr;
+
+ record = (uint32_t *)calloc(hdr->exportFuncsCount, sizeof(uint32_t));
+ ptr = record;
+
+ if (!record) {
+ goto bail;
+ }
+
+ for (ExportFuncList::const_iterator I = mExportFuncs.begin(),
+ E = mExportFuncs.end(); I != E; I++) {
+ *ptr++ = reinterpret_cast<uint32_t>(*I);
+ }
+
+ sysWriteFully(mCacheFd, reinterpret_cast<char const *>(record),
+ hdr->exportFuncsCount * sizeof(uint32_t),
+ "Write ExportFuncs");
+
+ free(record);
+ }
+
+
+ // TODO(all): Write Export Pragmas Table
+#if 0
+#else
+ // Note: As long as we have comment out export pragmas table code,
+ // we have to seek the position to correct offset.
+
+ lseek(mCacheFd, hdr->codeOffset, SEEK_SET);
+#endif
+
+ if (codeOffsetNeedPadding) {
+ // requires additional padding
+ lseek(mCacheFd, hdr->codeOffset, SEEK_SET);
+ }
+
+ // Write Generated Code and Global Variable
+ sysWriteFully(mCacheFd, mCodeDataAddr, MaxCodeSize + MaxGlobalVarSize,
+ "Write code and global variable");
+
+ goto close_return;
+
+bail:
+ if (ftruncate(mCacheFd, 0) != 0) {
+ LOGW("Warning: unable to truncate cache file: %s\n", strerror(errno));
+ }
+
+close_return:
+ free(hdr);
+ close(mCacheFd);
+ mCacheFd = -1;
+}
+
+
+// OpenCacheFile() returns fd of the cache file.
+// Input:
+// BCCchar *resName: Used to genCacheFileName()
+// bool createIfMissing: If false, turn off caching
+// Output:
+// returns fd: If -1: Failed
+// mCacheNew: If true, the returned fd is new. Otherwise, the fd is the
+// cache file's file descriptor
+// Note: openCacheFile() will check the cache file's validity,
+// such as Magic number, sourceWhen... dependencies.
+int Compiler::openCacheFile(const BCCchar *resName, bool createIfMissing) {
+ int fd, cc;
+ struct stat fdStat, fileStat;
+ bool readOnly = false;
+
+ char *cacheFileName = genCacheFileName(resName, ".oBCC");
+
+ mCacheNew = false;
+
+retry:
+ /*
+ * Try to open the cache file. If we've been asked to,
+ * create it if it doesn't exist.
+ */
+ fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
+ if (fd < 0) {
+ fd = open(cacheFileName, O_RDONLY, 0);
+ if (fd < 0) {
+ if (createIfMissing) {
+ LOGW("Can't open bcc-cache '%s': %s\n",
+ cacheFileName, strerror(errno));
+ mUseCache = false;
+ }
+ return fd;
+ }
+ readOnly = true;
+ }
+
+ /*
+ * Grab an exclusive lock on the cache file. If somebody else is
+ * working on it, we'll block here until they complete.
+ */
+ LOGV("bcc: locking cache file %s (fd=%d, boot=%d)\n",
+ cacheFileName, fd);
+
+ cc = flock(fd, LOCK_EX | LOCK_NB);
+ if (cc != 0) {
+ LOGD("bcc: sleeping on flock(%s)\n", cacheFileName);
+ cc = flock(fd, LOCK_EX);
+ }
+
+ if (cc != 0) {
+ LOGE("Can't lock bcc cache '%s': %d\n", cacheFileName, cc);
+ close(fd);
+ return -1;
+ }
+ LOGV("bcc: locked cache file\n");
+
+ /*
+ * Check to see if the fd we opened and locked matches the file in
+ * the filesystem. If they don't, then somebody else unlinked ours
+ * and created a new file, and we need to use that one instead. (If
+ * we caught them between the unlink and the create, we'll get an
+ * ENOENT from the file stat.)
+ */
+ cc = fstat(fd, &fdStat);
+ if (cc != 0) {
+ LOGE("Can't stat open file '%s'\n", cacheFileName);
+ LOGV("bcc: unlocking cache file %s\n", cacheFileName);
+ goto close_fail;
+ }
+ cc = stat(cacheFileName, &fileStat);
+ if (cc != 0 ||
+ fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino) {
+ LOGD("bcc: our open cache file is stale; sleeping and retrying\n");
+ LOGV("bcc: unlocking cache file %s\n", cacheFileName);
+ flock(fd, LOCK_UN);
+ close(fd);
+ usleep(250 * 1000); // if something is hosed, don't peg machine
+ goto retry;
+ }
+
+ /*
+ * We have the correct file open and locked. If the file size is zero,
+ * then it was just created by us, and we want to fill in some fields
+ * in the "bcc" header and set "mCacheNew". Otherwise, we want to
+ * verify that the fields in the header match our expectations, and
+ * reset the file if they don't.
+ */
+ if (fdStat.st_size == 0) {
+ if (readOnly) { // The device is readOnly --> close_fail
+ LOGW("bcc: file has zero length and isn't writable\n");
+ goto close_fail;
+ }
+ /*cc = createEmptyHeader(fd);
+ if (cc != 0)
+ goto close_fail;
+ */
+ mCacheNew = true;
+ LOGV("bcc: successfully initialized new cache file\n");
+ } else {
+ // Calculate sourceWhen
+ // XXX
+ uint32_t sourceWhen = 0;
+ uint32_t rslibWhen = 0;
+ uint32_t libRSWhen = 0;
+ uint32_t libbccWhen = 0;
+ if (!checkHeaderAndDependencies(fd,
+ sourceWhen,
+ rslibWhen,
+ libRSWhen,
+ libbccWhen)) {
+ // If checkHeaderAndDependencies returns 0: FAILED
+ // Will truncate the file and retry to createIfMissing the file
+
+ if (readOnly) { // Shouldn't be readonly.
+ /*
+ * We could unlink and rewrite the file if we own it or
+ * the "sticky" bit isn't set on the directory. However,
+ * we're not able to truncate it, which spoils things. So,
+ * give up now.
+ */
+ if (createIfMissing) {
+ LOGW("Cached file %s is stale and not writable\n",
+ cacheFileName);
+ }
+ goto close_fail;
+ }
+
+ /*
+ * If we truncate the existing file before unlinking it, any
+ * process that has it mapped will fail when it tries to touch
+ * the pages? Probably OK because we use MAP_PRIVATE.
+ */
+ LOGD("oBCC file is stale or bad; removing and retrying (%s)\n",
+ cacheFileName);
+ if (ftruncate(fd, 0) != 0) {
+ LOGW("Warning: unable to truncate cache file '%s': %s\n",
+ cacheFileName, strerror(errno));
+ /* keep going */
+ }
+ if (unlink(cacheFileName) != 0) {
+ LOGW("Warning: unable to remove cache file '%s': %d %s\n",
+ cacheFileName, errno, strerror(errno));
+ /* keep going; permission failure should probably be fatal */
+ }
+ LOGV("bcc: unlocking cache file %s\n", cacheFileName);
+ flock(fd, LOCK_UN);
+ close(fd);
+ goto retry;
+ } else {
+ // Got cacheFile! Good to go.
+ LOGV("Good cache file\n");
+ }
+ }
+
+ assert(fd >= 0);
+ return fd;
+
+close_fail:
+ flock(fd, LOCK_UN);
+ close(fd);
+ return -1;
+} // End of openCacheFile()
+
+char *Compiler::genCacheFileName(const char *fileName,
+ const char *subFileName) {
+ char nameBuf[512];
+ static const char kCachePath[] = "bcc-cache";
+ char absoluteFile[sizeof(nameBuf)];
+ const size_t kBufLen = sizeof(nameBuf) - 1;
+ const char *dataRoot;
+ char *cp;
+
+ // Get the absolute path of the raw/***.bc file.
+ absoluteFile[0] = '\0';
+ if (fileName[0] != '/') {
+ /*
+ * Generate the absolute path. This doesn't do everything it
+ * should, e.g. if filename is "./out/whatever" it doesn't crunch
+ * the leading "./" out, but it'll do.
+ */
+ if (getcwd(absoluteFile, kBufLen) == NULL) {
+ LOGE("Can't get CWD while opening raw/***.bc file\n");
+ return NULL;
+ }
+ // TODO(srhines): strncat() is a bit dangerous
+ strncat(absoluteFile, "/", kBufLen);
+ }
+ strncat(absoluteFile, fileName, kBufLen);
+
+ if (subFileName != NULL) {
+ strncat(absoluteFile, "/", kBufLen);
+ strncat(absoluteFile, subFileName, kBufLen);
+ }
+
+ /* Turn the path into a flat filename by replacing
+ * any slashes after the first one with '@' characters.
+ */
+ cp = absoluteFile + 1;
+ while (*cp != '\0') {
+ if (*cp == '/') {
+ *cp = '@';
+ }
+ cp++;
+ }
+
+ /* Build the name of the cache directory.
+ */
+ dataRoot = getenv("ANDROID_DATA");
+ if (dataRoot == NULL)
+ dataRoot = "/data";
+ snprintf(nameBuf, kBufLen, "%s/%s", dataRoot, kCachePath);
+
+ /* Tack on the file name for the actual cache file path.
+ */
+ strncat(nameBuf, absoluteFile, kBufLen);
+
+ LOGV("Cache file for '%s' '%s' is '%s'\n", fileName, subFileName, nameBuf);
+ return strdup(nameBuf);
+}
+
+/*
+ * Read the oBCC header, verify it, then read the dependent section
+ * and verify that data as well.
+ *
+ * On successful return, the file will be seeked immediately past the
+ * oBCC header.
+ */
+bool Compiler::checkHeaderAndDependencies(int fd,
+ uint32_t sourceWhen,
+ uint32_t rslibWhen,
+ uint32_t libRSWhen,
+ uint32_t libbccWhen) {
+ ssize_t actual;
+ oBCCHeader optHdr;
+ uint32_t val;
+ uint8_t const *magic, *magicVer;
+
+ /*
+ * Start at the start. The "bcc" header, when present, will always be
+ * the first thing in the file.
+ */
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ LOGE("bcc: failed to seek to start of file: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Read and do trivial verification on the bcc header. The header is
+ * always in host byte order.
+ */
+ actual = read(fd, &optHdr, sizeof(optHdr));
+ if (actual < 0) {
+ LOGE("bcc: failed reading bcc header: %s\n", strerror(errno));
+ goto bail;
+ } else if (actual != sizeof(optHdr)) {
+ LOGE("bcc: failed reading bcc header (got %d of %zd)\n",
+ (int) actual, sizeof(optHdr));
+ goto bail;
+ }
+
+ magic = optHdr.magic;
+ if (memcmp(magic, OBCC_MAGIC, 4) != 0) {
+ /* not an oBCC file, or previous attempt was interrupted */
+ LOGD("bcc: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
+ magic[0], magic[1], magic[2], magic[3]);
+ goto bail;
+ }
+
+ magicVer = optHdr.magicVersion;
+ if (memcmp(magic+4, OBCC_MAGIC_VERS, 4) != 0) {
+ LOGW("bcc: stale oBCC version (0x%02x %02x %02x %02x)\n",
+ magicVer[0], magicVer[1], magicVer[2], magicVer[3]);
+ goto bail;
+ }
+
+ /*
+ * Do the header flags match up with what we want?
+ *
+ * This is useful because it allows us to automatically regenerate
+ * a file when settings change (e.g. verification is now mandatory),
+ * but can cause difficulties if the thing we depend upon
+ * were handled differently than the current options specify.
+ *
+ * So, for now, we essentially ignore "expectVerify" and "expectOpt"
+ * by limiting the match mask.
+ *
+ * The only thing we really can't handle is incorrect byte-ordering.
+ */
+
+ val = optHdr.sourceWhen;
+ if (val && (val != sourceWhen)) {
+ LOGI("bcc: source file mod time mismatch (%08x vs %08x)\n",
+ val, sourceWhen);
+ goto bail;
+ }
+ val = optHdr.rslibWhen;
+ if (val && (val != rslibWhen)) {
+ LOGI("bcc: rslib file mod time mismatch (%08x vs %08x)\n",
+ val, rslibWhen);
+ goto bail;
+ }
+ val = optHdr.libRSWhen;
+ if (val && (val != libRSWhen)) {
+ LOGI("bcc: libRS file mod time mismatch (%08x vs %08x)\n",
+ val, libRSWhen);
+ goto bail;
+ }
+ val = optHdr.libbccWhen;
+ if (val && (val != libbccWhen)) {
+ LOGI("bcc: libbcc file mod time mismatch (%08x vs %08x)\n",
+ val, libbccWhen);
+ goto bail;
+ }
+
+ return true;
+
+bail:
+ return false;
+}
+
+} // namespace bcc