Home | History | Annotate | Download | only in BrainF
      1 //===-- BrainF.cpp - BrainF compiler example ----------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===--------------------------------------------------------------------===//
      9 //
     10 // This class compiles the BrainF language into LLVM assembly.
     11 //
     12 // The BrainF language has 8 commands:
     13 // Command   Equivalent C    Action
     14 // -------   ------------    ------
     15 // ,         *h=getchar();   Read a character from stdin, 255 on EOF
     16 // .         putchar(*h);    Write a character to stdout
     17 // -         --*h;           Decrement tape
     18 // +         ++*h;           Increment tape
     19 // <         --h;            Move head left
     20 // >         ++h;            Move head right
     21 // [         while(*h) {     Start loop
     22 // ]         }               End loop
     23 //
     24 //===--------------------------------------------------------------------===//
     25 
     26 #include "BrainF.h"
     27 #include "llvm/ADT/STLExtras.h"
     28 #include "llvm/IR/Constants.h"
     29 #include "llvm/IR/Instructions.h"
     30 #include "llvm/IR/Intrinsics.h"
     31 #include <iostream>
     32 using namespace llvm;
     33 
     34 //Set the constants for naming
     35 const char *BrainF::tapereg = "tape";
     36 const char *BrainF::headreg = "head";
     37 const char *BrainF::label   = "brainf";
     38 const char *BrainF::testreg = "test";
     39 
     40 Module *BrainF::parse(std::istream *in1, int mem, CompileFlags cf,
     41                       LLVMContext& Context) {
     42   in       = in1;
     43   memtotal = mem;
     44   comflag  = cf;
     45 
     46   header(Context);
     47   readloop(0, 0, 0, Context);
     48   delete builder;
     49   return module;
     50 }
     51 
     52 void BrainF::header(LLVMContext& C) {
     53   module = new Module("BrainF", C);
     54 
     55   //Function prototypes
     56 
     57   //declare void @llvm.memset.p0i8.i32(i8 *, i8, i32, i32, i1)
     58   Type *Tys[] = { Type::getInt8PtrTy(C), Type::getInt32Ty(C) };
     59   Function *memset_func = Intrinsic::getDeclaration(module, Intrinsic::memset,
     60                                                     Tys);
     61 
     62   //declare i32 @getchar()
     63   getchar_func = cast<Function>(module->
     64     getOrInsertFunction("getchar", IntegerType::getInt32Ty(C), NULL));
     65 
     66   //declare i32 @putchar(i32)
     67   putchar_func = cast<Function>(module->
     68     getOrInsertFunction("putchar", IntegerType::getInt32Ty(C),
     69                         IntegerType::getInt32Ty(C), NULL));
     70 
     71 
     72   //Function header
     73 
     74   //define void @brainf()
     75   brainf_func = cast<Function>(module->
     76     getOrInsertFunction("brainf", Type::getVoidTy(C), NULL));
     77 
     78   builder = new IRBuilder<>(BasicBlock::Create(C, label, brainf_func));
     79 
     80   //%arr = malloc i8, i32 %d
     81   ConstantInt *val_mem = ConstantInt::get(C, APInt(32, memtotal));
     82   BasicBlock* BB = builder->GetInsertBlock();
     83   Type* IntPtrTy = IntegerType::getInt32Ty(C);
     84   Type* Int8Ty = IntegerType::getInt8Ty(C);
     85   Constant* allocsize = ConstantExpr::getSizeOf(Int8Ty);
     86   allocsize = ConstantExpr::getTruncOrBitCast(allocsize, IntPtrTy);
     87   ptr_arr = CallInst::CreateMalloc(BB, IntPtrTy, Int8Ty, allocsize, val_mem,
     88                                    NULL, "arr");
     89   BB->getInstList().push_back(cast<Instruction>(ptr_arr));
     90 
     91   //call void @llvm.memset.p0i8.i32(i8 *%arr, i8 0, i32 %d, i32 1, i1 0)
     92   {
     93     Value *memset_params[] = {
     94       ptr_arr,
     95       ConstantInt::get(C, APInt(8, 0)),
     96       val_mem,
     97       ConstantInt::get(C, APInt(32, 1)),
     98       ConstantInt::get(C, APInt(1, 0))
     99     };
    100 
    101     CallInst *memset_call = builder->
    102       CreateCall(memset_func, memset_params);
    103     memset_call->setTailCall(false);
    104   }
    105 
    106   //%arrmax = getelementptr i8 *%arr, i32 %d
    107   if (comflag & flag_arraybounds) {
    108     ptr_arrmax = builder->
    109       CreateGEP(ptr_arr, ConstantInt::get(C, APInt(32, memtotal)), "arrmax");
    110   }
    111 
    112   //%head.%d = getelementptr i8 *%arr, i32 %d
    113   curhead = builder->CreateGEP(ptr_arr,
    114                                ConstantInt::get(C, APInt(32, memtotal/2)),
    115                                headreg);
    116 
    117 
    118 
    119   //Function footer
    120 
    121   //brainf.end:
    122   endbb = BasicBlock::Create(C, label, brainf_func);
    123 
    124   //call free(i8 *%arr)
    125   endbb->getInstList().push_back(CallInst::CreateFree(ptr_arr, endbb));
    126 
    127   //ret void
    128   ReturnInst::Create(C, endbb);
    129 
    130 
    131 
    132   //Error block for array out of bounds
    133   if (comflag & flag_arraybounds)
    134   {
    135     //@aberrormsg = internal constant [%d x i8] c"\00"
    136     Constant *msg_0 =
    137       ConstantDataArray::getString(C, "Error: The head has left the tape.",
    138                                    true);
    139 
    140     GlobalVariable *aberrormsg = new GlobalVariable(
    141       *module,
    142       msg_0->getType(),
    143       true,
    144       GlobalValue::InternalLinkage,
    145       msg_0,
    146       "aberrormsg");
    147 
    148     //declare i32 @puts(i8 *)
    149     Function *puts_func = cast<Function>(module->
    150       getOrInsertFunction("puts", IntegerType::getInt32Ty(C),
    151                       PointerType::getUnqual(IntegerType::getInt8Ty(C)), NULL));
    152 
    153     //brainf.aberror:
    154     aberrorbb = BasicBlock::Create(C, label, brainf_func);
    155 
    156     //call i32 @puts(i8 *getelementptr([%d x i8] *@aberrormsg, i32 0, i32 0))
    157     {
    158       Constant *zero_32 = Constant::getNullValue(IntegerType::getInt32Ty(C));
    159 
    160       Constant *gep_params[] = {
    161         zero_32,
    162         zero_32
    163       };
    164 
    165       Constant *msgptr = ConstantExpr::
    166         getGetElementPtr(aberrormsg, gep_params);
    167 
    168       Value *puts_params[] = {
    169         msgptr
    170       };
    171 
    172       CallInst *puts_call =
    173         CallInst::Create(puts_func,
    174                          puts_params,
    175                          "", aberrorbb);
    176       puts_call->setTailCall(false);
    177     }
    178 
    179     //br label %brainf.end
    180     BranchInst::Create(endbb, aberrorbb);
    181   }
    182 }
    183 
    184 void BrainF::readloop(PHINode *phi, BasicBlock *oldbb, BasicBlock *testbb,
    185                       LLVMContext &C) {
    186   Symbol cursym = SYM_NONE;
    187   int curvalue = 0;
    188   Symbol nextsym = SYM_NONE;
    189   int nextvalue = 0;
    190   char c;
    191   int loop;
    192   int direction;
    193 
    194   while(cursym != SYM_EOF && cursym != SYM_ENDLOOP) {
    195     // Write out commands
    196     switch(cursym) {
    197       case SYM_NONE:
    198         // Do nothing
    199         break;
    200 
    201       case SYM_READ:
    202         {
    203           //%tape.%d = call i32 @getchar()
    204           CallInst *getchar_call = builder->CreateCall(getchar_func, tapereg);
    205           getchar_call->setTailCall(false);
    206           Value *tape_0 = getchar_call;
    207 
    208           //%tape.%d = trunc i32 %tape.%d to i8
    209           Value *tape_1 = builder->
    210             CreateTrunc(tape_0, IntegerType::getInt8Ty(C), tapereg);
    211 
    212           //store i8 %tape.%d, i8 *%head.%d
    213           builder->CreateStore(tape_1, curhead);
    214         }
    215         break;
    216 
    217       case SYM_WRITE:
    218         {
    219           //%tape.%d = load i8 *%head.%d
    220           LoadInst *tape_0 = builder->CreateLoad(curhead, tapereg);
    221 
    222           //%tape.%d = sext i8 %tape.%d to i32
    223           Value *tape_1 = builder->
    224             CreateSExt(tape_0, IntegerType::getInt32Ty(C), tapereg);
    225 
    226           //call i32 @putchar(i32 %tape.%d)
    227           Value *putchar_params[] = {
    228             tape_1
    229           };
    230           CallInst *putchar_call = builder->
    231             CreateCall(putchar_func,
    232                        putchar_params);
    233           putchar_call->setTailCall(false);
    234         }
    235         break;
    236 
    237       case SYM_MOVE:
    238         {
    239           //%head.%d = getelementptr i8 *%head.%d, i32 %d
    240           curhead = builder->
    241             CreateGEP(curhead, ConstantInt::get(C, APInt(32, curvalue)),
    242                       headreg);
    243 
    244           //Error block for array out of bounds
    245           if (comflag & flag_arraybounds)
    246           {
    247             //%test.%d = icmp uge i8 *%head.%d, %arrmax
    248             Value *test_0 = builder->
    249               CreateICmpUGE(curhead, ptr_arrmax, testreg);
    250 
    251             //%test.%d = icmp ult i8 *%head.%d, %arr
    252             Value *test_1 = builder->
    253               CreateICmpULT(curhead, ptr_arr, testreg);
    254 
    255             //%test.%d = or i1 %test.%d, %test.%d
    256             Value *test_2 = builder->
    257               CreateOr(test_0, test_1, testreg);
    258 
    259             //br i1 %test.%d, label %main.%d, label %main.%d
    260             BasicBlock *nextbb = BasicBlock::Create(C, label, brainf_func);
    261             builder->CreateCondBr(test_2, aberrorbb, nextbb);
    262 
    263             //main.%d:
    264             builder->SetInsertPoint(nextbb);
    265           }
    266         }
    267         break;
    268 
    269       case SYM_CHANGE:
    270         {
    271           //%tape.%d = load i8 *%head.%d
    272           LoadInst *tape_0 = builder->CreateLoad(curhead, tapereg);
    273 
    274           //%tape.%d = add i8 %tape.%d, %d
    275           Value *tape_1 = builder->
    276             CreateAdd(tape_0, ConstantInt::get(C, APInt(8, curvalue)), tapereg);
    277 
    278           //store i8 %tape.%d, i8 *%head.%d\n"
    279           builder->CreateStore(tape_1, curhead);
    280         }
    281         break;
    282 
    283       case SYM_LOOP:
    284         {
    285           //br label %main.%d
    286           BasicBlock *testbb = BasicBlock::Create(C, label, brainf_func);
    287           builder->CreateBr(testbb);
    288 
    289           //main.%d:
    290           BasicBlock *bb_0 = builder->GetInsertBlock();
    291           BasicBlock *bb_1 = BasicBlock::Create(C, label, brainf_func);
    292           builder->SetInsertPoint(bb_1);
    293 
    294           // Make part of PHI instruction now, wait until end of loop to finish
    295           PHINode *phi_0 =
    296             PHINode::Create(PointerType::getUnqual(IntegerType::getInt8Ty(C)),
    297                             2, headreg, testbb);
    298           phi_0->addIncoming(curhead, bb_0);
    299           curhead = phi_0;
    300 
    301           readloop(phi_0, bb_1, testbb, C);
    302         }
    303         break;
    304 
    305       default:
    306         std::cerr << "Error: Unknown symbol.\n";
    307         abort();
    308         break;
    309     }
    310 
    311     cursym = nextsym;
    312     curvalue = nextvalue;
    313     nextsym = SYM_NONE;
    314 
    315     // Reading stdin loop
    316     loop = (cursym == SYM_NONE)
    317         || (cursym == SYM_MOVE)
    318         || (cursym == SYM_CHANGE);
    319     while(loop) {
    320       *in>>c;
    321       if (in->eof()) {
    322         if (cursym == SYM_NONE) {
    323           cursym = SYM_EOF;
    324         } else {
    325           nextsym = SYM_EOF;
    326         }
    327         loop = 0;
    328       } else {
    329         direction = 1;
    330         switch(c) {
    331           case '-':
    332             direction = -1;
    333             // Fall through
    334 
    335           case '+':
    336             if (cursym == SYM_CHANGE) {
    337               curvalue += direction;
    338               // loop = 1
    339             } else {
    340               if (cursym == SYM_NONE) {
    341                 cursym = SYM_CHANGE;
    342                 curvalue = direction;
    343                 // loop = 1
    344               } else {
    345                 nextsym = SYM_CHANGE;
    346                 nextvalue = direction;
    347                 loop = 0;
    348               }
    349             }
    350             break;
    351 
    352           case '<':
    353             direction = -1;
    354             // Fall through
    355 
    356           case '>':
    357             if (cursym == SYM_MOVE) {
    358               curvalue += direction;
    359               // loop = 1
    360             } else {
    361               if (cursym == SYM_NONE) {
    362                 cursym = SYM_MOVE;
    363                 curvalue = direction;
    364                 // loop = 1
    365               } else {
    366                 nextsym = SYM_MOVE;
    367                 nextvalue = direction;
    368                 loop = 0;
    369               }
    370             }
    371             break;
    372 
    373           case ',':
    374             if (cursym == SYM_NONE) {
    375               cursym = SYM_READ;
    376             } else {
    377               nextsym = SYM_READ;
    378             }
    379             loop = 0;
    380             break;
    381 
    382           case '.':
    383             if (cursym == SYM_NONE) {
    384               cursym = SYM_WRITE;
    385             } else {
    386               nextsym = SYM_WRITE;
    387             }
    388             loop = 0;
    389             break;
    390 
    391           case '[':
    392             if (cursym == SYM_NONE) {
    393               cursym = SYM_LOOP;
    394             } else {
    395               nextsym = SYM_LOOP;
    396             }
    397             loop = 0;
    398             break;
    399 
    400           case ']':
    401             if (cursym == SYM_NONE) {
    402               cursym = SYM_ENDLOOP;
    403             } else {
    404               nextsym = SYM_ENDLOOP;
    405             }
    406             loop = 0;
    407             break;
    408 
    409           // Ignore other characters
    410           default:
    411             break;
    412         }
    413       }
    414     }
    415   }
    416 
    417   if (cursym == SYM_ENDLOOP) {
    418     if (!phi) {
    419       std::cerr << "Error: Extra ']'\n";
    420       abort();
    421     }
    422 
    423     // Write loop test
    424     {
    425       //br label %main.%d
    426       builder->CreateBr(testbb);
    427 
    428       //main.%d:
    429 
    430       //%head.%d = phi i8 *[%head.%d, %main.%d], [%head.%d, %main.%d]
    431       //Finish phi made at beginning of loop
    432       phi->addIncoming(curhead, builder->GetInsertBlock());
    433       Value *head_0 = phi;
    434 
    435       //%tape.%d = load i8 *%head.%d
    436       LoadInst *tape_0 = new LoadInst(head_0, tapereg, testbb);
    437 
    438       //%test.%d = icmp eq i8 %tape.%d, 0
    439       ICmpInst *test_0 = new ICmpInst(*testbb, ICmpInst::ICMP_EQ, tape_0,
    440                                     ConstantInt::get(C, APInt(8, 0)), testreg);
    441 
    442       //br i1 %test.%d, label %main.%d, label %main.%d
    443       BasicBlock *bb_0 = BasicBlock::Create(C, label, brainf_func);
    444       BranchInst::Create(bb_0, oldbb, test_0, testbb);
    445 
    446       //main.%d:
    447       builder->SetInsertPoint(bb_0);
    448 
    449       //%head.%d = phi i8 *[%head.%d, %main.%d]
    450       PHINode *phi_1 = builder->
    451         CreatePHI(PointerType::getUnqual(IntegerType::getInt8Ty(C)), 1,
    452                   headreg);
    453       phi_1->addIncoming(head_0, testbb);
    454       curhead = phi_1;
    455     }
    456 
    457     return;
    458   }
    459 
    460   //End of the program, so go to return block
    461   builder->CreateBr(endbb);
    462 
    463   if (phi) {
    464     std::cerr << "Error: Missing ']'\n";
    465     abort();
    466   }
    467 }
    468