184 lines · cpp
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-exception6//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 now19// ./BrainF -jit -abc prog.bf #Run program now safely20// ./BrainF prog.bf #Write as BitCode21//22// lli prog.bf.bc #Run generated BitCode23//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/IR/BasicBlock.h"32#include "llvm/IR/Constants.h"33#include "llvm/IR/DerivedTypes.h"34#include "llvm/IR/Function.h"35#include "llvm/IR/Instructions.h"36#include "llvm/IR/LLVMContext.h"37#include "llvm/IR/Module.h"38#include "llvm/IR/Value.h"39#include "llvm/IR/Verifier.h"40#include "llvm/Support/CommandLine.h"41#include "llvm/Support/FileSystem.h"42#include "llvm/Support/ManagedStatic.h"43#include "llvm/Support/TargetSelect.h"44#include "llvm/Support/raw_ostream.h"45#include <cstdlib>46#include <fstream>47#include <iostream>48#include <memory>49#include <string>50#include <system_error>51#include <vector>52 53using namespace llvm;54 55//Command line options56 57static cl::opt<std::string>58InputFilename(cl::Positional, cl::desc("<input brainf>"));59 60static cl::opt<std::string>61OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"));62 63static cl::opt<bool>64ArrayBoundsChecking("abc", cl::desc("Enable array bounds checking"));65 66static cl::opt<bool>67JIT("jit", cl::desc("Run program Just-In-Time"));68 69//Add main function so can be fully compiled70void addMainFunction(Module *mod) {71 //define i32 @main(i32 %argc, i8 **%argv)72 FunctionType *main_func_fty = FunctionType::get(73 Type::getInt32Ty(mod->getContext()),74 {Type::getInt32Ty(mod->getContext()),75 PointerType::getUnqual(mod->getContext())},76 false);77 Function *main_func =78 Function::Create(main_func_fty, Function::ExternalLinkage, "main", mod);79 80 {81 Function::arg_iterator args = main_func->arg_begin();82 Value *arg_0 = &*args++;83 arg_0->setName("argc");84 Value *arg_1 = &*args++;85 arg_1->setName("argv");86 }87 88 //main.0:89 BasicBlock *bb = BasicBlock::Create(mod->getContext(), "main.0", main_func);90 91 //call void @brainf()92 {93 CallInst *brainf_call = CallInst::Create(mod->getFunction("brainf"),94 "", bb);95 brainf_call->setTailCall(false);96 }97 98 //ret i32 099 ReturnInst::Create(mod->getContext(),100 ConstantInt::get(mod->getContext(), APInt(32, 0)), bb);101}102 103int main(int argc, char **argv) {104 cl::ParseCommandLineOptions(argc, argv, " BrainF compiler\n");105 106 LLVMContext Context;107 108 if (InputFilename == "") {109 errs() << "Error: You must specify the filename of the program to "110 "be compiled. Use --help to see the options.\n";111 abort();112 }113 114 //Get the output stream115 raw_ostream *out = &outs();116 if (!JIT) {117 if (OutputFilename == "") {118 std::string base = InputFilename;119 if (InputFilename == "-") { base = "a"; }120 121 // Use default filename.122 OutputFilename = base+".bc";123 }124 if (OutputFilename != "-") {125 std::error_code EC;126 out = new raw_fd_ostream(OutputFilename, EC, sys::fs::OF_None);127 }128 }129 130 //Get the input stream131 std::istream *in = &std::cin;132 if (InputFilename != "-")133 in = new std::ifstream(InputFilename.c_str());134 135 //Gather the compile flags136 BrainF::CompileFlags cf = BrainF::flag_off;137 if (ArrayBoundsChecking)138 cf = BrainF::CompileFlags(cf | BrainF::flag_arraybounds);139 140 //Read the BrainF program141 BrainF bf;142 std::unique_ptr<Module> Mod(bf.parse(in, 65536, cf, Context)); // 64 KiB143 if (in != &std::cin)144 delete in;145 addMainFunction(Mod.get());146 147 //Verify generated code148 if (verifyModule(*Mod)) {149 errs() << "Error: module failed verification. This shouldn't happen.\n";150 abort();151 }152 153 //Write it out154 if (JIT) {155 InitializeNativeTarget();156 InitializeNativeTargetAsmPrinter();157 158 outs() << "------- Running JIT -------\n";159 Module &M = *Mod;160 ExecutionEngine *ee = EngineBuilder(std::move(Mod)).create();161 if (!ee) {162 errs() << "Error: execution engine creation failed.\n";163 abort();164 }165 std::vector<GenericValue> args;166 Function *brainf_func = M.getFunction("brainf");167 GenericValue gv = ee->runFunction(brainf_func, args);168 // Genereated code calls putchar, and output is not guaranteed without fflush.169 // The better place for fflush(stdout) call would be the generated code, but it170 // is unmanageable because stdout linkage name depends on stdlib implementation.171 fflush(stdout);172 } else {173 WriteBitcodeToFile(*Mod, *out);174 }175 176 //Clean up177 if (out != &outs())178 delete out;179 180 llvm_shutdown();181 182 return 0;183}184