fourthTier: We should have a reduced FTL LLVM pipeline tool in the repository
[WebKit-https.git] / Tools / ReducedFTL / ReducedFTL.c
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 /*
27  * Simple tool that takes some LLVM bitcode as input and "JITs" it, in the
28  * same way that the FTL would use LLVM to JIT the IR that it generates.
29  * This is meant for use as a reduction when communicating to LLVMers
30  * about bugs, and for quick "what-if" testing to see how our optimization
31  * pipeline performs. Because of its use as a reduction, this tool is
32  * intentionally standalone and it would be great if it continues to fit
33  * in one file.
34  */
35
36 #include <getopt.h>
37 #include <inttypes.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/mman.h>
43 #include <sys/time.h>
44 #include <unistd.h>
45
46 #include <llvm-c/Analysis.h>
47 #include <llvm-c/BitReader.h>
48 #include <llvm-c/Core.h>
49 #include <llvm-c/Disassembler.h>
50 #include <llvm-c/ExecutionEngine.h>
51 #include <llvm-c/Target.h>
52 #include <llvm-c/Transforms/PassManagerBuilder.h>
53 #include <llvm-c/Transforms/Scalar.h>
54
55 static void usage()
56 {
57     printf("Usage: ReducedFTL <file1> [<file2> ...]\n");
58     printf("\n");
59     printf("Options:\n");
60     printf("--verbose        Display more information, including module dumps.\n");
61     printf("--timing         Measure the time it takes to compile.\n");
62     printf("--disassemble    Disassemble all of the generated code at the end.\n");
63     printf("--mode <mode>    Set the optimization mode (either \"simple\" or \"opt\").\n");
64     printf("--contexts <arg> Set the number of contexts (either \"one\" or \"many\").\n");
65     printf("--help           Print this message.\n");
66     printf("\n");
67     printf("Unless you specify one of --verbose, --timing, or --disassemble, you will\n");
68     printf("not see any output.\n");
69     exit(1);
70 }
71
72 static double currentTime()
73 {
74     struct timeval now;
75     gettimeofday(&now, 0);
76     return now.tv_sec + now.tv_usec / 1000000.0;
77 }
78
79 struct MemorySection {
80     uint8_t *start;
81     size_t size;
82     struct MemorySection *next;
83 };
84
85 static struct MemorySection* sectionHead;
86
87 static uint8_t *mmAllocateCodeSection(
88     void *opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID)
89 {
90     size_t pageSize = getpagesize();
91     
92     uint8_t *start = mmap(
93         0, (size + pageSize - 1) & ~pageSize, 
94         PROT_WRITE | PROT_READ | PROT_EXEC,
95         MAP_ANON | MAP_PRIVATE, -1, 0);
96     if (start == (uint8_t*)-1) {
97         fprintf(stderr, "Unable to allocate %" PRIuPTR " bytes of executable memory.\n", size);
98         exit(1);
99     }
100     
101     struct MemorySection *section = malloc(sizeof(struct MemorySection));
102     section->start = start;
103     section->size = size;
104     section->next = sectionHead;
105     sectionHead = section;
106
107     return start;
108 }
109
110 static uint8_t *mmAllocateDataSection(
111     void *opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID,
112     LLVMBool isReadOnly)
113 {
114     return mmAllocateCodeSection(opaqueState, size, alignment, sectionID);
115 }
116
117 static LLVMBool mmApplyPermissions(void *opaque, char **message)
118 {
119     return 0;
120 }
121
122 static void mmDestroy(void *opaque)
123 {
124 }
125
126 static const char *symbolLookupCallback(
127     void *opaque, uint64_t referenceValue, uint64_t *referenceType, uint64_t referencePC,
128     const char **referenceName)
129 {
130     static char symbolString[20];
131     
132     switch (*referenceType) {
133     case LLVMDisassembler_ReferenceType_InOut_None:
134         return 0;
135     case LLVMDisassembler_ReferenceType_In_Branch:
136         *referenceName = 0;
137         *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
138         snprintf(
139             symbolString, sizeof(symbolString), "0x%lx",
140             (unsigned long)referenceValue);
141         return symbolString;
142     default:
143         fprintf(stderr, "Unexpected reference type!\n");
144         exit(1);
145         return 0;
146     }
147 }
148
149 int main(int c, char **v)
150 {
151     LLVMContextRef *contexts;
152     LLVMModuleRef *modules;
153     char *error;
154     const char *mode = "opt";
155     const char **filenames;
156     unsigned numFiles;
157     unsigned i;
158     bool moreOptions;
159     static int verboseFlag = 0;
160     static int timingFlag = 0;
161     static int disassembleFlag = 0;
162     bool manyContexts = true;
163     double beforeAll;
164     
165     if (c == 1)
166         usage();
167     
168     moreOptions = true;
169     while (moreOptions) {
170         static struct option longOptions[] = {
171             {"verbose", no_argument, &verboseFlag, 1},
172             {"timing", no_argument, &timingFlag, 1},
173             {"disassemble", no_argument, &disassembleFlag, 1},
174             {"mode", required_argument, 0, 0},
175             {"contexts", required_argument, 0, 0},
176             {"help", no_argument, 0, 0}
177         };
178         
179         int optionIndex;
180         int optionValue;
181         
182         optionValue = getopt_long(c, v, "", longOptions, &optionIndex);
183         
184         switch (optionValue) {
185         case -1:
186             moreOptions = false;
187             break;
188             
189         case 0: {
190             const char* thisOption = longOptions[optionIndex].name;
191             if (!strcmp(thisOption, "help"))
192                 usage();
193             if (!strcmp(thisOption, "contexts")) {
194                 if (!strcasecmp(optarg, "one"))
195                     manyContexts = false;
196                 else if (!strcasecmp(optarg, "many"))
197                     manyContexts = true;
198                 else {
199                     fprintf(stderr, "Invalid argument for --contexts.\n");
200                     exit(1);
201                 }
202                 break;
203             }
204             if (!strcmp(thisOption, "mode")) {
205                 mode = strdup(optarg);
206                 break;
207             }
208             break;
209         }
210             
211         case '?':
212             exit(0);
213             break;
214             
215         default:
216             printf("optionValue = %d\n", optionValue);
217             abort();
218             break;
219         }
220     }
221     
222     LLVMLinkInMCJIT();
223     LLVMInitializeNativeTarget();
224     LLVMInitializeX86AsmPrinter();
225     LLVMInitializeX86Disassembler();
226
227     filenames = (const char **)(v + optind);
228     numFiles = c - optind;
229     
230     contexts = malloc(sizeof(LLVMContextRef) * numFiles);
231     modules = malloc(sizeof(LLVMModuleRef) * numFiles);
232     
233     if (manyContexts) {
234         for (i = 0; i < numFiles; ++i)
235             contexts[i] = LLVMContextCreate();
236     } else {
237         LLVMContextRef context = LLVMContextCreate();
238         for (i = 0; i < numFiles; ++i)
239             contexts[i] = context;
240     }
241     
242     for (i = 0; i < numFiles; ++i) {
243         LLVMMemoryBufferRef buffer;
244         const char* filename = filenames[i];
245         
246         if (LLVMCreateMemoryBufferWithContentsOfFile(filename, &buffer, &error)) {
247             fprintf(stderr, "Error reading file %s: %s\n", filename, error);
248             exit(1);
249         }
250         
251         if (LLVMParseBitcodeInContext(contexts[i], buffer, modules + i, &error)) {
252             fprintf(stderr, "Error parsing file %s: %s\n", filename, error);
253             exit(1);
254         }
255         
256         LLVMDisposeMemoryBuffer(buffer);
257         
258         if (verboseFlag) {
259             printf("Module #%u (%s) after parsing:\n", i, filename);
260             LLVMDumpModule(modules[i]);
261         }
262     }
263
264     if (verboseFlag)
265         printf("Generating code for modules...\n");
266     
267     if (timingFlag)
268         beforeAll = currentTime();
269     for (i = 0; i < numFiles; ++i) {
270         LLVMModuleRef module;
271         LLVMExecutionEngineRef engine;
272         struct LLVMMCJITCompilerOptions options;
273         LLVMValueRef value;
274         LLVMPassManagerRef functionPasses = 0;
275         LLVMPassManagerRef modulePasses = 0;
276         
277         double before;
278         
279         if (timingFlag)
280             before = currentTime();
281         
282         module = modules[i];
283
284         LLVMInitializeMCJITCompilerOptions(&options, sizeof(options));
285         options.OptLevel = 2;
286         options.EnableFastISel = 0;
287         options.MCJMM = LLVMCreateSimpleMCJITMemoryManager(
288             0, mmAllocateCodeSection, mmAllocateDataSection, mmApplyPermissions, mmDestroy);
289     
290         if (LLVMCreateMCJITCompilerForModule(&engine, module, &options, sizeof(options), &error)) {
291             fprintf(stderr, "Error building MCJIT: %s\n", error);
292             exit(1);
293         }
294     
295         if (!strcasecmp(mode, "simple")) {
296             modulePasses = LLVMCreatePassManager();
297             LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), modulePasses);
298             LLVMAddConstantPropagationPass(modulePasses);
299             LLVMAddInstructionCombiningPass(modulePasses);
300             LLVMAddPromoteMemoryToRegisterPass(modulePasses);
301             LLVMAddBasicAliasAnalysisPass(modulePasses);
302             LLVMAddTypeBasedAliasAnalysisPass(modulePasses);
303             LLVMAddGVNPass(modulePasses);
304             LLVMAddCFGSimplificationPass(modulePasses);
305             LLVMRunPassManager(modulePasses, module);
306         } else if (!strcasecmp(mode, "opt")) {
307             LLVMPassManagerBuilderRef passBuilder;
308
309             passBuilder = LLVMPassManagerBuilderCreate();
310             LLVMPassManagerBuilderSetOptLevel(passBuilder, 2);
311             LLVMPassManagerBuilderSetSizeLevel(passBuilder, 0);
312         
313             functionPasses = LLVMCreateFunctionPassManagerForModule(module);
314             modulePasses = LLVMCreatePassManager();
315         
316             LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), modulePasses);
317         
318             LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder, functionPasses);
319             LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses);
320         
321             LLVMPassManagerBuilderDispose(passBuilder);
322         
323             LLVMInitializeFunctionPassManager(functionPasses);
324             for (value = LLVMGetFirstFunction(module); value; value = LLVMGetNextFunction(value))
325                 LLVMRunFunctionPassManager(functionPasses, value);
326             LLVMFinalizeFunctionPassManager(functionPasses);
327         
328             LLVMRunPassManager(modulePasses, module);
329         } else {
330             fprintf(stderr, "Bad optimization mode: %s.\n", mode);
331             fprintf(stderr, "Valid modes are: \"simple\" or \"opt\".\n");
332             exit(1);
333         }
334
335         if (verboseFlag) {
336             printf("Module #%d (%s) after optimization:\n", i, filenames[i]);
337             LLVMDumpModule(module);
338         }
339     
340         for (value = LLVMGetFirstFunction(module); value; value = LLVMGetNextFunction(value)) {
341             if (LLVMIsDeclaration(value))
342                 continue;
343             LLVMGetPointerToGlobal(engine, value);
344         }
345
346         if (functionPasses)
347             LLVMDisposePassManager(functionPasses);
348         if (modulePasses)
349             LLVMDisposePassManager(modulePasses);
350     
351         LLVMDisposeExecutionEngine(engine);
352         
353         if (timingFlag) {
354             double after = currentTime();
355             printf("Module #%d (%s) took %lf ms.\n", i, filenames[i], (after - before) * 1000);
356         }
357     }
358     if (timingFlag) {
359         double after = currentTime();
360         printf("Compilation took a total of %lf ms.\n", (after - beforeAll) * 1000);
361     }
362     
363     if (disassembleFlag) {
364         LLVMDisasmContextRef disassembler;
365         struct MemorySection *section;
366         
367         disassembler = LLVMCreateDisasm("x86_64-apple-darwin", 0, 0, 0, symbolLookupCallback);
368         if (!disassembler) {
369             fprintf(stderr, "Error building disassembler.\n");
370             exit(1);
371         }
372     
373         for (section = sectionHead; section; section = section->next) {
374             printf("Disassembly for section %p:\n", section);
375         
376             char pcString[20];
377             char instructionString[1000];
378             uint8_t *pc;
379             uint8_t *end;
380         
381             pc = section->start;
382             end = pc + section->size;
383         
384             while (pc < end) {
385                 snprintf(
386                     pcString, sizeof(pcString), "0x%lx",
387                     (unsigned long)(uintptr_t)pc);
388             
389                 size_t instructionSize = LLVMDisasmInstruction(
390                     disassembler, pc, end - pc, (uintptr_t)pc,
391                     instructionString, sizeof(instructionString));
392             
393                 if (!instructionSize)
394                     snprintf(instructionString, sizeof(instructionString), ".byte 0x%02x", *pc++);
395                 else
396                     pc += instructionSize;
397             
398                 printf("    %16s: %s\n", pcString, instructionString);
399             }
400         }
401     }
402     
403     return 0;
404 }
405