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 56using namespace llvm; 57 58//Command line options 59 60static cl::opt<std::string> 61InputFilename(cl::Positional, cl::desc("<input brainf>")); 62 63static cl::opt<std::string> 64OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); 65 66static cl::opt<bool> 67ArrayBoundsChecking("abc", cl::desc("Enable array bounds checking")); 68 69static cl::opt<bool> 70JIT("jit", cl::desc("Run program Just-In-Time")); 71 72//Add main function so can be fully compiled 73void addMainFunction(Module *mod) { 74 //define i32 @main(i32 %argc, i8 **%argv) 75 FunctionType *main_func_fty = FunctionType::get( 76 Type::getInt32Ty(mod->getContext()), 77 {Type::getInt32Ty(mod->getContext()), 78 Type::getInt8Ty(mod->getContext())->getPointerTo()->getPointerTo()}, 79 false); 80 Function *main_func = 81 Function::Create(main_func_fty, Function::ExternalLinkage, "main", 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(mod->getContext(), "main.0", main_func); 93 94 //call void @brainf() 95 { 96 CallInst *brainf_call = CallInst::Create(mod->getFunction("brainf"), 97 "", bb); 98 brainf_call->setTailCall(false); 99 } 100 101 //ret i32 0 102 ReturnInst::Create(mod->getContext(), 103 ConstantInt::get(mod->getContext(), APInt(32, 0)), bb); 104} 105 106int main(int argc, char **argv) { 107 cl::ParseCommandLineOptions(argc, argv, " 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(in, 65536, cf, Context)); // 64 KiB 146 if (in != &std::cin) 147 delete in; 148 addMainFunction(Mod.get()); 149 150 //Verify generated code 151 if (verifyModule(*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("brainf"); 170 GenericValue gv = ee->runFunction(brainf_func, 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(*Mod, *out); 177 } 178 179 //Clean up 180 if (out != &outs()) 181 delete out; 182 183 llvm_shutdown(); 184 185 return 0; 186} 187