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

/rpython/translator/c/genc.py

https://bitbucket.org/pypy/pypy/
Python | 900 lines | 815 code | 48 blank | 37 comment | 62 complexity | 7d2b196284cadd96fbb16af3a94e5480 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import contextlib
  2. import py
  3. import sys, os
  4. from rpython.rlib import exports
  5. from rpython.rtyper.lltypesystem.lltype import getfunctionptr
  6. from rpython.rtyper.lltypesystem import lltype
  7. from rpython.tool import runsubprocess
  8. from rpython.tool.nullpath import NullPyPathLocal
  9. from rpython.tool.udir import udir
  10. from rpython.translator.c import gc
  11. from rpython.translator.c.database import LowLevelDatabase
  12. from rpython.translator.c.extfunc import pre_include_code_lines
  13. from rpython.translator.c.support import log
  14. from rpython.translator.gensupp import uniquemodulename, NameManager
  15. from rpython.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. return self.build('-fprofile-generate')
  37. def probe(self, exe, args):
  38. # 'args' is a single string typically containing spaces
  39. # and quotes, which represents several arguments.
  40. self.compiler.platform.execute(exe, args)
  41. def after(self):
  42. return self.build('-fprofile-use')
  43. def build(self, option):
  44. eci = ExternalCompilationInfo(compile_extra=[option],
  45. link_extra=[option])
  46. return self.compiler._build(eci)
  47. class CCompilerDriver(object):
  48. def __init__(self, platform, cfiles, eci, outputfilename=None,
  49. profbased=False):
  50. # XXX config might contain additional link and compile options.
  51. # We need to fish for it somehow.
  52. self.platform = platform
  53. self.cfiles = cfiles
  54. self.eci = eci
  55. self.outputfilename = outputfilename
  56. self.profbased = profbased
  57. def _build(self, eci=ExternalCompilationInfo(), shared=False):
  58. outputfilename = self.outputfilename
  59. if shared:
  60. if outputfilename:
  61. basename = outputfilename
  62. else:
  63. basename = self.cfiles[0].purebasename
  64. outputfilename = 'lib' + basename
  65. return self.platform.compile(self.cfiles, self.eci.merge(eci),
  66. outputfilename=outputfilename,
  67. standalone=not shared)
  68. def build(self, shared=False):
  69. if self.profbased:
  70. return self._do_profbased()
  71. return self._build(shared=shared)
  72. def _do_profbased(self):
  73. ProfDriver, args = self.profbased
  74. profdrv = ProfDriver(self)
  75. dolog = getattr(log, profdrv.name)
  76. dolog(args)
  77. exename = profdrv.first()
  78. dolog('Gathering profile data from: %s %s' % (
  79. str(exename), args))
  80. profdrv.probe(exename, args)
  81. return profdrv.after()
  82. class CBuilder(object):
  83. c_source_filename = None
  84. _compiled = False
  85. modulename = None
  86. split = False
  87. def __init__(self, translator, entrypoint, config, gcpolicy=None,
  88. secondary_entrypoints=()):
  89. self.translator = translator
  90. self.entrypoint = entrypoint
  91. self.entrypoint_name = getattr(self.entrypoint, 'func_name', None)
  92. self.originalentrypoint = entrypoint
  93. self.config = config
  94. self.gcpolicy = gcpolicy # for tests only, e.g. rpython/memory/
  95. self.eci = self.get_eci()
  96. self.secondary_entrypoints = secondary_entrypoints
  97. def get_eci(self):
  98. pypy_include_dir = py.path.local(__file__).join('..')
  99. include_dirs = [pypy_include_dir]
  100. return ExternalCompilationInfo(include_dirs=include_dirs)
  101. def build_database(self):
  102. translator = self.translator
  103. gcpolicyclass = self.get_gcpolicyclass()
  104. if self.config.translation.gcrootfinder == "asmgcc":
  105. if not self.standalone:
  106. raise NotImplementedError("--gcrootfinder=asmgcc requires standalone")
  107. exctransformer = translator.getexceptiontransformer()
  108. db = LowLevelDatabase(translator, standalone=self.standalone,
  109. gcpolicyclass=gcpolicyclass,
  110. exctransformer=exctransformer,
  111. thread_enabled=self.config.translation.thread,
  112. sandbox=self.config.translation.sandbox)
  113. self.db = db
  114. # give the gc a chance to register interest in the start-up functions it
  115. # need (we call this for its side-effects of db.get())
  116. list(db.gcpolicy.gc_startup_code())
  117. # build entrypoint and eventually other things to expose
  118. pf = self.getentrypointptr()
  119. if isinstance(pf, list):
  120. for one_pf in pf:
  121. db.get(one_pf)
  122. self.c_entrypoint_name = None
  123. else:
  124. pfname = db.get(pf)
  125. for func, _ in self.secondary_entrypoints:
  126. bk = translator.annotator.bookkeeper
  127. db.get(getfunctionptr(bk.getdesc(func).getuniquegraph()))
  128. self.c_entrypoint_name = pfname
  129. for obj in exports.EXPORTS_obj2name.keys():
  130. db.getcontainernode(obj)
  131. exports.clear()
  132. db.complete()
  133. self.collect_compilation_info(db)
  134. return db
  135. have___thread = None
  136. def merge_eci(self, *ecis):
  137. self.eci = self.eci.merge(*ecis)
  138. def collect_compilation_info(self, db):
  139. # we need a concrete gcpolicy to do this
  140. self.merge_eci(db.gcpolicy.compilation_info())
  141. all = []
  142. for node in self.db.globalcontainers():
  143. eci = node.compilation_info()
  144. if eci:
  145. all.append(eci)
  146. for node in self.db.getstructdeflist():
  147. try:
  148. all.append(node.STRUCT._hints['eci'])
  149. except (AttributeError, KeyError):
  150. pass
  151. self.merge_eci(*all)
  152. def get_gcpolicyclass(self):
  153. if self.gcpolicy is None:
  154. name = self.config.translation.gctransformer
  155. if name == "framework":
  156. name = "%s+%s" % (name, self.config.translation.gcrootfinder)
  157. return gc.name_to_gcpolicy[name]
  158. return self.gcpolicy
  159. # use generate_source(defines=DEBUG_DEFINES) to force the #definition
  160. # of the macros that enable debugging assertions
  161. DEBUG_DEFINES = {'RPY_ASSERT': 1,
  162. 'RPY_LL_ASSERT': 1}
  163. def generate_source(self, db=None, defines={}, exe_name=None):
  164. assert self.c_source_filename is None
  165. if db is None:
  166. db = self.build_database()
  167. pf = self.getentrypointptr()
  168. if self.modulename is None:
  169. self.modulename = uniquemodulename('testing')
  170. modulename = self.modulename
  171. targetdir = udir.ensure(modulename, dir=1)
  172. if self.config.translation.dont_write_c_files:
  173. targetdir = NullPyPathLocal(targetdir)
  174. self.targetdir = targetdir
  175. defines = defines.copy()
  176. if self.config.translation.countmallocs:
  177. defines['COUNT_OP_MALLOCS'] = 1
  178. if self.config.translation.sandbox:
  179. defines['RPY_SANDBOXED'] = 1
  180. if CBuilder.have___thread is None:
  181. CBuilder.have___thread = self.translator.platform.check___thread()
  182. if not self.standalone:
  183. assert not self.config.translation.instrument
  184. else:
  185. defines['PYPY_STANDALONE'] = db.get(pf)
  186. if self.config.translation.instrument:
  187. defines['PYPY_INSTRUMENT'] = 1
  188. if CBuilder.have___thread:
  189. if not self.config.translation.no__thread:
  190. defines['USE___THREAD'] = 1
  191. if self.config.translation.shared:
  192. defines['PYPY_MAIN_FUNCTION'] = "pypy_main_startup"
  193. self.eci, cfile, extra, headers_to_precompile = \
  194. gen_source(db, modulename, targetdir,
  195. self.eci, defines=defines, split=self.split)
  196. self.c_source_filename = py.path.local(cfile)
  197. self.extrafiles = self.eventually_copy(extra)
  198. self.gen_makefile(targetdir, exe_name=exe_name,
  199. headers_to_precompile=headers_to_precompile)
  200. return cfile
  201. def eventually_copy(self, cfiles):
  202. extrafiles = []
  203. for fn in cfiles:
  204. fn = py.path.local(fn)
  205. if not fn.relto(udir):
  206. newname = self.targetdir.join(fn.basename)
  207. fn.copy(newname)
  208. fn = newname
  209. extrafiles.append(fn)
  210. return extrafiles
  211. class CStandaloneBuilder(CBuilder):
  212. standalone = True
  213. split = True
  214. executable_name = None
  215. shared_library_name = None
  216. _entrypoint_wrapper = None
  217. make_entrypoint_wrapper = True # for tests
  218. def getprofbased(self):
  219. profbased = None
  220. if self.config.translation.instrumentctl is not None:
  221. profbased = self.config.translation.instrumentctl
  222. else:
  223. # xxx handling config.translation.profopt is a bit messy, because
  224. # it could be an empty string (not to be confused with None) and
  225. # because noprofopt can be used as an override.
  226. profopt = self.config.translation.profopt
  227. if profopt is not None and not self.config.translation.noprofopt:
  228. profbased = (ProfOpt, profopt)
  229. return profbased
  230. def has_profopt(self):
  231. profbased = self.getprofbased()
  232. retval = (profbased and isinstance(profbased, tuple)
  233. and profbased[0] is ProfOpt)
  234. if retval and self.translator.platform.name == 'msvc':
  235. raise ValueError('Cannot do profile based optimization on MSVC,'
  236. 'it is not supported in free compiler version')
  237. def getentrypointptr(self):
  238. # XXX check that the entrypoint has the correct
  239. # signature: list-of-strings -> int
  240. if not self.make_entrypoint_wrapper:
  241. bk = self.translator.annotator.bookkeeper
  242. return getfunctionptr(bk.getdesc(self.entrypoint).getuniquegraph())
  243. if self._entrypoint_wrapper is not None:
  244. return self._entrypoint_wrapper
  245. #
  246. from rpython.annotator import model as annmodel
  247. from rpython.rtyper.lltypesystem import rffi
  248. from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
  249. from rpython.rtyper.llannotation import lltype_to_annotation
  250. entrypoint = self.entrypoint
  251. #
  252. def entrypoint_wrapper(argc, argv):
  253. """This is a wrapper that takes "Signed argc" and "char **argv"
  254. like the C main function, and puts them inside an RPython list
  255. of strings before invoking the real entrypoint() function.
  256. """
  257. list = [""] * argc
  258. i = 0
  259. while i < argc:
  260. list[i] = rffi.charp2str(argv[i])
  261. i += 1
  262. return entrypoint(list)
  263. #
  264. mix = MixLevelHelperAnnotator(self.translator.rtyper)
  265. args_s = [annmodel.SomeInteger(),
  266. lltype_to_annotation(rffi.CCHARPP)]
  267. s_result = annmodel.SomeInteger()
  268. graph = mix.getgraph(entrypoint_wrapper, args_s, s_result)
  269. mix.finish()
  270. res = getfunctionptr(graph)
  271. self._entrypoint_wrapper = res
  272. return res
  273. def cmdexec(self, args='', env=None, err=False, expect_crash=False, exe=None):
  274. assert self._compiled
  275. if sys.platform == 'win32':
  276. #Prevent opening a dialog box
  277. import ctypes
  278. winapi = ctypes.windll.kernel32
  279. SetErrorMode = winapi.SetErrorMode
  280. SetErrorMode.argtypes=[ctypes.c_int]
  281. SEM_FAILCRITICALERRORS = 1
  282. SEM_NOGPFAULTERRORBOX = 2
  283. SEM_NOOPENFILEERRORBOX = 0x8000
  284. flags = SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX \
  285. | SEM_NOOPENFILEERRORBOX
  286. #Since there is no GetErrorMode, do a double Set
  287. old_mode = SetErrorMode(flags)
  288. SetErrorMode(old_mode | flags)
  289. if env is None:
  290. envrepr = ''
  291. else:
  292. envrepr = ' [env=%r]' % (env,)
  293. if exe is None:
  294. exe = self.executable_name
  295. log.cmdexec('%s %s%s' % (exe, args, envrepr))
  296. res = self.translator.platform.execute(exe, args, env=env)
  297. if sys.platform == 'win32':
  298. SetErrorMode(old_mode)
  299. if res.returncode != 0:
  300. if expect_crash:
  301. return res.out, res.err
  302. print >> sys.stderr, res.err
  303. raise Exception("Returned %d" % (res.returncode,))
  304. if expect_crash:
  305. raise Exception("Program did not crash!")
  306. if err:
  307. return res.out, res.err
  308. return res.out
  309. def build_main_for_shared(self, shared_library_name, entrypoint, exe_name):
  310. import time
  311. time.sleep(1)
  312. self.shared_library_name = shared_library_name
  313. # build main program
  314. eci = self.get_eci()
  315. kw = {}
  316. if self.translator.platform.cc == 'gcc':
  317. kw['libraries'] = [self.shared_library_name.purebasename[3:]]
  318. kw['library_dirs'] = [self.targetdir]
  319. else:
  320. kw['libraries'] = [self.shared_library_name.new(ext='')]
  321. eci = eci.merge(ExternalCompilationInfo(
  322. separate_module_sources=['''
  323. int %s(int argc, char* argv[]);
  324. int main(int argc, char* argv[])
  325. { return %s(argc, argv); }
  326. ''' % (entrypoint, entrypoint)
  327. ],
  328. **kw
  329. ))
  330. eci = eci.convert_sources_to_files(
  331. cache_dir=self.targetdir)
  332. return self.translator.platform.compile(
  333. [], eci,
  334. outputfilename=exe_name)
  335. def compile(self, exe_name=None):
  336. assert self.c_source_filename
  337. assert not self._compiled
  338. shared = self.config.translation.shared
  339. extra_opts = []
  340. if self.config.translation.make_jobs != 1:
  341. extra_opts += ['-j', str(self.config.translation.make_jobs)]
  342. if self.config.translation.lldebug:
  343. extra_opts += ["lldebug"]
  344. elif self.config.translation.lldebug0:
  345. extra_opts += ["lldebug0"]
  346. self.translator.platform.execute_makefile(self.targetdir,
  347. extra_opts)
  348. if shared:
  349. self.shared_library_name = self.executable_name.new(
  350. purebasename='lib' + self.executable_name.purebasename,
  351. ext=self.translator.platform.so_ext)
  352. self._compiled = True
  353. return self.executable_name
  354. def gen_makefile(self, targetdir, exe_name=None, headers_to_precompile=[]):
  355. module_files = self.eventually_copy(self.eci.separate_module_files)
  356. self.eci.separate_module_files = []
  357. cfiles = [self.c_source_filename] + self.extrafiles + list(module_files)
  358. if exe_name is not None:
  359. exe_name = targetdir.join(exe_name)
  360. mk = self.translator.platform.gen_makefile(
  361. cfiles, self.eci,
  362. path=targetdir, exe_name=exe_name,
  363. headers_to_precompile=headers_to_precompile,
  364. no_precompile_cfiles = module_files,
  365. shared=self.config.translation.shared,
  366. icon=self.config.translation.icon)
  367. if self.has_profopt():
  368. profopt = self.config.translation.profopt
  369. mk.definition('ABS_TARGET', str(targetdir.join('$(TARGET)')))
  370. mk.definition('DEFAULT_TARGET', 'profopt')
  371. mk.definition('PROFOPT', profopt)
  372. rules = [
  373. ('debug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT" debug_target'),
  374. ('debug_exc', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DDO_LOG_EXC" debug_target'),
  375. ('debug_mem', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DPYPY_USE_TRIVIAL_MALLOC" debug_target'),
  376. ('llsafer', '', '$(MAKE) CFLAGS="-O2 -DRPY_LL_ASSERT" $(DEFAULT_TARGET)'),
  377. ('lldebug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'),
  378. ('profile', '', '$(MAKE) CFLAGS="-g -O1 -pg $(CFLAGS) -fno-omit-frame-pointer" LDFLAGS="-pg $(LDFLAGS)" $(DEFAULT_TARGET)'),
  379. ]
  380. if self.has_profopt():
  381. rules.append(
  382. ('profopt', '', [
  383. '$(MAKENOPROF)',
  384. '$(MAKE) CFLAGS="-fprofile-generate $(CFLAGS)" LDFLAGS="-fprofile-generate $(LDFLAGS)" $(TARGET)',
  385. 'cd $(RPYDIR)/translator/goal && $(ABS_TARGET) $(PROFOPT)',
  386. '$(MAKE) clean_noprof',
  387. '$(MAKE) CFLAGS="-fprofile-use $(CFLAGS)" LDFLAGS="-fprofile-use $(LDFLAGS)" $(TARGET)']))
  388. for rule in rules:
  389. mk.rule(*rule)
  390. if self.translator.platform.name == 'msvc':
  391. mk.rule('lldebug0','', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -Od -DMAX_STACK_SIZE=8192000 -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'),
  392. wildcards = '..\*.obj ..\*.pdb ..\*.lib ..\*.dll ..\*.manifest ..\*.exp *.pch'
  393. cmd = r'del /s %s $(DEFAULT_TARGET) $(TARGET) $(GCMAPFILES) $(ASMFILES)' % wildcards
  394. mk.rule('clean', '', cmd + ' *.gc?? ..\module_cache\*.gc??')
  395. mk.rule('clean_noprof', '', cmd)
  396. else:
  397. mk.rule('lldebug0','', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -O0 -DMAX_STACK_SIZE=8192000 -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'),
  398. mk.rule('clean', '', 'rm -f $(OBJECTS) $(DEFAULT_TARGET) $(TARGET) $(GCMAPFILES) $(ASMFILES) *.gc?? ../module_cache/*.gc??')
  399. mk.rule('clean_noprof', '', 'rm -f $(OBJECTS) $(DEFAULT_TARGET) $(TARGET) $(GCMAPFILES) $(ASMFILES)')
  400. #XXX: this conditional part is not tested at all
  401. if self.config.translation.gcrootfinder == 'asmgcc':
  402. if self.translator.platform.name == 'msvc':
  403. raise Exception("msvc no longer supports asmgcc")
  404. _extra = ''
  405. if self.config.translation.shared:
  406. _extra = ' -fPIC'
  407. _extra += ' -fdisable-tree-fnsplit' # seems to help
  408. mk.definition('DEBUGFLAGS',
  409. '-O2 -fomit-frame-pointer -g'+ _extra)
  410. if self.config.translation.shared:
  411. mk.definition('PYPY_MAIN_FUNCTION', "pypy_main_startup")
  412. else:
  413. mk.definition('PYPY_MAIN_FUNCTION', "main")
  414. mk.definition('PYTHON', get_recent_cpython_executable())
  415. mk.definition('GCMAPFILES', '$(subst .vmprof.s,.gcmap,$(subst .c,.gcmap,$(SOURCES)))')
  416. mk.definition('OBJECTS1', '$(subst .vmprof.s,.o,$(subst .c,.o,$(SOURCES)))')
  417. mk.definition('OBJECTS', '$(OBJECTS1) gcmaptable.s')
  418. # the CFLAGS passed to gcc when invoked to assembler the .s file
  419. # must not contain -g. This confuses gcc 5.1. (Note that it
  420. # would seem that gcc 5.1 with "-g" does not produce debugging
  421. # info in a format that gdb 4.7.1 can read.)
  422. mk.definition('CFLAGS_AS', '$(patsubst -g,,$(CFLAGS))')
  423. # the rule that transforms %.c into %.o, by compiling it to
  424. # %.s, then applying trackgcroot to get %.lbl.s and %.gcmap, and
  425. # finally by using the assembler ($(CC) again for now) to get %.o
  426. mk.rule('%.o %.gcmap', '%.c', [
  427. '$(CC) $(CFLAGS) $(CFLAGSEXTRA) -frandom-seed=$< '
  428. '-o $*.s -S $< $(INCLUDEDIRS)',
  429. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  430. '-t $*.s > $*.gctmp',
  431. '$(CC) $(CFLAGS_AS) -o $*.o -c $*.lbl.s',
  432. 'mv $*.gctmp $*.gcmap',
  433. 'rm $*.s $*.lbl.s'])
  434. # this is for manually written assembly files which needs to be parsed by asmgcc
  435. mk.rule('%.o %.gcmap', '%.vmprof.s', [
  436. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  437. '-t $*.vmprof.s > $*.gctmp',
  438. '$(CC) -o $*.o -c $*.vmprof.lbl.s',
  439. 'mv $*.gctmp $*.gcmap',
  440. 'rm $*.vmprof.lbl.s'])
  441. # the rule to compute gcmaptable.s
  442. mk.rule('gcmaptable.s', '$(GCMAPFILES)',
  443. [
  444. '$(PYTHON) $(RPYDIR)/translator/c/gcc/trackgcroot.py '
  445. '$(GCMAPFILES) > $@.tmp',
  446. 'mv $@.tmp $@'])
  447. else:
  448. if self.translator.platform.name == 'msvc':
  449. mk.definition('DEBUGFLAGS', '-MD -Zi')
  450. else:
  451. if self.config.translation.shared:
  452. mk.definition('DEBUGFLAGS', '-O1 -g -fPIC')
  453. else:
  454. mk.definition('DEBUGFLAGS', '-O1 -g')
  455. if self.translator.platform.name == 'msvc':
  456. mk.rule('debug_target', '$(DEFAULT_TARGET) $(WTARGET)', 'rem')
  457. else:
  458. mk.rule('debug_target', '$(DEFAULT_TARGET)', '#')
  459. mk.write()
  460. #self.translator.platform,
  461. # ,
  462. # self.eci, profbased=self.getprofbased()
  463. self.executable_name = mk.exe_name
  464. # ____________________________________________________________
  465. SPLIT_CRITERIA = 65535 # support VC++ 7.2
  466. #SPLIT_CRITERIA = 32767 # enable to support VC++ 6.0
  467. MARKER = '/*/*/' # provide an easy way to split after generating
  468. class SourceGenerator:
  469. one_source_file = True
  470. def __init__(self, database):
  471. self.database = database
  472. self.extrafiles = []
  473. self.headers_to_precompile = []
  474. self.path = None
  475. self.namespace = NameManager()
  476. def set_strategy(self, path, split=True):
  477. all_nodes = list(self.database.globalcontainers())
  478. # split off non-function nodes. We don't try to optimize these, yet.
  479. funcnodes = []
  480. othernodes = []
  481. for node in all_nodes:
  482. if node.nodekind == 'func':
  483. funcnodes.append(node)
  484. else:
  485. othernodes.append(node)
  486. if split:
  487. self.one_source_file = False
  488. self.funcnodes = funcnodes
  489. self.othernodes = othernodes
  490. self.path = path
  491. def uniquecname(self, name):
  492. assert name.endswith('.c')
  493. return self.namespace.uniquename(name[:-2]) + '.c'
  494. def makefile(self, name):
  495. log.writing(name)
  496. filepath = self.path.join(name)
  497. if name.endswith('.c'):
  498. self.extrafiles.append(filepath)
  499. if name.endswith('.h'):
  500. self.headers_to_precompile.append(filepath)
  501. return filepath.open('w')
  502. def getextrafiles(self):
  503. return self.extrafiles
  504. def getothernodes(self):
  505. return self.othernodes[:]
  506. def getbasecfilefornode(self, node, basecname):
  507. # For FuncNode instances, use the python source filename (relative to
  508. # the top directory):
  509. def invent_nice_name(g):
  510. # Lookup the filename from the function.
  511. # However, not all FunctionGraph objs actually have a "func":
  512. if hasattr(g, 'func'):
  513. if g.filename.endswith('.py'):
  514. localpath = py.path.local(g.filename)
  515. pypkgpath = localpath.pypkgpath()
  516. if pypkgpath:
  517. relpypath = localpath.relto(pypkgpath.dirname)
  518. assert relpypath, ("%r should be relative to %r" %
  519. (localpath, pypkgpath.dirname))
  520. if len(relpypath.split(os.path.sep)) > 2:
  521. # pypy detail to agregate the c files by directory,
  522. # since the enormous number of files was causing
  523. # memory issues linking on win32
  524. return os.path.split(relpypath)[0] + '.c'
  525. return relpypath.replace('.py', '.c')
  526. return None
  527. if hasattr(node.obj, 'graph'):
  528. # Regular RPython functions
  529. name = invent_nice_name(node.obj.graph)
  530. if name is not None:
  531. return name
  532. elif node._funccodegen_owner is not None:
  533. # Data nodes that belong to a known function
  534. graph = getattr(node._funccodegen_owner, 'graph', None)
  535. name = invent_nice_name(graph)
  536. if name is not None:
  537. return "data_" + name
  538. return basecname
  539. def splitnodesimpl(self, basecname, nodes, nextra, nbetween,
  540. split_criteria=SPLIT_CRITERIA):
  541. # Gather nodes by some criteria:
  542. nodes_by_base_cfile = {}
  543. for node in nodes:
  544. c_filename = self.getbasecfilefornode(node, basecname)
  545. if c_filename in nodes_by_base_cfile:
  546. nodes_by_base_cfile[c_filename].append(node)
  547. else:
  548. nodes_by_base_cfile[c_filename] = [node]
  549. # produce a sequence of nodes, grouped into files
  550. # which have no more than SPLIT_CRITERIA lines
  551. for basecname in sorted(nodes_by_base_cfile):
  552. iternodes = iter(nodes_by_base_cfile[basecname])
  553. done = [False]
  554. def subiter():
  555. used = nextra
  556. for node in iternodes:
  557. impl = '\n'.join(list(node.implementation())).split('\n')
  558. if not impl:
  559. continue
  560. cost = len(impl) + nbetween
  561. yield node, impl
  562. del impl
  563. if used + cost > split_criteria:
  564. # split if criteria met, unless we would produce nothing.
  565. raise StopIteration
  566. used += cost
  567. done[0] = True
  568. while not done[0]:
  569. yield self.uniquecname(basecname), subiter()
  570. @contextlib.contextmanager
  571. def write_on_included_file(self, f, name):
  572. fi = self.makefile(name)
  573. print >> f, '#include "%s"' % name
  574. yield fi
  575. fi.close()
  576. @contextlib.contextmanager
  577. def write_on_maybe_separate_source(self, f, name):
  578. print >> f, '/* %s */' % name
  579. if self.one_source_file:
  580. yield f
  581. else:
  582. fi = self.makefile(name)
  583. yield fi
  584. fi.close()
  585. def gen_readable_parts_of_source(self, f):
  586. split_criteria_big = SPLIT_CRITERIA
  587. if py.std.sys.platform != "win32":
  588. if self.database.gcpolicy.need_no_typeptr():
  589. pass # XXX gcc uses toooooons of memory???
  590. else:
  591. split_criteria_big = SPLIT_CRITERIA * 4
  592. #
  593. # All declarations
  594. #
  595. with self.write_on_included_file(f, 'structdef.h') as fi:
  596. gen_structdef(fi, self.database)
  597. with self.write_on_included_file(f, 'forwarddecl.h') as fi:
  598. gen_forwarddecl(fi, self.database)
  599. with self.write_on_included_file(f, 'preimpl.h') as fi:
  600. gen_preimpl(fi, self.database)
  601. #
  602. # Implementation of functions and global structures and arrays
  603. #
  604. print >> f
  605. print >> f, '/***********************************************************/'
  606. print >> f, '/*** Implementations ***/'
  607. print >> f
  608. print >> f, '#define PYPY_FILE_NAME "%s"' % os.path.basename(f.name)
  609. print >> f, '#include "src/g_include.h"'
  610. print >> f
  611. nextralines = 11 + 1
  612. for name, nodeiter in self.splitnodesimpl('nonfuncnodes.c',
  613. self.othernodes,
  614. nextralines, 1):
  615. with self.write_on_maybe_separate_source(f, name) as fc:
  616. if fc is not f:
  617. print >> fc, '/***********************************************************/'
  618. print >> fc, '/*** Non-function Implementations ***/'
  619. print >> fc
  620. print >> fc, '#include "common_header.h"'
  621. print >> fc, '#include "structdef.h"'
  622. print >> fc, '#include "forwarddecl.h"'
  623. print >> fc, '#include "preimpl.h"'
  624. print >> fc
  625. print >> fc, '#include "src/g_include.h"'
  626. print >> fc
  627. print >> fc, MARKER
  628. for node, impl in nodeiter:
  629. print >> fc, '\n'.join(impl)
  630. print >> fc, MARKER
  631. print >> fc, '/***********************************************************/'
  632. nextralines = 12
  633. for name, nodeiter in self.splitnodesimpl('implement.c',
  634. self.funcnodes,
  635. nextralines, 1,
  636. split_criteria_big):
  637. with self.write_on_maybe_separate_source(f, name) as fc:
  638. if fc is not f:
  639. print >> fc, '/***********************************************************/'
  640. print >> fc, '/*** Implementations ***/'
  641. print >> fc
  642. print >> fc, '#include "common_header.h"'
  643. print >> fc, '#include "structdef.h"'
  644. print >> fc, '#include "forwarddecl.h"'
  645. print >> fc, '#include "preimpl.h"'
  646. print >> fc, '#define PYPY_FILE_NAME "%s"' % name
  647. print >> fc, '#include "src/g_include.h"'
  648. print >> fc
  649. print >> fc, MARKER
  650. for node, impl in nodeiter:
  651. print >> fc, '\n'.join(impl)
  652. print >> fc, MARKER
  653. print >> fc, '/***********************************************************/'
  654. print >> f
  655. def gen_structdef(f, database):
  656. structdeflist = database.getstructdeflist()
  657. print >> f, '/***********************************************************/'
  658. print >> f, '/*** Structure definitions ***/'
  659. print >> f
  660. print >> f, "#ifndef _PYPY_STRUCTDEF_H"
  661. print >> f, "#define _PYPY_STRUCTDEF_H"
  662. for node in structdeflist:
  663. if hasattr(node, 'forward_decl'):
  664. if node.forward_decl:
  665. print >> f, node.forward_decl
  666. elif node.name is not None:
  667. print >> f, '%s %s;' % (node.typetag, node.name)
  668. print >> f
  669. for node in structdeflist:
  670. for line in node.definition():
  671. print >> f, line
  672. gen_threadlocal_structdef(f, database)
  673. print >> f, "#endif"
  674. def gen_threadlocal_structdef(f, database):
  675. from rpython.translator.c.support import cdecl
  676. print >> f
  677. bk = database.translator.annotator.bookkeeper
  678. fields = list(bk.thread_local_fields)
  679. fields.sort(key=lambda field: field.fieldname)
  680. for field in fields:
  681. print >> f, ('#define RPY_TLOFS_%s offsetof(' % field.fieldname +
  682. 'struct pypy_threadlocal_s, %s)' % field.fieldname)
  683. print >> f, 'struct pypy_threadlocal_s {'
  684. print >> f, '\tint ready;'
  685. print >> f, '\tchar *stack_end;'
  686. print >> f, '\tstruct pypy_threadlocal_s *prev, *next;'
  687. # note: if the four fixed fields above are changed, you need
  688. # to adapt threadlocal.c's linkedlist_head declaration too
  689. for field in fields:
  690. typename = database.gettype(field.FIELDTYPE)
  691. print >> f, '\t%s;' % cdecl(typename, field.fieldname)
  692. print >> f, '};'
  693. print >> f
  694. def gen_forwarddecl(f, database):
  695. print >> f, '/***********************************************************/'
  696. print >> f, '/*** Forward declarations ***/'
  697. print >> f
  698. print >> f, "#ifndef _PYPY_FORWARDDECL_H"
  699. print >> f, "#define _PYPY_FORWARDDECL_H"
  700. for node in database.globalcontainers():
  701. for line in node.forward_declaration():
  702. print >> f, line
  703. print >> f, "#endif"
  704. def gen_preimpl(f, database):
  705. f.write('#ifndef _PY_PREIMPL_H\n#define _PY_PREIMPL_H\n')
  706. if database.translator is None or database.translator.rtyper is None:
  707. return
  708. preimplementationlines = pre_include_code_lines(
  709. database, database.translator.rtyper)
  710. for line in preimplementationlines:
  711. print >> f, line
  712. f.write('#endif /* _PY_PREIMPL_H */\n')
  713. def gen_startupcode(f, database):
  714. # generate the start-up code and put it into a function
  715. print >> f, 'void RPython_StartupCode(void) {'
  716. bk = database.translator.annotator.bookkeeper
  717. if bk.thread_local_fields:
  718. print >> f, '\tRPython_ThreadLocals_ProgramInit();'
  719. for line in database.gcpolicy.gc_startup_code():
  720. print >> f,"\t" + line
  721. # put float infinities in global constants, we should not have so many of them for now to make
  722. # a table+loop preferable
  723. for dest, value in database.late_initializations:
  724. print >> f, "\t%s = %s;" % (dest, value)
  725. for node in database.containerlist:
  726. lines = list(node.startupcode())
  727. if lines:
  728. for line in lines:
  729. print >> f, '\t'+line
  730. print >> f, '}'
  731. def commondefs(defines):
  732. from rpython.rlib.rarithmetic import LONG_BIT, LONGLONG_BIT
  733. defines['PYPY_LONG_BIT'] = LONG_BIT
  734. defines['PYPY_LONGLONG_BIT'] = LONGLONG_BIT
  735. def add_extra_files(eci):
  736. srcdir = py.path.local(__file__).join('..', 'src')
  737. files = [
  738. srcdir / 'entrypoint.c', # ifdef PYPY_STANDALONE
  739. srcdir / 'mem.c',
  740. srcdir / 'exception.c',
  741. srcdir / 'rtyper.c', # ifdef HAVE_RTYPER
  742. srcdir / 'support.c',
  743. srcdir / 'profiling.c',
  744. srcdir / 'debug_print.c',
  745. srcdir / 'debug_traceback.c', # ifdef HAVE_RTYPER
  746. srcdir / 'asm.c',
  747. srcdir / 'instrument.c',
  748. srcdir / 'int.c',
  749. srcdir / 'stack.c',
  750. srcdir / 'threadlocal.c',
  751. ]
  752. if _CYGWIN:
  753. files.append(srcdir / 'cygwin_wait.c')
  754. return eci.merge(ExternalCompilationInfo(separate_module_files=files))
  755. def gen_source(database, modulename, targetdir,
  756. eci, defines={}, split=False):
  757. if isinstance(targetdir, str):
  758. targetdir = py.path.local(targetdir)
  759. filename = targetdir.join(modulename + '.c')
  760. f = filename.open('w')
  761. incfilename = targetdir.join('common_header.h')
  762. fi = incfilename.open('w')
  763. fi.write('#ifndef _PY_COMMON_HEADER_H\n#define _PY_COMMON_HEADER_H\n')
  764. #
  765. # Header
  766. #
  767. print >> f, '#include "common_header.h"'
  768. print >> f
  769. commondefs(defines)
  770. for key, value in defines.items():
  771. print >> fi, '#define %s %s' % (key, value)
  772. eci.write_c_header(fi)
  773. print >> fi, '#include "src/g_prerequisite.h"'
  774. fi.write('#endif /* _PY_COMMON_HEADER_H*/\n')
  775. fi.close()
  776. #
  777. # 1) All declarations
  778. # 2) Implementation of functions and global structures and arrays
  779. #
  780. sg = SourceGenerator(database)
  781. sg.set_strategy(targetdir, split)
  782. sg.gen_readable_parts_of_source(f)
  783. headers_to_precompile = sg.headers_to_precompile[:]
  784. headers_to_precompile.insert(0, incfilename)
  785. gen_startupcode(f, database)
  786. f.close()
  787. if 'PYPY_INSTRUMENT' in defines:
  788. fi = incfilename.open('a')
  789. n = database.instrument_ncounter
  790. print >>fi, "#define PYPY_INSTRUMENT_NCOUNTER %d" % n
  791. fi.close()
  792. eci = add_extra_files(eci)
  793. eci = eci.convert_sources_to_files()
  794. return eci, filename, sg.getextrafiles(), headers_to_precompile