//===-- BrainFDriver.cpp - BrainF compiler driver -------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This program converts the BrainF language into LLVM assembly, // which it can then run using the JIT or output as BitCode. // // This implementation has a tape of 65536 bytes, // with the head starting in the middle. // Range checking is off by default, so be careful. // It can be enabled with -abc. // // Use: // ./BrainF -jit prog.bf #Run program now // ./BrainF -jit -abc prog.bf #Run program now safely // ./BrainF prog.bf #Write as BitCode // // lli prog.bf.bc #Run generated BitCode // //===----------------------------------------------------------------------===// #include "BrainF.h" #include "llvm/ADT/APInt.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Value.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstdlib> #include <fstream> #include <iostream> #include <memory> #include <string> #include <system_error> #include <vector> using namespace llvm; //Command line options static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input brainf>")); static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); static cl::opt<bool> ArrayBoundsChecking("abc", cl::desc("Enable array bounds checking")); static cl::opt<bool> JIT("jit", cl::desc("Run program Just-In-Time")); //Add main function so can be fully compiled void addMainFunction(Module *mod) { //define i32 @main(i32 %argc, i8 **%argv) Function *main_func = cast<Function>(mod-> getOrInsertFunction("main", IntegerType::getInt32Ty(mod->getContext()), IntegerType::getInt32Ty(mod->getContext()), PointerType::getUnqual(PointerType::getUnqual( IntegerType::getInt8Ty(mod->getContext()))))); { Function::arg_iterator args = main_func->arg_begin(); Value *arg_0 = &*args++; arg_0->setName("argc"); Value *arg_1 = &*args++; arg_1->setName("argv"); } //main.0: BasicBlock *bb = BasicBlock::Create(mod->getContext(), "main.0", main_func); //call void @brainf() { CallInst *brainf_call = CallInst::Create(mod->getFunction("brainf"), "", bb); brainf_call->setTailCall(false); } //ret i32 0 ReturnInst::Create(mod->getContext(), ConstantInt::get(mod->getContext(), APInt(32, 0)), bb); } int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, " BrainF compiler\n"); LLVMContext Context; if (InputFilename == "") { errs() << "Error: You must specify the filename of the program to " "be compiled. Use --help to see the options.\n"; abort(); } //Get the output stream raw_ostream *out = &outs(); if (!JIT) { if (OutputFilename == "") { std::string base = InputFilename; if (InputFilename == "-") { base = "a"; } // Use default filename. OutputFilename = base+".bc"; } if (OutputFilename != "-") { std::error_code EC; out = new raw_fd_ostream(OutputFilename, EC, sys::fs::F_None); } } //Get the input stream std::istream *in = &std::cin; if (InputFilename != "-") in = new std::ifstream(InputFilename.c_str()); //Gather the compile flags BrainF::CompileFlags cf = BrainF::flag_off; if (ArrayBoundsChecking) cf = BrainF::CompileFlags(cf | BrainF::flag_arraybounds); //Read the BrainF program BrainF bf; std::unique_ptr<Module> Mod(bf.parse(in, 65536, cf, Context)); // 64 KiB if (in != &std::cin) delete in; addMainFunction(Mod.get()); //Verify generated code if (verifyModule(*Mod)) { errs() << "Error: module failed verification. This shouldn't happen.\n"; abort(); } //Write it out if (JIT) { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); outs() << "------- Running JIT -------\n"; Module &M = *Mod; ExecutionEngine *ee = EngineBuilder(std::move(Mod)).create(); if (!ee) { errs() << "Error: execution engine creation failed.\n"; abort(); } std::vector<GenericValue> args; Function *brainf_func = M.getFunction("brainf"); GenericValue gv = ee->runFunction(brainf_func, args); // Genereated code calls putchar, and output is not guaranteed without fflush. // The better place for fflush(stdout) call would be the generated code, but it // is unmanageable because stdout linkage name depends on stdlib implementation. fflush(stdout); } else { WriteBitcodeToFile(*Mod, *out); } //Clean up if (out != &outs()) delete out; llvm_shutdown(); return 0; }