PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/translator/driver.py

https://bitbucket.org/pypy/pypy/
Python | 617 lines | 550 code | 47 blank | 20 comment | 47 complexity | 1679dc2589b0493ac66cdad7d35f503e MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys, os
  2. import os.path
  3. import shutil
  4. from rpython.translator.translator import TranslationContext
  5. from rpython.translator.tool.taskengine import SimpleTaskEngine
  6. from rpython.translator.goal import query
  7. from rpython.translator.goal.timing import Timer
  8. from rpython.annotator.listdef import s_list_of_strings
  9. from rpython.annotator import policy as annpolicy
  10. from rpython.tool.udir import udir
  11. from rpython.rlib.debug import debug_start, debug_print, debug_stop
  12. from rpython.rlib.entrypoint import secondary_entrypoints,\
  13. annotated_jit_entrypoints
  14. import py
  15. from rpython.tool.ansi_print import AnsiLogger
  16. log = AnsiLogger("translation")
  17. def taskdef(deps, title, new_state=None, expected_states=[],
  18. idemp=False, earlycheck=None):
  19. def decorator(taskfunc):
  20. taskfunc.task_deps = deps
  21. taskfunc.task_title = title
  22. taskfunc.task_newstate = None
  23. taskfunc.task_expected_states = expected_states
  24. taskfunc.task_idempotent = idemp
  25. taskfunc.task_earlycheck = earlycheck
  26. return taskfunc
  27. return decorator
  28. # TODO:
  29. # sanity-checks using states
  30. # set of translation steps to profile
  31. PROFILE = set([])
  32. class Instrument(Exception):
  33. pass
  34. class ProfInstrument(object):
  35. name = "profinstrument"
  36. def __init__(self, datafile, compiler):
  37. self.datafile = datafile
  38. self.compiler = compiler
  39. def first(self):
  40. return self.compiler._build()
  41. def probe(self, exe, args):
  42. env = os.environ.copy()
  43. env['PYPY_INSTRUMENT_COUNTERS'] = str(self.datafile)
  44. self.compiler.platform.execute(exe, args, env=env)
  45. def after(self):
  46. # xxx
  47. os._exit(0)
  48. class TranslationDriver(SimpleTaskEngine):
  49. _backend_extra_options = {}
  50. def __init__(self, setopts=None, default_goal=None,
  51. disable=[],
  52. exe_name=None, extmod_name=None,
  53. config=None, overrides=None):
  54. from rpython.config import translationoption
  55. self.timer = Timer()
  56. SimpleTaskEngine.__init__(self)
  57. self.log = log
  58. if config is None:
  59. config = translationoption.get_combined_translation_config(translating=True)
  60. # XXX patch global variable with translation config
  61. translationoption._GLOBAL_TRANSLATIONCONFIG = config
  62. self.config = config
  63. if overrides is not None:
  64. self.config.override(overrides)
  65. if setopts is not None:
  66. self.config.set(**setopts)
  67. self.exe_name = exe_name
  68. self.extmod_name = extmod_name
  69. self.done = {}
  70. self.disable(disable)
  71. if default_goal:
  72. default_goal, = self.backend_select_goals([default_goal])
  73. if default_goal in self._maybe_skip():
  74. default_goal = None
  75. self.default_goal = default_goal
  76. self.extra_goals = []
  77. self.exposed = []
  78. # expose tasks
  79. def expose_task(task, backend_goal=None):
  80. if backend_goal is None:
  81. backend_goal = task
  82. def proc():
  83. return self.proceed(backend_goal)
  84. self.exposed.append(task)
  85. setattr(self, task, proc)
  86. backend, ts = self.get_backend_and_type_system()
  87. for task in self.tasks:
  88. explicit_task = task
  89. if task == 'annotate':
  90. expose_task(task)
  91. else:
  92. task, postfix = task.split('_')
  93. if task in ('rtype', 'backendopt', 'llinterpret',
  94. 'pyjitpl'):
  95. if ts:
  96. if ts == postfix:
  97. expose_task(task, explicit_task)
  98. else:
  99. expose_task(explicit_task)
  100. elif task in ('source', 'compile', 'run'):
  101. if backend:
  102. if backend == postfix:
  103. expose_task(task, explicit_task)
  104. elif ts:
  105. if ts == 'lltype':
  106. expose_task(explicit_task)
  107. else:
  108. expose_task(explicit_task)
  109. def set_extra_goals(self, goals):
  110. self.extra_goals = goals
  111. def set_backend_extra_options(self, extra_options):
  112. self._backend_extra_options = extra_options
  113. def get_info(self): # XXX more?
  114. d = {'backend': self.config.translation.backend}
  115. return d
  116. def get_backend_and_type_system(self):
  117. type_system = self.config.translation.type_system
  118. backend = self.config.translation.backend
  119. return backend, type_system
  120. def backend_select_goals(self, goals):
  121. backend, ts = self.get_backend_and_type_system()
  122. postfixes = [''] + ['_'+p for p in (backend, ts) if p]
  123. l = []
  124. for goal in goals:
  125. for postfix in postfixes:
  126. cand = "%s%s" % (goal, postfix)
  127. if cand in self.tasks:
  128. new_goal = cand
  129. break
  130. else:
  131. raise Exception("cannot infer complete goal from: %r" % goal)
  132. l.append(new_goal)
  133. return l
  134. def disable(self, to_disable):
  135. self._disabled = to_disable
  136. def _maybe_skip(self):
  137. maybe_skip = []
  138. if self._disabled:
  139. for goal in self.backend_select_goals(self._disabled):
  140. maybe_skip.extend(self._depending_on_closure(goal))
  141. return dict.fromkeys(maybe_skip).keys()
  142. def setup(self, entry_point, inputtypes, policy=None, extra={}, empty_translator=None):
  143. standalone = inputtypes is None
  144. self.standalone = standalone
  145. if standalone:
  146. # the 'argv' parameter
  147. inputtypes = [s_list_of_strings]
  148. self.inputtypes = inputtypes
  149. if policy is None:
  150. policy = annpolicy.AnnotatorPolicy()
  151. self.policy = policy
  152. self.extra = extra
  153. if empty_translator:
  154. translator = empty_translator
  155. else:
  156. translator = TranslationContext(config=self.config)
  157. self.entry_point = entry_point
  158. self.translator = translator
  159. self.libdef = None
  160. self.secondary_entrypoints = []
  161. if self.config.translation.secondaryentrypoints:
  162. for key in self.config.translation.secondaryentrypoints.split(","):
  163. try:
  164. points = secondary_entrypoints[key]
  165. except KeyError:
  166. raise KeyError("Entrypoint %r not found (not in %r)" %
  167. (key, secondary_entrypoints.keys()))
  168. self.secondary_entrypoints.extend(points)
  169. self.translator.driver_instrument_result = self.instrument_result
  170. def setup_library(self, libdef, policy=None, extra={}, empty_translator=None):
  171. """ Used by carbon python only. """
  172. self.setup(None, None, policy, extra, empty_translator)
  173. self.libdef = libdef
  174. self.secondary_entrypoints = libdef.functions
  175. def instrument_result(self, args):
  176. backend, ts = self.get_backend_and_type_system()
  177. if backend != 'c' or sys.platform == 'win32':
  178. raise Exception("instrumentation requires the c backend"
  179. " and unix for now")
  180. datafile = udir.join('_instrument_counters')
  181. makeProfInstrument = lambda compiler: ProfInstrument(datafile, compiler)
  182. pid = os.fork()
  183. if pid == 0:
  184. # child compiling and running with instrumentation
  185. self.config.translation.instrument = True
  186. self.config.translation.instrumentctl = (makeProfInstrument,
  187. args)
  188. raise Instrument
  189. else:
  190. pid, status = os.waitpid(pid, 0)
  191. if os.WIFEXITED(status):
  192. status = os.WEXITSTATUS(status)
  193. if status != 0:
  194. raise Exception("instrumentation child failed: %d" % status)
  195. else:
  196. raise Exception("instrumentation child aborted")
  197. import array, struct
  198. n = datafile.size()//struct.calcsize('L')
  199. datafile = datafile.open('rb')
  200. counters = array.array('L')
  201. counters.fromfile(datafile, n)
  202. datafile.close()
  203. return counters
  204. def info(self, msg):
  205. log.info(msg)
  206. def _profile(self, goal, func):
  207. from cProfile import Profile
  208. from rpython.tool.lsprofcalltree import KCacheGrind
  209. d = {'func':func}
  210. prof = Profile()
  211. prof.runctx("res = func()", globals(), d)
  212. KCacheGrind(prof).output(open(goal + ".out", "w"))
  213. return d['res']
  214. def _do(self, goal, func, *args, **kwds):
  215. title = func.task_title
  216. if goal in self.done:
  217. self.log.info("already done: %s" % title)
  218. return
  219. else:
  220. self.log.info("%s..." % title)
  221. debug_start('translation-task')
  222. debug_print('starting', goal)
  223. self.timer.start_event(goal)
  224. try:
  225. instrument = False
  226. try:
  227. if goal in PROFILE:
  228. res = self._profile(goal, func)
  229. else:
  230. res = func()
  231. except Instrument:
  232. instrument = True
  233. if not func.task_idempotent:
  234. self.done[goal] = True
  235. if instrument:
  236. self.proceed('compile')
  237. assert False, 'we should not get here'
  238. finally:
  239. try:
  240. debug_stop('translation-task')
  241. self.timer.end_event(goal)
  242. except (KeyboardInterrupt, SystemExit):
  243. raise
  244. except:
  245. pass
  246. #import gc; gc.dump_rpy_heap('rpyheap-after-%s.dump' % goal)
  247. return res
  248. @taskdef([], "Annotating&simplifying")
  249. def task_annotate(self):
  250. """ Annotate
  251. """
  252. # includes annotation and annotatation simplifications
  253. translator = self.translator
  254. policy = self.policy
  255. self.log.info('with policy: %s.%s' % (policy.__class__.__module__, policy.__class__.__name__))
  256. annotator = translator.buildannotator(policy=policy)
  257. if self.secondary_entrypoints is not None:
  258. for func, inputtypes in self.secondary_entrypoints:
  259. if inputtypes == Ellipsis:
  260. continue
  261. annotator.build_types(func, inputtypes, False)
  262. if self.entry_point:
  263. s = annotator.build_types(self.entry_point, self.inputtypes)
  264. translator.entry_point_graph = annotator.bookkeeper.getdesc(self.entry_point).getuniquegraph()
  265. else:
  266. s = None
  267. self.sanity_check_annotation()
  268. if self.entry_point and self.standalone and s.knowntype != int:
  269. raise Exception("stand-alone program entry point must return an "
  270. "int (and not, e.g., None or always raise an "
  271. "exception).")
  272. annotator.complete()
  273. annotator.simplify()
  274. return s
  275. def sanity_check_annotation(self):
  276. translator = self.translator
  277. irreg = query.qoutput(query.check_exceptblocks_qgen(translator))
  278. if irreg:
  279. self.log.info("Some exceptblocks seem insane")
  280. lost = query.qoutput(query.check_methods_qgen(translator))
  281. assert not lost, "lost methods, something gone wrong with the annotation of method defs"
  282. RTYPE = 'rtype_lltype'
  283. @taskdef(['annotate'], "RTyping")
  284. def task_rtype_lltype(self):
  285. """ RTyping - lltype version
  286. """
  287. rtyper = self.translator.buildrtyper()
  288. rtyper.specialize(dont_simplify_again=True)
  289. @taskdef([RTYPE], "JIT compiler generation")
  290. def task_pyjitpl_lltype(self):
  291. """ Generate bytecodes for JIT and flow the JIT helper functions
  292. lltype version
  293. """
  294. from rpython.jit.codewriter.policy import JitPolicy
  295. get_policy = self.extra.get('jitpolicy', None)
  296. if get_policy is None:
  297. self.jitpolicy = JitPolicy()
  298. else:
  299. self.jitpolicy = get_policy(self)
  300. #
  301. from rpython.jit.metainterp.warmspot import apply_jit
  302. apply_jit(self.translator, policy=self.jitpolicy,
  303. backend_name=self.config.translation.jit_backend, inline=True)
  304. #
  305. self.log.info("the JIT compiler was generated")
  306. @taskdef([RTYPE], "test of the JIT on the llgraph backend")
  307. def task_jittest_lltype(self):
  308. """ Run with the JIT on top of the llgraph backend
  309. """
  310. # parent process loop: spawn a child, wait for the child to finish,
  311. # print a message, and restart
  312. from rpython.translator.goal import unixcheckpoint
  313. unixcheckpoint.restartable_point(auto='run')
  314. # load the module rpython/jit/tl/jittest.py, which you can hack at
  315. # and restart without needing to restart the whole translation process
  316. from rpython.jit.tl import jittest
  317. jittest.jittest(self)
  318. BACKENDOPT = 'backendopt_lltype'
  319. @taskdef([RTYPE, '??pyjitpl_lltype', '??jittest_lltype'], "lltype back-end optimisations")
  320. def task_backendopt_lltype(self):
  321. """ Run all backend optimizations - lltype version
  322. """
  323. from rpython.translator.backendopt.all import backend_optimizations
  324. backend_optimizations(self.translator)
  325. STACKCHECKINSERTION = 'stackcheckinsertion_lltype'
  326. @taskdef(['?'+BACKENDOPT, RTYPE, 'annotate'], "inserting stack checks")
  327. def task_stackcheckinsertion_lltype(self):
  328. from rpython.translator.transform import insert_ll_stackcheck
  329. count = insert_ll_stackcheck(self.translator)
  330. self.log.info("inserted %d stack checks." % (count,))
  331. def possibly_check_for_boehm(self):
  332. if self.config.translation.gc == "boehm":
  333. from rpython.rtyper.tool.rffi_platform import configure_boehm
  334. from rpython.translator.platform import CompilationError
  335. try:
  336. configure_boehm(self.translator.platform)
  337. except CompilationError as e:
  338. i = 'Boehm GC not installed. Try e.g. "translate.py --gc=minimark"'
  339. raise Exception(str(e) + '\n' + i)
  340. @taskdef([STACKCHECKINSERTION, '?'+BACKENDOPT, RTYPE, '?annotate'],
  341. "Creating database for generating c source",
  342. earlycheck = possibly_check_for_boehm)
  343. def task_database_c(self):
  344. """ Create a database for further backend generation
  345. """
  346. translator = self.translator
  347. if translator.annotator is not None:
  348. translator.frozen = True
  349. standalone = self.standalone
  350. if standalone:
  351. from rpython.translator.c.genc import CStandaloneBuilder
  352. cbuilder = CStandaloneBuilder(self.translator, self.entry_point,
  353. config=self.config,
  354. secondary_entrypoints=
  355. self.secondary_entrypoints + annotated_jit_entrypoints)
  356. else:
  357. from rpython.translator.c.dlltool import CLibraryBuilder
  358. functions = [(self.entry_point, None)] + self.secondary_entrypoints + annotated_jit_entrypoints
  359. cbuilder = CLibraryBuilder(self.translator, self.entry_point,
  360. functions=functions,
  361. name='libtesting',
  362. config=self.config)
  363. if not standalone: # xxx more messy
  364. cbuilder.modulename = self.extmod_name
  365. database = cbuilder.build_database()
  366. self.log.info("database for generating C source was created")
  367. self.cbuilder = cbuilder
  368. self.database = database
  369. @taskdef(['database_c'], "Generating c source")
  370. def task_source_c(self):
  371. """ Create C source files from the generated database
  372. """
  373. cbuilder = self.cbuilder
  374. database = self.database
  375. if self._backend_extra_options.get('c_debug_defines', False):
  376. defines = cbuilder.DEBUG_DEFINES
  377. else:
  378. defines = {}
  379. if self.exe_name is not None:
  380. exe_name = self.exe_name % self.get_info()
  381. else:
  382. exe_name = None
  383. c_source_filename = cbuilder.generate_source(database, defines,
  384. exe_name=exe_name)
  385. self.log.info("written: %s" % (c_source_filename,))
  386. if self.config.translation.dump_static_data_info:
  387. from rpython.translator.tool.staticsizereport import dump_static_data_info
  388. targetdir = cbuilder.targetdir
  389. fname = dump_static_data_info(self.log, database, targetdir)
  390. dstname = self.compute_exe_name() + '.staticdata.info'
  391. shutil_copy(str(fname), str(dstname))
  392. self.log.info('Static data info written to %s' % dstname)
  393. def compute_exe_name(self, suffix=''):
  394. newexename = self.exe_name % self.get_info()
  395. if '/' not in newexename and '\\' not in newexename:
  396. newexename = './' + newexename
  397. newname = py.path.local(newexename)
  398. if suffix:
  399. newname = newname.new(purebasename = newname.purebasename + suffix)
  400. return newname
  401. def create_exe(self):
  402. """ Copy the compiled executable into current directory, which is
  403. pypy/goal on nightly builds
  404. """
  405. if self.exe_name is not None:
  406. exename = self.c_entryp
  407. newexename = mkexename(self.compute_exe_name())
  408. shutil_copy(str(exename), str(newexename))
  409. if self.cbuilder.shared_library_name is not None:
  410. soname = self.cbuilder.shared_library_name
  411. newsoname = newexename.new(basename=soname.basename)
  412. shutil_copy(str(soname), str(newsoname))
  413. self.log.info("copied: %s" % (newsoname,))
  414. if sys.platform == 'win32':
  415. # Copy pypyw.exe
  416. newexename = mkexename(self.compute_exe_name(suffix='w'))
  417. exe = py.path.local(exename)
  418. exename = exe.new(purebasename=exe.purebasename + 'w')
  419. shutil_copy(str(exename), str(newexename))
  420. # for pypy, the import library is renamed and moved to
  421. # libs/python27.lib, according to the pragma in pyconfig.h
  422. libname = self.config.translation.libname
  423. oldlibname = soname.new(ext='lib')
  424. if not libname:
  425. libname = oldlibname.basename
  426. libname = str(newsoname.dirpath().join(libname))
  427. shutil.copyfile(str(oldlibname), libname)
  428. self.log.info("copied: %s to %s" % (oldlibname, libname,))
  429. # the pdb file goes in the same place as pypy(w).exe
  430. ext_to_copy = ['pdb',]
  431. for ext in ext_to_copy:
  432. name = soname.new(ext=ext)
  433. newname = newexename.new(basename=soname.basename)
  434. shutil.copyfile(str(name), str(newname.new(ext=ext)))
  435. self.log.info("copied: %s" % (newname,))
  436. self.c_entryp = newexename
  437. self.log.info("created: %s" % (self.c_entryp,))
  438. @taskdef(['source_c'], "Compiling c source")
  439. def task_compile_c(self):
  440. """ Compile the generated C code using either makefile or
  441. translator/platform
  442. """
  443. cbuilder = self.cbuilder
  444. kwds = {}
  445. if self.standalone and self.exe_name is not None:
  446. kwds['exe_name'] = self.compute_exe_name().basename
  447. cbuilder.compile(**kwds)
  448. if self.standalone:
  449. self.c_entryp = cbuilder.executable_name
  450. self.create_exe()
  451. else:
  452. self.c_entryp = cbuilder.get_entry_point()
  453. @taskdef([STACKCHECKINSERTION, '?'+BACKENDOPT, RTYPE], "LLInterpreting")
  454. def task_llinterpret_lltype(self):
  455. from rpython.rtyper.llinterp import LLInterpreter
  456. translator = self.translator
  457. interp = LLInterpreter(translator.rtyper)
  458. bk = translator.annotator.bookkeeper
  459. graph = bk.getdesc(self.entry_point).getuniquegraph()
  460. v = interp.eval_graph(graph,
  461. self.extra.get('get_llinterp_args',
  462. lambda: [])())
  463. log.llinterpret("result -> %s" % v)
  464. def proceed(self, goals):
  465. if not goals:
  466. if self.default_goal:
  467. goals = [self.default_goal]
  468. else:
  469. self.log.info("nothing to do")
  470. return
  471. elif isinstance(goals, str):
  472. goals = [goals]
  473. goals.extend(self.extra_goals)
  474. goals = self.backend_select_goals(goals)
  475. result = self._execute(goals, task_skip = self._maybe_skip())
  476. self.log.info('usession directory: %s' % (udir,))
  477. return result
  478. @classmethod
  479. def from_targetspec(cls, targetspec_dic, config=None, args=None,
  480. empty_translator=None,
  481. disable=[],
  482. default_goal=None):
  483. if args is None:
  484. args = []
  485. driver = cls(config=config, default_goal=default_goal,
  486. disable=disable)
  487. target = targetspec_dic['target']
  488. spec = target(driver, args)
  489. try:
  490. entry_point, inputtypes, policy = spec
  491. except TypeError:
  492. # not a tuple at all
  493. entry_point = spec
  494. inputtypes = policy = None
  495. except ValueError:
  496. policy = None
  497. entry_point, inputtypes = spec
  498. driver.setup(entry_point, inputtypes,
  499. policy=policy,
  500. extra=targetspec_dic,
  501. empty_translator=empty_translator)
  502. return driver
  503. def prereq_checkpt_rtype(self):
  504. assert 'rpython.rtyper.rmodel' not in sys.modules, (
  505. "cannot fork because the rtyper has already been imported")
  506. prereq_checkpt_rtype_lltype = prereq_checkpt_rtype
  507. # checkpointing support
  508. def _event(self, kind, goal, func):
  509. if kind == 'planned' and func.task_earlycheck:
  510. func.task_earlycheck(self)
  511. if kind == 'pre':
  512. fork_before = self.config.translation.fork_before
  513. if fork_before:
  514. fork_before, = self.backend_select_goals([fork_before])
  515. if not fork_before in self.done and fork_before == goal:
  516. prereq = getattr(self, 'prereq_checkpt_%s' % goal, None)
  517. if prereq:
  518. prereq()
  519. from rpython.translator.goal import unixcheckpoint
  520. unixcheckpoint.restartable_point(auto='run')
  521. def mkexename(name):
  522. if sys.platform == 'win32':
  523. name = name.new(ext='exe')
  524. return name
  525. if os.name == 'posix':
  526. def shutil_copy(src, dst):
  527. # this version handles the case where 'dst' is an executable
  528. # currently being executed
  529. shutil.copy(src, dst + '~')
  530. os.rename(dst + '~', dst)
  531. else:
  532. shutil_copy = shutil.copy