llvm--emulator/stepper.py
2017-10-29 19:15:27 +01:00

232 lines
6.3 KiB
Python

import ll
import parser
def TODO(msg):
print('TODO, not implemented yet at {}'
.format(msg))
def err(msg):
print('ERROR: {}'
.format(msg))
def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, memory,
tdecs, fdecs, call_res):
if len(insns) == 0:
return terminate(terminator, blocks, stack_frames, ssa_env, global_env, memory)
ssa_target, next_insn = insns[0]
insns_rest = insns[1:]
# TODO
print('Evaluating {}'
.format(ll.insn2s(next_insn)))
res = None
if isinstance(next_insn, ll.Binop):
bop = next_insn.bop
left = next_insn.left
right = next_insn.right
left_v = eval_oper(left, ssa_env, global_env)
right_v = eval_oper(right, ssa_env, global_env)
res = eval_binop(bop, left_v, right_v)
# TODO
print('{} {}, {}'
.format(bop, left_v, right_v))
elif isinstance(next_insn, ll.Icmp):
cnd = next_insn.cnd
left = next_insn.left
right = next_insn.right
left_v = eval_oper(left, ssa_env, global_env)
right_v = eval_oper(right, ssa_env, global_env)
res = eval_icmp(cnd, left_v, right_v)
# TODO
print('icmp {} {}, {}'
.format(cnd, left_v, right_v))
else:
err('Unknown LLVM instruction: {}'
.format(next_insn))
if ssa_target is not None:
if ssa_target in ssa_env:
err('Cannot assign to variable twice: {}'
.format(ssa_target))
else:
# TODO
print('%{} <- {}'
.format(ssa_target, res))
ssa_env[ssa_target] = res
return insns_rest, terminator, blocks, stack_frames, ssa_env, memory, None
def terminate(terminator, blocks, stack_frames, ssa_env, global_env, memory):
def clear_block_from_ssa_env(insns, ssa_env):
for (id, insn) in insns:
if id is not None and id in ssa_env:
del ssa_env[id]
print('Evaluating {}'
.format(ll.terminator2s(terminator)))
if isinstance(terminator, ll.Ret):
oper = terminator.oper
if oper is None:
oper_v = None
else:
oper_v = eval_oper(oper, ssa_env, global_env)
# TODO
print('Returning {}'
.format(oper_v))
if len(stack_frames) == 0:
new_insns = []
new_terminator = None
new_blocks = {}
new_ssa_env = ssa_env
new_stack_frames = []
else:
new_insns, new_terminator, new_blocks, new_ssa_env = stack_frames[0]
new_stack_frames = stack_frames[1:]
return (new_insns, new_terminator, new_blocks, new_stack_frames,
new_ssa_env, memory, oper_v)
elif isinstance(terminator, ll.Br):
label = terminator.label
next_block = blocks[label]
new_insns = next_block.insns
new_terminator = next_block.terminator
# TODO: Might need to find a better solution as we will ignore
# multiple assignments, if they are spread over multiple
# blocks.
clear_block_from_ssa_env(new_insns, ssa_env)
# TODO
print('Jumping unconditionally to {}'
.format(label))
return (new_insns, new_terminator, blocks, stack_frames,
ssa_env, memory, None)
else:
err('Unknown LLVM terminator: {}'
.format(terminator))
def eval_oper(operand, ssa_env, global_env):
if isinstance(operand, ll.Null):
return 0
elif isinstance(operand, ll.Const):
return operand.val
elif isinstance(operand, ll.Gid):
TODO('eval_oper Gid')
elif isinstance(operand, ll.Id):
id = operand.val
try:
return ssa_env[id]
except KeyError:
err('Unable to find %{} in environment:\n{}'
.format(id, ssa_env))
def eval_binop(bop, left, right):
if bop == 'add':
return left + right
elif bop == 'sub':
return left - right
elif bop == 'mul':
return left * right
elif bop == 'sdiv':
return left // right
elif bop == 'shl':
return left << right
elif bop == 'ashr':
return left >> right
elif bop == 'lshr':
return (left >> right) % 0x10000000000000000
elif bop == 'and':
return left & right
elif bop == 'or':
return left | right
elif bop == 'xor':
return left ^ right
else:
err('Unknown LLVM Binary operator: {}'
.format(bop))
def eval_icmp(cnd, left, right):
if cnd == 'eq':
return left == right
elif cnd == 'ne':
return left != right
elif cnd == 'slt':
return left < right
elif cnd == 'sle':
return left <= right
elif cnd == 'sgt':
return left > right
elif cnd == 'sge':
return left >= right
else:
err('eval_icmp: Unknown cnd: {}'
.format(cnd))
return 0
def gogo():
p = parser.LLVMParser()
p.build()
data = r'''
define i64 @tigermain (i64 %U_mainSL_8, i64 %U_mainDummy_9) {
%a = add i64 3, 5 ; please be 8
br label %L1
L1:
%b = add i64 %a, %a
%c = icmp eq i64 %a, %b
ret i1 %c
L2:
%d = add i64 %a, 1
%e = add i64 10, %b
ret i64 %e
}
'''
print(data)
ast = p.parse(data)
tdecs = ast.tdecls
fdecs = ast.fdecls
global_env = ast.gdecls
tigermain = ast.fdecls['tigermain']
first_block = tigermain.body.first_block
blocks = tigermain.body.named_blocks
insns = first_block.insns
terminator = first_block.terminator
stack_frames = []
ssa_env = {}
# TODO: memory structure has not been decided yet
memory = [None]
call_res = None
while True:
(insns, terminator, blocks,
stack_frames, ssa_env, memory, call_res) = step(insns, terminator, blocks,
stack_frames, ssa_env,
global_env, memory, tdecs,
fdecs, call_res)
if terminator is None:
print('Stepping done! Final ssa_env:\n{}'
.format(ssa_env))
print('Program resulted in {}'.
format(call_res))
break
if __name__ == '__main__':
gogo()