compiler: add an option to emit optimization diagnostics

Add a -fgo-debug-optimization option to emit optimization
diagnostics. This can be used for testing optimizations. Apply
this to the range clear optimizations of maps and arrays.

Corresponding change on the GCC backend:

Index: gcc/go/gccgo.texi
===================================================================
--- gcc/go/gccgo.texi	(revision 269825)
+++ gcc/go/gccgo.texi	(working copy)
@@ -246,6 +246,11 @@
 that match the given suffix @var{n}.  This can be used to binary
 search across functions to uncover escape analysis bugs.

+@item -fgo-debug-optimization
+@cindex @option{-fgo-debug-optimization}
+@cindex @option{-fno-go-debug-optimization}
+Output optimization diagnostics.
+
 @item -fgo-c-header=@var{file}
 @cindex @option{-fgo-c-header}
 Write top-level named Go struct definitions to @var{file} as C code.
Index: gcc/go/go-c.h
===================================================================
--- gcc/go/go-c.h	(revision 269825)
+++ gcc/go/go-c.h	(working copy)
@@ -49,6 +49,7 @@
   int debug_escape_level;
   const char* debug_escape_hash;
   int64_t nil_check_size_threshold;
+  bool debug_optimization;
 };

 extern void go_create_gogo (const struct go_create_gogo_args*);
Index: gcc/go/go-lang.c
===================================================================
--- gcc/go/go-lang.c	(revision 269825)
+++ gcc/go/go-lang.c	(working copy)
@@ -118,6 +118,7 @@
   args.debug_escape_level = go_debug_escape_level;
   args.debug_escape_hash = go_debug_escape_hash;
   args.nil_check_size_threshold = TARGET_AIX ? -1 : 4096;
+  args.debug_optimization = go_debug_optimization;
   args.linemap = go_get_linemap();
   args.backend = go_get_backend();
   go_create_gogo (&args);
Index: gcc/go/lang.opt
===================================================================
--- gcc/go/lang.opt	(revision 269825)
+++ gcc/go/lang.opt	(working copy)
@@ -85,6 +85,10 @@
 Go Joined RejectNegative Var(go_debug_escape_hash) Init(0)
 -fgo-debug-escape-hash=<string>	Hash value to debug escape analysis.

+fgo-debug-optimization
+Go Var(go_debug_optimization) Init(0)
+Emit optimization diagnostics.
+
 o
 Go Joined Separate
 ; Documented in common.opt

With this option, we can introduce tests for the range clear
optimizations in the GCC testsuite:

Index: gcc/testsuite/go.dg/arrayclear.go
===================================================================
--- gcc/testsuite/go.dg/arrayclear.go	(nonexistent)
+++ gcc/testsuite/go.dg/arrayclear.go	(working copy)
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-fgo-debug-optimization" }
+
+package p
+
+var a [10]int
+
+func arrayClear() {
+	for i := range a { // { dg-error "array range clear" }
+		a[i] = 0
+	}
+}
+
+var s []int
+
+func sliceClear() {
+	for i := range s { // { dg-error "array range clear" }
+		s[i] = 0
+	}
+}
Index: gcc/testsuite/go.dg/mapclear.go
===================================================================
--- gcc/testsuite/go.dg/mapclear.go	(nonexistent)
+++ gcc/testsuite/go.dg/mapclear.go	(working copy)
@@ -0,0 +1,10 @@
+// { dg-do compile }
+// { dg-options "-fgo-debug-optimization" }
+
+package p
+
+func clear(m map[int]int) {
+	for k := range m { // { dg-error "map range clear" }
+		delete(m, k)
+	}
+}

Change-Id: I0d4c1bcc330b50332831b43457bd04f43ae5ec02
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/170002
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/go.cc b/go/go.cc
index d8da232..183664a 100644
--- a/go/go.cc
+++ b/go/go.cc
@@ -44,6 +44,8 @@
   if (args->debug_escape_hash != NULL)
     ::gogo->set_debug_escape_hash(args->debug_escape_hash);
   ::gogo->set_nil_check_size_threshold(args->nil_check_size_threshold);
+  if (args->debug_optimization)
+    ::gogo->set_debug_optimization(args->debug_optimization);
 }
 
 // Parse the input files.
diff --git a/go/gogo.cc b/go/gogo.cc
index f45576e..9f18e14 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -55,6 +55,7 @@
     check_divide_overflow_(true),
     compiling_runtime_(false),
     debug_escape_level_(0),
+    debug_optimization_(false),
     nil_check_size_threshold_(4096),
     verify_types_(),
     interface_types_(),
diff --git a/go/gogo.h b/go/gogo.h
index 1c9f0de..cfa238a 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -326,6 +326,16 @@
   set_debug_escape_hash(const std::string& s)
   { this->debug_escape_hash_ = s; }
 
+  // Return whether to output optimization diagnostics.
+  bool
+  debug_optimization() const
+  { return this->debug_optimization_; }
+
+  // Set the option to output optimization diagnostics.
+  void
+  set_debug_optimization(bool b)
+  { this->debug_optimization_ = b; }
+
   // Return the size threshold used to determine whether to issue
   // a nil-check for a given pointer dereference. A threshold of -1
   // implies that all potentially faulting dereference ops should
@@ -1075,6 +1085,9 @@
   // -fgo-debug-escape-hash option. The analysis is run only on
   // functions with names that hash to the matching value.
   std::string debug_escape_hash_;
+  // Whether to output optimization diagnostics, from the
+  // -fgo-debug-optimization option.
+  bool debug_optimization_;
   // Nil-check size threshhold.
   int64_t nil_check_size_threshold_;
   // A list of types to verify.
diff --git a/go/statements.cc b/go/statements.cc
index 1827f81..2e2d039 100644
--- a/go/statements.cc
+++ b/go/statements.cc
@@ -5512,6 +5512,8 @@
                                                      range_temp, loc);
       if (clear != NULL)
         {
+          if (gogo->debug_optimization())
+            go_inform(loc, "map range clear");
           temp_block->add_statement(clear);
           return Statement::make_block_statement(temp_block, loc);
         }
@@ -5527,6 +5529,8 @@
                                                        range_temp, loc);
       if (clear != NULL)
         {
+          if (gogo->debug_optimization())
+            go_inform(loc, "array range clear");
           temp_block->add_statement(clear);
           return Statement::make_block_statement(temp_block, loc);
         }