1 | //===-- BrainFDriver.cpp - BrainF compiler driver -------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This program converts the BrainF language into LLVM assembly, |
10 | // which it can then run using the JIT or output as BitCode. |
11 | // |
12 | // This implementation has a tape of 65536 bytes, |
13 | // with the head starting in the middle. |
14 | // Range checking is off by default, so be careful. |
15 | // It can be enabled with -abc. |
16 | // |
17 | // Use: |
18 | // ./BrainF -jit prog.bf #Run program now |
19 | // ./BrainF -jit -abc prog.bf #Run program now safely |
20 | // ./BrainF prog.bf #Write as BitCode |
21 | // |
22 | // lli prog.bf.bc #Run generated BitCode |
23 | // |
24 | //===----------------------------------------------------------------------===// |
25 | |
26 | #include "BrainF.h" |
27 | #include "llvm/ADT/APInt.h" |
28 | #include "llvm/Bitcode/BitcodeWriter.h" |
29 | #include "llvm/ExecutionEngine/ExecutionEngine.h" |
30 | #include "llvm/ExecutionEngine/GenericValue.h" |
31 | #include "llvm/ExecutionEngine/MCJIT.h" |
32 | #include "llvm/IR/BasicBlock.h" |
33 | #include "llvm/IR/Constants.h" |
34 | #include "llvm/IR/DerivedTypes.h" |
35 | #include "llvm/IR/Function.h" |
36 | #include "llvm/IR/Instructions.h" |
37 | #include "llvm/IR/LLVMContext.h" |
38 | #include "llvm/IR/Module.h" |
39 | #include "llvm/IR/Value.h" |
40 | #include "llvm/IR/Verifier.h" |
41 | #include "llvm/Support/Casting.h" |
42 | #include "llvm/Support/CommandLine.h" |
43 | #include "llvm/Support/FileSystem.h" |
44 | #include "llvm/Support/ManagedStatic.h" |
45 | #include "llvm/Support/TargetSelect.h" |
46 | #include "llvm/Support/raw_ostream.h" |
47 | #include <algorithm> |
48 | #include <cstdlib> |
49 | #include <fstream> |
50 | #include <iostream> |
51 | #include <memory> |
52 | #include <string> |
53 | #include <system_error> |
54 | #include <vector> |
55 | |
56 | using namespace llvm; |
57 | |
58 | //Command line options |
59 | |
60 | static cl::opt<std::string> |
61 | InputFilename(cl::Positional, cl::desc("<input brainf>" )); |
62 | |
63 | static cl::opt<std::string> |
64 | OutputFilename("o" , cl::desc("Output filename" ), cl::value_desc("filename" )); |
65 | |
66 | static cl::opt<bool> |
67 | ArrayBoundsChecking("abc" , cl::desc("Enable array bounds checking" )); |
68 | |
69 | static cl::opt<bool> |
70 | JIT("jit" , cl::desc("Run program Just-In-Time" )); |
71 | |
72 | //Add main function so can be fully compiled |
73 | void addMainFunction(Module *mod) { |
74 | //define i32 @main(i32 %argc, i8 **%argv) |
75 | FunctionType *main_func_fty = FunctionType::get( |
76 | Result: Type::getInt32Ty(C&: mod->getContext()), |
77 | Params: {Type::getInt32Ty(C&: mod->getContext()), |
78 | Type::getInt8Ty(C&: mod->getContext())->getPointerTo()->getPointerTo()}, |
79 | isVarArg: false); |
80 | Function *main_func = |
81 | Function::Create(Ty: main_func_fty, Linkage: Function::ExternalLinkage, N: "main" , M: mod); |
82 | |
83 | { |
84 | Function::arg_iterator args = main_func->arg_begin(); |
85 | Value *arg_0 = &*args++; |
86 | arg_0->setName("argc" ); |
87 | Value *arg_1 = &*args++; |
88 | arg_1->setName("argv" ); |
89 | } |
90 | |
91 | //main.0: |
92 | BasicBlock *bb = BasicBlock::Create(Context&: mod->getContext(), Name: "main.0" , Parent: main_func); |
93 | |
94 | //call void @brainf() |
95 | { |
96 | CallInst *brainf_call = CallInst::Create(Func: mod->getFunction(Name: "brainf" ), |
97 | NameStr: "" , InsertAtEnd: bb); |
98 | brainf_call->setTailCall(false); |
99 | } |
100 | |
101 | //ret i32 0 |
102 | ReturnInst::Create(C&: mod->getContext(), |
103 | retVal: ConstantInt::get(Context&: mod->getContext(), V: APInt(32, 0)), InsertAtEnd: bb); |
104 | } |
105 | |
106 | int main(int argc, char **argv) { |
107 | cl::ParseCommandLineOptions(argc, argv, Overview: " BrainF compiler\n" ); |
108 | |
109 | LLVMContext Context; |
110 | |
111 | if (InputFilename == "" ) { |
112 | errs() << "Error: You must specify the filename of the program to " |
113 | "be compiled. Use --help to see the options.\n" ; |
114 | abort(); |
115 | } |
116 | |
117 | //Get the output stream |
118 | raw_ostream *out = &outs(); |
119 | if (!JIT) { |
120 | if (OutputFilename == "" ) { |
121 | std::string base = InputFilename; |
122 | if (InputFilename == "-" ) { base = "a" ; } |
123 | |
124 | // Use default filename. |
125 | OutputFilename = base+".bc" ; |
126 | } |
127 | if (OutputFilename != "-" ) { |
128 | std::error_code EC; |
129 | out = new raw_fd_ostream(OutputFilename, EC, sys::fs::OF_None); |
130 | } |
131 | } |
132 | |
133 | //Get the input stream |
134 | std::istream *in = &std::cin; |
135 | if (InputFilename != "-" ) |
136 | in = new std::ifstream(InputFilename.c_str()); |
137 | |
138 | //Gather the compile flags |
139 | BrainF::CompileFlags cf = BrainF::flag_off; |
140 | if (ArrayBoundsChecking) |
141 | cf = BrainF::CompileFlags(cf | BrainF::flag_arraybounds); |
142 | |
143 | //Read the BrainF program |
144 | BrainF bf; |
145 | std::unique_ptr<Module> Mod(bf.parse(in1: in, mem: 65536, cf, C&: Context)); // 64 KiB |
146 | if (in != &std::cin) |
147 | delete in; |
148 | addMainFunction(mod: Mod.get()); |
149 | |
150 | //Verify generated code |
151 | if (verifyModule(M: *Mod)) { |
152 | errs() << "Error: module failed verification. This shouldn't happen.\n" ; |
153 | abort(); |
154 | } |
155 | |
156 | //Write it out |
157 | if (JIT) { |
158 | InitializeNativeTarget(); |
159 | InitializeNativeTargetAsmPrinter(); |
160 | |
161 | outs() << "------- Running JIT -------\n" ; |
162 | Module &M = *Mod; |
163 | ExecutionEngine *ee = EngineBuilder(std::move(Mod)).create(); |
164 | if (!ee) { |
165 | errs() << "Error: execution engine creation failed.\n" ; |
166 | abort(); |
167 | } |
168 | std::vector<GenericValue> args; |
169 | Function *brainf_func = M.getFunction(Name: "brainf" ); |
170 | GenericValue gv = ee->runFunction(F: brainf_func, ArgValues: args); |
171 | // Genereated code calls putchar, and output is not guaranteed without fflush. |
172 | // The better place for fflush(stdout) call would be the generated code, but it |
173 | // is unmanageable because stdout linkage name depends on stdlib implementation. |
174 | fflush(stdout); |
175 | } else { |
176 | WriteBitcodeToFile(M: *Mod, Out&: *out); |
177 | } |
178 | |
179 | //Clean up |
180 | if (out != &outs()) |
181 | delete out; |
182 | |
183 | llvm_shutdown(); |
184 | |
185 | return 0; |
186 | } |
187 | |