[Modern Media Controls] Toggle playback when clicking on the video on macOS
[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("--loop           Keep recompiling forever. Useful when attaching a profiler.\n");
66     printf("--fast-isel      Enable the \"fast\" instruction selector.\n");
67     printf("--help           Print this message.\n");
68     printf("\n");
69     printf("Unless you specify one of --verbose, --timing, or --disassemble, you will\n");
70     printf("not see any output.\n");
71     exit(1);
72 }
73
74 static double currentTime()
75 {
76     struct timeval now;
77     gettimeofday(&now, 0);
78     return now.tv_sec + now.tv_usec / 1000000.0;
79 }
80
81 struct MemorySection {
82     uint8_t *start;
83     size_t size;
84     struct MemorySection *next;
85 };
86
87 static struct MemorySection* sectionHead;
88
89 static size_t roundUpSize(size_t size)
90 {
91     size_t pageSize = getpagesize();
92     
93     return (size + pageSize - 1) & ~pageSize;
94 }
95
96 static uint8_t *mmAllocateCodeSection(
97     void *opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID)
98 {
99     uint8_t *start = mmap(
100         0,  roundUpSize(size),
101         PROT_WRITE | PROT_READ | PROT_EXEC,
102         MAP_ANON | MAP_PRIVATE, -1, 0);
103     if (start == (uint8_t*)-1) {
104         fprintf(stderr, "Unable to allocate %" PRIuPTR " bytes of executable memory.\n", size);
105         exit(1);
106     }
107     
108     struct MemorySection *section = malloc(sizeof(struct MemorySection));
109     section->start = start;
110     section->size = size;
111     section->next = sectionHead;
112     sectionHead = section;
113
114     return start;
115 }
116
117 static uint8_t *mmAllocateDataSection(
118     void *opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID,
119     LLVMBool isReadOnly)
120 {
121     return mmAllocateCodeSection(opaqueState, size, alignment, sectionID);
122 }
123
124 static LLVMBool mmApplyPermissions(void *opaque, char **message)
125 {
126     return 0;
127 }
128
129 static void mmDestroy(void *opaque)
130 {
131 }
132
133 static const char *symbolLookupCallback(
134     void *opaque, uint64_t referenceValue, uint64_t *referenceType, uint64_t referencePC,
135     const char **referenceName)
136 {
137     static char symbolString[20];
138     
139     switch (*referenceType) {
140     case LLVMDisassembler_ReferenceType_InOut_None:
141         return 0;
142     case LLVMDisassembler_ReferenceType_In_Branch:
143         *referenceName = 0;
144         *referenceType = LLVMDisassembler_ReferenceType_InOut_None;
145         snprintf(
146             symbolString, sizeof(symbolString), "0x%lx",
147             (unsigned long)referenceValue);
148         return symbolString;
149     default:
150         fprintf(stderr, "Unexpected reference type!\n");
151         exit(1);
152         return 0;
153     }
154 }
155
156 void webkit_osr_exit()
157 {
158     abort();
159 }
160
161 int main(int c, char **v)
162 {
163     LLVMContextRef *contexts;
164     LLVMModuleRef *modules;
165     char *error;
166     const char *mode = "opt";
167     const char **filenames;
168     unsigned numFiles;
169     unsigned i;
170     bool moreOptions;
171     static int verboseFlag = 0;
172     static int timingFlag = 0;
173     static int disassembleFlag = 0;
174     bool manyContexts = true;
175     bool loop = false;
176     double beforeAll;
177     bool fastIsel = false;
178     int jitOptLevel = 2;
179     struct MemorySection *section;
180     
181     if (c == 1)
182         usage();
183     
184     moreOptions = true;
185     while (moreOptions) {
186         static struct option longOptions[] = {
187             {"verbose", no_argument, &verboseFlag, 1},
188             {"timing", no_argument, &timingFlag, 1},
189             {"disassemble", no_argument, &disassembleFlag, 1},
190             {"mode", required_argument, 0, 0},
191             {"contexts", required_argument, 0, 0},
192             {"loop", no_argument, 0, 0},
193             {"fast-isel", no_argument, 0, 0},
194             {"jit-opt", required_argument, 0, 0},
195             {"help", no_argument, 0, 0}
196         };
197         
198         int optionIndex;
199         int optionValue;
200         
201         optionValue = getopt_long(c, v, "", longOptions, &optionIndex);
202         
203         switch (optionValue) {
204         case -1:
205             moreOptions = false;
206             break;
207             
208         case 0: {
209             const char* thisOption = longOptions[optionIndex].name;
210             if (!strcmp(thisOption, "help"))
211                 usage();
212             if (!strcmp(thisOption, "loop"))
213                 loop = true;
214             if (!strcmp(thisOption, "fast-isel"))
215                 fastIsel = true;
216             if (!strcmp(thisOption, "contexts")) {
217                 if (!strcasecmp(optarg, "one"))
218                     manyContexts = false;
219                 else if (!strcasecmp(optarg, "many"))
220                     manyContexts = true;
221                 else {
222                     fprintf(stderr, "Invalid argument for --contexts.\n");
223                     exit(1);
224                 }
225                 break;
226             }
227             if (!strcmp(thisOption, "jit-opt")) {
228                 if (sscanf(optarg, "%d", &jitOptLevel) != 1) {
229                     fprintf(stderr, "Invalid argument for --jit-opt.\n");
230                     exit(1);
231                 }
232                 break;
233             }
234             if (!strcmp(thisOption, "mode")) {
235                 mode = strdup(optarg);
236                 break;
237             }
238             break;
239         }
240             
241         case '?':
242             exit(0);
243             break;
244             
245         default:
246             printf("optionValue = %d\n", optionValue);
247             abort();
248             break;
249         }
250     }
251     
252     LLVMLinkInMCJIT();
253     LLVMInitializeNativeTarget();
254     LLVMInitializeX86AsmPrinter();
255     LLVMInitializeX86Disassembler();
256
257     filenames = (const char **)(v + optind);
258     numFiles = c - optind;
259     
260     if (!numFiles)
261         return 0;
262     
263     do {
264         contexts = malloc(sizeof(LLVMContextRef) * numFiles);
265         modules = malloc(sizeof(LLVMModuleRef) * numFiles);
266     
267         if (manyContexts) {
268             for (i = 0; i < numFiles; ++i)
269                 contexts[i] = LLVMContextCreate();
270         } else {
271             LLVMContextRef context = LLVMContextCreate();
272             for (i = 0; i < numFiles; ++i)
273                 contexts[i] = context;
274         }
275     
276         for (i = 0; i < numFiles; ++i) {
277             LLVMMemoryBufferRef buffer;
278             const char* filename = filenames[i];
279         
280             if (LLVMCreateMemoryBufferWithContentsOfFile(filename, &buffer, &error)) {
281                 fprintf(stderr, "Error reading file %s: %s\n", filename, error);
282                 exit(1);
283             }
284         
285             if (LLVMParseBitcodeInContext(contexts[i], buffer, modules + i, &error)) {
286                 fprintf(stderr, "Error parsing file %s: %s\n", filename, error);
287                 exit(1);
288             }
289         
290             LLVMDisposeMemoryBuffer(buffer);
291         
292             if (verboseFlag) {
293                 printf("Module #%u (%s) after parsing:\n", i, filename);
294                 LLVMDumpModule(modules[i]);
295             }
296         }
297
298         if (verboseFlag)
299             printf("Generating code for modules...\n");
300     
301         if (timingFlag)
302             beforeAll = currentTime();
303         for (i = 0; i < numFiles; ++i) {
304             LLVMModuleRef module;
305             LLVMExecutionEngineRef engine;
306             struct LLVMMCJITCompilerOptions options;
307             LLVMValueRef value;
308             LLVMPassManagerRef functionPasses = 0;
309             LLVMPassManagerRef modulePasses = 0;
310         
311             double before;
312         
313             if (timingFlag)
314                 before = currentTime();
315         
316             module = modules[i];
317
318             LLVMInitializeMCJITCompilerOptions(&options, sizeof(options));
319             options.OptLevel = jitOptLevel;
320             options.EnableFastISel = fastIsel;
321             options.MCJMM = LLVMCreateSimpleMCJITMemoryManager(
322                 0, mmAllocateCodeSection, mmAllocateDataSection, mmApplyPermissions, mmDestroy);
323     
324             if (LLVMCreateMCJITCompilerForModule(&engine, module, &options, sizeof(options), &error)) {
325                 fprintf(stderr, "Error building MCJIT: %s\n", error);
326                 exit(1);
327             }
328     
329             if (!strcasecmp(mode, "simple")) {
330                 modulePasses = LLVMCreatePassManager();
331                 LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), modulePasses);
332                 LLVMAddPromoteMemoryToRegisterPass(modulePasses);
333                 LLVMAddConstantPropagationPass(modulePasses);
334                 LLVMAddInstructionCombiningPass(modulePasses);
335                 LLVMAddBasicAliasAnalysisPass(modulePasses);
336                 LLVMAddTypeBasedAliasAnalysisPass(modulePasses);
337                 LLVMAddGVNPass(modulePasses);
338                 LLVMAddCFGSimplificationPass(modulePasses);
339                 LLVMRunPassManager(modulePasses, module);
340             } else if (!strcasecmp(mode, "opt")) {
341                 LLVMPassManagerBuilderRef passBuilder;
342
343                 passBuilder = LLVMPassManagerBuilderCreate();
344                 LLVMPassManagerBuilderSetOptLevel(passBuilder, 2);
345                 LLVMPassManagerBuilderSetSizeLevel(passBuilder, 0);
346         
347                 functionPasses = LLVMCreateFunctionPassManagerForModule(module);
348                 modulePasses = LLVMCreatePassManager();
349         
350                 LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), modulePasses);
351         
352                 LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder, functionPasses);
353                 LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses);
354         
355                 LLVMPassManagerBuilderDispose(passBuilder);
356         
357                 LLVMInitializeFunctionPassManager(functionPasses);
358                 for (value = LLVMGetFirstFunction(module); value; value = LLVMGetNextFunction(value))
359                     LLVMRunFunctionPassManager(functionPasses, value);
360                 LLVMFinalizeFunctionPassManager(functionPasses);
361         
362                 LLVMRunPassManager(modulePasses, module);
363             } else {
364                 fprintf(stderr, "Bad optimization mode: %s.\n", mode);
365                 fprintf(stderr, "Valid modes are: \"simple\" or \"opt\".\n");
366                 exit(1);
367             }
368
369             if (verboseFlag) {
370                 printf("Module #%d (%s) after optimization:\n", i, filenames[i]);
371                 LLVMDumpModule(module);
372             }
373     
374             for (value = LLVMGetFirstFunction(module); value; value = LLVMGetNextFunction(value)) {
375                 if (LLVMIsDeclaration(value))
376                     continue;
377                 LLVMGetPointerToGlobal(engine, value);
378             }
379
380             if (functionPasses)
381                 LLVMDisposePassManager(functionPasses);
382             if (modulePasses)
383                 LLVMDisposePassManager(modulePasses);
384     
385             LLVMDisposeExecutionEngine(engine);
386         
387             if (timingFlag) {
388                 double after = currentTime();
389                 printf("Module #%d (%s) took %lf ms.\n", i, filenames[i], (after - before) * 1000);
390             }
391         }
392
393         if (manyContexts) {
394             for (i = 0; i < numFiles; ++i)
395                 LLVMContextDispose(contexts[i]);
396         } else
397             LLVMContextDispose(contexts[0]);
398         
399         if (timingFlag) {
400             double after = currentTime();
401             printf("Compilation took a total of %lf ms.\n", (after - beforeAll) * 1000);
402         }
403     
404         if (disassembleFlag) {
405             LLVMDisasmContextRef disassembler;
406         
407             disassembler = LLVMCreateDisasm("x86_64-apple-darwin", 0, 0, 0, symbolLookupCallback);
408             if (!disassembler) {
409                 fprintf(stderr, "Error building disassembler.\n");
410                 exit(1);
411             }
412     
413             for (section = sectionHead; section; section = section->next) {
414                 printf("Disassembly for section %p:\n", section);
415         
416                 char pcString[20];
417                 char instructionString[1000];
418                 uint8_t *pc;
419                 uint8_t *end;
420         
421                 pc = section->start;
422                 end = pc + section->size;
423         
424                 while (pc < end) {
425                     snprintf(
426                         pcString, sizeof(pcString), "0x%lx",
427                         (unsigned long)(uintptr_t)pc);
428             
429                     size_t instructionSize = LLVMDisasmInstruction(
430                         disassembler, pc, end - pc, (uintptr_t)pc,
431                         instructionString, sizeof(instructionString));
432             
433                     if (!instructionSize)
434                         snprintf(instructionString, sizeof(instructionString), ".byte 0x%02x", *pc++);
435                     else
436                         pc += instructionSize;
437             
438                     printf("    %16s: %s\n", pcString, instructionString);
439                 }
440             }
441         }
442         
443         for (section = sectionHead; section;) {
444             struct MemorySection* nextSection = section->next;
445             
446             munmap(section->start, roundUpSize(section->size));
447             free(section);
448             
449             section = nextSection;
450         }
451         sectionHead = 0;
452         
453     } while (loop);
454     
455     return 0;
456 }
457