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

/rpython/translator/c/test/test_genc.py

https://bitbucket.org/pypy/pypy/
Python | 628 lines | 603 code | 20 blank | 5 comment | 31 complexity | 84a95d267fc96c6b7f0c332053a83e69 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import ctypes
  2. import re
  3. from collections import OrderedDict
  4. import py
  5. from rpython.rlib.rfloat import NAN, INFINITY
  6. from rpython.rlib.entrypoint import entrypoint_highlevel
  7. from rpython.rlib.unroll import unrolling_iterable
  8. from rpython.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint, intmask
  9. from rpython.rlib.objectmodel import specialize
  10. from rpython.rtyper.lltypesystem import lltype
  11. from rpython.rtyper.lltypesystem.lltype import *
  12. from rpython.rtyper.lltypesystem.rstr import STR
  13. from rpython.tool.nullpath import NullPyPathLocal
  14. from rpython.translator.c import genc
  15. from rpython.translator.backendopt.merge_if_blocks import merge_if_blocks
  16. from rpython.translator.interactive import Translation
  17. from rpython.translator.translator import TranslationContext, graphof
  18. signed_ffffffff = r_longlong(0xffffffff)
  19. unsigned_ffffffff = r_ulonglong(0xffffffff)
  20. def llrepr_in(v):
  21. if r_uint is not r_ulonglong and isinstance(v, r_ulonglong):
  22. return "%d:%d" % (intmask(v >> 32), intmask(v & unsigned_ffffffff))
  23. elif isinstance(v, r_longlong):
  24. return "%d:%d" % (intmask(v >> 32), intmask(v & signed_ffffffff))
  25. elif isinstance(v, float):
  26. return repr(v) # extra precision than str(v)
  27. elif isinstance(v, str):
  28. if v.isalnum():
  29. return v
  30. else: # escape the string
  31. return '/' + ','.join([str(ord(c)) for c in v])
  32. return str(v)
  33. @specialize.argtype(0)
  34. def llrepr_out(v):
  35. if isinstance(v, float):
  36. from rpython.rlib.rfloat import formatd, DTSF_ADD_DOT_0
  37. return formatd(v, 'r', 0, DTSF_ADD_DOT_0)
  38. return str(v) # always return a string, to get consistent types
  39. def parse_longlong(a):
  40. p0, p1 = a.split(":")
  41. return (r_longlong(int(p0)) << 32) + (r_longlong(int(p1)) &
  42. signed_ffffffff)
  43. def parse_ulonglong(a):
  44. p0, p1 = a.split(":")
  45. return (r_ulonglong(int(p0)) << 32) + (r_ulonglong(int(p1)) &
  46. unsigned_ffffffff)
  47. def compile(fn, argtypes, view=False, gcpolicy="none", backendopt=True,
  48. annotatorpolicy=None, thread=False, **kwds):
  49. argtypes_unroll = unrolling_iterable(enumerate(argtypes))
  50. for argtype in argtypes:
  51. if argtype not in [int, float, str, bool, r_ulonglong, r_longlong,
  52. r_uint]:
  53. raise Exception("Unsupported argtype, %r" % (argtype,))
  54. def entry_point(argv):
  55. args = ()
  56. for i, argtype in argtypes_unroll:
  57. a = argv[i + 1]
  58. if argtype is int:
  59. args += (int(a),)
  60. elif argtype is r_uint:
  61. args += (r_uint(int(a)),)
  62. elif argtype is r_longlong:
  63. args += (parse_longlong(a),)
  64. elif argtype is r_ulonglong:
  65. args += (parse_ulonglong(a),)
  66. elif argtype is bool:
  67. if a == 'True':
  68. args += (True,)
  69. else:
  70. assert a == 'False'
  71. args += (False,)
  72. elif argtype is float:
  73. if a == 'inf':
  74. args += (INFINITY,)
  75. elif a == '-inf':
  76. args += (-INFINITY,)
  77. elif a == 'nan':
  78. args += (NAN,)
  79. else:
  80. args += (float(a),)
  81. else:
  82. if a.startswith('/'): # escaped string
  83. if len(a) == 1:
  84. a = ''
  85. else:
  86. l = a[1:].split(',')
  87. a = ''.join([chr(int(x)) for x in l])
  88. args += (a,)
  89. res = fn(*args)
  90. print "THE RESULT IS:", llrepr_out(res), ";"
  91. return 0
  92. t = Translation(entry_point, None, gc=gcpolicy, backend="c",
  93. policy=annotatorpolicy, thread=thread, **kwds)
  94. if not backendopt:
  95. t.disable(["backendopt_lltype"])
  96. t.driver.config.translation.countmallocs = True
  97. t.annotate()
  98. try:
  99. if py.test.config.option.view:
  100. t.view()
  101. except AttributeError:
  102. pass
  103. t.rtype()
  104. if backendopt:
  105. t.backendopt()
  106. try:
  107. if py.test.config.option.view:
  108. t.view()
  109. except AttributeError:
  110. pass
  111. t.compile_c()
  112. ll_res = graphof(t.context, fn).getreturnvar().concretetype
  113. def output(stdout):
  114. for line in stdout.splitlines(False):
  115. if len(repr(line)) == len(line) + 2: # no escaped char
  116. print line
  117. else:
  118. print 'REPR:', repr(line)
  119. def f(*args, **kwds):
  120. expected_extra_mallocs = kwds.pop('expected_extra_mallocs', 0)
  121. expected_exception_name = kwds.pop('expected_exception_name', None)
  122. assert not kwds
  123. assert len(args) == len(argtypes)
  124. for arg, argtype in zip(args, argtypes):
  125. assert isinstance(arg, argtype)
  126. stdout = t.driver.cbuilder.cmdexec(
  127. " ".join([llrepr_in(arg) for arg in args]),
  128. expect_crash=(expected_exception_name is not None))
  129. #
  130. if expected_exception_name is not None:
  131. stdout, stderr = stdout
  132. print '--- stdout ---'
  133. output(stdout)
  134. print '--- stderr ---'
  135. output(stderr)
  136. print '--------------'
  137. stderr, prevline, lastline, empty = stderr.rsplit('\n', 3)
  138. assert empty == ''
  139. expected = 'Fatal RPython error: ' + expected_exception_name
  140. assert lastline == expected or prevline == expected
  141. return None
  142. output(stdout)
  143. stdout, lastline, empty = stdout.rsplit('\n', 2)
  144. assert empty == ''
  145. assert lastline.startswith('MALLOC COUNTERS: ')
  146. mallocs, frees = map(int, lastline.split()[2:])
  147. assert stdout.endswith(' ;')
  148. pos = stdout.rindex('THE RESULT IS: ')
  149. res = stdout[pos + len('THE RESULT IS: '):-2]
  150. #
  151. if isinstance(expected_extra_mallocs, int):
  152. assert mallocs - frees == expected_extra_mallocs
  153. else:
  154. assert mallocs - frees in expected_extra_mallocs
  155. #
  156. if ll_res in [lltype.Signed, lltype.Unsigned, lltype.SignedLongLong,
  157. lltype.UnsignedLongLong]:
  158. return int(res)
  159. elif ll_res == lltype.Bool:
  160. return bool(int(res))
  161. elif ll_res == lltype.Char:
  162. assert len(res) == 1
  163. return res
  164. elif ll_res == lltype.Float:
  165. return float(res)
  166. elif ll_res == lltype.Ptr(STR):
  167. return res
  168. elif ll_res == lltype.Void:
  169. return None
  170. raise NotImplementedError("parsing %s" % (ll_res,))
  171. class CompilationResult(object):
  172. def __repr__(self):
  173. return 'CompilationResult(%s)' % (fn.__name__,)
  174. def __call__(self, *args, **kwds):
  175. return f(*args, **kwds)
  176. cr = CompilationResult()
  177. cr.t = t
  178. cr.builder = t.driver.cbuilder
  179. return cr
  180. def test_simple():
  181. def f(x):
  182. return x * 2
  183. f1 = compile(f, [int])
  184. assert f1(5) == 10
  185. assert f1(-123) == -246
  186. py.test.raises(Exception, f1, "world") # check that it's really typed
  187. def test_int_becomes_float():
  188. # used to crash "very often": the long chain of mangle() calls end
  189. # up converting the return value of f() from an int to a float, but
  190. # if blocks are followed in random order by the annotator, it will
  191. # very likely first follow the call to llrepr_out() done after the
  192. # call to f(), getting an int first (and a float only later).
  193. @specialize.arg(1)
  194. def mangle(x, chain):
  195. if chain:
  196. return mangle(x, chain[1:])
  197. return x - 0.5
  198. def f(x):
  199. if x > 10:
  200. x = mangle(x, (1,1,1,1,1,1,1,1,1,1))
  201. return x + 1
  202. f1 = compile(f, [int])
  203. assert f1(5) == 6
  204. assert f1(12) == 12.5
  205. def test_string_arg():
  206. def f(s):
  207. total = 0
  208. for c in s:
  209. total += ord(c)
  210. return total + len(s)
  211. f1 = compile(f, [str])
  212. for check in ['x', '', '\x00', '\x01', '\n', '\x7f', '\xff',
  213. '\x00\x00', '\x00\x01']:
  214. assert f1(check) == len(check) + sum(map(ord, check))
  215. def test_dont_write_source_files():
  216. from rpython.annotator.listdef import s_list_of_strings
  217. def f(argv):
  218. return len(argv)*2
  219. t = TranslationContext()
  220. t.buildannotator().build_types(f, [s_list_of_strings])
  221. t.buildrtyper().specialize()
  222. t.config.translation.countmallocs = True
  223. t.config.translation.dont_write_c_files = True
  224. builder = genc.CStandaloneBuilder(t, f, config=t.config)
  225. builder.generate_source()
  226. assert isinstance(builder.targetdir, NullPyPathLocal)
  227. for f in builder.targetdir.listdir():
  228. assert not str(f).endswith('.c')
  229. def test_rlist():
  230. def f(x):
  231. l = [x]
  232. l.append(x+1)
  233. return l[0] * l[-1]
  234. f1 = compile(f, [int])
  235. assert f1(5) == 30
  236. #assert f1(x=5) == 30
  237. def test_rptr():
  238. S = GcStruct('testing', ('x', Signed), ('y', Signed))
  239. def f(i):
  240. if i < 0:
  241. p = nullptr(S)
  242. else:
  243. p = malloc(S)
  244. p.x = i*2
  245. if i > 0:
  246. return p.x
  247. else:
  248. return -42
  249. f1 = compile(f, [int])
  250. assert f1(5) == 10
  251. #assert f1(i=5) == 10
  252. assert f1(1) == 2
  253. assert f1(0) == -42
  254. assert f1(-1) == -42
  255. assert f1(-5) == -42
  256. def test_empty_string():
  257. A = Array(Char, hints={'nolength': True})
  258. p = malloc(A, 1, immortal=True)
  259. def f():
  260. return p[0]
  261. f1 = compile(f, [])
  262. assert f1() == '\x00'
  263. def test_rstr():
  264. def fn(i):
  265. return "hello"[i]
  266. f1 = compile(fn, [int])
  267. res = f1(1)
  268. assert res == 'e'
  269. def test_recursive_struct():
  270. # B has an A as its super field, and A has a pointer to B.
  271. class A:
  272. pass
  273. class B(A):
  274. pass
  275. def fn(i):
  276. a = A()
  277. b = B()
  278. a.b = b
  279. b.i = i
  280. return a.b.i
  281. f1 = compile(fn, [int])
  282. res = f1(42)
  283. assert res == 42
  284. def test_recursive_struct_2():
  285. class L:
  286. def __init__(self, target):
  287. self.target = target
  288. class RL(L):
  289. pass
  290. class SL(L):
  291. pass
  292. class B:
  293. def __init__(self, exits):
  294. self.exits = exits
  295. def fn(i):
  296. rl = RL(None)
  297. b = B([rl])
  298. sl = SL(b)
  299. f1 = compile(fn, [int])
  300. f1(42)
  301. def test_infinite_float():
  302. x = 1.0
  303. while x != x / 2:
  304. x *= 3.1416
  305. def fn():
  306. return x
  307. f1 = compile(fn, [])
  308. res = f1()
  309. assert res > 0 and res == res / 2
  310. def fn():
  311. return -x
  312. f1 = compile(fn, [])
  313. res = f1()
  314. assert res < 0 and res == res / 2
  315. class Box:
  316. def __init__(self, d):
  317. self.d = d
  318. b1 = Box(x)
  319. b2 = Box(-x)
  320. b3 = Box(1.5)
  321. def f(i):
  322. if i==0:
  323. b = b1
  324. elif i==1:
  325. b = b2
  326. else:
  327. b = b3
  328. return b.d
  329. f1 = compile(f, [int])
  330. res = f1(0)
  331. assert res > 0 and res == res / 2
  332. res = f1(1)
  333. assert res < 0 and res == res / 2
  334. res = f1(3)
  335. assert res == 1.5
  336. def test_infinite_float_in_array():
  337. from rpython.rlib.rfloat import INFINITY, NAN, isnan
  338. lst = [INFINITY, -INFINITY, NAN]
  339. def fn(i):
  340. return lst[i]
  341. f1 = compile(fn, [int])
  342. res = f1(0)
  343. assert res == INFINITY
  344. res = f1(1)
  345. assert res == -INFINITY
  346. res = f1(2)
  347. assert isnan(res)
  348. def test_nan_and_special_values():
  349. from rpython.rlib.rfloat import isnan, isinf, isfinite, copysign
  350. inf = 1e300 * 1e300
  351. assert isinf(inf)
  352. nan = inf/inf
  353. assert isnan(nan)
  354. for value, checker in [
  355. (inf, lambda x: isinf(x) and x > 0.0),
  356. (-inf, lambda x: isinf(x) and x < 0.0),
  357. (nan, isnan),
  358. (42.0, isfinite),
  359. (0.0, lambda x: not x and copysign(1., x) == 1.),
  360. (-0.0, lambda x: not x and copysign(1., x) == -1.),
  361. ]:
  362. def f():
  363. return value
  364. f1 = compile(f, [])
  365. res = f1()
  366. assert checker(res)
  367. l = [value]
  368. def g(x):
  369. return l[x]
  370. g2 = compile(g, [int])
  371. res = g2(0)
  372. assert checker(res)
  373. l2 = [(-value, -value), (value, value)]
  374. def h(x):
  375. return l2[x][1]
  376. h3 = compile(h, [int])
  377. res = h3(1)
  378. assert checker(res)
  379. def test_prebuilt_instance_with_dict():
  380. class A:
  381. pass
  382. a = A()
  383. a.d = {}
  384. a.d['hey'] = 42
  385. def t():
  386. a.d['hey'] = 2
  387. return a.d['hey']
  388. f = compile(t, [])
  389. assert f() == 2
  390. def test_long_strings():
  391. s1 = 'hello'
  392. s2 = ''.join([chr(i) for i in range(256)])
  393. s3 = 'abcd'*17
  394. s4 = open(__file__, 'rb').read(2049)
  395. choices = [s1, s2, s3, s4]
  396. def f(i, j):
  397. return choices[i][j]
  398. f1 = compile(f, [int, int])
  399. for i, s in enumerate(choices):
  400. j = 0
  401. while j < len(s):
  402. c = s[j]
  403. assert f1(i, j) == c
  404. j += 1
  405. if j > 100:
  406. j += 10
  407. def test_keepalive():
  408. from rpython.rlib import objectmodel
  409. def f():
  410. x = [1]
  411. y = ['b']
  412. objectmodel.keepalive_until_here(x, y)
  413. return 1
  414. f1 = compile(f, [])
  415. assert f1() == 1
  416. def test_print():
  417. def f():
  418. for i in range(10):
  419. print "xxx"
  420. fn = compile(f, [])
  421. fn()
  422. def test_name():
  423. def f():
  424. return 3
  425. f.c_name = 'pypy_xyz_f'
  426. f.exported_symbol = True
  427. t = Translation(f, [], backend="c")
  428. t.annotate()
  429. t.compile_c()
  430. if py.test.config.option.view:
  431. t.view()
  432. assert hasattr(ctypes.CDLL(str(t.driver.c_entryp)), 'pypy_xyz_f')
  433. def test_entrypoints():
  434. def f():
  435. return 3
  436. key = "test_entrypoints42"
  437. @entrypoint_highlevel(key, [int], "foobar")
  438. def g(x):
  439. return x + 42
  440. t = Translation(f, [], backend="c", secondaryentrypoints="test_entrypoints42")
  441. t.annotate()
  442. t.compile_c()
  443. if py.test.config.option.view:
  444. t.view()
  445. assert hasattr(ctypes.CDLL(str(t.driver.c_entryp)), 'foobar')
  446. def test_exportstruct():
  447. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  448. from rpython.rlib.exports import export_struct
  449. def f():
  450. return 42
  451. FOO = Struct("FOO", ("field1", Signed))
  452. foo = malloc(FOO, flavor="raw")
  453. foo.field1 = 43
  454. export_struct("BarStruct", foo._obj)
  455. t = Translation(f, [], backend="c")
  456. t.annotate()
  457. t.compile_c()
  458. if py.test.config.option.view:
  459. t.view()
  460. assert hasattr(ctypes.CDLL(str(t.driver.c_entryp)), 'BarStruct')
  461. free(foo, flavor="raw")
  462. def test_recursive_llhelper():
  463. from rpython.rtyper.annlowlevel import llhelper
  464. from rpython.rtyper.lltypesystem import lltype
  465. from rpython.rlib.objectmodel import specialize
  466. FT = lltype.ForwardReference()
  467. FTPTR = lltype.Ptr(FT)
  468. STRUCT = lltype.Struct("foo", ("bar", FTPTR))
  469. FT.become(lltype.FuncType([lltype.Ptr(STRUCT)], lltype.Signed))
  470. class A:
  471. def __init__(self, func, name):
  472. self.func = func
  473. self.name = name
  474. def _freeze_(self):
  475. return True
  476. @specialize.memo()
  477. def make_func(self):
  478. f = getattr(self, "_f", None)
  479. if f is not None:
  480. return f
  481. f = lambda *args: self.func(*args)
  482. f.c_name = self.name
  483. f.relax_sig_check = True
  484. f.__name__ = "WRAP%s" % (self.name, )
  485. self._f = f
  486. return f
  487. def get_llhelper(self):
  488. return llhelper(FTPTR, self.make_func())
  489. def f(s):
  490. if s.bar == t.bar:
  491. lltype.free(s, flavor="raw")
  492. return 1
  493. lltype.free(s, flavor="raw")
  494. return 0
  495. def g(x):
  496. return 42
  497. def chooser(x):
  498. s = lltype.malloc(STRUCT, flavor="raw")
  499. if x:
  500. s.bar = llhelper(FTPTR, a_f.make_func())
  501. else:
  502. s.bar = llhelper(FTPTR, a_g.make_func())
  503. return f(s)
  504. a_f = A(f, "f")
  505. a_g = A(g, "g")
  506. t = lltype.malloc(STRUCT, flavor="raw", immortal=True)
  507. t.bar = llhelper(FTPTR, a_f.make_func())
  508. fn = compile(chooser, [bool])
  509. assert fn(True)
  510. def test_ordered_dict():
  511. expected = [('ea', 1), ('bb', 2), ('c', 3), ('d', 4), ('e', 5),
  512. ('ef', 6)]
  513. d = OrderedDict(expected)
  514. def f():
  515. assert d.items() == expected
  516. fn = compile(f, [])
  517. fn()
  518. def test_inhibit_tail_call():
  519. def foobar_fn(n):
  520. return 42
  521. foobar_fn._dont_inline_ = True
  522. def main(n):
  523. return foobar_fn(n)
  524. #
  525. t = Translation(main, [int], backend="c")
  526. t.rtype()
  527. t.context._graphof(foobar_fn).inhibit_tail_call = True
  528. t.source_c()
  529. lines = t.driver.cbuilder.c_source_filename.join('..',
  530. 'rpython_translator_c_test.c').readlines()
  531. for i, line in enumerate(lines):
  532. if '= pypy_g_foobar_fn' in line:
  533. break
  534. else:
  535. assert 0, "the call was not found in the C source"
  536. assert 'PYPY_INHIBIT_TAIL_CALL();' in lines[i+1]
  537. def get_generated_c_source(fn, types):
  538. """Return the generated C source for fn."""
  539. t = Translation(fn, types, backend="c")
  540. t.annotate()
  541. merge_if_blocks(t.driver.translator.graphs[0])
  542. c_filename_path = t.source_c()
  543. return t.driver.cbuilder.c_source_filename.join('..',
  544. 'rpython_translator_c_test.c').read()
  545. def test_generated_c_source_no_gotos():
  546. # We want simple functions to have no indirection/goto.
  547. # Instead, PyPy can inline blocks when they aren't reused.
  548. def main(x):
  549. return x + 1
  550. c_src = get_generated_c_source(main, [int])
  551. assert 'goto' not in c_src
  552. assert not re.search(r'block\w*:(?! \(inlined\))', c_src)