Codegen for 'for' seems to be working
Better testing will be available when mutable variables are introduced.
This commit is contained in:
parent
0b061cbfd7
commit
29ebf869cb
1 changed files with 89 additions and 9 deletions
98
chapter5.py
98
chapter5.py
|
|
@ -524,6 +524,80 @@ class LLVMCodeGenerator(object):
|
||||||
phi.add_incoming(else_val, else_bb)
|
phi.add_incoming(else_val, else_bb)
|
||||||
return phi
|
return phi
|
||||||
|
|
||||||
|
def _codegen_ForExprAST(self, node):
|
||||||
|
# Output this as:
|
||||||
|
# ...
|
||||||
|
# start = startexpr
|
||||||
|
# goto loop
|
||||||
|
# loop:
|
||||||
|
# variable = phi [start, loopheader], [nextvariable, loopend]
|
||||||
|
# ...
|
||||||
|
# bodyexpr
|
||||||
|
# ...
|
||||||
|
# loopend:
|
||||||
|
# step = stepexpr
|
||||||
|
# nextvariable = variable + step
|
||||||
|
# endcond = endexpr
|
||||||
|
# br endcond, loop, endloop
|
||||||
|
# outloop:
|
||||||
|
|
||||||
|
# Emit the start expr first, without the variable in scope.
|
||||||
|
start_val = self._codegen(node.start_expr)
|
||||||
|
preheader_bb = self.builder.block
|
||||||
|
loop_bb = self.builder.function.append_basic_block('loop')
|
||||||
|
|
||||||
|
# Insert an explicit fall through from the current block to loop_bb
|
||||||
|
self.builder.branch(loop_bb)
|
||||||
|
self.builder.position_at_start(loop_bb)
|
||||||
|
|
||||||
|
# Start the PHI node with an entry for start
|
||||||
|
phi = self.builder.phi(ir.DoubleType(), node.id_name)
|
||||||
|
phi.add_incoming(start_val, preheader_bb)
|
||||||
|
|
||||||
|
# Within the loop, the variable is defined equal to the PHI node. If it
|
||||||
|
# shadows an existing variable, we have to restore it, so save it now.
|
||||||
|
oldval = self.func_symtab.get(node.id_name)
|
||||||
|
self.func_symtab[node.id_name] = phi
|
||||||
|
|
||||||
|
# Emit the body of the loop. This, like any other expr, can change the
|
||||||
|
# current BB. Note that we ignore the value computed by the body.
|
||||||
|
body_val = self._codegen(node.body)
|
||||||
|
|
||||||
|
if node.step_expr is None:
|
||||||
|
stepval = self.builder.constant(ir.DoubleType(), 1.0)
|
||||||
|
else:
|
||||||
|
stepval = self._codegen(node.step_expr)
|
||||||
|
nextvar = self.builder.fadd(phi, stepval, 'nextvar')
|
||||||
|
|
||||||
|
# Compute the end condition
|
||||||
|
endcond = self._codegen(node.end_expr)
|
||||||
|
cmp = self.builder.fcmp_ordered(
|
||||||
|
'!=', endcond, self.builder.constant(ir.DoubleType(), 0.0),
|
||||||
|
'loopcond')
|
||||||
|
|
||||||
|
# Create the 'after loop' block and insert it
|
||||||
|
loop_end_bb = self.builder.block
|
||||||
|
after_bb = self.builder.function.append_basic_block('afterloop')
|
||||||
|
|
||||||
|
# Insert the conditional branch into the end of loop_end_bb
|
||||||
|
self.builder.cbranch(cmp, loop_bb, after_bb)
|
||||||
|
|
||||||
|
# New code will be inserted into after_bb
|
||||||
|
self.builder.position_at_start(after_bb)
|
||||||
|
|
||||||
|
# Add a new entry to the PHI node for the backedge
|
||||||
|
phi.add_incoming(nextvar, loop_end_bb)
|
||||||
|
|
||||||
|
# Remove the loop variable from the symbol table; if it shadowed an
|
||||||
|
# existing variable, restore that.
|
||||||
|
if oldval is None:
|
||||||
|
del self.func_symtab[node.id_name]
|
||||||
|
else:
|
||||||
|
self.func_symtab[node.id_name] = oldval
|
||||||
|
|
||||||
|
# The 'for' expression always returns 0
|
||||||
|
return self.builder.constant(ir.DoubleType(), 0.0)
|
||||||
|
|
||||||
def _codegen_CallExprAST(self, node):
|
def _codegen_CallExprAST(self, node):
|
||||||
callee_func = self.module.globals.get(node.callee, None)
|
callee_func = self.module.globals.get(node.callee, None)
|
||||||
if callee_func is None or not isinstance(callee_func, ir.Function):
|
if callee_func is None or not isinstance(callee_func, ir.Function):
|
||||||
|
|
@ -668,14 +742,20 @@ class TestEvaluator(unittest.TestCase):
|
||||||
self.assertEqual(e.evaluate('foo(10, 2, 300)'), 4)
|
self.assertEqual(e.evaluate('foo(10, 2, 300)'), 4)
|
||||||
self.assertEqual(e.evaluate('foo(100, 2000, 30)'), 60)
|
self.assertEqual(e.evaluate('foo(100, 2000, 30)'), 60)
|
||||||
|
|
||||||
|
def test_for(self):
|
||||||
|
# For doesn't return anything, so just make sure evaluating it doesn't
|
||||||
|
# crash.
|
||||||
|
e = KaleidoscopeEvaluator()
|
||||||
|
e.evaluate('''
|
||||||
|
def foo(a b c)
|
||||||
|
if a < b
|
||||||
|
then for x = 1.0, x < b, c in x+a+c*b
|
||||||
|
else c * 2''')
|
||||||
|
self.assertEqual(e.evaluate('foo(1, 2, 3)'), 0)
|
||||||
|
self.assertEqual(e.evaluate('foo(3, 2, 30)'), 60)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#buf = 'def foo(a b) a * if a < b then if a < 3 then 10 else 3 + a else b'
|
kalei = KaleidoscopeEvaluator()
|
||||||
buf = 'def foo(a b) for x = 1.1, x < b, a in x - b'
|
kalei.evaluate('def foo(a b x) for x = 68, x < b, a in x+a')
|
||||||
print(Parser(buf).parse_toplevel().dump())
|
print(kalei.evaluate('foo(2, 79, 22)', optimize=True, llvmdump=True))
|
||||||
#kalei = KaleidoscopeEvaluator()
|
|
||||||
#print(kalei.evaluate(buf))
|
|
||||||
#print(kalei.evaluate('foo(12, 5)', optimize=True, llvmdump=True))
|
|
||||||
#print(kalei.evaluate('def foo(x) (1+2+x)*(x+(1+2))'))
|
|
||||||
#print(kalei.evaluate('foo(3)', optimize=True, llvmdump=True))
|
|
||||||
#print(kalei.evaluate('foo(adder(3, 3)*4)', optimize=True, llvmdump=True))
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue