Assertion error in codegen due to sharing C2 node in post store barrier?

Zixian Cai zixian.cai at anu.edu.au
Wed Oct 19 07:56:34 UTC 2022


Hi all,

I put more print statements in matcher.cpp and narrowed down the bug.

When transforming CallLeaf, a bunch of arguments are pushed to the mstack. The AddP (48) node is into a MachNode after CallLeaf.
When transforming CompareAndSwapP, the AddP operand is recursively passed to ReduceInst.
https://github.com/openjdk/jdk/blob/f502ab85c987be827d36b0a29f77ec5ce5bb3d01/src/hotspot/share/opto/matcher.cpp#L1973
But because AddP has been transformed (and thus set_new_node has been called), the second call to ReduceInst with AddP will fail the assertion.
(C->node_arena()->contains(s->_leaf) || !has_new_node(s->_leaf)
https://github.com/openjdk/jdk/blob/f502ab85c987be827d36b0a29f77ec5ce5bb3d01/src/hotspot/share/opto/matcher.cpp#L1787
Because AddP is not a new node (not MachNode), and it has been matched (has a new node).

This is not a problem on x86_64, because when matching CompareAndSwapP, the AddP operand is passed to ReduceOper instead of ReduceInst.
https://github.com/openjdk/jdk/blob/f502ab85c987be827d36b0a29f77ec5ce5bb3d01/src/hotspot/share/opto/matcher.cpp#L1960

I’m wondering whether any of you have had this problem before, and what a clean solution might look like.

Sincerely,
Zixian

On 19/10/2022, 00:42, "Zixian Cai" <zixian.cai at anu.edu.au> wrote:
Hi all,

I’m working on a C2 write barrier. Interesting enough, the barrier code works fine on x86_64, and only fails with RISC-V. I’m wondering whether there’s anything in the RISC-V codegen that might cause the above behaviour.

I would really appreciate if people could point out the correct pattern for writing such barrier code, or any hint for further debugging.

Below are more details.

The barrier in its simplest form transforms each oop store into an oop store and a runtime call. Here is an example for CAS.

  virtual Node* atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, Node* new_val, const Type* value_type) const {
    Node* result = BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type);
    if (access.is_oop()) object_reference_write_post(access.kit(), access.base(), access.addr().node(), new_val);
    return result;
  }

void MMTkObjectBarrierSetC2::object_reference_write_post(GraphKit* kit, Node* src, Node* slot, Node* val) const {
  if (can_remove_barrier(kit, &kit->gvn(), src, slot, val, /* skip_const_null */ true)) return;

  MMTkIdealKit ideal(kit, true);

  const TypeFunc* tf = __ func_type(src->bottom_type(), slot->bottom_type(), val->bottom_type());
  Node* x = __ make_leaf_call(tf, FN_ADDR(MMTkBarrierSetRuntime::object_reference_write_post_call), "mmtk_barrier_call", src, slot, val);

  kit->final_sync(ideal); // Final sync IdealKit and GraphKit.
}

Currently, I’m having an assertion error in ReduceInst (frame #0).

#  Internal Error (/home/zixianc/mmtk-riscv/jdk-mmtk/src/hotspot/share/opto/matcher.cpp:1791), pid=1275, tid=1291
#  assert(C->node_arena()->contains(s->_leaf) || !has_new_node(s->_leaf)) failed: duplicating node that's already been matched

I have been debugging in gdb and narrowed down the offending method to java.util.concurrent.ConcurrentHashMap::casTabAt. The s->_leaf in above is the AddP (o48) below.

In gdb, the call stack of the C2 compiler starts with match_tree(CompareAndSwapP (o58)) (frame #3), which in turn calls ReduceInst (frame #2), ReduceInst_Interior (frame #1), and finally ReduceInst on AddP (o48) (frame #0).
Dumping AddP (o48) gives the following.

leaf->dump(3)

o28  ConI  === o0  [[ o29 ]]  #int:3
o27  ConvI2L  === _ o11  [[ o29 ]]  #long:minint..maxint
o0  Root  === o0 o44 o74  [[ o0 o1 o3 o39 o36 o28 o32 o76 0 ]] inner
o29  LShiftL  === _ o27 o28  [[ o49 ]]
o3  Start  === o3 o0  [[ o3 o5 o6 o7 o8 o9 o10 o11 o12 o13 ]]  #{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:java/util/concurrent/ConcurrentHashMap$Node *[int:>=0] *, 6:int, 7:java/util/concurrent/ConcurrentHashMap$Node *, 8:java/util/co
ncurrent/ConcurrentHashMap$Node *}
o32  ConL  === o0  [[ o48 ]]  #long:24
o49  AddP  === _ o10 o10 o29  [[ o48 ]]
o10  Parm  === o3  [[ o37 o61 o49 o49 o48 ]] Parm0: java/util/concurrent/ConcurrentHashMap$Node *[int:>=0] *
o48  AddP  === _ o10 o49 o32  [[ o58 o61 ]]

leaf->dump(-3)

o48  AddP  === _ o10 o49 o32  [[ o58 o61 ]]
o58  CompareAndSwapP  === o55 o56 o48 o75  |o45  [[ o59 o69 o74 9 11 34 ]]
o61  CallLeaf  === o55 o1 o56 o1 o1 (o10 o48 o13 ) [[ o62 o63 ]] # mmtk_barrier_call void ( java/util/concurrent/ConcurrentHashMap$Node *[int:>=0]:NotNull *, java/util/concurrent/ConcurrentHashMap$Node *[int:>=0]:NotNull+any *, java/util/concurrent/ConcurrentH
ashMap$Node * )
o59  SCMemProj  === o58  [[ o64 32 ]]   Memory: @BotPTR *+bot, idx=Bot;
o69  MemBarAcquire  === o66 o1 o67 o1 o1 o58  [[ o70 o71 10 ]]
o74  Return  === o70 o6 o71 o8 o9 returns o58  [[ o0 1 ]]
  9  Ret  === o70 o6 o71 o8 o9 o58  [[ ]]
11  membar_acquire  === o66 o1 o67 o1 o1 o58  [[ ]]  !jvms: ConcurrentHashMap::casTabAt @ bci:17 (line 765)
34  SCMemProj  === o58  [[ ]]   Memory: @BotPTR *+bot, idx=Bot; !jvms: ConcurrentHashMap::casTabAt @ bci:17 (line 765)
o62  Proj  === o61  [[ o65 ]] #0
o63  Proj  === o61  [[ o64 ]] #2  Memory: @rawptr:BotPTR, idx=Raw;
o64  MergeMem  === _ o1 o56 o63 o59  [[ o65 13 ]]  { N63:rawptr:BotPTR N59:java/lang/Object *[int:>=0]+any * }  Memory: @BotPTR *+bot, idx=Bot;
32  MergeMem  === _ 0 25 33 o59  [[ ]]  { N33:rawptr:BotPTR N59:java/lang/Object *[int:>=0]+any * }  Memory: @BotPTR *+bot, idx=Bot; !jvms: ConcurrentHashMap::casTabAt @ bci:17 (line 765)
o70  Proj  === o69  [[ o74 9 ]] #0
o71  Proj  === o69  [[ o74 9 ]] #2  Memory: @BotPTR *+bot, idx=Bot;
10  MachProj  === o69  [[ ]] #0/unmatched !jvms: ConcurrentHashMap::casTabAt @ bci:17 (line 765)
o0  Root  === o0 o44 o74  [[ o0 o1 o3 o39 o36 o28 o32 o76 0 ]] inner
  1  Root  === 1 2 o74  [[ 1 6 ]] inner
o65  MemBarCPUOrder  === o62 o1 o64 o1 o1  [[ o66 o67 12 ]]

My guess is that the AddP node (o48) has been reduced when generating machine code for CallLeaf (o61), so when visiting CompareAndSwapP (o58), AddP (o48) will be visited again, and thus failing the assertion.

If I change
  const TypeFunc* tf = __ func_type(src->bottom_type(), slot->bottom_type(), val->bottom_type());
  Node* x = __ make_leaf_call(tf, FN_ADDR(MMTkBarrierSetRuntime::object_reference_write_post_call), "mmtk_barrier_call", src, slot, val);
into
  const TypeFunc* tf = __ func_type(src->bottom_type());
  Node* x = __ make_leaf_call(tf, FN_ADDR(MMTkBarrierSetRuntime::object_reference_write_post_call), "mmtk_barrier_call", src);
the assertion error will disappear.
Note that the slot is the AddP node (an offset from the src pointer).

Sincerely,
Zixian
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/riscv-port-dev/attachments/20221019/0f7cfb7f/attachment-0001.htm>


More information about the riscv-port-dev mailing list