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, i1)
70 Type
*Tys
[] = {PointerType::getUnqual(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 Type
* IntPtrTy
= IntegerType::getInt32Ty(C
);
93 Type
* Int8Ty
= IntegerType::getInt8Ty(C
);
94 Constant
* allocsize
= ConstantExpr::getSizeOf(Int8Ty
);
95 allocsize
= ConstantExpr::getTruncOrBitCast(allocsize
, IntPtrTy
);
96 ptr_arr
= builder
->CreateMalloc(IntPtrTy
, Int8Ty
, allocsize
, val_mem
, nullptr,
99 //call void @llvm.memset.p0i8.i32(i8 *%arr, i8 0, i32 %d, i1 0)
101 Value
*memset_params
[] = {
103 ConstantInt::get(C
, APInt(8, 0)),
105 ConstantInt::get(C
, APInt(1, 0))
108 CallInst
*memset_call
= builder
->
109 CreateCall(memset_func
, memset_params
);
110 memset_call
->setTailCall(false);
113 //%arrmax = getelementptr i8 *%arr, i32 %d
114 if (comflag
& flag_arraybounds
) {
115 ptr_arrmax
= builder
->CreateGEP(
116 Int8Ty
, ptr_arr
, ConstantInt::get(C
, APInt(32, memtotal
)), "arrmax");
119 //%head.%d = getelementptr i8 *%arr, i32 %d
120 curhead
= builder
->CreateGEP(
121 Int8Ty
, ptr_arr
, ConstantInt::get(C
, APInt(32, memtotal
/ 2)), headreg
);
126 endbb
= BasicBlock::Create(C
, label
, brainf_func
);
128 // call free(i8 *%arr)
129 builder
->SetInsertPoint(endbb
);
130 builder
->CreateFree(ptr_arr
);
133 ReturnInst::Create(C
, endbb
);
135 //Error block for array out of bounds
136 if (comflag
& flag_arraybounds
)
138 //@aberrormsg = internal constant [%d x i8] c"\00"
140 ConstantDataArray::getString(C
, "Error: The head has left the tape.",
143 GlobalVariable
*aberrormsg
= new GlobalVariable(
147 GlobalValue::InternalLinkage
,
151 //declare i32 @puts(i8 *)
152 FunctionCallee puts_func
= module
->getOrInsertFunction(
153 "puts", IntegerType::getInt32Ty(C
),
154 PointerType::getUnqual(IntegerType::getInt8Ty(C
)));
157 aberrorbb
= BasicBlock::Create(C
, label
, brainf_func
);
159 //call i32 @puts(i8 *getelementptr([%d x i8] *@aberrormsg, i32 0, i32 0))
161 Constant
*zero_32
= Constant::getNullValue(IntegerType::getInt32Ty(C
));
163 Constant
*gep_params
[] = {
168 Constant
*msgptr
= ConstantExpr::
169 getGetElementPtr(aberrormsg
->getValueType(), aberrormsg
, gep_params
);
171 Value
*puts_params
[] = {
175 CallInst
*puts_call
=
176 CallInst::Create(puts_func
,
179 puts_call
->setTailCall(false);
182 //br label %brainf.end
183 BranchInst::Create(endbb
, aberrorbb
);
187 void BrainF::readloop(PHINode
*phi
, BasicBlock
*oldbb
, BasicBlock
*testbb
,
189 Symbol cursym
= SYM_NONE
;
191 Symbol nextsym
= SYM_NONE
;
196 Type
*Int8Ty
= IntegerType::getInt8Ty(C
);
198 while(cursym
!= SYM_EOF
&& cursym
!= SYM_ENDLOOP
) {
199 // Write out commands
207 //%tape.%d = call i32 @getchar()
208 CallInst
*getchar_call
=
209 builder
->CreateCall(getchar_func
, {}, tapereg
);
210 getchar_call
->setTailCall(false);
211 Value
*tape_0
= getchar_call
;
213 //%tape.%d = trunc i32 %tape.%d to i8
214 Value
*tape_1
= builder
->
215 CreateTrunc(tape_0
, IntegerType::getInt8Ty(C
), tapereg
);
217 //store i8 %tape.%d, i8 *%head.%d
218 builder
->CreateStore(tape_1
, curhead
);
224 //%tape.%d = load i8 *%head.%d
225 LoadInst
*tape_0
= builder
->CreateLoad(Int8Ty
, curhead
, tapereg
);
227 //%tape.%d = sext i8 %tape.%d to i32
228 Value
*tape_1
= builder
->
229 CreateSExt(tape_0
, IntegerType::getInt32Ty(C
), tapereg
);
231 //call i32 @putchar(i32 %tape.%d)
232 Value
*putchar_params
[] = {
235 CallInst
*putchar_call
= builder
->
236 CreateCall(putchar_func
,
238 putchar_call
->setTailCall(false);
244 //%head.%d = getelementptr i8 *%head.%d, i32 %d
245 curhead
= builder
->CreateGEP(Int8Ty
, curhead
,
246 ConstantInt::get(C
, APInt(32, curvalue
)),
249 //Error block for array out of bounds
250 if (comflag
& flag_arraybounds
)
252 //%test.%d = icmp uge i8 *%head.%d, %arrmax
253 Value
*test_0
= builder
->
254 CreateICmpUGE(curhead
, ptr_arrmax
, testreg
);
256 //%test.%d = icmp ult i8 *%head.%d, %arr
257 Value
*test_1
= builder
->
258 CreateICmpULT(curhead
, ptr_arr
, testreg
);
260 //%test.%d = or i1 %test.%d, %test.%d
261 Value
*test_2
= builder
->
262 CreateOr(test_0
, test_1
, testreg
);
264 //br i1 %test.%d, label %main.%d, label %main.%d
265 BasicBlock
*nextbb
= BasicBlock::Create(C
, label
, brainf_func
);
266 builder
->CreateCondBr(test_2
, aberrorbb
, nextbb
);
269 builder
->SetInsertPoint(nextbb
);
276 //%tape.%d = load i8 *%head.%d
277 LoadInst
*tape_0
= builder
->CreateLoad(Int8Ty
, curhead
, tapereg
);
279 //%tape.%d = add i8 %tape.%d, %d
280 Value
*tape_1
= builder
->
281 CreateAdd(tape_0
, ConstantInt::get(C
, APInt(8, curvalue
)), tapereg
);
283 //store i8 %tape.%d, i8 *%head.%d\n"
284 builder
->CreateStore(tape_1
, curhead
);
291 BasicBlock
*testbb
= BasicBlock::Create(C
, label
, brainf_func
);
292 builder
->CreateBr(testbb
);
295 BasicBlock
*bb_0
= builder
->GetInsertBlock();
296 BasicBlock
*bb_1
= BasicBlock::Create(C
, label
, brainf_func
);
297 builder
->SetInsertPoint(bb_1
);
299 // Make part of PHI instruction now, wait until end of loop to finish
300 PHINode
*phi_0
= PHINode::Create(PointerType::getUnqual(Int8Ty
), 2,
302 phi_0
->addIncoming(curhead
, bb_0
);
305 readloop(phi_0
, bb_1
, testbb
, C
);
310 std::cerr
<< "Error: Unknown symbol.\n";
316 curvalue
= nextvalue
;
319 // Reading stdin loop
320 loop
= (cursym
== SYM_NONE
)
321 || (cursym
== SYM_MOVE
)
322 || (cursym
== SYM_CHANGE
);
326 if (cursym
== SYM_NONE
) {
340 if (cursym
== SYM_CHANGE
) {
341 curvalue
+= direction
;
344 if (cursym
== SYM_NONE
) {
346 curvalue
= direction
;
349 nextsym
= SYM_CHANGE
;
350 nextvalue
= direction
;
361 if (cursym
== SYM_MOVE
) {
362 curvalue
+= direction
;
365 if (cursym
== SYM_NONE
) {
367 curvalue
= direction
;
371 nextvalue
= direction
;
378 if (cursym
== SYM_NONE
) {
387 if (cursym
== SYM_NONE
) {
396 if (cursym
== SYM_NONE
) {
405 if (cursym
== SYM_NONE
) {
406 cursym
= SYM_ENDLOOP
;
408 nextsym
= SYM_ENDLOOP
;
413 // Ignore other characters
421 if (cursym
== SYM_ENDLOOP
) {
423 std::cerr
<< "Error: Extra ']'\n";
430 builder
->CreateBr(testbb
);
434 //%head.%d = phi i8 *[%head.%d, %main.%d], [%head.%d, %main.%d]
435 //Finish phi made at beginning of loop
436 phi
->addIncoming(curhead
, builder
->GetInsertBlock());
439 //%tape.%d = load i8 *%head.%d
440 LoadInst
*tape_0
= new LoadInst(Int8Ty
, head_0
, tapereg
, testbb
);
442 //%test.%d = icmp eq i8 %tape.%d, 0
443 ICmpInst
*test_0
= new ICmpInst(*testbb
, ICmpInst::ICMP_EQ
, tape_0
,
444 ConstantInt::get(C
, APInt(8, 0)), testreg
);
446 //br i1 %test.%d, label %main.%d, label %main.%d
447 BasicBlock
*bb_0
= BasicBlock::Create(C
, label
, brainf_func
);
448 BranchInst::Create(bb_0
, oldbb
, test_0
, testbb
);
451 builder
->SetInsertPoint(bb_0
);
453 //%head.%d = phi i8 *[%head.%d, %main.%d]
455 builder
->CreatePHI(PointerType::getUnqual(Int8Ty
), 1, headreg
);
456 phi_1
->addIncoming(head_0
, testbb
);
463 //End of the program, so go to return block
464 builder
->CreateBr(endbb
);
467 std::cerr
<< "Error: Missing ']'\n";