PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/translator/c/genc.py

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