Home | History | Annotate | Line # | Download | only in BrainF
      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       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 
    106 int 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