| /* |
| * Copyright (C) 2013 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * Simple tool that takes some LLVM bitcode as input and "JITs" it, in the |
| * same way that the FTL would use LLVM to JIT the IR that it generates. |
| * This is meant for use as a reduction when communicating to LLVMers |
| * about bugs, and for quick "what-if" testing to see how our optimization |
| * pipeline performs. Because of its use as a reduction, this tool is |
| * intentionally standalone and it would be great if it continues to fit |
| * in one file. |
| */ |
| |
| #include <getopt.h> |
| #include <inttypes.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include <llvm-c/Analysis.h> |
| #include <llvm-c/BitReader.h> |
| #include <llvm-c/Core.h> |
| #include <llvm-c/Disassembler.h> |
| #include <llvm-c/ExecutionEngine.h> |
| #include <llvm-c/Target.h> |
| #include <llvm-c/Transforms/PassManagerBuilder.h> |
| #include <llvm-c/Transforms/Scalar.h> |
| |
| static void usage() |
| { |
| printf("Usage: ReducedFTL <file1> [<file2> ...]\n"); |
| printf("\n"); |
| printf("Options:\n"); |
| printf("--verbose Display more information, including module dumps.\n"); |
| printf("--timing Measure the time it takes to compile.\n"); |
| printf("--disassemble Disassemble all of the generated code at the end.\n"); |
| printf("--mode <mode> Set the optimization mode (either \"simple\" or \"opt\").\n"); |
| printf("--contexts <arg> Set the number of contexts (either \"one\" or \"many\").\n"); |
| printf("--loop Keep recompiling forever. Useful when attaching a profiler.\n"); |
| printf("--fast-isel Enable the \"fast\" instruction selector.\n"); |
| printf("--help Print this message.\n"); |
| printf("\n"); |
| printf("Unless you specify one of --verbose, --timing, or --disassemble, you will\n"); |
| printf("not see any output.\n"); |
| exit(1); |
| } |
| |
| static double currentTime() |
| { |
| struct timeval now; |
| gettimeofday(&now, 0); |
| return now.tv_sec + now.tv_usec / 1000000.0; |
| } |
| |
| struct MemorySection { |
| uint8_t *start; |
| size_t size; |
| struct MemorySection *next; |
| }; |
| |
| static struct MemorySection* sectionHead; |
| |
| static size_t roundUpSize(size_t size) |
| { |
| size_t pageSize = getpagesize(); |
| |
| return (size + pageSize - 1) & ~pageSize; |
| } |
| |
| static uint8_t *mmAllocateCodeSection( |
| void *opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID) |
| { |
| uint8_t *start = mmap( |
| 0, roundUpSize(size), |
| PROT_WRITE | PROT_READ | PROT_EXEC, |
| MAP_ANON | MAP_PRIVATE, -1, 0); |
| if (start == (uint8_t*)-1) { |
| fprintf(stderr, "Unable to allocate %" PRIuPTR " bytes of executable memory.\n", size); |
| exit(1); |
| } |
| |
| struct MemorySection *section = malloc(sizeof(struct MemorySection)); |
| section->start = start; |
| section->size = size; |
| section->next = sectionHead; |
| sectionHead = section; |
| |
| return start; |
| } |
| |
| static uint8_t *mmAllocateDataSection( |
| void *opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID, |
| LLVMBool isReadOnly) |
| { |
| return mmAllocateCodeSection(opaqueState, size, alignment, sectionID); |
| } |
| |
| static LLVMBool mmApplyPermissions(void *opaque, char **message) |
| { |
| return 0; |
| } |
| |
| static void mmDestroy(void *opaque) |
| { |
| } |
| |
| static const char *symbolLookupCallback( |
| void *opaque, uint64_t referenceValue, uint64_t *referenceType, uint64_t referencePC, |
| const char **referenceName) |
| { |
| static char symbolString[20]; |
| |
| switch (*referenceType) { |
| case LLVMDisassembler_ReferenceType_InOut_None: |
| return 0; |
| case LLVMDisassembler_ReferenceType_In_Branch: |
| *referenceName = 0; |
| *referenceType = LLVMDisassembler_ReferenceType_InOut_None; |
| snprintf( |
| symbolString, sizeof(symbolString), "0x%lx", |
| (unsigned long)referenceValue); |
| return symbolString; |
| default: |
| fprintf(stderr, "Unexpected reference type!\n"); |
| exit(1); |
| return 0; |
| } |
| } |
| |
| void webkit_osr_exit() |
| { |
| abort(); |
| } |
| |
| int main(int c, char **v) |
| { |
| LLVMContextRef *contexts; |
| LLVMModuleRef *modules; |
| char *error; |
| const char *mode = "opt"; |
| const char **filenames; |
| unsigned numFiles; |
| unsigned i; |
| bool moreOptions; |
| static int verboseFlag = 0; |
| static int timingFlag = 0; |
| static int disassembleFlag = 0; |
| bool manyContexts = true; |
| bool loop = false; |
| double beforeAll; |
| bool fastIsel = false; |
| int jitOptLevel = 2; |
| struct MemorySection *section; |
| |
| if (c == 1) |
| usage(); |
| |
| moreOptions = true; |
| while (moreOptions) { |
| static struct option longOptions[] = { |
| {"verbose", no_argument, &verboseFlag, 1}, |
| {"timing", no_argument, &timingFlag, 1}, |
| {"disassemble", no_argument, &disassembleFlag, 1}, |
| {"mode", required_argument, 0, 0}, |
| {"contexts", required_argument, 0, 0}, |
| {"loop", no_argument, 0, 0}, |
| {"fast-isel", no_argument, 0, 0}, |
| {"jit-opt", required_argument, 0, 0}, |
| {"help", no_argument, 0, 0} |
| }; |
| |
| int optionIndex; |
| int optionValue; |
| |
| optionValue = getopt_long(c, v, "", longOptions, &optionIndex); |
| |
| switch (optionValue) { |
| case -1: |
| moreOptions = false; |
| break; |
| |
| case 0: { |
| const char* thisOption = longOptions[optionIndex].name; |
| if (!strcmp(thisOption, "help")) |
| usage(); |
| if (!strcmp(thisOption, "loop")) |
| loop = true; |
| if (!strcmp(thisOption, "fast-isel")) |
| fastIsel = true; |
| if (!strcmp(thisOption, "contexts")) { |
| if (!strcasecmp(optarg, "one")) |
| manyContexts = false; |
| else if (!strcasecmp(optarg, "many")) |
| manyContexts = true; |
| else { |
| fprintf(stderr, "Invalid argument for --contexts.\n"); |
| exit(1); |
| } |
| break; |
| } |
| if (!strcmp(thisOption, "jit-opt")) { |
| if (sscanf(optarg, "%d", &jitOptLevel) != 1) { |
| fprintf(stderr, "Invalid argument for --jit-opt.\n"); |
| exit(1); |
| } |
| break; |
| } |
| if (!strcmp(thisOption, "mode")) { |
| mode = strdup(optarg); |
| break; |
| } |
| break; |
| } |
| |
| case '?': |
| exit(0); |
| break; |
| |
| default: |
| printf("optionValue = %d\n", optionValue); |
| abort(); |
| break; |
| } |
| } |
| |
| LLVMLinkInMCJIT(); |
| LLVMInitializeNativeTarget(); |
| LLVMInitializeX86AsmPrinter(); |
| LLVMInitializeX86Disassembler(); |
| |
| filenames = (const char **)(v + optind); |
| numFiles = c - optind; |
| |
| if (!numFiles) |
| return 0; |
| |
| do { |
| contexts = malloc(sizeof(LLVMContextRef) * numFiles); |
| modules = malloc(sizeof(LLVMModuleRef) * numFiles); |
| |
| if (manyContexts) { |
| for (i = 0; i < numFiles; ++i) |
| contexts[i] = LLVMContextCreate(); |
| } else { |
| LLVMContextRef context = LLVMContextCreate(); |
| for (i = 0; i < numFiles; ++i) |
| contexts[i] = context; |
| } |
| |
| for (i = 0; i < numFiles; ++i) { |
| LLVMMemoryBufferRef buffer; |
| const char* filename = filenames[i]; |
| |
| if (LLVMCreateMemoryBufferWithContentsOfFile(filename, &buffer, &error)) { |
| fprintf(stderr, "Error reading file %s: %s\n", filename, error); |
| exit(1); |
| } |
| |
| if (LLVMParseBitcodeInContext(contexts[i], buffer, modules + i, &error)) { |
| fprintf(stderr, "Error parsing file %s: %s\n", filename, error); |
| exit(1); |
| } |
| |
| LLVMDisposeMemoryBuffer(buffer); |
| |
| if (verboseFlag) { |
| printf("Module #%u (%s) after parsing:\n", i, filename); |
| LLVMDumpModule(modules[i]); |
| } |
| } |
| |
| if (verboseFlag) |
| printf("Generating code for modules...\n"); |
| |
| if (timingFlag) |
| beforeAll = currentTime(); |
| for (i = 0; i < numFiles; ++i) { |
| LLVMModuleRef module; |
| LLVMExecutionEngineRef engine; |
| struct LLVMMCJITCompilerOptions options; |
| LLVMValueRef value; |
| LLVMPassManagerRef functionPasses = 0; |
| LLVMPassManagerRef modulePasses = 0; |
| |
| double before; |
| |
| if (timingFlag) |
| before = currentTime(); |
| |
| module = modules[i]; |
| |
| LLVMInitializeMCJITCompilerOptions(&options, sizeof(options)); |
| options.OptLevel = jitOptLevel; |
| options.EnableFastISel = fastIsel; |
| options.MCJMM = LLVMCreateSimpleMCJITMemoryManager( |
| 0, mmAllocateCodeSection, mmAllocateDataSection, mmApplyPermissions, mmDestroy); |
| |
| if (LLVMCreateMCJITCompilerForModule(&engine, module, &options, sizeof(options), &error)) { |
| fprintf(stderr, "Error building MCJIT: %s\n", error); |
| exit(1); |
| } |
| |
| if (!strcasecmp(mode, "simple")) { |
| modulePasses = LLVMCreatePassManager(); |
| LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), modulePasses); |
| LLVMAddPromoteMemoryToRegisterPass(modulePasses); |
| LLVMAddConstantPropagationPass(modulePasses); |
| LLVMAddInstructionCombiningPass(modulePasses); |
| LLVMAddBasicAliasAnalysisPass(modulePasses); |
| LLVMAddTypeBasedAliasAnalysisPass(modulePasses); |
| LLVMAddGVNPass(modulePasses); |
| LLVMAddCFGSimplificationPass(modulePasses); |
| LLVMRunPassManager(modulePasses, module); |
| } else if (!strcasecmp(mode, "opt")) { |
| LLVMPassManagerBuilderRef passBuilder; |
| |
| passBuilder = LLVMPassManagerBuilderCreate(); |
| LLVMPassManagerBuilderSetOptLevel(passBuilder, 2); |
| LLVMPassManagerBuilderSetSizeLevel(passBuilder, 0); |
| |
| functionPasses = LLVMCreateFunctionPassManagerForModule(module); |
| modulePasses = LLVMCreatePassManager(); |
| |
| LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), modulePasses); |
| |
| LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder, functionPasses); |
| LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses); |
| |
| LLVMPassManagerBuilderDispose(passBuilder); |
| |
| LLVMInitializeFunctionPassManager(functionPasses); |
| for (value = LLVMGetFirstFunction(module); value; value = LLVMGetNextFunction(value)) |
| LLVMRunFunctionPassManager(functionPasses, value); |
| LLVMFinalizeFunctionPassManager(functionPasses); |
| |
| LLVMRunPassManager(modulePasses, module); |
| } else { |
| fprintf(stderr, "Bad optimization mode: %s.\n", mode); |
| fprintf(stderr, "Valid modes are: \"simple\" or \"opt\".\n"); |
| exit(1); |
| } |
| |
| if (verboseFlag) { |
| printf("Module #%d (%s) after optimization:\n", i, filenames[i]); |
| LLVMDumpModule(module); |
| } |
| |
| for (value = LLVMGetFirstFunction(module); value; value = LLVMGetNextFunction(value)) { |
| if (LLVMIsDeclaration(value)) |
| continue; |
| LLVMGetPointerToGlobal(engine, value); |
| } |
| |
| if (functionPasses) |
| LLVMDisposePassManager(functionPasses); |
| if (modulePasses) |
| LLVMDisposePassManager(modulePasses); |
| |
| LLVMDisposeExecutionEngine(engine); |
| |
| if (timingFlag) { |
| double after = currentTime(); |
| printf("Module #%d (%s) took %lf ms.\n", i, filenames[i], (after - before) * 1000); |
| } |
| } |
| |
| if (manyContexts) { |
| for (i = 0; i < numFiles; ++i) |
| LLVMContextDispose(contexts[i]); |
| } else |
| LLVMContextDispose(contexts[0]); |
| |
| if (timingFlag) { |
| double after = currentTime(); |
| printf("Compilation took a total of %lf ms.\n", (after - beforeAll) * 1000); |
| } |
| |
| if (disassembleFlag) { |
| LLVMDisasmContextRef disassembler; |
| |
| disassembler = LLVMCreateDisasm("x86_64-apple-darwin", 0, 0, 0, symbolLookupCallback); |
| if (!disassembler) { |
| fprintf(stderr, "Error building disassembler.\n"); |
| exit(1); |
| } |
| |
| for (section = sectionHead; section; section = section->next) { |
| printf("Disassembly for section %p:\n", section); |
| |
| char pcString[20]; |
| char instructionString[1000]; |
| uint8_t *pc; |
| uint8_t *end; |
| |
| pc = section->start; |
| end = pc + section->size; |
| |
| while (pc < end) { |
| snprintf( |
| pcString, sizeof(pcString), "0x%lx", |
| (unsigned long)(uintptr_t)pc); |
| |
| size_t instructionSize = LLVMDisasmInstruction( |
| disassembler, pc, end - pc, (uintptr_t)pc, |
| instructionString, sizeof(instructionString)); |
| |
| if (!instructionSize) |
| snprintf(instructionString, sizeof(instructionString), ".byte 0x%02x", *pc++); |
| else |
| pc += instructionSize; |
| |
| printf(" %16s: %s\n", pcString, instructionString); |
| } |
| } |
| } |
| |
| for (section = sectionHead; section;) { |
| struct MemorySection* nextSection = section->next; |
| |
| munmap(section->start, roundUpSize(section->size)); |
| free(section); |
| |
| section = nextSection; |
| } |
| sectionHead = 0; |
| |
| } while (loop); |
| |
| return 0; |
| } |
| |