PageRenderTime 69ms CodeModel.GetById 13ms app.highlight 48ms RepoModel.GetById 2ms app.codeStats 0ms

/pypy/jit/backend/test/calling_convention_test.py

https://bitbucket.org/pypy/pypy/
Python | 354 lines | 323 code | 31 blank | 0 comment | 29 complexity | 229a4b880a18c93030e62e57d2186a9b MD5 | raw file
  1from pypy.jit.metainterp.history import (AbstractFailDescr,
  2                                         AbstractDescr,
  3                                         BasicFailDescr,
  4                                         BoxInt, Box, BoxPtr,
  5                                         JitCellToken,
  6                                         ConstInt, ConstPtr,
  7                                         BoxObj, Const,
  8                                         ConstObj, BoxFloat, ConstFloat)
  9from pypy.jit.metainterp.resoperation import ResOperation, rop
 10from pypy.jit.metainterp.typesystem import deref
 11from pypy.jit.codewriter.effectinfo import EffectInfo
 12from pypy.jit.tool.oparser import parse
 13from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rffi, rclass
 14from pypy.rpython.ootypesystem import ootype
 15from pypy.rpython.annlowlevel import llhelper
 16from pypy.rpython.llinterp import LLException
 17from pypy.jit.codewriter import heaptracker, longlong
 18from pypy.rlib.rarithmetic import intmask
 19from pypy.jit.backend.detect_cpu import getcpuclass
 20from pypy.jit.backend.test.runner_test import Runner
 21
 22def boxfloat(x):
 23    return BoxFloat(longlong.getfloatstorage(x))
 24
 25def constfloat(x):
 26    return ConstFloat(longlong.getfloatstorage(x))
 27
 28class FakeStats(object):
 29    pass
 30class TestCallingConv(Runner):
 31    type_system = 'lltype'
 32    Ptr = lltype.Ptr
 33    FuncType = lltype.FuncType
 34
 35    def setup_class(cls):
 36        cls.cpu = getcpuclass()(rtyper=None, stats=FakeStats())
 37        cls.cpu.setup_once()
 38
 39    def _prepare_args(self, args, floats, ints):
 40        local_floats = list(floats)
 41        local_ints = list(ints)
 42        expected_result = 0.0
 43        arguments = []
 44        for i in range(len(args)):
 45            x = args[i]
 46            if x[0] == 'f':
 47                x = local_floats.pop()
 48                t = longlong.getfloatstorage(x)
 49                arguments.append(t)
 50            else:
 51                x = local_ints.pop()
 52                arguments.append(x)
 53            expected_result += x
 54        return arguments, expected_result
 55
 56    @classmethod
 57    def get_funcbox(cls, cpu, func_ptr):
 58        addr = llmemory.cast_ptr_to_adr(func_ptr)
 59        return ConstInt(heaptracker.adr2int(addr))
 60
 61    def test_call_aligned_with_spilled_values(self):
 62        from pypy.rlib.libffi import types
 63        cpu = self.cpu
 64        if not cpu.supports_floats:
 65            py.test.skip('requires floats')
 66
 67
 68        def func(*args):
 69            return float(sum(args))
 70
 71        F = lltype.Float
 72        I = lltype.Signed
 73        floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56]
 74        ints = [7, 11, 23, 13, -42, 1111, 95, 1]
 75        for case in range(256):
 76            local_floats = list(floats)
 77            local_ints = list(ints)
 78            args = []
 79            spills = []
 80            funcargs = []
 81            float_count = 0
 82            int_count = 0
 83            for i in range(8):
 84                if case & (1<<i):
 85                    args.append('f%d' % float_count)
 86                    spills.append('force_spill(f%d)' % float_count)
 87                    float_count += 1
 88                    funcargs.append(F)
 89                else:
 90                    args.append('i%d' % int_count)
 91                    spills.append('force_spill(i%d)' % int_count)
 92                    int_count += 1
 93                    funcargs.append(I)
 94
 95            arguments = ', '.join(args)
 96            spill_ops = '\n'.join(spills)
 97
 98            FUNC = self.FuncType(funcargs, F)
 99            FPTR = self.Ptr(FUNC)
100            func_ptr = llhelper(FPTR, func)
101            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
102                                        EffectInfo.MOST_GENERAL)
103            funcbox = self.get_funcbox(cpu, func_ptr)
104
105            ops = '[%s]\n' % arguments
106            ops += '%s\n' % spill_ops
107            ops += 'f99 = call(ConstClass(func_ptr), %s, descr=calldescr)\n' % arguments
108            ops += 'finish(f99, %s)\n' % arguments
109
110            loop = parse(ops, namespace=locals())
111            looptoken = JitCellToken()
112            done_number = self.cpu.get_fail_descr_number(loop.operations[-1].getdescr())
113            self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
114            argvals, expected_result = self._prepare_args(args, floats, ints)
115
116            res = self.cpu.execute_token(looptoken, *argvals)
117            x = longlong.getrealfloat(cpu.get_latest_value_float(0))
118            assert abs(x - expected_result) < 0.0001
119
120    def test_call_aligned_with_imm_values(self):
121        from pypy.rlib.libffi import types
122        cpu = self.cpu
123        if not cpu.supports_floats:
124            py.test.skip('requires floats')
125
126
127        def func(*args):
128            return float(sum(args))
129
130        F = lltype.Float
131        I = lltype.Signed
132        floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56]
133        ints = [7, 11, 23, 13, -42, 1111, 95, 1]
134        for case in range(256):
135            result = 0.0
136            args = []
137            argslist = []
138            local_floats = list(floats)
139            local_ints = list(ints)
140            for i in range(8):
141                if case & (1<<i):
142                    args.append(F)
143                    arg = local_floats.pop()
144                    result += arg
145                    argslist.append(constfloat(arg))
146                else:
147                    args.append(I)
148                    arg = local_ints.pop()
149                    result += arg
150                    argslist.append(ConstInt(arg))
151            FUNC = self.FuncType(args, F)
152            FPTR = self.Ptr(FUNC)
153            func_ptr = llhelper(FPTR, func)
154            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
155                                        EffectInfo.MOST_GENERAL)
156            funcbox = self.get_funcbox(cpu, func_ptr)
157
158            res = self.execute_operation(rop.CALL,
159                                         [funcbox] + argslist,
160                                         'float', descr=calldescr)
161            assert abs(res.getfloat() - result) < 0.0001
162
163    def test_call_aligned_with_args_on_the_stack(self):
164        from pypy.rlib.libffi import types
165        cpu = self.cpu
166        if not cpu.supports_floats:
167            py.test.skip('requires floats')
168
169
170        def func(*args):
171            return float(sum(args))
172
173        F = lltype.Float
174        I = lltype.Signed
175        floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56]
176        ints = [7, 11, 23, 13, -42, 1111, 95, 1]
177        for case in range(256):
178            result = 0.0
179            args = []
180            argslist = []
181            local_floats = list(floats)
182            local_ints = list(ints)
183            for i in range(8):
184                if case & (1<<i):
185                    args.append(F)
186                    arg = local_floats.pop()
187                    result += arg
188                    argslist.append(boxfloat(arg))
189                else:
190                    args.append(I)
191                    arg = local_ints.pop()
192                    result += arg
193                    argslist.append(BoxInt(arg))
194            FUNC = self.FuncType(args, F)
195            FPTR = self.Ptr(FUNC)
196            func_ptr = llhelper(FPTR, func)
197            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
198                                        EffectInfo.MOST_GENERAL)
199            funcbox = self.get_funcbox(cpu, func_ptr)
200
201            res = self.execute_operation(rop.CALL,
202                                         [funcbox] + argslist,
203                                         'float', descr=calldescr)
204            assert abs(res.getfloat() - result) < 0.0001
205
206    def test_call_alignment_call_assembler(self):
207        from pypy.rlib.libffi import types
208        cpu = self.cpu
209        if not cpu.supports_floats:
210            py.test.skip('requires floats')
211
212        fdescr3 = BasicFailDescr(3)
213        fdescr4 = BasicFailDescr(4)
214
215        def assembler_helper(failindex, virtualizable):
216            assert 0, 'should not be called, but was with failindex (%d)' % failindex
217            return 13
218
219        FUNCPTR = lltype.Ptr(lltype.FuncType([lltype.Signed, llmemory.GCREF],
220                                             lltype.Signed))
221        class FakeJitDriverSD:
222            index_of_virtualizable = -1
223            _assembler_helper_ptr = llhelper(FUNCPTR, assembler_helper)
224            assembler_helper_adr = llmemory.cast_ptr_to_adr(
225                _assembler_helper_ptr)
226
227        floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56]
228        ints = [7, 11, 23, 42, -42, 1111, 95, 1]
229
230        for case in range(256):
231            float_count = 0
232            int_count = 0
233            args = []
234            called_ops = ''
235            total_index = -1
236            for i in range(8):
237                if case & (1<<i):
238                    args.append('f%d' % float_count)
239                else:
240                    args.append('i%d' % int_count)
241                    called_ops += 'f%d = cast_int_to_float(i%d)\n' % (
242                        float_count, int_count)
243                    int_count += 1
244                if total_index == -1:
245                    total_index = float_count
246                    float_count += 1
247                else:
248                    called_ops += 'f%d = float_add(f%d, f%d)\n' % (
249                        float_count + 1, total_index, float_count)
250                    total_index = float_count + 1
251                    float_count += 2
252            arguments = ', '.join(args)
253            called_ops = '[%s]\n' % arguments + called_ops
254            called_ops += 'finish(f%d, descr=fdescr3)\n' % total_index
255            # compile called loop
256            called_loop = parse(called_ops, namespace=locals())
257            called_looptoken = JitCellToken()
258            called_looptoken.outermost_jitdriver_sd = FakeJitDriverSD()
259            done_number = self.cpu.get_fail_descr_number(called_loop.operations[-1].getdescr())
260            self.cpu.compile_loop(called_loop.inputargs, called_loop.operations, called_looptoken)
261
262            argvals, expected_result = self._prepare_args(args, floats, ints)
263            res = cpu.execute_token(called_looptoken, *argvals)
264            assert res.identifier == 3
265            t = longlong.getrealfloat(cpu.get_latest_value_float(0))
266            assert abs(t - expected_result) < 0.0001
267
268            ARGS = []
269            RES = lltype.Float
270            for x in args:
271                if x[0] == 'f':
272                    ARGS.append(lltype.Float)
273                else:
274                    ARGS.append(lltype.Signed)
275            FakeJitDriverSD.portal_calldescr = self.cpu.calldescrof(
276                lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES,
277                EffectInfo.MOST_GENERAL)
278            ops = '''
279            [%s]
280            f99 = call_assembler(%s, descr=called_looptoken)
281            guard_not_forced()[]
282            finish(f99, descr=fdescr4)
283            ''' % (arguments, arguments)
284            loop = parse(ops, namespace=locals())
285            # we want to take the fast path
286            self.cpu.done_with_this_frame_float_v = done_number
287            try:
288                othertoken = JitCellToken()
289                self.cpu.compile_loop(loop.inputargs, loop.operations, othertoken)
290
291                # prepare call to called_loop
292                argvals, _ = self._prepare_args(args, floats, ints)
293                res = cpu.execute_token(othertoken, *argvals)
294                x = longlong.getrealfloat(cpu.get_latest_value_float(0))
295                assert res.identifier == 4
296                assert abs(x - expected_result) < 0.0001
297            finally:
298                del self.cpu.done_with_this_frame_float_v
299
300    def test_call_with_singlefloats(self):
301        cpu = self.cpu
302        if not cpu.supports_floats or not cpu.supports_singlefloats:
303            py.test.skip('requires floats and singlefloats')
304
305        import random
306        from pypy.rlib.libffi import types
307        from pypy.rlib.rarithmetic import r_singlefloat
308
309        def func(*args):
310            res = 0.0
311            for i, x in enumerate(args):
312                res += (i + 1.1) * float(x)
313            return res
314
315        F = lltype.Float
316        S = lltype.SingleFloat
317        I = lltype.Signed
318        floats = [random.random() - 0.5 for i in range(8)]
319        singlefloats = [r_singlefloat(random.random() - 0.5) for i in range(8)]
320        ints = [random.randrange(-99, 99) for i in range(8)]
321        for repeat in range(100):
322            args = []
323            argvalues = []
324            argslist = []
325            local_floats = list(floats)
326            local_singlefloats = list(singlefloats)
327            local_ints = list(ints)
328            for i in range(8):
329                case = random.randrange(0, 3)
330                if case == 0:
331                    args.append(F)
332                    arg = local_floats.pop()
333                    argslist.append(boxfloat(arg))
334                elif case == 1:
335                    args.append(S)
336                    arg = local_singlefloats.pop()
337                    argslist.append(BoxInt(longlong.singlefloat2int(arg)))
338                else:
339                    args.append(I)
340                    arg = local_ints.pop()
341                    argslist.append(BoxInt(arg))
342                argvalues.append(arg)
343            FUNC = self.FuncType(args, F)
344            FPTR = self.Ptr(FUNC)
345            func_ptr = llhelper(FPTR, func)
346            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
347                                        EffectInfo.MOST_GENERAL)
348            funcbox = self.get_funcbox(cpu, func_ptr)
349
350            res = self.execute_operation(rop.CALL,
351                                         [funcbox] + argslist,
352                                         'float', descr=calldescr)
353            expected = func(*argvalues)
354            assert abs(res.getfloat() - expected) < 0.0001