feat(wpcarro/scratch): Proof-of-concept register VM
I heard that register VMs might be slightly faster than stack VMs, and then it occurred to me that I wouldn't know how to write a register VM if I tried. So I wrote one (sort of). Change-Id: I15309bca88f4b43f6e04957acedc90d9adf16673 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6902 Reviewed-by: wpcarro <wpcarro@gmail.com> Autosubmit: wpcarro <wpcarro@gmail.com> Tested-by: BuildkiteCI
This commit is contained in:
parent
8190046190
commit
1ab252470a
1 changed files with 161 additions and 0 deletions
161
users/wpcarro/scratch/compiler/register_vm.py
Normal file
161
users/wpcarro/scratch/compiler/register_vm.py
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
# Silly proof-of-concept register VM.
|
||||||
|
|
||||||
|
def compile_binary_op(op, ast):
|
||||||
|
result = []
|
||||||
|
for x in compile(ast[1]):
|
||||||
|
result.append(x)
|
||||||
|
result.append(PUSH_REG)
|
||||||
|
result.append(RES)
|
||||||
|
for x in compile(ast[2]):
|
||||||
|
result.append(x)
|
||||||
|
result.append(ASSIGN_REG_REG)
|
||||||
|
result.append(Y)
|
||||||
|
result.append(RES)
|
||||||
|
result.append(POP)
|
||||||
|
result.append(X)
|
||||||
|
result.append(op)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def compile(ast):
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if ast[0] == 'CONST':
|
||||||
|
result.append(ASSIGN_REG_LIT)
|
||||||
|
result.append(RES)
|
||||||
|
result.append(ast[1])
|
||||||
|
elif ast[0] == 'ADD':
|
||||||
|
result += compile_binary_op(ADD, ast)
|
||||||
|
elif ast[0] == 'SUB':
|
||||||
|
result += compile_binary_op(SUB, ast)
|
||||||
|
elif ast[0] == 'MUL':
|
||||||
|
result += compile_binary_op(MUL, ast)
|
||||||
|
elif ast[0] == 'DIV':
|
||||||
|
result += compile_binary_op(DIV, ast)
|
||||||
|
elif ast[0] == 'RETURN':
|
||||||
|
result.append(RETURN)
|
||||||
|
else:
|
||||||
|
raise Exception('Cannot compile unknown AST node: {}'.format(ast[0]))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# opcodes
|
||||||
|
ASSIGN_REG_LIT = 0x0
|
||||||
|
ASSIGN_REG_REG = 0x1
|
||||||
|
ADD = 0x2
|
||||||
|
SUB = 0x3
|
||||||
|
MUL = 0x4
|
||||||
|
DIV = 0x5
|
||||||
|
SWAP = 0x6
|
||||||
|
RETURN = 0x7
|
||||||
|
PUSH_REG = 0x8
|
||||||
|
POP = 0x9
|
||||||
|
|
||||||
|
# register indices
|
||||||
|
X = 0x0
|
||||||
|
Y = 0x1
|
||||||
|
RES = 0x2
|
||||||
|
|
||||||
|
registers = [0x0] * 8
|
||||||
|
stack = []
|
||||||
|
|
||||||
|
def reg_name(i):
|
||||||
|
if i == X: return 'x'
|
||||||
|
if i == Y: return 'x'
|
||||||
|
if i == RES: return 'res'
|
||||||
|
|
||||||
|
def print_instructions(xs):
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
while i < len(xs):
|
||||||
|
if xs[i] == ASSIGN_REG_LIT:
|
||||||
|
# print('ASSIGN_REG_LIT {} {}'.format(reg_name(xs[i + 1]), xs[i + 2]))
|
||||||
|
print('{} <- {}'.format(reg_name(xs[i + 1]), xs[i + 2]))
|
||||||
|
i += 3
|
||||||
|
elif xs[i] == ASSIGN_REG_REG:
|
||||||
|
# print('ASSIGN_REG_REG {} {}'.format(reg_name(xs[i + 1]), reg_name(xs[i + 2])))
|
||||||
|
print('{} <- ${}'.format(reg_name(xs[i + 1]), reg_name(xs[i + 2])))
|
||||||
|
i += 3
|
||||||
|
elif xs[i] == ADD:
|
||||||
|
print('add')
|
||||||
|
i += 1
|
||||||
|
elif xs[i] == SUB:
|
||||||
|
print('sub')
|
||||||
|
i += 1
|
||||||
|
elif xs[i] == MUL:
|
||||||
|
print('mul')
|
||||||
|
i += 1
|
||||||
|
elif xs[i] == DIV:
|
||||||
|
print('div')
|
||||||
|
i += 1
|
||||||
|
elif xs[i] == PUSH_REG:
|
||||||
|
print('push ${}'.format(reg_name(xs[i + 1])))
|
||||||
|
i += 2
|
||||||
|
elif xs[i] == POP:
|
||||||
|
print('{} <- pop'.format(reg_name(xs[i + 1])))
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
raise Exception('Cannot print instruction: {}'.format(xs[i]))
|
||||||
|
|
||||||
|
def eval(instructions):
|
||||||
|
print_instructions(instructions)
|
||||||
|
ip = 0
|
||||||
|
cont = True
|
||||||
|
while ip < len(instructions):
|
||||||
|
if instructions[ip] == ASSIGN_REG_LIT:
|
||||||
|
r = instructions[ip + 1]
|
||||||
|
x = instructions[ip + 2]
|
||||||
|
registers[r] = x
|
||||||
|
ip += 3
|
||||||
|
elif instructions[ip] == ASSIGN_REG_REG:
|
||||||
|
r_dst = instructions[ip + 1]
|
||||||
|
r_src = instructions[ip + 2]
|
||||||
|
registers[r_dst] = registers[r_src]
|
||||||
|
ip += 3
|
||||||
|
elif instructions[ip] == ADD:
|
||||||
|
registers[RES] = registers[X] + registers[Y]
|
||||||
|
ip += 1
|
||||||
|
elif instructions[ip] == MUL:
|
||||||
|
registers[RES] = registers[X] * registers[Y]
|
||||||
|
ip += 1
|
||||||
|
elif instructions[ip] == SUB:
|
||||||
|
registers[RES] = registers[X] - registers[Y]
|
||||||
|
ip += 1
|
||||||
|
elif instructions[ip] == MUL:
|
||||||
|
registers[RES] = registers[X] * registers[Y]
|
||||||
|
ip += 1
|
||||||
|
elif instructions[ip] == DIV:
|
||||||
|
registers[RES] = registers[X] / registers[Y]
|
||||||
|
ip += 1
|
||||||
|
elif instructions[ip] == SWAP:
|
||||||
|
r1 = instructions[ip + 1]
|
||||||
|
r2 = instructions[ip + 2]
|
||||||
|
registers[r1], registers[r2] = registers[r2], registers[r1]
|
||||||
|
ip += 3
|
||||||
|
elif instructions[ip] == RETURN:
|
||||||
|
ip += 1
|
||||||
|
cont = False
|
||||||
|
return registers[RES]
|
||||||
|
elif instructions[ip] == PUSH_REG:
|
||||||
|
src = instructions[ip + 1]
|
||||||
|
stack.append(registers[src])
|
||||||
|
ip += 2
|
||||||
|
elif instructions[ip] == POP:
|
||||||
|
dst = instructions[ip + 1]
|
||||||
|
registers[dst] = stack.pop()
|
||||||
|
ip += 2
|
||||||
|
else:
|
||||||
|
raise Exception('Cannot eval instruction: {}'.format(instructions[ip]))
|
||||||
|
return registers[RES]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ast = ['ADD',
|
||||||
|
['MUL',
|
||||||
|
['MUL', ['CONST', 2], ['CONST', 3]],
|
||||||
|
['DIV', ['CONST', 5], ['CONST', 5]]],
|
||||||
|
['ADD',
|
||||||
|
['SUB', ['CONST', 10], ['CONST', 1]],
|
||||||
|
['MUL', ['CONST', 2], ['CONST', 2]]]]
|
||||||
|
|
||||||
|
print('result: {}'.format(eval(compile(ast))))
|
||||||
|
|
||||||
|
main()
|
Loading…
Reference in a new issue