mips: Fix N32 ABI return value handling (#813)
authorXi Ruoyao <xry111@xry111.site>
Thu, 15 Feb 2024 12:52:13 +0000 (20:52 +0800)
committerGitHub <noreply@github.com>
Thu, 15 Feb 2024 12:52:13 +0000 (07:52 -0500)
In N32 ABI, 8-bit or 16-bit integers should be extended following the
signedness of the integer, but 32-bit integers should always be
sign-extended to 64-bit (note that N32 ABI only works on 64-bit CPUs).

So handling this in everything using libffi would be nasty.  And the
libffi code for architectures with a similar rule (LoongArch & RISC-V)
also properly handle this.  Let's do this work in libffi for MIPS N32
too.

This fixes two failures in Python 3.12.1 ctypes test.

src/mips/ffi.c
src/mips/n32.S

index e7043258f3fe61f021e245ea0ff552d171d00b9b..5c8c6bc5d4473d5b299c6da258e7dd0ee3b85b45 100644 (file)
@@ -647,9 +647,9 @@ static ffi_status ffi_prep_cif_machdep_int(ffi_cif *cif, unsigned nfixedargs)
 
       case FFI_TYPE_POINTER:
        if (cif->abi == FFI_N32_SOFT_FLOAT || cif->abi == FFI_N32)
-         cif->flags += FFI_TYPE_UINT32 << (FFI_FLAG_BITS * 8);
+         cif->flags += FFI_TYPE_SINT32 << (FFI_FLAG_BITS * 8);
        else
-         cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+         cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 8);
        break;
 
       case FFI_TYPE_FLOAT:
@@ -661,7 +661,7 @@ static ffi_status ffi_prep_cif_machdep_int(ffi_cif *cif, unsigned nfixedargs)
        /* else fall through */
       case FFI_TYPE_DOUBLE:
        if (soft_float)
-         cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+         cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 8);
        else
          cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 8);
        break;
@@ -715,8 +715,16 @@ static ffi_status ffi_prep_cif_machdep_int(ffi_cif *cif, unsigned nfixedargs)
            }
          break;
        }
+      case FFI_TYPE_UINT32:
+       /* In the N32 or N64 ABI unsigned 32-bit integer should be
+          *sign*-extended.  */
+       cif->flags += FFI_TYPE_SINT32 << (FFI_FLAG_BITS * 8);
+       break;
+      case FFI_TYPE_SINT64:
+       cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 8);
+       break;
       default:
-       cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+       cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 8);
        break;
       }
   }
index e1938d1149d314a1722f9067c8edcc30ffa59aa0..df58e800ade4a9ac228ecb5c9a0fbd12c393b4e2 100644 (file)
@@ -236,19 +236,54 @@ callit:
        # Shift the return type flag over
        SRL     t6, 8*FFI_FLAG_BITS
 
-       beq     t6, FFI_TYPE_SINT32, retint
-       bne     t6, FFI_TYPE_INT, retuint32
-retint:
+       bne     t6, FFI_TYPE_UINT64, retsint32
+
+retuint64:
        jal     t9
        REG_L   t4, 4*FFI_SIZEOF_ARG($fp)
-       REG_S   v0, 0(t4)
+       sd      v0, 0(t4)
        b       epilogue
 
-retuint32:
-       bne     t6, FFI_TYPE_UINT32, retfloat
+retsint32:
+       bne     t6, FFI_TYPE_SINT32, retuint16
        jal     t9
        REG_L   t4, 4*FFI_SIZEOF_ARG($fp)
-       sw      v0, 0(t4)
+       sll     v0, v0, 0
+       sd      v0, 0(t4)
+       b       epilogue
+
+retuint16:
+       bne     t6, FFI_TYPE_UINT16, retsint16
+       jal     t9
+       REG_L   t4, 4*FFI_SIZEOF_ARG($fp)
+       andi    v0, v0, 0xffff
+       sd      v0, 0(t4)
+       b       epilogue
+
+retsint16:
+       bne     t6, FFI_TYPE_SINT16, retuint8
+       jal     t9
+       REG_L   t4, 4*FFI_SIZEOF_ARG($fp)
+       dsll    v0, v0, 48
+       dsra    v0, v0, 48
+       sd      v0, 0(t4)
+       b       epilogue
+
+retuint8:
+       bne     t6, FFI_TYPE_UINT8, retsint8
+       jal     t9
+       REG_L   t4, 4*FFI_SIZEOF_ARG($fp)
+       andi    v0, v0, 0xff
+       sd      v0, 0(t4)
+       b       epilogue
+
+retsint8:
+       bne     t6, FFI_TYPE_SINT8, retfloat
+       jal     t9
+       REG_L   t4, 4*FFI_SIZEOF_ARG($fp)
+       sd      v0, 0(t4)
+       dsll    v0, v0, 56
+       dsra    v0, v0, 56
        b       epilogue
 
 retfloat:
@@ -585,19 +620,35 @@ $do_closure:
 
        jalr    t9
 
+cls_retuint64:
        # Return flags are in v0
-       bne     v0, FFI_TYPE_SINT32, cls_retuint32
+       bne     v0, FFI_TYPE_UINT64, cls_retsint32
+       ld      v0, V0_OFF2($sp)
+       b       cls_epilogue
+
+cls_retsint32:
+       bne     v0, FFI_TYPE_SINT32, cls_retsint16
        lw      v0, V0_OFF2($sp)
        b       cls_epilogue
 
-cls_retuint32:
-       bne     v0, FFI_TYPE_UINT32, cls_retint
-       lwu     v0, V0_OFF2($sp)
+cls_retsint16:
+       bne     v0, FFI_TYPE_SINT16, cls_retuint16
+       l     v0, V0_OFF2($sp)
        b       cls_epilogue
 
-cls_retint:
-       bne     v0, FFI_TYPE_INT, cls_retfloat
-       REG_L   v0, V0_OFF2($sp)
+cls_retuint16:
+       bne     v0, FFI_TYPE_UINT16, cls_retsint8
+       lhu     v0, V0_OFF2($sp)
+       b       cls_epilogue
+
+cls_retsint8:
+       bne     v0, FFI_TYPE_SINT8, cls_retuint8
+       lb      v0, V0_OFF2($sp)
+       b       cls_epilogue
+
+cls_retuint8:
+       bne     v0, FFI_TYPE_UINT8, cls_retfloat
+       lbu     v0, V0_OFF2($sp)
        b       cls_epilogue
 
 cls_retfloat: