On 27-jep401ea3+1-1 with --enable-preview and the default JIT flags (InlineTypePassFieldsAsArgs=true, InlineTypeReturnedAsFields=true,   UseFieldFlattening=true), I'm seeing one specific case where a value-class return allocates on every call.

  The value class is a tagged-union, ~32 bytes:

  public value class SimValue {
      private final byte kindOrdinal;
      private final byte signedAndFlags;
      private final short width;
      private final long a, b, c;
      private final Object ref;  // null for scalar kinds
      public static SimValue bitwiseAnd(SimValue a, SimValue b) { /* 230 bytes */ }       public static SimValue ofTwoStateScalar(int w, long v, boolean s) { /* 85 bytes */ }
      public long toLong() { /* 181 bytes */ }
      // ...
  }

  // Hot loop:
  for (int i = 0; i < arr.length; i++) {
      SimValue r = SimValue.bitwiseAnd(arr[i], mask);
      sum += r.toLong();
  }

  -XX:+PrintInlining -XX:+PrintCompilation on the loop:

  1346 %  3  FlatteningBenchmark::arithReturnOp @ 12 (45 bytes)
                @ 24  SimValue::bitwiseAnd (230 bytes)  failed to inline: callee is too large   1345    4  SimValue::bitwiseAnd (230 bytes)            ; standalone C2 compile   1346 %  3  arithReturnOp made not entrant: OSR invalidation of lower level                 @ 24  SimValue::bitwiseAnd (230 bytes)  failed to inline: already compiled into a big method
  1349 %  4  arithReturnOp @ 12 (45 bytes)
                @ 24  SimValue::bitwiseAnd (230 bytes)  failed to inline: already compiled into a big method

  Steady-state JFR pins SimValue.bitwiseAnd at 95.95% of allocations; the loop runs at ~120 Mops/sec vs. ~550 Mops/sec for an equivalent
  read-only loop (arr[i].toLong()).

  What works on the same JDK build:

  - Field flattening (SimValue stored in an identity-class field) — zero allocation.
  - Array flattening (SimValue[]) — zero allocation.
  - Cross-method mutation (holder.assign(srcHolder.value)) — zero allocation (parameter side scalarizes).

  So the question is specifically about the return side when the callee is too large to inline:

  1. Is InlineTypeReturnedAsFields only honored when the JIT inlines the callee, or should a standalone-compiled callee also use a   scalarized calling convention so its return value can stay on the caller's stack?   2. If scalarization requires inlining, is the right fix to split bitwiseAnd into a tiny X/Z-free fast path that's small   enough to inline, with the existing 230-byte body kept as the cold fallback? Anything I should be aware of about how that interacts with
   the C2 inlining budget for value-class-returning methods?

Reply via email to