1 //===-- BrainF.cpp - BrainF compiler example ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This class compiles the BrainF language into LLVM assembly.
11 // The BrainF language has 8 commands:
12 // Command Equivalent C Action
13 // ------- ------------ ------
14 // , *h=getchar(); Read a character from stdin, 255 on EOF
15 // . putchar(*h); Write a character to stdout
16 // - --*h; Decrement tape
17 // + ++*h; Increment tape
18 // < --h; Move head left
19 // > ++h; Move head right
20 // [ while(*h) { Start loop
23 //===----------------------------------------------------------------------===//
26 #include "llvm/ADT/APInt.h"
27 #include "llvm/IR/BasicBlock.h"
28 #include "llvm/IR/Constant.h"
29 #include "llvm/IR/Constants.h"
30 #include "llvm/IR/DerivedTypes.h"
31 #include "llvm/IR/Function.h"
32 #include "llvm/IR/GlobalValue.h"
33 #include "llvm/IR/GlobalVariable.h"
34 #include "llvm/IR/InstrTypes.h"
35 #include "llvm/IR/Instruction.h"
36 #include "llvm/IR/Instructions.h"
37 #include "llvm/IR/Intrinsics.h"
38 #include "llvm/IR/Module.h"
39 #include "llvm/IR/Type.h"
40 #include "llvm/Support/Casting.h"
46 //Set the constants for naming
47 const char *BrainF::tapereg
= "tape";
48 const char *BrainF::headreg
= "head";
49 const char *BrainF::label
= "brainf";
50 const char *BrainF::testreg
= "test";
52 Module
*BrainF::parse(std::istream
*in1
, int mem
, CompileFlags cf
,
53 LLVMContext
& Context
) {
59 readloop(nullptr, nullptr, nullptr, Context
);
64 void BrainF::header(LLVMContext
& C
) {
65 module
= new Module("BrainF", C
);
69 //declare void @llvm.memset.p0i8.i32(i8 *, i8, i32, i32, i1)
70 Type
*Tys
[] = { Type::getInt8PtrTy(C
), Type::getInt32Ty(C
) };
71 Function
*memset_func
= Intrinsic::getDeclaration(module
, Intrinsic::memset
,
74 //declare i32 @getchar()
76 module
->getOrInsertFunction("getchar", IntegerType::getInt32Ty(C
));
78 //declare i32 @putchar(i32)
79 putchar_func
= module
->getOrInsertFunction(
80 "putchar", IntegerType::getInt32Ty(C
), IntegerType::getInt32Ty(C
));
84 //define void @brainf()
85 brainf_func
= Function::Create(FunctionType::get(Type::getVoidTy(C
), false),
86 Function::ExternalLinkage
, "brainf", module
);
88 builder
= new IRBuilder
<>(BasicBlock::Create(C
, label
, brainf_func
));
90 //%arr = malloc i8, i32 %d
91 ConstantInt
*val_mem
= ConstantInt::get(C
, APInt(32, memtotal
));
92 BasicBlock
* BB
= builder
->GetInsertBlock();
93 Type
* IntPtrTy
= IntegerType::getInt32Ty(C
);
94 Type
* Int8Ty
= IntegerType::getInt8Ty(C
);
95 Constant
* allocsize
= ConstantExpr::getSizeOf(Int8Ty
);
96 allocsize
= ConstantExpr::getTruncOrBitCast(allocsize
, IntPtrTy
);
97 ptr_arr
= CallInst::CreateMalloc(BB
, IntPtrTy
, Int8Ty
, allocsize
, val_mem
,
99 BB
->getInstList().push_back(cast
<Instruction
>(ptr_arr
));
101 //call void @llvm.memset.p0i8.i32(i8 *%arr, i8 0, i32 %d, i32 1, i1 0)
103 Value
*memset_params
[] = {
105 ConstantInt::get(C
, APInt(8, 0)),
107 ConstantInt::get(C
, APInt(32, 1)),
108 ConstantInt::get(C
, APInt(1, 0))
111 CallInst
*memset_call
= builder
->
112 CreateCall(memset_func
, memset_params
);
113 memset_call
->setTailCall(false);
116 //%arrmax = getelementptr i8 *%arr, i32 %d
117 if (comflag
& flag_arraybounds
) {
118 ptr_arrmax
= builder
->
119 CreateGEP(ptr_arr
, ConstantInt::get(C
, APInt(32, memtotal
)), "arrmax");
122 //%head.%d = getelementptr i8 *%arr, i32 %d
123 curhead
= builder
->CreateGEP(ptr_arr
,
124 ConstantInt::get(C
, APInt(32, memtotal
/2)),
130 endbb
= BasicBlock::Create(C
, label
, brainf_func
);
132 //call free(i8 *%arr)
133 endbb
->getInstList().push_back(CallInst::CreateFree(ptr_arr
, endbb
));
136 ReturnInst::Create(C
, endbb
);
138 //Error block for array out of bounds
139 if (comflag
& flag_arraybounds
)
141 //@aberrormsg = internal constant [%d x i8] c"\00"
143 ConstantDataArray::getString(C
, "Error: The head has left the tape.",
146 GlobalVariable
*aberrormsg
= new GlobalVariable(
150 GlobalValue::InternalLinkage
,
154 //declare i32 @puts(i8 *)
155 FunctionCallee puts_func
= module
->getOrInsertFunction(
156 "puts", IntegerType::getInt32Ty(C
),
157 PointerType::getUnqual(IntegerType::getInt8Ty(C
)));
160 aberrorbb
= BasicBlock::Create(C
, label
, brainf_func
);
162 //call i32 @puts(i8 *getelementptr([%d x i8] *@aberrormsg, i32 0, i32 0))
164 Constant
*zero_32
= Constant::getNullValue(IntegerType::getInt32Ty(C
));
166 Constant
*gep_params
[] = {
171 Constant
*msgptr
= ConstantExpr::
172 getGetElementPtr(aberrormsg
->getValueType(), aberrormsg
, gep_params
);
174 Value
*puts_params
[] = {
178 CallInst
*puts_call
=
179 CallInst::Create(puts_func
,
182 puts_call
->setTailCall(false);
185 //br label %brainf.end
186 BranchInst::Create(endbb
, aberrorbb
);
190 void BrainF::readloop(PHINode
*phi
, BasicBlock
*oldbb
, BasicBlock
*testbb
,
192 Symbol cursym
= SYM_NONE
;
194 Symbol nextsym
= SYM_NONE
;
200 while(cursym
!= SYM_EOF
&& cursym
!= SYM_ENDLOOP
) {
201 // Write out commands
209 //%tape.%d = call i32 @getchar()
210 CallInst
*getchar_call
=
211 builder
->CreateCall(getchar_func
, {}, tapereg
);
212 getchar_call
->setTailCall(false);
213 Value
*tape_0
= getchar_call
;
215 //%tape.%d = trunc i32 %tape.%d to i8
216 Value
*tape_1
= builder
->
217 CreateTrunc(tape_0
, IntegerType::getInt8Ty(C
), tapereg
);
219 //store i8 %tape.%d, i8 *%head.%d
220 builder
->CreateStore(tape_1
, curhead
);
226 //%tape.%d = load i8 *%head.%d
227 LoadInst
*tape_0
= builder
->CreateLoad(curhead
, tapereg
);
229 //%tape.%d = sext i8 %tape.%d to i32
230 Value
*tape_1
= builder
->
231 CreateSExt(tape_0
, IntegerType::getInt32Ty(C
), tapereg
);
233 //call i32 @putchar(i32 %tape.%d)
234 Value
*putchar_params
[] = {
237 CallInst
*putchar_call
= builder
->
238 CreateCall(putchar_func
,
240 putchar_call
->setTailCall(false);
246 //%head.%d = getelementptr i8 *%head.%d, i32 %d
248 CreateGEP(curhead
, ConstantInt::get(C
, APInt(32, curvalue
)),
251 //Error block for array out of bounds
252 if (comflag
& flag_arraybounds
)
254 //%test.%d = icmp uge i8 *%head.%d, %arrmax
255 Value
*test_0
= builder
->
256 CreateICmpUGE(curhead
, ptr_arrmax
, testreg
);
258 //%test.%d = icmp ult i8 *%head.%d, %arr
259 Value
*test_1
= builder
->
260 CreateICmpULT(curhead
, ptr_arr
, testreg
);
262 //%test.%d = or i1 %test.%d, %test.%d
263 Value
*test_2
= builder
->
264 CreateOr(test_0
, test_1
, testreg
);
266 //br i1 %test.%d, label %main.%d, label %main.%d
267 BasicBlock
*nextbb
= BasicBlock::Create(C
, label
, brainf_func
);
268 builder
->CreateCondBr(test_2
, aberrorbb
, nextbb
);
271 builder
->SetInsertPoint(nextbb
);
278 //%tape.%d = load i8 *%head.%d
279 LoadInst
*tape_0
= builder
->CreateLoad(curhead
, tapereg
);
281 //%tape.%d = add i8 %tape.%d, %d
282 Value
*tape_1
= builder
->
283 CreateAdd(tape_0
, ConstantInt::get(C
, APInt(8, curvalue
)), tapereg
);
285 //store i8 %tape.%d, i8 *%head.%d\n"
286 builder
->CreateStore(tape_1
, curhead
);
293 BasicBlock
*testbb
= BasicBlock::Create(C
, label
, brainf_func
);
294 builder
->CreateBr(testbb
);
297 BasicBlock
*bb_0
= builder
->GetInsertBlock();
298 BasicBlock
*bb_1
= BasicBlock::Create(C
, label
, brainf_func
);
299 builder
->SetInsertPoint(bb_1
);
301 // Make part of PHI instruction now, wait until end of loop to finish
303 PHINode::Create(PointerType::getUnqual(IntegerType::getInt8Ty(C
)),
305 phi_0
->addIncoming(curhead
, bb_0
);
308 readloop(phi_0
, bb_1
, testbb
, C
);
313 std::cerr
<< "Error: Unknown symbol.\n";
319 curvalue
= nextvalue
;
322 // Reading stdin loop
323 loop
= (cursym
== SYM_NONE
)
324 || (cursym
== SYM_MOVE
)
325 || (cursym
== SYM_CHANGE
);
329 if (cursym
== SYM_NONE
) {
343 if (cursym
== SYM_CHANGE
) {
344 curvalue
+= direction
;
347 if (cursym
== SYM_NONE
) {
349 curvalue
= direction
;
352 nextsym
= SYM_CHANGE
;
353 nextvalue
= direction
;
364 if (cursym
== SYM_MOVE
) {
365 curvalue
+= direction
;
368 if (cursym
== SYM_NONE
) {
370 curvalue
= direction
;
374 nextvalue
= direction
;
381 if (cursym
== SYM_NONE
) {
390 if (cursym
== SYM_NONE
) {
399 if (cursym
== SYM_NONE
) {
408 if (cursym
== SYM_NONE
) {
409 cursym
= SYM_ENDLOOP
;
411 nextsym
= SYM_ENDLOOP
;
416 // Ignore other characters
424 if (cursym
== SYM_ENDLOOP
) {
426 std::cerr
<< "Error: Extra ']'\n";
433 builder
->CreateBr(testbb
);
437 //%head.%d = phi i8 *[%head.%d, %main.%d], [%head.%d, %main.%d]
438 //Finish phi made at beginning of loop
439 phi
->addIncoming(curhead
, builder
->GetInsertBlock());
442 //%tape.%d = load i8 *%head.%d
443 LoadInst
*tape_0
= new LoadInst(head_0
, tapereg
, testbb
);
445 //%test.%d = icmp eq i8 %tape.%d, 0
446 ICmpInst
*test_0
= new ICmpInst(*testbb
, ICmpInst::ICMP_EQ
, tape_0
,
447 ConstantInt::get(C
, APInt(8, 0)), testreg
);
449 //br i1 %test.%d, label %main.%d, label %main.%d
450 BasicBlock
*bb_0
= BasicBlock::Create(C
, label
, brainf_func
);
451 BranchInst::Create(bb_0
, oldbb
, test_0
, testbb
);
454 builder
->SetInsertPoint(bb_0
);
456 //%head.%d = phi i8 *[%head.%d, %main.%d]
457 PHINode
*phi_1
= builder
->
458 CreatePHI(PointerType::getUnqual(IntegerType::getInt8Ty(C
)), 1,
460 phi_1
->addIncoming(head_0
, testbb
);
467 //End of the program, so go to return block
468 builder
->CreateBr(endbb
);
471 std::cerr
<< "Error: Missing ']'\n";