From: Cherry Zhang Date: Fri, 21 Jun 2019 22:42:18 +0000 (+0000) Subject: compiler: intrinsify some math/bits functions X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d54cf3a449fad57fcad8d2eafb8bb3d49585ea65;p=gcc.git compiler: intrinsify some math/bits functions Let the Go frontend recognize some math/bits functions and turn them into intrinsics. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/183266 * go-gcc.cc (Gcc_backend::Gcc_backend): Define math/bits builtins. From-SVN: r272579 --- diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index 6e8b3a297a0..9842c85925e 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,8 @@ +2019-06-21 Cherry Zhang + + * go-gcc.cc (Gcc_backend::Gcc_backend): Define math/bits + builtins. + 2019-05-30 Jim Wilson * go-gcc.cc (Gcc_backend::Gcc_backend): Add BUILT_IN_ATOMIC_FETCH_AND_1 diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index 91043b51463..879149eb22f 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -613,7 +613,7 @@ Gcc_backend::Gcc_backend() NULL_TREE), false, false); - // Used by runtime/internal/sys. + // Used by runtime/internal/sys and math/bits. this->define_builtin(BUILT_IN_CTZ, "__builtin_ctz", "ctz", build_function_type_list(integer_type_node, unsigned_type_node, @@ -624,6 +624,31 @@ Gcc_backend::Gcc_backend() long_long_unsigned_type_node, NULL_TREE), true, false); + this->define_builtin(BUILT_IN_CLZ, "__builtin_clz", "clz", + build_function_type_list(integer_type_node, + unsigned_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_CLZLL, "__builtin_clzll", "clzll", + build_function_type_list(integer_type_node, + long_long_unsigned_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_POPCOUNT, "__builtin_popcount", "popcount", + build_function_type_list(integer_type_node, + unsigned_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_POPCOUNTLL, "__builtin_popcountll", "popcountll", + build_function_type_list(integer_type_node, + long_long_unsigned_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_BSWAP16, "__builtin_bswap16", "bswap16", + build_function_type_list(uint16_type_node, + uint16_type_node, + NULL_TREE), + true, false); this->define_builtin(BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32", build_function_type_list(uint32_type_node, uint32_type_node, diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 13e366a78c1..d51917defb5 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -fdf0af774aabb31ba8a62f358b7b40dfe8b35da9 +9b5a43baaf391005989d140109261e5a8e1b1b63 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index b9cf0f3b1e0..04aacf88831 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -11063,6 +11063,25 @@ Call_expression::intrinsify(Gogo* gogo, package = "runtime/internal/atomic"; } + if (package == "runtime/internal/sys") + { + // runtime/internal/sys functions and math/bits functions + // are very similar. In order not to duplicate code, we just + // redirect to the latter and let the code below to handle them. + if (name == "Bswap32") + name = "ReverseBytes32"; + else if (name == "Bswap64") + name = "ReverseBytes64"; + else if (name == "Ctz32") + name = "TrailingZeros32"; + else if (name == "Ctz64") + name = "TrailingZeros64"; + else + return NULL; + + package = "math/bits"; + } + if (package == "runtime") { // Handle a couple of special runtime functions. In the runtime @@ -11093,21 +11112,44 @@ Call_expression::intrinsify(Gogo* gogo, return Expression::make_cast(uintptr_type, call, loc); } } - else if (package == "runtime/internal/sys") + else if (package == "math/bits") { - if (name == "Bswap32" + if ((name == "ReverseBytes16" || name == "ReverseBytes32" + || name == "ReverseBytes64" || name == "ReverseBytes") && this->args_ != NULL && this->args_->size() == 1) { + Runtime::Function code; + if (name == "ReverseBytes16") + code = Runtime::BUILTIN_BSWAP16; + else if (name == "ReverseBytes32") + code = Runtime::BUILTIN_BSWAP32; + else if (name == "ReverseBytes64") + code = Runtime::BUILTIN_BSWAP64; + else if (name == "ReverseBytes") + code = (int_size == 8 ? Runtime::BUILTIN_BSWAP64 : Runtime::BUILTIN_BSWAP32); + else + go_unreachable(); Expression* arg = this->args_->front(); - return Runtime::make_call(Runtime::BUILTIN_BSWAP32, loc, 1, arg); + Expression* call = Runtime::make_call(code, loc, 1, arg); + if (name == "ReverseBytes") + return Expression::make_cast(uint_type, call, loc); + return call; } - else if (name == "Bswap64" + else if ((name == "TrailingZeros8" || name == "TrailingZeros16") && this->args_ != NULL && this->args_->size() == 1) { + // GCC does not have a ctz8 or ctz16 intrinsic. We do + // ctz32(0x100 | arg) or ctz32(0x10000 | arg). Expression* arg = this->args_->front(); - return Runtime::make_call(Runtime::BUILTIN_BSWAP64, loc, 1, arg); + arg = Expression::make_cast(uint32_type, arg, loc); + unsigned long mask = (name == "TrailingZeros8" ? 0x100 : 0x10000); + Expression* c = Expression::make_integer_ul(mask, uint32_type, loc); + arg = Expression::make_binary(OPERATOR_OR, arg, c, loc); + Expression* call = Runtime::make_call(Runtime::BUILTIN_CTZ, loc, 1, arg); + return Expression::make_cast(int_type, call, loc); } - else if (name == "Ctz32" + else if ((name == "TrailingZeros32" + || (name == "TrailingZeros" && int_size == 4)) && this->args_ != NULL && this->args_->size() == 1) { Expression* arg = this->args_->front(); @@ -11125,7 +11167,8 @@ Call_expression::intrinsify(Gogo* gogo, call = Expression::make_cast(int_type, call, loc); return Expression::make_conditional(cmp, c32, call, loc); } - else if (name == "Ctz64" + else if ((name == "TrailingZeros64" + || (name == "TrailingZeros" && int_size == 8)) && this->args_ != NULL && this->args_->size() == 1) { Expression* arg = this->args_->front(); @@ -11143,6 +11186,99 @@ Call_expression::intrinsify(Gogo* gogo, call = Expression::make_cast(int_type, call, loc); return Expression::make_conditional(cmp, c64, call, loc); } + else if ((name == "LeadingZeros8" || name == "LeadingZeros16" + || name == "Len8" || name == "Len16") + && this->args_ != NULL && this->args_->size() == 1) + { + // GCC does not have a clz8 ir clz16 intrinsic. We do + // clz32(arg<<24 | 0xffffff) or clz32(arg<<16 | 0xffff). + Expression* arg = this->args_->front(); + arg = Expression::make_cast(uint32_type, arg, loc); + unsigned long shift = + ((name == "LeadingZeros8" || name == "Len8") ? 24 : 16); + Expression* c = Expression::make_integer_ul(shift, uint32_type, loc); + arg = Expression::make_binary(OPERATOR_LSHIFT, arg, c, loc); + unsigned long mask = + ((name == "LeadingZeros8" || name == "Len8") ? 0xffffff : 0xffff); + c = Expression::make_integer_ul(mask, uint32_type, loc); + arg = Expression::make_binary(OPERATOR_OR, arg, c, loc); + Expression* call = Runtime::make_call(Runtime::BUILTIN_CLZ, loc, 1, arg); + call = Expression::make_cast(int_type, call, loc); + // len = width - clz + if (name == "Len8") + { + c = Expression::make_integer_ul(8, int_type, loc); + return Expression::make_binary(OPERATOR_MINUS, c, call, loc); + } + else if (name == "Len16") + { + c = Expression::make_integer_ul(16, int_type, loc); + return Expression::make_binary(OPERATOR_MINUS, c, call, loc); + } + return call; + } + else if ((name == "LeadingZeros32" || name == "Len32" + || ((name == "LeadingZeros" || name == "Len") && int_size == 4)) + && this->args_ != NULL && this->args_->size() == 1) + { + Expression* arg = this->args_->front(); + if (!arg->is_variable()) + { + Temporary_statement* ts = Statement::make_temporary(uint32_type, arg, loc); + inserter->insert(ts); + arg = Expression::make_temporary_reference(ts, loc); + } + // arg == 0 ? 32 : __builtin_clz(arg) + Expression* zero = Expression::make_integer_ul(0, uint32_type, loc); + Expression* cmp = Expression::make_binary(OPERATOR_EQEQ, arg, zero, loc); + Expression* c32 = Expression::make_integer_ul(32, int_type, loc); + Expression* call = Runtime::make_call(Runtime::BUILTIN_CLZ, loc, 1, arg->copy()); + call = Expression::make_cast(int_type, call, loc); + Expression* cond = Expression::make_conditional(cmp, c32, call, loc); + // len = 32 - clz + if (name == "Len32" || name == "Len") + return Expression::make_binary(OPERATOR_MINUS, c32->copy(), cond, loc); + return cond; + } + else if ((name == "LeadingZeros64" || name == "Len64" + || ((name == "LeadingZeros" || name == "Len") && int_size == 8)) + && this->args_ != NULL && this->args_->size() == 1) + { + Expression* arg = this->args_->front(); + if (!arg->is_variable()) + { + Temporary_statement* ts = Statement::make_temporary(uint64_type, arg, loc); + inserter->insert(ts); + arg = Expression::make_temporary_reference(ts, loc); + } + // arg == 0 ? 64 : __builtin_clzll(arg) + Expression* zero = Expression::make_integer_ul(0, uint64_type, loc); + Expression* cmp = Expression::make_binary(OPERATOR_EQEQ, arg, zero, loc); + Expression* c64 = Expression::make_integer_ul(64, int_type, loc); + Expression* call = Runtime::make_call(Runtime::BUILTIN_CLZLL, loc, 1, arg->copy()); + call = Expression::make_cast(int_type, call, loc); + Expression* cond = Expression::make_conditional(cmp, c64, call, loc); + // len = 64 - clz + if (name == "Len64" || name == "Len") + return Expression::make_binary(OPERATOR_MINUS, c64->copy(), cond, loc); + return cond; + } + else if ((name == "OnesCount8" || name == "OnesCount16" + || name == "OnesCount32" || name == "OnesCount64" + || name == "OnesCount") + && this->args_ != NULL && this->args_->size() == 1) + { + Runtime::Function code; + if (name == "OnesCount64") + code = Runtime::BUILTIN_POPCOUNTLL; + else if (name == "OnesCount") + code = (int_size == 8 ? Runtime::BUILTIN_POPCOUNTLL : Runtime::BUILTIN_POPCOUNT); + else + code = Runtime::BUILTIN_POPCOUNT; + Expression* arg = this->args_->front(); + Expression* call = Runtime::make_call(code, loc, 1, arg); + return Expression::make_cast(int_type, call, loc); + } } else if (package == "runtime/internal/atomic") { diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc index 28aca44706e..e35658b8c5f 100644 --- a/gcc/go/gofrontend/runtime.cc +++ b/gcc/go/gofrontend/runtime.cc @@ -32,6 +32,8 @@ enum Runtime_function_type RFT_INT, // Go type uint8, C type uint8_t. RFT_UINT8, + // Go type uint16, C type uint16_t. + RFT_UINT16, // Go type int32, C type int32_t. RFT_INT32, // Go type uint32, C type uint32_t. @@ -115,6 +117,10 @@ runtime_function_type(Runtime_function_type bft) t = Type::lookup_integer_type("uint8"); break; + case RFT_UINT16: + t = Type::lookup_integer_type("uint16"); + break; + case RFT_INT32: t = Type::lookup_integer_type("int32"); break; @@ -257,6 +263,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, case RFT_BOOLPTR: case RFT_INT: case RFT_UINT8: + case RFT_UINT16: case RFT_INT32: case RFT_UINT32: case RFT_INT64: diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index a966cd422ec..27d5e472f6f 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -428,6 +428,8 @@ DEF_GO_RUNTIME(BUILTIN_DWARF_CFA, "__builtin_dwarf_cfa", P0(), R1(POINTER)) // Swap bytes. +DEF_GO_RUNTIME(BUILTIN_BSWAP16, "__builtin_bswap16", P1(UINT16), + R1(UINT16)) DEF_GO_RUNTIME(BUILTIN_BSWAP32, "__builtin_bswap32", P1(UINT32), R1(UINT32)) DEF_GO_RUNTIME(BUILTIN_BSWAP64, "__builtin_bswap64", P1(UINT64), @@ -437,6 +439,14 @@ DEF_GO_RUNTIME(BUILTIN_BSWAP64, "__builtin_bswap64", P1(UINT64), DEF_GO_RUNTIME(BUILTIN_CTZ, "__builtin_ctz", P1(UINT32), R1(INT32)) DEF_GO_RUNTIME(BUILTIN_CTZLL, "__builtin_ctzll", P1(UINT64), R1(INT32)) +// Count leading zeros. +DEF_GO_RUNTIME(BUILTIN_CLZ, "__builtin_clz", P1(UINT32), R1(INT32)) +DEF_GO_RUNTIME(BUILTIN_CLZLL, "__builtin_clzll", P1(UINT64), R1(INT32)) + +// Count one bits. +DEF_GO_RUNTIME(BUILTIN_POPCOUNT, "__builtin_popcount", P1(UINT32), R1(INT32)) +DEF_GO_RUNTIME(BUILTIN_POPCOUNTLL, "__builtin_popcountll", P1(UINT64), R1(INT32)) + // Atomics. DEF_GO_RUNTIME(ATOMIC_LOAD_4, "__atomic_load_4", P2(POINTER, INT32), R1(UINT32))