# HG changeset patch # User Michael Pavone # Date 1549696198 28800 # Node ID e4fe5a450d054ec86e31cad154f1809ba2de720b # Parent 48a43dff4dc05747e88f5e0b6629d34e98e6568d Added option to CPU DSL to produce a threaded interpreter using computed goto diff -r 48a43dff4dc0 -r e4fe5a450d05 Makefile --- a/Makefile Thu Feb 07 09:43:25 2019 -0800 +++ b/Makefile Fri Feb 08 23:09:58 2019 -0800 @@ -302,7 +302,7 @@ $(CC) -o vos_prog_info vos_prog_info.o vos_program_module.o %.c : %.cpu - ./cpu_dsl.py $< > $@ + ./cpu_dsl.py -d goto $< > $@ %.o : %.S $(CC) -c -o $@ $< diff -r 48a43dff4dc0 -r e4fe5a450d05 cpu_dsl.py --- a/cpu_dsl.py Thu Feb 07 09:43:25 2019 -0800 +++ b/cpu_dsl.py Fri Feb 08 23:09:58 2019 -0800 @@ -130,7 +130,13 @@ self.newLocals = [] fieldVals,_ = self.getFieldVals(value) self.processOps(prog, fieldVals, output, otype, self.implementation) - begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context)\n{' + + if prog.dispatch == 'call': + begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context)\n{' + elif prog.dispatch == 'goto': + begin = '\n' + self.generateName(value) + ': {' + else: + raise Exception('Unsupported dispatch type ' + prog.dispatch) if prog.needFlagCoalesce: begin += prog.flags.coalesceFlags(prog, otype) if prog.needFlagDisperse: @@ -138,6 +144,8 @@ for var in self.newLocals: begin += '\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var) prog.popScope() + if prog.dispatch == 'goto': + output += prog.nextInstruction(otype) return begin + ''.join(output) + '\n}' def __str__(self): @@ -327,7 +335,12 @@ table = 'main' else: table = params[1] - return '\n\timpl_{tbl}[{op}](context);'.format(tbl = table, op = params[0]) + if prog.dispatch == 'call': + return '\n\timpl_{tbl}[{op}](context);'.format(tbl = table, op = params[0]) + elif prog.dispatch == 'goto': + return '\n\tgoto *impl_{tbl}[{op}];'.format(tbl = table, op = params[0]) + else: + raise Exception('Unsupported dispatch type ' + prog.dispatch) def _updateFlagsCImpl(prog, params, rawParams): autoUpdate, explicit = prog.flags.parseFlagUpdate(params[0]) @@ -1315,7 +1328,7 @@ hFile.write('\n') hFile.close() - def _buildTable(self, otype, table, body): + def _buildTable(self, otype, table, body, lateBody): pieces = [] opmap = [None] * (1 << self.opsize) bodymap = {} @@ -1333,34 +1346,61 @@ opmap[val] = inst.generateName(val) bodymap[val] = inst.generateBody(val, self, otype) - pieces.append('\nstatic impl_fun impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) - for inst in range(0, len(opmap)): - op = opmap[inst] - if op is None: - pieces.append('\n\tunimplemented,') - else: - pieces.append('\n\t' + op + ',') - body.append(bodymap[inst]) - pieces.append('\n};') + if self.dispatch == 'call': + pieces.append('\nstatic impl_fun impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) + for inst in range(0, len(opmap)): + op = opmap[inst] + if op is None: + pieces.append('\n\tunimplemented,') + else: + pieces.append('\n\t' + op + ',') + body.append(bodymap[inst]) + pieces.append('\n};') + elif self.dispatch == 'goto': + body.append('\n\tstatic void *impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) + for inst in range(0, len(opmap)): + op = opmap[inst] + if op is None: + body.append('\n\t\t&&unimplemented,') + else: + body.append('\n\t\t&&' + op + ',') + lateBody.append(bodymap[inst]) + body.append('\n\t};') + else: + raise Exception("unimplmeneted dispatch type " + self.dispatch) body.extend(pieces) + + def nextInstruction(self, otype): + output = [] + if self.dispatch == 'goto': + output.append('\n\tif (context->cycles >= target_cycle) { return; }') + self.meta = {} + self.temp = {} + self.subroutines[self.body].inline(self, [], output, otype, None) + return output def build(self, otype): body = [] pieces = [] for include in self.includes: body.append('#include "{0}"\n'.format(include)) - body.append('\nstatic void unimplemented({pre}context *context)'.format(pre = self.prefix)) - body.append('\n{') - body.append('\n\tfatal_error("Unimplemented instruction\\n");') - body.append('\n}\n') - body.append('\ntypedef void (*impl_fun)({pre}context *context);'.format(pre=self.prefix)) + if self.dispatch == 'call': + body.append('\nstatic void unimplemented({pre}context *context)'.format(pre = self.prefix)) + body.append('\n{') + body.append('\n\tfatal_error("Unimplemented instruction\\n");') + body.append('\n}\n') + body.append('\ntypedef void (*impl_fun)({pre}context *context);'.format(pre=self.prefix)) + for table in self.extra_tables: + body.append('\nstatic impl_fun impl_{name}[{sz}];'.format(name = table, sz=(1 << self.opsize))) + body.append('\nstatic impl_fun impl_main[{sz}];'.format(sz=(1 << self.opsize))) + elif self.dispatch == 'goto': + body.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type)) + body.append('\n{') + for table in self.extra_tables: - body.append('\nstatic impl_fun impl_{name}[{sz}];'.format(name = table, sz=(1 << self.opsize))) - body.append('\nstatic impl_fun impl_main[{sz}];'.format(sz=(1 << self.opsize))) - for table in self.extra_tables: - self._buildTable(otype, table, body) - self._buildTable(otype, 'main', body) - if self.body in self.subroutines: + self._buildTable(otype, table, body, pieces) + self._buildTable(otype, 'main', body, pieces) + if self.dispatch == 'call' and self.body in self.subroutines: pieces.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type)) pieces.append('\n{') pieces.append('\n\twhile (context->cycles < target_cycle)') @@ -1370,6 +1410,11 @@ self.subroutines[self.body].inline(self, [], pieces, otype, None) pieces.append('\n\t}') pieces.append('\n}') + elif self.dispatch == 'goto': + body += self.nextInstruction(otype) + pieces.append('\nunimplemented:') + pieces.append('\n\tfatal_error("Unimplemented instruction\\n");') + pieces.append('\n}') return ''.join(body) + ''.join(pieces) def checkBool(self, name): @@ -1489,7 +1534,8 @@ def getRootScope(self): return self.scopes[0] -def parse(f): +def parse(args): + f = args.source instructions = {} subroutines = {} registers = None @@ -1577,9 +1623,19 @@ print(errors) else: p = Program(registers, instructions, subroutines, info, flags) + p.dispatch = args.dispatch p.declares = declares p.booleans['dynarec'] = False p.booleans['interp'] = True + if args.define: + for define in args.define: + name,sep,val = define.partition('=') + name = name.strip() + val = val.strip() + if sep: + p.booleans[name] = bool(val) + else: + p.booleans[name] = True if 'header' in info: print('#include "{0}"'.format(info['header'][0])) @@ -1589,8 +1645,12 @@ print(p.build('c')) def main(argv): - f = open(argv[1]) - parse(f) + from argparse import ArgumentParser, FileType + argParser = ArgumentParser(description='CPU emulator DSL compiler') + argParser.add_argument('source', type=FileType('r')) + argParser.add_argument('-D', '--define', action='append') + argParser.add_argument('-d', '--dispatch', choices=('call', 'switch', 'goto'), default='call') + parse(argParser.parse_args(argv[1:])) if __name__ == '__main__': from sys import argv