PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/translator/c/genc.py

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