// REQUIRES: aarch64-registered-target // RUN: %clang_cc1 -triple aarch64 -target-feature +fp-armv8 -S -o /dev/null -target-abi aapcs -verify=fp-hard %s // RUN: %clang_cc1 -triple aarch64 -target-feature -fp-armv8 -S -o /dev/null -target-abi aapcs-soft -verify=nofp-soft %s // RUN: %clang_cc1 -triple aarch64 -target-feature -fp-armv8 -S -o /dev/null -target-abi aapcs -verify=nofp-hard %s // RUN: %clang_cc1 -triple aarch64 -target-feature -fp-armv8 -o /dev/null -target-abi aapcs -O1 -verify=nofp-hard,nofp-hard-opt -emit-llvm %s // No run line needed for soft-float ABI with an FPU because that is rejected by the driver // With the hard-float ABI and a target with an FPU, FP arguments are passed in // FP registers, no diagnostics needed. // fp-hard-no-diagnostics // With the soft-float ABI, FP arguments are passed in integer registers, no // diagnostics needed. // nofp-soft-no-diagnostics // With the hard-float ABI but no FPU, FP arguments cannot be passed in an // ABI-compatible way, so we report errors for these cases: struct HFA { float x, y; }; struct non_HFA { float x; int y; }; // Floating-point arguments are returns are rejected void test_fp16_arg(__fp16 a) {} // nofp-hard-error@-1 {{'a' requires '__fp16' type support, but ABI 'aapcs' does not support it}} __fp16 test_fp16_ret(void) { return 3.141; } // nofp-hard-error@-1 {{'test_fp16_ret' requires '__fp16' type support, but ABI 'aapcs' does not support it}} void test_float_arg(float a) {} // nofp-hard-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}} float test_float_ret(void) { return 3.141f; } // nofp-hard-error@-1 {{'test_float_ret' requires 'float' type support, but ABI 'aapcs' does not support it}} void test_double_arg(double a) {} // nofp-hard-error@-1 {{'a' requires 'double' type support, but ABI 'aapcs' does not support it}} double test_double_ret(void) { return 3.141; } // nofp-hard-error@-1 {{'test_double_ret' requires 'double' type support, but ABI 'aapcs' does not support it}} void test_long_double_arg(long double a) {} // nofp-hard-error@-1 {{'a' requires 'long double' type support, but ABI 'aapcs' does not support it}} long double test_long_double_ret(void) { return 3.141L; } // nofp-hard-error@-1 {{'test_long_double_ret' requires 'long double' type support, but ABI 'aapcs' does not support it}} // HFAs would be passed in floating-point registers, so are rejected. void test_hfa_arg(struct HFA a) {} // nofp-hard-error@-1 {{'a' requires 'struct HFA' type support, but ABI 'aapcs' does not support it}} struct HFA test_hfa_ret(void) { return (struct HFA){}; } // nofp-hard-error@-1 {{'test_hfa_ret' requires 'struct HFA' type support, but ABI 'aapcs' does not support it}} // Note: vector types cannot be created at all for targets without an FPU, so // it is not possible to create a function which passes/returns them when using // either the default or soft-float ABI. This is tested elsewhere. // This struct contains a floating-point type, but is not an HFA, so can be // passed/returned without affecting the ABI. struct non_HFA test_non_hfa_ret(void) { return (struct non_HFA){}; } void test_non_hfa_arg(struct non_HFA a) {} // This inline function does not get code-generated because there is no use of // it in this file, so we we don't emit an error for it, matching GCC's // behaviour. inline void test_float_arg_inline(float a) {} // This inline function is used, so we emit the error if we generate code for // it. The code isn't generated at -O0, so no error is emitted there. inline void test_float_arg_inline_used(float a) {} // nofp-hard-opt-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}} void use_inline() { test_float_arg_inline_used(1.0f); } // nofp-hard-error@-1 {{'test_float_arg_inline_used' requires 'float' type support, but ABI 'aapcs' does not support it}} // The always_inline attribute causes an inline function to always be // code-genned, even at -O0, so we always emit the error. __attribute((always_inline)) inline void test_float_arg_always_inline_used(float a) {} // nofp-hard-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}} void use_always_inline() { test_float_arg_always_inline_used(1.0f); } // nofp-hard-error@-1 {{'test_float_arg_always_inline_used' requires 'float' type support, but ABI 'aapcs' does not support it}} // Floating-point expressions, global variables and local variables do not // affect the ABI, so are allowed. GCC does reject some uses of floating point // types like this, but it does so after optimisation, which we can't // accurately match in clang. int test_expr_float(int a) { return a + 1.0f; } int test_expr_double(int a) { return a + 1.0; } float global_float = 2.0f * 3.5f; float global_double = 2.0 * 3.5; int test_var_float(int a) { float f = a; f *= 6.0; return (int)f; } int test_var_double(int a) { double d = a; d *= 6.0; return (int)d; } extern void extern_float_arg(float); extern float extern_float_ret(void); void call_extern_float_arg() { extern_float_arg(1.0f); } // nofp-hard-error@-1 {{'extern_float_arg' requires 'float' type support, but ABI 'aapcs' does not support it}} void call_extern_float_ret() { extern_float_ret(); } // nofp-hard-error@-1 {{'extern_float_ret' requires 'float' type support, but ABI 'aapcs' does not support it}} // Definitions of variadic functions, and calls to them which only use integer // argument registers, are both fine. void variadic(int, ...); void call_variadic_int() { variadic(0, 1); } // Calls to variadic functions with floating-point arguments are an error, // since this would require floating-point registers. void call_variadic_double() { variadic(0, 1.0); } // nofp-hard-error@-1 {{'variadic' requires 'double' type support, but ABI 'aapcs' does not support it}} // Calls through function pointers are also diagnosed. void (*fptr)(float); void call_indirect() { fptr(1.0f); } // nofp-hard-error@-1 {{'call_indirect' requires 'float' type support, but ABI 'aapcs' does not support it}}