29a30
> from __future__ import with_statement
316,323c317,318
< if isinstance(exec_output, dict):
< self.exec_output = exec_output
< elif isinstance(exec_output, str):
< # If the exec_output arg is a single string, we replicate
< # it for each of the CPU models, substituting and
< # %(CPU_foo)s params appropriately.
< self.exec_output = parser.expandCpuSymbolsToDict(exec_output)
< self.decode_block = parser.expandCpuSymbolsToString(decode_block)
---
> self.exec_output = exec_output
> self.decode_block = decode_block
325a321,332
> # Write these code chunks out to the filesystem. They will be properly
> # interwoven by the write_top_level_files().
> def emit(self):
> if self.header_output:
> self.parser.get_file('header').write(self.header_output)
> if self.decoder_output:
> self.parser.get_file('decoder').write(self.decoder_output)
> if self.exec_output:
> self.parser.get_file('exec').write(self.exec_output)
> if self.decode_block:
> self.parser.get_file('decode_block').write(self.decode_block)
>
329,332d335
< exec_output = {}
< for cpu in self.parser.cpuModels:
< n = cpu.name
< exec_output[n] = self.exec_output[n] + other.exec_output[n]
336c339
< exec_output,
---
> self.exec_output + other.exec_output,
345,346c348
< for cpu in self.parser.cpuModels:
< self.exec_output[cpu.name] = pre + self.exec_output[cpu.name]
---
> self.exec_output = pre + self.exec_output
1174c1176,1177
< # Output file template
---
> # ISA Parser
> # parses ISA DSL and emits C++ headers and source
1177,1213d1179
< file_template = '''
< /*
< * DO NOT EDIT THIS FILE!!!
< *
< * It was automatically generated from the ISA description in %(filename)s
< */
<
< %(includes)s
<
< %(global_output)s
<
< namespace %(namespace)s {
<
< %(namespace_output)s
<
< } // namespace %(namespace)s
<
< %(decode_function)s
< '''
<
< max_inst_regs_template = '''
< /*
< * DO NOT EDIT THIS FILE!!!
< *
< * It was automatically generated from the ISA description in %(filename)s
< */
<
< namespace %(namespace)s {
<
< const int MaxInstSrcRegs = %(MaxInstSrcRegs)d;
< const int MaxInstDestRegs = %(MaxInstDestRegs)d;
< const int MaxMiscDestRegs = %(MaxMiscDestRegs)d;
<
< } // namespace %(namespace)s
<
< '''
<
1218a1185,1186
> self.filename = None # for output file watermarking/scaremongering
>
1226a1195,1204
> # Track open files and, if applicable, how many chunks it has been
> # split into so far.
> self.files = {}
> self.splits = {}
>
> # isa_name / namespace identifier from namespace declaration.
> # before the namespace declaration, None.
> self.isa_name = None
> self.namespace = None
>
1245a1224,1398
> def __getitem__(self, i): # Allow object (self) to be
> return getattr(self, i) # passed to %-substitutions
>
> # Change the file suffix of a base filename:
> # (e.g.) decoder.cc -> decoder-g.cc.inc for 'global' outputs
> def suffixize(self, s, sec):
> extn = re.compile('(\.[^\.]+)$') # isolate extension
> if self.namespace:
> return extn.sub(r'-ns\1.inc', s) # insert some text on either side
> else:
> return extn.sub(r'-g\1.inc', s)
>
> # Get the file object for emitting code into the specified section
> # (header, decoder, exec, decode_block).
> def get_file(self, section):
> if section == 'decode_block':
> filename = 'decode-method.cc.inc'
> else:
> if section == 'header':
> file = 'decoder.hh'
> else:
> file = '%s.cc' % section
> filename = self.suffixize(file, section)
> try:
> return self.files[filename]
> except KeyError: pass
>
> f = self.open(filename)
> self.files[filename] = f
>
> # The splittable files are the ones with many independent
> # per-instruction functions - the decoder's instruction constructors
> # and the instruction execution (execute()) methods. These both have
> # the suffix -ns.cc.inc, meaning they are within the namespace part
> # of the ISA, contain object-emitting C++ source, and are included
> # into other top-level files. These are the files that need special
> # #define's to allow parts of them to be compiled separately. Rather
> # than splitting the emissions into separate files, the monolithic
> # output of the ISA parser is maintained, but the value (or lack
> # thereof) of the __SPLIT definition during C preprocessing will
> # select the different chunks. If no 'split' directives are used,
> # the cpp emissions have no effect.
> if re.search('-ns.cc.inc$', filename):
> print >>f, '#if !defined(__SPLIT) || (__SPLIT == 1)'
> self.splits[f] = 1
> # ensure requisite #include's
> elif filename in ['decoder-g.cc.inc', 'exec-g.cc.inc']:
> print >>f, '#include "decoder.hh"'
> elif filename == 'decoder-g.hh.inc':
> print >>f, '#include "base/bitfield.hh"'
>
> return f
>
> # Weave together the parts of the different output sections by
> # #include'ing them into some very short top-level .cc/.hh files.
> # These small files make it much clearer how this tool works, since
> # you directly see the chunks emitted as files that are #include'd.
> def write_top_level_files(self):
> dep = self.open('inc.d', bare=True)
>
> # decoder header - everything depends on this
> file = 'decoder.hh'
> with self.open(file) as f:
> inc = []
>
> fn = 'decoder-g.hh.inc'
> assert(fn in self.files)
> f.write('#include "%s"\n' % fn)
> inc.append(fn)
>
> fn = 'decoder-ns.hh.inc'
> assert(fn in self.files)
> f.write('namespace %s {\n#include "%s"\n}\n'
> % (self.namespace, fn))
> inc.append(fn)
>
> print >>dep, file+':', ' '.join(inc)
>
> # decoder method - cannot be split
> file = 'decoder.cc'
> with self.open(file) as f:
> inc = []
>
> fn = 'decoder-g.cc.inc'
> assert(fn in self.files)
> f.write('#include "%s"\n' % fn)
> inc.append(fn)
>
> fn = 'decode-method.cc.inc'
> # is guaranteed to have been written for parse to complete
> f.write('#include "%s"\n' % fn)
> inc.append(fn)
>
> inc.append("decoder.hh")
> print >>dep, file+':', ' '.join(inc)
>
> extn = re.compile('(\.[^\.]+)$')
>
> # instruction constructors
> splits = self.splits[self.get_file('decoder')]
> file_ = 'inst-constrs.cc'
> for i in range(1, splits+1):
> if splits > 1:
> file = extn.sub(r'-%d\1' % i, file_)
> else:
> file = file_
> with self.open(file) as f:
> inc = []
>
> fn = 'decoder-g.cc.inc'
> assert(fn in self.files)
> f.write('#include "%s"\n' % fn)
> inc.append(fn)
>
> fn = 'decoder-ns.cc.inc'
> assert(fn in self.files)
> print >>f, 'namespace %s {' % self.namespace
> if splits > 1:
> print >>f, '#define __SPLIT %u' % i
> print >>f, '#include "%s"' % fn
> print >>f, '}'
> inc.append(fn)
>
> inc.append("decoder.hh")
> print >>dep, file+':', ' '.join(inc)
>
> # instruction execution per-CPU model
> splits = self.splits[self.get_file('exec')]
> for cpu in self.cpuModels:
> for i in range(1, splits+1):
> if splits > 1:
> file = extn.sub(r'_%d\1' % i, cpu.filename)
> else:
> file = cpu.filename
> with self.open(file) as f:
> inc = []
>
> fn = 'exec-g.cc.inc'
> assert(fn in self.files)
> f.write('#include "%s"\n' % fn)
> inc.append(fn)
>
> f.write(cpu.includes+"\n")
>
> fn = 'exec-ns.cc.inc'
> assert(fn in self.files)
> print >>f, 'namespace %s {' % self.namespace
> print >>f, '#define CPU_EXEC_CONTEXT %s' \
> % cpu.strings['CPU_exec_context']
> if splits > 1:
> print >>f, '#define __SPLIT %u' % i
> print >>f, '#include "%s"' % fn
> print >>f, '}'
> inc.append(fn)
>
> inc.append("decoder.hh")
> print >>dep, file+':', ' '.join(inc)
>
> # max_inst_regs.hh
> self.update('max_inst_regs.hh',
> '''namespace %(namespace)s {
> const int MaxInstSrcRegs = %(maxInstSrcRegs)d;
> const int MaxInstDestRegs = %(maxInstDestRegs)d;
> const int MaxMiscDestRegs = %(maxMiscDestRegs)d;\n}\n''' % self)
> print >>dep, 'max_inst_regs.hh:'
>
> dep.close()
>
>
> scaremonger_template ='''// DO NOT EDIT
> // This file was automatically generated from an ISA description:
> // %(filename)s
>
> ''';
>
1267c1420
< 'OUTPUT', 'SIGNED', 'TEMPLATE'
---
> 'OUTPUT', 'SIGNED', 'SPLIT', 'TEMPLATE'
1420,1435c1573
< 'specification : opt_defs_and_outputs name_decl opt_defs_and_outputs decode_block'
< global_code = t[1]
< isa_name = t[2]
< namespace = isa_name + "Inst"
< # wrap the decode block as a function definition
< t[4].wrap_decode_block('''
< StaticInstPtr
< %(isa_name)s::Decoder::decodeInst(%(isa_name)s::ExtMachInst machInst)
< {
< using namespace %(namespace)s;
< ''' % vars(), '}')
< # both the latter output blocks and the decode block are in
< # the namespace
< namespace_code = t[3] + t[4]
< # pass it all back to the caller of yacc.parse()
< t[0] = (isa_name, namespace, global_code, namespace_code)
---
> 'specification : opt_defs_and_outputs top_level_decode_block'
1437,1440c1575,1576
< # ISA name declaration looks like "namespace <foo>;"
< def p_name_decl(self, t):
< 'name_decl : NAMESPACE ID SEMI'
< t[0] = t[2]
---
> for f in self.splits.iterkeys():
> f.write('\n#endif\n')
1442,1443c1578,1589
< # 'opt_defs_and_outputs' is a possibly empty sequence of
< # def and/or output statements.
---
> for f in self.files.itervalues(): # close ALL the files;
> f.close() # not doing so can cause compilation to fail
>
> self.write_top_level_files()
>
> t[0] = True
>
> # 'opt_defs_and_outputs' is a possibly empty sequence of def and/or
> # output statements. Its productions do the hard work of eventually
> # instantiating a GenCode, which are generally emitted (written to disk)
> # as soon as possible, except for the decode_block, which has to be
> # accumulated into one large function of nested switch/case blocks.
1446d1591
< t[0] = GenCode(self)
1450d1594
< t[0] = t[1]
1454d1597
< t[0] = t[1]
1458d1600
< t[0] = t[1] + t[2]
1460a1603
> # They are all processed as they are seen.
1462c1605,1606
< '''def_or_output : def_format
---
> '''def_or_output : name_decl
> | def_format
1468,1471c1612,1639
< | output_header
< | output_decoder
< | output_exec
< | global_let'''
---
> | output
> | global_let
> | split'''
>
> # Utility function used by both invocations of splitting - explicit
> # 'split' keyword and split() function inside "let {{ }};" blocks.
> def split(self, sec, write=False):
> assert(sec != 'header' and "header cannot be split")
>
> f = self.get_file(sec)
> self.splits[f] += 1
> s = '\n#endif\n#if __SPLIT == %u\n' % self.splits[f]
> if write:
> f.write(s)
> else:
> return s
>
> # split output file to reduce compilation time
> def p_split(self, t):
> 'split : SPLIT output_type SEMI'
> assert(self.isa_name and "'split' not allowed before namespace decl")
>
> self.split(t[2], True)
>
> def p_output_type(self, t):
> '''output_type : DECODER
> | HEADER
> | EXEC'''
1473a1642,1648
> # ISA name declaration looks like "namespace <foo>;"
> def p_name_decl(self, t):
> 'name_decl : NAMESPACE ID SEMI'
> assert(self.isa_name == None and "Only 1 namespace decl permitted")
> self.isa_name = t[2]
> self.namespace = t[2] + 'Inst'
>
1488,1490c1663,1666
< def p_output_header(self, t):
< 'output_header : OUTPUT HEADER CODELIT SEMI'
< t[0] = GenCode(self, header_output = self.process_output(t[3]))
---
> def p_output(self, t):
> 'output : OUTPUT output_type CODELIT SEMI'
> kwargs = { t[2]+'_output' : self.process_output(t[3]) }
> GenCode(self, **kwargs).emit()
1492,1499d1667
< def p_output_decoder(self, t):
< 'output_decoder : OUTPUT DECODER CODELIT SEMI'
< t[0] = GenCode(self, decoder_output = self.process_output(t[3]))
<
< def p_output_exec(self, t):
< 'output_exec : OUTPUT EXEC CODELIT SEMI'
< t[0] = GenCode(self, exec_output = self.process_output(t[3]))
<
1505a1674,1675
> def _split(sec):
> return self.split(sec)
1510a1681,1696
> self.exportContext["split"] = _split
> split_setup = '''
> def wrap(func):
> def split(sec):
> globals()[sec + '_output'] += func(sec)
> return split
> split = wrap(split)
> del wrap
> '''
> # This tricky setup (immediately above) allows us to just write
> # (e.g.) "split('exec')" in the Python code and the split #ifdef's
> # will automatically be added to the exec_output variable. The inner
> # Python execution environment doesn't know about the split points,
> # so we carefully inject and wrap a closure that can retrieve the
> # next split's #define from the parser and add it to the current
> # emission-in-progress.
1512c1698
< exec fixPythonIndentation(t[2]) in self.exportContext
---
> exec split_setup+fixPythonIndentation(t[2]) in self.exportContext
1517,1521c1703,1707
< t[0] = GenCode(self,
< header_output=self.exportContext["header_output"],
< decoder_output=self.exportContext["decoder_output"],
< exec_output=self.exportContext["exec_output"],
< decode_block=self.exportContext["decode_block"])
---
> GenCode(self,
> header_output=self.exportContext["header_output"],
> decoder_output=self.exportContext["decoder_output"],
> exec_output=self.exportContext["exec_output"],
> decode_block=self.exportContext["decode_block"]).emit()
1534d1719
< t[0] = GenCode(self) # contributes nothing to the output C++ file
1549d1733
< t[0] = GenCode(self) # contributes nothing to the output C++ file
1560c1744
< t[0] = GenCode(self, header_output=hash_define)
---
> GenCode(self, header_output=hash_define).emit()
1569c1753
< t[0] = GenCode(self, header_output=hash_define)
---
> GenCode(self, header_output=hash_define).emit()
1578c1762
< t[0] = GenCode(self, header_output=hash_define)
---
> GenCode(self, header_output=hash_define).emit()
1597a1782,1783
> if t[3] in self.templateMap:
> print "warning: template %s already defined" % t[3]
1599d1784
< t[0] = GenCode(self)
1607d1791
< t[0] = GenCode(self)
1677a1862,1873
> def p_top_level_decode_block(self, t):
> 'top_level_decode_block : decode_block'
> codeObj = t[1]
> codeObj.wrap_decode_block('''
> StaticInstPtr
> %(isa_name)s::Decoder::decodeInst(%(isa_name)s::ExtMachInst machInst)
> {
> using namespace %(namespace)s;
> ''' % self, '}')
>
> codeObj.emit()
>
2091a2288,2296
> def open(self, name, bare=False):
> '''Open the output file for writing and include scary warning.'''
> filename = os.path.join(self.output_dir, name)
> f = open(filename, 'w')
> if f:
> if not bare:
> f.write(ISAParser.scaremonger_template % self)
> return f
>
2093,2096c2298,2300
< '''Update the output file. If the contents are unchanged,
< the scons hash feature will avoid recompilation.'''
< file = os.path.join(self.output_dir, file)
< f = open(file, 'w')
---
> '''Update the output file only. Scons should handle the case when
> the new contents are unchanged using its built-in hash feature.'''
> f = self.open(file)
2135a2340,2341
> AlreadyGenerated = {}
>
2138a2345,2358
> # The build system can end up running the ISA parser twice: once to
> # finalize the build dependencies, and then to actually generate
> # the files it expects (in src/arch/$ARCH/generated). This code
> # doesn't do anything different either time, however; the SCons
> # invocations just expect different things. Since this code runs
> # within SCons, we can just remember that we've already run and
> # not perform a completely unnecessary run, since the ISA parser's
> # effect is idempotent.
> if isa_desc_file in ISAParser.AlreadyGenerated:
> return
>
> # grab the last three path components of isa_desc_file
> self.filename = '/'.join(isa_desc_file.split('/')[-3:])
>
2147,2149c2367,2368
< # Parse it.
< (isa_name, namespace, global_code, namespace_code) = \
< self.parse_string(isa_desc)
---
> # Parse.
> self.parse_string(isa_desc)
2151,2153c2370
< # grab the last three path components of isa_desc_file to put in
< # the output
< filename = '/'.join(isa_desc_file.split('/')[-3:])
---
> ISAParser.AlreadyGenerated[isa_desc_file] = None
2155,2188d2371
< # generate decoder.hh
< includes = '#include "base/bitfield.hh" // for bitfield support'
< global_output = global_code.header_output
< namespace_output = namespace_code.header_output
< decode_function = ''
< self.update('decoder.hh', file_template % vars())
<
< # generate decoder.cc
< includes = '#include "decoder.hh"'
< global_output = global_code.decoder_output
< namespace_output = namespace_code.decoder_output
< # namespace_output += namespace_code.decode_block
< decode_function = namespace_code.decode_block
< self.update('decoder.cc', file_template % vars())
<
< # generate per-cpu exec files
< for cpu in self.cpuModels:
< includes = '#include "decoder.hh"\n'
< includes += cpu.includes
< global_output = global_code.exec_output[cpu.name]
< namespace_output = namespace_code.exec_output[cpu.name]
< decode_function = ''
< self.update(cpu.filename, file_template % vars())
<
< # The variable names here are hacky, but this will creat local
< # variables which will be referenced in vars() which have the
< # value of the globals.
< MaxInstSrcRegs = self.maxInstSrcRegs
< MaxInstDestRegs = self.maxInstDestRegs
< MaxMiscDestRegs = self.maxMiscDestRegs
< # max_inst_regs.hh
< self.update('max_inst_regs.hh',
< max_inst_regs_template % vars())
<