Skip to content

Commit 731f07e

Browse files
committed
variables, assignments, conditionals
1 parent c9e2fba commit 731f07e

File tree

4 files changed

+127
-40
lines changed

4 files changed

+127
-40
lines changed

compiler/llvm_backend.py

Lines changed: 116 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88
import llvmlite.ir as ir
99
import llvmlite.binding as llvm
1010

11-
12-
int8_t = ir.IntType(8) # for booleans
11+
bool_t = ir.IntType(1) # for booleans
12+
int8_t = ir.IntType(8) # chars
1313
int32_t = ir.IntType(32) # for ints
1414
voidptr_t = ir.IntType(8).as_pointer()
1515

1616

1717
class LlvmBackend(Visitor):
1818
locals = []
1919
counter = 0
20+
globals = {}
21+
externs = {}
2022

2123
def __init__(self, ts: TypeSystem):
2224
llvm.initialize()
@@ -45,17 +47,54 @@ def newLocal(self):
4547
# TOP LEVEL & DECLARATIONS
4648

4749
def Program(self, node: Program):
50+
# globals for commonly used strings
51+
self.globals = {
52+
'true': self.global_constant('true',
53+
ir.ArrayType(int8_t, 5),
54+
self.make_bytearray('True\00'.encode('ascii'))),
55+
'false': self.global_constant('false',
56+
ir.ArrayType(int8_t, 6),
57+
self.make_bytearray('False\00'.encode('ascii'))),
58+
'fmt_i': self.global_constant('fmt_i',
59+
ir.ArrayType(int8_t, 4),
60+
self.make_bytearray('%i\n\00'.encode('ascii'))),
61+
'fmt_s': self.global_constant('fmt_s',
62+
ir.ArrayType(int8_t, 4),
63+
self.make_bytearray('%s\n\00'.encode('ascii'))),
64+
'fmt_assert': self.global_constant('fmt_assert',
65+
ir.ArrayType(int8_t, 29),
66+
self.make_bytearray('Assertion failed on line %i\n\00'.encode('ascii')))
67+
}
68+
69+
printf_t = ir.FunctionType(int32_t, [voidptr_t], True)
70+
self.externs['printf'] = ir.Function(self.module, printf_t, 'printf')
71+
setjmp_t = ir.FunctionType(int32_t, [int32_t], True)
72+
self.externs['setjmp'] = ir.Function(self.module, setjmp_t, 'setjmp')
73+
# TODO make this the right type
74+
longjmp_t = ir.FunctionType(int32_t, [int32_t], True)
75+
self.externs['longjmp'] = ir.Function(
76+
self.module, longjmp_t, 'longjmp')
77+
4878
funcType = ir.FunctionType(ir.VoidType(), [])
4979
func = ir.Function(self.module, funcType, "__main__")
80+
5081
self.enterScope()
51-
bb_entry = func.append_basic_block('entry')
52-
self.builder = ir.IRBuilder(bb_entry)
82+
entry_block = func.append_basic_block('entry')
83+
self.builder = ir.IRBuilder(entry_block)
84+
self.visitStmtList(
85+
[d for d in node.declarations if isinstance(d, VarDef)])
5386
self.visitStmtList(node.statements)
5487
self.builder.ret_void()
5588
self.exitScope()
5689

5790
def VarDef(self, node: VarDef):
58-
pass
91+
val = self.visit(node.value)
92+
saved_block = self.builder.block
93+
addr = self.create_entry_block_alloca(
94+
node.getName(), node.var.t.getLLVMType())
95+
self.builder.position_at_end(saved_block)
96+
self.builder.store(val, addr)
97+
self.locals[-1][node.getName()] = addr
5998

6099
def ClassDef(self, node: ClassDef):
61100
pass
@@ -82,14 +121,18 @@ def FuncDef(self, node: FuncDef):
82121

83122
# STATEMENTS
84123

85-
def NonLocalDecl(self, node: NonLocalDecl):
86-
pass
87-
88-
def GlobalDecl(self, node: GlobalDecl):
89-
pass
90-
91124
def AssignStmt(self, node: AssignStmt):
92-
pass
125+
val = self.visit(node.value)
126+
for var in node.targets[::-1]:
127+
if isinstance(var, MemberExpr):
128+
raise Exception("unimplemented")
129+
elif isinstance(var, IndexExpr):
130+
raise Exception("unimplemented")
131+
elif isinstance(var, Identifier):
132+
addr = self.locals[-1][var.name]
133+
self.builder.store(val, addr)
134+
else:
135+
raise Exception("Illegal assignment")
93136

94137
def IfStmt(self, node: IfStmt):
95138
pass
@@ -164,7 +207,7 @@ def UnaryExpr(self, node: UnaryExpr):
164207
val = self.visit(node.operand)
165208
return self.builder.neg(val)
166209
elif node.operator == "not":
167-
false = ir.Constant(int8_t, 0)
210+
false = ir.Constant(bool_t, 0)
168211
val = self.visit(node.operand)
169212
return self.builder.icmp_unsigned('==', false, val)
170213

@@ -195,33 +238,63 @@ def ReturnStmt(self, node: ReturnStmt):
195238

196239
def Identifier(self, node: Identifier):
197240
addr = self.locals[-1][node.name]
241+
assert addr is not None
198242
return self.builder.load(addr, node.name)
199243

200244
def MemberExpr(self, node: MemberExpr):
201245
pass
202246

203247
def IfExpr(self, node: IfExpr):
204-
pass
248+
return self.ifHelper(lambda: self.visit(node.condition),
249+
lambda: self.visit(node.thenExpr),
250+
lambda: self.visit(node.elseExpr),
251+
node.inferredType.getLLVMType())
252+
253+
def ifHelper(self, condFn, thenFn, elseFn, t):
254+
cond = condFn()
255+
256+
then_block = self.builder.append_basic_block()
257+
else_block = self.builder.append_basic_block()
258+
merge_block = self.builder.append_basic_block()
259+
self.builder.cbranch(cond, then_block, else_block)
260+
261+
self.builder.position_at_start(then_block)
262+
then_val = thenFn()
263+
self.builder.branch(merge_block)
264+
then_block = self.builder.block
265+
266+
self.builder.position_at_start(else_block)
267+
else_val = elseFn()
268+
self.builder.branch(merge_block)
269+
else_block = self.builder.block
270+
271+
self.builder.position_at_start(merge_block)
272+
273+
phi = self.builder.phi(t)
274+
phi.add_incoming(then_val, then_block)
275+
phi.add_incoming(else_val, else_block)
276+
return phi
205277

206278
def MethodCallExpr(self, node: MethodCallExpr):
207279
pass
208280

209281
# LITERALS
210282

211283
def BooleanLiteral(self, node: BooleanLiteral):
212-
return ir.Constant(int8_t, 1 if node.value else 0)
284+
return ir.Constant(bool_t, 1 if node.value else 0)
213285

214286
def IntegerLiteral(self, node: IntegerLiteral):
215287
return ir.Constant(int32_t, node.value)
216288

217289
def NoneLiteral(self, node: NoneLiteral):
218-
return ir.Constant(int32_t, 0)
290+
return ir.Constant(ir.PointerType(None), None)
219291

220292
def StringLiteral(self, node: StringLiteral):
221293
const = self.make_bytearray((node.value + '\00').encode('ascii'))
222-
alloca = self.builder.alloca(ir.ArrayType(int8_t, len(node.value) + 1), name=self.newLocal())
294+
alloca = self.builder.alloca(ir.ArrayType(
295+
int8_t, len(node.value) + 1), name=self.newLocal())
223296
self.builder.store(const, alloca)
224-
return alloca
297+
return self.builder.bitcast(alloca, voidptr_t)
225298

226299
# TYPES
227300

@@ -238,13 +311,18 @@ def ClassType(self, node: ClassType):
238311

239312
def emit_print(self, arg: Expr):
240313
if isinstance(arg.inferredType, ListValueType) or arg.inferredType.className not in {"bool", "int", "str"}:
241-
raise Exception("unsupported")
314+
raise Exception("Only bool, int, or str may be printed")
242315
if arg.inferredType.className == "bool":
243-
raise Exception("TODO")
316+
text = self.ifHelper(
317+
lambda: arg,
318+
lambda: self.builder.bitcast(self.globals['true'], voidptr_t),
319+
lambda: self.builder.bitcast(self.globals['false'], voidptr_t),
320+
voidptr_t)
321+
return self.printf(self.globals['fmt_s'], True, text)
244322
elif arg.inferredType.className == 'int':
245-
return self.printf("%i\n", False, self.visit(arg))
323+
return self.printf(self.globals['fmt_i'], False, self.visit(arg))
246324
else:
247-
return self.printf("%s\n", True, self.visit(arg))
325+
return self.printf(self.globals['fmt_s'], True, self.visit(arg))
248326

249327
# UTILS
250328

@@ -259,16 +337,19 @@ def is_null(self, value):
259337
def is_nonnull(self, value):
260338
return self.builder.icmp_unsigned('!=', value.type(None), value)
261339

262-
def printf(self, format: str, cast: bool, arg):
263-
func_t = ir.FunctionType(int32_t, [voidptr_t], True)
264-
fmt_bytes = self.make_bytearray((format + '\00').encode('ascii'))
265-
alloca = self.builder.alloca(ir.ArrayType(int8_t, 4), name=self.newLocal())
266-
self.builder.store(fmt_bytes, alloca)
267-
try:
268-
fn = self.module.get_global('printf')
269-
except KeyError:
270-
fn = ir.Function(self.module, func_t, 'printf')
271-
fmt_ptr = self.builder.bitcast(alloca, voidptr_t)
272-
if cast:
273-
arg = self.builder.bitcast(arg, voidptr_t)
274-
return self.builder.call(fn, [fmt_ptr, arg])
340+
def printf(self, format, cast: bool, arg):
341+
fmt_ptr = self.builder.bitcast(format, voidptr_t)
342+
return self.builder.call(self.externs['printf'], [fmt_ptr, arg])
343+
344+
def create_entry_block_alloca(self, name, t):
345+
builder = ir.IRBuilder()
346+
builder.position_at_start(self.builder.function.entry_basic_block)
347+
return builder.alloca(t, size=None, name=name)
348+
349+
def global_constant(self, name, t, value):
350+
module = self.module
351+
data = ir.GlobalVariable(module, t, name, 0)
352+
data.linkage = 'internal'
353+
data.global_constant = True
354+
data.initializer = value
355+
return data

compiler/types/classvaluetype.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,13 @@ def toJSON(self, dump_location=True) -> dict:
130130

131131
def getLLVMType(self) -> ir.Type:
132132
if self.className == SpecialClass.BOOL:
133-
return ir.IntType(8)
133+
return ir.IntType(1)
134134
elif self.className == SpecialClass.STR:
135-
raise ir.IntType(8).as_pointer()
135+
return ir.IntType(8).as_pointer()
136136
elif self.className == SpecialClass.OBJECT:
137137
raise Exception("unsupported")
138138
elif self.className == SpecialClass.NONE:
139-
raise ir.VoidType()
139+
return ir.VoidType()
140140
elif self.className == SpecialClass.EMPTY:
141141
raise Exception("unsupported")
142142
elif self.className == SpecialClass.INT:

foobar.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
print(-5 % -2)
1+
x: str = "1"
2+
y: str = "0"
3+
print(x)
4+
print(y)
5+
x = y = "2"
6+
print(x)
7+
print(y)

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
'jvm - output JVM bytecode formatted for the Krakatau assembler\n' +
1414
'cil - output CIL bytecode formatted for the Mono ilasm assembler\n' +
1515
'wasm - output WASM in WAT format\n' +
16-
'llvm - output LLVM\n'
16+
'llvm - output LLVM IR\n'
1717
)
1818

1919

0 commit comments

Comments
 (0)