diff mbox

[gccgo] Clean up runtime error handling

Message ID mcr7hiuclsl.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor Sept. 10, 2010, 3:36 a.m. UTC
This gccgo patch cleans up the runtime error handling and makes it a bit
more like the 6g compiler.  This turns on -fnon-call-exceptions for Go.
That may be overkill for Go, but Go can throw an exception (a panic) on
a nil memory reference or a division by zero.  Committed to gccgo
branch.

Ian
diff mbox

Patch

diff -r 30ec2dd46cc2 go/expressions.cc
--- a/go/expressions.cc	Thu Sep 09 14:58:38 2010 -0700
+++ b/go/expressions.cc	Thu Sep 09 20:30:35 2010 -0700
@@ -390,9 +390,10 @@ 
 			   space);
   space = save_expr(space);
 
+  tree ref = build_fold_indirect_ref_loc(location, space);
+  TREE_THIS_NOTRAP(ref) = 1;
   tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
-			     build_fold_indirect_ref_loc(location, space),
-			     rhs_tree);
+			     ref, rhs_tree);
 
   elt->value = fold_convert_loc(location, TREE_TYPE(field), space);
 
@@ -3919,15 +3920,8 @@ 
 					   expr,
 					   fold_convert(TREE_TYPE(expr),
 							null_pointer_node));
-	    // FIXME: This should be a different error message.
-	    static tree bad_index_fndecl;
-	    tree crash = Gogo::call_builtin(&bad_index_fndecl,
-					    loc,
-					    "__go_bad_index",
-					    0,
-					    void_type_node);
-	    TREE_NOTHROW(bad_index_fndecl) = 0;
-	    TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+	    tree crash = Gogo::runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
+					     loc);
 	    expr = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(expr),
 				   build3(COND_EXPR, void_type_node,
 					  compare, crash, NULL_TREE),
@@ -8860,14 +8854,14 @@ 
 					      boolean_type_node, start_tree,
 					      length_tree));
 
-  static tree bad_index_fndecl;
-  tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				  loc,
-				  "__go_bad_index",
-				  0,
-				  void_type_node);
-  TREE_NOTHROW(bad_index_fndecl) = 0;
-  TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+  int code = (array_type->length() != NULL
+	      ? (this->end_ == NULL
+		 ? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS
+		 : RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS)
+	      : (this->end_ == NULL
+		 ? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS
+		 : RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS));
+  tree crash = Gogo::runtime_error(code, loc);
 
   if (this->end_ == NULL)
     {
@@ -9164,15 +9158,10 @@ 
 
   start_tree = fold_convert_loc(loc, length_type, start_tree);
 
-  // FIXME: Duplicates Array_index::do_get_tree.
-  static tree bad_index_fndecl;
-  tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				  this->location(),
-				  "__go_bad_index",
-				  0,
-				  void_type_node);
-  TREE_NOTHROW(bad_index_fndecl) = 0;
-  TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+  int code = (this->end_ == NULL
+	      ? RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS
+	      : RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS);
+  tree crash = Gogo::runtime_error(code, loc);
 
   if (this->end_ == NULL)
     {
@@ -10610,8 +10599,9 @@ 
       space = save_expr(space);
 
       tree s = fold_convert(build_pointer_type(TREE_TYPE(values)), space);
-      set = build2(MODIFY_EXPR, void_type_node,
-		   build_fold_indirect_ref(s), values);
+      tree ref = build_fold_indirect_ref_loc(this->location(), s);
+      TREE_THIS_NOTRAP(ref) = 1;
+      set = build2(MODIFY_EXPR, void_type_node, ref, values);
     }
 
   // Build a constructor for the open array.
@@ -11529,10 +11519,10 @@ 
 						expr_size, this->location());
   space = fold_convert(build_pointer_type(TREE_TYPE(expr_tree)), space);
   space = save_expr(space);
+  tree ref = build_fold_indirect_ref_loc(this->location(), space);
+  TREE_THIS_NOTRAP(ref) = 1;
   tree ret = build2(COMPOUND_EXPR, TREE_TYPE(space),
-		    build2(MODIFY_EXPR, void_type_node,
-			   build_fold_indirect_ref(space),
-			   expr_tree),
+		    build2(MODIFY_EXPR, void_type_node, ref, expr_tree),
 		    space);
   SET_EXPR_LOCATION(ret, this->location());
   return ret;
diff -r 30ec2dd46cc2 go/go-lang.c
--- a/go/go-lang.c	Thu Sep 09 14:58:38 2010 -0700
+++ b/go/go-lang.c	Thu Sep 09 20:30:35 2010 -0700
@@ -122,6 +122,9 @@ 
   /* The builtin math functions should not set errno.  */
   flag_errno_math = 0;
 
+  /* By default assume that floating point math does not trap.  */
+  flag_trapping_math = 0;
+
   /* We default to always showing a column number.  */
   flag_show_column = 1;
 
@@ -136,6 +139,7 @@ 
 
   /* Exceptions are used to handle recovering from panics.  */
   flag_exceptions = 1;
+  flag_non_call_exceptions = 1;
   using_eh_for_cleanups ();
 
   return CL_Go;
diff -r 30ec2dd46cc2 go/gogo-tree.cc
--- a/go/gogo-tree.cc	Thu Sep 09 14:58:38 2010 -0700
+++ b/go/gogo-tree.cc	Thu Sep 09 20:30:35 2010 -0700
@@ -908,6 +908,7 @@ 
 	{
 	  gcc_assert(TREE_CODE(ret) == INDIRECT_REF);
 	  ret = build_fold_indirect_ref(TREE_OPERAND(ret, 0));
+	  TREE_THIS_NOTRAP(ret) = 1;
 	}
       return ret;
     }
@@ -1082,6 +1083,7 @@ 
 		    space = save_expr(space);
 		    space = fold_convert_loc(loc, result_type, space);
 		    tree spaceref = build_fold_indirect_ref_loc(loc, space);
+		    TREE_THIS_NOTRAP(spaceref) = 1;
 		    tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
 					       spaceref, subinit);
 		    init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
@@ -1153,6 +1155,7 @@ 
     {
       gcc_assert(POINTER_TYPE_P(TREE_TYPE(ret)));
       ret = build_fold_indirect_ref(ret);
+      TREE_THIS_NOTRAP(ret) = 1;
     }
 
   this->tree_ = ret;
@@ -1399,21 +1402,24 @@ 
       val_type = TREE_TYPE(TREE_TYPE(var_decl));
     }
   gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
+  source_location loc = DECL_SOURCE_LOCATION(var_decl);
   std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
   name += ".pointer";
   tree id = get_identifier_from_string(name);
-  tree parm_decl = build_decl(DECL_SOURCE_LOCATION(var_decl), PARM_DECL, id,
-			      build_pointer_type(val_type));
+  tree parm_decl = build_decl(loc, PARM_DECL, id, build_pointer_type(val_type));
   DECL_CONTEXT(parm_decl) = current_function_decl;
   DECL_ARG_TYPE(parm_decl) = TREE_TYPE(parm_decl);
 
   gcc_assert(DECL_INITIAL(var_decl) == NULL_TREE);
   // The receiver might be passed as a null pointer.
-  tree check = build2(NE_EXPR, boolean_type_node, parm_decl,
-		      fold_convert(TREE_TYPE(parm_decl), null_pointer_node));
-  tree ind = build_fold_indirect_ref(parm_decl);
+  tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node, parm_decl,
+			       fold_convert_loc(loc, TREE_TYPE(parm_decl),
+						null_pointer_node));
+  tree ind = build_fold_indirect_ref_loc(loc, parm_decl);
+  TREE_THIS_NOTRAP(ind) = 1;
   tree zero_init = no->var_value()->type()->get_init_tree(gogo, false);
-  tree init = build3(COND_EXPR, TREE_TYPE(ind), check, ind, zero_init);
+  tree init = fold_build3_loc(loc, COND_EXPR, TREE_TYPE(ind),
+			      check, ind, zero_init);
 
   if (is_in_heap)
     {
@@ -1422,11 +1428,20 @@ 
 					 no->location());
       space = save_expr(space);
       space = fold_convert(build_pointer_type(val_type), space);
-      init = build2(COMPOUND_EXPR, TREE_TYPE(space),
-		    build2(MODIFY_EXPR, void_type_node,
-			   build_fold_indirect_ref(space),
-			   build_fold_indirect_ref(parm_decl)),
-		    space);
+      tree spaceref = build_fold_indirect_ref_loc(no->location(), space);
+      TREE_THIS_NOTRAP(spaceref) = 1;
+      tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node,
+				   parm_decl,
+				   fold_convert_loc(loc, TREE_TYPE(parm_decl),
+						    null_pointer_node));
+      tree parmref = build_fold_indirect_ref_loc(no->location(), parm_decl);
+      TREE_THIS_NOTRAP(parmref) = 1;
+      tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
+				 spaceref, parmref);
+      init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
+			     build3(COND_EXPR, void_type_node,
+				    check, set, NULL_TREE),
+			     space);
     }
 
   DECL_INITIAL(var_decl) = init;
@@ -1463,10 +1478,10 @@ 
   tree space = gogo->allocate_memory(no->var_value()->type(), size, loc);
   space = save_expr(space);
   space = fold_convert(TREE_TYPE(var_decl), space);
+  tree spaceref = build_fold_indirect_ref_loc(loc, space);
+  TREE_THIS_NOTRAP(spaceref) = 1;
   tree init = build2(COMPOUND_EXPR, TREE_TYPE(space),
-		     build2(MODIFY_EXPR, void_type_node,
-			    build_fold_indirect_ref(space),
-			    parm_decl),
+		     build2(MODIFY_EXPR, void_type_node, spaceref, parm_decl),
 		     space);
   DECL_INITIAL(var_decl) = init;
 
@@ -4109,6 +4124,25 @@ 
   return ret;
 }
 
+// Build a call to the runtime error function.
+
+tree
+Gogo::runtime_error(int code, source_location location)
+{
+  static tree runtime_error_fndecl;
+  tree ret = Gogo::call_builtin(&runtime_error_fndecl,
+				location,
+				"__go_runtime_error",
+				1,
+				void_type_node,
+				integer_type_node,
+				build_int_cst(integer_type_node, code));
+  // The runtime error function panics and does not return.
+  TREE_NOTHROW(runtime_error_fndecl) = 0;
+  TREE_THIS_VOLATILE(runtime_error_fndecl) = 1;
+  return ret;
+}
+
 // Send VAL on CHANNEL.  If BLOCKING is true, the resulting tree has a
 // void type.  If BLOCKING is false, the resulting tree has a boolean
 // type, and it will evaluate as true if the value was sent.  If
diff -r 30ec2dd46cc2 go/gogo.h
--- a/go/gogo.h	Thu Sep 09 14:58:38 2010 -0700
+++ b/go/gogo.h	Thu Sep 09 20:30:35 2010 -0700
@@ -410,6 +410,10 @@ 
   call_builtin(tree* pdecl, source_location, const char* name, int nargs,
 	       tree rettype, ...);
 
+  // Build a call to the runtime error function.
+  static tree
+  runtime_error(int code, source_location);
+
   // Build a builtin struct with a list of fields.
   static tree
   builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
@@ -2556,6 +2560,45 @@ 
   tree block_tree_;
 };
 
+// Runtime error codes.  These must match the values in
+// libgo/runtime/go-runtime-error.c.
+
+// Slice index out of bounds: negative or larger than the length of
+// the slice.
+static const int RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS = 0;
+
+// Array index out of bounds.
+static const int RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS = 1;
+
+// String index out of bounds.
+static const int RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS = 2;
+
+// Slice slice out of bounds: negative or larger than the length of
+// the slice or high bound less than low bound.
+static const int RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS = 3;
+
+// Array slice out of bounds.
+static const int RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS = 4;
+
+// String slice out of bounds.
+static const int RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS = 5;
+
+// Dereference of nil pointer.  This is used when there is a
+// dereference of a pointer to a very large struct or array, to ensure
+// that a gigantic array is not used a proxy to access random memory
+// locations.
+static const int RUNTIME_ERROR_NIL_DEREFERENCE = 6;
+
+// Slice length or capacity out of bounds in make: negative or
+// overflow or length greater than capacity.
+static const int RUNTIME_ERROR_MAKE_SLICE_OUT_OF_BOUNDS = 7;
+
+// Map capacity out of bounds in make: negative or overflow.
+static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8;
+
+// Channel capacity out of bounds in make: negative or overflow.
+static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9;
+
 // This is used by some of the langhooks.
 extern Gogo* go_get_gogo();
 
diff -r 30ec2dd46cc2 go/types.cc
--- a/go/types.cc	Thu Sep 09 14:58:38 2010 -0700
+++ b/go/types.cc	Thu Sep 09 20:30:35 2010 -0700
@@ -3751,14 +3751,8 @@ 
 
   if (bad_index != NULL_TREE && bad_index != boolean_false_node)
     {
-      static tree bad_index_fndecl;
-      tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				      location,
-				      "__go_bad_makeslice",
-				      0,
-				      void_type_node);
-      TREE_NOTHROW(bad_index_fndecl) = 0;
-      TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+      tree crash = Gogo::runtime_error(RUNTIME_ERROR_MAKE_SLICE_OUT_OF_BOUNDS,
+				       location);
       space = build2(COMPOUND_EXPR, TREE_TYPE(space),
 		     build3(COND_EXPR, void_type_node,
 			    bad_index, crash, NULL_TREE),
@@ -4160,16 +4154,8 @@ 
     return ret;
   else
     {
-      // FIXME: Wrong message.
-      static tree bad_index_fndecl;
-      tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				      location,
-				      "__go_bad_index",
-				      0,
-				      void_type_node);
-      TREE_NOTHROW(bad_index_fndecl) = 0;
-      TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
-
+      tree crash = Gogo::runtime_error(RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS,
+				       location);
       return build2(COMPOUND_EXPR, TREE_TYPE(ret),
 		    build3(COND_EXPR, void_type_node,
 			   bad_index, crash, NULL_TREE),
@@ -4367,16 +4353,8 @@ 
     return ret;
   else
     {
-      // FIXME: Wrong message.
-      static tree bad_index_fndecl;
-      tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				      location,
-				      "__go_bad_index",
-				      0,
-				      void_type_node);
-      TREE_NOTHROW(bad_index_fndecl) = 0;
-      TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
-
+      tree crash = Gogo::runtime_error(RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS,
+				       location);
       return build2(COMPOUND_EXPR, TREE_TYPE(ret),
 		    build3(COND_EXPR, void_type_node,
 			   bad_index, crash, NULL_TREE),
diff -r 30ec2dd46cc2 libgo/Makefile.am
--- a/libgo/Makefile.am	Thu Sep 09 14:58:38 2010 -0700
+++ b/libgo/Makefile.am	Thu Sep 09 20:30:35 2010 -0700
@@ -294,8 +294,6 @@ 
 runtime_files = \
 	runtime/go-assert.c \
 	runtime/go-assert-interface.c \
-	runtime/go-bad-index.c \
-	runtime/go-bad-makeslice.c \
 	runtime/go-byte-array-to-string.c \
 	runtime/go-breakpoint.c \
 	runtime/go-caller.c \
@@ -339,6 +337,7 @@ 
 	runtime/go-reflect-chan.c \
 	runtime/go-reflect-map.c \
 	runtime/go-rune.c \
+	runtime/go-runtime-error.c \
 	runtime/go-sched.c \
 	runtime/go-select.c \
 	runtime/go-semacquire.c \
diff -r 30ec2dd46cc2 libgo/runtime/go-bad-index.c
--- a/libgo/runtime/go-bad-index.c	Thu Sep 09 14:58:38 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@ 
-/* go-bad-index.c -- bad array/string index error in Go.
-
-   Copyright 2009 The Go Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style
-   license that can be found in the LICENSE file.  */
-
-#include "go-panic.h"
-
-extern void __go_bad_index () __attribute__ ((noreturn));
-
-void
-__go_bad_index ()
-{
-  __go_panic_msg ("index or slice out of range");
-}
diff -r 30ec2dd46cc2 libgo/runtime/go-bad-makeslice.c
--- a/libgo/runtime/go-bad-makeslice.c	Thu Sep 09 14:58:38 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@ 
-/* go-bad-makeslice.c -- bad call to make for a slice in Go.
-
-   Copyright 2010 The Go Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style
-   license that can be found in the LICENSE file.  */
-
-#include "go-panic.h"
-
-extern void __go_bad_makeslice () __attribute__ ((noreturn));
-
-void
-__go_bad_makeslice ()
-{
-  __go_panic_msg ("slice len or cap out of range");
-}
diff -r 30ec2dd46cc2 libgo/runtime/go-runtime-error.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgo/runtime/go-runtime-error.c	Thu Sep 09 20:30:35 2010 -0700
@@ -0,0 +1,84 @@ 
+/* go-runtime-error.c -- Go runtime error.
+
+   Copyright 2010 The Go Authors. All rights reserved.
+   Use of this source code is governed by a BSD-style
+   license that can be found in the LICENSE file.  */
+
+#include "go-panic.h"
+
+/* The compiler generates calls to this function.  This enum values
+   are known to the compiler and used by compiled code.  Any change
+   here must be reflected in the compiler.  */
+
+enum
+{
+  /* Slice index out of bounds: negative or larger than the length of
+     the slice.  */
+  SLICE_INDEX_OUT_OF_BOUNDS = 0,
+
+  /* Array index out of bounds.  */
+  ARRAY_INDEX_OUT_OF_BOUNDS = 1,
+
+  /* String index out of bounds.  */
+  STRING_INDEX_OUT_OF_BOUNDS = 2,
+
+  /* Slice slice out of bounds: negative or larger than the length of
+     the slice or high bound less than low bound.  */
+  SLICE_SLICE_OUT_OF_BOUNDS = 3,
+
+  /* Array slice out of bounds.  */
+  ARRAY_SLICE_OUT_OF_BOUNDS = 4,
+
+  /* String slice out of bounds.  */
+  STRING_SLICE_OUT_OF_BOUNDS = 5,
+
+  /* Dereference of nil pointer.  This is used when there is a
+     dereference of a pointer to a very large struct or array, to
+     ensure that a gigantic array is not used a proxy to access random
+     memory locations.  */
+  NIL_DEREFERENCE = 6,
+
+  /* Slice length or capacity out of bounds in make: negative or
+     overflow or length greater than capacity.  */
+  MAKE_SLICE_OUT_OF_BOUNDS = 7,
+
+  /* Map capacity out of bounds in make: negative or overflow.  */
+  MAKE_MAP_OUT_OF_BOUNDS = 8,
+
+  /* Channel capacity out of bounds in make: negative or overflow.  */
+  MAKE_CHAN_OUT_OF_BOUNDS = 9
+};
+
+extern void __go_runtime_error () __attribute__ ((noreturn));
+
+void
+__go_runtime_error (int i)
+{
+  switch (i)
+    {
+    case SLICE_INDEX_OUT_OF_BOUNDS:
+    case ARRAY_INDEX_OUT_OF_BOUNDS:
+    case STRING_INDEX_OUT_OF_BOUNDS:
+      __go_panic_msg ("index out of range");
+
+    case SLICE_SLICE_OUT_OF_BOUNDS:
+    case ARRAY_SLICE_OUT_OF_BOUNDS:
+    case STRING_SLICE_OUT_OF_BOUNDS:
+      __go_panic_msg ("slice bounds out of range");
+
+    case NIL_DEREFERENCE:
+      __go_panic_msg ("nil pointer dereference");
+
+    case MAKE_SLICE_OUT_OF_BOUNDS:
+      __go_panic_msg ("make slice len or cap out of range");
+
+    case MAKE_MAP_OUT_OF_BOUNDS:
+      __go_panic_msg ("make map len out of range");
+
+    case MAKE_CHAN_OUT_OF_BOUNDS:
+      __go_panic_msg ("make chan len out of range");
+
+    default:
+      __go_panic_msg ("unknown runtime error");
+    }
+}
diff -r 30ec2dd46cc2 libgo/runtime/go-signal.c
--- a/libgo/runtime/go-signal.c	Thu Sep 09 14:58:38 2010 -0700
+++ b/libgo/runtime/go-signal.c	Thu Sep 09 20:30:35 2010 -0700
@@ -115,7 +115,7 @@ 
 
 #ifdef SIGFPE
     case SIGFPE:
-      msg = "division by zero or floating point error";
+      msg = "integer divide by zero or floating point error";
       break;
 #endif