From 395389bf9492a69db79747943d32abd0b7ac36c3 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 17 May 2019 00:21:22 +0000 Subject: [PATCH] compiler: intrinsify runtime/internal/atomic functions Currently runtime/internal/atomic functions are implemented in C using C compiler intrinsics. This CL lets the Go frontend recognize these functions and turn them into intrinsics directly. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/176918 * go-gcc.cc (Gcc_backend::Gcc_backend): Define atomic builtins. From-SVN: r271308 --- gcc/go/ChangeLog | 4 + gcc/go/go-gcc.cc | 103 +++++++++++++ gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 243 +++++++++++++++++++++++++++++++ gcc/go/gofrontend/runtime.cc | 7 + gcc/go/gofrontend/runtime.def | 32 ++++ 6 files changed, 390 insertions(+), 1 deletion(-) diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index 10c2b6959f6..255d3b5df6b 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,7 @@ +2019-05-16 Cherry Zhang + + * go-gcc.cc (Gcc_backend::Gcc_backend): Define atomic builtins. + 2019-05-08 Cherry Zhang * go-gcc.cc (Gcc_backend::Gcc_backend): Define memmove builtin. diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index e0e4333602c..1b26f2bac93 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -776,6 +776,109 @@ Gcc_backend::Gcc_backend() this->define_builtin(BUILT_IN_UNREACHABLE, "__builtin_unreachable", NULL, build_function_type(void_type_node, void_list_node), true, true); + + // We provide some atomic functions. + t = build_function_type_list(uint32_type_node, + ptr_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_LOAD_4, "__atomic_load_4", NULL, + t, false, false); + + t = build_function_type_list(uint64_type_node, + ptr_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_LOAD_8, "__atomic_load_8", NULL, + t, false, false); + + t = build_function_type_list(void_type_node, + ptr_type_node, + uint32_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_STORE_4, "__atomic_store_4", NULL, + t, false, false); + + t = build_function_type_list(void_type_node, + ptr_type_node, + uint64_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_STORE_8, "__atomic_store_8", NULL, + t, false, false); + + t = build_function_type_list(uint32_type_node, + ptr_type_node, + uint32_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_EXCHANGE_4, "__atomic_exchange_4", NULL, + t, false, false); + + t = build_function_type_list(uint64_type_node, + ptr_type_node, + uint64_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_EXCHANGE_8, "__atomic_exchange_8", NULL, + t, false, false); + + t = build_function_type_list(boolean_type_node, + ptr_type_node, + ptr_type_node, + uint32_type_node, + boolean_type_node, + integer_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4, + "__atomic_compare_exchange_4", NULL, + t, false, false); + + t = build_function_type_list(boolean_type_node, + ptr_type_node, + ptr_type_node, + uint64_type_node, + boolean_type_node, + integer_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8, + "__atomic_compare_exchange_8", NULL, + t, false, false); + + t = build_function_type_list(uint32_type_node, + ptr_type_node, + uint32_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_ADD_FETCH_4, "__atomic_add_fetch_4", NULL, + t, false, false); + + t = build_function_type_list(uint64_type_node, + ptr_type_node, + uint64_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_ADD_FETCH_8, "__atomic_add_fetch_8", NULL, + t, false, false); + + t = build_function_type_list(unsigned_char_type_node, + ptr_type_node, + unsigned_char_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_AND_FETCH_1, "__atomic_and_fetch_1", NULL, + t, false, false); + + t = build_function_type_list(unsigned_char_type_node, + ptr_type_node, + unsigned_char_type_node, + integer_type_node, + NULL_TREE); + this->define_builtin(BUILT_IN_ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1", NULL, + t, false, false); } // Get an unnamed integer type. diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index c3da5bfce87..578bef74be3 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -c0c8ad50627e3a59267e6e3de233a0b30cf64150 +f8a3668cbcfa3f8cd6c26c62bce416714cd401fc 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 d162b056211..dd559b4c0dd 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -10460,9 +10460,16 @@ Call_expression::intrinsify(Gogo* gogo, Location loc = this->location(); Type* int_type = Type::lookup_integer_type("int"); + Type* int32_type = Type::lookup_integer_type("int32"); + Type* int64_type = Type::lookup_integer_type("int64"); + Type* uint_type = Type::lookup_integer_type("uint"); Type* uint32_type = Type::lookup_integer_type("uint32"); Type* uint64_type = Type::lookup_integer_type("uint64"); Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* pointer_type = Type::make_pointer_type(Type::make_void_type()); + + int int_size = int_type->named_type()->real_type()->integer_type()->bits() / 8; + int ptr_size = uintptr_type->named_type()->real_type()->integer_type()->bits() / 8; if (package == "runtime") { @@ -10545,6 +10552,242 @@ Call_expression::intrinsify(Gogo* gogo, return Expression::make_conditional(cmp, c64, call, loc); } } + else if (package == "runtime/internal/atomic") + { + int memorder = __ATOMIC_SEQ_CST; + + if ((name == "Load" || name == "Load64" || name == "Loadint64" || name == "Loadp" + || name == "Loaduint" || name == "Loaduintptr" || name == "LoadAcq") + && this->args_ != NULL && this->args_->size() == 1) + { + if (int_size < 8 && (name == "Load64" || name == "Loadint64")) + // On 32-bit architectures we need to check alignment. + // Not intrinsify for now. + return NULL; + + Runtime::Function code; + Type* res_type; + if (name == "Load") + { + code = Runtime::ATOMIC_LOAD_4; + res_type = uint32_type; + } + else if (name == "Load64") + { + code = Runtime::ATOMIC_LOAD_8; + res_type = uint64_type; + } + else if (name == "Loadint64") + { + code = Runtime::ATOMIC_LOAD_8; + res_type = int64_type; + } + else if (name == "Loaduint") + { + code = (int_size == 8 + ? Runtime::ATOMIC_LOAD_8 + : Runtime::ATOMIC_LOAD_4); + res_type = uint_type; + } + else if (name == "Loaduintptr") + { + code = (ptr_size == 8 + ? Runtime::ATOMIC_LOAD_8 + : Runtime::ATOMIC_LOAD_4); + res_type = uintptr_type; + } + else if (name == "Loadp") + { + code = (ptr_size == 8 + ? Runtime::ATOMIC_LOAD_8 + : Runtime::ATOMIC_LOAD_4); + res_type = pointer_type; + } + else if (name == "LoadAcq") + { + code = Runtime::ATOMIC_LOAD_4; + res_type = uint32_type; + memorder = __ATOMIC_ACQUIRE; + } + else + go_unreachable(); + Expression* a1 = this->args_->front(); + Expression* a2 = Expression::make_integer_ul(memorder, int32_type, loc); + Expression* call = Runtime::make_call(code, loc, 2, a1, a2); + return Expression::make_unsafe_cast(res_type, call, loc); + } + + if ((name == "Store" || name == "Store64" || name == "StorepNoWB" + || name == "Storeuintptr" || name == "StoreRel") + && this->args_ != NULL && this->args_->size() == 2) + { + if (int_size < 8 && name == "Store64") + return NULL; + + Runtime::Function code; + Expression* a1 = this->args_->at(0); + Expression* a2 = this->args_->at(1); + if (name == "Store") + code = Runtime::ATOMIC_STORE_4; + else if (name == "Store64") + code = Runtime::ATOMIC_STORE_8; + else if (name == "Storeuintptr") + code = (ptr_size == 8 ? Runtime::ATOMIC_STORE_8 : Runtime::ATOMIC_STORE_4); + else if (name == "StorepNoWB") + { + code = (ptr_size == 8 ? Runtime::ATOMIC_STORE_8 : Runtime::ATOMIC_STORE_4); + a2 = Expression::make_unsafe_cast(uintptr_type, a2, loc); + a2 = Expression::make_cast(uint64_type, a2, loc); + } + else if (name == "StoreRel") + { + code = Runtime::ATOMIC_STORE_4; + memorder = __ATOMIC_RELEASE; + } + else + go_unreachable(); + Expression* a3 = Expression::make_integer_ul(memorder, int32_type, loc); + return Runtime::make_call(code, loc, 3, a1, a2, a3); + } + + if ((name == "Xchg" || name == "Xchg64" || name == "Xchguintptr") + && this->args_ != NULL && this->args_->size() == 2) + { + if (int_size < 8 && name == "Xchg64") + return NULL; + + Runtime::Function code; + Type* res_type; + if (name == "Xchg") + { + code = Runtime::ATOMIC_EXCHANGE_4; + res_type = uint32_type; + } + else if (name == "Xchg64") + { + code = Runtime::ATOMIC_EXCHANGE_8; + res_type = uint64_type; + } + else if (name == "Xchguintptr") + { + code = (ptr_size == 8 + ? Runtime::ATOMIC_EXCHANGE_8 + : Runtime::ATOMIC_EXCHANGE_4); + res_type = uintptr_type; + } + else + go_unreachable(); + Expression* a1 = this->args_->at(0); + Expression* a2 = this->args_->at(1); + Expression* a3 = Expression::make_integer_ul(memorder, int32_type, loc); + Expression* call = Runtime::make_call(code, loc, 3, a1, a2, a3); + return Expression::make_cast(res_type, call, loc); + } + + if ((name == "Cas" || name == "Cas64" || name == "Casuintptr" + || name == "Casp1" || name == "CasRel") + && this->args_ != NULL && this->args_->size() == 3) + { + if (int_size < 8 && name == "Cas64") + return NULL; + + Runtime::Function code; + Expression* a1 = this->args_->at(0); + + // Builtin cas takes a pointer to the old value. + // Store it in a temporary and take the address. + Expression* a2 = this->args_->at(1); + Temporary_statement* ts = Statement::make_temporary(NULL, a2, loc); + inserter->insert(ts); + a2 = Expression::make_temporary_reference(ts, loc); + a2 = Expression::make_unary(OPERATOR_AND, a2, loc); + + Expression* a3 = this->args_->at(2); + if (name == "Cas") + code = Runtime::ATOMIC_COMPARE_EXCHANGE_4; + else if (name == "Cas64") + code = Runtime::ATOMIC_COMPARE_EXCHANGE_8; + else if (name == "Casuintptr") + code = (ptr_size == 8 + ? Runtime::ATOMIC_COMPARE_EXCHANGE_8 + : Runtime::ATOMIC_COMPARE_EXCHANGE_4); + else if (name == "Casp1") + { + code = (ptr_size == 8 + ? Runtime::ATOMIC_COMPARE_EXCHANGE_8 + : Runtime::ATOMIC_COMPARE_EXCHANGE_4); + a3 = Expression::make_unsafe_cast(uintptr_type, a3, loc); + a3 = Expression::make_cast(uint64_type, a3, loc); + } + else if (name == "CasRel") + { + code = Runtime::ATOMIC_COMPARE_EXCHANGE_4; + memorder = __ATOMIC_RELEASE; + } + else + go_unreachable(); + Expression* a4 = Expression::make_boolean(false, loc); + Expression* a5 = Expression::make_integer_ul(memorder, int32_type, loc); + Expression* a6 = Expression::make_integer_ul(__ATOMIC_RELAXED, int32_type, loc); + return Runtime::make_call(code, loc, 6, a1, a2, a3, a4, a5, a6); + } + + if ((name == "Xadd" || name == "Xadd64" || name == "Xaddint64" + || name == "Xadduintptr") + && this->args_ != NULL && this->args_->size() == 2) + { + if (int_size < 8 && (name == "Xadd64" || name == "Xaddint64")) + return NULL; + + Runtime::Function code; + Type* res_type; + if (name == "Xadd") + { + code = Runtime::ATOMIC_ADD_FETCH_4; + res_type = uint32_type; + } + else if (name == "Xadd64") + { + code = Runtime::ATOMIC_ADD_FETCH_8; + res_type = uint64_type; + } + else if (name == "Xaddint64") + { + code = Runtime::ATOMIC_ADD_FETCH_8; + res_type = int64_type; + } + else if (name == "Xadduintptr") + { + code = (ptr_size == 8 + ? Runtime::ATOMIC_ADD_FETCH_8 + : Runtime::ATOMIC_ADD_FETCH_4); + res_type = uintptr_type; + } + else + go_unreachable(); + Expression* a1 = this->args_->at(0); + Expression* a2 = this->args_->at(1); + Expression* a3 = Expression::make_integer_ul(memorder, int32_type, loc); + Expression* call = Runtime::make_call(code, loc, 3, a1, a2, a3); + return Expression::make_cast(res_type, call, loc); + } + + if ((name == "And8" || name == "Or8") + && this->args_ != NULL && this->args_->size() == 2) + { + Runtime::Function code; + if (name == "And8") + code = Runtime::ATOMIC_AND_FETCH_1; + else if (name == "Or8") + code = Runtime::ATOMIC_OR_FETCH_1; + else + go_unreachable(); + Expression* a1 = this->args_->at(0); + Expression* a2 = this->args_->at(1); + Expression* a3 = Expression::make_integer_ul(memorder, int32_type, loc); + return Runtime::make_call(code, loc, 3, a1, a2, a3); + } + } return NULL; } diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc index 289d6bf8297..28aca44706e 100644 --- a/gcc/go/gofrontend/runtime.cc +++ b/gcc/go/gofrontend/runtime.cc @@ -30,6 +30,8 @@ enum Runtime_function_type RFT_BOOLPTR, // Go type int, C type intgo. RFT_INT, + // Go type uint8, C type uint8_t. + RFT_UINT8, // Go type int32, C type int32_t. RFT_INT32, // Go type uint32, C type uint32_t. @@ -109,6 +111,10 @@ runtime_function_type(Runtime_function_type bft) t = Type::lookup_integer_type("int"); break; + case RFT_UINT8: + t = Type::lookup_integer_type("uint8"); + break; + case RFT_INT32: t = Type::lookup_integer_type("int32"); break; @@ -250,6 +256,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, case RFT_BOOL: case RFT_BOOLPTR: case RFT_INT: + case RFT_UINT8: case RFT_INT32: case RFT_UINT32: case RFT_INT64: diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index a5264eebada..226eeac4345 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -396,6 +396,38 @@ 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)) +// Atomics. +DEF_GO_RUNTIME(ATOMIC_LOAD_4, "__atomic_load_4", P2(POINTER, INT32), + R1(UINT32)) +DEF_GO_RUNTIME(ATOMIC_LOAD_8, "__atomic_load_8", P2(POINTER, INT32), + R1(UINT64)) +DEF_GO_RUNTIME(ATOMIC_STORE_4, "__atomic_store_4", P3(POINTER, UINT32, INT32), + R0()) +DEF_GO_RUNTIME(ATOMIC_STORE_8, "__atomic_store_8", P3(POINTER, UINT64, INT32), + R0()) +DEF_GO_RUNTIME(ATOMIC_EXCHANGE_4, "__atomic_exchange_4", P3(POINTER, UINT32, INT32), + R1(UINT32)) +DEF_GO_RUNTIME(ATOMIC_EXCHANGE_8, "__atomic_exchange_8", P3(POINTER, UINT64, INT32), + R1(UINT64)) +DEF_GO_RUNTIME(ATOMIC_COMPARE_EXCHANGE_4, "__atomic_compare_exchange_4", + P6(POINTER, POINTER, UINT32, BOOL, INT32, INT32), + R1(BOOL)) +DEF_GO_RUNTIME(ATOMIC_COMPARE_EXCHANGE_8, "__atomic_compare_exchange_8", + P6(POINTER, POINTER, UINT64, BOOL, INT32, INT32), + R1(BOOL)) +DEF_GO_RUNTIME(ATOMIC_ADD_FETCH_4, "__atomic_add_fetch_4", + P3(POINTER, UINT32, INT32), + R1(UINT32)) +DEF_GO_RUNTIME(ATOMIC_ADD_FETCH_8, "__atomic_add_fetch_8", + P3(POINTER, UINT64, INT32), + R1(UINT64)) +DEF_GO_RUNTIME(ATOMIC_AND_FETCH_1, "__atomic_and_fetch_1", + P3(POINTER, UINT8, INT32), + R1(UINT8)) +DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1", + P3(POINTER, UINT8, INT32), + R1(UINT8)) + // Remove helper macros. #undef ABFT6 #undef ABFT2 -- 2.30.2