compiler: add 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 CL
makes the compiler recognize these functions and turn them into
intrinsics directly, as the gc compiler does.

This CL sets up a way for adding intrinsics in the compiler.
More intrinsics will be added in later CLs.

Also move the handling of runtime.getcallerpc/sp to the new way
of generating intrinsics.

Change-Id: I1929f88327d9035beb860c8b1416198bf8527155
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/176917
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.cc b/go/expressions.cc
index 8af0dd4..d162b05 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -10252,42 +10252,6 @@
 						  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 @@
   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 @@
       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
diff --git a/go/expressions.h b/go/expressions.h
index b1811ea..21a214d 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -2427,7 +2427,7 @@
   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*,
diff --git a/go/gogo.cc b/go/gogo.cc
index 06657cb..02120f2 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -4566,11 +4566,6 @@
 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 @@
       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 @@
   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*
diff --git a/go/gogo.h b/go/gogo.h
index 448da04..e67a8a5 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -764,11 +764,6 @@
   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
diff --git a/go/runtime.cc b/go/runtime.cc
index 7ecbf68..289d6bf 100644
--- a/go/runtime.cc
+++ b/go/runtime.cc
@@ -32,6 +32,8 @@
   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 @@
 	  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 @@
     case RFT_BOOLPTR:
     case RFT_INT:
     case RFT_INT32:
+    case RFT_UINT32:
     case RFT_INT64:
     case RFT_UINT64:
     case RFT_UINTPTR:
diff --git a/go/runtime.def b/go/runtime.def
index c17b481..a5264ee 100644
--- a/go/runtime.def
+++ b/go/runtime.def
@@ -376,6 +376,26 @@
 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