ShenandoahGC cmpxchg_oop oldval register *NOT* be clobbered

Leslie Zhai zhaixiang at loongson.cn
Sun Apr 23 02:26:57 UTC 2023


Hi,

Cui weilong found that Renaissance philosophers thrown ClassCastException[1] with -XX:+UseShenandoahGC for LoongArch.
So I just disabled C2 compiler via -XX:TieredStopAtLevel=3 for fastdebug, then it was not able to reproduce the issue.
At very beginning I thought it might be lack of mov oldval to tmp register in `cmpxchg_oop` by compared with AArch64:

```
  enc_class aarch64_enc_cmpxchg_oop_shenandoah(memory mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, iRegINoSp res) %{
     MacroAssembler _masm(&cbuf);
     guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding”);
=> Register tmp = $tmp$$Register;
=> __ mov(tmp, $oldval$$Register); // Must not clobber oldval.
     ShenandoahBarrierSet::assembler()->cmpxchg_oop(&_masm, $mem$$Register, tmp, $newval$$Register,
/*acquire*/ false, /*release*/ true, /*is_cae*/ false, $res$$Register);
%}
```

I thought it was lack of mov oldval to tmp register for LoongArch:

```
  enc_class loongarch_enc_cmpxchg_oop_shenandoah(memory mem, mRegP oldval, mRegP newval, mRegI res) %{
     MacroAssembler _masm(&cbuf);
     guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding”);
=> Be lack of move oldval to tmp register
     ShenandoahBarrierSet::assembler()->cmpxchg_oop(&_masm, $mem$$Register, $oldval$$Register, $newval$$Register,
/*acquire*/ false, /*is_cae*/ false, $res$$Register);
%}
```

But I actually fixed the issue by allocating a tmp register for `cmpxchg_oop`, for example:

```
encode %{
  enc_class loongarch_enc_cmpxchg_oop_shenandoah(memory mem, mRegP oldval, mRegP newval, mRegP tmp /* allocated tmp register */, mRegI res) %{
    MacroAssembler _masm(&cbuf);
    guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding");
    Register tmp = $tmp$$Register; // allocated tmp register
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(&_masm, $mem$$Register, $oldval$$Register, $newval$$Register, tmp /* allocated tmp register */,
                                                   /*acquire*/ false, /*is_cae*/ false, $res$$Register);
  %}   
...
%}

instruct compareAndSwapP_shenandoah(mRegI res, indirect mem, mRegP oldval, mRegP newval, mRegP tmp) %{
  match(Set res (ShenandoahCompareAndSwapP mem (Binary oldval newval)));

  effect(TEMP tmp); // allocated tmp register

  predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire
            && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst);

  format %{
    "cmpxchg_shenandoah $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval"
  %}   
  ins_encode(loongarch_enc_cmpxchg_oop_shenandoah(mem, oldval, newval, tmp /* allocated tmp register */, res));

  ins_pipe(pipe_slow);
%}

```

Sun guoyun, my colleague, reviewed my patch and found that oldval register *NOT* be clobbered at all, and suggested to remove it for AArch64[2] too, for example:

```
diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad
index 0572e7d8d11..da75df59b6d 100644
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoah_aarch64.ad
@@ -28,233 +28,209 @@ source_hpp %{
 %}
   encode %{
-  enc_class aarch64_enc_cmpxchg_oop_shenandoah(memory mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, iRegINoSp res) %{
+  enc_class aarch64_enc_cmpxchg_oop_shenandoah(memory mem, iRegP oldval, iRegP newval, iRegINoSp res) %{
     MacroAssembler _masm(&cbuf);
     guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding");
-    Register tmp = $tmp$$Register;
-    __ mov(tmp, $oldval$$Register); // Must not clobber oldval.
-    ShenandoahBarrierSet::assembler()->cmpxchg_oop(&_masm, $mem$$Register, tmp, $newval$$Register,
+    ShenandoahBarrierSet::assembler()->cmpxchg_oop(&_masm, $mem$$Register, $oldval$$Register, $newval$$Register,
                                                    /*acquire*/ false, /*release*/ true, /*is_cae*/ false, $res$$Register);
   %}
```

I only ran jtreg tier1 fastdebug[3] for AArch64 on Mac mini M2:

```
make run-test CONF=fastdebug TEST="tier1" JTREG="VM_OPTIONS=-XX:+UseShenandoahGC;VERBOSE=summary"
```

Do I need to run more tests for AArch64? For example jtreg tier2~3, and some benchmarks such as Renaissance or DaCapo?
Please point out my fault!


1.
[cuiweilong at localhost test]$ jdk-17.0.5/bin/java -XX:+UseShenandoahGC -jar Renaissance/renaissance-gpl-0.14.1.jar philosophers
====== philosophers (scala) [default], iteration 0 started ======
GC before operation: completed in 59.346 ms, heap usage 72.000 MB -> 3.321 MB.
Exception in thread "Thread-3" java.lang.ClassCastException: class scala.concurrent.stm.Txn$Preparing$ cannot be cast to class scala.concurrent.stm.Txn$RolledBack (scala.concurrent.stm.Txn$Preparing$ and scala.concurrent.stm.Txn$RolledBack are in unnamed module of loader java.net.URLClassLoader @12e61fe6)
at scala.concurrent.stm.ccstm.InTxnImpl.topLevelComplete(InTxnImpl.scala:623)
at scala.concurrent.stm.ccstm.InTxnImpl.topLevelAttempt(InTxnImpl.scala:529)
at scala.concurrent.stm.ccstm.InTxnImpl.topLevelAtomicImpl(InTxnImpl.scala:398)
at scala.concurrent.stm.ccstm.InTxnImpl.atomic(InTxnImpl.scala:259)
at scala.concurrent.stm.ccstm.CCSTMExecutor.apply(CCSTMExecutor.scala:24)
at org.renaissance.scala.stm.RealityShowPhilosophers$PhilosopherThread.$anonfun$run$1(RealityShowPhilosophers.scala:29)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:158)
at org.renaissance.scala.stm.RealityShowPhilosophers$PhilosopherThread.run(RealityShowPhilosophers.scala:27)
Exception in thread "Thread-15" java.lang.ClassCastException
Exception in thread "Thread-11" java.lang.ClassCastException
Exception in thread "Thread-6" java.lang.ClassCastException
Exception in thread "Thread-13” java.lang.ClassCastException


2.
ShenandoahGC oldval register *NOT* be clobbered and fix copy_and_paste typo for store_at  https://github.com/xiangzhai/jdk/commit/186b0ea48f712bd83cf3f8cc0200a14209679340


3.
Before:

jtreg:test/hotspot/jtreg:tier1                     1805   1793        7      5
jtreg:test/jdk:tier1                                    2314   2306        5      3
jtreg:test/langtools:tier1                          4373   2707  1650    16
jtreg:test/jaxp:tier1                                        0         0        0      0
jtreg:test/lib-test:tier1                                  28       28        0      0

After:
jtreg:test/hotspot/jtreg:tier1                     1805  1793         7       5
jtreg:test/jdk:tier1                                    2314  2305         6       3
jtreg:test/langtools:tier1                          4372  2696   1661     15
jtreg:test/jaxp:tier1                                        0        0         0       0
jtreg:test/lib-test:tier1                                  28      28         0       0


Thanks,
Leslie Zhai


More information about the shenandoah-dev mailing list