Commit d54cf3a4 by Cherry Zhang Committed by Ian Lance Taylor

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
parent f4e7200b
2019-06-21 Cherry Zhang <cherryyz@google.com>
* go-gcc.cc (Gcc_backend::Gcc_backend): Define math/bits
builtins.
2019-05-30 Jim Wilson <jimw@sifive.com> 2019-05-30 Jim Wilson <jimw@sifive.com>
* go-gcc.cc (Gcc_backend::Gcc_backend): Add BUILT_IN_ATOMIC_FETCH_AND_1 * go-gcc.cc (Gcc_backend::Gcc_backend): Add BUILT_IN_ATOMIC_FETCH_AND_1
......
...@@ -613,7 +613,7 @@ Gcc_backend::Gcc_backend() ...@@ -613,7 +613,7 @@ Gcc_backend::Gcc_backend()
NULL_TREE), NULL_TREE),
false, false); false, false);
// Used by runtime/internal/sys. // Used by runtime/internal/sys and math/bits.
this->define_builtin(BUILT_IN_CTZ, "__builtin_ctz", "ctz", this->define_builtin(BUILT_IN_CTZ, "__builtin_ctz", "ctz",
build_function_type_list(integer_type_node, build_function_type_list(integer_type_node,
unsigned_type_node, unsigned_type_node,
...@@ -624,6 +624,31 @@ Gcc_backend::Gcc_backend() ...@@ -624,6 +624,31 @@ Gcc_backend::Gcc_backend()
long_long_unsigned_type_node, long_long_unsigned_type_node,
NULL_TREE), NULL_TREE),
true, false); 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", this->define_builtin(BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32",
build_function_type_list(uint32_type_node, build_function_type_list(uint32_type_node,
uint32_type_node, uint32_type_node,
......
fdf0af774aabb31ba8a62f358b7b40dfe8b35da9 9b5a43baaf391005989d140109261e5a8e1b1b63
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -11063,6 +11063,25 @@ Call_expression::intrinsify(Gogo* gogo, ...@@ -11063,6 +11063,25 @@ Call_expression::intrinsify(Gogo* gogo,
package = "runtime/internal/atomic"; 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") if (package == "runtime")
{ {
// Handle a couple of special runtime functions. In the runtime // Handle a couple of special runtime functions. In the runtime
...@@ -11093,21 +11112,44 @@ Call_expression::intrinsify(Gogo* gogo, ...@@ -11093,21 +11112,44 @@ Call_expression::intrinsify(Gogo* gogo,
return Expression::make_cast(uintptr_type, call, loc); 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) && 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(); 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) && 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(); 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) && this->args_ != NULL && this->args_->size() == 1)
{ {
Expression* arg = this->args_->front(); Expression* arg = this->args_->front();
...@@ -11125,7 +11167,8 @@ Call_expression::intrinsify(Gogo* gogo, ...@@ -11125,7 +11167,8 @@ Call_expression::intrinsify(Gogo* gogo,
call = Expression::make_cast(int_type, call, loc); call = Expression::make_cast(int_type, call, loc);
return Expression::make_conditional(cmp, c32, 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) && this->args_ != NULL && this->args_->size() == 1)
{ {
Expression* arg = this->args_->front(); Expression* arg = this->args_->front();
...@@ -11143,6 +11186,99 @@ Call_expression::intrinsify(Gogo* gogo, ...@@ -11143,6 +11186,99 @@ Call_expression::intrinsify(Gogo* gogo,
call = Expression::make_cast(int_type, call, loc); call = Expression::make_cast(int_type, call, loc);
return Expression::make_conditional(cmp, c64, 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") else if (package == "runtime/internal/atomic")
{ {
......
...@@ -32,6 +32,8 @@ enum Runtime_function_type ...@@ -32,6 +32,8 @@ enum Runtime_function_type
RFT_INT, RFT_INT,
// Go type uint8, C type uint8_t. // Go type uint8, C type uint8_t.
RFT_UINT8, RFT_UINT8,
// Go type uint16, C type uint16_t.
RFT_UINT16,
// Go type int32, C type int32_t. // Go type int32, C type int32_t.
RFT_INT32, RFT_INT32,
// Go type uint32, C type uint32_t. // Go type uint32, C type uint32_t.
...@@ -115,6 +117,10 @@ runtime_function_type(Runtime_function_type bft) ...@@ -115,6 +117,10 @@ runtime_function_type(Runtime_function_type bft)
t = Type::lookup_integer_type("uint8"); t = Type::lookup_integer_type("uint8");
break; break;
case RFT_UINT16:
t = Type::lookup_integer_type("uint16");
break;
case RFT_INT32: case RFT_INT32:
t = Type::lookup_integer_type("int32"); t = Type::lookup_integer_type("int32");
break; break;
...@@ -257,6 +263,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, ...@@ -257,6 +263,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
case RFT_BOOLPTR: case RFT_BOOLPTR:
case RFT_INT: case RFT_INT:
case RFT_UINT8: case RFT_UINT8:
case RFT_UINT16:
case RFT_INT32: case RFT_INT32:
case RFT_UINT32: case RFT_UINT32:
case RFT_INT64: case RFT_INT64:
......
...@@ -428,6 +428,8 @@ DEF_GO_RUNTIME(BUILTIN_DWARF_CFA, "__builtin_dwarf_cfa", P0(), ...@@ -428,6 +428,8 @@ DEF_GO_RUNTIME(BUILTIN_DWARF_CFA, "__builtin_dwarf_cfa", P0(),
R1(POINTER)) R1(POINTER))
// Swap bytes. // Swap bytes.
DEF_GO_RUNTIME(BUILTIN_BSWAP16, "__builtin_bswap16", P1(UINT16),
R1(UINT16))
DEF_GO_RUNTIME(BUILTIN_BSWAP32, "__builtin_bswap32", P1(UINT32), DEF_GO_RUNTIME(BUILTIN_BSWAP32, "__builtin_bswap32", P1(UINT32),
R1(UINT32)) R1(UINT32))
DEF_GO_RUNTIME(BUILTIN_BSWAP64, "__builtin_bswap64", P1(UINT64), DEF_GO_RUNTIME(BUILTIN_BSWAP64, "__builtin_bswap64", P1(UINT64),
...@@ -437,6 +439,14 @@ 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_CTZ, "__builtin_ctz", P1(UINT32), R1(INT32))
DEF_GO_RUNTIME(BUILTIN_CTZLL, "__builtin_ctzll", P1(UINT64), 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. // Atomics.
DEF_GO_RUNTIME(ATOMIC_LOAD_4, "__atomic_load_4", P2(POINTER, INT32), DEF_GO_RUNTIME(ATOMIC_LOAD_4, "__atomic_load_4", P2(POINTER, INT32),
R1(UINT32)) R1(UINT32))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment