LCOV - code coverage report
Current view: top level - gcc/go/gofrontend - wb.cc (source / functions) Hit Total Coverage
Test: gcc.info Lines: 451 463 97.4 %
Date: 2020-07-04 13:15:35 Functions: 19 19 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // wb.cc -- Add write barriers as needed.
       2                 :            : 
       3                 :            : // Copyright 2017 The Go Authors. All rights reserved.
       4                 :            : // Use of this source code is governed by a BSD-style
       5                 :            : // license that can be found in the LICENSE file.
       6                 :            : 
       7                 :            : #include "go-system.h"
       8                 :            : 
       9                 :            : #include "go-c.h"
      10                 :            : #include "go-diagnostics.h"
      11                 :            : #include "operator.h"
      12                 :            : #include "lex.h"
      13                 :            : #include "types.h"
      14                 :            : #include "expressions.h"
      15                 :            : #include "statements.h"
      16                 :            : #include "runtime.h"
      17                 :            : #include "gogo.h"
      18                 :            : 
      19                 :            : // Mark variables whose addresses are taken and do some other
      20                 :            : // cleanups.  This has to be done before the write barrier pass and
      21                 :            : // after the escape analysis pass.  It would be nice to do this
      22                 :            : // elsewhere but there isn't an obvious place.
      23                 :            : 
      24                 :       6240 : class Mark_address_taken : public Traverse
      25                 :            : {
      26                 :            :  public:
      27                 :       3120 :   Mark_address_taken(Gogo* gogo)
      28                 :       3120 :     : Traverse(traverse_functions
      29                 :            :                | traverse_statements
      30                 :            :                | traverse_expressions),
      31                 :       3120 :       gogo_(gogo), function_(NULL)
      32                 :            :   { }
      33                 :            : 
      34                 :            :   int
      35                 :            :   function(Named_object*);
      36                 :            : 
      37                 :            :   int
      38                 :            :   statement(Block*, size_t*, Statement*);
      39                 :            : 
      40                 :            :   int
      41                 :            :   expression(Expression**);
      42                 :            : 
      43                 :            :  private:
      44                 :            :   // General IR.
      45                 :            :   Gogo* gogo_;
      46                 :            :   // The function we are traversing.
      47                 :            :   Named_object* function_;
      48                 :            : };
      49                 :            : 
      50                 :            : // Record a function.
      51                 :            : 
      52                 :            : int
      53                 :     242128 : Mark_address_taken::function(Named_object* no)
      54                 :            : {
      55                 :     242128 :   go_assert(this->function_ == NULL);
      56                 :     242128 :   this->function_ = no;
      57                 :     242128 :   int t = no->func_value()->traverse(this);
      58                 :     242128 :   this->function_ = NULL;
      59                 :            : 
      60                 :     242128 :   if (t == TRAVERSE_EXIT)
      61                 :          0 :     return t;
      62                 :            :   return TRAVERSE_SKIP_COMPONENTS;
      63                 :            : }
      64                 :            : 
      65                 :            : // Traverse a statement.
      66                 :            : 
      67                 :            : int
      68                 :    4522333 : Mark_address_taken::statement(Block* block, size_t* pindex, Statement* s)
      69                 :            : {
      70                 :            :   // If this is an assignment of the form s = append(s, ...), expand
      71                 :            :   // it now, so that we can assign it to the left hand side in the
      72                 :            :   // middle of the expansion and possibly skip a write barrier.
      73                 :    4522333 :   Assignment_statement* as = s->assignment_statement();
      74                 :     811891 :   if (as != NULL && !as->lhs()->is_sink_expression())
      75                 :            :     {
      76                 :     807336 :       Call_expression* rce = as->rhs()->call_expression();
      77                 :      12349 :       if (rce != NULL
      78                 :      12347 :           && rce->builtin_call_expression() != NULL
      79                 :      12347 :           && (rce->builtin_call_expression()->code()
      80                 :            :               == Builtin_call_expression::BUILTIN_APPEND)
      81                 :      12130 :           && Expression::is_same_variable(as->lhs(), rce->args()->front()))
      82                 :            :         {
      83                 :      10837 :           Statement_inserter inserter = Statement_inserter(block, pindex);
      84                 :      10837 :           Expression* a =
      85                 :      10837 :             rce->builtin_call_expression()->flatten_append(this->gogo_,
      86                 :            :                                                            this->function_,
      87                 :            :                                                            &inserter,
      88                 :            :                                                            as->lhs(),
      89                 :            :                                                            block);
      90                 :      10837 :           go_assert(a == NULL);
      91                 :            :           // That does the assignment, so remove this statement.
      92                 :      10837 :           Expression* e = Expression::make_boolean(true, s->location());
      93                 :      10837 :           Statement* dummy = Statement::make_statement(e, true);
      94                 :      10837 :           block->replace_statement(*pindex, dummy);
      95                 :            :         }
      96                 :            :     }
      97                 :    4522333 :   return TRAVERSE_CONTINUE;
      98                 :            : }
      99                 :            : 
     100                 :            : // Mark variable addresses taken.
     101                 :            : 
     102                 :            : int
     103                 :   11566404 : Mark_address_taken::expression(Expression** pexpr)
     104                 :            : {
     105                 :   11566404 :   Expression* expr = *pexpr;
     106                 :   11566404 :   Unary_expression* ue = expr->unary_expression();
     107                 :     709066 :   if (ue != NULL)
     108                 :     709066 :     ue->check_operand_address_taken(this->gogo_);
     109                 :            : 
     110                 :   11566404 :   Array_index_expression* aie = expr->array_index_expression();
     111                 :     164627 :   if (aie != NULL
     112                 :     164627 :       && aie->end() != NULL
     113                 :      35104 :       && !aie->array()->type()->is_slice_type())
     114                 :            :     {
     115                 :            :       // Slice of an array. The escape analysis models this with
     116                 :            :       // a child Node representing the address of the array.
     117                 :      12908 :       bool escapes = false;
     118                 :      12908 :       Node* n = Node::make_node(expr);
     119                 :      12908 :       if (n->child() == NULL
     120                 :      12908 :           || (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
     121                 :            :         escapes = true;
     122                 :      12908 :       aie->array()->address_taken(escapes);
     123                 :            :     }
     124                 :            : 
     125                 :   11566404 :   if (expr->allocation_expression() != NULL)
     126                 :            :     {
     127                 :       6753 :       Node* n = Node::make_node(expr);
     128                 :       6753 :       if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
     129                 :       1286 :         expr->allocation_expression()->set_allocate_on_stack();
     130                 :            :     }
     131                 :   11566404 :   if (expr->heap_expression() != NULL)
     132                 :            :     {
     133                 :      41402 :       Node* n = Node::make_node(expr);
     134                 :      41402 :       if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
     135                 :       5850 :         expr->heap_expression()->set_allocate_on_stack();
     136                 :            :     }
     137                 :   11566404 :   if (expr->slice_literal() != NULL)
     138                 :            :     {
     139                 :      86428 :       Node* n = Node::make_node(expr);
     140                 :      86428 :       if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
     141                 :      29225 :         expr->slice_literal()->set_storage_does_not_escape();
     142                 :            :     }
     143                 :            : 
     144                 :            :   // Rewrite non-escaping makeslice with constant size to stack allocation.
     145                 :   11566404 :   Slice_value_expression* sve = expr->slice_value_expression();
     146                 :       7386 :   if (sve != NULL)
     147                 :            :     {
     148                 :       7386 :       std::pair<Call_expression*, Temporary_statement*> p =
     149                 :       7386 :         Expression::find_makeslice_call(sve);
     150                 :       7386 :       Call_expression* call = p.first;
     151                 :       7386 :       Temporary_statement* ts = p.second;
     152                 :       7386 :       if (call != NULL
     153                 :       7386 :           && Node::make_node(call)->encoding() == Node::ESCAPE_NONE)
     154                 :            :         {
     155                 :       1386 :           Expression* len_arg = call->args()->at(1);
     156                 :       1386 :           Expression* cap_arg = call->args()->at(2);
     157                 :       2772 :           Numeric_constant nclen;
     158                 :       1386 :           Numeric_constant nccap;
     159                 :       1386 :           unsigned long vlen;
     160                 :       1386 :           unsigned long vcap;
     161                 :       1386 :           if (len_arg->numeric_constant_value(&nclen)
     162                 :        691 :               && cap_arg->numeric_constant_value(&nccap)
     163                 :        510 :               && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
     164                 :       1896 :               && nccap.to_unsigned_long(&vcap) == Numeric_constant::NC_UL_VALID)
     165                 :            :             {
     166                 :            :               // Stack allocate an array and make a slice value from it.
     167                 :        510 :               Location loc = expr->location();
     168                 :       1020 :               Type* elmt_type = expr->type()->array_type()->element_type();
     169                 :        510 :               Expression* len_expr =
     170                 :        510 :                 Expression::make_integer_ul(vcap, cap_arg->type(), loc);
     171                 :        510 :               Type* array_type = Type::make_array_type(elmt_type, len_expr);
     172                 :        510 :               Expression* alloc = Expression::make_allocation(array_type, loc);
     173                 :        510 :               alloc->allocation_expression()->set_allocate_on_stack();
     174                 :        510 :               Type* ptr_type = Type::make_pointer_type(elmt_type);
     175                 :        510 :               Expression* ptr = Expression::make_unsafe_cast(ptr_type, alloc,
     176                 :            :                                                              loc);
     177                 :        510 :               Expression* slice =
     178                 :        510 :                 Expression::make_slice_value(expr->type(), ptr, len_arg,
     179                 :            :                                              cap_arg, loc);
     180                 :        510 :               *pexpr = slice;
     181                 :        510 :               if (ts != NULL && ts->uses() == 1)
     182                 :        510 :                 ts->set_init(Expression::make_nil(loc));
     183                 :            :             }
     184                 :            :         }
     185                 :            :     }
     186                 :   11566404 :   return TRAVERSE_CONTINUE;
     187                 :            : }
     188                 :            : 
     189                 :            : // Check variables and closures do not escape when compiling runtime.
     190                 :            : 
     191                 :         14 : class Check_escape : public Traverse
     192                 :            : {
     193                 :            :  public:
     194                 :          7 :   Check_escape(Gogo* gogo)
     195                 :          7 :     : Traverse(traverse_expressions | traverse_variables),
     196                 :          7 :       gogo_(gogo)
     197                 :            :   { }
     198                 :            : 
     199                 :            :   int
     200                 :            :   expression(Expression**);
     201                 :            : 
     202                 :            :   int
     203                 :            :   variable(Named_object*);
     204                 :            : 
     205                 :            :  private:
     206                 :            :   Gogo* gogo_;
     207                 :            : };
     208                 :            : 
     209                 :            : int
     210                 :      55367 : Check_escape::variable(Named_object* no)
     211                 :            : {
     212                 :      55575 :   if ((no->is_variable() && no->var_value()->is_in_heap())
     213                 :     101336 :       || (no->is_result_variable()
     214                 :       9398 :           && no->result_var_value()->is_in_heap()))
     215                 :          0 :     go_error_at(no->location(),
     216                 :            :                 "%s escapes to heap, not allowed in runtime",
     217                 :          0 :                 no->message_name().c_str());
     218                 :      55367 :   return TRAVERSE_CONTINUE;
     219                 :            : }
     220                 :            : 
     221                 :            : int
     222                 :     752801 : Check_escape::expression(Expression** pexpr)
     223                 :            : {
     224                 :     752801 :   Expression* expr = *pexpr;
     225                 :     752801 :   Func_expression* fe = expr->func_expression();
     226                 :      55306 :   if (fe != NULL && fe->closure() != NULL)
     227                 :            :     {
     228                 :        369 :       Node* n = Node::make_node(expr);
     229                 :        369 :       if (n->encoding() == Node::ESCAPE_HEAP)
     230                 :          0 :         go_error_at(expr->location(),
     231                 :            :                     "heap-allocated closure, not allowed in runtime");
     232                 :            :     }
     233                 :     752801 :   return TRAVERSE_CONTINUE;
     234                 :            : }
     235                 :            : 
     236                 :            : // Collect all writebarrierrec functions.  This is used when compiling
     237                 :            : // the runtime package, to propagate //go:nowritebarrierrec.
     238                 :            : 
     239                 :         14 : class Collect_writebarrierrec_functions : public Traverse
     240                 :            : {
     241                 :            :  public:
     242                 :          7 :   Collect_writebarrierrec_functions(std::vector<Named_object*>* worklist)
     243                 :          7 :     : Traverse(traverse_functions),
     244                 :          7 :       worklist_(worklist)
     245                 :            :   { }
     246                 :            : 
     247                 :            :  private:
     248                 :            :   int
     249                 :            :   function(Named_object*);
     250                 :            : 
     251                 :            :   // The collected functions are put here.
     252                 :            :   std::vector<Named_object*>* worklist_;
     253                 :            : };
     254                 :            : 
     255                 :            : int
     256                 :      14600 : Collect_writebarrierrec_functions::function(Named_object* no)
     257                 :            : {
     258                 :      14600 :   if (no->is_function()
     259                 :      14600 :       && no->func_value()->enclosing() == NULL
     260                 :      28678 :       && (no->func_value()->pragmas() & GOPRAGMA_NOWRITEBARRIERREC) != 0)
     261                 :            :     {
     262                 :        458 :       go_assert((no->func_value()->pragmas() & GOPRAGMA_MARK) == 0);
     263                 :        458 :       this->worklist_->push_back(no);
     264                 :            :     }
     265                 :      14600 :   return TRAVERSE_CONTINUE;
     266                 :            : }
     267                 :            : 
     268                 :            : // Collect all callees of this function.  We only care about locally
     269                 :            : // defined, known, functions.
     270                 :            : 
     271                 :          7 : class Collect_callees : public Traverse
     272                 :            : {
     273                 :            :  public:
     274                 :          7 :   Collect_callees(std::vector<Named_object*>* worklist)
     275                 :          7 :     : Traverse(traverse_expressions),
     276                 :          7 :       worklist_(worklist)
     277                 :          7 :   { }
     278                 :            : 
     279                 :            :  private:
     280                 :            :   int
     281                 :            :   expression(Expression**);
     282                 :            : 
     283                 :            :   // The collected callees are put here.
     284                 :            :   std::vector<Named_object*>* worklist_;
     285                 :            : };
     286                 :            : 
     287                 :            : int
     288                 :     171944 : Collect_callees::expression(Expression** pexpr)
     289                 :            : {
     290                 :     171944 :   Call_expression* ce = (*pexpr)->call_expression();
     291                 :      14952 :   if (ce != NULL)
     292                 :            :     {
     293                 :      14952 :       Func_expression* fe = ce->fn()->func_expression();
     294                 :      14743 :       if (fe != NULL)
     295                 :            :         {
     296                 :      14743 :           Named_object* no = fe->named_object();
     297                 :      14743 :           if (no->package() == NULL && no->is_function())
     298                 :            :             {
     299                 :            :               // The function runtime.systemstack is special, in that
     300                 :            :               // it is a common way to call a function in the runtime:
     301                 :            :               // mark its argument if we can.
     302                 :       9972 :               if (Gogo::unpack_hidden_name(no->name()) != "systemstack")
     303                 :       9895 :                 this->worklist_->push_back(no);
     304                 :         77 :               else if (ce->args()->size() > 0)
     305                 :            :                 {
     306                 :      14820 :                   fe = ce->args()->front()->func_expression();
     307                 :         77 :                   if (fe != NULL)
     308                 :            :                     {
     309                 :         77 :                       no = fe->named_object();
     310                 :         77 :                       if (no->package() == NULL && no->is_function())
     311                 :         77 :                         this->worklist_->push_back(no);
     312                 :            :                     }
     313                 :            :                 }
     314                 :            :             }
     315                 :            :         }
     316                 :            :     }
     317                 :     171944 :   return TRAVERSE_CONTINUE;
     318                 :            : }
     319                 :            : 
     320                 :            : // When compiling the runtime package, propagate //go:nowritebarrierrec
     321                 :            : // annotations.  A function marked as //go:nowritebarrierrec does not
     322                 :            : // permit write barriers, and also all the functions that it calls,
     323                 :            : // recursively, do not permit write barriers.  Except that a
     324                 :            : // //go:yeswritebarrierrec annotation permits write barriers even if
     325                 :            : // called by a //go:nowritebarrierrec function.  Here we turn
     326                 :            : // //go:nowritebarrierrec into //go:nowritebarrier, as appropriate.
     327                 :            : 
     328                 :            : void
     329                 :          7 : Gogo::propagate_writebarrierrec()
     330                 :            : {
     331                 :          7 :   std::vector<Named_object*> worklist;
     332                 :         14 :   Collect_writebarrierrec_functions cwf(&worklist);
     333                 :          7 :   this->traverse(&cwf);
     334                 :            : 
     335                 :         14 :   Collect_callees cc(&worklist);
     336                 :            : 
     337                 :      10437 :   while (!worklist.empty())
     338                 :            :     {
     339                 :      10430 :       Named_object* no = worklist.back();
     340                 :      10430 :       worklist.pop_back();
     341                 :            : 
     342                 :      10430 :       unsigned int pragmas = no->func_value()->pragmas();
     343                 :      10430 :       if ((pragmas & GOPRAGMA_MARK) != 0)
     344                 :            :         {
     345                 :            :           // We've already seen this function.
     346                 :       7256 :           continue;
     347                 :            :         }
     348                 :       3174 :       if ((pragmas & GOPRAGMA_YESWRITEBARRIERREC) != 0)
     349                 :            :         {
     350                 :            :           // We don't want to propagate //go:nowritebarrierrec into
     351                 :            :           // this function or it's callees.
     352                 :        126 :           continue;
     353                 :            :         }
     354                 :            : 
     355                 :       3048 :       no->func_value()->set_pragmas(pragmas
     356                 :            :                                     | GOPRAGMA_NOWRITEBARRIER
     357                 :            :                                     | GOPRAGMA_MARK);
     358                 :            : 
     359                 :       3048 :       no->func_value()->traverse(&cc);
     360                 :            :     }
     361                 :          7 : }
     362                 :            : 
     363                 :            : // Add write barriers to the IR.  This are required by the concurrent
     364                 :            : // garbage collector.  A write barrier is needed for any write of a
     365                 :            : // pointer into memory controlled by the garbage collector.  Write
     366                 :            : // barriers are not required for writes to local variables that live
     367                 :            : // on the stack.  Write barriers are only required when the runtime
     368                 :            : // enables them, which can be checked using a run time check on
     369                 :            : // runtime.writeBarrier.enabled.
     370                 :            : //
     371                 :            : // Essentially, for each assignment A = B, where A is or contains a
     372                 :            : // pointer, and where A is not, or at any rate may not be, a stack
     373                 :            : // variable, we rewrite it into
     374                 :            : //     if runtime.writeBarrier.enabled {
     375                 :            : //         typedmemmove(typeof(A), &A, &B)
     376                 :            : //     } else {
     377                 :            : //         A = B
     378                 :            : //     }
     379                 :            : //
     380                 :            : // The test of runtime.writeBarrier.Enabled is implemented by treating
     381                 :            : // the variable as a *uint32, and testing *runtime.writeBarrier != 0.
     382                 :            : // This is compatible with the definition in the runtime package.
     383                 :            : //
     384                 :            : // For types that are pointer shared (pointers, maps, chans, funcs),
     385                 :            : // we replaced the call to typedmemmove with gcWriteBarrier(&A, B).
     386                 :            : // As far as the GC is concerned, all pointers are the same, so it
     387                 :            : // doesn't need the type descriptor.
     388                 :            : //
     389                 :            : // There are possible optimizations that are not implemented.
     390                 :            : //
     391                 :            : // runtime.writeBarrier can only change when the goroutine is
     392                 :            : // preempted, which in practice means when a call is made into the
     393                 :            : // runtime package, so we could optimize by only testing it once
     394                 :            : // between function calls.
     395                 :            : //
     396                 :            : // A slice could be handled with a call to gcWriteBarrier plus two
     397                 :            : // integer moves.
     398                 :            : 
     399                 :            : // Traverse the IR adding write barriers.
     400                 :            : 
     401                 :            : class Write_barriers : public Traverse
     402                 :            : {
     403                 :            :  public:
     404                 :       3120 :   Write_barriers(Gogo* gogo)
     405                 :       3120 :     : Traverse(traverse_functions
     406                 :            :                | traverse_blocks
     407                 :            :                | traverse_variables
     408                 :            :                | traverse_statements),
     409                 :            :       gogo_(gogo), function_(NULL), statements_added_(),
     410                 :       3120 :       nonwb_pointers_()
     411                 :       3120 :   { }
     412                 :            : 
     413                 :            :   int
     414                 :            :   function(Named_object*);
     415                 :            : 
     416                 :            :   int
     417                 :            :   block(Block*);
     418                 :            : 
     419                 :            :   int
     420                 :            :   variable(Named_object*);
     421                 :            : 
     422                 :            :   int
     423                 :            :   statement(Block*, size_t* pindex, Statement*);
     424                 :            : 
     425                 :            :  private:
     426                 :            :   // General IR.
     427                 :            :   Gogo* gogo_;
     428                 :            :   // Current function.
     429                 :            :   Function* function_;
     430                 :            :   // Statements introduced.
     431                 :            :   Statement_inserter::Statements statements_added_;
     432                 :            :   // Within a single block, pointer variables that point to values
     433                 :            :   // that do not need write barriers.
     434                 :            :   Unordered_set(const Named_object*) nonwb_pointers_;
     435                 :            : };
     436                 :            : 
     437                 :            : // Traverse a function.  Just record it for later.
     438                 :            : 
     439                 :            : int
     440                 :     242128 : Write_barriers::function(Named_object* no)
     441                 :            : {
     442                 :     242128 :   go_assert(this->function_ == NULL);
     443                 :     242128 :   this->function_ = no->func_value();
     444                 :     242128 :   int t = this->function_->traverse(this);
     445                 :     242128 :   this->function_ = NULL;
     446                 :            : 
     447                 :     242128 :   if (t == TRAVERSE_EXIT)
     448                 :          0 :     return t;
     449                 :            :   return TRAVERSE_SKIP_COMPONENTS;
     450                 :            : }
     451                 :            : 
     452                 :            : // Traverse a block.  Clear anything we know about local pointer
     453                 :            : // variables.
     454                 :            : 
     455                 :            : int
     456                 :    1840150 : Write_barriers::block(Block*)
     457                 :            : {
     458                 :    1840150 :   this->nonwb_pointers_.clear();
     459                 :    1840150 :   return TRAVERSE_CONTINUE;
     460                 :            : }
     461                 :            : 
     462                 :            : // Insert write barriers for a global variable: ensure that variable
     463                 :            : // initialization is handled correctly.  This is rarely needed, since
     464                 :            : // we currently don't enable background GC until after all global
     465                 :            : // variables are initialized.  But we do need this if an init function
     466                 :            : // calls runtime.GC.
     467                 :            : 
     468                 :            : int
     469                 :    1149536 : Write_barriers::variable(Named_object* no)
     470                 :            : {
     471                 :            :   // We handle local variables in the variable declaration statement.
     472                 :            :   // We only have to handle global variables here.
     473                 :    1149536 :   if (!no->is_variable())
     474                 :            :     return TRAVERSE_CONTINUE;
     475                 :     951692 :   Variable* var = no->var_value();
     476                 :     951692 :   if (!var->is_global())
     477                 :            :     return TRAVERSE_CONTINUE;
     478                 :            : 
     479                 :            :   // Nothing to do if there is no initializer.
     480                 :     258958 :   Expression* init = var->init();
     481                 :     258958 :   if (init == NULL)
     482                 :            :     return TRAVERSE_CONTINUE;
     483                 :            : 
     484                 :            :   // Nothing to do for variables that do not contain any pointers.
     485                 :      17445 :   if (!var->type()->has_pointer())
     486                 :            :     return TRAVERSE_CONTINUE;
     487                 :            : 
     488                 :            :   // Nothing to do if the initializer is static.
     489                 :      15318 :   init = Expression::make_cast(var->type(), init, var->location());
     490                 :      29844 :   if (!var->has_pre_init() && init->is_static_initializer())
     491                 :            :     return TRAVERSE_CONTINUE;
     492                 :            : 
     493                 :            :   // Nothing to do for a type that can not be in the heap, or a
     494                 :            :   // pointer to a type that can not be in the heap.
     495                 :      10916 :   if (!var->type()->in_heap())
     496                 :            :     return TRAVERSE_CONTINUE;
     497                 :      10916 :   if (var->type()->points_to() != NULL && !var->type()->points_to()->in_heap())
     498                 :            :     return TRAVERSE_CONTINUE;
     499                 :            : 
     500                 :            :   // Otherwise change the initializer into a pre_init assignment
     501                 :            :   // statement with a write barrier.
     502                 :            : 
     503                 :            :   // We can't check for a dependency of the variable on itself after
     504                 :            :   // we make this change, because the preinit statement will always
     505                 :            :   // depend on the variable (since it assigns to it).  So check for a
     506                 :            :   // self-dependency now.
     507                 :      10916 :   this->gogo_->check_self_dep(no);
     508                 :            : 
     509                 :            :   // Replace the initializer.
     510                 :      10916 :   Location loc = init->location();
     511                 :      10916 :   Expression* ref = Expression::make_var_reference(no, loc);
     512                 :            : 
     513                 :      10916 :   Statement_inserter inserter(this->gogo_, var, &this->statements_added_);
     514                 :      10916 :   Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter,
     515                 :      10916 :                                                         ref, init, loc);
     516                 :      10916 :   this->statements_added_.insert(s);
     517                 :            : 
     518                 :      10916 :   var->add_preinit_statement(this->gogo_, s);
     519                 :      10916 :   var->clear_init();
     520                 :            : 
     521                 :      10916 :   return TRAVERSE_CONTINUE;
     522                 :            : }
     523                 :            : 
     524                 :            : // Insert write barriers for statements.
     525                 :            : 
     526                 :            : int
     527                 :    4679849 : Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
     528                 :            : {
     529                 :    4679849 :   if (this->statements_added_.find(s) != this->statements_added_.end())
     530                 :            :     return TRAVERSE_SKIP_COMPONENTS;
     531                 :            : 
     532                 :    4640273 :   switch (s->classification())
     533                 :            :     {
     534                 :            :     default:
     535                 :            :       break;
     536                 :            : 
     537                 :     307543 :     case Statement::STATEMENT_VARIABLE_DECLARATION:
     538                 :     307543 :       {
     539                 :     307543 :         Variable_declaration_statement* vds =
     540                 :     307543 :           s->variable_declaration_statement();
     541                 :     307543 :         Named_object* no = vds->var();
     542                 :     307543 :         Variable* var = no->var_value();
     543                 :            : 
     544                 :            :         // We may need to emit a write barrier for the initialization
     545                 :            :         // of the variable.
     546                 :            : 
     547                 :            :         // Nothing to do for a variable with no initializer.
     548                 :     307543 :         Expression* init = var->init();
     549                 :     307543 :         if (init == NULL)
     550                 :            :           break;
     551                 :            : 
     552                 :            :         // Nothing to do if the variable is not in the heap.  Only
     553                 :            :         // local variables get declaration statements, and local
     554                 :            :         // variables on the stack do not require write barriers.
     555                 :     239599 :         if (!var->is_in_heap())
     556                 :            :           {
     557                 :            :             // If this is a pointer variable, and assigning through
     558                 :            :             // the initializer does not require a write barrier,
     559                 :            :             // record that fact.
     560                 :     233459 :             if (var->type()->points_to() != NULL
     561                 :     233459 :                 && this->gogo_->is_nonwb_pointer(init, &this->nonwb_pointers_))
     562                 :     302381 :               this->nonwb_pointers_.insert(no);
     563                 :            : 
     564                 :            :             break;
     565                 :            :           }
     566                 :            : 
     567                 :            :         // Nothing to do if the variable does not contain any pointers.
     568                 :       6140 :         if (!var->type()->has_pointer())
     569                 :            :           break;
     570                 :            : 
     571                 :            :         // Nothing to do for a type that can not be in the heap, or a
     572                 :            :         // pointer to a type that can not be in the heap.
     573                 :       5162 :         if (!var->type()->in_heap())
     574                 :            :           break;
     575                 :       5162 :         if (var->type()->points_to() != NULL
     576                 :       5162 :             && !var->type()->points_to()->in_heap())
     577                 :            :           break;
     578                 :            : 
     579                 :            :         // Otherwise initialize the variable with a write barrier.
     580                 :            : 
     581                 :       5162 :         Function* function = this->function_;
     582                 :       5162 :         Location loc = init->location();
     583                 :       5162 :         Statement_inserter inserter(block, pindex, &this->statements_added_);
     584                 :            : 
     585                 :            :         // Insert the variable declaration statement with no
     586                 :            :         // initializer, so that the variable exists.
     587                 :       5162 :         var->clear_init();
     588                 :       5162 :         inserter.insert(s);
     589                 :            : 
     590                 :            :         // Create a statement that initializes the variable with a
     591                 :            :         // write barrier.
     592                 :       5162 :         Expression* ref = Expression::make_var_reference(no, loc);
     593                 :       5162 :         Statement* assign = this->gogo_->assign_with_write_barrier(function,
     594                 :            :                                                                    block,
     595                 :            :                                                                    &inserter,
     596                 :            :                                                                    ref, init,
     597                 :       5162 :                                                                    loc);
     598                 :       5162 :         this->statements_added_.insert(assign);
     599                 :            : 
     600                 :            :         // Replace the old variable declaration statement with the new
     601                 :            :         // initialization.
     602                 :       5162 :         block->replace_statement(*pindex, assign);
     603                 :            :       }
     604                 :       5162 :       break;
     605                 :            : 
     606                 :     843238 :     case Statement::STATEMENT_ASSIGNMENT:
     607                 :     843238 :       {
     608                 :     843238 :         Assignment_statement* as = s->assignment_statement();
     609                 :            : 
     610                 :     843238 :         Expression* lhs = as->lhs();
     611                 :     843238 :         Expression* rhs = as->rhs();
     612                 :            : 
     613                 :            :         // Keep track of variables whose values do not escape.
     614                 :     843238 :         Var_expression* lhsve = lhs->var_expression();
     615                 :     542704 :         if (lhsve != NULL && lhsve->type()->points_to() != NULL)
     616                 :            :           {
     617                 :      39855 :             Named_object* no = lhsve->named_object();
     618                 :      39855 :             if (this->gogo_->is_nonwb_pointer(rhs, &this->nonwb_pointers_))
     619                 :        906 :               this->nonwb_pointers_.insert(no);
     620                 :            :             else
     621                 :      78804 :               this->nonwb_pointers_.erase(no);
     622                 :            :           }
     623                 :            : 
     624                 :     843238 :         if (as->omit_write_barrier())
     625                 :            :           break;
     626                 :            : 
     627                 :            :         // We may need to emit a write barrier for the assignment.
     628                 :            : 
     629                 :     829505 :         if (!this->gogo_->assign_needs_write_barrier(lhs,
     630                 :            :                                                      &this->nonwb_pointers_))
     631                 :            :           break;
     632                 :            : 
     633                 :            :         // Change the assignment to use a write barrier.
     634                 :      46250 :         Function* function = this->function_;
     635                 :      46250 :         Location loc = as->location();
     636                 :      46250 :         Statement_inserter inserter =
     637                 :      46250 :             Statement_inserter(block, pindex, &this->statements_added_);
     638                 :      46250 :         Statement* assign = this->gogo_->assign_with_write_barrier(function,
     639                 :            :                                                                    block,
     640                 :            :                                                                    &inserter,
     641                 :            :                                                                    lhs, rhs,
     642                 :      46250 :                                                                    loc);
     643                 :      46250 :         this->statements_added_.insert(assign);
     644                 :      46250 :         block->replace_statement(*pindex, assign);
     645                 :            :       }
     646                 :      46250 :       break;
     647                 :            :     }
     648                 :            : 
     649                 :            :   return TRAVERSE_CONTINUE;
     650                 :            : }
     651                 :            : 
     652                 :            : // The write barrier pass.
     653                 :            : 
     654                 :            : void
     655                 :       3378 : Gogo::add_write_barriers()
     656                 :            : {
     657                 :       3378 :   if (saw_errors())
     658                 :        258 :     return;
     659                 :            : 
     660                 :       6240 :   Mark_address_taken mat(this);
     661                 :       3120 :   this->traverse(&mat);
     662                 :            : 
     663                 :       3120 :   if (this->compiling_runtime() && this->package_name() == "runtime")
     664                 :            :     {
     665                 :          7 :       this->propagate_writebarrierrec();
     666                 :            : 
     667                 :         14 :       Check_escape chk(this);
     668                 :          7 :       this->traverse(&chk);
     669                 :            :     }
     670                 :            : 
     671                 :       6240 :   Write_barriers wb(this);
     672                 :       3120 :   this->traverse(&wb);
     673                 :            : }
     674                 :            : 
     675                 :            : // Return the runtime.writeBarrier variable.
     676                 :            : 
     677                 :            : Named_object*
     678                 :     174790 : Gogo::write_barrier_variable()
     679                 :            : {
     680                 :     174790 :   static Named_object* write_barrier_var;
     681                 :     174790 :   if (write_barrier_var == NULL)
     682                 :            :     {
     683                 :       2135 :       Location bloc = Linemap::predeclared_location();
     684                 :            : 
     685                 :       2135 :       Type* bool_type = Type::lookup_bool_type();
     686                 :       2135 :       Array_type* pad_type = Type::make_array_type(this->lookup_global("byte")->type_value(),
     687                 :            :                                                    Expression::make_integer_ul(3, NULL, bloc));
     688                 :       2135 :       Type* uint64_type = Type::lookup_integer_type("uint64");
     689                 :       2135 :       Type* wb_type = Type::make_builtin_struct_type(5,
     690                 :            :                                                      "enabled", bool_type,
     691                 :            :                                                      "pad", pad_type,
     692                 :            :                                                      "needed", bool_type,
     693                 :            :                                                      "cgo", bool_type,
     694                 :            :                                                      "alignme", uint64_type);
     695                 :            : 
     696                 :       2135 :       Variable* var = new Variable(wb_type, NULL,
     697                 :       2135 :                                     true, false, false, bloc);
     698                 :            : 
     699                 :       2135 :       bool add_to_globals;
     700                 :       4270 :       Package* package = this->add_imported_package("runtime", "_", false,
     701                 :            :                                                     "runtime", "runtime",
     702                 :       4270 :                                                     bloc, &add_to_globals);
     703                 :       2135 :       write_barrier_var = Named_object::make_variable("writeBarrier",
     704                 :       4270 :                                                       package, var);
     705                 :            :     }
     706                 :            : 
     707                 :     174790 :   return write_barrier_var;
     708                 :            : }
     709                 :            : 
     710                 :            : // Return whether an assignment that sets LHS needs a write barrier.
     711                 :            : // NONWB_POINTERS is a set of variables that point to values that do
     712                 :            : // not need write barriers.
     713                 :            : 
     714                 :            : bool
     715                 :     835093 : Gogo::assign_needs_write_barrier(
     716                 :            :     Expression* lhs,
     717                 :            :     Unordered_set(const Named_object*)* nonwb_pointers)
     718                 :            : {
     719                 :            :   // Nothing to do if the variable does not contain any pointers.
     720                 :     835093 :   if (!lhs->type()->has_pointer())
     721                 :            :     return false;
     722                 :            : 
     723                 :            :   // An assignment to a field or an array index is handled like an
     724                 :            :   // assignment to the struct.
     725                 :     382828 :   while (true)
     726                 :            :     {
     727                 :            :       // Nothing to do for a type that can not be in the heap, or a
     728                 :            :       // pointer to a type that can not be in the heap.  We check this
     729                 :            :       // at each level of a struct.
     730                 :     382828 :       if (!lhs->type()->in_heap())
     731                 :            :         return false;
     732                 :     382681 :       if (lhs->type()->points_to() != NULL
     733                 :     455004 :           && !lhs->type()->points_to()->in_heap())
     734                 :            :         return false;
     735                 :            : 
     736                 :            :       // For a struct assignment, we don't need a write barrier if all
     737                 :            :       // the field types can not be in the heap.
     738                 :     380221 :       Struct_type* st = lhs->type()->struct_type();
     739                 :      63151 :       if (st != NULL)
     740                 :            :         {
     741                 :      63151 :           bool in_heap = false;
     742                 :      63151 :           const Struct_field_list* fields = st->fields();
     743                 :      91287 :           for (Struct_field_list::const_iterator p = fields->begin();
     744                 :      91287 :                p != fields->end();
     745                 :      91287 :                p++)
     746                 :            :             {
     747                 :      91252 :               Type* ft = p->type();
     748                 :      91252 :               if (!ft->has_pointer())
     749                 :      27465 :                 continue;
     750                 :      63787 :               if (!ft->in_heap())
     751                 :          0 :                 continue;
     752                 :      63787 :               if (ft->points_to() != NULL && !ft->points_to()->in_heap())
     753                 :        671 :                 continue;
     754                 :            :               in_heap = true;
     755                 :            :               break;
     756                 :            :             }
     757                 :      63151 :           if (!in_heap)
     758                 :            :             return false;
     759                 :            :         }
     760                 :            : 
     761                 :     380186 :       Field_reference_expression* fre = lhs->field_reference_expression();
     762                 :      42907 :       if (fre != NULL)
     763                 :            :         {
     764                 :      42907 :           lhs = fre->expr();
     765                 :      42907 :           continue;
     766                 :            :         }
     767                 :            : 
     768                 :     337279 :       Array_index_expression* aie = lhs->array_index_expression();
     769                 :      24574 :       if (aie != NULL
     770                 :      24574 :           && aie->end() == NULL
     771                 :      24574 :           && !aie->array()->type()->is_slice_type())
     772                 :            :         {
     773                 :      12526 :           lhs = aie->array();
     774                 :      12526 :           continue;
     775                 :            :         }
     776                 :            : 
     777                 :     324753 :       break;
     778                 :            :     }
     779                 :            : 
     780                 :            :   // Nothing to do for an assignment to a temporary.
     781                 :     324753 :   if (lhs->temporary_reference_expression() != NULL)
     782                 :            :     return false;
     783                 :            : 
     784                 :            :   // Nothing to do for an assignment to a sink.
     785                 :     282053 :   if (lhs->is_sink_expression())
     786                 :            :     return false;
     787                 :            : 
     788                 :            :   // Nothing to do for an assignment to a local variable that is not
     789                 :            :   // on the heap.
     790                 :     282053 :   Var_expression* ve = lhs->var_expression();
     791                 :     238883 :   if (ve != NULL)
     792                 :            :     {
     793                 :     238883 :       Named_object* no = ve->named_object();
     794                 :     238883 :       if (no->is_variable())
     795                 :            :         {
     796                 :      78997 :           Variable* var = no->var_value();
     797                 :     124648 :           if (!var->is_global() && !var->is_in_heap())
     798                 :            :             return false;
     799                 :            :         }
     800                 :     159886 :       else if (no->is_result_variable())
     801                 :            :         {
     802                 :     159886 :           Result_variable* rvar = no->result_var_value();
     803                 :     159886 :           if (!rvar->is_in_heap())
     804                 :            :             return false;
     805                 :            :         }
     806                 :            :     }
     807                 :            : 
     808                 :            :   // Nothing to do for an assignment to *(convert(&x)) where
     809                 :            :   // x is local variable or a temporary variable.
     810                 :      49236 :   Unary_expression* ue = lhs->unary_expression();
     811                 :      29644 :   if (ue != NULL
     812                 :      29644 :       && ue->op() == OPERATOR_MULT
     813                 :      29644 :       && this->is_nonwb_pointer(ue->operand(), nonwb_pointers))
     814                 :        278 :     return false;
     815                 :            : 
     816                 :            :   // Write barrier needed in other cases.
     817                 :            :   return true;
     818                 :            : }
     819                 :            : 
     820                 :            : // Return whether EXPR is the address of a variable that can be set
     821                 :            : // without a write barrier.  That is, if this returns true, then an
     822                 :            : // assignment to *EXPR does not require a write barrier.
     823                 :            : // NONWB_POINTERS is a set of variables that point to values that do
     824                 :            : // not need write barriers.
     825                 :            : 
     826                 :            : bool
     827                 :     111228 : Gogo::is_nonwb_pointer(Expression* expr,
     828                 :            :                        Unordered_set(const Named_object*)* nonwb_pointers)
     829                 :            : {
     830                 :     121927 :   while (true)
     831                 :            :     {
     832                 :     121927 :       if (expr->conversion_expression() != NULL)
     833                 :       7944 :         expr = expr->conversion_expression()->expr();
     834                 :     113983 :       else if (expr->unsafe_conversion_expression() != NULL)
     835                 :       2755 :         expr = expr->unsafe_conversion_expression()->expr();
     836                 :            :       else
     837                 :            :         break;
     838                 :            :     }
     839                 :            : 
     840                 :     111228 :   Var_expression* ve = expr->var_expression();
     841                 :     111228 :   if (ve != NULL
     842                 :     111228 :       && nonwb_pointers != NULL
     843                 :     111228 :       && nonwb_pointers->find(ve->named_object()) != nonwb_pointers->end())
     844                 :        232 :     return true;
     845                 :            : 
     846                 :     110996 :   Unary_expression* ue = expr->unary_expression();
     847                 :       4299 :   if (ue == NULL || ue->op() != OPERATOR_AND)
     848                 :            :     return false;
     849                 :       3567 :   if (this->assign_needs_write_barrier(ue->operand(), nonwb_pointers))
     850                 :       1769 :     return false;
     851                 :            :   return true;
     852                 :            : }
     853                 :            : 
     854                 :            : // Return a statement that sets LHS to RHS using a write barrier.
     855                 :            : // ENCLOSING is the enclosing block.
     856                 :            : 
     857                 :            : Statement*
     858                 :      63267 : Gogo::assign_with_write_barrier(Function* function, Block* enclosing,
     859                 :            :                                 Statement_inserter* inserter, Expression* lhs,
     860                 :            :                                 Expression* rhs, Location loc)
     861                 :            : {
     862                 :      63267 :   if (function != NULL && (function->pragmas() & GOPRAGMA_NOWRITEBARRIER) != 0)
     863                 :          0 :     go_error_at(loc, "write barrier prohibited");
     864                 :            : 
     865                 :      63267 :   Type* type = lhs->type();
     866                 :      63267 :   go_assert(type->has_pointer());
     867                 :            : 
     868                 :      63267 :   Expression* addr;
     869                 :      63267 :   if (lhs->unary_expression() != NULL
     870                 :       4541 :       && lhs->unary_expression()->op() == OPERATOR_MULT)
     871                 :       4541 :     addr = lhs->unary_expression()->operand();
     872                 :            :   else
     873                 :            :     {
     874                 :      58726 :       addr = Expression::make_unary(OPERATOR_AND, lhs, loc);
     875                 :      58726 :       addr->unary_expression()->set_does_not_escape();
     876                 :            :     }
     877                 :      63267 :   Temporary_statement* lhs_temp = Statement::make_temporary(NULL, addr, loc);
     878                 :      63267 :   inserter->insert(lhs_temp);
     879                 :      63267 :   lhs = Expression::make_temporary_reference(lhs_temp, loc);
     880                 :            : 
     881                 :      63267 :   if (!Type::are_identical(type, rhs->type(),
     882                 :            :                            Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
     883                 :            :                            NULL)
     884                 :       2757 :       && rhs->type()->interface_type() != NULL
     885                 :      63267 :       && !rhs->is_variable())
     886                 :            :     {
     887                 :            :       // May need a temporary for interface conversion.
     888                 :          0 :       Temporary_statement* temp = Statement::make_temporary(NULL, rhs, loc);
     889                 :          0 :       inserter->insert(temp);
     890                 :          0 :       rhs = Expression::make_temporary_reference(temp, loc);
     891                 :            :     }
     892                 :      63267 :   rhs = Expression::convert_for_assignment(this, type, rhs, loc);
     893                 :      63267 :   Temporary_statement* rhs_temp = NULL;
     894                 :     104388 :   if (!rhs->is_variable() && !rhs->is_constant())
     895                 :            :     {
     896                 :      38300 :       rhs_temp = Statement::make_temporary(NULL, rhs, loc);
     897                 :      38300 :       inserter->insert(rhs_temp);
     898                 :      38300 :       rhs = Expression::make_temporary_reference(rhs_temp, loc);
     899                 :            :     }
     900                 :            : 
     901                 :      63267 :   Expression* indir =
     902                 :      63267 :       Expression::make_dereference(lhs, Expression::NIL_CHECK_DEFAULT, loc);
     903                 :      63267 :   Statement* assign = Statement::make_assignment(indir, rhs, loc);
     904                 :            : 
     905                 :      63267 :   lhs = Expression::make_temporary_reference(lhs_temp, loc);
     906                 :      63267 :   if (rhs_temp != NULL)
     907                 :      38300 :     rhs = Expression::make_temporary_reference(rhs_temp, loc);
     908                 :            : 
     909                 :      63267 :   Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type());
     910                 :      63267 :   lhs = Expression::make_unsafe_cast(unsafe_ptr_type, lhs, loc);
     911                 :            : 
     912                 :      63267 :   Type* uintptr_type = Type::lookup_integer_type("uintptr");
     913                 :      63267 :   Expression* call;
     914                 :      63267 :   switch (type->base()->classification())
     915                 :            :     {
     916                 :          0 :     default:
     917                 :          0 :       go_unreachable();
     918                 :            : 
     919                 :            :     case Type::TYPE_ERROR:
     920                 :            :       return assign;
     921                 :            : 
     922                 :      23391 :     case Type::TYPE_POINTER:
     923                 :      23391 :     case Type::TYPE_FUNCTION:
     924                 :      23391 :     case Type::TYPE_MAP:
     925                 :      23391 :     case Type::TYPE_CHANNEL:
     926                 :      23391 :       {
     927                 :            :         // These types are all represented by a single pointer.
     928                 :      23391 :         rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
     929                 :      23391 :         call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
     930                 :            :       }
     931                 :      23391 :       break;
     932                 :            : 
     933                 :       9614 :     case Type::TYPE_STRING:
     934                 :       9614 :       {
     935                 :            :         // Assign the length field directly.
     936                 :       9614 :         Expression* llen =
     937                 :       9614 :           Expression::make_string_info(indir->copy(),
     938                 :            :                                        Expression::STRING_INFO_LENGTH,
     939                 :            :                                        loc);
     940                 :       9614 :         Expression* rlen =
     941                 :       9614 :           Expression::make_string_info(rhs,
     942                 :            :                                        Expression::STRING_INFO_LENGTH,
     943                 :            :                                        loc);
     944                 :       9614 :         Statement* as = Statement::make_assignment(llen, rlen, loc);
     945                 :       9614 :         inserter->insert(as);
     946                 :            : 
     947                 :            :         // Assign the data field with a write barrier.
     948                 :       9614 :         lhs =
     949                 :       9614 :           Expression::make_string_info(indir->copy(),
     950                 :            :                                        Expression::STRING_INFO_DATA,
     951                 :            :                                        loc);
     952                 :       9614 :         rhs =
     953                 :       9614 :           Expression::make_string_info(rhs,
     954                 :            :                                        Expression::STRING_INFO_DATA,
     955                 :            :                                        loc);
     956                 :       9614 :         assign = Statement::make_assignment(lhs, rhs, loc);
     957                 :       9614 :         lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
     958                 :       9614 :         rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
     959                 :       9614 :         call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
     960                 :            :       }
     961                 :       9614 :       break;
     962                 :            : 
     963                 :      11015 :     case Type::TYPE_INTERFACE:
     964                 :      11015 :       {
     965                 :            :         // Assign the first field directly.
     966                 :            :         // The first field is either a type descriptor or a method table.
     967                 :            :         // Type descriptors are either statically created, or created by
     968                 :            :         // the reflect package. For the latter the reflect package keeps
     969                 :            :         // all references.
     970                 :            :         // Method tables are either statically created or persistently
     971                 :            :         // allocated.
     972                 :            :         // In all cases they don't need a write barrier.
     973                 :      11015 :         Expression* ltab =
     974                 :      11015 :           Expression::make_interface_info(indir->copy(),
     975                 :            :                                           Expression::INTERFACE_INFO_METHODS,
     976                 :            :                                           loc);
     977                 :      11015 :         Expression* rtab =
     978                 :      11015 :           Expression::make_interface_info(rhs,
     979                 :            :                                           Expression::INTERFACE_INFO_METHODS,
     980                 :            :                                           loc);
     981                 :      11015 :         Statement* as = Statement::make_assignment(ltab, rtab, loc);
     982                 :      11015 :         inserter->insert(as);
     983                 :            : 
     984                 :            :         // Assign the data field with a write barrier.
     985                 :      11015 :         lhs =
     986                 :      11015 :           Expression::make_interface_info(indir->copy(),
     987                 :            :                                           Expression::INTERFACE_INFO_OBJECT,
     988                 :            :                                           loc);
     989                 :      11015 :         rhs =
     990                 :      11015 :           Expression::make_interface_info(rhs,
     991                 :            :                                           Expression::INTERFACE_INFO_OBJECT,
     992                 :            :                                           loc);
     993                 :      11015 :         assign = Statement::make_assignment(lhs, rhs, loc);
     994                 :      11015 :         lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
     995                 :      11015 :         rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
     996                 :      11015 :         call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
     997                 :            :       }
     998                 :      11015 :       break;
     999                 :            : 
    1000                 :      14092 :     case Type::TYPE_ARRAY:
    1001                 :      14092 :       if (type->is_slice_type())
    1002                 :            :        {
    1003                 :            :           // Assign the lenth fields directly.
    1004                 :      14015 :           Expression* llen =
    1005                 :      14015 :             Expression::make_slice_info(indir->copy(),
    1006                 :            :                                         Expression::SLICE_INFO_LENGTH,
    1007                 :            :                                         loc);
    1008                 :      14015 :           Expression* rlen =
    1009                 :      14015 :             Expression::make_slice_info(rhs,
    1010                 :            :                                         Expression::SLICE_INFO_LENGTH,
    1011                 :            :                                         loc);
    1012                 :      14015 :           Statement* as = Statement::make_assignment(llen, rlen, loc);
    1013                 :      14015 :           inserter->insert(as);
    1014                 :            : 
    1015                 :            :           // Assign the capacity fields directly.
    1016                 :      14015 :           Expression* lcap =
    1017                 :      14015 :             Expression::make_slice_info(indir->copy(),
    1018                 :            :                                         Expression::SLICE_INFO_CAPACITY,
    1019                 :            :                                         loc);
    1020                 :      14015 :           Expression* rcap =
    1021                 :      14015 :             Expression::make_slice_info(rhs,
    1022                 :            :                                         Expression::SLICE_INFO_CAPACITY,
    1023                 :            :                                         loc);
    1024                 :      14015 :           as = Statement::make_assignment(lcap, rcap, loc);
    1025                 :      14015 :           inserter->insert(as);
    1026                 :            : 
    1027                 :            :           // Assign the data field with a write barrier.
    1028                 :      14015 :           lhs =
    1029                 :      14015 :             Expression::make_slice_info(indir->copy(),
    1030                 :            :                                         Expression::SLICE_INFO_VALUE_POINTER,
    1031                 :            :                                         loc);
    1032                 :      14015 :           rhs =
    1033                 :      14015 :             Expression::make_slice_info(rhs,
    1034                 :            :                                         Expression::SLICE_INFO_VALUE_POINTER,
    1035                 :            :                                         loc);
    1036                 :      14015 :           assign = Statement::make_assignment(lhs, rhs, loc);
    1037                 :      14015 :           lhs = Expression::make_unary(OPERATOR_AND, lhs, loc);
    1038                 :      14015 :           rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
    1039                 :      14015 :           call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
    1040                 :      14015 :           break;
    1041                 :            :         }
    1042                 :            :       // fallthrough
    1043                 :            : 
    1044                 :       5232 :     case Type::TYPE_STRUCT:
    1045                 :       5232 :       if (type->is_direct_iface_type())
    1046                 :            :         {
    1047                 :         41 :           rhs = Expression::unpack_direct_iface(rhs, loc);
    1048                 :         41 :           rhs = Expression::make_unsafe_cast(uintptr_type, rhs, loc);
    1049                 :         41 :           call = Runtime::make_call(Runtime::GCWRITEBARRIER, loc, 2, lhs, rhs);
    1050                 :            :         }
    1051                 :            :       else
    1052                 :            :         {
    1053                 :            :           // TODO: split assignments for small struct/array?
    1054                 :       5191 :           rhs = Expression::make_unary(OPERATOR_AND, rhs, loc);
    1055                 :       5191 :           rhs->unary_expression()->set_does_not_escape();
    1056                 :       5191 :           call = Runtime::make_call(Runtime::TYPEDMEMMOVE, loc, 3,
    1057                 :            :                                     Expression::make_type_descriptor(type, loc),
    1058                 :            :                                     lhs, rhs);
    1059                 :            :         }
    1060                 :            :       break;
    1061                 :            :     }
    1062                 :            : 
    1063                 :      63267 :   return this->check_write_barrier(enclosing, assign,
    1064                 :      63267 :                                    Statement::make_statement(call, false));
    1065                 :            : }
    1066                 :            : 
    1067                 :            : // Return a statement that tests whether write barriers are enabled
    1068                 :            : // and executes either the efficient code or the write barrier
    1069                 :            : // function call, depending.
    1070                 :            : 
    1071                 :            : Statement*
    1072                 :     174790 : Gogo::check_write_barrier(Block* enclosing, Statement* without,
    1073                 :            :                           Statement* with)
    1074                 :            : {
    1075                 :     174790 :   Location loc = without->location();
    1076                 :     174790 :   Named_object* wb = this->write_barrier_variable();
    1077                 :            :   // We pretend that writeBarrier is a uint32, so that we do a
    1078                 :            :   // 32-bit load.  That is what the gc toolchain does.
    1079                 :     174790 :   Type* void_type = Type::make_void_type();
    1080                 :     174790 :   Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
    1081                 :     174790 :   Type* uint32_type = Type::lookup_integer_type("uint32");
    1082                 :     174790 :   Type* puint32_type = Type::make_pointer_type(uint32_type);
    1083                 :     174790 :   Expression* ref = Expression::make_var_reference(wb, loc);
    1084                 :     174790 :   ref = Expression::make_unary(OPERATOR_AND, ref, loc);
    1085                 :     174790 :   ref = Expression::make_cast(unsafe_pointer_type, ref, loc);
    1086                 :     174790 :   ref = Expression::make_cast(puint32_type, ref, loc);
    1087                 :     174790 :   ref = Expression::make_dereference(ref,
    1088                 :            :                                      Expression::NIL_CHECK_NOT_NEEDED, loc);
    1089                 :     174790 :   Expression* zero = Expression::make_integer_ul(0, ref->type(), loc);
    1090                 :     174790 :   Expression* cond = Expression::make_binary(OPERATOR_EQEQ, ref, zero, loc);
    1091                 :            : 
    1092                 :     174790 :   Block* then_block = new Block(enclosing, loc);
    1093                 :     174790 :   then_block->add_statement(without);
    1094                 :            : 
    1095                 :     174790 :   Block* else_block = new Block(enclosing, loc);
    1096                 :     174790 :   else_block->add_statement(with);
    1097                 :            : 
    1098                 :     174790 :   return Statement::make_if_statement(cond, then_block, else_block, loc);
    1099                 :            : }

Generated by: LCOV version 1.0

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto --enable-host-shared. GCC test suite is run with the built compiler.