alpha: select ARCH_SUPPORTS_INT128

gcc supports __int128 on Alpha, and the 64x64 to 128-bit multiply that
the users in the scheduler and in lib/ care about maps onto mulq/umulh
rather than an out-of-line libgcc helper.

Variable 128-bit shifts are another matter. gcc open-codes them when
optimizing for speed, but with CONFIG_CC_OPTIMIZE_FOR_SIZE=y it calls the
libgcc routines __ashlti3, __ashrti3 and __lshrti3 instead. The kernel
does not link against libgcc, so provide the three helpers in C, as s390
does, and export them for modules. They are built into the kernel proper
rather than lib.a so the exports are present even when nothing built in
references them. Like the s390 versions they are marked noinstr, since
the compiler may emit calls to them from noinstr code.

Gate the select on CC_HAS_INT128 as the other 64-bit architectures do.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Matt Turner <mattst88@gmail.com>
Tested-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Magnus Lindholm <linmag7@gmail.com>
Link: https://lore.kernel.org/r/20261017174007.695344-1-mattst88@gmail.com
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
This commit is contained in:
Matt Turner 2026-10-17 13:40:07 -04:00 committed by Magnus Lindholm
parent d8df465bc4
commit 3382beeda2
4 changed files with 79 additions and 0 deletions

View File

@ -16,6 +16,7 @@ config ALPHA
select ARCH_NO_PREEMPT
select ARCH_NO_SG_CHAIN
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_INT128 if CC_HAS_INT128
select ARCH_USE_CMPXCHG_LOCKREF
select FORCE_PCI
select PCI_DOMAINS if PCI

View File

@ -17,3 +17,9 @@ extern void __remlu(void);
extern void __divqu(void);
extern void __remqu(void);
extern unsigned long __udiv_qrnnd(unsigned long *, unsigned long, unsigned long , unsigned long);
#ifdef CONFIG_ARCH_SUPPORTS_INT128
extern __int128_t __ashlti3(__int128_t a, int shift);
extern __int128_t __ashrti3(__int128_t a, int shift);
extern __int128_t __lshrti3(__int128_t a, int shift);
#endif

View File

@ -35,6 +35,8 @@ lib-y = __divqu.o __remqu.o __divlu.o __remlu.o \
callback_srm.o srm_puts.o srm_printk.o \
fls.o
obj-$(CONFIG_ARCH_SUPPORTS_INT128) += tishift.o
# The division routines are built from single source, with different defines.
AFLAGS___divqu.o = -DDIV
AFLAGS___remqu.o = -DREM

70
arch/alpha/lib/tishift.c Normal file
View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 128-bit shift helpers.
*
* gcc open-codes a variable 128-bit shift when optimizing for speed, but
* calls these libgcc routines when optimizing for size.
*/
#include <linux/export.h>
#include <linux/types.h>
#include <asm/asm-prototypes.h>
union ti {
__int128_t val;
struct {
u64 low;
u64 high;
};
};
noinstr __int128_t __ashlti3(__int128_t a, int shift)
{
union ti ti = { .val = a };
if (!shift)
return ti.val;
if (shift < 64) {
ti.high = (ti.high << shift) | (ti.low >> (64 - shift));
ti.low <<= shift;
} else {
ti.high = ti.low << (shift - 64);
ti.low = 0;
}
return ti.val;
}
EXPORT_SYMBOL(__ashlti3);
noinstr __int128_t __ashrti3(__int128_t a, int shift)
{
union ti ti = { .val = a };
if (!shift)
return ti.val;
if (shift < 64) {
ti.low = (ti.low >> shift) | (ti.high << (64 - shift));
ti.high = (s64)ti.high >> shift;
} else {
ti.low = (s64)ti.high >> (shift - 64);
ti.high = (s64)ti.high >> 63;
}
return ti.val;
}
EXPORT_SYMBOL(__ashrti3);
noinstr __int128_t __lshrti3(__int128_t a, int shift)
{
union ti ti = { .val = a };
if (!shift)
return ti.val;
if (shift < 64) {
ti.low = (ti.low >> shift) | (ti.high << (64 - shift));
ti.high >>= shift;
} else {
ti.low = ti.high >> (shift - 64);
ti.high = 0;
}
return ti.val;
}
EXPORT_SYMBOL(__lshrti3);