Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Built-in function naming scheme #19

Open
kito-cheng opened this issue Apr 1, 2021 · 8 comments
Open

Built-in function naming scheme #19

kito-cheng opened this issue Apr 1, 2021 · 8 comments

Comments

@kito-cheng
Copy link
Collaborator

kito-cheng commented Apr 1, 2021

Preface

This topic is compiler internal implementation specific, here is another issue (#18_ for discuss intrinsic naming scheme.

This issue intend to discus the built-in function naming scheme, generally those functions named with __builtin_<arch> prefix, there is an issue is: should we have consistent naming for built-in functions among different compiler, or just keep the intrinsic interface consistent is enough.

Scope

This scope are limited to the built-in functions are used for intrinsic function only, because non-intrinsic purposed built-in function like __builtin_thread_pointer has no good reason to have different name among different compiler

How Other Target Implement

Most target implement their target specific built-in function and then use that to implement their intrinsic functions, here is an example from ARM:

__extension__ extern __inline int16x8_t
__attribute__  ((__always_inline__, __gnu_inline__, __artificial__))
vsubw_s8 (int16x8_t __a, int8x8_t __b)
{
  return (int16x8_t)__builtin_neon_vsubwsv8qi (__a, __b);
}

vsubw_s8 is one of intrinsic function for NEON, but the compiler is implement that as __builtin_neon_vsubwsv8qi internally. the general motivation is prevent namespace pollution,
so we prevent compiler to declare the name of built-in function to same as name of intrinsic function directly.

However not everyone using this scheme, AArch64 SVE using pragma trick to declare the intrinsic function without introduce lots of __builtin_ functions, this would be useful if there is lots of intrinsic function, this could be significant reduce the compilation time, and I also intend to use this trick on GCC RVV implementation, since there is almost ~20k intrinsic function, that might increase the compilation time 30 sec ~ 60 sec.

https://github.com/gcc-mirror/gcc/blob/master/gcc/config/aarch64/arm_sve.h

#pragma GCC aarch64 "arm_sve.h"

The interested thing is SVE implementation on clang/LLVM didn't using same trick, it just using the most common way, create built-in function with prefix __builtin_ and then define intrinsic in the header file.

Discussion

  1. Should we have consistent naming for built-in functions among different compiler, or intrinsic interface consistent is enough?
  • Pros for consistent built-in functions:
    • Preprocessed source also portable among different compiler
  • Cons for consistent built-in functions:
    • Restrict the compiler implementation can't using other implementation trick like prama trick.
  1. What is the naming scheme if we want a consistent built-in function definitions?
  • __builtin_riscv_<asm_mnemonic>
  • __builtin_riscv_<asm_mnemonic>_<16|32|64|x>
  • ...

The pros and cons analysis might be very incomplete, feel free to share your thought here.
Any comment are appreciate!

@kito-cheng
Copy link
Collaborator Author

@topperc I am not family with x86 stuffs, could you share the status/implementation on x86 land?

@topperc
Copy link
Contributor

topperc commented Apr 2, 2021

Most X86 builtins start with __builtin_ia32_. Those are called in inline functions or macros in various header files. clang uses native constructs as much as possible in the x86 headers so has fewer builtins that gcc. Where both compilers have a builtin they usually have the same name. At least that was the case for older ISAs where clang was lagging gcc in implementation and it was easy to go check what name gcc was already using.

There have been concerns raised with compile time of x86 intrinsics due to the size of the headers. For the last 10 years or more all new ISAs are only available from a single header file x86intrin.h that keeps getting larger. There are no preprocessor directives to skip based on which ISAs are enabled. This is required due to __attribute__(__target__) allowing a user to enable an ISA on a per function basis.

@kito-cheng
Copy link
Collaborator Author

For those builtin function which used for intrinsic function, I am prefer DO NOT have too much restriction on that:

  • In theory user should only use intrinsic function name rather than internal builtin name.
  • Relax this could let compiler has more flexible to select best way to implement.

@luismarques
Copy link
Contributor

* In theory user should only use intrinsic function name rather than internal builtin name.
* Relax this could let compiler has more flexible to select best way to implement.

What would be good/canonical examples of the flexibility gained by this layer of indirection? Are there good historical examples we can reference?

@kito-cheng
Copy link
Collaborator Author

In my understanding that prevent target to corrupts the global name space without two underline prefix, using a indirection layer (intrinsic wrapper -> built-in function).

@jim-wilson do you know does here some historical reason for lots of target doing this?

@kito-cheng
Copy link
Collaborator Author

kito-cheng commented May 13, 2021

Ana also mention one thing about that intrinsic might not implement by built-in function, it might implement some compiler extension, ARM Neon is an good example for that, vadd are simply implement by operator+ of GNU vector extension:

https://github.com/gcc-mirror/gcc/blob/master/gcc/config/arm/arm_neon.h#L533

/* vadd  */
__extension__ extern __inline int8x8_t
__attribute__  ((__always_inline__, __gnu_inline__, __artificial__))
vadd_s8 (int8x8_t __a, int8x8_t __b)
{
  return __a + __b;
}

@jim-wilson
Copy link
Collaborator

ISO C says "All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.", so __builtin is clearly safe for the compiler to use. The builtin part of that just makes it clear that it is a compiler builtin and not some libgcc or other library routine. Builtin functions don't have declarations that can be used for type checking and nice error messages. So it is useful to define an inline function with types that then calls the builtin, put the inline function in a header file, and ask the user to call that instead of the builtin. If there is a problem, the user gets an error that points at the header file, which is generally easier to understand than something that just complains about an incorrect call to a builtin but doesn't give you a declaration to look at. If you have good enough error messages, maybe you don't need the inline function in the header file. If you do have the inline functions in a header file, they don't necessarily need to call a builtin if there are other ways to get the correct result.

@kito-cheng
Copy link
Collaborator Author

Both RISC-V LLVM and GNU folks are fine with no strict rule on those builtin functions, I gonna to write a PR for this recently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants