PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/translator/c/genc.py

https://bitbucket.org/kcr/pypy
Python | 823 lines | 747 code | 44 blank | 32 comment | 60 complexity | 0c9c93e7150b4e4124221d12e9332682 MD5 | raw file
Possible License(s): Apache-2.0
  1. import contextlib
  2. import py
  3. import sys, os
  4. from rpython.rlib import exports
  5. from rpython.rtyper.typesystem import getfunctionptr
  6. from rpython.tool import runsubprocess
  7. from rpython.tool.nullpath import NullPyPathLocal
  8. from rpython.tool.udir import udir
  9. from rpython.translator.c import gc
  10. from rpython.translator.c.database import LowLevelDatabase
  11. from rpython.translator.c.extfunc import pre_include_code_lines
  12. from rpython.translator.c.support import log
  13. from rpython.translator.gensupp import uniquemodulename, NameManager
  14. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  15. _CYGWIN = sys.platform == 'cygwin'
  16. _CPYTHON_RE = py.std.re.compile('^Python 2.[567]')
  17. def get_recent_cpython_executable():
  18. if sys.platform == 'win32':
  19. python = sys.executable.replace('\\', '/')
  20. else:
  21. python = sys.executable
  22. # Is there a command 'python' that runs python 2.5-2.7?
  23. # If there is, then we can use it instead of sys.executable
  24. returncode, stdout, stderr = runsubprocess.run_subprocess(
  25. "python", "-V")
  26. if _CPYTHON_RE.match(stdout) or _CPYTHON_RE.match(stderr):
  27. python = 'python'
  28. return python
  29. class ProfOpt(object):
  30. #XXX assuming gcc style flags for now
  31. name = "profopt"
  32. def __init__(self, compiler):
  33. self.compiler = compiler
  34. def first(self):
  35. platform = self.compiler.platform
  36. if platform.name.startswith('darwin'):
  37. # XXX incredible hack for darwin
  38. STR = '/*--no-profiling-for-this-file!--*/'
  39. no_prof = []
  40. prof = []
  41. for cfile in self.compiler.cfiles:
  42. if STR in cfile.read():
  43. no_prof.append(cfile)
  44. else:
  45. prof.append(cfile)
  46. p_eci = self.compiler.eci.merge(
  47. ExternalCompilationInfo(compile_extra=['-fprofile-generate'],
  48. link_extra=['-fprofile-generate']))
  49. ofiles = platform._compile_o_files(prof, p_eci)
  50. _, eci = self.compiler.eci.get_module_files()
  51. ofiles += platform._compile_o_files(no_prof, eci)
  52. return platform._finish_linking(ofiles, p_eci, None, True)
  53. else:
  54. return self.build('-fprofile-generate')
  55. def probe(self, exe, args):
  56. # 'args' is a single string typically containing spaces
  57. # and quotes, which represents several arguments.
  58. self.compiler.platform.execute(exe, args)
  59. def after(self):
  60. return self.build('-fprofile-use')
  61. def build(self, option):
  62. eci = ExternalCompilationInfo(compile_extra=[option],
  63. link_extra=[option])
  64. return self.compiler._build(eci)
  65. class CCompilerDriver(object):
  66. def __init__(self, platform, cfiles, eci, outputfilename=None,
  67. profbased=False):
  68. # XXX config might contain additional link and compile options.
  69. # We need to fish for it somehow.
  70. self.platform = platform
  71. self.cfiles = cfiles
  72. self.eci = eci
  73. self.outputfilename = outputfilename
  74. self.profbased = profbased
  75. def _build(self, eci=ExternalCompilationInfo(), shared=False):
  76. outputfilename = self.outputfilename
  77. if shared:
  78. if outputfilename:
  79. basename = outputfilename
  80. else:
  81. basename = self.cfiles[0].purebasename
  82. outputfilename = 'lib' + basename
  83. return self.platform.compile(self.cfiles, self.eci.merge(eci),
  84. outputfilename=outputfilename,
  85. standalone=not shared)
  86. def build(self, shared=False):
  87. if self.profbased:
  88. return self._do_profbased()
  89. return self._build(shared=shared)
  90. def _do_profbased(self):
  91. ProfDriver, args = self.profbased
  92. profdrv = ProfDriver(self)
  93. dolog = getattr(log, profdrv.name)
  94. dolog(args)
  95. exename = profdrv.first()
  96. dolog('Gathering profile data from: %s %s' % (
  97. str(exename), args))
  98. profdrv.probe(exename, args)
  99. return profdrv.after()
  100. class CBuilder(object):
  101. c_source_filename = None
  102. _compiled = False
  103. modulename = None
  104. split = False
  105. def __init__(self, translator, entrypoint, config, gcpolicy=None,
  106. secondary_entrypoints=()):
  107. self.translator = translator
  108. self.entrypoint = entrypoint
  109. self.entrypoint_name = getattr(self.entrypoint, 'func_name', None)
  110. self.originalentrypoint = entrypoint
  111. self.config = config
  112. self.gcpolicy = gcpolicy # for tests only, e.g. rpython/memory/
  113. self.eci = self.get_eci()
  114. self.secondary_entrypoints = secondary_entrypoints
  115. def get_eci(self):
  116. pypy_include_dir = py.path.local(__file__).join('..')
  117. include_dirs = [pypy_include_dir]
  118. return ExternalCompilationInfo(include_dirs=include_dirs)
  119. def build_database(self):
  120. translator = self.translator
  121. gcpolicyclass = self.get_gcpolicyclass()
  122. if self.config.translation.gcrootfinder == "asmgcc":
  123. if not self.standalone:
  124. raise NotImplementedError("--gcrootfinder=asmgcc requires standalone")
  125. db = LowLevelDatabase(translator, standalone=self.standalone,
  126. gcpolicyclass=gcpolicyclass,
  127. thread_enabled=self.config.translation.thread,
  128. sandbox=self.config.translation.sandbox)
  129. self.db = db
  130. # give the gc a chance to register interest in the start-up functions it
  131. # need (we call this for its side-effects of db.get())
  132. list(db.gcpolicy.gc_startup_code())
  133. # build entrypoint and eventually other things to expose
  134. pf = self.getentrypointptr()
  135. if isinstance(pf, list):
  136. for one_pf in pf:
  137. db.get(one_pf)
  138. self.c_entrypoint_name = None
  139. else:
  140. pfname = db.get(pf)
  141. for func, _ in self.secondary_entrypoints:
  142. bk = translator.annotator.bookkeeper
  143. db.get(getfunctionptr(bk.getdesc(func).getuniquegraph()))
  144. self.c_entrypoint_name = pfname
  145. for obj in exports.EXPORTS_obj2name.keys():
  146. db.getcontainernode(obj)
  147. exports.clear()
  148. db.complete()
  149. self.collect_compilation_info(db)
  150. return db
  151. have___thread = None
  152. def merge_eci(self, *ecis):
  153. self.eci = self.eci.merge(*ecis)
  154. def collect_compilation_info(self, db):
  155. # we need a concrete gcpolicy to do this
  156. self.merge_eci(db.gcpolicy.compilation_info())
  157. all = []
  158. for node in self.db.globalcontainers():
  159. eci = node.compilation_info()
  160. if eci:
  161. all.append(eci)
  162. for node in self.db.getstructdeflist():
  163. try:
  164. all.append(node.STRUCT._hints['eci'])
  165. except (AttributeError, KeyError):
  166. pass
  167. self.merge_eci(*all)
  168. def get_gcpolicyclass(self):
  169. if self.gcpolicy is None:
  170. name = self.config.translation.gctransformer
  171. if name == "framework":
  172. name = "%s+%s" % (name, self.config.translation.gcrootfinder)
  173. return gc.name_to_gcpolicy[name]
  174. return self.gcpolicy
  175. # use generate_source(defines=DEBUG_DEFINES) to force the #definition
  176. # of the macros that enable debugging assertions
  177. DEBUG_DEFINES = {'RPY_ASSERT': 1,
  178. 'RPY_LL_ASSERT': 1}
  179. def generate_graphs_for_llinterp(self, db=None):
  180. # prepare the graphs as when the source is generated, but without
  181. # actually generating the source.
  182. if db is None:
  183. db = self.build_database()
  184. graphs = db.all_graphs()
  185. db.gctransformer.prepare_inline_helpers(graphs)
  186. for node in db.containerlist:
  187. if hasattr(node, 'funcgens'):
  188. for funcgen in node.funcgens:
  189. funcgen.patch_graph(copy_graph=False)
  190. return db
  191. def generate_source(self, db=None, defines={}, exe_name=None):
  192. assert self.c_source_filename is None
  193. if db is None:
  194. db = self.build_database()
  195. pf = self.getentrypointptr()
  196. if self.modulename is None:
  197. self.modulename = uniquemodulename('testing')
  198. modulename = self.modulename
  199. targetdir = udir.ensure(modulename, dir=1)
  200. if self.config.translation.dont_write_c_files:
  201. targetdir = NullPyPathLocal(targetdir)
  202. self.targetdir = targetdir
  203. defines = defines.copy()
  204. if self.config.translation.countmallocs:
  205. defines['COUNT_OP_MALLOCS'] = 1
  206. if self.config.translation.sandbox:
  207. defines['RPY_SANDBOXED'] = 1
  208. if CBuilder.have___thread is None:
  209. CBuilder.have___thread = self.translator.platform.check___thread()
  210. if not self.standalone:
  211. assert not self.config.translation.instrument
  212. else:
  213. defines['PYPY_STANDALONE'] = db.get(pf)
  214. if self.config.translation.instrument:
  215. defines['PYPY_INSTRUMENT'] = 1
  216. if CBuilder.have___thread:
  217. if not self.config.translation.no__thread:
  218. defines['USE___THREAD'] = 1
  219. if self.config.translation.shared:
  220. defines['PYPY_MAIN_FUNCTION'] = "pypy_main_startup"
  221. self.eci = self.eci.merge(ExternalCompilationInfo(
  222. export_symbols=["pypy_main_startup"]))
  223. self.eci, cfile, extra = gen_source(db, modulename, targetdir,
  224. self.eci, defines=defines,
  225. split=self.split)
  226. self.c_source_filename = py.path.local(cfile)
  227. self.extrafiles = self.eventually_copy(extra)
  228. self.gen_makefile(targetdir, exe_name=exe_name)
  229. return cfile
  230. def eventually_copy(self, cfiles):
  231. extrafiles = []
  232. for fn in cfiles:
  233. fn = py.path.local(fn)
  234. if not fn.relto(udir):
  235. newname = self.targetdir.join(fn.basename)
  236. fn.copy(newname)
  237. fn = newname
  238. extrafiles.append(fn)
  239. return extrafiles
  240. class CStandaloneBuilder(CBuilder):
  241. standalone = True
  242. split = True
  243. executable_name = None
  244. shared_library_name = None
  245. def getprofbased(self):
  246. profbased = None
  247. if self.config.translation.instrumentctl is not None:
  248. profbased = self.config.translation.instrumentctl
  249. else:
  250. # xxx handling config.translation.profopt is a bit messy, because
  251. # it could be an empty string (not to be confused with None) and
  252. # because noprofopt can be used as an override.
  253. profopt = self.config.translation.profopt
  254. if profopt is not None and not self.config.translation.noprofopt:
  255. profbased = (ProfOpt, profopt)
  256. return profbased
  257. def has_profopt(self):
  258. profbased = self.getprofbased()
  259. return (profbased and isinstance(profbased, tuple)
  260. and profbased[0] is ProfOpt)
  261. def getentrypointptr(self):
  262. # XXX check that the entrypoint has the correct
  263. # signature: list-of-strings -> int
  264. bk = self.translator.annotator.bookkeeper
  265. return getfunctionptr(bk.getdesc(self.entrypoint).getuniquegraph())
  266. def cmdexec(self, args='', env=None, err=False, expect_crash=False):
  267. assert self._compiled
  268. res = self.translator.platform.execute(self.executable_name, args,
  269. env=env)
  270. if res.returncode != 0:
  271. if expect_crash:
  272. return res.out, res.err
  273. print >> sys.stderr, res.err
  274. raise Exception("Returned %d" % (res.returncode,))
  275. if expect_crash:
  276. raise Exception("Program did not crash!")
  277. if err:
  278. return res.out, res.err
  279. return res.out
  280. def build_main_for_shared(self, shared_library_name, entrypoint, exe_name):
  281. import time
  282. time.sleep(1)
  283. self.shared_library_name = shared_library_name
  284. # build main program
  285. eci = self.get_eci()
  286. kw = {}
  287. if self.translator.platform.cc == 'gcc':
  288. kw['libraries'] = [self.shared_library_name.purebasename[3:]]
  289. kw['library_dirs'] = [self.targetdir]
  290. else:
  291. kw['libraries'] = [self.shared_library_name.new(ext='')]
  292. eci = eci.merge(ExternalCompilationInfo(
  293. separate_module_sources=['''
  294. int %s(int argc, char* argv[]);
  295. int main(int argc, char* argv[])
  296. { return %s(argc, argv); }
  297. ''' % (entrypoint, entrypoint)
  298. ],
  299. **kw
  300. ))
  301. eci = eci.convert_sources_to_files(
  302. cache_dir=self.targetdir)
  303. return self.translator.platform.compile(
  304. [], eci,
  305. outputfilename=exe_name)
  306. def compile(self, exe_name=None):
  307. assert self.c_source_filename
  308. assert not self._compiled
  309. shared = self.config.translation.shared
  310. extra_opts = []
  311. if self.config.translation.make_jobs != 1:
  312. extra_opts += ['-j', str(self.config.translation.make_jobs)]
  313. if self.config.translation.lldebug:
  314. extra_opts += ["lldebug"]
  315. self.translator.platform.execute_makefile(self.targetdir,
  316. extra_opts)
  317. if shared:
  318. self.shared_library_name = self.executable_name.new(
  319. purebasename='lib' + self.executable_name.purebasename,
  320. ext=self.translator.platform.so_ext)
  321. self._compiled = True
  322. return self.executable_name
  323. def gen_makefile(self, targetdir, exe_name=None):
  324. cfiles = [self.c_source_filename] + self.extrafiles
  325. if exe_name is not None:
  326. exe_name = targetdir.join(exe_name)
  327. mk = self.translator.platform.gen_makefile(
  328. cfiles, self.eci,
  329. path=targetdir, exe_name=exe_name,
  330. shared=self.config.translation.shared)
  331. if self.has_profopt():
  332. profopt = self.config.translation.profopt
  333. mk.definition('ABS_TARGET', '$(shell python -c "import sys,os; print os.path.abspath(sys.argv[1])" $(TARGET))')
  334. mk.definition('DEFAULT_TARGET', 'profopt')
  335. mk.definition('PROFOPT', profopt)
  336. rules = [
  337. ('clean', '', 'rm -f $(OBJECTS) $(TARGET) $(GCMAPFILES) $(ASMFILES) *.gc?? ../module_cache/*.gc??'),
  338. ('clean_noprof', '', 'rm -f $(OBJECTS) $(TARGET) $(GCMAPFILES) $(ASMFILES)'),
  339. ('debug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT" debug_target'),
  340. ('debug_exc', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DDO_LOG_EXC" debug_target'),
  341. ('debug_mem', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPYPY_USE_TRIVIAL_MALLOC" debug_target'),
  342. ('no_obmalloc', '', '$(MAKE) CFLAGS="-g -O2 -DRPY_ASSERT -DPYPY_NO_OBMALLOC" $(TARGET)'),
  343. ('linuxmemchk', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPPY_USE_LINUXMEMCHK" debug_target'),
  344. ('llsafer', '', '$(MAKE) CFLAGS="-O2 -DRPY_LL_ASSERT" $(TARGET)'),
  345. ('lldebug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'),
  346. ('profile', '', '$(MAKE) CFLAGS="-g -O1 -pg $(CFLAGS) -fno-omit-frame-pointer" LDFLAGS="-pg $(LDFLAGS)" $(TARGET)'),
  347. ]
  348. if self.has_profopt():
  349. rules.append(
  350. ('profopt', '', [
  351. '$(MAKENOPROF)',
  352. '$(MAKE) CFLAGS="-fprofile-generate $(CFLAGS)" LDFLAGS="-fprofile-generate $(LDFLAGS)" $(TARGET)',
  353. 'cd $(RPYDIR)/translator/goal && $(ABS_TARGET) $(PROFOPT)',
  354. '$(MAKE) clean_noprof',
  355. '$(MAKE) CFLAGS="-fprofile-use $(CFLAGS)" LDFLAGS="-fprofile-use $(LDFLAGS)" $(TARGET)']))
  356. for rule in rules:
  357. mk.rule(*rule)
  358. #XXX: this conditional part is not tested at all
  359. if self.config.translation.gcrootfinder == 'asmgcc':
  360. trackgcfiles = [cfile[:cfile.rfind('.')] for cfile in mk.cfiles]
  361. if self.translator.platform.name == 'msvc':
  362. trackgcfiles = [f for f in trackgcfiles
  363. if f.startswith(('implement', 'testing',
  364. '../module_cache/module'))]
  365. sfiles = ['%s.s' % (c,) for c in trackgcfiles]
  366. lblsfiles = ['%s.lbl.s' % (c,) for c in trackgcfiles]
  367. gcmapfiles = ['%s.gcmap' % (c,) for c in trackgcfiles]
  368. mk.definition('ASMFILES', sfiles)
  369. mk.definition('ASMLBLFILES', lblsfiles)
  370. mk.definition('GCMAPFILES', gcmapfiles)
  371. if sys.platform == 'win32':
  372. mk.definition('DEBUGFLAGS', '/MD /Zi')
  373. else:
  374. mk.definition('DEBUGFLAGS', '-O2 -fomit-frame-pointer -g')
  375. if self.config.translation.shared:
  376. mk.definition('PYPY_MAIN_FUNCTION', "pypy_main_startup")
  377. else:
  378. mk.definition('PYPY_MAIN_FUNCTION', "main")
  379. mk.definition('PYTHON', get_recent_cpython_executable())
  380. if self.translator.platform.name == 'msvc':
  381. lblofiles = []
  382. for cfile in mk.cfiles:
  383. f = cfile[:cfile.rfind('.')]
  384. if f in trackgcfiles:
  385. ofile = '%s.lbl.obj' % (f,)
  386. else:
  387. ofile = '%s.obj' % (f,)
  388. lblofiles.append(ofile)
  389. mk.definition('ASMLBLOBJFILES', lblofiles)
  390. mk.definition('OBJECTS', 'gcmaptable.obj $(ASMLBLOBJFILES)')
  391. # /Oi (enable intrinsics) and /Ob1 (some inlining) are mandatory
  392. # even in debug builds
  393. mk.definition('ASM_CFLAGS', '$(CFLAGS) $(CFLAGSEXTRA) /Oi /Ob1')
  394. mk.rule('.SUFFIXES', '.s', [])
  395. mk.rule('.s.obj', '',
  396. 'cmd /c $(MASM) /nologo /Cx /Cp /Zm /coff /Fo$@ /c $< $(INCLUDEDIRS)')
  397. mk.rule('.c.gcmap', '',
  398. ['$(CC) /nologo $(ASM_CFLAGS) /c /FAs /Fa$*.s $< $(INCLUDEDIRS)',
  399. 'cmd /c $(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py -fmsvc -t $*.s > $@']
  400. )
  401. mk.rule('gcmaptable.c', '$(GCMAPFILES)',
  402. 'cmd /c $(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py -fmsvc $(GCMAPFILES) > $@')
  403. else:
  404. mk.definition('OBJECTS', '$(ASMLBLFILES) gcmaptable.s')
  405. mk.rule('%.s', '%.c', '$(CC) $(CFLAGS) $(CFLAGSEXTRA) -frandom-seed=$< -o $@ -S $< $(INCLUDEDIRS)')
  406. mk.rule('%.lbl.s %.gcmap', '%.s',
  407. [
  408. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  409. '-t $< > $*.gctmp',
  410. 'mv $*.gctmp $*.gcmap'])
  411. mk.rule('gcmaptable.s', '$(GCMAPFILES)',
  412. [
  413. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  414. '$(GCMAPFILES) > $@.tmp',
  415. 'mv $@.tmp $@'])
  416. mk.rule('.PRECIOUS', '%.s', "# don't remove .s files if Ctrl-C'ed")
  417. else:
  418. if sys.platform == 'win32':
  419. mk.definition('DEBUGFLAGS', '/MD /Zi')
  420. else:
  421. mk.definition('DEBUGFLAGS', '-O1 -g')
  422. if sys.platform == 'win32':
  423. mk.rule('debug_target', 'debugmode_$(DEFAULT_TARGET)', 'rem')
  424. else:
  425. mk.rule('debug_target', '$(TARGET)', '#')
  426. mk.write()
  427. #self.translator.platform,
  428. # ,
  429. # self.eci, profbased=self.getprofbased()
  430. self.executable_name = mk.exe_name
  431. # ____________________________________________________________
  432. SPLIT_CRITERIA = 65535 # support VC++ 7.2
  433. #SPLIT_CRITERIA = 32767 # enable to support VC++ 6.0
  434. MARKER = '/*/*/' # provide an easy way to split after generating
  435. class SourceGenerator:
  436. one_source_file = True
  437. def __init__(self, database):
  438. self.database = database
  439. self.extrafiles = []
  440. self.path = None
  441. self.namespace = NameManager()
  442. def set_strategy(self, path, split=True):
  443. all_nodes = list(self.database.globalcontainers())
  444. # split off non-function nodes. We don't try to optimize these, yet.
  445. funcnodes = []
  446. othernodes = []
  447. for node in all_nodes:
  448. if node.nodekind == 'func':
  449. funcnodes.append(node)
  450. else:
  451. othernodes.append(node)
  452. if split:
  453. self.one_source_file = False
  454. self.funcnodes = funcnodes
  455. self.othernodes = othernodes
  456. self.path = path
  457. def uniquecname(self, name):
  458. assert name.endswith('.c')
  459. return self.namespace.uniquename(name[:-2]) + '.c'
  460. def makefile(self, name):
  461. log.writing(name)
  462. filepath = self.path.join(name)
  463. if name.endswith('.c'):
  464. self.extrafiles.append(filepath)
  465. return filepath.open('w')
  466. def getextrafiles(self):
  467. return self.extrafiles
  468. def getothernodes(self):
  469. return self.othernodes[:]
  470. def getbasecfilefornode(self, node, basecname):
  471. # For FuncNode instances, use the python source filename (relative to
  472. # the top directory):
  473. def invent_nice_name(g):
  474. # Lookup the filename from the function.
  475. # However, not all FunctionGraph objs actually have a "func":
  476. if hasattr(g, 'func'):
  477. if g.filename.endswith('.py'):
  478. localpath = py.path.local(g.filename)
  479. pypkgpath = localpath.pypkgpath()
  480. if pypkgpath:
  481. relpypath = localpath.relto(pypkgpath.dirname)
  482. return relpypath.replace('.py', '.c')
  483. return None
  484. if hasattr(node.obj, 'graph'):
  485. # Regular RPython functions
  486. name = invent_nice_name(node.obj.graph)
  487. if name is not None:
  488. return name
  489. elif node._funccodegen_owner is not None:
  490. # Data nodes that belong to a known function
  491. graph = getattr(node._funccodegen_owner, 'graph', None)
  492. name = invent_nice_name(graph)
  493. if name is not None:
  494. return "data_" + name
  495. return basecname
  496. def splitnodesimpl(self, basecname, nodes, nextra, nbetween,
  497. split_criteria=SPLIT_CRITERIA):
  498. # Gather nodes by some criteria:
  499. nodes_by_base_cfile = {}
  500. for node in nodes:
  501. c_filename = self.getbasecfilefornode(node, basecname)
  502. if c_filename in nodes_by_base_cfile:
  503. nodes_by_base_cfile[c_filename].append(node)
  504. else:
  505. nodes_by_base_cfile[c_filename] = [node]
  506. # produce a sequence of nodes, grouped into files
  507. # which have no more than SPLIT_CRITERIA lines
  508. for basecname in sorted(nodes_by_base_cfile):
  509. iternodes = iter(nodes_by_base_cfile[basecname])
  510. done = [False]
  511. def subiter():
  512. used = nextra
  513. for node in iternodes:
  514. impl = '\n'.join(list(node.implementation())).split('\n')
  515. if not impl:
  516. continue
  517. cost = len(impl) + nbetween
  518. yield node, impl
  519. del impl
  520. if used + cost > split_criteria:
  521. # split if criteria met, unless we would produce nothing.
  522. raise StopIteration
  523. used += cost
  524. done[0] = True
  525. while not done[0]:
  526. yield self.uniquecname(basecname), subiter()
  527. @contextlib.contextmanager
  528. def write_on_included_file(self, f, name):
  529. fi = self.makefile(name)
  530. print >> f, '#include "%s"' % name
  531. yield fi
  532. fi.close()
  533. @contextlib.contextmanager
  534. def write_on_maybe_separate_source(self, f, name):
  535. print >> f, '/* %s */' % name
  536. if self.one_source_file:
  537. yield f
  538. else:
  539. fi = self.makefile(name)
  540. yield fi
  541. fi.close()
  542. def gen_readable_parts_of_source(self, f):
  543. split_criteria_big = SPLIT_CRITERIA
  544. if py.std.sys.platform != "win32":
  545. if self.database.gcpolicy.need_no_typeptr():
  546. pass # XXX gcc uses toooooons of memory???
  547. else:
  548. split_criteria_big = SPLIT_CRITERIA * 4
  549. #
  550. # All declarations
  551. #
  552. with self.write_on_included_file(f, 'structdef.h') as fi:
  553. gen_structdef(fi, self.database)
  554. with self.write_on_included_file(f, 'forwarddecl.h') as fi:
  555. gen_forwarddecl(fi, self.database)
  556. with self.write_on_included_file(f, 'preimpl.h') as fi:
  557. gen_preimpl(fi, self.database)
  558. #
  559. # Implementation of functions and global structures and arrays
  560. #
  561. print >> f
  562. print >> f, '/***********************************************************/'
  563. print >> f, '/*** Implementations ***/'
  564. print >> f
  565. print >> f, '#define PYPY_FILE_NAME "%s"' % os.path.basename(f.name)
  566. print >> f, '#include "src/g_include.h"'
  567. print >> f
  568. nextralines = 11 + 1
  569. for name, nodeiter in self.splitnodesimpl('nonfuncnodes.c',
  570. self.othernodes,
  571. nextralines, 1):
  572. with self.write_on_maybe_separate_source(f, name) as fc:
  573. if fc is not f:
  574. print >> fc, '/***********************************************************/'
  575. print >> fc, '/*** Non-function Implementations ***/'
  576. print >> fc
  577. print >> fc, '#include "common_header.h"'
  578. print >> fc, '#include "structdef.h"'
  579. print >> fc, '#include "forwarddecl.h"'
  580. print >> fc, '#include "preimpl.h"'
  581. print >> fc
  582. print >> fc, '#include "src/g_include.h"'
  583. print >> fc
  584. print >> fc, MARKER
  585. for node, impl in nodeiter:
  586. print >> fc, '\n'.join(impl)
  587. print >> fc, MARKER
  588. print >> fc, '/***********************************************************/'
  589. nextralines = 12
  590. for name, nodeiter in self.splitnodesimpl('implement.c',
  591. self.funcnodes,
  592. nextralines, 1,
  593. split_criteria_big):
  594. with self.write_on_maybe_separate_source(f, name) as fc:
  595. if fc is not f:
  596. print >> fc, '/***********************************************************/'
  597. print >> fc, '/*** Implementations ***/'
  598. print >> fc
  599. print >> fc, '#define PYPY_FILE_NAME "%s"' % name
  600. print >> fc, '#include "common_header.h"'
  601. print >> fc, '#include "structdef.h"'
  602. print >> fc, '#include "forwarddecl.h"'
  603. print >> fc, '#include "preimpl.h"'
  604. print >> fc, '#include "src/g_include.h"'
  605. print >> fc
  606. print >> fc, MARKER
  607. for node, impl in nodeiter:
  608. print >> fc, '\n'.join(impl)
  609. print >> fc, MARKER
  610. print >> fc, '/***********************************************************/'
  611. print >> f
  612. def gen_structdef(f, database):
  613. structdeflist = database.getstructdeflist()
  614. print >> f, '/***********************************************************/'
  615. print >> f, '/*** Structure definitions ***/'
  616. print >> f
  617. for node in structdeflist:
  618. if hasattr(node, 'forward_decl'):
  619. if node.forward_decl:
  620. print >> f, node.forward_decl
  621. elif node.name is not None:
  622. print >> f, '%s %s;' % (node.typetag, node.name)
  623. print >> f
  624. for node in structdeflist:
  625. for line in node.definition():
  626. print >> f, line
  627. def gen_forwarddecl(f, database):
  628. print >> f, '/***********************************************************/'
  629. print >> f, '/*** Forward declarations ***/'
  630. print >> f
  631. for node in database.globalcontainers():
  632. for line in node.forward_declaration():
  633. print >> f, line
  634. def gen_preimpl(f, database):
  635. if database.translator is None or database.translator.rtyper is None:
  636. return
  637. preimplementationlines = pre_include_code_lines(
  638. database, database.translator.rtyper)
  639. for line in preimplementationlines:
  640. print >> f, line
  641. def gen_startupcode(f, database):
  642. # generate the start-up code and put it into a function
  643. print >> f, 'char *RPython_StartupCode(void) {'
  644. print >> f, '\tchar *error = NULL;'
  645. for line in database.gcpolicy.gc_startup_code():
  646. print >> f,"\t" + line
  647. # put float infinities in global constants, we should not have so many of them for now to make
  648. # a table+loop preferable
  649. for dest, value in database.late_initializations:
  650. print >> f, "\t%s = %s;" % (dest, value)
  651. firsttime = True
  652. for node in database.containerlist:
  653. lines = list(node.startupcode())
  654. if lines:
  655. if firsttime:
  656. firsttime = False
  657. else:
  658. print >> f, '\tif (error) return error;'
  659. for line in lines:
  660. print >> f, '\t'+line
  661. print >> f, '\treturn error;'
  662. print >> f, '}'
  663. def commondefs(defines):
  664. from rpython.rlib.rarithmetic import LONG_BIT, LONGLONG_BIT
  665. defines['PYPY_LONG_BIT'] = LONG_BIT
  666. defines['PYPY_LONGLONG_BIT'] = LONGLONG_BIT
  667. def add_extra_files(eci):
  668. srcdir = py.path.local(__file__).join('..', 'src')
  669. files = [
  670. srcdir / 'entrypoint.c', # ifdef PYPY_STANDALONE
  671. srcdir / 'allocator.c', # ifdef PYPY_STANDALONE
  672. srcdir / 'mem.c',
  673. srcdir / 'exception.c',
  674. srcdir / 'rtyper.c', # ifdef HAVE_RTYPER
  675. srcdir / 'support.c',
  676. srcdir / 'profiling.c',
  677. srcdir / 'debug_print.c',
  678. srcdir / 'debug_traceback.c', # ifdef HAVE_RTYPER
  679. srcdir / 'asm.c',
  680. srcdir / 'instrument.c',
  681. srcdir / 'int.c',
  682. ]
  683. if _CYGWIN:
  684. files.append(srcdir / 'cygwin_wait.c')
  685. return eci.merge(ExternalCompilationInfo(separate_module_files=files))
  686. def gen_source(database, modulename, targetdir,
  687. eci, defines={}, split=False):
  688. if isinstance(targetdir, str):
  689. targetdir = py.path.local(targetdir)
  690. filename = targetdir.join(modulename + '.c')
  691. f = filename.open('w')
  692. incfilename = targetdir.join('common_header.h')
  693. fi = incfilename.open('w')
  694. #
  695. # Header
  696. #
  697. print >> f, '#include "common_header.h"'
  698. print >> f
  699. commondefs(defines)
  700. for key, value in defines.items():
  701. print >> fi, '#define %s %s' % (key, value)
  702. eci.write_c_header(fi)
  703. print >> fi, '#include "src/g_prerequisite.h"'
  704. fi.close()
  705. #
  706. # 1) All declarations
  707. # 2) Implementation of functions and global structures and arrays
  708. #
  709. sg = SourceGenerator(database)
  710. sg.set_strategy(targetdir, split)
  711. database.prepare_inline_helpers()
  712. sg.gen_readable_parts_of_source(f)
  713. gen_startupcode(f, database)
  714. f.close()
  715. if 'PYPY_INSTRUMENT' in defines:
  716. fi = incfilename.open('a')
  717. n = database.instrument_ncounter
  718. print >>fi, "#define PYPY_INSTRUMENT_NCOUNTER %d" % n
  719. fi.close()
  720. eci = add_extra_files(eci)
  721. eci = eci.convert_sources_to_files()
  722. files, eci = eci.get_module_files()
  723. return eci, filename, sg.getextrafiles() + list(files)