< prev index next >

src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp

Print this page
@@ -410,10 +410,14 @@
  
    IdealKit ideal(kit, true);
  
    Node* tls = __ thread(); // ThreadLocalStorage
  
+   BarrierSet* bs = BarrierSet::barrier_set();
+   CardTableBarrierSet* ctbs = barrier_set_cast<CardTableBarrierSet>(bs);
+   CardTable* ct = ctbs->card_table();
+ 
    Node* no_base = __ top();
    float likely = PROB_LIKELY_MAG(3);
    float unlikely = PROB_UNLIKELY_MAG(3);
    Node* young_card = __ ConI((jint)G1CardTable::g1_young_card_val());
    Node* dirty_card = __ ConI((jint)G1CardTable::dirty_card_val());

@@ -457,40 +461,66 @@
      Node* xor_res =  __ URShiftX ( __ XorX( cast,  __ CastPX(__ ctrl(), val)), __ ConI(HeapRegion::LogOfHRGrainBytes));
  
      // if (xor_res == 0) same region so skip
      __ if_then(xor_res, BoolTest::ne, zeroX, likely); {
  
-       // No barrier if we are storing a NULL
-       __ if_then(val, BoolTest::ne, kit->null(), likely); {
+       // if ((unsigned)(card_offset - low_map_offset) >= (high_map_offset - low_map_offset)) stack allocated object, so skip
+       if (kit->C->do_stack_allocation()) {
+         state()->add_enqueue_barrier(static_cast<CastP2XNode*>(cast));
+         Node* low_off = kit->longcon(ct->byte_map_bottom_offset());
+         Node* delta_off = kit->longcon(ct->byte_map_top_offset() - ct->byte_map_bottom_offset());
+         Node* sub_off = __ SubL(cast, low_off);
  
-         // Ok must mark the card if not already dirty
+         __ uif_then(sub_off, BoolTest::le, delta_off, likely); } {
  
-         // load the original value of the card
-         Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw);
+           // No barrier if we are storing a NULL
+           __ if_then(val, BoolTest::ne, kit->null(), likely); {
+ 
+             // Ok must mark the card if not already dirty
+ 
+             // load the original value of the card
+             Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw);
  
-         __ if_then(card_val, BoolTest::ne, young_card, unlikely); {
-           kit->sync_kit(ideal);
-           kit->insert_mem_bar(Op_MemBarVolatile, oop_store);
-           __ sync_kit(kit);
+             __ if_then(card_val, BoolTest::ne, young_card, unlikely); {
+               kit->sync_kit(ideal);
+               kit->insert_mem_bar(Op_MemBarVolatile, oop_store);
+               __ sync_kit(kit);
  
-           Node* card_val_reload = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw);
-           __ if_then(card_val_reload, BoolTest::ne, dirty_card); {
-             g1_mark_card(kit, ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf);
+               Node* card_val_reload = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw);
+               __ if_then(card_val_reload, BoolTest::ne, dirty_card); {
+                 g1_mark_card(kit, ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf);
+               } __ end_if();
+             } __ end_if();
            } __ end_if();
-         } __ end_if();
-       } __ end_if();
+       } if (kit->C->do_stack_allocation()) {
+         __ end_if();
+       }
      } __ end_if();
    } else {
      // The Object.clone() intrinsic uses this path if !ReduceInitialCardMarks.
      // We don't need a barrier here if the destination is a newly allocated object
      // in Eden. Otherwise, GC verification breaks because we assume that cards in Eden
      // are set to 'g1_young_gen' (see G1CardTable::verify_g1_young_region()).
      assert(!use_ReduceInitialCardMarks(), "can only happen with card marking");
-     Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw);
-     __ if_then(card_val, BoolTest::ne, young_card); {
-       g1_mark_card(kit, ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf);
-     } __ end_if();
+ 
+     // if ((unsigned)(card_offset - low_map_offset) >= (high_map_offset - low_map_offset)) stack allocated object, so skip
+     if (kit->C->do_stack_allocation()) {
+       state()->add_enqueue_barrier(static_cast<CastP2XNode*>(cast));
+       Node* low_off = kit->longcon(ct->byte_map_bottom_offset());
+       Node* delta_off = kit->longcon(ct->byte_map_top_offset() - ct->byte_map_bottom_offset());
+       Node* sub_off = __ SubL(cast, low_off);
+ 
+       __ uif_then(sub_off, BoolTest::le, delta_off, likely); } {
+ 
+         Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw);
+         __ if_then(card_val, BoolTest::ne, young_card); {
+           g1_mark_card(kit, ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf);
+         } __ end_if();
+ 
+       } if (kit->C->do_stack_allocation()) {
+         __ end_if();
+       }
    }
  
    // Final sync IdealKit and GraphKit.
    kit->final_sync(ideal);
  }

@@ -659,17 +689,125 @@
    }
  
    return strcmp(call->_name, "write_ref_field_pre_entry") == 0 || strcmp(call->_name, "write_ref_field_post_entry") == 0;
  }
  
+ bool G1BarrierSetC2::process_barrier_node(Node* node, PhaseIterGVN& igvn) const {
+   assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required");
+ 
+   // Must have a control node
+   if (node->in(0) == NULL) {
+     return false;
+   }
+ 
+   // Search for CastP2X->Xor->URShift->Cmp path which
+   // checks if the store done to a different from the value's region.
+   Node* xorx = node->find_out_with(Op_XorX);
+   BoolNode* bool_node = NULL;
+ 
+   if (xorx != NULL) {
+ 
+     Node* shift = shift = xorx->unique_out();
+     Node* cmpx = shift->unique_out();
+ 
+     assert(cmpx->is_Cmp() && cmpx->unique_out()->is_Bool() &&
+             cmpx->unique_out()->as_Bool()->_test._test == BoolTest::ne,
+             "missing region check in G1 post barrier");
+ 
+     Node* bol = cmpx->unique_out();
+     assert(bol->unique_out()->is_If(), "should find if after the bool node");
+     Node* if_node = bol->unique_out();
+     Node* if_true = if_node->find_out_with(Op_IfTrue);
+     assert(if_true != NULL, "there should be false projection");
+ 
+     Node* iff_check = if_true->find_out_with(Op_If);
+     // Not a barrier with bound check
+     if (iff_check == NULL) {
+       return false;
+     }
+ 
+     Node* iff_check_in_1_node = iff_check->in(1);
+     if (!iff_check_in_1_node->is_Bool()) {
+       return false;
+     }
+     bool_node = iff_check_in_1_node->as_Bool();
+ 
+   } else {
+     // this "could" be the the path followed when !use_ReduceInitialCardMarks() is
+     // used or when the two sides of the barrier are scalar replaced
+     //assert(false, "we managed to get here!!! process_barrier_node");
+     Node *addl_node = node->find_out_with(Op_AddL);
+     if (addl_node == NULL) {
+       return false;
+     }
+ 
+     Node* cmpx = addl_node->unique_out();
+     assert(cmpx->is_Cmp() && cmpx->unique_out()->is_Bool() &&
+           cmpx->unique_out()->as_Bool()->_test._test == BoolTest::le,
+           "missing region check in G1 post barrier");
+ 
+     bool_node = cmpx->unique_out()->as_Bool();
+   }
+ 
+   if (bool_node->_test._test != BoolTest::le) {
+     return false;
+   }
+ 
+   // the input to the bool is the CMPX
+   Node* bool_node_in_1_node = bool_node->in(1);
+   if (!bool_node_in_1_node->is_Cmp()) {
+     return false;
+   }
+   CmpNode* cmp_node = bool_node_in_1_node->as_Cmp();
+ 
+   // the input to the CMPX is the card_table_top_offset constant
+   Node* cmp_node_in_2_node = cmp_node->in(2);
+   if (!cmp_node_in_2_node->is_Con()) {
+     return false;
+   }
+ 
+   BarrierSet* bs = BarrierSet::barrier_set();
+   CardTableBarrierSet* ctbs = barrier_set_cast<CardTableBarrierSet>(bs);
+   CardTable* ct = ctbs->card_table();
+   size_t constant = ct->byte_map_top_offset() - ct->byte_map_bottom_offset();
+ 
+   // Check that the input to this CMP node is the expected constant
+   const TypeX* otype = cmp_node_in_2_node->find_intptr_t_type();
+   if (otype != NULL && otype->is_con() &&
+       size_t(otype->get_con()) != constant) {
+     // Constant offset but not the card table size constant so just return
+     return false;
+   }
+ 
+   // we can't change the compare or the constant so create a new constant(0) and replace the variable
+   Node* cmp_node_in_1_node = cmp_node->in(1);
+   ConNode* zeroConstant_node = igvn.makecon(TypeX_ZERO);
+   if (cmp_node_in_1_node->_idx == zeroConstant_node->_idx) {
+     // we can get here via different nodes - but we only want to change the input once
+     return false;
+   }
+ 
+   igvn.rehash_node_delayed(cmp_node);
+   int numReplaced = cmp_node->replace_edge(cmp_node_in_1_node, zeroConstant_node);
+   assert(numReplaced == 1, "Failed to replace the card_offset with Conx(0)");
+   return true;
+ }
+ 
  void G1BarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const {
    assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required");
-   assert(node->outcnt() <= 2, "expects 1 or 2 users: Xor and URShift nodes");
+   assert(node->outcnt() <= 3, "expects 1, 2 or 3 users: Xor, URShift and SubL nodes");
    // It could be only one user, URShift node, in Object.clone() intrinsic
    // but the new allocation is passed to arraycopy stub and it could not
    // be scalar replaced. So we don't check the case.
  
+   // Certain loop optimisations may introduce a CastP2X node with
+   // ConvL2I in case of an AllocateArray op. Check for that case
+   // here and do not attempt to eliminate it as write barrier.
+   if (macro->C->do_stack_allocation() && !state()->is_a_barrier(static_cast<CastP2XNode*>(node))) {
+     return;
+   }
+ 
    // An other case of only one user (Xor) is when the value check for NULL
    // in G1 post barrier is folded after CCP so the code which used URShift
    // is removed.
  
    // Take Region node before eliminating post barrier since it also

@@ -718,11 +856,18 @@
            }
          }
        }
      }
    } else {
-     assert(!use_ReduceInitialCardMarks(), "can only happen with card marking");
+     // In a scenario where the two sides of the barrier are scalar replaced
+     // or stack allocated, the XorX node will be visited more than once, because
+     // both edges will be CastP2X nodes from two distinct allocates. In certain
+     // instances, the removal of the CastP2X node will result in removal of the
+     // XorX node, causing the assert below to be hit when eliminate_gc_barrier is
+     // called for the second node.
+     // assert(!use_ReduceInitialCardMarks(), "can only happen with card marking");
+ 
      // This is a G1 post barrier emitted by the Object.clone() intrinsic.
      // Search for the CastP2X->URShiftX->AddP->LoadB->Cmp path which checks if the card
      // is marked as young_gen and replace the Cmp with 0 (false) to collapse the barrier.
      Node* shift = node->find_out_with(Op_URShiftX);
      assert(shift != NULL, "missing G1 post barrier");

@@ -736,12 +881,16 @@
      macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ));
      // There is no G1 pre barrier in this case
    }
    // Now CastP2X can be removed since it is used only on dead path
    // which currently still alive until igvn optimize it.
-   assert(node->outcnt() == 0 || node->unique_out()->Opcode() == Op_URShiftX, "");
+   // TODO: fix this following assert becuase of SUBL
+   // assert(node->outcnt() == 0 || node->unique_out()->Opcode() == Op_URShiftX, "");
    macro->replace_node(node, macro->top());
+ 
+   // Remove this node from our state
+   state()->remove_enqueue_barrier(static_cast<CastP2XNode*>(node));
  }
  
  Node* G1BarrierSetC2::step_over_gc_barrier(Node* c) const {
    if (!use_ReduceInitialCardMarks() &&
        c != NULL && c->is_Region() && c->req() == 3) {
< prev index next >