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
[] = { 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, i1 0)
103 Value
*memset_params
[] = {
105 ConstantInt::get(C
, APInt(8, 0)),
107 ConstantInt::get(C
, APInt(1, 0))
110 CallInst
*memset_call
= builder
->
111 CreateCall(memset_func
, memset_params
);
112 memset_call
->setTailCall(false);
115 //%arrmax = getelementptr i8 *%arr, i32 %d
116 if (comflag
& flag_arraybounds
) {
117 ptr_arrmax
= builder
->CreateGEP(
118 Int8Ty
, ptr_arr
, ConstantInt::get(C
, APInt(32, memtotal
)), "arrmax");
121 //%head.%d = getelementptr i8 *%arr, i32 %d
122 curhead
= builder
->CreateGEP(
123 Int8Ty
, ptr_arr
, ConstantInt::get(C
, APInt(32, memtotal
/ 2)), headreg
);
128 endbb
= BasicBlock::Create(C
, label
, brainf_func
);
130 //call free(i8 *%arr)
131 endbb
->getInstList().push_back(CallInst::CreateFree(ptr_arr
, endbb
));
134 ReturnInst::Create(C
, endbb
);
136 //Error block for array out of bounds
137 if (comflag
& flag_arraybounds
)
139 //@aberrormsg = internal constant [%d x i8] c"\00"
141 ConstantDataArray::getString(C
, "Error: The head has left the tape.",
144 GlobalVariable
*aberrormsg
= new GlobalVariable(
148 GlobalValue::InternalLinkage
,
152 //declare i32 @puts(i8 *)
153 FunctionCallee puts_func
= module
->getOrInsertFunction(
154 "puts", IntegerType::getInt32Ty(C
),
155 PointerType::getUnqual(IntegerType::getInt8Ty(C
)));
158 aberrorbb
= BasicBlock::Create(C
, label
, brainf_func
);
160 //call i32 @puts(i8 *getelementptr([%d x i8] *@aberrormsg, i32 0, i32 0))
162 Constant
*zero_32
= Constant::getNullValue(IntegerType::getInt32Ty(C
));
164 Constant
*gep_params
[] = {
169 Constant
*msgptr
= ConstantExpr::
170 getGetElementPtr(aberrormsg
->getValueType(), aberrormsg
, gep_params
);
172 Value
*puts_params
[] = {
176 CallInst
*puts_call
=
177 CallInst::Create(puts_func
,
180 puts_call
->setTailCall(false);
183 //br label %brainf.end
184 BranchInst::Create(endbb
, aberrorbb
);
188 void BrainF::readloop(PHINode
*phi
, BasicBlock
*oldbb
, BasicBlock
*testbb
,
190 Symbol cursym
= SYM_NONE
;
192 Symbol nextsym
= SYM_NONE
;
197 Type
*Int8Ty
= IntegerType::getInt8Ty(C
);
199 while(cursym
!= SYM_EOF
&& cursym
!= SYM_ENDLOOP
) {
200 // Write out commands
208 //%tape.%d = call i32 @getchar()
209 CallInst
*getchar_call
=
210 builder
->CreateCall(getchar_func
, {}, tapereg
);
211 getchar_call
->setTailCall(false);
212 Value
*tape_0
= getchar_call
;
214 //%tape.%d = trunc i32 %tape.%d to i8
215 Value
*tape_1
= builder
->
216 CreateTrunc(tape_0
, IntegerType::getInt8Ty(C
), tapereg
);
218 //store i8 %tape.%d, i8 *%head.%d
219 builder
->CreateStore(tape_1
, curhead
);
225 //%tape.%d = load i8 *%head.%d
226 LoadInst
*tape_0
= builder
->CreateLoad(Int8Ty
, curhead
, tapereg
);
228 //%tape.%d = sext i8 %tape.%d to i32
229 Value
*tape_1
= builder
->
230 CreateSExt(tape_0
, IntegerType::getInt32Ty(C
), tapereg
);
232 //call i32 @putchar(i32 %tape.%d)
233 Value
*putchar_params
[] = {
236 CallInst
*putchar_call
= builder
->
237 CreateCall(putchar_func
,
239 putchar_call
->setTailCall(false);
245 //%head.%d = getelementptr i8 *%head.%d, i32 %d
246 curhead
= builder
->CreateGEP(Int8Ty
, curhead
,
247 ConstantInt::get(C
, APInt(32, curvalue
)),
250 //Error block for array out of bounds
251 if (comflag
& flag_arraybounds
)
253 //%test.%d = icmp uge i8 *%head.%d, %arrmax
254 Value
*test_0
= builder
->
255 CreateICmpUGE(curhead
, ptr_arrmax
, testreg
);
257 //%test.%d = icmp ult i8 *%head.%d, %arr
258 Value
*test_1
= builder
->
259 CreateICmpULT(curhead
, ptr_arr
, testreg
);
261 //%test.%d = or i1 %test.%d, %test.%d
262 Value
*test_2
= builder
->
263 CreateOr(test_0
, test_1
, testreg
);
265 //br i1 %test.%d, label %main.%d, label %main.%d
266 BasicBlock
*nextbb
= BasicBlock::Create(C
, label
, brainf_func
);
267 builder
->CreateCondBr(test_2
, aberrorbb
, nextbb
);
270 builder
->SetInsertPoint(nextbb
);
277 //%tape.%d = load i8 *%head.%d
278 LoadInst
*tape_0
= builder
->CreateLoad(Int8Ty
, curhead
, tapereg
);
280 //%tape.%d = add i8 %tape.%d, %d
281 Value
*tape_1
= builder
->
282 CreateAdd(tape_0
, ConstantInt::get(C
, APInt(8, curvalue
)), tapereg
);
284 //store i8 %tape.%d, i8 *%head.%d\n"
285 builder
->CreateStore(tape_1
, curhead
);
292 BasicBlock
*testbb
= BasicBlock::Create(C
, label
, brainf_func
);
293 builder
->CreateBr(testbb
);
296 BasicBlock
*bb_0
= builder
->GetInsertBlock();
297 BasicBlock
*bb_1
= BasicBlock::Create(C
, label
, brainf_func
);
298 builder
->SetInsertPoint(bb_1
);
300 // Make part of PHI instruction now, wait until end of loop to finish
301 PHINode
*phi_0
= PHINode::Create(PointerType::getUnqual(Int8Ty
), 2,
303 phi_0
->addIncoming(curhead
, bb_0
);
306 readloop(phi_0
, bb_1
, testbb
, C
);
311 std::cerr
<< "Error: Unknown symbol.\n";
317 curvalue
= nextvalue
;
320 // Reading stdin loop
321 loop
= (cursym
== SYM_NONE
)
322 || (cursym
== SYM_MOVE
)
323 || (cursym
== SYM_CHANGE
);
327 if (cursym
== SYM_NONE
) {
341 if (cursym
== SYM_CHANGE
) {
342 curvalue
+= direction
;
345 if (cursym
== SYM_NONE
) {
347 curvalue
= direction
;
350 nextsym
= SYM_CHANGE
;
351 nextvalue
= direction
;
362 if (cursym
== SYM_MOVE
) {
363 curvalue
+= direction
;
366 if (cursym
== SYM_NONE
) {
368 curvalue
= direction
;
372 nextvalue
= direction
;
379 if (cursym
== SYM_NONE
) {
388 if (cursym
== SYM_NONE
) {
397 if (cursym
== SYM_NONE
) {
406 if (cursym
== SYM_NONE
) {
407 cursym
= SYM_ENDLOOP
;
409 nextsym
= SYM_ENDLOOP
;
414 // Ignore other characters
422 if (cursym
== SYM_ENDLOOP
) {
424 std::cerr
<< "Error: Extra ']'\n";
431 builder
->CreateBr(testbb
);
435 //%head.%d = phi i8 *[%head.%d, %main.%d], [%head.%d, %main.%d]
436 //Finish phi made at beginning of loop
437 phi
->addIncoming(curhead
, builder
->GetInsertBlock());
440 //%tape.%d = load i8 *%head.%d
441 LoadInst
*tape_0
= new LoadInst(Int8Ty
, head_0
, tapereg
, testbb
);
443 //%test.%d = icmp eq i8 %tape.%d, 0
444 ICmpInst
*test_0
= new ICmpInst(*testbb
, ICmpInst::ICMP_EQ
, tape_0
,
445 ConstantInt::get(C
, APInt(8, 0)), testreg
);
447 //br i1 %test.%d, label %main.%d, label %main.%d
448 BasicBlock
*bb_0
= BasicBlock::Create(C
, label
, brainf_func
);
449 BranchInst::Create(bb_0
, oldbb
, test_0
, testbb
);
452 builder
->SetInsertPoint(bb_0
);
454 //%head.%d = phi i8 *[%head.%d, %main.%d]
456 builder
->CreatePHI(PointerType::getUnqual(Int8Ty
), 1, headreg
);
457 phi_1
->addIncoming(head_0
, testbb
);
464 //End of the program, so go to return block
465 builder
->CreateBr(endbb
);
468 std::cerr
<< "Error: Missing ']'\n";