This patch to the Go frontend by Cherry Zhang adds intrinsics for runtime/internal/sys functions.
runtime/internal/sys.Ctz32/64 and Bswap32/64 are currently implemented with compiler builtin functions. But if they are called from another package, the compiler does not know and therefore cannot turn them into compiler intrinsics. This patch makes the compiler recognize these functions and turn them into intrinsics directly, as the gc compiler does. This patch sets up a way for adding intrinsics in the compiler. More intrinsics will be added in later patches. Also move the handling of runtime.getcallerpc/sp to the new way of generating intrinsics. Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed to mainline. Ian
Index: gcc/go/gofrontend/MERGE =================================================================== --- gcc/go/gofrontend/MERGE (revision 271276) +++ gcc/go/gofrontend/MERGE (working copy) @@ -1,4 +1,4 @@ -2df0879e7880057293c0a59be6868a3e6ea5105b +c0c8ad50627e3a59267e6e3de233a0b30cf64150 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 271276) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -10252,42 +10252,6 @@ Call_expression::do_lower(Gogo* gogo, Na bme->location()); } - // Handle a couple of special runtime functions. In the runtime - // package, getcallerpc returns the PC of the caller, and - // getcallersp returns the frame pointer of the caller. Implement - // these by turning them into calls to GCC builtin functions. We - // could implement them in normal code, but then we would have to - // explicitly unwind the stack. These functions are intended to be - // efficient. Note that this technique obviously only works for - // direct calls, but that is the only way they are used. - if (gogo->compiling_runtime() && gogo->package_name() == "runtime") - { - Func_expression* fe = this->fn_->func_expression(); - if (fe != NULL - && fe->named_object()->is_function_declaration() - && fe->named_object()->package() == NULL) - { - std::string n = Gogo::unpack_hidden_name(fe->named_object()->name()); - if ((this->args_ == NULL || this->args_->size() == 0) - && n == "getcallerpc") - { - static Named_object* builtin_return_address; - int arg = 0; - return this->lower_to_builtin(&builtin_return_address, - "__builtin_return_address", - &arg); - } - else if ((this->args_ == NULL || this->args_->size() == 0) - && n == "getcallersp") - { - static Named_object* builtin_dwarf_cfa; - return this->lower_to_builtin(&builtin_dwarf_cfa, - "__builtin_dwarf_cfa", - NULL); - } - } - } - // If this is a call to an imported function for which we have an // inlinable function body, add it to the list of functions to give // to the backend as inlining opportunities. @@ -10401,31 +10365,6 @@ Call_expression::lower_varargs(Gogo* gog this->varargs_are_lowered_ = true; } -// Return a call to __builtin_return_address or __builtin_dwarf_cfa. - -Expression* -Call_expression::lower_to_builtin(Named_object** pno, const char* name, - int* arg) -{ - if (*pno == NULL) - *pno = Gogo::declare_builtin_rf_address(name, arg != NULL); - - Location loc = this->location(); - - Expression* fn = Expression::make_func_reference(*pno, NULL, loc); - Expression_list *args = new Expression_list(); - if (arg != NULL) - { - Expression* a = Expression::make_integer_ul(*arg, NULL, loc); - args->push_back(a); - } - Expression* call = Expression::make_call(fn, args, false, loc); - - // The builtin functions return void*, but the Go functions return uintptr. - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - return Expression::make_cast(uintptr_type, call, loc); -} - // Flatten a call with multiple results into a temporary. Expression* @@ -10491,9 +10430,125 @@ Call_expression::do_flatten(Gogo* gogo, this->args_ = args; } + // Lower to compiler intrinsic if possible. + Func_expression* fe = this->fn_->func_expression(); + if (fe != NULL + && (fe->named_object()->is_function_declaration() + || fe->named_object()->is_function())) + { + Expression* ret = this->intrinsify(gogo, inserter); + if (ret != NULL) + return ret; + } + return this; } +// Lower a call to a compiler intrinsic if possible. +// Returns NULL if it is not an intrinsic. + +Expression* +Call_expression::intrinsify(Gogo* gogo, + Statement_inserter* inserter) +{ + Func_expression* fe = this->fn_->func_expression(); + Named_object* no = fe->named_object(); + std::string name = Gogo::unpack_hidden_name(no->name()); + std::string package = (no->package() != NULL + ? no->package()->pkgpath() + : gogo->pkgpath()); + Location loc = this->location(); + + Type* int_type = Type::lookup_integer_type("int"); + Type* uint32_type = Type::lookup_integer_type("uint32"); + Type* uint64_type = Type::lookup_integer_type("uint64"); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + if (package == "runtime") + { + // Handle a couple of special runtime functions. In the runtime + // package, getcallerpc returns the PC of the caller, and + // getcallersp returns the frame pointer of the caller. Implement + // these by turning them into calls to GCC builtin functions. We + // could implement them in normal code, but then we would have to + // explicitly unwind the stack. These functions are intended to be + // efficient. Note that this technique obviously only works for + // direct calls, but that is the only way they are used. + if (name == "getcallerpc" + && (this->args_ == NULL || this->args_->size() == 0)) + { + Expression* arg = Expression::make_integer_ul(0, uint32_type, loc); + Expression* call = + Runtime::make_call(Runtime::BUILTIN_RETURN_ADDRESS, loc, + 1, arg); + // The builtin functions return void*, but the Go functions return uintptr. + return Expression::make_cast(uintptr_type, call, loc); + } + else if (name == "getcallersp" + && (this->args_ == NULL || this->args_->size() == 0)) + + { + Expression* call = + Runtime::make_call(Runtime::BUILTIN_DWARF_CFA, loc, 0); + // The builtin functions return void*, but the Go functions return uintptr. + return Expression::make_cast(uintptr_type, call, loc); + } + } + else if (package == "runtime/internal/sys") + { + if (name == "Bswap32" + && this->args_ != NULL && this->args_->size() == 1) + { + Expression* arg = this->args_->front(); + return Runtime::make_call(Runtime::BUILTIN_BSWAP32, loc, 1, arg); + } + else if (name == "Bswap64" + && this->args_ != NULL && this->args_->size() == 1) + { + Expression* arg = this->args_->front(); + return Runtime::make_call(Runtime::BUILTIN_BSWAP64, loc, 1, arg); + } + else if (name == "Ctz32" + && 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_ctz(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_CTZ, loc, 1, arg->copy()); + call = Expression::make_cast(int_type, call, loc); + return Expression::make_conditional(cmp, c32, call, loc); + } + else if (name == "Ctz64" + && 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_ctzll(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_CTZLL, loc, 1, arg->copy()); + call = Expression::make_cast(int_type, call, loc); + return Expression::make_conditional(cmp, c64, call, loc); + } + } + + return NULL; +} + // Make implicit type conversions explicit. void Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 271276) +++ gcc/go/gofrontend/expressions.h (working copy) @@ -2427,7 +2427,7 @@ class Call_expression : public Expressio check_argument_type(int, const Type*, const Type*, Location, bool); Expression* - lower_to_builtin(Named_object**, const char*, int*); + intrinsify(Gogo*, Statement_inserter*); Expression* interface_method_function(Interface_field_reference_expression*, Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 271276) +++ gcc/go/gofrontend/gogo.cc (working copy) @@ -4566,11 +4566,6 @@ Build_recover_thunks::function(Named_obj Expression* Build_recover_thunks::can_recover_arg(Location location) { - static Named_object* builtin_return_address; - if (builtin_return_address == NULL) - builtin_return_address = - Gogo::declare_builtin_rf_address("__builtin_return_address", true); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); static Named_object* can_recover; if (can_recover == NULL) @@ -4589,20 +4584,15 @@ Build_recover_thunks::can_recover_arg(Lo can_recover->func_declaration_value()->set_asm_name("runtime.canrecover"); } - Expression* fn = Expression::make_func_reference(builtin_return_address, - NULL, location); - Expression* zexpr = Expression::make_integer_ul(0, NULL, location); - Expression_list *args = new Expression_list(); - args->push_back(zexpr); - - Expression* call = Expression::make_call(fn, args, false, location); + Expression* call = Runtime::make_call(Runtime::BUILTIN_RETURN_ADDRESS, + location, 1, zexpr); call = Expression::make_unsafe_cast(uintptr_type, call, location); - args = new Expression_list(); + Expression_list* args = new Expression_list(); args->push_back(call); - fn = Expression::make_func_reference(can_recover, NULL, location); + Expression* fn = Expression::make_func_reference(can_recover, NULL, location); return Expression::make_call(fn, args, false, location); } @@ -4622,33 +4612,6 @@ Gogo::build_recover_thunks() this->traverse(&build_recover_thunks); } -// Return a declaration for __builtin_return_address or -// __builtin_dwarf_cfa. - -Named_object* -Gogo::declare_builtin_rf_address(const char* name, bool hasarg) -{ - const Location bloc = Linemap::predeclared_location(); - - Typed_identifier_list* param_types = new Typed_identifier_list(); - if (hasarg) - { - Type* uint32_type = Type::lookup_integer_type("uint32"); - param_types->push_back(Typed_identifier("l", uint32_type, bloc)); - } - - Typed_identifier_list* return_types = new Typed_identifier_list(); - Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); - return_types->push_back(Typed_identifier("", voidptr_type, bloc)); - - Function_type* fntype = Type::make_function_type(NULL, param_types, - return_types, bloc); - Named_object* ret = Named_object::make_function_declaration(name, NULL, - fntype, bloc); - ret->func_declaration_value()->set_asm_name(name); - return ret; -} - // Build a call to the runtime error function. Expression* Index: gcc/go/gofrontend/gogo.h =================================================================== --- gcc/go/gofrontend/gogo.h (revision 271276) +++ gcc/go/gofrontend/gogo.h (working copy) @@ -764,11 +764,6 @@ class Gogo void build_recover_thunks(); - // Return a declaration for __builtin_return_address or - // __builtin_dwarf_cfa. - static Named_object* - declare_builtin_rf_address(const char* name, bool hasarg); - // Simplify statements which might use thunks: go and defer // statements. void Index: gcc/go/gofrontend/runtime.cc =================================================================== --- gcc/go/gofrontend/runtime.cc (revision 271182) +++ gcc/go/gofrontend/runtime.cc (working copy) @@ -32,6 +32,8 @@ enum Runtime_function_type RFT_INT, // Go type int32, C type int32_t. RFT_INT32, + // Go type uint32, C type uint32_t. + RFT_UINT32, // Go type int64, C type int64_t. RFT_INT64, // Go type uint64, C type uint64_t. @@ -111,6 +113,10 @@ runtime_function_type(Runtime_function_t t = Type::lookup_integer_type("int32"); break; + case RFT_UINT32: + t = Type::lookup_integer_type("uint32"); + break; + case RFT_INT64: t = Type::lookup_integer_type("int64"); break; @@ -245,6 +251,7 @@ convert_to_runtime_function_type(Runtime case RFT_BOOLPTR: case RFT_INT: case RFT_INT32: + case RFT_UINT32: case RFT_INT64: case RFT_UINT64: case RFT_UINTPTR: Index: gcc/go/gofrontend/runtime.def =================================================================== --- gcc/go/gofrontend/runtime.def (revision 271182) +++ gcc/go/gofrontend/runtime.def (working copy) @@ -376,6 +376,26 @@ DEF_GO_RUNTIME(UNREACHABLE, "__builtin_u DEF_GO_RUNTIME(BUILTIN_MEMMOVE, "__builtin_memmove", P3(POINTER, POINTER, UINTPTR), R0()) +// Various intrinsics. + +// Get the caller's PC, used for runtime.getcallerpc. +DEF_GO_RUNTIME(BUILTIN_RETURN_ADDRESS, "__builtin_return_address", + P1(UINT32), R1(POINTER)) + +// Get the caller's SP, used for runtime.getcallersp. +DEF_GO_RUNTIME(BUILTIN_DWARF_CFA, "__builtin_dwarf_cfa", P0(), + R1(POINTER)) + +// Swap bytes. +DEF_GO_RUNTIME(BUILTIN_BSWAP32, "__builtin_bswap32", P1(UINT32), + R1(UINT32)) +DEF_GO_RUNTIME(BUILTIN_BSWAP64, "__builtin_bswap64", P1(UINT64), + R1(UINT64)) + +// Count trailing zeros. +DEF_GO_RUNTIME(BUILTIN_CTZ, "__builtin_ctz", P1(UINT32), R1(INT32)) +DEF_GO_RUNTIME(BUILTIN_CTZLL, "__builtin_ctzll", P1(UINT64), R1(INT32)) + // Remove helper macros. #undef ABFT6 #undef ABFT2