Skip to content
1 change: 1 addition & 0 deletions libc/cmake/modules/LLVMLibCArchitectures.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ if(LIBC_TARGET_ARCHITECTURE STREQUAL "arm")
set(LIBC_TARGET_ARCHITECTURE_IS_ARM TRUE)
elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "aarch64")
set(LIBC_TARGET_ARCHITECTURE_IS_AARCH64 TRUE)
set(LIBC_TARGET_LONG_DOUBLE_IS_FLOAT128 TRUE)
elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64")
set(LIBC_TARGET_ARCHITECTURE_IS_X86_64 TRUE)
elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "i386")
Expand Down
4 changes: 4 additions & 0 deletions libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ function(_get_common_compile_options output_var flags)
if(LLVM_LIBC_COMPILER_IS_GCC_COMPATIBLE)
list(APPEND compile_options "-fpie")

if(LIBC_TARGET_LONG_DOUBLE_IS_FLOAT128 OR LIBC_TARGET_LONG_DOUBLE_IS_FLOAT64)
list(APPEND compile_options "-DLIBC_ALIAS_LONG_DOUBLE")
endif()

if(LLVM_LIBC_FULL_BUILD)
# Only add -ffreestanding flag in non-GPU full build mode.
if(NOT LIBC_TARGET_OS_IS_GPU)
Expand Down
39 changes: 30 additions & 9 deletions libc/src/__support/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,42 @@

#define LLVM_LIBC_ATTR(name) EXPAND_THEN_SECOND(LLVM_LIBC_FUNCTION_ATTR_##name)

// MacOS needs to be excluded because it does not support aliasing.
#if defined(LIBC_COPT_PUBLIC_PACKAGING) && (!defined(__APPLE__))
#define LLVM_LIBC_FUNCTION_IMPL(type, name, arglist) \
// MacOS needs to be excluded because it does not support [[gnu::aliasing]].
#ifndef __APPLE__

#if defined(LIBC_COPT_PUBLIC_PACKAGING)
#define LLVM_LIBC_FUNCTION(type, name, arglist) \
LLVM_LIBC_ATTR(name) \
LLVM_LIBC_FUNCTION_ATTR decltype(LIBC_NAMESPACE::name) \
__##name##_impl__ __asm__(#name); \
__##name##_impl__ asm(#name); \
decltype(LIBC_NAMESPACE::name) name [[gnu::alias(#name)]]; \
type __##name##_impl__ arglist
#else
#define LLVM_LIBC_FUNCTION_IMPL(type, name, arglist) type name arglist
#endif

// This extra layer of macro allows `name` to be a macro to rename a function.
#define LLVM_LIBC_ALIAS(name, func) \
decltype(LIBC_NAMESPACE::name) LIBC_NAMESPACE::name [[gnu::alias(#func)]]; \
extern "C" decltype(LIBC_NAMESPACE::name) name [[gnu::alias(#func)]]; \
static_assert(true, "Require semicolon")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it a bit unusual for the top-level macros to require semicolon at the end, and it's not common in other libc implementations, see for example: https://sourceware.org/git/?p=glibc.git;a=blob;f=string/basename.c;h=dd2c8182dbc5df71b89100aab33ae98ff4e176ff;hb=HEAD#l28. So my preference would be to avoid requiring semicolons at the end of LLVM_LIBC_ALIAS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that forcing the semicolons at the end of these top level macros plays much nicer with clang-format, especially when they are inside #if blocks. I started to add those to newer top level test macros, and we don't have to occasionally fight with clang-format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion one way or another, but if we want to commit to this, we should add it to the docs and file an issue for fixing the other macros.

#else
#define LLVM_LIBC_FUNCTION(type, name, arglist) \
LLVM_LIBC_FUNCTION_IMPL(type, name, arglist)
LLVM_LIBC_ATTR(name) \
LLVM_LIBC_FUNCTION_ATTR decltype(LIBC_NAMESPACE::name) \
__##name##_impl__ asm("__" #name "_impl__"); \
decltype(LIBC_NAMESPACE::name) name [[gnu::alias("__" #name "_impl__")]]; \
type __##name##_impl__ arglist

#define LLVM_LIBC_ALIAS(name, func) \
decltype(LIBC_NAMESPACE::name) LIBC_NAMESPACE::name \
[[gnu::alias("__" #func "_impl__")]]; \
static_assert(true, "Require semicolon")
#endif // LIBC_COPT_PUBLIC_PACKAGING

#else

#define LLVM_LIBC_FUNCTION(type, name, arglist) type name arglist

#define LLVM_LIBC_ALIAS(name, func) static_assert(true, "Require semicolon")

#endif // !__APPLE__

namespace LIBC_NAMESPACE_DECL {
namespace internal {
Expand Down
9 changes: 9 additions & 0 deletions libc/src/__support/macros/properties/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,13 @@ using float16 = _Float16;
// LIBC_TYPES_HAS_FLOAT128 and 'float128' type are provided by
// "include/llvm-libc-types/float128.h"

// Alias long double functions if requested.
#ifdef LIBC_ALIAS_LONG_DOUBLE
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
#define LIBC_ALIAS_LONG_DOUBLE_TO_DOUBLE
#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
#define LIBC_ALIAS_LONG_DOUBLE_TO_FLOAT128
#endif
#endif // LIBC_ALIAS_LONG_DOUBLE
Comment on lines +62 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch seems fine, but this feels like it should be possible with templates (checking double == long double somehow)


#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_TYPES_H
30 changes: 25 additions & 5 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,30 @@ function(add_math_entrypoint_object name)
# We prefer machine specific implementation if available. Hence we check
# that first and return early if we are able to add an alias target for the
# machine specific implementation.
get_fq_target_name("${LIBC_TARGET_ARCHITECTURE}.${name}" fq_machine_specific_target_name)
if(NOT ARGN)
set(alias_entrypoint ${name})
else()
set(alias_entrypoint ${ARGN})
endif()

get_fq_target_name("${LIBC_TARGET_ARCHITECTURE}.${alias_entrypoint}" fq_machine_specific_target_name)
if(TARGET ${fq_machine_specific_target_name})
add_entrypoint_object(
${name}
ALIAS
DEPENDS
.${LIBC_TARGET_ARCHITECTURE}.${name}
.${LIBC_TARGET_ARCHITECTURE}.${alias_entrypoint}
)
return()
endif()

get_fq_target_name("generic.${name}" fq_generic_target_name)
get_fq_target_name("generic.${alias_entrypoint}" fq_generic_target_name)
if(TARGET ${fq_generic_target_name})
add_entrypoint_object(
${name}
ALIAS
DEPENDS
.generic.${name}
.generic.${alias_entrypoint}
)
return()
endif()
Expand All @@ -40,6 +46,20 @@ function(add_math_entrypoint_object name)
)
endfunction()

function(add_long_double_math_entrypoint_object name)
get_fq_target_name(${name} fq_double_math_target)
get_fq_target_name("${name}l" fq_long_double_math_target)
get_fq_target_name("${name}f128" fq_float128_math_target)

if(LIBC_TARGET_LONG_DOUBLE_IS_DOUBLE)
add_math_entrypoint_object("${name}l" "${name}")
elseif(LIBC_TARGET_LONG_DOUBLE_IS_FLOAT128)
add_math_entrypoint_object("${name}l" "${name}f128")
else()
add_math_entrypoint_object("${name}l")
endif()
endfunction()

add_math_entrypoint_object(acos)
add_math_entrypoint_object(acosf)
add_math_entrypoint_object(acosf16)
Expand Down Expand Up @@ -88,9 +108,9 @@ add_math_entrypoint_object(ceilf128)

add_math_entrypoint_object(copysign)
add_math_entrypoint_object(copysignf)
add_math_entrypoint_object(copysignl)
add_math_entrypoint_object(copysignf16)
add_math_entrypoint_object(copysignf128)
add_long_double_math_entrypoint_object(copysign)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this even necessary? We should just be able to enable this entrypoint like anything else and then just let the preprocessor figure out whether or not it should be an alias.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it wouldn't be a problem for the libc target but it will be a problem for tests. Because our tests for copysignl only include libc.src.math.copysignl target, and we cannot have copysignl in one target aliasing to copysign in libc.src.math.copysign target.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But they're still independent targets, right? It's just that one is a single alias rather than an implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My plan in the future is to add all the variations: copysign, copysigndd, copysignf80, copysignf128, and just alias copysignl to one of them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main difficulty is that the alias attribute wants the function to be defined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to answer to your question about the cmake changes: we could leave the cmake inplace, and make copysignl.cpp generate nothing when copysignl function is aliased to copysign or copysignf128, but then we will need to change the testing dependency for copysignl_test, because copysignl symbol is not defined in libc.src.math.copysignl -> libc.src.math.generic.copysignl anymore, due to copysignl symbol now has to be in the same translation unit with copysign or copysignf128. So it is actually easier to let libc.src.copysignl point to libc.src.math.generic.copysign or libc.src.math.generic.copysignf128 instead.


add_math_entrypoint_object(cos)
add_math_entrypoint_object(cosf)
Expand Down
8 changes: 8 additions & 0 deletions libc/src/math/generic/copysign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "src/__support/FPUtil/ManipulationFunctions.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

Expand All @@ -22,3 +23,10 @@ LLVM_LIBC_FUNCTION(double, copysign, (double x, double y)) {
}

} // namespace LIBC_NAMESPACE_DECL

#if defined(LIBC_ALIAS_LONG_DOUBLE_TO_DOUBLE)
#include "src/math/copysignl.h"

LLVM_LIBC_ALIAS(copysignl, copysign);

#endif // LIBC_ALIAS_LONG_DOUBLE_TO_DOUBLE
7 changes: 7 additions & 0 deletions libc/src/math/generic/copysignf128.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ LLVM_LIBC_FUNCTION(float128, copysignf128, (float128 x, float128 y)) {
}

} // namespace LIBC_NAMESPACE_DECL

#if defined(LIBC_ALIAS_LONG_DOUBLE_TO_FLOAT128)
#include "src/math/copysignl.h"

LLVM_LIBC_ALIAS(copysignl, copysignf128);

#endif // LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128
7 changes: 7 additions & 0 deletions libc/src/math/generic/copysignl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@

namespace LIBC_NAMESPACE_DECL {

// TODO: Change this implementation to copysignf80 and add long double alias
// similar to copysign and copysignf128.
#if !defined(LIBC_ALIAS_LONG_DOUBLE_TO_DOUBLE) && \
!defined(LIBC_ALIAS_LONG_DOUBLE_TO_FLOAT128)

LLVM_LIBC_FUNCTION(long double, copysignl, (long double x, long double y)) {
return fputil::copysign(x, y);
}

#endif

} // namespace LIBC_NAMESPACE_DECL
Loading