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