diff gcc/go/gofrontend/expressions.cc @ 131:84e7813d76e9

gcc-8.2
author mir3636
date Thu, 25 Oct 2018 07:37:49 +0900
parents 04ced10e8804
children 1830386684a0
line wrap: on
line diff
--- a/gcc/go/gofrontend/expressions.cc	Fri Oct 27 22:46:09 2017 +0900
+++ b/gcc/go/gofrontend/expressions.cc	Thu Oct 25 07:37:49 2018 +0900
@@ -131,6 +131,40 @@
   this->do_determine_type(&context);
 }
 
+// Return true if two expressions refer to the same variable or struct
+// field.  This can only be true when there are no side effects.
+
+bool
+Expression::is_same_variable(Expression* a, Expression* b)
+{
+  if (a->classification() != b->classification())
+    return false;
+
+  Var_expression* av = a->var_expression();
+  if (av != NULL)
+    return av->named_object() == b->var_expression()->named_object();
+
+  Field_reference_expression* af = a->field_reference_expression();
+  if (af != NULL)
+    {
+      Field_reference_expression* bf = b->field_reference_expression();
+      return (af->field_index() == bf->field_index()
+	      && Expression::is_same_variable(af->expr(), bf->expr()));
+    }
+
+  Unary_expression* au = a->unary_expression();
+  if (au != NULL)
+    {
+      Unary_expression* bu = b->unary_expression();
+      return (au->op() == OPERATOR_MULT
+	      && bu->op() == OPERATOR_MULT
+	      && Expression::is_same_variable(au->operand(),
+					      bu->operand()));
+    }
+
+  return false;
+}
+
 // Return an expression handling any conversions which must be done during
 // assignment.
 
@@ -144,7 +178,10 @@
       || rhs->is_error_expression())
     return Expression::make_error(location);
 
-  bool are_identical = Type::are_identical(lhs_type, rhs_type, false, NULL);
+  bool are_identical = Type::are_identical(lhs_type, rhs_type,
+					   (Type::COMPARE_ERRORS
+					    | Type::COMPARE_TAGS),
+					   NULL);
   if (!are_identical && lhs_type->interface_type() != NULL)
     {
       if (rhs_type->interface_type() == NULL)
@@ -290,7 +327,7 @@
       Expression::make_interface_info(rhs, INTERFACE_INFO_METHODS, location);
 
   Expression* descriptor =
-      Expression::make_unary(OPERATOR_MULT, mtable, location);
+      Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, location);
   descriptor = Expression::make_field_reference(descriptor, 0, location);
   Expression* nil = Expression::make_nil(location);
 
@@ -307,7 +344,9 @@
                                            bool for_type_guard,
                                            Location location)
 {
-  if (Type::are_identical(lhs_type, rhs->type(), false, NULL))
+  if (Type::are_identical(lhs_type, rhs->type(),
+			  Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+			  NULL))
     return rhs;
 
   Interface_type* lhs_interface_type = lhs_type->interface_type();
@@ -393,7 +432,8 @@
     {
       obj = Expression::make_unsafe_cast(Type::make_pointer_type(lhs_type), obj,
                                          location);
-      obj = Expression::make_unary(OPERATOR_MULT, obj, location);
+      obj = Expression::make_dereference(obj, NIL_CHECK_NOT_NEEDED,
+                                         location);
     }
   return Expression::make_compound(check_iface, obj, location);
 }
@@ -771,7 +811,7 @@
     go_unreachable();
 
   Bexpression* ret =
-      context->backend()->var_expression(bvar, this->in_lvalue_pos_, loc);
+      context->backend()->var_expression(bvar, loc);
   if (is_in_heap)
     ret = context->backend()->indirect_expression(btype, ret, true, loc);
   return ret;
@@ -782,7 +822,7 @@
 void
 Var_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
 {
-  ast_dump_context->ostream() << this->variable_->name() ;
+  ast_dump_context->ostream() << this->variable_->message_name() ;
 }
 
 // Make a reference to a variable in an expression.
@@ -858,7 +898,7 @@
 void
 Enclosed_var_expression::do_dump_expression(Ast_dump_context* adc) const
 {
-  adc->ostream() << this->variable_->name();
+  adc->ostream() << this->variable_->message_name();
 }
 
 // Make a reference to a variable within an enclosing function.
@@ -898,10 +938,7 @@
 {
   Gogo* gogo = context->gogo();
   Bvariable* bvar = this->statement_->get_backend_variable(context);
-  Varexpr_context ve_ctxt = (this->is_lvalue_ ? VE_lvalue : VE_rvalue);
-
-  Bexpression* ret = gogo->backend()->var_expression(bvar, ve_ctxt,
-                                                     this->location());
+  Bexpression* ret = gogo->backend()->var_expression(bvar, this->location());
 
   // The backend can't always represent the same set of recursive types
   // that the Go frontend can.  In some cases this means that a
@@ -972,7 +1009,7 @@
   Location loc = this->location();
   Gogo* gogo = context->gogo();
   Bvariable* bvar = this->statement_->get_backend_variable(context);
-  Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, VE_lvalue, loc);
+  Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, loc);
 
   Named_object* fn = context->function();
   go_assert(fn != NULL);
@@ -980,7 +1017,7 @@
   Bexpression* bexpr = this->expr_->get_backend(context);
   Bstatement* set = gogo->backend()->assignment_statement(bfn, lvar_ref,
                                                           bexpr, loc);
-  Bexpression* var_ref = gogo->backend()->var_expression(bvar, VE_rvalue, loc);
+  Bexpression* var_ref = gogo->backend()->var_expression(bvar, loc);
   Bexpression* ret = gogo->backend()->compound_expression(set, var_ref, loc);
   return ret;
 }
@@ -1084,11 +1121,11 @@
 	gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL,
 					    false, loc, &decl);
       Bexpression* var_ref =
-          gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
+          gogo->backend()->var_expression(this->bvar_, loc);
       var_ref = gogo->backend()->compound_expression(decl, var_ref, loc);
       return var_ref;
     }
-  return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
+  return gogo->backend()->var_expression(this->bvar_, loc);
 }
 
 // Ast dump for sink expression.
@@ -1302,7 +1339,7 @@
   Named_object* no = this->fn_;
   Location loc = no->location();
   if (this->dvar_ != NULL)
-    return context->backend()->var_expression(this->dvar_, VE_rvalue, loc);
+    return context->backend()->var_expression(this->dvar_, loc);
 
   Gogo* gogo = context->gogo();
   std::string var_name(gogo->function_descriptor_name(no));
@@ -1312,6 +1349,16 @@
       && Linemap::is_predeclared_location(no->location()))
     is_descriptor = true;
 
+  // The runtime package implements some functions defined in the
+  // syscall package.  Let the syscall package define the descriptor
+  // in this case.
+  if (gogo->compiling_runtime()
+      && gogo->package_name() == "runtime"
+      && no->is_function()
+      && !no->func_value()->asm_name().empty()
+      && no->func_value()->asm_name().compare(0, 8, "syscall.") == 0)
+    is_descriptor = true;
+
   Btype* btype = this->type()->get_backend(gogo);
 
   Bvariable* bvar;
@@ -1322,9 +1369,24 @@
   else
     {
       Location bloc = Linemap::predeclared_location();
+
+      // The runtime package has hash/equality functions that are
+      // referenced by type descriptors outside of the runtime, so the
+      // function descriptors must be visible even though they are not
+      // exported.
+      bool is_exported_runtime = false;
+      if (gogo->compiling_runtime()
+	  && gogo->package_name() == "runtime"
+	  && (no->name().find("hash") != std::string::npos
+	      || no->name().find("equal") != std::string::npos))
+	is_exported_runtime = true;
+
       bool is_hidden = ((no->is_function()
 			 && no->func_value()->enclosing() != NULL)
+			|| (Gogo::is_hidden_name(no->name())
+			    && !is_exported_runtime)
 			|| Gogo::is_thunk(no));
+
       bvar = context->backend()->immutable_struct(var_name, asm_name,
                                                   is_hidden, false,
 						  btype, bloc);
@@ -1340,7 +1402,7 @@
     }
 
   this->dvar_ = bvar;
-  return gogo->backend()->var_expression(bvar, VE_rvalue, loc);
+  return gogo->backend()->var_expression(bvar, loc);
 }
 
 // Print a function descriptor expression.
@@ -1920,10 +1982,16 @@
   do_copy()
   {
     if (this->is_character_constant_)
-      return Expression::make_character(&this->val_, this->type_,
+      return Expression::make_character(&this->val_,
+					(this->type_ == NULL
+					 ? NULL
+					 : this->type_->copy_expressions()),
 					this->location());
     else
-      return Expression::make_integer_z(&this->val_, this->type_,
+      return Expression::make_integer_z(&this->val_,
+					(this->type_ == NULL
+					 ? NULL
+					 : this->type_->copy_expressions()),
 					this->location());
   }
 
@@ -2315,7 +2383,10 @@
 
   Expression*
   do_copy()
-  { return Expression::make_float(&this->val_, this->type_,
+  { return Expression::make_float(&this->val_,
+				  (this->type_ == NULL
+				   ? NULL
+				   : this->type_->copy_expressions()),
 				  this->location()); }
 
   Bexpression*
@@ -2506,7 +2577,10 @@
   Expression*
   do_copy()
   {
-    return Expression::make_complex(&this->val_, this->type_,
+    return Expression::make_complex(&this->val_,
+				    (this->type_ == NULL
+				     ? NULL
+				     : this->type_->copy_expressions()),
 				    this->location());
   }
 
@@ -2839,8 +2913,16 @@
 
   if (this->seen_ || nc->lowering())
     {
-      this->report_error(_("constant refers to itself"));
+      if (nc->type() == NULL || !nc->type()->is_error_type())
+	{
+	  Location loc = this->location();
+	  if (!this->seen_)
+	    loc = nc->location();
+	  go_error_at(loc, "constant refers to itself");
+	}
+      this->set_is_error();
       this->type_ = Type::make_error_type();
+      nc->set_type(this->type_);
       return this->type_;
     }
 
@@ -2860,6 +2942,9 @@
 
   this->seen_ = false;
 
+  if (ret->is_error_type())
+    nc->set_type(ret);
+
   return ret;
 }
 
@@ -3309,7 +3394,9 @@
   if (!this->expr_->is_static_initializer())
     return false;
 
-  if (Type::are_identical(type, expr_type, false, NULL))
+  if (Type::are_identical(type, expr_type,
+			  Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+			  NULL))
     return true;
 
   if (type->is_string_type() && expr_type->is_string_type())
@@ -3401,6 +3488,16 @@
   this->set_is_error();
 }
 
+// Copy.
+
+Expression*
+Type_conversion_expression::do_copy()
+{
+  return new Type_conversion_expression(this->type_->copy_expressions(),
+					this->expr_->copy(),
+					this->location());
+}
+
 // Get the backend representation for a type conversion.
 
 Bexpression*
@@ -3413,7 +3510,9 @@
   Btype* btype = type->get_backend(gogo);
   Location loc = this->location();
 
-  if (Type::are_identical(type, expr_type, false, NULL))
+  if (Type::are_identical(type, expr_type,
+			  Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+			  NULL))
     {
       Bexpression* bexpr = this->expr_->get_backend(context);
       return gogo->backend()->convert_expression(btype, bexpr, loc);
@@ -3602,6 +3701,16 @@
   return false;
 }
 
+// Copy.
+
+Expression*
+Unsafe_type_conversion_expression::do_copy()
+{
+  return new Unsafe_type_conversion_expression(this->type_->copy_expressions(),
+					       this->expr_->copy(),
+					       this->location());
+}
+
 // Convert to backend representation.
 
 Bexpression*
@@ -3702,7 +3811,7 @@
 // called after escape analysis but before inserting write barriers.
 
 void
-Unary_expression::check_operand_address_taken(Gogo* gogo)
+Unary_expression::check_operand_address_taken(Gogo*)
 {
   if (this->op_ != OPERATOR_AND)
     return;
@@ -3716,13 +3825,6 @@
   if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
     this->escapes_ = false;
 
-  // When compiling the runtime, the address operator does not cause
-  // local variables to escape.  When escape analysis becomes the
-  // default, this should be changed to make it an error if we have an
-  // address operator that escapes.
-  if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
-    this->escapes_ = false;
-
   Named_object* var = NULL;
   if (this->expr_->var_expression() != NULL)
     var = this->expr_->var_expression()->named_object();
@@ -3845,24 +3947,20 @@
       && !this->expr_->is_variable())
     {
       go_assert(this->expr_->type()->points_to() != NULL);
-      Type* ptype = this->expr_->type()->points_to();
-      if (!ptype->is_void_type())
+      switch (this->requires_nil_check(gogo))
         {
-          int64_t s;
-          bool ok = ptype->backend_type_size(gogo, &s);
-          if (!ok)
+          case NIL_CHECK_ERROR_ENCOUNTERED:
             {
               go_assert(saw_errors());
               return Expression::make_error(this->location());
             }
-          if (s >= 4096 || this->issue_nil_check_)
-            {
-              Temporary_statement* temp =
-                  Statement::make_temporary(NULL, this->expr_, location);
-              inserter->insert(temp);
-              this->expr_ =
-                  Expression::make_temporary_reference(temp, location);
-            }
+          case NIL_CHECK_NOT_NEEDED:
+            break;
+          case NIL_CHECK_NEEDED:
+            this->create_temp_ = true;
+            break;
+          case NIL_CHECK_DEFAULT:
+            go_unreachable();
         }
     }
 
@@ -3963,6 +4061,41 @@
   return false;
 }
 
+// Return whether this dereference expression requires an explicit nil
+// check. If we are dereferencing the pointer to a large struct
+// (greater than the specified size threshold), we need to check for
+// nil. We don't bother to check for small structs because we expect
+// the system to crash on a nil pointer dereference. However, if we
+// know the address of this expression is being taken, we must always
+// check for nil.
+Unary_expression::Nil_check_classification
+Unary_expression::requires_nil_check(Gogo* gogo) 
+{
+  go_assert(this->op_ == OPERATOR_MULT);
+  go_assert(this->expr_->type()->points_to() != NULL);
+
+  if (this->issue_nil_check_ == NIL_CHECK_NEEDED)
+    return NIL_CHECK_NEEDED;
+  else if (this->issue_nil_check_ == NIL_CHECK_NOT_NEEDED)
+    return NIL_CHECK_NOT_NEEDED;
+
+  Type* ptype = this->expr_->type()->points_to();
+  int64_t type_size = -1;
+  if (!ptype->is_void_type())
+    {
+      bool ok = ptype->backend_type_size(gogo, &type_size);
+      if (!ok)
+        return NIL_CHECK_ERROR_ENCOUNTERED;
+    }
+
+  int64_t size_cutoff = gogo->nil_check_size_threshold();
+  if (size_cutoff == -1 || (type_size != -1 && type_size >= size_cutoff))
+    this->issue_nil_check_ = NIL_CHECK_NEEDED;
+  else
+    this->issue_nil_check_ = NIL_CHECK_NOT_NEEDED;
+  return this->issue_nil_check_;
+}
+
 // Apply unary opcode OP to UNC, setting NC.  Return true if this
 // could be done, false if not.  On overflow, issues an error and sets
 // *ISSUED_ERROR.
@@ -4286,7 +4419,7 @@
 	  Temporary_statement* temp = sut->temporary();
 	  Bvariable* bvar = temp->get_backend_variable(context);
           Bexpression* bvar_expr =
-              gogo->backend()->var_expression(bvar, VE_lvalue, loc);
+              gogo->backend()->var_expression(bvar, loc);
           Bexpression* bval = sut->expression()->get_backend(context);
 
           Named_object* fn = context->function();
@@ -4363,7 +4496,8 @@
 	      // initialize the value once, so we can use this directly
 	      // rather than copying it.  In that case we can't make it
 	      // read-only, because the program is permitted to change it.
-	      copy_to_heap = context->function() != NULL;
+	      copy_to_heap = (context->function() != NULL
+                              || context->is_const());
 	    }
 	  std::string asm_name(go_selectively_encode_id(var_name));
 	  Bvariable* implicit =
@@ -4373,7 +4507,7 @@
 	  gogo->backend()->implicit_variable_set_init(implicit, var_name, btype,
 						      true, copy_to_heap, false,
 						      bexpr);
-	  bexpr = gogo->backend()->var_expression(implicit, VE_rvalue, loc);
+	  bexpr = gogo->backend()->var_expression(implicit, loc);
 
 	  // If we are not copying a slice initializer to the heap,
 	  // then it can be changed by the program, so if it can
@@ -4383,7 +4517,7 @@
 	      && this->expr_->type()->has_pointer())
 	    {
 	      Bexpression* root =
-                  gogo->backend()->var_expression(implicit, VE_rvalue, loc);
+                  gogo->backend()->var_expression(implicit, loc);
 	      root = gogo->backend()->address_expression(root, loc);
 	      Type* type = Type::make_pointer_type(this->expr_->type());
 	      gogo->add_gc_root(Expression::make_backend(root, type, loc));
@@ -4400,7 +4534,7 @@
                                                 true, false, btype, loc);
           gogo->backend()->immutable_struct_set_init(decl, var_name, true,
 						     false, btype, loc, bexpr);
-          bexpr = gogo->backend()->var_expression(decl, VE_rvalue, loc);
+          bexpr = gogo->backend()->var_expression(decl, loc);
         }
 
       go_assert(!this->create_temp_ || this->expr_->is_variable());
@@ -4411,43 +4545,55 @@
       {
         go_assert(this->expr_->type()->points_to() != NULL);
 
-	// If we are dereferencing the pointer to a large struct, we
-	// need to check for nil.  We don't bother to check for small
-	// structs because we expect the system to crash on a nil
-	// pointer dereference.	 However, if we know the address of this
-	// expression is being taken, we must always check for nil.
-
+        bool known_valid = false;
         Type* ptype = this->expr_->type()->points_to();
         Btype* pbtype = ptype->get_backend(gogo);
-        if (!ptype->is_void_type())
-	  {
-            int64_t s;
-            bool ok = ptype->backend_type_size(gogo, &s);
-            if (!ok)
+        switch (this->requires_nil_check(gogo))
+          {
+            case NIL_CHECK_NOT_NEEDED:
+              break;
+            case NIL_CHECK_ERROR_ENCOUNTERED:
               {
                 go_assert(saw_errors());
                 return gogo->backend()->error_expression();
               }
-	    if (s >= 4096 || this->issue_nil_check_)
-	      {
+            case NIL_CHECK_NEEDED:
+              {
                 go_assert(this->expr_->is_variable());
+
+                // If we're nil-checking the result of a set-and-use-temporary
+                // expression, then pick out the target temp and use that
+                // for the final result of the conditional.
+                Bexpression* tbexpr = bexpr;
+                Bexpression* ubexpr = bexpr;
+                Set_and_use_temporary_expression* sut =
+                    this->expr_->set_and_use_temporary_expression();
+                if (sut != NULL) {
+                  Temporary_statement* temp = sut->temporary();
+                  Bvariable* bvar = temp->get_backend_variable(context);
+                  ubexpr = gogo->backend()->var_expression(bvar, loc);
+                }
                 Bexpression* nil =
-		  Expression::make_nil(loc)->get_backend(context);
+                    Expression::make_nil(loc)->get_backend(context);
                 Bexpression* compare =
-                    gogo->backend()->binary_expression(OPERATOR_EQEQ, bexpr,
+                    gogo->backend()->binary_expression(OPERATOR_EQEQ, tbexpr,
                                                        nil, loc);
                 Bexpression* crash =
-		  gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
-				      loc)->get_backend(context);
+                    gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
+                                        loc)->get_backend(context);
                 Bfunction* bfn = context->function()->func_value()->get_decl();
                 bexpr = gogo->backend()->conditional_expression(bfn, btype,
                                                                 compare,
-                                                                crash, bexpr,
+                                                                crash, ubexpr,
                                                                 loc);
-
-	      }
-	  }
-        ret = gogo->backend()->indirect_expression(pbtype, bexpr, false, loc);
+                known_valid = true;
+                break;
+              }
+            case NIL_CHECK_DEFAULT:
+              go_unreachable();
+          }
+        ret = gogo->backend()->indirect_expression(pbtype, bexpr,
+                                                   known_valid, loc);
       }
       break;
 
@@ -4532,6 +4678,19 @@
   return new Unary_expression(op, expr, location);
 }
 
+Expression*
+Expression::make_dereference(Expression* ptr,
+                             Nil_check_classification docheck,
+                             Location location)
+{
+  Expression* deref = Expression::make_unary(OPERATOR_MULT, ptr, location);
+  if (docheck == NIL_CHECK_NEEDED)
+    deref->unary_expression()->set_requires_nil_check(true);
+  else if (docheck == NIL_CHECK_NOT_NEEDED)
+    deref->unary_expression()->set_requires_nil_check(false);
+  return deref;
+}
+
 // If this is an indirection through a pointer, return the expression
 // being pointed through.  Otherwise return this.
 
@@ -5283,7 +5442,10 @@
   Struct_type* st2 = this->right_->type()->struct_type();
   if (st2 == NULL)
     return this;
-  if (st != st2 && !Type::are_identical(st, st2, false, NULL))
+  if (st != st2
+      && !Type::are_identical(st, st2,
+			      Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+			      NULL))
     return this;
   if (!Type::are_compatible_for_comparison(true, this->left_->type(),
 					   this->right_->type(), NULL))
@@ -5362,7 +5524,10 @@
   Array_type* at2 = this->right_->type()->array_type();
   if (at2 == NULL)
     return this;
-  if (at != at2 && !Type::are_identical(at, at2, false, NULL))
+  if (at != at2
+      && !Type::are_identical(at, at2,
+			      Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+			      NULL))
     return this;
   if (!Type::are_compatible_for_comparison(true, this->left_->type(),
 					   this->right_->type(), NULL))
@@ -6752,7 +6917,7 @@
 		 ? this->expr_type_
 		 : this->expr_->type());
   etype = etype->deref();
-  if (!Type::are_identical(rtype, etype, true, NULL))
+  if (!Type::are_identical(rtype, etype, Type::COMPARE_TAGS, NULL))
     this->report_error(_("method type does not match object type"));
 }
 
@@ -6798,7 +6963,8 @@
 
   if (orig_fntype == NULL || !orig_fntype->is_method())
     {
-      ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name());
+      ins.first->second =
+	Named_object::make_erroneous_name(gogo->thunk_name());
       return ins.first->second;
     }
 
@@ -6806,8 +6972,8 @@
   // The type here is wrong--it should be the C function type.  But it
   // doesn't really matter.
   Type* vt = Type::make_pointer_type(Type::make_void_type());
-  sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
-  sfl->push_back(Struct_field(Typed_identifier("val.1",
+  sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc)));
+  sfl->push_back(Struct_field(Typed_identifier("val",
 					       orig_fntype->receiver()->type(),
 					       loc)));
   Struct_type* st = Type::make_struct_type(sfl, loc);
@@ -6816,7 +6982,7 @@
 
   Function_type* new_fntype = orig_fntype->copy_with_names();
 
-  std::string thunk_name = Gogo::thunk_name();
+  std::string thunk_name = gogo->thunk_name();
   Named_object* new_no = gogo->start_function(thunk_name, new_fntype,
 					      false, loc);
 
@@ -6832,7 +6998,7 @@
   // Field 0 of the closure is the function code pointer, field 1 is
   // the value on which to invoke the method.
   Expression* arg = Expression::make_var_reference(cp, loc);
-  arg = Expression::make_unary(OPERATOR_MULT, arg, loc);
+  arg = Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED, loc);
   arg = Expression::make_field_reference(arg, 1, loc);
 
   Expression* bme = Expression::make_bound_method(arg, method, fn, loc);
@@ -6896,7 +7062,8 @@
 					      Expression::make_nil(loc),
 					      loc);
       cond = Expression::make_binary(OPERATOR_OROR, cond, n, loc);
-      *ref = Expression::make_unary(OPERATOR_MULT, *ref, loc);
+      *ref = Expression::make_dereference(*ref, Expression::NIL_CHECK_DEFAULT,
+                                          loc);
       go_assert((*ref)->type()->struct_type() == stype);
     }
   *ref = Expression::make_field_reference(*ref, field_indexes->field_index,
@@ -6951,7 +7118,7 @@
   Expression* val = expr;
   if (fntype->receiver()->type()->points_to() == NULL
       && val->type()->points_to() != NULL)
-    val = Expression::make_unary(OPERATOR_MULT, val, loc);
+    val = Expression::make_dereference(val, NIL_CHECK_DEFAULT, loc);
 
   // Note that we are ignoring this->expr_type_ here.  The thunk will
   // expect a closure whose second field has type this->expr_type_ (if
@@ -6961,10 +7128,10 @@
   // away with this.
 
   Struct_field_list* fields = new Struct_field_list();
-  fields->push_back(Struct_field(Typed_identifier("fn.0",
+  fields->push_back(Struct_field(Typed_identifier("fn",
 						  thunk->func_value()->type(),
 						  loc)));
-  fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc)));
+  fields->push_back(Struct_field(Typed_identifier("val", val->type(), loc)));
   Struct_type* st = Type::make_struct_type(fields, loc);
   st->set_is_struct_incomparable();
 
@@ -6973,21 +7140,14 @@
   vals->push_back(val);
 
   Expression* ret = Expression::make_struct_composite_literal(st, vals, loc);
-
-  if (!gogo->compiling_runtime() || gogo->package_name() != "runtime")
-    ret = Expression::make_heap_expression(ret, loc);
-  else
-    {
-      // When compiling the runtime, method closures do not escape.
-      // When escape analysis becomes the default, and applies to
-      // method closures, this should be changed to make it an error
-      // if a method closure escapes.
-      Temporary_statement* ctemp = Statement::make_temporary(st, ret, loc);
-      inserter->insert(ctemp);
-      ret = Expression::make_temporary_reference(ctemp, loc);
-      ret = Expression::make_unary(OPERATOR_AND, ret, loc);
-      ret->unary_expression()->set_does_not_escape();
-    }
+  ret = Expression::make_heap_expression(ret, loc);
+
+  Node* n = Node::make_node(this);
+  if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
+    ret->heap_expression()->set_allocate_on_stack();
+  else if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
+    go_error_at(loc, "%s escapes to heap, not allowed in runtime",
+                n->ast_format(gogo).c_str());
 
   // If necessary, check whether the expression or any embedded
   // pointers are nil.
@@ -7058,114 +7218,6 @@
 // Class Builtin_call_expression.  This is used for a call to a
 // builtin function.
 
-class Builtin_call_expression : public Call_expression
-{
- public:
-  Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
-			  bool is_varargs, Location location);
-
- protected:
-  // This overrides Call_expression::do_lower.
-  Expression*
-  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
-
-  Expression*
-  do_flatten(Gogo*, Named_object*, Statement_inserter*);
-
-  bool
-  do_is_constant() const;
-
-  bool
-  do_numeric_constant_value(Numeric_constant*) const;
-
-  bool
-  do_discarding_value();
-
-  Type*
-  do_type();
-
-  void
-  do_determine_type(const Type_context*);
-
-  void
-  do_check_types(Gogo*);
-
-  Expression*
-  do_copy();
-
-  Bexpression*
-  do_get_backend(Translate_context*);
-
-  void
-  do_export(Export*) const;
-
-  virtual bool
-  do_is_recover_call() const;
-
-  virtual void
-  do_set_recover_arg(Expression*);
-
- private:
-  // The builtin functions.
-  enum Builtin_function_code
-    {
-      BUILTIN_INVALID,
-
-      // Predeclared builtin functions.
-      BUILTIN_APPEND,
-      BUILTIN_CAP,
-      BUILTIN_CLOSE,
-      BUILTIN_COMPLEX,
-      BUILTIN_COPY,
-      BUILTIN_DELETE,
-      BUILTIN_IMAG,
-      BUILTIN_LEN,
-      BUILTIN_MAKE,
-      BUILTIN_NEW,
-      BUILTIN_PANIC,
-      BUILTIN_PRINT,
-      BUILTIN_PRINTLN,
-      BUILTIN_REAL,
-      BUILTIN_RECOVER,
-
-      // Builtin functions from the unsafe package.
-      BUILTIN_ALIGNOF,
-      BUILTIN_OFFSETOF,
-      BUILTIN_SIZEOF
-    };
-
-  Expression*
-  one_arg() const;
-
-  bool
-  check_one_arg();
-
-  static Type*
-  real_imag_type(Type*);
-
-  static Type*
-  complex_type(Type*);
-
-  Expression*
-  lower_make(Statement_inserter*);
-
-  Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*);
-
-  bool
-  check_int_value(Expression*, bool is_length, bool* small);
-
-  // A pointer back to the general IR structure.  This avoids a global
-  // variable, or passing it around everywhere.
-  Gogo* gogo_;
-  // The builtin function being called.
-  Builtin_function_code code_;
-  // Used to stop endless loops when the length of an array uses len
-  // or cap of the array itself.
-  mutable bool seen_;
-  // Whether the argument is set for calls to BUILTIN_RECOVER.
-  bool recover_arg_is_set_;
-};
-
 Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
 						 Expression* fn,
 						 Expression_list* args,
@@ -7372,7 +7424,32 @@
 								  loc);
 	    Expression* e3 = Expression::make_temporary_reference(key_temp,
 								  loc);
-	    e3 = Expression::make_unary(OPERATOR_AND, e3, loc);
+
+	    // If the call to delete is deferred, and is in a loop,
+	    // then the loop will only have a single instance of the
+	    // temporary variable.  Passing the address of the
+	    // temporary variable here means that the deferred call
+	    // will see the last value in the loop, not the current
+	    // value.  So for this unusual case copy the value into
+	    // the heap.
+	    if (!this->is_deferred())
+	      e3 = Expression::make_unary(OPERATOR_AND, e3, loc);
+	    else
+	      {
+		Expression* a = Expression::make_allocation(mt->key_type(),
+							    loc);
+		Temporary_statement* atemp =
+		  Statement::make_temporary(NULL, a, loc);
+		inserter->insert(atemp);
+
+		a = Expression::make_temporary_reference(atemp, loc);
+		a = Expression::make_dereference(a, NIL_CHECK_NOT_NEEDED, loc);
+		Statement* s = Statement::make_assignment(a, e3, loc);
+		inserter->insert(s);
+
+		e3 = Expression::make_temporary_reference(atemp, loc);
+	      }
+
 	    return Runtime::make_call(Runtime::MAPDELETE, this->location(),
 				      3, e1, e2, e3);
 	  }
@@ -7418,7 +7495,7 @@
       break;
 
     case BUILTIN_APPEND:
-      return this->flatten_append(gogo, function, inserter);
+      return this->flatten_append(gogo, function, inserter, NULL, NULL);
 
     case BUILTIN_COPY:
       {
@@ -7536,11 +7613,17 @@
 	  return Expression::make_error(this->location());
 	}
       len_arg = Expression::make_integer_ul(0, NULL, loc);
+      len_small = true;
     }
   else
     {
       len_arg = *parg;
       len_arg->determine_type(&int_context);
+      if (len_arg->type()->integer_type() == NULL)
+	{
+	  go_error_at(len_arg->location(), "non-integer len argument in make");
+	  return Expression::make_error(this->location());
+	}
       if (!this->check_int_value(len_arg, true, &len_small))
 	return Expression::make_error(this->location());
       ++parg;
@@ -7548,17 +7631,22 @@
 
   Expression* cap_arg = NULL;
   bool cap_small = false;
+  Numeric_constant nclen;
+  Numeric_constant nccap;
+  unsigned long vlen;
+  unsigned long vcap;
   if (is_slice && parg != args->end())
     {
       cap_arg = *parg;
       cap_arg->determine_type(&int_context);
+      if (cap_arg->type()->integer_type() == NULL)
+	{
+	  go_error_at(cap_arg->location(), "non-integer cap argument in make");
+	  return Expression::make_error(this->location());
+	}
       if (!this->check_int_value(cap_arg, false, &cap_small))
 	return Expression::make_error(this->location());
 
-      Numeric_constant nclen;
-      Numeric_constant nccap;
-      unsigned long vlen;
-      unsigned long vcap;
       if (len_arg->numeric_constant_value(&nclen)
 	  && cap_arg->numeric_constant_value(&nccap)
 	  && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
@@ -7583,19 +7671,25 @@
   Expression* call;
   if (is_slice)
     {
+      if (cap_arg == NULL)
+	{
+          cap_small = len_small;
+          if (len_arg->numeric_constant_value(&nclen)
+              && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID)
+            cap_arg = Expression::make_integer_ul(vlen, len_arg->type(), loc);
+          else
+            {
+              Temporary_statement* temp = Statement::make_temporary(NULL,
+                                                                    len_arg,
+                                                                    loc);
+              inserter->insert(temp);
+              len_arg = Expression::make_temporary_reference(temp, loc);
+              cap_arg = Expression::make_temporary_reference(temp, loc);
+            }
+	}
+
       Type* et = type->array_type()->element_type();
       Expression* type_arg = Expression::make_type_descriptor(et, type_loc);
-      if (cap_arg == NULL)
-	{
-	  Temporary_statement* temp = Statement::make_temporary(NULL,
-								len_arg,
-								loc);
-	  inserter->insert(temp);
-	  len_arg = Expression::make_temporary_reference(temp, loc);
-	  cap_arg = Expression::make_temporary_reference(temp, loc);
-	  cap_small = len_small;
-	}
-
       Runtime::Function code = Runtime::MAKESLICE;
       if (!len_small || !cap_small)
 	code = Runtime::MAKESLICE64;
@@ -7604,14 +7698,31 @@
   else if (is_map)
     {
       Expression* type_arg = Expression::make_type_descriptor(type, type_loc);
-      call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg,
-				Expression::make_nil(loc),
-				Expression::make_nil(loc));
+      if (!len_small)
+	call = Runtime::make_call(Runtime::MAKEMAP64, loc, 3, type_arg,
+				  len_arg,
+				  Expression::make_nil(loc));
+      else
+	{
+	  Numeric_constant nclen;
+	  unsigned long vlen;
+	  if (len_arg->numeric_constant_value(&nclen)
+	      && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
+	      && vlen <= Map_type::bucket_size)
+	    call = Runtime::make_call(Runtime::MAKEMAP_SMALL, loc, 0);
+	  else
+	    call = Runtime::make_call(Runtime::MAKEMAP, loc, 3, type_arg,
+				      len_arg,
+				      Expression::make_nil(loc));
+	}
     }
   else if (is_chan)
     {
       Expression* type_arg = Expression::make_type_descriptor(type, type_loc);
-      call = Runtime::make_call(Runtime::MAKECHAN, loc, 2, type_arg, len_arg);
+      Runtime::Function code = Runtime::MAKECHAN;
+      if (!len_small)
+	code = Runtime::MAKECHAN64;
+      call = Runtime::make_call(code, loc, 2, type_arg, len_arg);
     }
   else
     go_unreachable();
@@ -7621,11 +7732,18 @@
 
 // Flatten a call to the predeclared append function.  We do this in
 // the flatten phase, not the lowering phase, so that we run after
-// type checking and after order_evaluations.
+// type checking and after order_evaluations.  If ASSIGN_LHS is not
+// NULL, this append is the right-hand-side of an assignment and
+// ASSIGN_LHS is the left-hand-side; in that case, set LHS directly
+// rather than returning a slice.  This lets us omit a write barrier
+// in common cases like a = append(a, ...) when the slice does not
+// need to grow.  ENCLOSING is not NULL iff ASSIGN_LHS is not NULL.
 
 Expression*
 Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
-					Statement_inserter* inserter)
+					Statement_inserter* inserter,
+					Expression* assign_lhs,
+					Block* enclosing)
 {
   if (this->is_error_expression())
     return this;
@@ -7642,6 +7760,8 @@
   if (args->size() == 1)
     {
       // append(s) evaluates to s.
+      if (assign_lhs != NULL)
+	return NULL;
       return args->front();
     }
 
@@ -7758,14 +7878,46 @@
   // FIXME: Mark this index as not requiring bounds checks.
   ref = Expression::make_index(ref, zero, ref2, NULL, loc);
 
-  Expression* rhs = Expression::make_conditional(cond, call, ref, loc);
-
-  gogo->lower_expression(function, inserter, &rhs);
-  gogo->flatten_expression(function, inserter, &rhs);
-
-  Expression* lhs = Expression::make_temporary_reference(s1tmp, loc);
-  Statement* assign = Statement::make_assignment(lhs, rhs, loc);
-  inserter->insert(assign);
+  if (assign_lhs == NULL)
+    {
+      Expression* rhs = Expression::make_conditional(cond, call, ref, loc);
+
+      gogo->lower_expression(function, inserter, &rhs);
+      gogo->flatten_expression(function, inserter, &rhs);
+
+      ref = Expression::make_temporary_reference(s1tmp, loc);
+      Statement* assign = Statement::make_assignment(ref, rhs, loc);
+      inserter->insert(assign);
+    }
+  else
+    {
+      gogo->lower_expression(function, inserter, &cond);
+      gogo->flatten_expression(function, inserter, &cond);
+      gogo->lower_expression(function, inserter, &call);
+      gogo->flatten_expression(function, inserter, &call);
+      gogo->lower_expression(function, inserter, &ref);
+      gogo->flatten_expression(function, inserter, &ref);
+
+      Block* then_block = new Block(enclosing, loc);
+      Assignment_statement* assign =
+	Statement::make_assignment(assign_lhs, call, loc);
+      then_block->add_statement(assign);
+
+      Block* else_block = new Block(enclosing, loc);
+      assign = Statement::make_assignment(assign_lhs->copy(), ref, loc);
+      // This assignment will not change the pointer value, so it does
+      // not need a write barrier.
+      assign->set_omit_write_barrier();
+      else_block->add_statement(assign);
+
+      Statement* s = Statement::make_if_statement(cond, then_block,
+						  else_block, loc);
+      inserter->insert(s);
+
+      ref = Expression::make_temporary_reference(s1tmp, loc);
+      assign = Statement::make_assignment(ref, assign_lhs->copy(), loc);
+      inserter->insert(assign);
+    }
 
   if (this->is_varargs())
     {
@@ -7802,12 +7954,17 @@
 	  Expression* off = Expression::make_integer_ul(i, int_type, loc);
 	  ref2 = Expression::make_binary(OPERATOR_PLUS, ref2, off, loc);
 	  // FIXME: Mark this index as not requiring bounds checks.
-	  lhs = Expression::make_index(ref, ref2, NULL, NULL, loc);
+	  Expression* lhs = Expression::make_index(ref, ref2, NULL, NULL,
+						   loc);
 	  gogo->lower_expression(function, inserter, &lhs);
 	  gogo->flatten_expression(function, inserter, &lhs);
 	  // The flatten pass runs after the write barrier pass, so we
 	  // need to insert a write barrier here if necessary.
-	  if (!gogo->assign_needs_write_barrier(lhs))
+	  // However, if ASSIGN_LHS is not NULL, we have been called
+	  // directly before the write barrier pass.
+	  Statement* assign;
+	  if (assign_lhs != NULL
+	      || !gogo->assign_needs_write_barrier(lhs))
 	    assign = Statement::make_assignment(lhs, *pa, loc);
 	  else
 	    {
@@ -7819,6 +7976,9 @@
 	}
     }
 
+  if (assign_lhs != NULL)
+    return NULL;
+
   return Expression::make_temporary_reference(s1tmp, loc);
 }
 
@@ -7968,8 +8128,10 @@
 int
 Find_call_expression::expression(Expression** pexpr)
 {
-  if ((*pexpr)->call_expression() != NULL
-      || (*pexpr)->receive_expression() != NULL)
+  Expression* expr = *pexpr;
+  if (!expr->is_constant()
+      && (expr->call_expression() != NULL
+	  || expr->receive_expression() != NULL))
     {
       this->found_ = true;
       return TRAVERSE_EXIT;
@@ -7977,6 +8139,24 @@
   return TRAVERSE_CONTINUE;
 }
 
+// Return whether calling len or cap on EXPR, of array type, is a
+// constant.  The language spec says "the expressions len(s) and
+// cap(s) are constants if the type of s is an array or pointer to an
+// array and the expression s does not contain channel receives or
+// (non-constant) function calls."
+
+bool
+Builtin_call_expression::array_len_is_constant(Expression* expr)
+{
+  go_assert(expr->type()->deref()->array_type() != NULL
+	    && !expr->type()->deref()->is_slice_type());
+  if (expr->is_constant())
+    return true;
+  Find_call_expression find_call;
+  Expression::traverse(&expr, &find_call);
+  return !find_call.found();
+}
+
 // Return whether this is constant: len of a string constant, or len
 // or cap of an array, or unsafe.Sizeof, unsafe.Offsetof,
 // unsafe.Alignof.
@@ -8004,20 +8184,14 @@
 	    && !arg_type->points_to()->is_slice_type())
 	  arg_type = arg_type->points_to();
 
-	// The len and cap functions are only constant if there are no
-	// function calls or channel operations in the arguments.
-	// Otherwise we have to make the call.
-	if (!arg->is_constant())
-	  {
-	    Find_call_expression find_call;
-	    Expression::traverse(&arg, &find_call);
-	    if (find_call.found())
-	      return false;
-	  }
-
 	if (arg_type->array_type() != NULL
 	    && arg_type->array_type()->length() != NULL)
-	  return true;
+          {
+	    this->seen_ = true;
+	    bool ret = Builtin_call_expression::array_len_is_constant(arg);
+	    this->seen_ = false;
+	    return ret;
+          }
 
 	if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
 	  {
@@ -8178,6 +8352,11 @@
             return false;
           if (st->named_type() != NULL)
             st->named_type()->convert(this->gogo_);
+          if (st->is_error_type())
+            {
+              go_assert(saw_errors());
+              return false;
+            }
           int64_t offset;
           this->seen_ = true;
           bool ok = st->struct_type()->backend_field_offset(this->gogo_,
@@ -8240,7 +8419,9 @@
 	  && !rnc.type()->is_abstract()
 	  && inc.type() != NULL
 	  && !inc.type()->is_abstract()
-	  && !Type::are_identical(rnc.type(), inc.type(), false, NULL))
+	  && !Type::are_identical(rnc.type(), inc.type(),
+				  Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+				  NULL))
 	return false;
 
       mpfr_t r;
@@ -8327,12 +8508,19 @@
       return Type::make_error_type();
 
     case BUILTIN_NEW:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->empty())
+	  return Type::make_error_type();
+	return Type::make_pointer_type(args->front()->type());
+      }
+
     case BUILTIN_MAKE:
       {
 	const Expression_list* args = this->args();
 	if (args == NULL || args->empty())
 	  return Type::make_error_type();
-	return Type::make_pointer_type(args->front()->type());
+	return args->front()->type();
       }
 
     case BUILTIN_CAP:
@@ -8714,7 +8902,7 @@
 	if (arg2_type->is_slice_type())
 	  {
 	    Type* e2 = arg2_type->array_type()->element_type();
-	    if (!Type::are_identical(e1, e2, true, NULL))
+	    if (!Type::are_identical(e1, e2, Type::COMPARE_TAGS, NULL))
 	      this->report_error(_("element types must be the same"));
 	  }
 	else if (arg2_type->is_string_type())
@@ -8852,7 +9040,8 @@
 		 || args->back()->type()->is_error())
 	  this->set_is_error();
 	else if (!Type::are_identical(args->front()->type(),
-				      args->back()->type(), true, NULL))
+				      args->back()->type(),
+				      Type::COMPARE_TAGS, NULL))
 	  this->report_error(_("complex arguments must have identical types"));
 	else if (args->front()->type()->float_type() == NULL)
 	  this->report_error(_("complex arguments must have "
@@ -8878,6 +9067,10 @@
 
   if (this->varargs_are_lowered())
     bce->set_varargs_are_lowered();
+  if (this->is_deferred())
+    bce->set_is_deferred();
+  if (this->is_concurrent())
+    bce->set_is_concurrent();
   return bce;
 }
 
@@ -8922,7 +9115,8 @@
 	    arg_type = arg_type->points_to();
 	    go_assert(arg_type->array_type() != NULL
 		       && !arg_type->is_slice_type());
-            arg = Expression::make_unary(OPERATOR_MULT, arg, location);
+            arg = Expression::make_dereference(arg, NIL_CHECK_DEFAULT,
+                                               location);
 	  }
 
 	Type* int_type = Type::lookup_integer_type("int");
@@ -8956,8 +9150,9 @@
 							  arg, nil, location);
 		Expression* zero = Expression::make_integer_ul(0, int_type,
 							       location);
-		Expression* indir = Expression::make_unary(OPERATOR_MULT,
-							   arg, location);
+                Expression* indir =
+                    Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED,
+                                                 location);
 		val = Expression::make_conditional(cmp, zero, indir, location);
 	      }
 	    else
@@ -8998,8 +9193,9 @@
 							  arg, nil, location);
 		Expression* zero = Expression::make_integer_ul(0, int_type,
 							       location);
-		Expression* indir = Expression::make_unary(OPERATOR_MULT,
-							   parg, location);
+                Expression* indir =
+                    Expression::make_dereference(parg, NIL_CHECK_NOT_NEEDED,
+                                                 location);
 		val = Expression::make_conditional(cmp, zero, indir, location);
 	      }
 	    else
@@ -9457,8 +9653,16 @@
 
   // Recognize a call to a builtin function.
   if (fntype->is_builtin())
-    return new Builtin_call_expression(gogo, this->fn_, this->args_,
-				       this->is_varargs_, loc);
+    {
+      Builtin_call_expression* bce =
+	new Builtin_call_expression(gogo, this->fn_, this->args_,
+				    this->is_varargs_, loc);
+      if (this->is_deferred_)
+	bce->set_is_deferred();
+      if (this->is_concurrent_)
+	bce->set_is_concurrent();
+      return bce;
+    }
 
   // If this call returns multiple results, create a temporary
   // variable to hold them.
@@ -9553,14 +9757,8 @@
   // 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.  The actual
-  // argument to these functions is always the address of a parameter;
-  // we don't need that for the GCC builtin functions, so we just
-  // ignore it.
-  if (gogo->compiling_runtime()
-      && this->args_ != NULL
-      && this->args_->size() == 1
-      && gogo->package_name() == "runtime")
+  // 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
@@ -9568,14 +9766,16 @@
 	  && fe->named_object()->package() == NULL)
 	{
 	  std::string n = Gogo::unpack_hidden_name(fe->named_object()->name());
-	  if (n == "getcallerpc")
+	  if ((this->args_ == NULL || this->args_->size() == 0)
+	      && n == "getcallerpc")
 	    {
 	      static Named_object* builtin_return_address;
 	      return this->lower_to_builtin(&builtin_return_address,
 					    "__builtin_return_address",
 					    0);
 	    }
-	  else if (n == "getcallersp")
+	  else if ((this->args_ == NULL || this->args_->size() == 0)
+		   && n == "getcallersp")
 	    {
 	      static Named_object* builtin_frame_address;
 	      return this->lower_to_builtin(&builtin_frame_address,
@@ -9601,12 +9801,6 @@
 			       Type* varargs_type, size_t param_count,
                                Slice_storage_escape_disp escape_disp)
 {
-  // When compiling the runtime, varargs slices do not escape.  When
-  // escape analysis becomes the default, this should be changed to
-  // make it an error if we have a varargs slice that escapes.
-  if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
-    escape_disp = SLICE_STORAGE_DOES_NOT_ESCAPE;
-
   if (this->varargs_are_lowered_)
     return;
 
@@ -9759,7 +9953,8 @@
       for (; pa != this->args_->end(); ++pa, ++pp)
 	{
 	  go_assert(pp != fntype->parameters()->end());
-	  if (Type::are_identical(pp->type(), (*pa)->type(), true, NULL))
+	  if (Type::are_identical(pp->type(), (*pa)->type(),
+				  Type::COMPARE_TAGS, NULL))
 	    args->push_back(*pa);
 	  else
 	    {
@@ -10077,7 +10272,7 @@
     }
 
   const Typed_identifier_list* parameters = fntype->parameters();
-  if (this->args_ == NULL)
+  if (this->args_ == NULL || this->args_->size() == 0)
     {
       if (parameters != NULL && !parameters->empty())
 	this->report_error(_("not enough arguments"));
@@ -10136,6 +10331,10 @@
 
   if (this->varargs_are_lowered_)
     call->set_varargs_are_lowered();
+  if (this->is_deferred_)
+    call->set_is_deferred();
+  if (this->is_concurrent_)
+    call->set_is_concurrent();
   return call;
 }
 
@@ -10277,7 +10476,7 @@
           Type::make_pointer_type(
               Type::make_pointer_type(Type::make_void_type()));
       fn = Expression::make_unsafe_cast(pfntype, this->fn_, location);
-      fn = Expression::make_unary(OPERATOR_MULT, fn, location);
+      fn = Expression::make_dereference(fn, NIL_CHECK_NOT_NEEDED, location);
     }
   else
     {
@@ -10535,8 +10734,8 @@
 	   && type->points_to()->array_type() != NULL
 	   && !type->points_to()->is_slice_type())
     {
-      Expression* deref = Expression::make_unary(OPERATOR_MULT, left,
-						 location);
+      Expression* deref =
+          Expression::make_dereference(left, NIL_CHECK_DEFAULT, location);
 
       // For an ordinary index into the array, the pointer will be
       // dereferenced.  For a slice it will not--the resulting slice
@@ -10714,7 +10913,7 @@
 // Check types of an array index.
 
 void
-Array_index_expression::do_check_types(Gogo* gogo)
+Array_index_expression::do_check_types(Gogo*)
 {
   Numeric_constant nc;
   unsigned long v;
@@ -10833,19 +11032,24 @@
       if (!this->array_->is_addressable())
 	this->report_error(_("slice of unaddressable value"));
       else
-	{
-	  bool escapes = true;
-
-	  // When compiling the runtime, a slice operation does not
-	  // cause local variables to escape.  When escape analysis
-	  // becomes the default, this should be changed to make it an
-	  // error if we have a slice operation that escapes.
-	  if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
-	    escapes = false;
-
-	  this->array_->address_taken(escapes);
-	}
-    }
+        // Set the array address taken but not escape. The escape
+        // analysis will make it escape to heap when needed.
+        this->array_->address_taken(false);
+    }
+}
+
+// The subexpressions of an array index must be evaluated in order.
+// If this is indexing into an array, rather than a slice, then only
+// the index should be evaluated.  Since this is called for values on
+// the left hand side of an assigment, evaluating the array, meaning
+// copying the array, will cause a different array to be modified.
+
+bool
+Array_index_expression::do_must_eval_subexpressions_in_order(
+    int* skip) const
+{
+  *skip = this->array_->type()->is_slice_type() ? 0 : 1;
+  return true;
 }
 
 // Flatten array indexing by using temporary variables for slices and indexes.
@@ -10921,6 +11125,14 @@
   return this->array_->is_addressable();
 }
 
+void
+Array_index_expression::do_address_taken(bool escapes)
+{
+  // In &x[0], if x is a slice, then x's address is not taken.
+  if (!this->array_->type()->is_slice_type())
+    this->array_->address_taken(escapes);
+}
+
 // Get the backend representation for an array index.
 
 Bexpression*
@@ -11300,7 +11512,8 @@
   Location loc = this->location();
   Expression* string_arg = this->string_;
   if (this->string_->type()->points_to() != NULL)
-    string_arg = Expression::make_unary(OPERATOR_MULT, this->string_, loc);
+    string_arg = Expression::make_dereference(this->string_,
+                                              NIL_CHECK_NOT_NEEDED, loc);
 
   Expression* bad_index = Expression::check_bounds(this->start_, loc);
 
@@ -11436,7 +11649,9 @@
       return Expression::make_error(loc);
     }
 
-  if (!Type::are_identical(mt->key_type(), this->index_->type(), false, NULL))
+  if (!Type::are_identical(mt->key_type(), this->index_->type(),
+			   Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
+			   NULL))
     {
       if (this->index_->type()->interface_type() != NULL
 	  && !this->index_->is_variable())
@@ -11534,8 +11749,9 @@
   go_assert(this->value_pointer_ != NULL
             && this->value_pointer_->is_variable());
 
-  Expression* val = Expression::make_unary(OPERATOR_MULT, this->value_pointer_,
-					   this->location());
+  Expression* val = Expression::make_dereference(this->value_pointer_,
+                                                 NIL_CHECK_NOT_NEEDED,
+                                                 this->location());
   return val->get_backend(context);
 }
 
@@ -11645,7 +11861,7 @@
   Location loc = this->location();
 
   std::string s = "fieldtrack \"";
-  Named_type* nt = this->expr_->type()->named_type();
+  Named_type* nt = this->expr_->type()->unalias()->named_type();
   if (nt == NULL || nt->named_object()->package() == NULL)
     s.append(gogo->pkgpath());
   else
@@ -11771,7 +11987,7 @@
   Expression* ref = this->expr_;
   Location loc = this->location();
   if (ref->type()->points_to() != NULL)
-    ref = Expression::make_unary(OPERATOR_MULT, ref, loc);
+    ref = Expression::make_dereference(ref, NIL_CHECK_DEFAULT, loc);
 
   Expression* mtable =
       Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc);
@@ -11781,7 +11997,8 @@
   unsigned int index;
   const Struct_field* field = mtable_type->find_local_field(name, &index);
   go_assert(field != NULL);
-  mtable = Expression::make_unary(OPERATOR_MULT, mtable, loc);
+
+  mtable = Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, loc);
   return Expression::make_field_reference(mtable, index, loc);
 }
 
@@ -11793,7 +12010,8 @@
 {
   Expression* expr = this->expr_;
   if (expr->type()->points_to() != NULL)
-    expr = Expression::make_unary(OPERATOR_MULT, expr, this->location());
+    expr = Expression::make_dereference(expr, NIL_CHECK_DEFAULT,
+                                        this->location());
   return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT,
                                          this->location());
 }
@@ -11824,10 +12042,9 @@
   if (!this->expr_->is_variable())
     {
       Temporary_statement* temp =
-	Statement::make_temporary(this->expr_->type(), NULL, this->location());
+	Statement::make_temporary(NULL, this->expr_, this->location());
       inserter->insert(temp);
-      this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_,
-							   this->location());
+      this->expr_ = Expression::make_temporary_reference(temp, this->location());
     }
   return this;
 }
@@ -11932,25 +12149,25 @@
 
   const Typed_identifier* method_id = type->find_method(name);
   if (method_id == NULL)
-    return Named_object::make_erroneous_name(Gogo::thunk_name());
+    return Named_object::make_erroneous_name(gogo->thunk_name());
 
   Function_type* orig_fntype = method_id->type()->function_type();
   if (orig_fntype == NULL)
-    return Named_object::make_erroneous_name(Gogo::thunk_name());
+    return Named_object::make_erroneous_name(gogo->thunk_name());
 
   Struct_field_list* sfl = new Struct_field_list();
   // The type here is wrong--it should be the C function type.  But it
   // doesn't really matter.
   Type* vt = Type::make_pointer_type(Type::make_void_type());
-  sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
-  sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc)));
+  sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc)));
+  sfl->push_back(Struct_field(Typed_identifier("val", type, loc)));
   Struct_type* st = Type::make_struct_type(sfl, loc);
   st->set_is_struct_incomparable();
   Type* closure_type = Type::make_pointer_type(st);
 
   Function_type* new_fntype = orig_fntype->copy_with_names();
 
-  std::string thunk_name = Gogo::thunk_name();
+  std::string thunk_name = gogo->thunk_name();
   Named_object* new_no = gogo->start_function(thunk_name, new_fntype,
 					      false, loc);
 
@@ -11966,7 +12183,7 @@
   // Field 0 of the closure is the function code pointer, field 1 is
   // the value on which to invoke the method.
   Expression* arg = Expression::make_var_reference(cp, loc);
-  arg = Expression::make_unary(OPERATOR_MULT, arg, loc);
+  arg = Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED, loc);
   arg = Expression::make_field_reference(arg, 1, loc);
 
   Expression *ifre = Expression::make_interface_field_reference(arg, name,
@@ -12038,10 +12255,10 @@
   Location loc = this->location();
 
   Struct_field_list* fields = new Struct_field_list();
-  fields->push_back(Struct_field(Typed_identifier("fn.0",
+  fields->push_back(Struct_field(Typed_identifier("fn",
 						  thunk->func_value()->type(),
 						  loc)));
-  fields->push_back(Struct_field(Typed_identifier("val.1",
+  fields->push_back(Struct_field(Typed_identifier("val",
 						  this->expr_->type(),
 						  loc)));
   Struct_type* st = Type::make_struct_type(fields, loc);
@@ -12290,7 +12507,7 @@
       return f;
     }
 
-  Named_object* no = gogo->start_function(Gogo::thunk_name(), fntype, false,
+  Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false,
 					  location);
 
   Named_object* vno = gogo->lookup(receiver_name, NULL);
@@ -12395,7 +12612,8 @@
 Allocation_expression::do_copy()
 {
   Allocation_expression* alloc =
-    new Allocation_expression(this->type_, this->location());
+    new Allocation_expression(this->type_->copy_expressions(),
+			      this->location());
   if (this->allocate_on_stack_)
     alloc->set_allocate_on_stack();
   return alloc;
@@ -12408,10 +12626,9 @@
 {
   Gogo* gogo = context->gogo();
   Location loc = this->location();
-
-  Node* n = Node::make_node(this);
-  if (this->allocate_on_stack_
-      || (n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
+  Btype* btype = this->type_->get_backend(gogo);
+
+  if (this->allocate_on_stack_)
     {
       int64_t size;
       bool ok = this->type_->backend_type_size(gogo, &size);
@@ -12420,10 +12637,20 @@
           go_assert(saw_errors());
           return gogo->backend()->error_expression();
         }
-      return gogo->backend()->stack_allocation_expression(size, loc);
-    }
-
-  Btype* btype = this->type_->get_backend(gogo);
+      Bstatement* decl;
+      Named_object* fn = context->function();
+      go_assert(fn != NULL);
+      Bfunction* fndecl = fn->func_value()->get_or_make_decl(gogo, fn);
+      Bexpression* zero = gogo->backend()->zero_expression(btype);
+      Bvariable* temp =
+        gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
+                                            zero, true, loc, &decl);
+      Bexpression* ret = gogo->backend()->var_expression(temp, loc);
+      ret = gogo->backend()->address_expression(ret, loc);
+      ret = gogo->backend()->compound_expression(decl, ret, loc);
+      return ret;
+    }
+
   Bexpression* space =
     gogo->allocate_memory(this->type_, loc)->get_backend(context);
   Btype* pbtype = gogo->backend()->pointer_type(btype);
@@ -12626,6 +12853,22 @@
   go_assert(pv == this->vals()->end());
 }
 
+// Copy.
+
+Expression*
+Struct_construction_expression::do_copy()
+{
+  Struct_construction_expression* ret =
+    new Struct_construction_expression(this->type_->copy_expressions(),
+				       (this->vals() == NULL
+					? NULL
+					: this->vals()->copy()),
+				       this->location());
+  if (this->traverse_order() != NULL)
+    ret->set_traverse_order(this->traverse_order());
+  return ret;
+}
+
 // Flatten a struct construction expression.  Store the values into
 // temporaries in case they need interface conversion.
 
@@ -13017,6 +13260,20 @@
 				  type, indexes, vals, location)
 { go_assert(type->array_type() != NULL && !type->is_slice_type()); }
 
+
+// Copy.
+
+Expression*
+Fixed_array_construction_expression::do_copy()
+{
+  Type* t = this->type()->copy_expressions();
+  return new Fixed_array_construction_expression(t, this->indexes(),
+						 (this->vals() == NULL
+						  ? NULL
+						  : this->vals()->copy()),
+						 this->location());
+}
+
 // Return the backend representation for constructing a fixed array.
 
 Bexpression*
@@ -13151,6 +13408,19 @@
   ast_dump_context->dump_expression(this->slice_storage_);
 }
 
+// Copy.
+
+Expression*
+Slice_construction_expression::do_copy()
+{
+  return new Slice_construction_expression(this->type()->copy_expressions(),
+					   this->indexes(),
+					   (this->vals() == NULL
+					    ? NULL
+					    : this->vals()->copy()),
+					   this->location());
+}
+
 // Return the backend representation for constructing a slice.
 
 Bexpression*
@@ -13187,13 +13457,8 @@
     }
   else
     {
+      go_assert(this->storage_escapes_ || this->element_count() == 0);
       space = Expression::make_heap_expression(this->array_val_, loc);
-      Node* n = Node::make_node(this);
-      if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
-	{
-	  n = Node::make_node(space);
-	  n->set_encoding(Node::ESCAPE_NONE);
-	}
     }
 
   // Build a constructor for the slice.
@@ -13369,6 +13634,18 @@
     }
 }
 
+// Copy.
+
+Expression*
+Map_construction_expression::do_copy()
+{
+  return new Map_construction_expression(this->type_->copy_expressions(),
+					 (this->vals_ == NULL
+					  ? NULL
+					  : this->vals_->copy()),
+					 this->location());
+}
+
 // Return the backend representation for constructing a map.
 
 Bexpression*
@@ -13531,6 +13808,7 @@
 
   for (int depth = 0; depth < this->depth_; ++depth)
     {
+      type = type->deref();
       if (type->array_type() != NULL)
 	type = type->array_type()->element_type();
       else if (type->map_type() != NULL)
@@ -14048,6 +14326,23 @@
   return new Map_construction_expression(type, this->vals_, location);
 }
 
+// Copy.
+
+Expression*
+Composite_literal_expression::do_copy()
+{
+  Composite_literal_expression* ret =
+    new Composite_literal_expression(this->type_->copy_expressions(),
+				     this->depth_, this->has_keys_,
+				     (this->vals_ == NULL
+				      ? NULL
+				      : this->vals_->copy()),
+				     this->all_are_names_,
+				     this->location());
+  ret->key_path_ = this->key_path_;
+  return ret;
+}
+
 // Dump ast representation for a composite literal expression.
 
 void
@@ -14224,6 +14519,16 @@
     }
 }
 
+// Copy.
+
+Expression*
+Type_guard_expression::do_copy()
+{
+  return new Type_guard_expression(this->expr_->copy(),
+				   this->type_->copy_expressions(),
+				   this->location());
+}
+
 // Return the backend representation for a type guard expression.
 
 Bexpression*
@@ -14287,8 +14592,7 @@
   Btype* btype = this->type()->get_backend(gogo);
 
   Expression* alloc = Expression::make_allocation(etype, loc);
-  Node* n = Node::make_node(this);
-  if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
+  if (this->allocate_on_stack_)
     alloc->allocation_expression()->set_allocate_on_stack();
   Bexpression* space = alloc->get_backend(context);
 
@@ -14307,9 +14611,9 @@
   // don't do this in the write barrier pass because in some cases
   // backend conversion can introduce new Heap_expression values.
   Bstatement* assn;
-  if (!etype->has_pointer())
-    {
-      space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc);
+  if (!etype->has_pointer() || this->allocate_on_stack_)
+    {
+      space = gogo->backend()->var_expression(space_temp, loc);
       Bexpression* ref =
 	gogo->backend()->indirect_expression(expr_btype, space, true, loc);
       assn = gogo->backend()->assignment_statement(fndecl, ref, bexpr, loc);
@@ -14322,12 +14626,12 @@
 					    expr_btype, bexpr, true, loc,
 					    &edecl);
       Bexpression* btempref = gogo->backend()->var_expression(btemp,
-							      VE_lvalue, loc);
+							      loc);
       Bexpression* addr = gogo->backend()->address_expression(btempref, loc);
 
       Expression* td = Expression::make_type_descriptor(etype, loc);
       Type* etype_ptr = Type::make_pointer_type(etype);
-      space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc);
+      space = gogo->backend()->var_expression(space_temp, loc);
       Expression* elhs = Expression::make_backend(space, etype_ptr, loc);
       Expression* erhs = Expression::make_backend(addr, etype_ptr, loc);
       Expression* call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,
@@ -14337,7 +14641,7 @@
       assn = gogo->backend()->compound_statement(edecl, s);
     }
   decl = gogo->backend()->compound_statement(decl, assn);
-  space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc);
+  space = gogo->backend()->var_expression(space_temp, loc);
   return gogo->backend()->compound_expression(decl, space, loc);
 }
 
@@ -14661,7 +14965,7 @@
 
   Bvariable* bvar = this->type_->gc_ptrmask_var(gogo, ptrsize, ptrdata);
   Location bloc = Linemap::predeclared_location();
-  Bexpression* bref = gogo->backend()->var_expression(bvar, VE_rvalue, bloc);
+  Bexpression* bref = gogo->backend()->var_expression(bvar, bloc);
   Bexpression* baddr = gogo->backend()->address_expression(bref, bloc);
 
   Type* uint8_type = Type::lookup_integer_type("uint8");
@@ -14952,7 +15256,8 @@
   Expression*
   do_copy()
   {
-    return new Slice_value_expression(this->type_, this->valptr_->copy(),
+    return new Slice_value_expression(this->type_->copy_expressions(),
+				      this->valptr_->copy(),
                                       this->len_->copy(), this->cap_->copy(),
                                       this->location());
   }
@@ -15213,7 +15518,7 @@
   Expression*
   do_copy()
   {
-    return new Interface_value_expression(this->type_,
+    return new Interface_value_expression(this->type_->copy_expressions(),
                                           this->first_field_->copy(),
                                           this->obj_->copy(), this->location());
   }
@@ -15308,7 +15613,9 @@
   Expression*
   do_copy()
   {
-    return new Interface_mtable_expression(this->itype_, this->type_,
+    Interface_type* itype = this->itype_->copy_expressions()->interface_type();
+    return new Interface_mtable_expression(itype,
+					   this->type_->copy_expressions(),
                                            this->is_pointer_, this->location());
   }
 
@@ -15380,8 +15687,7 @@
   Gogo* gogo = context->gogo();
   Location loc = Linemap::predeclared_location();
   if (this->bvar_ != NULL)
-    return gogo->backend()->var_expression(this->bvar_, VE_rvalue,
-                                           this->location());
+    return gogo->backend()->var_expression(this->bvar_, this->location());
 
   const Typed_identifier_list* interface_methods = this->itype_->methods();
   go_assert(!interface_methods->empty());
@@ -15421,8 +15727,7 @@
       this->bvar_ =
           gogo->backend()->immutable_struct_reference(mangled_name, asm_name,
                                                       btype, loc);
-      return gogo->backend()->var_expression(this->bvar_, VE_rvalue,
-                                             this->location());
+      return gogo->backend()->var_expression(this->bvar_, this->location());
     }
 
   // The first element is the type descriptor.
@@ -15487,7 +15792,7 @@
 						  !is_public, btype, loc);
   gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false,
                                              !is_public, btype, loc, ctor);
-  return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
+  return gogo->backend()->var_expression(this->bvar_, loc);
 }
 
 void
@@ -15664,7 +15969,8 @@
 Conditional_expression::do_type()
 {
   Type* result_type = Type::make_void_type();
-  if (Type::are_identical(this->then_->type(), this->else_->type(), false,
+  if (Type::are_identical(this->then_->type(), this->else_->type(),
+			  Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
                           NULL))
     result_type = this->then_->type();
   else if (this->then_->is_nil_expression()
@@ -15798,6 +16104,13 @@
   return TRAVERSE_CONTINUE;
 }
 
+Expression*
+Backend_expression::do_copy()
+{
+  return new Backend_expression(this->bexpr_, this->type_->copy_expressions(),
+				this->location());
+}
+
 void
 Backend_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
 {
@@ -16026,10 +16339,16 @@
   this->clear();
   this->classification_ = NC_FLOAT;
   this->type_ = type;
+
   // Numeric constants do not have negative zero values, so remove
   // them here.  They also don't have infinity or NaN values, but we
   // should never see them here.
-  if (mpfr_zero_p(val))
+  int bits = 0;
+  if (type != NULL
+      && type->float_type() != NULL
+      && !type->float_type()->is_abstract())
+    bits = type->float_type()->bits();
+  if (Numeric_constant::is_float_neg_zero(val, bits))
     mpfr_init_set_ui(this->u_.float_val, 0, GMP_RNDN);
   else
     mpfr_init_set(this->u_.float_val, val, GMP_RNDN);
@@ -16043,8 +16362,60 @@
   this->clear();
   this->classification_ = NC_COMPLEX;
   this->type_ = type;
+
+  // Avoid negative zero as in set_float.
+  int bits = 0;
+  if (type != NULL
+      && type->complex_type() != NULL
+      && !type->complex_type()->is_abstract())
+    bits = type->complex_type()->bits() / 2;
+
+  mpfr_t real;
+  mpfr_init_set(real, mpc_realref(val), GMP_RNDN);
+  if (Numeric_constant::is_float_neg_zero(real, bits))
+    mpfr_set_ui(real, 0, GMP_RNDN);
+
+  mpfr_t imag;
+  mpfr_init_set(imag, mpc_imagref(val), GMP_RNDN);
+  if (Numeric_constant::is_float_neg_zero(imag, bits))
+    mpfr_set_ui(imag, 0, GMP_RNDN);
+
   mpc_init2(this->u_.complex_val, mpc_precision);
-  mpc_set(this->u_.complex_val, val, MPC_RNDNN);
+  mpc_set_fr_fr(this->u_.complex_val, real, imag, MPC_RNDNN);
+
+  mpfr_clear(real);
+  mpfr_clear(imag);
+}
+
+// Return whether VAL, at a precision of BITS, is a negative zero.
+// BITS may be zero in which case it is ignored.
+
+bool
+Numeric_constant::is_float_neg_zero(const mpfr_t val, int bits)
+{
+  if (!mpfr_signbit(val))
+    return false;
+  if (mpfr_zero_p(val))
+    return true;
+  mp_exp_t min_exp;
+  switch (bits)
+    {
+    case 0:
+      return false;
+    case 32:
+      // In a denormalized float32 the exponent is -126, and there are
+      // 24 bits of which at least the last must be 1, so the smallest
+      // representable non-zero exponent is -126 - (24 - 1) == -149.
+      min_exp = -149;
+      break;
+    case 64:
+      // Minimum exponent is -1022, there are 53 bits.
+      min_exp = -1074;
+      break;
+    default:
+      go_unreachable();
+    }
+  return mpfr_get_exp(val) < min_exp;
 }
 
 // Get an int value.