sparc: Re-add abi compliant structure support The original code, removed in the "rewrite" patch, was incorrect for large structures, and required dynamic allocation of a trampoline on every ffi_call. Instead, allocate a 4k entry table of all possible structure returns. The table is 80k, but is read-only and dynamically paged, which ought to be better than allocating the trampoline. This is difficult to test with gcc. One can only use -O0 at present. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63668.
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
diff --git a/src/sparc/ffi.c b/src/sparc/ffi.c
index 19c3586..d5212d8 100644
--- a/src/sparc/ffi.c
+++ b/src/sparc/ffi.c
@@ -66,7 +66,8 @@ ffi_prep_cif_machdep(ffi_cif *cif)
break;
case FFI_TYPE_LONGDOUBLE:
case FFI_TYPE_STRUCT:
- flags = SPARC_RET_STRUCT;
+ flags = (rtype->size & 0xfff) << SPARC_SIZEMASK_SHIFT;
+ flags |= SPARC_RET_STRUCT;
break;
case FFI_TYPE_SINT8:
flags = SPARC_RET_SINT8;
@@ -187,7 +188,7 @@ ffi_prep_args_v8(ffi_cif *cif, unsigned long *argp, void *rvalue, void **avalue)
if (rvalue == NULL)
{
- if (flags == SPARC_RET_STRUCT)
+ if ((flags & SPARC_FLAG_RET_MASK) == SPARC_RET_STRUCT)
{
/* Since we pass the pointer to the callee, we need a value.
We allowed for this space in ffi_call, before ffi_call_v8
@@ -290,7 +291,8 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
/* If we've not got a return value, we need to create one if we've
got to pass the return value to the callee. Otherwise ignore it. */
- if (rvalue == NULL && cif->flags == SPARC_RET_STRUCT)
+ if (rvalue == NULL
+ && (cif->flags & SPARC_FLAG_RET_MASK) == SPARC_RET_STRUCT)
bytes += ALIGN (cif->rtype->size, 8);
ffi_call_v8(cif, fn, rvalue, avalue, -bytes, closure);
@@ -382,9 +384,14 @@ ffi_closure_sparc_inner_v8(ffi_cif *cif,
avalue = alloca(nargs * sizeof(void *));
/* Copy the caller's structure return address so that the closure
- returns the data directly to the caller. */
- if (flags == SPARC_RET_STRUCT)
- rvalue = (void *)*argp;
+ returns the data directly to the caller. Also install it so we
+ can return the address in %o0. */
+ if ((flags & SPARC_FLAG_RET_MASK) == SPARC_RET_STRUCT)
+ {
+ void *new_rvalue = (void *)*argp;
+ *(void **)rvalue = new_rvalue;
+ rvalue = new_rvalue;
+ }
/* Always skip the structure return address. */
argp++;
diff --git a/src/sparc/ffi64.c b/src/sparc/ffi64.c
index 02f3d75..a4e41d2 100644
--- a/src/sparc/ffi64.c
+++ b/src/sparc/ffi64.c
@@ -197,7 +197,7 @@ ffi_prep_cif_machdep(ffi_cif *cif)
int all_mask = (1 << word_size) - 1;
int fp_mask = size_mask >> 8;
- flags = (size_mask << SPARC_FLTMASK_SHIFT) | SPARC_RET_STRUCT;
+ flags = (size_mask << SPARC_SIZEMASK_SHIFT) | SPARC_RET_STRUCT;
/* For special cases of all-int or all-fp, we can return
the value directly without popping through a struct copy. */
diff --git a/src/sparc/internal.h b/src/sparc/internal.h
index f9387d4..0a66472 100644
--- a/src/sparc/internal.h
+++ b/src/sparc/internal.h
@@ -23,4 +23,4 @@
#define SPARC_FLAG_RET_IN_MEM 32
#define SPARC_FLAG_FP_ARGS 64
-#define SPARC_FLTMASK_SHIFT 8
+#define SPARC_SIZEMASK_SHIFT 8
diff --git a/src/sparc/v8.S b/src/sparc/v8.S
index 66cf76f..3a811ef 100644
--- a/src/sparc/v8.S
+++ b/src/sparc/v8.S
@@ -91,6 +91,7 @@ C(ffi_call_v8):
add %sp, 32, %sp ! deallocate prep frame
and %o0, SPARC_FLAG_RET_MASK, %l0 ! save return type
+ srl %o0, SPARC_SIZEMASK_SHIFT, %l1 ! save return size
ld [%sp+64+4], %o0 ! load all argument registers
ld [%sp+64+8], %o1
ld [%sp+64+12], %o2
@@ -182,22 +183,35 @@ E SPARC_RET_F_1
ret
restore
- ! Struct returning functions expect and skip the unimp here.
.align 8
-8: call %i1
- mov %i5, %g2 ! load static chain
- unimp 4
+9: sth %o0, [%i2]
ret
restore
-
.align 8
-9: sth %o0, [%i2]
+10: stb %o0, [%i2]
ret
restore
+
+ ! Struct returning functions expect and skip the unimp here.
+ ! To make it worse, conforming callees examine the unimp and
+ ! make sure the low 12 bits of the unimp match the size of
+ ! the struct being returned.
.align 8
-10: stb %o0, [%i2]
+8: call 1f ! load pc in %o7
+ sll %l1, 2, %l0 ! size * 4
+1: sll %l1, 4, %l1 ! size * 16
+ add %l0, %l1, %l0 ! size * 20
+ add %o7, %l0, %o7 ! o7 = 0b + size*20
+ jmp %o7+(2f-8b)
+ mov %i5, %g2 ! load static chain
+2:
+.rept 0x1000
+ call %i1
+ nop
+ unimp (. - 2b) / 20
ret
restore
+.endr
cfi_endproc
.size C(ffi_call_v8),. - C(ffi_call_v8)
@@ -275,6 +289,7 @@ E SPARC_RET_VOID
ret
restore
E SPARC_RET_STRUCT
+ ld [%i2], %i0
jmp %i7+12
restore
E SPARC_RET_UINT8
diff --git a/src/sparc/v9.S b/src/sparc/v9.S
index d848f9a..52732d3 100644
--- a/src/sparc/v9.S
+++ b/src/sparc/v9.S
@@ -188,7 +188,7 @@ E SPARC_RET_F_1
std %f6, [%l2+56]
! Copy the structure into place.
- srl %l0, SPARC_FLTMASK_SHIFT, %o0 ! load size_mask
+ srl %l0, SPARC_SIZEMASK_SHIFT, %o0 ! load size_mask
mov %i2, %o1 ! load dst
mov %l2, %o2 ! load src_gp
call C(ffi_struct_float_copy)