On Thu, 16 Jul 2026 04:27:05 GMT, Kuai Wei <[email protected]> wrote: >> I recently noticed a behavioral discrepancy in >> jdk.internal.util.ArraysSupport.vectorizedMismatch between the Java >> implementation and the platform intrinsic implementations. >> >> Current behavior >> >> The Java implementation may leave a tail of elements unchecked, returning >> the bitwise complement of the number of remaining elements (i.e., >> ~remaining). >> The x86_64 intrinsic, by contrast, compares all elements and simply returns >> -1 when no mismatch is found. >> >> Proposed change >> >> This PR refines the Java implementation so that it always compares all >> elements and returns -1 when no mismatch is found, matching the x86_64 >> intrinsic behavior. >> >> A regression test is included at >> `test/hotspot/jtreg/compiler/intrinsics/VectorizedMismatchReturnDiffTest.java` >> which demonstrates the original behavioral difference. >> >> ## Test >> - [x] tier1 test suites on linux x86_64 >> - [x] tier1 test suites on linux aarch64 >> >> --------- >> - [x] I confirm that I make this contribution in accordance with the >> [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai). > > Kuai Wei has updated the pull request incrementally with two additional > commits since the last revision: > > - Fix indent > - Recovery comments
The design that allows a N < -1 returns makes it easy for the assembly intrinsic to do incomplete work, putting a burden on its caller to handle work the stub won’t do. The purpose of this division of labor is to avoid requiring assembly authors to write code that handles every corner case – they only need to handle most of the input bytes, not all of them. This is a general principle. We have found in the past that putting too many responsibilities on assembly code leads to bugs which are expensive to detect and fix. To avoid that risk, assembly code needs to do the job it’s good for (hand-tuned performance of tight loops) and then get out of the way, not try to do extra jobs that Java is just as good at it. Because bugs in Java code are cheap to detect and fix, compared to assembly code. The main application of this general principle is not with the mismatch intrinsic, but rather with bounds-checking logic, which assembly code must not try to perform (way too many scars from that behavior). For mismatch, the potential problem of "doing too much" is pretty benign: We guess there might be some benefit for the assembly code to handle (say) a misaligned prefix plus only the aligned words of one of the arrays, or (say) an integral multiple of a vector size – which cannot be specified at the Java level. But assembly programmers can, frankly, be trusted to handle part-vector post-loops as well as pre-loops, and there are platform-specific masking idioms to help them. But the general principle is why the API is so permissive. It was a policy decision when the intrinsic was designed. It puts responsibility for corner cases on the Java caller, where they belong – if in fact the assembly stub chooses to refuse a corner case. But mismatch, perhaps, does not need to refuse a corner case. Perhaps. I’m leaving that question open at the moment. We could keep the originally designed permissiveness by adding a Java wrapper that handles the any corner cases refused by the intrinsic. That’s where hacks like unsafe unaligned loads and masking would go, in the wrapper. And the wrapper would do ALL the work, if the intrinsic is disabled for some reason – it can return ~N, saying "please do it all, Java". That should go in one place. Do we find ourselves replicating such defensive logic today? A refactor in Java is cheaper than changing an assembly-language intrinsic. Suppose all intrinsics never return ~N, and in fact always return -1. That’s today’s argument – why not just settle on -1 and give poor Java a break. (See the tension there? Changing an intrinsic API to simplify Java code? That’s exactly backwards.) But maybe we can get away with it; maybe the original design decision to return -1-unprocessed was a YAGNI move, and maybe all intrinsic implementations forever will happily process all matching characters. Forever. The tension there is we are betting that future CPUs will be equally happy to handle all the inputs (notably, the post-loops over vector-fragments). Frankly, I think it’s likely, given where the VPU design trends have gone recently (since the mismatch API was designed). There seem to be no more vector units that segfault on misaligned input, and V-ISAs are increasingly user-friendly. So that’s how the problem lays itself out, for me. Lazy intrinsics (in this case, not all case) don’t seem to be a requirement, given the V-ISAs on the map now. But, on the other hand, if you are annoyed that some Java call sites are violating DRY, don’t reach for assembly code, go press the refactor button in your Java IDE. That is, keep the laziness and fix the Java code, or (if we are willing to bet that assembly stubs can forevermore be non-lazy) change the API as suggested, and completely: Nobody ever returns N<-1, ever. HTH ------------- PR Comment: https://git.openjdk.org/jdk/pull/31802#issuecomment-5050852321
