[v1] aarch64: Add fp8 scalar types
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
The ACLE defines a new scalar type, __mfp8. This is an opaque 8bit types that
can only be used by fp8 intrinsics. Additionally, the mfloat8_t type is made
available in arm_neon.h and arm_sve.h as an alias of the same.
This implementation uses an INTEGER_TYPE, with precision 8 to represent
__mfp8. Conversions to int and other types are disabled via the
TARGET_INVALID_CONVERSION hook.
Additionally, operations that are typically available to integer types are
disabled via TARGET_INVALID_UNARY_OP and TARGET_INVALID_BINARY_OP hooks.
gcc/ChangeLog:
* config/aarch64/aarch64-builtins.cc (aarch64_mfp8_type_node): Add node
for __mfp8 type.
(aarch64_mfp8_ptr_type_node): Add node for __mfp8 pointer type.
(aarch64_init_fp8_types): New function to initialise fp8 types and
register with language backends.
* config/aarch64/aarch64.cc (aarch64_invalid_conversion): Add function
implementing TARGET_INVALID_CONVERSION hook that blocks conversion to
and from __mfp8 type.
(aarch64_invalid_unary_op): Add function implementing TARGET_UNARY_OP
hook that blocks operations on __mfp8 other than &.
(aarch64_invalid_binary_op): Extend TARGET_BINARY_OP hook to disallow
operations on __mfp8 type.
(TARGET_INVALID_CONVERSION): Add define.
(TARGET_INVALID_UNARY_OP): Likewise.
* config/aarch64/aarch64.h (aarch64_mfp8_type_node): Add node for __mfp8
type.
(aarch64_mfp8_ptr_type_node): Add node for __mfp8 pointer type.
* config/aarch64/arm_neon.h (mfloat8_t): Add typedef.
* config/aarch64/arm_sve.h (mfloat8_t): Likewise.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/fp8_scalar_1.c: New tests in C.
* gcc.target/aarch64/fp8_scalar_typecheck_1.c: Likewise.
* gcc.target/aarch64/fp8_scalar_typecheck_2.C: New tests in C++.
---
Hi,
Is this ok for master? I do not have commit rights yet, if ok, can someone commit it on my behalf?
Regression tested with aarch64-unknown-linux-gnu.
Thanks,
Claudio Bantaloukas
gcc/config/aarch64/aarch64-builtins.cc | 23 ++
gcc/config/aarch64/aarch64.cc | 60 +++
gcc/config/aarch64/aarch64.h | 5 +
gcc/config/aarch64/arm_neon.h | 2 +
gcc/config/aarch64/arm_sve.h | 1 +
.../gcc.target/aarch64/fp8_scalar_1.c | 108 ++++++
.../aarch64/fp8_scalar_typecheck_1.c | 329 ++++++++++++++++
.../aarch64/fp8_scalar_typecheck_2.C | 354 ++++++++++++++++++
8 files changed, 882 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_2.C
Comments
Claudio Bantaloukas <claudio.bantaloukas@arm.com> writes:
> The ACLE defines a new scalar type, __mfp8. This is an opaque 8bit types that
> can only be used by fp8 intrinsics. Additionally, the mfloat8_t type is made
> available in arm_neon.h and arm_sve.h as an alias of the same.
>
> This implementation uses an INTEGER_TYPE, with precision 8 to represent
> __mfp8. Conversions to int and other types are disabled via the
> TARGET_INVALID_CONVERSION hook.
> Additionally, operations that are typically available to integer types are
> disabled via TARGET_INVALID_UNARY_OP and TARGET_INVALID_BINARY_OP hooks.
>
> gcc/ChangeLog:
>
> * config/aarch64/aarch64-builtins.cc (aarch64_mfp8_type_node): Add node
> for __mfp8 type.
> (aarch64_mfp8_ptr_type_node): Add node for __mfp8 pointer type.
> (aarch64_init_fp8_types): New function to initialise fp8 types and
> register with language backends.
> * config/aarch64/aarch64.cc (aarch64_invalid_conversion): Add function
> implementing TARGET_INVALID_CONVERSION hook that blocks conversion to
> and from __mfp8 type.
> (aarch64_invalid_unary_op): Add function implementing TARGET_UNARY_OP
> hook that blocks operations on __mfp8 other than &.
> (aarch64_invalid_binary_op): Extend TARGET_BINARY_OP hook to disallow
> operations on __mfp8 type.
> (TARGET_INVALID_CONVERSION): Add define.
> (TARGET_INVALID_UNARY_OP): Likewise.
> * config/aarch64/aarch64.h (aarch64_mfp8_type_node): Add node for __mfp8
> type.
> (aarch64_mfp8_ptr_type_node): Add node for __mfp8 pointer type.
> * config/aarch64/arm_neon.h (mfloat8_t): Add typedef.
> * config/aarch64/arm_sve.h (mfloat8_t): Likewise.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/aarch64/fp8_scalar_1.c: New tests in C.
> * gcc.target/aarch64/fp8_scalar_typecheck_1.c: Likewise.
> * gcc.target/aarch64/fp8_scalar_typecheck_2.C: New tests in C++.
C++ tests should go in g++.target instead.
I think the new type needs to be mangled explicitly, so that the
overloads in:
int foo(__mfp8) { return 1; }
int foo(unsigned char) { return 2; }
int bar(__mfp8 x) { return foo(x); }
are distinct. It'd also be good to have a constexpr version of foo
in the tests, to make sure that the right overload is chosen.
> ---
>
> Hi,
> Is this ok for master? I do not have commit rights yet, if ok, can someone commit it on my behalf?
>
> Regression tested with aarch64-unknown-linux-gnu.
>
> Thanks,
> Claudio Bantaloukas
>
> gcc/config/aarch64/aarch64-builtins.cc | 23 ++
> gcc/config/aarch64/aarch64.cc | 60 +++
> gcc/config/aarch64/aarch64.h | 5 +
> gcc/config/aarch64/arm_neon.h | 2 +
> gcc/config/aarch64/arm_sve.h | 1 +
> .../gcc.target/aarch64/fp8_scalar_1.c | 108 ++++++
> .../aarch64/fp8_scalar_typecheck_1.c | 329 ++++++++++++++++
> .../aarch64/fp8_scalar_typecheck_2.C | 354 ++++++++++++++++++
> 8 files changed, 882 insertions(+)
> create mode 100644 gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c
> create mode 100644 gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c
> create mode 100644 gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_2.C
>
> [...]
> @@ -1721,6 +1726,22 @@ aarch64_init_builtin_rsqrt (void)
> }
> }
>
> +static void
> +aarch64_init_fp8_types (void)
The function should have a comment before it.
> +{
> + aarch64_mfp8_type_node = make_node (INTEGER_TYPE);
> + TYPE_PRECISION (aarch64_mfp8_type_node) = 8;
> + TYPE_MIN_VALUE (aarch64_mfp8_type_node)
> + = TYPE_MIN_VALUE (unsigned_char_type_node);
> + TYPE_MAX_VALUE (aarch64_mfp8_type_node)
> + = TYPE_MAX_VALUE (unsigned_char_type_node);
If we're using the unsigned range, we should also set TYPE_UNSIGNED.
That said...
> + layout_type (aarch64_mfp8_type_node);
...it looks like the code above could be replaced by:
aarch64_mfp8_type_node = make_unsigned_type (8);
which would also give TYPE_MIN_VALUE and TYPE_MAX_VALUE the "right" types.
I was surprised that the tests worked so well with just a standard
integer type, without having to use build_distinct_type_copy.
But since they do, I agree we shouldn't use build_distinct_type_copy
unless a specific reason comes up.
> + SET_TYPE_MODE (aarch64_mfp8_type_node, QImode);
> +
> + lang_hooks.types.register_builtin_type (aarch64_mfp8_type_node, "__mfp8");
> + aarch64_mfp8_ptr_type_node = build_pointer_type (aarch64_mfp8_type_node);
> +}
> +
> /* Initialize the backend types that support the user-visible __fp16
> type, also initialize a pointer to that type, to be used when
> forming HFAs. */
> @@ -2128,6 +2149,8 @@ aarch64_general_init_builtins (void)
> {
> aarch64_init_fpsr_fpcr_builtins ();
>
> + aarch64_init_fp8_types ();
> +
> aarch64_init_fp16_types ();
>
> aarch64_init_bf16_types ();
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index 9810f2c0390..e774b95e430 100644
> --- a/gcc/config/aarch64/aarch64.cc
> +++ b/gcc/config/aarch64/aarch64.cc
> @@ -22450,6 +22450,35 @@ aarch64_mangle_type (const_tree type)
> return NULL;
> }
>
> +/* Implement TARGET_INVALID_CONVERSION.
> +
> +Return the diagnostic message when it is invalid to convert from fromtype to
> +totype, or NULL if validity should be determined by the front end. */
The usual style is not to reiterate the hook description, since when
that's happened in the past, the comments have become out of date
wrt the documentation. So just:
/* Implement TARGET_INVALID_CONVERSION. */
should be good enough.
> +
> +static const char *
> +aarch64_invalid_conversion (const_tree fromtype, const_tree totype)
> +{
> + /* Do not allow conversions to/from FP8. */
> + bool fromtype_is_fp8
> + = ((fromtype) && (TYPE_MODE (fromtype) == QImode)
> + && (TYPE_MAIN_VARIANT (fromtype) == aarch64_mfp8_type_node));
> + bool totype_is_fp8
> + = ((totype)
> + && (TYPE_MODE (totype) == QImode
> + && TYPE_MAIN_VARIANT (totype) == aarch64_mfp8_type_node));
Did you see null fromtypes and totypes? It doesn't look like the other
targets have needed to handle them, and it's not clear what the correct
behaviour would be in that case.
The QImode tests also look redundant.
Trying it locally, things seemed to work for me with:
bool fromtype_is_fp8
= TYPE_MAIN_VARIANT (fromtype) == aarch64_mfp8_type_node;
bool totype_is_fp8
= TYPE_MAIN_VARIANT (totype) == aarch64_mfp8_type_node;
> +
> + /* But do allow conversions between volatile and const __mfp8 */
> + if (fromtype_is_fp8 && totype_is_fp8)
> + return NULL;
> +
> + if (fromtype_is_fp8)
> + return N_ ("invalid conversion from type %<mfloat8_t%>");
> + if (totype_is_fp8)
> + return N_ ("invalid conversion to type %<mfloat8_t%>");
> + /* Conversion allowed. */
> + return NULL;
> +}
> +
> /* Implement TARGET_VERIFY_TYPE_CONTEXT. */
>
> static bool
> @@ -28967,6 +28996,24 @@ aarch64_stack_protect_guard (void)
> return NULL_TREE;
> }
>
> +/* Implement TARGET_INVALID_UNARY_OP.
> +
> + Return the diagnostic message string if the unary operation OP is
> + not permitted on TYPE, NULL otherwise. */
Similar comment about the comment here.
> +
> +static const char *
> +aarch64_invalid_unary_op (int op, const_tree type)
> +{
> + /* Reject all single-operand operations on __mfp8 except for &. */
> + if ((TYPE_MODE (type) == QImode)
> + && (TYPE_MAIN_VARIANT (type) == aarch64_mfp8_type_node)
> + && (op != ADDR_EXPR))
Just:
if (TYPE_MAIN_VARIANT (type) == aarch64_mfp8_type_node
&& op != ADDR_EXPR)
should be enough. (GCC style is not to add brackets around individual
comparisons, unless they're needed or span multiple lines.)
> + return N_ ("operation not permitted on type %<mfloat8_t%>");
> +
> + /* Operation allowed. */
> + return NULL;
> +}
> +
> /* Return the diagnostic message string if the binary operation OP is
> not permitted on TYPE1 and TYPE2, NULL otherwise. */
>
> @@ -28982,6 +29029,13 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
> != aarch64_sve::builtin_type_p (type2)))
> return N_("cannot combine GNU and SVE vectors in a binary operation");
>
> + /* Reject all 2-operand operations on __mfp8. */
> + if (((TYPE_MODE (type1) == QImode)
> + && (TYPE_MAIN_VARIANT (type1) == aarch64_mfp8_type_node))
> + || ((TYPE_MODE (type2) == QImode)
> + && (TYPE_MAIN_VARIANT (type2) == aarch64_mfp8_type_node)))
Similarly here.
> + return N_ ("operation not permitted on type %<mfloat8_t%>");
> +
> /* Operation allowed. */
> return NULL;
> }
> [...]
> diff --git a/gcc/config/aarch64/arm_neon.h b/gcc/config/aarch64/arm_neon.h
> index e376685489d..0092314cf75 100644
> --- a/gcc/config/aarch64/arm_neon.h
> +++ b/gcc/config/aarch64/arm_neon.h
> @@ -72,6 +72,8 @@ typedef __Poly16_t poly16_t;
> typedef __Poly64_t poly64_t;
> typedef __Poly128_t poly128_t;
>
> +typedef __mfp8 mfloat8_t;
> +
> typedef __fp16 float16_t;
> typedef float float32_t;
> typedef double float64_t;
> diff --git a/gcc/config/aarch64/arm_sve.h b/gcc/config/aarch64/arm_sve.h
> index aa0bd9909f9..61c440fef56 100644
> --- a/gcc/config/aarch64/arm_sve.h
> +++ b/gcc/config/aarch64/arm_sve.h
> @@ -29,6 +29,7 @@
> #include <arm_private_fp8.h>
> #include <arm_bf16.h>
>
> +typedef __mfp8 mfloat8_t;
> typedef __fp16 float16_t;
> typedef float float32_t;
> typedef double float64_t;
Ultran minor nit, but it'd be good to have a consistent separation style
between arm_neon.h and arm_sve.h. (Don't mind which we use.)
> diff --git a/gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c
> new file mode 100644
> index 00000000000..6925653e33c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c
> @@ -0,0 +1,108 @@
> +/* Test the fp8 ACLE intrinsics family. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -march=armv9.4-a+fp8" } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +#include <arm_neon.h>
> +
> +/*
> +**stacktest1:
> +** sub sp, sp, #16
> +** sxtb w0, w0
(This would become an AND if we do switch to an unsigned type.)
> +** strb w0, \[sp, 15\]
> +** ldrb w0, \[sp, 15\]
> +** add sp, sp, 16
> +** ret
> +*/
> +mfloat8_t
> +stacktest1 (mfloat8_t __a)
> +{
> + volatile mfloat8_t b = __a;
> + return b;
> +}
> +
> +/*
> +**fp8_mov_ww:
> +** dup b1, v2.b\[0\]
> +** ret
> +*/
> +void
> +fp8_mov_ww (void)
> +{
> + register mfloat8_t x asm ("h2");
> + register mfloat8_t y asm ("h1");
> + asm volatile ("" : "=w"(x));
> + y = x;
> + asm volatile ("" ::"w"(y));
> +}
> +
> +/*
> +**fp8_mov_rw:
> +** dup v1.8b, w1
> +** ret
> +*/
> +void
> +fp8_mov_rw (void)
> +{
> + register mfloat8_t x asm ("w1");
> + register mfloat8_t y asm ("h1");
> + asm volatile ("" : "=r"(x));
> + y = x;
> + asm volatile ("" ::"w"(y));
> +}
> +
> +/*
> +**fp8_mov_wr:
> +** umov w1, v1.b\[0\]
> +** ret
> +*/
> +void
> +fp8_mov_wr (void)
> +{
> + register mfloat8_t x asm ("h1");
> + register mfloat8_t y asm ("w1");
> + asm volatile ("" : "=w"(x));
> + y = x;
> + asm volatile ("" ::"r"(y));
> +}
> +
> +/*
> +**fp8_mov_rr:
> +** mov w1, w2
> +** ret
> +*/
> +void
> +fp8_mov_rr (void)
> +{
> + register mfloat8_t x asm ("w2");
> + register mfloat8_t y asm ("w1");
> + asm volatile ("" : "=r"(x));
> + y = x;
> + asm volatile ("" ::"r"(y));
> +}
> +
> +/*
> +**fp8_mov_rm:
> +** strb w2, \[x0\]
> +** ret
> +*/
> +void
> +fp8_mov_rm (mfloat8_t *ptr)
> +{
> + register mfloat8_t x asm ("w2");
> + asm volatile ("" : "=r"(x));
> + *ptr = x;
> +}
> +
> +/*
> +**fp8_mov_mr:
> +** ldrb w2, \[x0\]
> +** ret
> +*/
> +void
> +fp8_mov_mr (mfloat8_t *ptr)
> +{
> + register mfloat8_t y asm ("w2");
> + y = *ptr;
> + asm volatile ("" ::"r"(y));
> +}
Nice tests :-)
It would be good to test loads and stores for FPRs as well,
for completeness.
> diff --git a/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c
> new file mode 100644
> index 00000000000..122dc5aa2b5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c
> @@ -0,0 +1,329 @@
> +/* Test that there is no conversion between ints and mfloat8_t. */
> +/* { dg-do assemble } */
> +/* { dg-options "-O1 -march=armv9.4-a+fp8" } */
> +
> +#include <arm_neon.h>
> +#include <stdint.h>
> +
> +mfloat8_t glob_fp8;
> +
> +int is_an_int;
> +uint8_t is_a_uint8;
> +int8_t is_an_int8;
> +short is_a_short_int;
> +float is_a_float;
> +double is_a_double;
> +
> +uint8_t *uint8_ptr;
I think we should also test that:
mfloat8_t extern_fn1(void);
unsigned char extern_fn1(void);
mfloat8_t extern_fn2(void);
uint8_t extern_fn2(void);
unsigned char extern_fn3(void);
mfloat8_t extern_fn3(void);
uint8_t extern_fn4(void);
mfloat8_t extern_fn4(void);
give errors on the second definition. Same for C++.
> +
> +mfloat8_t
> +invalid_from_fp8 (uint16_t __a)
> +{
> + mfloat8_t b = __a; // { dg-error "invalid conversion to type 'mfloat8_t'" }
> + return b;
> +}
> +
> +uint16_t
> +invalid_to_fp8 (mfloat8_t __a)
> +{
> + uint16_t b = __a; // { dg-error "invalid conversion from type 'mfloat8_t'" }
> + return b;
> +}
> +
> +mfloat8_t
> +foo1 (void)
> +{
> + return (mfloat8_t)0x1234; // { dg-error {invalid conversion to type 'mfloat8_t'} }
> +}
> +mfloat8_t
> +foo2 (void)
> +{
> + return (mfloat8_t)(short)0x1234; // { dg-error {invalid conversion to type 'mfloat8_t'} }
> +}
> +
> +mfloat8_t
> +footest (mfloat8_t scalar0)
> +{
> +
> + /* Initialisation */
> +
> + mfloat8_t scalar1_1;
> + mfloat8_t scalar1_2 = glob_fp8;
> + mfloat8_t scalar1_3 = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_4
> + = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_5
> + = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_6
> + = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_8
> + = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_9
> + = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_10
> + = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_11
> + = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + int initi_1_1
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + float initi_1_2
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + short initi_1_4
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + double initi_1_5
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + uint8_t initi_1_6
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + int8_t initi_1_7
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + mfloat8_t scalar2_1 = {};
> + mfloat8_t scalar2_2 = { glob_fp8 };
> + mfloat8_t scalar2_3
> + = { 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_4
> + = { 0.1 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_5
> + = { is_a_float }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_6
> + = { is_an_int }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_8 = {
> + is_a_double
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_9 = {
> + is_a_short_int
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_10
> + = { is_a_uint8 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_11
> + = { is_an_int8 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + int initi_2_1
> + = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + float initi_2_2
> + = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + short initi_2_4
> + = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + double initi_2_5
> + = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + uint8_t initi_2_6
> + = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + int8_t initi_2_7
> + = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Assignments. */
> +
> + glob_fp8 = glob_fp8;
> + glob_fp8 = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8 = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8 = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + is_an_int
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_float
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_double
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_short_int
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_uint8
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_an_int8
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Casting. */
> +
> + (void)glob_fp8;
> + (mfloat8_t) glob_fp8;
> +
> + (int)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (float)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (double)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (short)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (uint8_t)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (int8_t)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + (mfloat8_t) is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t) is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t) is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t)
> + is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t) is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t) is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + /* Compound literals. */
> +
> + (mfloat8_t){};
> + (mfloat8_t){ glob_fp8 };
> + (mfloat8_t){ 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){ 0.1 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + is_a_float
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + is_an_int
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + is_a_double
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + is_a_short_int
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + is_a_uint8
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + is_an_int8
> + }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + (int){ glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (float){
> + glob_fp8
> + }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (double){
> + glob_fp8
> + }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (short){
> + glob_fp8
> + }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (uint8_t){
> + glob_fp8
> + }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (int8_t){
> + glob_fp8
> + }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Arrays and Structs. */
> +
> + typedef mfloat8_t array_type[2];
> + extern mfloat8_t extern_array[];
> +
> + mfloat8_t array[2];
> + mfloat8_t zero_length_array[0];
> + mfloat8_t empty_init_array[] = {};
> + typedef mfloat8_t some_other_type[is_an_int];
> +
> + struct struct1
> + {
> + mfloat8_t a;
> + };
> +
> + union union1
> + {
> + mfloat8_t a;
> + };
> +
> + /* Addressing and dereferencing. */
> +
> + mfloat8_t *fp8_ptr = &scalar0;
> + scalar0 = *fp8_ptr;
> +
> + /* Pointer assignment. */
> +
> + mfloat8_t *fp8_ptr2 = fp8_ptr;
> + mfloat8_t *fp8_ptr3 = array;
> +
> + /* Pointer arithmetic. */
> +
> + ++fp8_ptr;
> + --fp8_ptr;
> + fp8_ptr++;
> + fp8_ptr--;
> + fp8_ptr += 1;
> + fp8_ptr -= 1;
> + fp8_ptr - fp8_ptr2;
> + fp8_ptr = &fp8_ptr3[0];
> + fp8_ptr = &fp8_ptr3[1];
> +
> + /* Simple comparison. */
> + scalar0 > glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + glob_fp8
> + == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 > is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_float
> + == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 > 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 > 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0.1 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0
> + > is_an_int; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_an_int
> + == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Pointer comparison. */
> +
> + fp8_ptr == &scalar0;
> + fp8_ptr != &scalar0;
> + fp8_ptr < &scalar0;
> + fp8_ptr <= &scalar0;
> + fp8_ptr > &scalar0;
> + fp8_ptr >= &scalar0;
> + fp8_ptr == fp8_ptr2;
> + fp8_ptr != fp8_ptr2;
> + fp8_ptr < fp8_ptr2;
> + fp8_ptr <= fp8_ptr2;
> + fp8_ptr > fp8_ptr2;
> + fp8_ptr >= fp8_ptr2;
> +
> + /* Conditional expressions. */
> +
> + 0 ? scalar0 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? scalar0
> + : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? is_a_float
> + : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? scalar0 : 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? 0 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? 0.1 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? scalar0 : 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? fp8_ptr : fp8_ptr2;
> + 0 ? fp8_ptr : uint8_ptr; /* { dg-error {pointer type mismatch in conditional expression} } */
> + 0 ? uint8_ptr : fp8_ptr; /* { dg-error {pointer type mismatch in conditional expression} } */
> +
> + scalar0 ? scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 ? is_a_float /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 ? scalar0 : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 ? is_a_float : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Unary operators. */
> +
> + +scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + -scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + ~scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + !scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + *scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + __real scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + __imag scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + ++scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + --scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + scalar0++; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + scalar0--; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> +
> + /* Binary arithmetic operations. */
> +
> + scalar0 = glob_fp8 + scalar1_2; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 = glob_fp8 + *fp8_ptr; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 = glob_fp8
> + + 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0
> + = glob_fp8 + 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0
> + = glob_fp8
> + + is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
Hmm, so all the errors for binary operations come from the usual
integer conversions, rather than from the binary operation itself.
That makes sense.
Could you also test fp8 op fp8 only for the other binary operations,
including && and ||?
For C++, it would be good to test:
#include <type_traits>
static_assert(!std::is_integral<__mfp8>(), "not integral");
static_assert(!std::is_signed<__mfp8>(), "not signed");
static_assert(!std::is_unsigned<__mfp8>(), "not unsigned");
to make sure that we maintain the abstraction. Was kind-of surprised
that this Just Works -- didn't look into the mechanics of how it does.
Thanks,
Richard
> +
> + return scalar0;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_2.C b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_2.C
> new file mode 100644
> index 00000000000..f0a3c756c84
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_2.C
> @@ -0,0 +1,354 @@
> +/* Test that mfloat8_t is only usable with intrinsics, thus not convertible. */
> +/* { dg-do assemble } */
> +/* { dg-options "-O1 -march=armv9.4-a+fp8 -Wno-narrowing" } */
> +
> +#include <arm_neon.h>
> +#include <stdint.h>
> +
> +mfloat8_t glob_fp8;
> +
> +int is_an_int;
> +uint8_t is_a_uint8;
> +int8_t is_an_int8;
> +short is_a_short_int;
> +float is_a_float;
> +double is_a_double;
> +
> +uint8_t *uint8_ptr;
> +
> +mfloat8_t
> +invalid_from_fp8 (uint16_t __a)
> +{
> + mfloat8_t b = __a; /* { dg-error "invalid conversion to type 'mfloat8_t'" } */
> + return b;
> +}
> +
> +uint16_t
> +invalid_to_fp8 (mfloat8_t __a)
> +{
> + uint16_t b = __a; /*{ dg-error "invalid conversion from type 'mfloat8_t'" } */
> + return b;
> +}
> +
> +mfloat8_t
> +foo1 (void)
> +{
> + return (mfloat8_t)0x1234; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +}
> +
> +mfloat8_t
> +foo2 (void)
> +{
> + return (mfloat8_t)(short)0x1234; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +}
> +
> +mfloat8_t
> +footest (mfloat8_t scalar0)
> +{
> +
> + /* Initialisation */
> +
> + mfloat8_t scalar1_1;
> + mfloat8_t scalar1_2 = glob_fp8;
> + mfloat8_t scalar1_3
> + = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_4
> + = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_5
> + = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_6
> + = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_8
> + = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_9 = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_10
> + = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar1_11
> + = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + int initi_1_1
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + float initi_1_2
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + short initi_1_4
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + double initi_1_5
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + uint8_t initi_1_6
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + int8_t initi_1_7
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + mfloat8_t scalar2_1 = {};
> + mfloat8_t scalar2_2 = { glob_fp8 };
> + mfloat8_t scalar2_3
> + = { 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_4
> + = { 0.1 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + mfloat8_t scalar2_5 = {
> + is_a_float /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + mfloat8_t scalar2_6 = {
> + is_an_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + mfloat8_t scalar2_8 = {
> + is_a_double /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + mfloat8_t scalar2_9 = {
> + is_a_short_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + mfloat8_t scalar2_10 = {
> + is_a_uint8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + mfloat8_t scalar2_11 = {
> + is_an_int8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> +
> + int initi_2_1 = {
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + float initi_2_2 = {
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + short initi_2_4 = {
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + double initi_2_5 = {
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + uint8_t initi_2_6 = {
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + int8_t initi_2_7 = {
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> +
> + /* Assignments. */
> +
> + glob_fp8 = glob_fp8;
> + glob_fp8 = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8 = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8 = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + glob_fp8
> + = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + is_an_int
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_float
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_double
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_short_int
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_uint8
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_an_int8
> + = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Casting. */
> +
> + (void)glob_fp8;
> + (mfloat8_t) glob_fp8;
> +
> + (int)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (float)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (double)
> + glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (short)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (uint8_t)
> + glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + (int8_t)
> + glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + (mfloat8_t)
> + is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t)
> + is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t)
> + is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t) is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t)
> + is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t)
> + is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> +
> + /* Compound literals. */
> +
> + (mfloat8_t){};
> + (mfloat8_t){ glob_fp8 };
> + (mfloat8_t){ 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + (mfloat8_t){
> + 0.1 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + (mfloat8_t){
> + is_a_float /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + (mfloat8_t){
> + is_an_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + (mfloat8_t){
> + is_a_double /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + (mfloat8_t){
> + is_a_short_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + (mfloat8_t){
> + is_a_uint8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> + (mfloat8_t){
> + is_an_int8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
> + };
> +
> + (int){
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + (float){
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + (double){
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + (short){
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + (uint8_t){
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> + (int8_t){
> + glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + };
> +
> + /* Arrays and Structs. */
> +
> + typedef mfloat8_t array_type[2];
> + extern mfloat8_t extern_array[];
> +
> + mfloat8_t array[2];
> + mfloat8_t zero_length_array[0];
> + mfloat8_t empty_init_array[] = {};
> + typedef mfloat8_t some_other_type[is_an_int];
> +
> + struct struct1
> + {
> + mfloat8_t a;
> + };
> +
> + union union1
> + {
> + mfloat8_t a;
> + };
> +
> + /* Addressing and dereferencing. */
> +
> + mfloat8_t *fp8_ptr = &scalar0;
> + scalar0 = *fp8_ptr;
> +
> + /* Pointer assignment. */
> +
> + mfloat8_t *fp8_ptr2 = fp8_ptr;
> + mfloat8_t *fp8_ptr3 = array;
> +
> + /* Pointer arithmetic. */
> +
> + ++fp8_ptr;
> + --fp8_ptr;
> + fp8_ptr++;
> + fp8_ptr--;
> + fp8_ptr += 1;
> + fp8_ptr -= 1;
> + fp8_ptr - fp8_ptr2;
> + fp8_ptr = &fp8_ptr3[0];
> + fp8_ptr = &fp8_ptr3[1];
> +
> + /* Simple comparison. */
> + scalar0
> + > glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + glob_fp8
> + == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 > is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_a_float
> + == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 > 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 > 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0.1 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0
> + > is_an_int; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + is_an_int
> + == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Pointer comparison. */
> +
> + fp8_ptr == &scalar0;
> + fp8_ptr != &scalar0;
> + fp8_ptr < &scalar0;
> + fp8_ptr <= &scalar0;
> + fp8_ptr > &scalar0;
> + fp8_ptr >= &scalar0;
> + fp8_ptr == fp8_ptr2;
> + fp8_ptr != fp8_ptr2;
> + fp8_ptr < fp8_ptr2;
> + fp8_ptr <= fp8_ptr2;
> + fp8_ptr > fp8_ptr2;
> + fp8_ptr >= fp8_ptr2;
> +
> + /* Conditional expressions. */
> +
> + 0 ? scalar0 : scalar0;
> + 0 ? scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + : is_a_float;
> + 0 ? is_a_float
> + : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? scalar0 : 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? 0 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? 0.1
> + : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + 0 ? scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + : 0.1;
> + 0 ? fp8_ptr : fp8_ptr2;
> + 0 ? fp8_ptr : uint8_ptr; /* { dg-error {conditional expression between distinct pointer types} } */
> + 0 ? uint8_ptr : fp8_ptr; /* { dg-error {conditional expression between distinct pointer types} } */
> +
> + scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + ? scalar0
> + : scalar0;
> + scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + ? is_a_float
> + : scalar0;
> + scalar0 ? scalar0 : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 ? is_a_float : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + /* Unary operators. */
> +
> + +scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + -scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + ~scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + !scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + *scalar0; /* { dg-error {invalid type argument of unary} } */
> + __real scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + __imag scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + ++scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + --scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + scalar0++; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> + scalar0--; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
> +
> + /* Binary arithmetic operations. */
> +
> + scalar0 = glob_fp8 + scalar1_2; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 = glob_fp8 + *fp8_ptr; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 = glob_fp8
> + + 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 = glob_fp8
> + + 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> + scalar0 = glob_fp8 + is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
> +
> + return scalar0;
> +}
On 02/08/2024 12:17, Richard Sandiford wrote:
> Claudio Bantaloukas <claudio.bantaloukas@arm.com> writes:
>> The ACLE defines a new scalar type, __mfp8. This is an opaque 8bit types that
>> can only be used by fp8 intrinsics. Additionally, the mfloat8_t type is made
>> available in arm_neon.h and arm_sve.h as an alias of the same.
>>
>> This implementation uses an INTEGER_TYPE, with precision 8 to represent
>> __mfp8. Conversions to int and other types are disabled via the
>> TARGET_INVALID_CONVERSION hook.
>> Additionally, operations that are typically available to integer types are
>> disabled via TARGET_INVALID_UNARY_OP and TARGET_INVALID_BINARY_OP hooks.
>>
>> gcc/ChangeLog:
>>
>> * config/aarch64/aarch64-builtins.cc (aarch64_mfp8_type_node): Add node
>> for __mfp8 type.
>> (aarch64_mfp8_ptr_type_node): Add node for __mfp8 pointer type.
>> (aarch64_init_fp8_types): New function to initialise fp8 types and
>> register with language backends.
>> * config/aarch64/aarch64.cc (aarch64_invalid_conversion): Add function
>> implementing TARGET_INVALID_CONVERSION hook that blocks conversion to
>> and from __mfp8 type.
>> (aarch64_invalid_unary_op): Add function implementing TARGET_UNARY_OP
>> hook that blocks operations on __mfp8 other than &.
>> (aarch64_invalid_binary_op): Extend TARGET_BINARY_OP hook to disallow
>> operations on __mfp8 type.
>> (TARGET_INVALID_CONVERSION): Add define.
>> (TARGET_INVALID_UNARY_OP): Likewise.
>> * config/aarch64/aarch64.h (aarch64_mfp8_type_node): Add node for __mfp8
>> type.
>> (aarch64_mfp8_ptr_type_node): Add node for __mfp8 pointer type.
>> * config/aarch64/arm_neon.h (mfloat8_t): Add typedef.
>> * config/aarch64/arm_sve.h (mfloat8_t): Likewise.
>>
>> gcc/testsuite/ChangeLog:
>>
>> * gcc.target/aarch64/fp8_scalar_1.c: New tests in C.
>> * gcc.target/aarch64/fp8_scalar_typecheck_1.c: Likewise.
>> * gcc.target/aarch64/fp8_scalar_typecheck_2.C: New tests in C++.
>
Hi Richard,
thank you for the super fast review!
> C++ tests should go in g++.target instead.
Done
> I think the new type needs to be mangled explicitly, so that the
> overloads in:
>
> int foo(__mfp8) { return 1; }
> int foo(unsigned char) { return 2; }
> int bar(__mfp8 x) { return foo(x); }
>
> are distinct. It'd also be good to have a constexpr version of foo
> in the tests, to make sure that the right overload is chosen.
Added both regular and constexpr overloading checks.
>>
>> +static void
>> +aarch64_init_fp8_types (void)
>
> The function should have a comment before it.
>
Added
>> +{
>> + aarch64_mfp8_type_node = make_node (INTEGER_TYPE);
>> + TYPE_PRECISION (aarch64_mfp8_type_node) = 8;
>> + TYPE_MIN_VALUE (aarch64_mfp8_type_node)
>> + = TYPE_MIN_VALUE (unsigned_char_type_node);
>> + TYPE_MAX_VALUE (aarch64_mfp8_type_node)
>> + = TYPE_MAX_VALUE (unsigned_char_type_node);
>
> If we're using the unsigned range, we should also set TYPE_UNSIGNED.
> That said...
>
>> + layout_type (aarch64_mfp8_type_node);
>
> ...it looks like the code above could be replaced by:
>
> aarch64_mfp8_type_node = make_unsigned_type (8);
>
> which would also give TYPE_MIN_VALUE and TYPE_MAX_VALUE the "right" types.
Done, this has reduced the function considerably, thanks!
> I was surprised that the tests worked so well with just a standard
> integer type, without having to use build_distinct_type_copy.
> But since they do, I agree we shouldn't use build_distinct_type_copy
> unless a specific reason comes up.
>
Haven't found a specific reason to up to now.
>>
>> +/* Implement TARGET_INVALID_CONVERSION.
>> +
>> +Return the diagnostic message when it is invalid to convert from fromtype to
>> +totype, or NULL if validity should be determined by the front end. */
>
> The usual style is not to reiterate the hook description, since when
> that's happened in the past, the comments have become out of date
> wrt the documentation. So just:
>
> /* Implement TARGET_INVALID_CONVERSION. */
>
> should be good enough.
Done
>> +
>> +static const char *
>> +aarch64_invalid_conversion (const_tree fromtype, const_tree totype)
>> +{
>> + /* Do not allow conversions to/from FP8. */
>> + bool fromtype_is_fp8
>> + = ((fromtype) && (TYPE_MODE (fromtype) == QImode)
>> + && (TYPE_MAIN_VARIANT (fromtype) == aarch64_mfp8_type_node));
>> + bool totype_is_fp8
>> + = ((totype)
>> + && (TYPE_MODE (totype) == QImode
>> + && TYPE_MAIN_VARIANT (totype) == aarch64_mfp8_type_node));
>
> Did you see null fromtypes and totypes? It doesn't look like the other
> targets have needed to handle them, and it's not clear what the correct
> behaviour would be in that case.
>
> The QImode tests also look redundant.
>
> Trying it locally, things seemed to work for me with:
>
> bool fromtype_is_fp8
> = TYPE_MAIN_VARIANT (fromtype) == aarch64_mfp8_type_node;
> bool totype_is_fp8
> = TYPE_MAIN_VARIANT (totype) == aarch64_mfp8_type_node;
A previous version of this patch was not setting the minval and maxval
of the type. This was causing narrowing tests to fail in C++ and the
conversion checking would be called with a null fromtype with this code,
causing an ICE
__mfp8 global_fp8{};
With minval and maxval properly set, the ICEs no longer occur. I'm
simplifying the checks as you propose.
>>
>> +/* Implement TARGET_INVALID_UNARY_OP.
>> +
>> + Return the diagnostic message string if the unary operation OP is
>> + not permitted on TYPE, NULL otherwise. */
>
> Similar comment about the comment here.
Done
>
>> +
>> +static const char *
>> +aarch64_invalid_unary_op (int op, const_tree type)
>> +{
>> + /* Reject all single-operand operations on __mfp8 except for &. */
>> + if ((TYPE_MODE (type) == QImode)
>> + && (TYPE_MAIN_VARIANT (type) == aarch64_mfp8_type_node)
>> + && (op != ADDR_EXPR))
>
> Just:
>
> if (TYPE_MAIN_VARIANT (type) == aarch64_mfp8_type_node
> && op != ADDR_EXPR)
>
> should be enough. (GCC style is not to add brackets around individual
> comparisons, unless they're needed or span multiple lines.)
Done
>> + return N_ ("operation not permitted on type %<mfloat8_t%>");
>> +
>> + /* Operation allowed. */
>> + return NULL;
>> +}
>> +
>> /* Return the diagnostic message string if the binary operation OP is
>> not permitted on TYPE1 and TYPE2, NULL otherwise. */
>>
>> @@ -28982,6 +29029,13 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
>> != aarch64_sve::builtin_type_p (type2)))
>> return N_("cannot combine GNU and SVE vectors in a binary operation");
>>
>> + /* Reject all 2-operand operations on __mfp8. */
>> + if (((TYPE_MODE (type1) == QImode)
>> + && (TYPE_MAIN_VARIANT (type1) == aarch64_mfp8_type_node))
>> + || ((TYPE_MODE (type2) == QImode)
>> + && (TYPE_MAIN_VARIANT (type2) == aarch64_mfp8_type_node)))
>
> Similarly here.
Done
>
>> + return N_ ("operation not permitted on type %<mfloat8_t%>");
>> +
>> /* Operation allowed. */
>> return NULL;
>> }
>> [...]
>> diff --git a/gcc/config/aarch64/arm_neon.h b/gcc/config/aarch64/arm_neon.h
>> index e376685489d..0092314cf75 100644
>> --- a/gcc/config/aarch64/arm_neon.h
>> +++ b/gcc/config/aarch64/arm_neon.h
>> @@ -72,6 +72,8 @@ typedef __Poly16_t poly16_t;
>> typedef __Poly64_t poly64_t;
>> typedef __Poly128_t poly128_t;
>>
>> +typedef __mfp8 mfloat8_t;
>> +
>> typedef __fp16 float16_t;
>> typedef float float32_t;
>> typedef double float64_t;
>> diff --git a/gcc/config/aarch64/arm_sve.h b/gcc/config/aarch64/arm_sve.h
>> index aa0bd9909f9..61c440fef56 100644
>> --- a/gcc/config/aarch64/arm_sve.h
>> +++ b/gcc/config/aarch64/arm_sve.h
>> @@ -29,6 +29,7 @@
>> #include <arm_private_fp8.h>
>> #include <arm_bf16.h>
>>
>> +typedef __mfp8 mfloat8_t;
>> typedef __fp16 float16_t;
>> typedef float float32_t;
>> typedef double float64_t;
>
> Ultran minor nit, but it'd be good to have a consistent separation style
> between arm_neon.h and arm_sve.h. (Don't mind which we use.)
Separated __mfp8 in both files.
>> diff --git a/gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c
>> new file mode 100644
>> index 00000000000..6925653e33c
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_1.c
>> @@ -0,0 +1,108 @@
>> +/* Test the fp8 ACLE intrinsics family. */
>> +/* { dg-do compile } */
>> +/* { dg-options "-O2 -march=armv9.4-a+fp8" } */
>> +/* { dg-final { check-function-bodies "**" "" "" } } */
>> +
>> +#include <arm_neon.h>
>> +
>> +/*
>> +**stacktest1:
>> +** sub sp, sp, #16
>> +** sxtb w0, w0
>
> (This would become an AND if we do switch to an unsigned type.)
Yep!
>> +** strb w0, \[sp, 15\]
>> +** ldrb w0, \[sp, 15\]
>> +** add sp, sp, 16
>> +** ret
>> +*/
>> +mfloat8_t
>> +stacktest1 (mfloat8_t __a)
>> +{
>> + volatile mfloat8_t b = __a;
>> + return b;
>> +}
>> +
>> +/*
>> +**fp8_mov_ww:
>> +** dup b1, v2.b\[0\]
>> +** ret
>> +*/
>> +void
>> +fp8_mov_ww (void)
>> +{
>> + register mfloat8_t x asm ("h2");
>> + register mfloat8_t y asm ("h1");
>> + asm volatile ("" : "=w"(x));
>> + y = x;
>> + asm volatile ("" ::"w"(y));
>> +}
>> +
>> +/*
>> +**fp8_mov_rw:
>> +** dup v1.8b, w1
>> +** ret
>> +*/
>> +void
>> +fp8_mov_rw (void)
>> +{
>> + register mfloat8_t x asm ("w1");
>> + register mfloat8_t y asm ("h1");
>> + asm volatile ("" : "=r"(x));
>> + y = x;
>> + asm volatile ("" ::"w"(y));
>> +}
>> +
>> +/*
>> +**fp8_mov_wr:
>> +** umov w1, v1.b\[0\]
>> +** ret
>> +*/
>> +void
>> +fp8_mov_wr (void)
>> +{
>> + register mfloat8_t x asm ("h1");
>> + register mfloat8_t y asm ("w1");
>> + asm volatile ("" : "=w"(x));
>> + y = x;
>> + asm volatile ("" ::"r"(y));
>> +}
>> +
>> +/*
>> +**fp8_mov_rr:
>> +** mov w1, w2
>> +** ret
>> +*/
>> +void
>> +fp8_mov_rr (void)
>> +{
>> + register mfloat8_t x asm ("w2");
>> + register mfloat8_t y asm ("w1");
>> + asm volatile ("" : "=r"(x));
>> + y = x;
>> + asm volatile ("" ::"r"(y));
>> +}
>> +
>> +/*
>> +**fp8_mov_rm:
>> +** strb w2, \[x0\]
>> +** ret
>> +*/
>> +void
>> +fp8_mov_rm (mfloat8_t *ptr)
>> +{
>> + register mfloat8_t x asm ("w2");
>> + asm volatile ("" : "=r"(x));
>> + *ptr = x;
>> +}
>> +
>> +/*
>> +**fp8_mov_mr:
>> +** ldrb w2, \[x0\]
>> +** ret
>> +*/
>> +void
>> +fp8_mov_mr (mfloat8_t *ptr)
>> +{
>> + register mfloat8_t y asm ("w2");
>> + y = *ptr;
>> + asm volatile ("" ::"r"(y));
>> +}
>
> Nice tests :-)
Yay bfloat types!
> It would be good to test loads and stores for FPRs as well,
> for completeness.
>
>> diff --git a/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c
>> new file mode 100644
>> index 00000000000..122dc5aa2b5
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/aarch64/fp8_scalar_typecheck_1.c
>> @@ -0,0 +1,329 @@
>> +/* Test that there is no conversion between ints and mfloat8_t. */
>> +/* { dg-do assemble } */
>> +/* { dg-options "-O1 -march=armv9.4-a+fp8" } */
>> +
>> +#include <arm_neon.h>
>> +#include <stdint.h>
>> +
>> +mfloat8_t glob_fp8;
>> +
>> +int is_an_int;
>> +uint8_t is_a_uint8;
>> +int8_t is_an_int8;
>> +short is_a_short_int;
>> +float is_a_float;
>> +double is_a_double;
>> +
>> +uint8_t *uint8_ptr;
>
> I think we should also test that:
>
> mfloat8_t extern_fn1(void);
> unsigned char extern_fn1(void);
>
> mfloat8_t extern_fn2(void);
> uint8_t extern_fn2(void);
>
> unsigned char extern_fn3(void);
> mfloat8_t extern_fn3(void);
>
> uint8_t extern_fn4(void);
> mfloat8_t extern_fn4(void);
Done, also adding a check using mfloat8_t and uint8_t in the argument.
> give errors on the second definition. Same for C++.
Done, minus the argument overloading.
>> + /* Unary operators. */
>> +
>> + +scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + -scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + ~scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + !scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + *scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
>> + __real scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + __imag scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + ++scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + --scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + scalar0++; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> + scalar0--; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
>> +
>> + /* Binary arithmetic operations. */
>> +
>> + scalar0 = glob_fp8 + scalar1_2; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
>> + scalar0 = glob_fp8 + *fp8_ptr; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
>> + scalar0 = glob_fp8
>> + + 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
>> + scalar0
>> + = glob_fp8 + 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
>> + scalar0
>> + = glob_fp8
>> + + is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
>
> Hmm, so all the errors for binary operations come from the usual
> integer conversions, rather than from the binary operation itself.
> That makes sense.
>
> Could you also test fp8 op fp8 only for the other binary operations,
> including && and ||?
Done, all fail on invalid conversion from type 'mfloat8_t'
>
> For C++, it would be good to test:
>
> #include <type_traits>
>
> static_assert(!std::is_integral<__mfp8>(), "not integral");
> static_assert(!std::is_signed<__mfp8>(), "not signed");
> static_assert(!std::is_unsigned<__mfp8>(), "not unsigned");
>
> to make sure that we maintain the abstraction. Was kind-of surprised
> that this Just Works -- didn't look into the mechanics of how it does.
Done
> Thanks,
> Richard
Thank you!
I'll ask Andrew to check and post the updated version for me as I'll be
on holidays.
Cheers,
Claudio
@@ -961,6 +961,11 @@ static GTY(()) tree aarch64_simd_intOI_type_node = NULL_TREE;
static GTY(()) tree aarch64_simd_intCI_type_node = NULL_TREE;
static GTY(()) tree aarch64_simd_intXI_type_node = NULL_TREE;
+/* The user-visible __mfp8 type, and a pointer to that type. Used
+ across the back-end. */
+tree aarch64_mfp8_type_node = NULL_TREE;
+tree aarch64_mfp8_ptr_type_node = NULL_TREE;
+
/* The user-visible __fp16 type, and a pointer to that type. Used
across the back-end. */
tree aarch64_fp16_type_node = NULL_TREE;
@@ -1721,6 +1726,22 @@ aarch64_init_builtin_rsqrt (void)
}
}
+static void
+aarch64_init_fp8_types (void)
+{
+ aarch64_mfp8_type_node = make_node (INTEGER_TYPE);
+ TYPE_PRECISION (aarch64_mfp8_type_node) = 8;
+ TYPE_MIN_VALUE (aarch64_mfp8_type_node)
+ = TYPE_MIN_VALUE (unsigned_char_type_node);
+ TYPE_MAX_VALUE (aarch64_mfp8_type_node)
+ = TYPE_MAX_VALUE (unsigned_char_type_node);
+ layout_type (aarch64_mfp8_type_node);
+ SET_TYPE_MODE (aarch64_mfp8_type_node, QImode);
+
+ lang_hooks.types.register_builtin_type (aarch64_mfp8_type_node, "__mfp8");
+ aarch64_mfp8_ptr_type_node = build_pointer_type (aarch64_mfp8_type_node);
+}
+
/* Initialize the backend types that support the user-visible __fp16
type, also initialize a pointer to that type, to be used when
forming HFAs. */
@@ -2128,6 +2149,8 @@ aarch64_general_init_builtins (void)
{
aarch64_init_fpsr_fpcr_builtins ();
+ aarch64_init_fp8_types ();
+
aarch64_init_fp16_types ();
aarch64_init_bf16_types ();
@@ -22450,6 +22450,35 @@ aarch64_mangle_type (const_tree type)
return NULL;
}
+/* Implement TARGET_INVALID_CONVERSION.
+
+Return the diagnostic message when it is invalid to convert from fromtype to
+totype, or NULL if validity should be determined by the front end. */
+
+static const char *
+aarch64_invalid_conversion (const_tree fromtype, const_tree totype)
+{
+ /* Do not allow conversions to/from FP8. */
+ bool fromtype_is_fp8
+ = ((fromtype) && (TYPE_MODE (fromtype) == QImode)
+ && (TYPE_MAIN_VARIANT (fromtype) == aarch64_mfp8_type_node));
+ bool totype_is_fp8
+ = ((totype)
+ && (TYPE_MODE (totype) == QImode
+ && TYPE_MAIN_VARIANT (totype) == aarch64_mfp8_type_node));
+
+ /* But do allow conversions between volatile and const __mfp8 */
+ if (fromtype_is_fp8 && totype_is_fp8)
+ return NULL;
+
+ if (fromtype_is_fp8)
+ return N_ ("invalid conversion from type %<mfloat8_t%>");
+ if (totype_is_fp8)
+ return N_ ("invalid conversion to type %<mfloat8_t%>");
+ /* Conversion allowed. */
+ return NULL;
+}
+
/* Implement TARGET_VERIFY_TYPE_CONTEXT. */
static bool
@@ -28967,6 +28996,24 @@ aarch64_stack_protect_guard (void)
return NULL_TREE;
}
+/* Implement TARGET_INVALID_UNARY_OP.
+
+ Return the diagnostic message string if the unary operation OP is
+ not permitted on TYPE, NULL otherwise. */
+
+static const char *
+aarch64_invalid_unary_op (int op, const_tree type)
+{
+ /* Reject all single-operand operations on __mfp8 except for &. */
+ if ((TYPE_MODE (type) == QImode)
+ && (TYPE_MAIN_VARIANT (type) == aarch64_mfp8_type_node)
+ && (op != ADDR_EXPR))
+ return N_ ("operation not permitted on type %<mfloat8_t%>");
+
+ /* Operation allowed. */
+ return NULL;
+}
+
/* Return the diagnostic message string if the binary operation OP is
not permitted on TYPE1 and TYPE2, NULL otherwise. */
@@ -28982,6 +29029,13 @@ aarch64_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
!= aarch64_sve::builtin_type_p (type2)))
return N_("cannot combine GNU and SVE vectors in a binary operation");
+ /* Reject all 2-operand operations on __mfp8. */
+ if (((TYPE_MODE (type1) == QImode)
+ && (TYPE_MAIN_VARIANT (type1) == aarch64_mfp8_type_node))
+ || ((TYPE_MODE (type2) == QImode)
+ && (TYPE_MAIN_VARIANT (type2) == aarch64_mfp8_type_node)))
+ return N_ ("operation not permitted on type %<mfloat8_t%>");
+
/* Operation allowed. */
return NULL;
}
@@ -30699,6 +30753,12 @@ aarch64_libgcc_floating_mode_supported_p
#undef TARGET_MANGLE_TYPE
#define TARGET_MANGLE_TYPE aarch64_mangle_type
+#undef TARGET_INVALID_CONVERSION
+#define TARGET_INVALID_CONVERSION aarch64_invalid_conversion
+
+#undef TARGET_INVALID_UNARY_OP
+#define TARGET_INVALID_UNARY_OP aarch64_invalid_unary_op
+
#undef TARGET_INVALID_BINARY_OP
#define TARGET_INVALID_BINARY_OP aarch64_invalid_binary_op
@@ -1447,6 +1447,11 @@ extern const char *aarch64_rewrite_mcpu (int argc, const char **argv);
#define ASM_OUTPUT_POOL_EPILOGUE aarch64_asm_output_pool_epilogue
+/* This type is the user-visible __mfp8, and a pointer to that type. We
+ need it in many places in the backend. Defined in aarch64-builtins.cc. */
+extern GTY(()) tree aarch64_mfp8_type_node;
+extern GTY(()) tree aarch64_mfp8_ptr_type_node;
+
/* This type is the user-visible __fp16, and a pointer to that type. We
need it in many places in the backend. Defined in aarch64-builtins.cc. */
extern GTY(()) tree aarch64_fp16_type_node;
@@ -72,6 +72,8 @@ typedef __Poly16_t poly16_t;
typedef __Poly64_t poly64_t;
typedef __Poly128_t poly128_t;
+typedef __mfp8 mfloat8_t;
+
typedef __fp16 float16_t;
typedef float float32_t;
typedef double float64_t;
@@ -29,6 +29,7 @@
#include <arm_private_fp8.h>
#include <arm_bf16.h>
+typedef __mfp8 mfloat8_t;
typedef __fp16 float16_t;
typedef float float32_t;
typedef double float64_t;
new file mode 100644
@@ -0,0 +1,108 @@
+/* Test the fp8 ACLE intrinsics family. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=armv9.4-a+fp8" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <arm_neon.h>
+
+/*
+**stacktest1:
+** sub sp, sp, #16
+** sxtb w0, w0
+** strb w0, \[sp, 15\]
+** ldrb w0, \[sp, 15\]
+** add sp, sp, 16
+** ret
+*/
+mfloat8_t
+stacktest1 (mfloat8_t __a)
+{
+ volatile mfloat8_t b = __a;
+ return b;
+}
+
+/*
+**fp8_mov_ww:
+** dup b1, v2.b\[0\]
+** ret
+*/
+void
+fp8_mov_ww (void)
+{
+ register mfloat8_t x asm ("h2");
+ register mfloat8_t y asm ("h1");
+ asm volatile ("" : "=w"(x));
+ y = x;
+ asm volatile ("" ::"w"(y));
+}
+
+/*
+**fp8_mov_rw:
+** dup v1.8b, w1
+** ret
+*/
+void
+fp8_mov_rw (void)
+{
+ register mfloat8_t x asm ("w1");
+ register mfloat8_t y asm ("h1");
+ asm volatile ("" : "=r"(x));
+ y = x;
+ asm volatile ("" ::"w"(y));
+}
+
+/*
+**fp8_mov_wr:
+** umov w1, v1.b\[0\]
+** ret
+*/
+void
+fp8_mov_wr (void)
+{
+ register mfloat8_t x asm ("h1");
+ register mfloat8_t y asm ("w1");
+ asm volatile ("" : "=w"(x));
+ y = x;
+ asm volatile ("" ::"r"(y));
+}
+
+/*
+**fp8_mov_rr:
+** mov w1, w2
+** ret
+*/
+void
+fp8_mov_rr (void)
+{
+ register mfloat8_t x asm ("w2");
+ register mfloat8_t y asm ("w1");
+ asm volatile ("" : "=r"(x));
+ y = x;
+ asm volatile ("" ::"r"(y));
+}
+
+/*
+**fp8_mov_rm:
+** strb w2, \[x0\]
+** ret
+*/
+void
+fp8_mov_rm (mfloat8_t *ptr)
+{
+ register mfloat8_t x asm ("w2");
+ asm volatile ("" : "=r"(x));
+ *ptr = x;
+}
+
+/*
+**fp8_mov_mr:
+** ldrb w2, \[x0\]
+** ret
+*/
+void
+fp8_mov_mr (mfloat8_t *ptr)
+{
+ register mfloat8_t y asm ("w2");
+ y = *ptr;
+ asm volatile ("" ::"r"(y));
+}
new file mode 100644
@@ -0,0 +1,329 @@
+/* Test that there is no conversion between ints and mfloat8_t. */
+/* { dg-do assemble } */
+/* { dg-options "-O1 -march=armv9.4-a+fp8" } */
+
+#include <arm_neon.h>
+#include <stdint.h>
+
+mfloat8_t glob_fp8;
+
+int is_an_int;
+uint8_t is_a_uint8;
+int8_t is_an_int8;
+short is_a_short_int;
+float is_a_float;
+double is_a_double;
+
+uint8_t *uint8_ptr;
+
+mfloat8_t
+invalid_from_fp8 (uint16_t __a)
+{
+ mfloat8_t b = __a; // { dg-error "invalid conversion to type 'mfloat8_t'" }
+ return b;
+}
+
+uint16_t
+invalid_to_fp8 (mfloat8_t __a)
+{
+ uint16_t b = __a; // { dg-error "invalid conversion from type 'mfloat8_t'" }
+ return b;
+}
+
+mfloat8_t
+foo1 (void)
+{
+ return (mfloat8_t)0x1234; // { dg-error {invalid conversion to type 'mfloat8_t'} }
+}
+mfloat8_t
+foo2 (void)
+{
+ return (mfloat8_t)(short)0x1234; // { dg-error {invalid conversion to type 'mfloat8_t'} }
+}
+
+mfloat8_t
+footest (mfloat8_t scalar0)
+{
+
+ /* Initialisation */
+
+ mfloat8_t scalar1_1;
+ mfloat8_t scalar1_2 = glob_fp8;
+ mfloat8_t scalar1_3 = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_4
+ = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_5
+ = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_6
+ = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_8
+ = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_9
+ = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_10
+ = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_11
+ = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ int initi_1_1
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ float initi_1_2
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ short initi_1_4
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ double initi_1_5
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ uint8_t initi_1_6
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ int8_t initi_1_7
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ mfloat8_t scalar2_1 = {};
+ mfloat8_t scalar2_2 = { glob_fp8 };
+ mfloat8_t scalar2_3
+ = { 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_4
+ = { 0.1 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_5
+ = { is_a_float }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_6
+ = { is_an_int }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_8 = {
+ is_a_double
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_9 = {
+ is_a_short_int
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_10
+ = { is_a_uint8 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_11
+ = { is_an_int8 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ int initi_2_1
+ = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ float initi_2_2
+ = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ short initi_2_4
+ = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ double initi_2_5
+ = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ uint8_t initi_2_6
+ = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ int8_t initi_2_7
+ = { glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Assignments. */
+
+ glob_fp8 = glob_fp8;
+ glob_fp8 = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8 = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8 = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ is_an_int
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_float
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_double
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_short_int
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_uint8
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_an_int8
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Casting. */
+
+ (void)glob_fp8;
+ (mfloat8_t) glob_fp8;
+
+ (int)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (float)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (double)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (short)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (uint8_t)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (int8_t)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ (mfloat8_t) is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t) is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t) is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t)
+ is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t) is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t) is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ /* Compound literals. */
+
+ (mfloat8_t){};
+ (mfloat8_t){ glob_fp8 };
+ (mfloat8_t){ 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){ 0.1 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ is_a_float
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ is_an_int
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ is_a_double
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ is_a_short_int
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ is_a_uint8
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ is_an_int8
+ }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ (int){ glob_fp8 }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (float){
+ glob_fp8
+ }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (double){
+ glob_fp8
+ }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (short){
+ glob_fp8
+ }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (uint8_t){
+ glob_fp8
+ }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (int8_t){
+ glob_fp8
+ }; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Arrays and Structs. */
+
+ typedef mfloat8_t array_type[2];
+ extern mfloat8_t extern_array[];
+
+ mfloat8_t array[2];
+ mfloat8_t zero_length_array[0];
+ mfloat8_t empty_init_array[] = {};
+ typedef mfloat8_t some_other_type[is_an_int];
+
+ struct struct1
+ {
+ mfloat8_t a;
+ };
+
+ union union1
+ {
+ mfloat8_t a;
+ };
+
+ /* Addressing and dereferencing. */
+
+ mfloat8_t *fp8_ptr = &scalar0;
+ scalar0 = *fp8_ptr;
+
+ /* Pointer assignment. */
+
+ mfloat8_t *fp8_ptr2 = fp8_ptr;
+ mfloat8_t *fp8_ptr3 = array;
+
+ /* Pointer arithmetic. */
+
+ ++fp8_ptr;
+ --fp8_ptr;
+ fp8_ptr++;
+ fp8_ptr--;
+ fp8_ptr += 1;
+ fp8_ptr -= 1;
+ fp8_ptr - fp8_ptr2;
+ fp8_ptr = &fp8_ptr3[0];
+ fp8_ptr = &fp8_ptr3[1];
+
+ /* Simple comparison. */
+ scalar0 > glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ glob_fp8
+ == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 > is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_float
+ == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 > 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 > 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0.1 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0
+ > is_an_int; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_an_int
+ == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Pointer comparison. */
+
+ fp8_ptr == &scalar0;
+ fp8_ptr != &scalar0;
+ fp8_ptr < &scalar0;
+ fp8_ptr <= &scalar0;
+ fp8_ptr > &scalar0;
+ fp8_ptr >= &scalar0;
+ fp8_ptr == fp8_ptr2;
+ fp8_ptr != fp8_ptr2;
+ fp8_ptr < fp8_ptr2;
+ fp8_ptr <= fp8_ptr2;
+ fp8_ptr > fp8_ptr2;
+ fp8_ptr >= fp8_ptr2;
+
+ /* Conditional expressions. */
+
+ 0 ? scalar0 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? scalar0
+ : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? is_a_float
+ : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? scalar0 : 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? 0 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? 0.1 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? scalar0 : 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? fp8_ptr : fp8_ptr2;
+ 0 ? fp8_ptr : uint8_ptr; /* { dg-error {pointer type mismatch in conditional expression} } */
+ 0 ? uint8_ptr : fp8_ptr; /* { dg-error {pointer type mismatch in conditional expression} } */
+
+ scalar0 ? scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 ? is_a_float /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 ? scalar0 : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 ? is_a_float : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Unary operators. */
+
+ +scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ -scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ ~scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ !scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ *scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ __real scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ __imag scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ ++scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ --scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ scalar0++; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ scalar0--; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+
+ /* Binary arithmetic operations. */
+
+ scalar0 = glob_fp8 + scalar1_2; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 = glob_fp8 + *fp8_ptr; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 = glob_fp8
+ + 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0
+ = glob_fp8 + 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0
+ = glob_fp8
+ + is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ return scalar0;
+}
new file mode 100644
@@ -0,0 +1,354 @@
+/* Test that mfloat8_t is only usable with intrinsics, thus not convertible. */
+/* { dg-do assemble } */
+/* { dg-options "-O1 -march=armv9.4-a+fp8 -Wno-narrowing" } */
+
+#include <arm_neon.h>
+#include <stdint.h>
+
+mfloat8_t glob_fp8;
+
+int is_an_int;
+uint8_t is_a_uint8;
+int8_t is_an_int8;
+short is_a_short_int;
+float is_a_float;
+double is_a_double;
+
+uint8_t *uint8_ptr;
+
+mfloat8_t
+invalid_from_fp8 (uint16_t __a)
+{
+ mfloat8_t b = __a; /* { dg-error "invalid conversion to type 'mfloat8_t'" } */
+ return b;
+}
+
+uint16_t
+invalid_to_fp8 (mfloat8_t __a)
+{
+ uint16_t b = __a; /*{ dg-error "invalid conversion from type 'mfloat8_t'" } */
+ return b;
+}
+
+mfloat8_t
+foo1 (void)
+{
+ return (mfloat8_t)0x1234; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+}
+
+mfloat8_t
+foo2 (void)
+{
+ return (mfloat8_t)(short)0x1234; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+}
+
+mfloat8_t
+footest (mfloat8_t scalar0)
+{
+
+ /* Initialisation */
+
+ mfloat8_t scalar1_1;
+ mfloat8_t scalar1_2 = glob_fp8;
+ mfloat8_t scalar1_3
+ = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_4
+ = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_5
+ = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_6
+ = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_8
+ = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_9 = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_10
+ = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar1_11
+ = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ int initi_1_1
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ float initi_1_2
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ short initi_1_4
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ double initi_1_5
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ uint8_t initi_1_6
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ int8_t initi_1_7
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ mfloat8_t scalar2_1 = {};
+ mfloat8_t scalar2_2 = { glob_fp8 };
+ mfloat8_t scalar2_3
+ = { 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_4
+ = { 0.1 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ mfloat8_t scalar2_5 = {
+ is_a_float /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ mfloat8_t scalar2_6 = {
+ is_an_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ mfloat8_t scalar2_8 = {
+ is_a_double /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ mfloat8_t scalar2_9 = {
+ is_a_short_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ mfloat8_t scalar2_10 = {
+ is_a_uint8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ mfloat8_t scalar2_11 = {
+ is_an_int8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+
+ int initi_2_1 = {
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ float initi_2_2 = {
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ short initi_2_4 = {
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ double initi_2_5 = {
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ uint8_t initi_2_6 = {
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ int8_t initi_2_7 = {
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+
+ /* Assignments. */
+
+ glob_fp8 = glob_fp8;
+ glob_fp8 = 0; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8 = 0.1; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8 = is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ glob_fp8
+ = is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ is_an_int
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_float
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_double
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_short_int
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_uint8
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_an_int8
+ = glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Casting. */
+
+ (void)glob_fp8;
+ (mfloat8_t) glob_fp8;
+
+ (int)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (float)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (double)
+ glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (short)glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (uint8_t)
+ glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ (int8_t)
+ glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ (mfloat8_t)
+ is_an_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t)
+ is_a_float; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t)
+ is_a_double; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t) is_a_short_int; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t)
+ is_a_uint8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t)
+ is_an_int8; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+
+ /* Compound literals. */
+
+ (mfloat8_t){};
+ (mfloat8_t){ glob_fp8 };
+ (mfloat8_t){ 0 }; /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ (mfloat8_t){
+ 0.1 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ (mfloat8_t){
+ is_a_float /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ (mfloat8_t){
+ is_an_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ (mfloat8_t){
+ is_a_double /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ (mfloat8_t){
+ is_a_short_int /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ (mfloat8_t){
+ is_a_uint8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+ (mfloat8_t){
+ is_an_int8 /* { dg-error {invalid conversion to type 'mfloat8_t'} } */
+ };
+
+ (int){
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ (float){
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ (double){
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ (short){
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ (uint8_t){
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+ (int8_t){
+ glob_fp8 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ };
+
+ /* Arrays and Structs. */
+
+ typedef mfloat8_t array_type[2];
+ extern mfloat8_t extern_array[];
+
+ mfloat8_t array[2];
+ mfloat8_t zero_length_array[0];
+ mfloat8_t empty_init_array[] = {};
+ typedef mfloat8_t some_other_type[is_an_int];
+
+ struct struct1
+ {
+ mfloat8_t a;
+ };
+
+ union union1
+ {
+ mfloat8_t a;
+ };
+
+ /* Addressing and dereferencing. */
+
+ mfloat8_t *fp8_ptr = &scalar0;
+ scalar0 = *fp8_ptr;
+
+ /* Pointer assignment. */
+
+ mfloat8_t *fp8_ptr2 = fp8_ptr;
+ mfloat8_t *fp8_ptr3 = array;
+
+ /* Pointer arithmetic. */
+
+ ++fp8_ptr;
+ --fp8_ptr;
+ fp8_ptr++;
+ fp8_ptr--;
+ fp8_ptr += 1;
+ fp8_ptr -= 1;
+ fp8_ptr - fp8_ptr2;
+ fp8_ptr = &fp8_ptr3[0];
+ fp8_ptr = &fp8_ptr3[1];
+
+ /* Simple comparison. */
+ scalar0
+ > glob_fp8; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ glob_fp8
+ == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 > is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_a_float
+ == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 > 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 > 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0.1 == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0
+ > is_an_int; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ is_an_int
+ == scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Pointer comparison. */
+
+ fp8_ptr == &scalar0;
+ fp8_ptr != &scalar0;
+ fp8_ptr < &scalar0;
+ fp8_ptr <= &scalar0;
+ fp8_ptr > &scalar0;
+ fp8_ptr >= &scalar0;
+ fp8_ptr == fp8_ptr2;
+ fp8_ptr != fp8_ptr2;
+ fp8_ptr < fp8_ptr2;
+ fp8_ptr <= fp8_ptr2;
+ fp8_ptr > fp8_ptr2;
+ fp8_ptr >= fp8_ptr2;
+
+ /* Conditional expressions. */
+
+ 0 ? scalar0 : scalar0;
+ 0 ? scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ : is_a_float;
+ 0 ? is_a_float
+ : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? scalar0 : 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? 0 : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? 0.1
+ : scalar0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ 0 ? scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ : 0.1;
+ 0 ? fp8_ptr : fp8_ptr2;
+ 0 ? fp8_ptr : uint8_ptr; /* { dg-error {conditional expression between distinct pointer types} } */
+ 0 ? uint8_ptr : fp8_ptr; /* { dg-error {conditional expression between distinct pointer types} } */
+
+ scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ ? scalar0
+ : scalar0;
+ scalar0 /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ ? is_a_float
+ : scalar0;
+ scalar0 ? scalar0 : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 ? is_a_float : is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ /* Unary operators. */
+
+ +scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ -scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ ~scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ !scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ *scalar0; /* { dg-error {invalid type argument of unary} } */
+ __real scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ __imag scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ ++scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ --scalar0; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ scalar0++; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+ scalar0--; /* { dg-error {operation not permitted on type 'mfloat8_t'} } */
+
+ /* Binary arithmetic operations. */
+
+ scalar0 = glob_fp8 + scalar1_2; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 = glob_fp8 + *fp8_ptr; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 = glob_fp8
+ + 0.1; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 = glob_fp8
+ + 0; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+ scalar0 = glob_fp8 + is_a_float; /* { dg-error {invalid conversion from type 'mfloat8_t'} } */
+
+ return scalar0;
+}