PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/translator/c/genc.py

https://bitbucket.org/kkris/pypy
Python | 821 lines | 745 code | 44 blank | 32 comment | 60 complexity | b8d1a0ddebde7c659e09bb0146d71b52 MD5 | raw file
  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. self.translator.platform.execute_makefile(self.targetdir,
  314. extra_opts)
  315. if shared:
  316. self.shared_library_name = self.executable_name.new(
  317. purebasename='lib' + self.executable_name.purebasename,
  318. ext=self.translator.platform.so_ext)
  319. self._compiled = True
  320. return self.executable_name
  321. def gen_makefile(self, targetdir, exe_name=None):
  322. cfiles = [self.c_source_filename] + self.extrafiles
  323. if exe_name is not None:
  324. exe_name = targetdir.join(exe_name)
  325. mk = self.translator.platform.gen_makefile(
  326. cfiles, self.eci,
  327. path=targetdir, exe_name=exe_name,
  328. shared=self.config.translation.shared)
  329. if self.has_profopt():
  330. profopt = self.config.translation.profopt
  331. mk.definition('ABS_TARGET', '$(shell python -c "import sys,os; print os.path.abspath(sys.argv[1])" $(TARGET))')
  332. mk.definition('DEFAULT_TARGET', 'profopt')
  333. mk.definition('PROFOPT', profopt)
  334. rules = [
  335. ('clean', '', 'rm -f $(OBJECTS) $(TARGET) $(GCMAPFILES) $(ASMFILES) *.gc?? ../module_cache/*.gc??'),
  336. ('clean_noprof', '', 'rm -f $(OBJECTS) $(TARGET) $(GCMAPFILES) $(ASMFILES)'),
  337. ('debug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT" debug_target'),
  338. ('debug_exc', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DDO_LOG_EXC" debug_target'),
  339. ('debug_mem', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPYPY_USE_TRIVIAL_MALLOC" debug_target'),
  340. ('no_obmalloc', '', '$(MAKE) CFLAGS="-g -O2 -DRPY_ASSERT -DPYPY_NO_OBMALLOC" $(TARGET)'),
  341. ('linuxmemchk', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPPY_USE_LINUXMEMCHK" debug_target'),
  342. ('llsafer', '', '$(MAKE) CFLAGS="-O2 -DRPY_LL_ASSERT" $(TARGET)'),
  343. ('lldebug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'),
  344. ('profile', '', '$(MAKE) CFLAGS="-g -O1 -pg $(CFLAGS) -fno-omit-frame-pointer" LDFLAGS="-pg $(LDFLAGS)" $(TARGET)'),
  345. ]
  346. if self.has_profopt():
  347. rules.append(
  348. ('profopt', '', [
  349. '$(MAKENOPROF)',
  350. '$(MAKE) CFLAGS="-fprofile-generate $(CFLAGS)" LDFLAGS="-fprofile-generate $(LDFLAGS)" $(TARGET)',
  351. 'cd $(RPYDIR)/translator/goal && $(ABS_TARGET) $(PROFOPT)',
  352. '$(MAKE) clean_noprof',
  353. '$(MAKE) CFLAGS="-fprofile-use $(CFLAGS)" LDFLAGS="-fprofile-use $(LDFLAGS)" $(TARGET)']))
  354. for rule in rules:
  355. mk.rule(*rule)
  356. #XXX: this conditional part is not tested at all
  357. if self.config.translation.gcrootfinder == 'asmgcc':
  358. trackgcfiles = [cfile[:cfile.rfind('.')] for cfile in mk.cfiles]
  359. if self.translator.platform.name == 'msvc':
  360. trackgcfiles = [f for f in trackgcfiles
  361. if f.startswith(('implement', 'testing',
  362. '../module_cache/module'))]
  363. sfiles = ['%s.s' % (c,) for c in trackgcfiles]
  364. lblsfiles = ['%s.lbl.s' % (c,) for c in trackgcfiles]
  365. gcmapfiles = ['%s.gcmap' % (c,) for c in trackgcfiles]
  366. mk.definition('ASMFILES', sfiles)
  367. mk.definition('ASMLBLFILES', lblsfiles)
  368. mk.definition('GCMAPFILES', gcmapfiles)
  369. if sys.platform == 'win32':
  370. mk.definition('DEBUGFLAGS', '/MD /Zi')
  371. else:
  372. mk.definition('DEBUGFLAGS', '-O2 -fomit-frame-pointer -g')
  373. if self.config.translation.shared:
  374. mk.definition('PYPY_MAIN_FUNCTION', "pypy_main_startup")
  375. else:
  376. mk.definition('PYPY_MAIN_FUNCTION', "main")
  377. mk.definition('PYTHON', get_recent_cpython_executable())
  378. if self.translator.platform.name == 'msvc':
  379. lblofiles = []
  380. for cfile in mk.cfiles:
  381. f = cfile[:cfile.rfind('.')]
  382. if f in trackgcfiles:
  383. ofile = '%s.lbl.obj' % (f,)
  384. else:
  385. ofile = '%s.obj' % (f,)
  386. lblofiles.append(ofile)
  387. mk.definition('ASMLBLOBJFILES', lblofiles)
  388. mk.definition('OBJECTS', 'gcmaptable.obj $(ASMLBLOBJFILES)')
  389. # /Oi (enable intrinsics) and /Ob1 (some inlining) are mandatory
  390. # even in debug builds
  391. mk.definition('ASM_CFLAGS', '$(CFLAGS) $(CFLAGSEXTRA) /Oi /Ob1')
  392. mk.rule('.SUFFIXES', '.s', [])
  393. mk.rule('.s.obj', '',
  394. 'cmd /c $(MASM) /nologo /Cx /Cp /Zm /coff /Fo$@ /c $< $(INCLUDEDIRS)')
  395. mk.rule('.c.gcmap', '',
  396. ['$(CC) /nologo $(ASM_CFLAGS) /c /FAs /Fa$*.s $< $(INCLUDEDIRS)',
  397. 'cmd /c $(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py -fmsvc -t $*.s > $@']
  398. )
  399. mk.rule('gcmaptable.c', '$(GCMAPFILES)',
  400. 'cmd /c $(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py -fmsvc $(GCMAPFILES) > $@')
  401. else:
  402. mk.definition('OBJECTS', '$(ASMLBLFILES) gcmaptable.s')
  403. mk.rule('%.s', '%.c', '$(CC) $(CFLAGS) $(CFLAGSEXTRA) -frandom-seed=$< -o $@ -S $< $(INCLUDEDIRS)')
  404. mk.rule('%.lbl.s %.gcmap', '%.s',
  405. [
  406. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  407. '-t $< > $*.gctmp',
  408. 'mv $*.gctmp $*.gcmap'])
  409. mk.rule('gcmaptable.s', '$(GCMAPFILES)',
  410. [
  411. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  412. '$(GCMAPFILES) > $@.tmp',
  413. 'mv $@.tmp $@'])
  414. mk.rule('.PRECIOUS', '%.s', "# don't remove .s files if Ctrl-C'ed")
  415. else:
  416. if sys.platform == 'win32':
  417. mk.definition('DEBUGFLAGS', '/MD /Zi')
  418. else:
  419. mk.definition('DEBUGFLAGS', '-O1 -g')
  420. if sys.platform == 'win32':
  421. mk.rule('debug_target', 'debugmode_$(DEFAULT_TARGET)', 'rem')
  422. else:
  423. mk.rule('debug_target', '$(TARGET)', '#')
  424. mk.write()
  425. #self.translator.platform,
  426. # ,
  427. # self.eci, profbased=self.getprofbased()
  428. self.executable_name = mk.exe_name
  429. # ____________________________________________________________
  430. SPLIT_CRITERIA = 65535 # support VC++ 7.2
  431. #SPLIT_CRITERIA = 32767 # enable to support VC++ 6.0
  432. MARKER = '/*/*/' # provide an easy way to split after generating
  433. class SourceGenerator:
  434. one_source_file = True
  435. def __init__(self, database):
  436. self.database = database
  437. self.extrafiles = []
  438. self.path = None
  439. self.namespace = NameManager()
  440. def set_strategy(self, path, split=True):
  441. all_nodes = list(self.database.globalcontainers())
  442. # split off non-function nodes. We don't try to optimize these, yet.
  443. funcnodes = []
  444. othernodes = []
  445. for node in all_nodes:
  446. if node.nodekind == 'func':
  447. funcnodes.append(node)
  448. else:
  449. othernodes.append(node)
  450. if split:
  451. self.one_source_file = False
  452. self.funcnodes = funcnodes
  453. self.othernodes = othernodes
  454. self.path = path
  455. def uniquecname(self, name):
  456. assert name.endswith('.c')
  457. return self.namespace.uniquename(name[:-2]) + '.c'
  458. def makefile(self, name):
  459. log.writing(name)
  460. filepath = self.path.join(name)
  461. if name.endswith('.c'):
  462. self.extrafiles.append(filepath)
  463. return filepath.open('w')
  464. def getextrafiles(self):
  465. return self.extrafiles
  466. def getothernodes(self):
  467. return self.othernodes[:]
  468. def getbasecfilefornode(self, node, basecname):
  469. # For FuncNode instances, use the python source filename (relative to
  470. # the top directory):
  471. def invent_nice_name(g):
  472. # Lookup the filename from the function.
  473. # However, not all FunctionGraph objs actually have a "func":
  474. if hasattr(g, 'func'):
  475. if g.filename.endswith('.py'):
  476. localpath = py.path.local(g.filename)
  477. pypkgpath = localpath.pypkgpath()
  478. if pypkgpath:
  479. relpypath = localpath.relto(pypkgpath.dirname)
  480. return relpypath.replace('.py', '.c')
  481. return None
  482. if hasattr(node.obj, 'graph'):
  483. # Regular RPython functions
  484. name = invent_nice_name(node.obj.graph)
  485. if name is not None:
  486. return name
  487. elif node._funccodegen_owner is not None:
  488. # Data nodes that belong to a known function
  489. graph = getattr(node._funccodegen_owner, 'graph', None)
  490. name = invent_nice_name(graph)
  491. if name is not None:
  492. return "data_" + name
  493. return basecname
  494. def splitnodesimpl(self, basecname, nodes, nextra, nbetween,
  495. split_criteria=SPLIT_CRITERIA):
  496. # Gather nodes by some criteria:
  497. nodes_by_base_cfile = {}
  498. for node in nodes:
  499. c_filename = self.getbasecfilefornode(node, basecname)
  500. if c_filename in nodes_by_base_cfile:
  501. nodes_by_base_cfile[c_filename].append(node)
  502. else:
  503. nodes_by_base_cfile[c_filename] = [node]
  504. # produce a sequence of nodes, grouped into files
  505. # which have no more than SPLIT_CRITERIA lines
  506. for basecname in sorted(nodes_by_base_cfile):
  507. iternodes = iter(nodes_by_base_cfile[basecname])
  508. done = [False]
  509. def subiter():
  510. used = nextra
  511. for node in iternodes:
  512. impl = '\n'.join(list(node.implementation())).split('\n')
  513. if not impl:
  514. continue
  515. cost = len(impl) + nbetween
  516. yield node, impl
  517. del impl
  518. if used + cost > split_criteria:
  519. # split if criteria met, unless we would produce nothing.
  520. raise StopIteration
  521. used += cost
  522. done[0] = True
  523. while not done[0]:
  524. yield self.uniquecname(basecname), subiter()
  525. @contextlib.contextmanager
  526. def write_on_included_file(self, f, name):
  527. fi = self.makefile(name)
  528. print >> f, '#include "%s"' % name
  529. yield fi
  530. fi.close()
  531. @contextlib.contextmanager
  532. def write_on_maybe_separate_source(self, f, name):
  533. print >> f, '/* %s */' % name
  534. if self.one_source_file:
  535. yield f
  536. else:
  537. fi = self.makefile(name)
  538. yield fi
  539. fi.close()
  540. def gen_readable_parts_of_source(self, f):
  541. split_criteria_big = SPLIT_CRITERIA
  542. if py.std.sys.platform != "win32":
  543. if self.database.gcpolicy.need_no_typeptr():
  544. pass # XXX gcc uses toooooons of memory???
  545. else:
  546. split_criteria_big = SPLIT_CRITERIA * 4
  547. #
  548. # All declarations
  549. #
  550. with self.write_on_included_file(f, 'structdef.h') as fi:
  551. gen_structdef(fi, self.database)
  552. with self.write_on_included_file(f, 'forwarddecl.h') as fi:
  553. gen_forwarddecl(fi, self.database)
  554. with self.write_on_included_file(f, 'preimpl.h') as fi:
  555. gen_preimpl(fi, self.database)
  556. #
  557. # Implementation of functions and global structures and arrays
  558. #
  559. print >> f
  560. print >> f, '/***********************************************************/'
  561. print >> f, '/*** Implementations ***/'
  562. print >> f
  563. print >> f, '#define PYPY_FILE_NAME "%s"' % os.path.basename(f.name)
  564. print >> f, '#include "src/g_include.h"'
  565. print >> f
  566. nextralines = 11 + 1
  567. for name, nodeiter in self.splitnodesimpl('nonfuncnodes.c',
  568. self.othernodes,
  569. nextralines, 1):
  570. with self.write_on_maybe_separate_source(f, name) as fc:
  571. if fc is not f:
  572. print >> fc, '/***********************************************************/'
  573. print >> fc, '/*** Non-function Implementations ***/'
  574. print >> fc
  575. print >> fc, '#include "common_header.h"'
  576. print >> fc, '#include "structdef.h"'
  577. print >> fc, '#include "forwarddecl.h"'
  578. print >> fc, '#include "preimpl.h"'
  579. print >> fc
  580. print >> fc, '#include "src/g_include.h"'
  581. print >> fc
  582. print >> fc, MARKER
  583. for node, impl in nodeiter:
  584. print >> fc, '\n'.join(impl)
  585. print >> fc, MARKER
  586. print >> fc, '/***********************************************************/'
  587. nextralines = 12
  588. for name, nodeiter in self.splitnodesimpl('implement.c',
  589. self.funcnodes,
  590. nextralines, 1,
  591. split_criteria_big):
  592. with self.write_on_maybe_separate_source(f, name) as fc:
  593. if fc is not f:
  594. print >> fc, '/***********************************************************/'
  595. print >> fc, '/*** Implementations ***/'
  596. print >> fc
  597. print >> fc, '#define PYPY_FILE_NAME "%s"' % name
  598. print >> fc, '#include "common_header.h"'
  599. print >> fc, '#include "structdef.h"'
  600. print >> fc, '#include "forwarddecl.h"'
  601. print >> fc, '#include "preimpl.h"'
  602. print >> fc, '#include "src/g_include.h"'
  603. print >> fc
  604. print >> fc, MARKER
  605. for node, impl in nodeiter:
  606. print >> fc, '\n'.join(impl)
  607. print >> fc, MARKER
  608. print >> fc, '/***********************************************************/'
  609. print >> f
  610. def gen_structdef(f, database):
  611. structdeflist = database.getstructdeflist()
  612. print >> f, '/***********************************************************/'
  613. print >> f, '/*** Structure definitions ***/'
  614. print >> f
  615. for node in structdeflist:
  616. if hasattr(node, 'forward_decl'):
  617. if node.forward_decl:
  618. print >> f, node.forward_decl
  619. elif node.name is not None:
  620. print >> f, '%s %s;' % (node.typetag, node.name)
  621. print >> f
  622. for node in structdeflist:
  623. for line in node.definition():
  624. print >> f, line
  625. def gen_forwarddecl(f, database):
  626. print >> f, '/***********************************************************/'
  627. print >> f, '/*** Forward declarations ***/'
  628. print >> f
  629. for node in database.globalcontainers():
  630. for line in node.forward_declaration():
  631. print >> f, line
  632. def gen_preimpl(f, database):
  633. if database.translator is None or database.translator.rtyper is None:
  634. return
  635. preimplementationlines = pre_include_code_lines(
  636. database, database.translator.rtyper)
  637. for line in preimplementationlines:
  638. print >> f, line
  639. def gen_startupcode(f, database):
  640. # generate the start-up code and put it into a function
  641. print >> f, 'char *RPython_StartupCode(void) {'
  642. print >> f, '\tchar *error = NULL;'
  643. for line in database.gcpolicy.gc_startup_code():
  644. print >> f,"\t" + line
  645. # put float infinities in global constants, we should not have so many of them for now to make
  646. # a table+loop preferable
  647. for dest, value in database.late_initializations:
  648. print >> f, "\t%s = %s;" % (dest, value)
  649. firsttime = True
  650. for node in database.containerlist:
  651. lines = list(node.startupcode())
  652. if lines:
  653. if firsttime:
  654. firsttime = False
  655. else:
  656. print >> f, '\tif (error) return error;'
  657. for line in lines:
  658. print >> f, '\t'+line
  659. print >> f, '\treturn error;'
  660. print >> f, '}'
  661. def commondefs(defines):
  662. from rpython.rlib.rarithmetic import LONG_BIT, LONGLONG_BIT
  663. defines['PYPY_LONG_BIT'] = LONG_BIT
  664. defines['PYPY_LONGLONG_BIT'] = LONGLONG_BIT
  665. def add_extra_files(eci):
  666. srcdir = py.path.local(__file__).join('..', 'src')
  667. files = [
  668. srcdir / 'entrypoint.c', # ifdef PYPY_STANDALONE
  669. srcdir / 'allocator.c', # ifdef PYPY_STANDALONE
  670. srcdir / 'mem.c',
  671. srcdir / 'exception.c',
  672. srcdir / 'rtyper.c', # ifdef HAVE_RTYPER
  673. srcdir / 'support.c',
  674. srcdir / 'profiling.c',
  675. srcdir / 'debug_print.c',
  676. srcdir / 'debug_traceback.c', # ifdef HAVE_RTYPER
  677. srcdir / 'asm.c',
  678. srcdir / 'instrument.c',
  679. srcdir / 'int.c',
  680. ]
  681. if _CYGWIN:
  682. files.append(srcdir / 'cygwin_wait.c')
  683. return eci.merge(ExternalCompilationInfo(separate_module_files=files))
  684. def gen_source(database, modulename, targetdir,
  685. eci, defines={}, split=False):
  686. if isinstance(targetdir, str):
  687. targetdir = py.path.local(targetdir)
  688. filename = targetdir.join(modulename + '.c')
  689. f = filename.open('w')
  690. incfilename = targetdir.join('common_header.h')
  691. fi = incfilename.open('w')
  692. #
  693. # Header
  694. #
  695. print >> f, '#include "common_header.h"'
  696. print >> f
  697. commondefs(defines)
  698. for key, value in defines.items():
  699. print >> fi, '#define %s %s' % (key, value)
  700. eci.write_c_header(fi)
  701. print >> fi, '#include "src/g_prerequisite.h"'
  702. fi.close()
  703. #
  704. # 1) All declarations
  705. # 2) Implementation of functions and global structures and arrays
  706. #
  707. sg = SourceGenerator(database)
  708. sg.set_strategy(targetdir, split)
  709. database.prepare_inline_helpers()
  710. sg.gen_readable_parts_of_source(f)
  711. gen_startupcode(f, database)
  712. f.close()
  713. if 'PYPY_INSTRUMENT' in defines:
  714. fi = incfilename.open('a')
  715. n = database.instrument_ncounter
  716. print >>fi, "#define PYPY_INSTRUMENT_NCOUNTER %d" % n
  717. fi.close()
  718. eci = add_extra_files(eci)
  719. eci = eci.convert_sources_to_files()
  720. files, eci = eci.get_module_files()
  721. return eci, filename, sg.getextrafiles() + list(files)