[loong64] Replace broken big integer arithmetic implementations

The slightly incomprehensible LoongArch64 implementation for
bigint_subtract() is observed to produce incorrect results for some
input values.

Replace the suspicious LoongArch64 implementations of bigint_add(),
bigint_subtract(), bigint_rol() and bigint_ror(), and add a test case
for a subtraction that was producing an incorrect result with the
previous implementation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/1128/head
Michael Brown 2024-01-19 16:35:02 +00:00
parent bac13ba1f6
commit 908174ec7e
2 changed files with 115 additions and 78 deletions

View File

@ -53,34 +53,37 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
uint64_t *discard_value; uint64_t *discard_value;
uint64_t discard_addend_i; uint64_t discard_addend_i;
uint64_t discard_value_i; uint64_t discard_value_i;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size; unsigned int discard_size;
__asm__ __volatile__ ( "move $t0, $zero\n"
"1:\n\t" __asm__ __volatile__ ( "\n1:\n\t"
"ld.d %3, %0, 0\n\t" /* Load addend[i] and value[i] */
"ld.d %3, %0, 0\n\t"
"ld.d %4, %1, 0\n\t"
/* Add carry flag and addend */
"add.d %4, %4, %5\n\t"
"sltu %6, %4, %5\n\t"
"add.d %4, %4, %3\n\t"
"sltu %5, %4, %3\n\t"
"or %5, %5, %6\n\t"
/* Store value[i] */
"st.d %4, %1, 0\n\t"
/* Loop */
"addi.d %0, %0, 8\n\t" "addi.d %0, %0, 8\n\t"
"ld.d %4, %1, 0\n\t"
"add.d %4, %4, $t0\n\t"
"sltu $t0, %4, $t0\n\t"
"add.d %4, %4, %3\n\t"
"sltu $t1, %4, %3\n\t"
"or $t0, $t0, $t1\n\t"
"st.d %4, %1, 0\n\t"
"addi.d %1, %1, 8\n\t" "addi.d %1, %1, 8\n\t"
"addi.w %2, %2, -1\n\t" "addi.w %2, %2, -1\n\t"
"bnez %2, 1b" "bnez %2, 1b\n\t"
: "=r" ( discard_addend ), : "=r" ( discard_addend ),
"=r" ( discard_value ), "=r" ( discard_value ),
"=r" ( discard_size ), "=r" ( discard_size ),
"=r" ( discard_addend_i ), "=r" ( discard_addend_i ),
"=r" ( discard_value_i ), "=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value ) "+m" ( *value )
: "0" ( addend0 ), : "0" ( addend0 ), "1" ( value0 ),
"1" ( value0 ), "2" ( size ), "5" ( 0 ) );
"2" ( size )
: "t0", "t1" );
} }
/** /**
@ -93,35 +96,43 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
static inline __attribute__ (( always_inline )) void static inline __attribute__ (( always_inline )) void
bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0, bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
unsigned int size ) { unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_subtrahend; uint64_t *discard_subtrahend;
uint64_t *discard_value; uint64_t *discard_value;
uint64_t discard_subtrahend_i; uint64_t discard_subtrahend_i;
uint64_t discard_value_i; uint64_t discard_value_i;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size; unsigned int discard_size;
unsigned int flag = 0;
discard_subtrahend = (uint64_t*) subtrahend0; __asm__ __volatile__ ( "\n1:\n\t"
discard_value = value0; /* Load subtrahend[i] and value[i] */
discard_size = size; "ld.d %3, %0, 0\n\t"
"ld.d %4, %1, 0\n\t"
do { /* Subtract carry flag and subtrahend */
discard_subtrahend_i = *discard_subtrahend; "sltu %6, %4, %5\n\t"
discard_subtrahend++; "sub.d %4, %4, %5\n\t"
discard_value_i = *discard_value; "sltu %5, %4, %3\n\t"
"sub.d %4, %4, %3\n\t"
discard_value_i = discard_value_i - discard_subtrahend_i - flag; "or %5, %5, %6\n\t"
/* Store value[i] */
if ( *discard_value < (discard_subtrahend_i + flag)) { "st.d %4, %1, 0\n\t"
flag = 1; /* Loop */
} else { "addi.d %0, %0, 8\n\t"
flag = 0; "addi.d %1, %1, 8\n\t"
} "addi.w %2, %2, -1\n\t"
"bnez %2, 1b\n\t"
*discard_value = discard_value_i; : "=r" ( discard_subtrahend ),
"=r" ( discard_value ),
discard_value++; "=r" ( discard_size ),
discard_size -= 1; "=r" ( discard_subtrahend_i ),
} while (discard_size != 0); "=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
"2" ( size ), "5" ( 0 ) );
} }
/** /**
@ -132,30 +143,37 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
*/ */
static inline __attribute__ (( always_inline )) void static inline __attribute__ (( always_inline )) void
bigint_rol_raw ( uint64_t *value0, unsigned int size ) { bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value; uint64_t *discard_value;
uint64_t discard_value_i; uint64_t discard_value_i;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size; unsigned int discard_size;
uint64_t current_value_i;
unsigned int flag = 0;
discard_value = value0; __asm__ __volatile__ ( "\n1:\n\t"
discard_size = size; /* Load value[i] */
do { "ld.d %2, %0, 0\n\t"
discard_value_i = *discard_value; /* Shift left */
current_value_i = discard_value_i; "rotri.d %2, %2, 63\n\t"
"andi %4, %2, 1\n\t"
discard_value_i += discard_value_i + flag; "xor %2, %2, %4\n\t"
"or %2, %2, %3\n\t"
if (discard_value_i < current_value_i) { "move %3, %4\n\t"
flag = 1; /* Store value[i] */
} else { "st.d %2, %0, 0\n\t"
flag = 0; /* Loop */
} "addi.d %0, %0, 8\n\t"
"addi.w %1, %1, -1\n\t"
*discard_value = discard_value_i; "bnez %1, 1b\n\t"
discard_value++; : "=r" ( discard_value ),
discard_size -= 1; "=r" ( discard_size ),
} while ( discard_size != 0 ); "=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size ), "3" ( 0 )
: "cc" );
} }
/** /**
@ -166,27 +184,37 @@ bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
*/ */
static inline __attribute__ (( always_inline )) void static inline __attribute__ (( always_inline )) void
bigint_ror_raw ( uint64_t *value0, unsigned int size ) { bigint_ror_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value; uint64_t *discard_value;
uint64_t discard_value_i; uint64_t discard_value_i;
uint64_t discard_value_j; uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size; unsigned int discard_size;
discard_value = value0; __asm__ __volatile__ ( "\n1:\n\t"
discard_size = size; /* Load value[i] */
"ld.d %2, %0, -8\n\t"
discard_value_j = 0; /* Shift right */
"andi %4, %2, 1\n\t"
do { "xor %2, %2, %4\n\t"
discard_size -= 1; "or %2, %2, %3\n\t"
"move %3, %4\n\t"
discard_value_i = *(discard_value + discard_size); "rotri.d %2, %2, 1\n\t"
/* Store value[i] */
discard_value_j = (discard_value_j << 63) | (discard_value_i >> 1); "st.d %2, %0, -8\n\t"
/* Loop */
*(discard_value + discard_size) = discard_value_j; "addi.d %0, %0, -8\n\t"
"addi.w %1, %1, -1\n\t"
discard_value_j = discard_value_i; "bnez %1, 1b\n\t"
} while ( discard_size > 0 ); : "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( value0 + size ), "1" ( size ), "3" ( 0 )
: "cc" );
} }
/** /**

View File

@ -714,6 +714,15 @@ static void bigint_test_exec ( void ) {
bigint_subtract_ok ( BIGINT ( 0xbb, 0x77, 0x32, 0x5a ), bigint_subtract_ok ( BIGINT ( 0xbb, 0x77, 0x32, 0x5a ),
BIGINT ( 0x5a, 0xd5, 0xfe, 0x28 ), BIGINT ( 0x5a, 0xd5, 0xfe, 0x28 ),
BIGINT ( 0x9f, 0x5e, 0xcb, 0xce ) ); BIGINT ( 0x9f, 0x5e, 0xcb, 0xce ) );
bigint_subtract_ok ( BIGINT ( 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff ),
BIGINT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x2a ),
BIGINT ( 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x2b ) );
bigint_subtract_ok ( BIGINT ( 0x7b, 0xaa, 0x16, 0xcf, 0x15, 0x87, bigint_subtract_ok ( BIGINT ( 0x7b, 0xaa, 0x16, 0xcf, 0x15, 0x87,
0xe0, 0x4f, 0x2c, 0xa3, 0xec, 0x2f, 0xe0, 0x4f, 0x2c, 0xa3, 0xec, 0x2f,
0x46, 0xfb, 0x83, 0xc6, 0xe0, 0xee, 0x46, 0xfb, 0x83, 0xc6, 0xe0, 0xee,