1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
import py, os, sys
from rpython.jit.metainterp.test.support import LLJitMixin
from rpython.rlib.objectmodel import specialize, instantiate
from rpython.rlib import rarithmetic, rbigint, jit
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rtyper import llinterp
from pypy.interpreter.baseobjspace import InternalSpaceCache, W_Root
from pypy.module.cppyy import interp_cppyy, capi, executor
# These tests are for the backend that support the fast path only.
if capi.identify() == 'CINT':
py.test.skip("CINT does not support fast path")
elif capi.identify() == 'loadable_capi':
py.test.skip("can not currently use FakeSpace with _cffi_backend")
elif os.getenv("CPPYY_DISABLE_FASTPATH"):
py.test.skip("fast path is disabled by CPPYY_DISABLE_FASTPATH envar")
# load cpyext early, or its global vars are counted as leaks in the test
# (note that the module is not otherwise used in the test itself)
import pypy.module.cpyext
# change capi's direct_ptradd and exchange_address to being jit-opaque
@jit.dont_look_inside
def _opaque_direct_ptradd(ptr, offset):
address = rffi.cast(rffi.CCHARP, ptr)
return rffi.cast(capi.C_OBJECT, lltype.direct_ptradd(address, offset))
capi.direct_ptradd = _opaque_direct_ptradd
@jit.dont_look_inside
def _opaque_exchange_address(ptr, cif_descr, index):
offset = rffi.cast(rffi.LONG, cif_descr.exchange_args[index])
return rffi.ptradd(ptr, offset)
capi.exchange_address = _opaque_exchange_address
# add missing alt_errno (??)
def get_tlobj(self):
try:
return self._tlobj
except AttributeError:
from rpython.rtyper.lltypesystem import rffi
PERRNO = rffi.CArrayPtr(rffi.INT)
fake_p_errno = lltype.malloc(PERRNO.TO, 1, flavor='raw', zero=True,
track_allocation=False)
self._tlobj = {'RPY_TLOFS_p_errno': fake_p_errno,
'RPY_TLOFS_alt_errno': rffi.cast(rffi.INT, 0),
#'thread_ident': ...,
}
return self._tlobj
llinterp.LLInterpreter.get_tlobj = get_tlobj
currpath = py.path.local(__file__).dirpath()
test_dct = str(currpath.join("example01Dict.so"))
def setup_module(mod):
if sys.platform == 'win32':
py.test.skip("win32 not supported so far")
err = os.system("cd '%s' && make example01Dict.so" % currpath)
if err:
raise OSError("'make' failed (see stderr)")
class FakeBase(W_Root):
typename = None
class FakeBool(FakeBase):
typename = "bool"
def __init__(self, val):
self.val = val
class FakeInt(FakeBase):
typename = "int"
def __init__(self, val):
self.val = val
class FakeLong(FakeBase):
typename = "long"
def __init__(self, val):
self.val = val
class FakeFloat(FakeBase):
typename = "float"
def __init__(self, val):
self.val = val
class FakeString(FakeBase):
typename = "str"
def __init__(self, val):
self.val = val
class FakeType(FakeBase):
typename = "type"
def __init__(self, name):
self.name = name
self.__name__ = name
def getname(self, space, name):
return self.name
class FakeBuffer(FakeBase):
typedname = "buffer"
def __init__(self, val):
self.val = val
def get_raw_address(self):
raise ValueError("no raw buffer")
class FakeException(FakeType):
def __init__(self, space, name):
FakeType.__init__(self, name)
self.msg = name
self.space = space
class FakeUserDelAction(object):
def __init__(self, space):
pass
def register_callback(self, w_obj, callback, descrname):
pass
def perform(self, executioncontext, frame):
pass
class FakeState(object):
def __init__(self, space):
self.slowcalls = 0
class FakeSpace(object):
fake = True
w_None = None
w_str = FakeType("str")
w_int = FakeType("int")
w_float = FakeType("float")
def __init__(self):
self.fromcache = InternalSpaceCache(self).getorbuild
self.user_del_action = FakeUserDelAction(self)
class dummy: pass
self.config = dummy()
self.config.translating = False
# kill calls to c_call_i (i.e. slow path)
def c_call_i(space, cppmethod, cppobject, nargs, args):
assert not "slow path called"
return capi.c_call_i(space, cppmethod, cppobject, nargs, args)
executor.get_executor(self, 'int').__class__.c_stubcall = staticmethod(c_call_i)
self.w_AttributeError = FakeException(self, "AttributeError")
self.w_KeyError = FakeException(self, "KeyError")
self.w_NotImplementedError = FakeException(self, "NotImplementedError")
self.w_ReferenceError = FakeException(self, "ReferenceError")
self.w_RuntimeError = FakeException(self, "RuntimeError")
self.w_SystemError = FakeException(self, "SystemError")
self.w_TypeError = FakeException(self, "TypeError")
self.w_ValueError = FakeException(self, "ValueError")
def issequence_w(self, w_obj):
return True
def wrap(self, obj):
assert 0
@specialize.argtype(1)
def newbool(self, obj):
return FakeBool(obj)
@specialize.argtype(1)
def newint(self, obj):
if not isinstance(obj, int):
return FakeLong(rbigint.rbigint.fromrarith_int(obj))
return FakeInt(obj)
@specialize.argtype(1)
def newlong(self, obj):
return FakeLong(rbigint.rbigint.fromint(obj))
@specialize.argtype(1)
def newlong_from_rarith_int(self, obj):
return FakeLong(rbigint.rbigint.fromrarith_int(obj))
def newlong_from_rbigint(self, val):
return FakeLong(obj)
@specialize.argtype(1)
def newfloat(self, obj):
return FakeFloat(obj)
@specialize.argtype(1)
def newbytes(self, obj):
return FakeString(obj)
@specialize.argtype(1)
def newtext(self, obj):
return FakeString(obj)
def float_w(self, w_obj, allow_conversion=True):
assert isinstance(w_obj, FakeFloat)
return w_obj.val
@specialize.arg(1)
def interp_w(self, RequiredClass, w_obj, can_be_None=False):
if can_be_None and w_obj is None:
return None
if not isinstance(w_obj, RequiredClass):
raise TypeError
return w_obj
def getarg_w(self, code, w_obj): # for retrieving buffers
return FakeBuffer(w_obj)
def exception_match(self, typ, sub):
return typ is sub
def is_w(self, w_one, w_two):
return w_one is w_two
def int_w(self, w_obj, allow_conversion=True):
assert isinstance(w_obj, FakeInt)
return w_obj.val
def uint_w(self, w_obj):
assert isinstance(w_obj, FakeLong)
return rarithmetic.r_uint(w_obj.val.touint())
def str_w(self, w_obj):
assert isinstance(w_obj, FakeString)
return w_obj.val
def str(self, obj):
assert isinstance(obj, str)
return obj
c_int_w = int_w
r_longlong_w = int_w
r_ulonglong_w = uint_w
def is_(self, w_obj1, w_obj2):
return w_obj1 is w_obj2
def isinstance_w(self, w_obj, w_type):
assert isinstance(w_obj, FakeBase)
return w_obj.typename == w_type.name
def is_true(self, w_obj):
return not not w_obj
def type(self, w_obj):
return FakeType("fake")
def getattr(self, w_obj, w_name):
assert isinstance(w_obj, FakeException)
assert self.str_w(w_name) == "__name__"
return FakeString(w_obj.name)
def findattr(self, w_obj, w_name):
return None
def allocate_instance(self, cls, w_type):
return instantiate(cls)
def call_function(self, w_func, *args_w):
return None
def _freeze_(self):
return True
class TestFastPathJIT(LLJitMixin):
def _run_zjit(self, method_name):
space = FakeSpace()
drv = jit.JitDriver(greens=[], reds=["i", "inst", "cppmethod"])
def f():
lib = interp_cppyy.load_dictionary(space, "./example01Dict.so")
cls = interp_cppyy.scope_byname(space, "example01")
inst = cls.get_overload("example01").call(None, [FakeInt(0)])
cppmethod = cls.get_overload(method_name)
assert isinstance(inst, interp_cppyy.W_CPPInstance)
i = 10
while i > 0:
drv.jit_merge_point(inst=inst, cppmethod=cppmethod, i=i)
cppmethod.call(inst, [FakeInt(i)])
i -= 1
return 7
f()
space = FakeSpace()
result = self.meta_interp(f, [], listops=True, backendopt=True, listcomp=True)
self.check_jitcell_token_count(1) # same for fast and slow path??
# rely on replacement of capi calls to raise exception instead (see FakeSpace.__init__)
def test01_simple(self):
"""Test fast path being taken for methods"""
self._run_zjit("addDataToInt")
def test02_overload(self):
"""Test fast path being taken for overloaded methods"""
self._run_zjit("overloadedAddDataToInt")
def test03_const_ref(self):
"""Test fast path being taken for methods with const ref arguments"""
self._run_zjit("addDataToIntConstRef")
|