[pypy-commit] pypy default: hg merge arm-longlong
arigo
noreply at buildbot.pypy.org
Mon Sep 1 17:06:57 CEST 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r73271:8ae0ea93f7fe
Date: 2014-09-01 17:05 +0200
http://bitbucket.org/pypy/pypy/changeset/8ae0ea93f7fe/
Log: hg merge arm-longlong
Reintroduce longlong support in the ARM backend. Tested with the
hard-float variant of the calling convention; can't test the soft-
float variant for now. I'll test myself at a later date or just
handle bug reports --- if any: I think that for soft-float, we
simply need to consider longlongs as equivalent to 64-bit floats (as
we do now).
diff --git a/rpython/jit/backend/arm/callbuilder.py b/rpython/jit/backend/arm/callbuilder.py
--- a/rpython/jit/backend/arm/callbuilder.py
+++ b/rpython/jit/backend/arm/callbuilder.py
@@ -80,15 +80,6 @@
self.mc.gen_load_int(r.ip.value, n)
self.mc.SUB_rr(r.sp.value, r.sp.value, r.ip.value)
- def _must_remap_fnloc(self):
- fnloc = self.fnloc
- if fnloc.is_stack():
- return True
- if self.is_call_release_gil:
- if fnloc is r.r5 or fnloc is r.r6 or fnloc is r.r7:
- return True
- return False
-
def call_releasegil_addr_and_move_real_arguments(self, fastgil):
assert self.is_call_release_gil
assert not self.asm._is_asmgcc()
@@ -121,7 +112,7 @@
self.mc.STREX(r.r3.value, r.ip.value, r.r6.value, c=c.EQ)
# try to claim the lock
self.mc.CMP_ri(r.r3.value, 0, cond=c.EQ) # did this succeed?
- self.mc.DMB(c=c.EQ)
+ self.mc.DMB()
# the success of the lock acquisition is defined by
# 'EQ is true', or equivalently by 'r3 == 0'.
#
@@ -268,7 +259,7 @@
# or on the stack, which we can not access later
# If this happens to be the case we remap the register to r4 and use r4
# to call the function
- if self.fnloc in r.argument_regs or self._must_remap_fnloc():
+ if not self.fnloc.is_imm():
non_float_locs.append(self.fnloc)
non_float_regs.append(r.r4)
self.fnloc = r.r4
@@ -285,29 +276,23 @@
def get_next_vfp(self, tp):
assert tp in 'fS'
- if self.next_arg_vfp == -1:
- return None
- if tp == 'S':
+ if tp == 'f':
+ # 64bit double
+ i = max(self.next_arg_vfp, (self.next_arg_svfp + 1) >> 1)
+ if i >= len(r.vfp_argument_regs):
+ self.next_arg_svfp = 1000 # stop that sequence too
+ return None
+ self.next_arg_vfp = i + 1
+ return r.vfp_argument_regs[i]
+ else:
+ # 32bit float
i = self.next_arg_svfp
- next_vfp = (i >> 1) + 1
- if not (i + 1) & 1: # i is even
- self.next_arg_vfp = max(self.next_arg_vfp, next_vfp)
- self.next_arg_svfp = self.next_arg_vfp << 1
- else:
- self.next_arg_svfp += 1
- self.next_arg_vfp = next_vfp
- lst = r.svfp_argument_regs
- else: # 64bit double
- i = self.next_arg_vfp
- self.next_arg_vfp += 1
- if self.next_arg_svfp >> 1 == i:
- self.next_arg_svfp = self.next_arg_vfp << 1
- lst = r.vfp_argument_regs
- try:
- return lst[i]
- except IndexError:
- self.next_arg_vfp = self.next_arg_svfp = -1
- return None
+ if not (i & 1): # if i is even
+ i = max(i, self.next_arg_vfp << 1)
+ if i >= len(r.svfp_argument_regs):
+ return None
+ self.next_arg_svfp = i + 1
+ return r.svfp_argument_regs[i]
def prepare_arguments(self):
non_float_locs = []
@@ -316,34 +301,64 @@
float_regs = []
stack_args = []
singlefloats = None
+ longlong_mask = 0
arglocs = self.arglocs
argtypes = self.argtypes
- count = 0 # stack alignment counter
+ r_register_count = 0
on_stack = 0
+
for i in range(len(arglocs)):
argtype = INT
if i < len(argtypes) and argtypes[i] == 'S':
argtype = argtypes[i]
arg = arglocs[i]
+
if arg.is_float():
- argtype = FLOAT
- reg = self.get_next_vfp(argtype)
- if reg:
- assert len(float_regs) < len(r.vfp_argument_regs)
- float_locs.append(arg)
- assert reg not in float_regs
- float_regs.append(reg)
- else: # float argument that needs to go on the stack
- if count % 2 != 0:
- stack_args.append(None)
- count = 0
- on_stack += 1
- stack_args.append(arg)
- on_stack += 2
+ if i < len(argtypes) and argtypes[i] == 'L':
+ # A longlong argument. It uses two regular argument
+ # positions, but aligned to an even number. This is
+ # a bit strange, but it is the case even for registers:
+ # it can be in r0-r1 or in r2-r3 but not in r1-r2.
+ assert arg.is_float()
+ if r_register_count == 0:
+ # will temporarily load the register into d8
+ float_locs.append(arg)
+ float_regs.append(r.d8)
+ longlong_mask |= 1
+ r_register_count = 2
+ continue
+ elif r_register_count <= 2:
+ # will temporarily load the register into d9
+ float_locs.append(arg)
+ float_regs.append(r.d9)
+ longlong_mask |= 2
+ r_register_count = 4
+ continue
+ elif r_register_count == 3:
+ r_register_count = 4
+ else:
+ # A 64-bit float argument. Goes into the next free v#
+ # register, or if none, to the stack aligned to an
+ # even number of words.
+ argtype = FLOAT
+ reg = self.get_next_vfp(argtype)
+ if reg:
+ float_locs.append(arg)
+ assert reg not in float_regs
+ float_regs.append(reg)
+ continue
+ # float or longlong argument that needs to go on the stack
+ if on_stack & 1: # odd: realign
+ stack_args.append(None)
+ on_stack += 1
+ stack_args.append(arg)
+ on_stack += 2
+
elif argtype == 'S':
- # Singlefloat argument
+ # Singlefloat (32-bit) argument. Goes into the next free
+ # v# register, or if none, to the stack in a single word.
if singlefloats is None:
singlefloats = []
tgt = self.get_next_vfp(argtype)
@@ -351,32 +366,36 @@
singlefloats.append((arg, tgt))
else: # Singlefloat argument that needs to go on the stack
# treated the same as a regular core register argument
- count += 1
+ stack_args.append(arg)
on_stack += 1
- stack_args.append(arg)
else:
- if len(non_float_regs) < len(r.argument_regs):
- reg = r.argument_regs[len(non_float_regs)]
+ # Regular one-word argument. Goes into the next register
+ # free from the list r0, r1, r2, r3, or to the stack.
+ if r_register_count < len(r.argument_regs):
+ reg = r.argument_regs[r_register_count]
+ r_register_count += 1
non_float_locs.append(arg)
non_float_regs.append(reg)
else: # non-float argument that needs to go on the stack
- count += 1
+ stack_args.append(arg)
on_stack += 1
- stack_args.append(arg)
+
# align the stack
- if count % 2 != 0:
+ if on_stack & 1: # odd: realign
stack_args.append(None)
on_stack += 1
self._push_stack_args(stack_args, on_stack*WORD)
+
# Check that the address of the function we want to call is not
# currently stored in one of the registers used to pass the arguments
# or on the stack, which we can not access later
# If this happens to be the case we remap the register to r4 and use r4
# to call the function
- if self.fnloc in non_float_regs or self._must_remap_fnloc():
+ if not self.fnloc.is_imm():
non_float_locs.append(self.fnloc)
non_float_regs.append(r.r4)
self.fnloc = r.r4
+
# remap values stored in vfp registers
remap_frame_layout(self.asm, float_locs, float_regs, r.vfp_ip)
if singlefloats:
@@ -392,13 +411,22 @@
src = r.ip
if src.is_core_reg():
self.mc.VMOV_cs(dest.value, src.value)
+
# remap values stored in core registers
remap_frame_layout(self.asm, non_float_locs, non_float_regs, r.ip)
+ if longlong_mask & 1:
+ self.mc.FMRRD(r.r0.value, r.r1.value, r.d8.value)
+ if longlong_mask & 2:
+ self.mc.FMRRD(r.r2.value, r.r3.value, r.d9.value)
+
def load_result(self):
resloc = self.resloc
if self.restype == 'S':
self.mc.VMOV_sc(resloc.value, r.s0.value)
+ elif self.restype == 'L':
+ assert resloc.is_vfp_reg()
+ self.mc.FMDRR(resloc.value, r.r0.value, r.r1.value)
# ensure the result is wellformed and stored in the correct location
if resloc is not None and resloc.is_core_reg():
self._ensure_result_bit_extension(resloc,
@@ -408,7 +436,10 @@
if self.resloc is None:
return [], []
if self.resloc.is_vfp_reg():
- return [], [r.d0]
+ if self.restype == 'L': # long long
+ return [r.r0, r.r1], []
+ else:
+ return [], [r.d0]
assert self.resloc.is_core_reg()
return [r.r0], []
diff --git a/rpython/jit/backend/arm/codebuilder.py b/rpython/jit/backend/arm/codebuilder.py
--- a/rpython/jit/backend/arm/codebuilder.py
+++ b/rpython/jit/backend/arm/codebuilder.py
@@ -332,13 +332,17 @@
| (rd & 0xF) << 12
| (rn & 0xF) << 16)
- def DMB(self, c=cond.AL):
- self.write32(c << 28 | 0x157ff05f)
+ def DMB(self):
+ # note: 'cond' is only permitted on Thumb here
+ self.write32(0xf57ff05f)
DIV = binary_helper_call('int_div')
MOD = binary_helper_call('int_mod')
UDIV = binary_helper_call('uint_div')
+ FMDRR = VMOV_cr # uh, there are synonyms?
+ FMRRD = VMOV_rc
+
def _encode_reg_list(self, instr, regs):
for reg in regs:
instr |= 0x1 << reg
diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py
--- a/rpython/jit/backend/arm/runner.py
+++ b/rpython/jit/backend/arm/runner.py
@@ -20,7 +20,7 @@
IS_64_BIT = False
supports_floats = True
- supports_longlong = False # incomplete, notably in callbuilder.py
+ supports_longlong = True
supports_singlefloats = True
from rpython.jit.backend.arm.arch import JITFRAME_FIXED_SIZE
diff --git a/rpython/jit/backend/arm/test/test_callbuilder.py b/rpython/jit/backend/arm/test/test_callbuilder.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/arm/test/test_callbuilder.py
@@ -0,0 +1,47 @@
+from rpython.jit.backend.arm.callbuilder import HardFloatCallBuilder
+from rpython.jit.backend.arm import registers as r
+
+
+
+def test_hf_vfp_registers_all_singlefloat():
+ hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+ got = [hf.get_next_vfp('S') for i in range(18)]
+ assert got == [r.s0, r.s1, r.s2, r.s3, r.s4, r.s5, r.s6, r.s7,
+ r.s8, r.s9, r.s10, r.s11, r.s12, r.s13, r.s14, r.s15,
+ None, None]
+
+def test_hf_vfp_registers_all_doublefloat():
+ hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+ got = [hf.get_next_vfp('f') for i in range(10)]
+ assert got == [r.d0, r.d1, r.d2, r.d3, r.d4, r.d5, r.d6, r.d7,
+ None, None]
+
+def test_hf_vfp_registers_mixture():
+ hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+ got = [hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('S'), hf.get_next_vfp('f')]
+ assert got == [r.s0, r.d1,
+ r.s1, r.d2,
+ r.s6, r.d4,
+ r.s7, r.d5,
+ r.s12, r.d7,
+ r.s13, None,
+ None, None]
+
+def test_hf_vfp_registers_mixture_2():
+ hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+ got = [hf.get_next_vfp('f'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('f'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('f'), hf.get_next_vfp('f'),
+ hf.get_next_vfp('f'), hf.get_next_vfp('S'),
+ hf.get_next_vfp('f'), hf.get_next_vfp('S')]
+ assert got == [r.d0, r.d1,
+ r.d2, r.d3,
+ r.d4, r.d5,
+ r.d6, r.s14,
+ None, None] # <- and not r.s15 for the last item
diff --git a/rpython/jit/backend/arm/test/test_instr_codebuilder.py b/rpython/jit/backend/arm/test/test_instr_codebuilder.py
--- a/rpython/jit/backend/arm/test/test_instr_codebuilder.py
+++ b/rpython/jit/backend/arm/test/test_instr_codebuilder.py
@@ -199,6 +199,14 @@
self.cb.DMB()
self.assert_equal('DMB')
+ def test_fmdrr(self):
+ self.cb.FMDRR(r.d11.value, r.r9.value, r.r14.value)
+ self.assert_equal('FMDRR d11, r9, r14')
+
+ def test_fmrrd(self):
+ self.cb.FMRRD(r.r9.value, r.r14.value, r.d11.value)
+ self.assert_equal('FMRRD r9, r14, d11')
+
def test_size_of_gen_load_int():
for v, n in [(5, 4), (6, 4), (7, 2)]:
diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -2718,12 +2718,11 @@
assert r == result
def test_call_release_gil_variable_function_and_arguments(self):
- # NOTE NOTE NOTE
- # This also works as a test for ctypes and libffi.
- # On some platforms, one of these is buggy...
+ from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.rlib.libffi import types
from rpython.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
from rpython.rlib.rarithmetic import r_singlefloat
+ from rpython.translator.c import primitive
cpu = self.cpu
rnd = random.Random(525)
@@ -2752,25 +2751,76 @@
(types.float, rffi.FLOAT),
] * 4
- for k in range(100):
+ NB_TESTS = 100
+ c_source = []
+ all_tests = []
+ export_symbols = []
+
+ def prepare_c_source():
+ """Pick a random choice of argument types and length,
+ and build a C function with these arguments. The C
+ function will simply copy them all into static global
+ variables. There are then additional functions to fetch
+ them, one per argument, with a signature 'void(ARG *)'.
+ """
POSSIBLE_TYPES = [rnd.choice(ALL_TYPES)
for i in range(random.randrange(2, 5))]
load_factor = rnd.random()
keepalive_factor = rnd.random()
#
- def pseudo_c_function(*args):
- seen.append(list(args))
- #
ffitypes = []
ARGTYPES = []
for i in range(rnd.randrange(4, 20)):
ffitype, TP = rnd.choice(POSSIBLE_TYPES)
ffitypes.append(ffitype)
ARGTYPES.append(TP)
+ fn_name = 'vartest%d' % k
+ all_tests.append((ARGTYPES, ffitypes, fn_name))
#
- FPTR = self.Ptr(self.FuncType(ARGTYPES, lltype.Void))
- func_ptr = llhelper(FPTR, pseudo_c_function)
- funcbox = self.get_funcbox(cpu, func_ptr)
+ fn_args = []
+ for i, ARG in enumerate(ARGTYPES):
+ arg_decl = primitive.cdecl(primitive.PrimitiveType[ARG],
+ 'x%d' % i)
+ fn_args.append(arg_decl)
+ var_name = 'argcopy_%s_x%d' % (fn_name, i)
+ var_decl = primitive.cdecl(primitive.PrimitiveType[ARG],
+ var_name)
+ c_source.append('static %s;' % var_decl)
+ getter_name = '%s_get%d' % (fn_name, i)
+ export_symbols.append(getter_name)
+ c_source.append('void %s(%s) { *p = %s; }' % (
+ getter_name,
+ primitive.cdecl(primitive.PrimitiveType[ARG], '*p'),
+ var_name))
+ export_symbols.append(fn_name)
+ c_source.append('')
+ c_source.append('static void real%s(%s)' % (
+ fn_name, ', '.join(fn_args)))
+ c_source.append('{')
+ for i in range(len(ARGTYPES)):
+ c_source.append(' argcopy_%s_x%d = x%d;' % (fn_name, i, i))
+ c_source.append('}')
+ c_source.append('void *%s(void)' % fn_name)
+ c_source.append('{')
+ c_source.append(' return (void *)&real%s;' % fn_name)
+ c_source.append('}')
+ c_source.append('')
+
+ for k in range(NB_TESTS):
+ prepare_c_source()
+
+ eci = ExternalCompilationInfo(
+ separate_module_sources=['\n'.join(c_source)],
+ export_symbols=export_symbols)
+
+ for k in range(NB_TESTS):
+ ARGTYPES, ffitypes, fn_name = all_tests[k]
+ func_getter_ptr = rffi.llexternal(fn_name, [], lltype.Signed,
+ compilation_info=eci, _nowrapper=True)
+ load_factor = rnd.random()
+ keepalive_factor = rnd.random()
+ #
+ func_raw = func_getter_ptr()
calldescr = cpu._calldescr_dynamic_for_tests(ffitypes, types.void)
faildescr = BasicFailDescr(1)
#
@@ -2790,7 +2840,7 @@
print
print codes
#
- argvalues = [funcbox.getint()]
+ argvalues = [func_raw]
for TP in ARGTYPES:
r = (rnd.random() - 0.5) * 999999999999.9
r = rffi.cast(TP, r)
@@ -2840,16 +2890,26 @@
looptoken = JitCellToken()
self.cpu.compile_loop(argboxes, ops, looptoken)
#
- seen = []
deadframe = self.cpu.execute_token(looptoken, *argvalues_normal)
fail = self.cpu.get_latest_descr(deadframe)
assert fail.identifier == 0
expected = argvalues[1:]
- [got] = seen
- different_values = ['%r != %r' % (a, b)
- for a, b in zip(got, expected)
- if a != b]
- assert got == expected, ', '.join(different_values)
+ got = []
+ for i, ARG in enumerate(ARGTYPES):
+ PARG = rffi.CArrayPtr(ARG)
+ getter_name = '%s_get%d' % (fn_name, i)
+ getter_ptr = rffi.llexternal(getter_name, [PARG], lltype.Void,
+ compilation_info=eci,
+ _nowrapper=True)
+ my_arg = lltype.malloc(PARG.TO, 1, zero=True, flavor='raw')
+ getter_ptr(my_arg)
+ got.append(my_arg[0])
+ lltype.free(my_arg, flavor='raw')
+ different_values = ['x%d: got %r, expected %r' % (i, a, b)
+ for i, (a, b) in enumerate(zip(got, expected))
+ if a != b]
+ assert got == expected, '\n'.join(
+ ['bad args, signature %r' % codes[1:]] + different_values)
def test_guard_not_invalidated(self):
More information about the pypy-commit
mailing list