Fix #3108: conversion opcodes must update runtime type tag#5009
Merged
Conversation
Follow-up to #4985. The PUTFIELD fix there was correct but only addressed half of the chained-assignment NPE. Repro (ddyer's original program): private double boardScaleIndex, newBoardScaleIndex; boardScaleIndex = newBoardScaleIndex = Math.min(n, (int) expr); javac emits PUSH_INT (Math.min result), I2D, DUP2_X1, two PUTFIELDs. The I2D / I2L / F2D / F2L / L2D macros only wrote SP[-1].data.* and left the runtime type tag untouched. BC_DUP2_X1 / BC_DUP2_X2 / BC_DUP_X2 dispatch via IS_DOUBLE_WORD(...) on that tag, so an I2D-produced double whose tag still reads CN1_TYPE_INT sent the dup down the cat-1 branch: - SP bumped by +2 instead of +1 - three slots reshuffled as if all single-word - first putfield happened to read the right operands (lucky) - second putfield read the double's IEEE-754 bit pattern as the object reference and crashed with NPE on iOS Reproduced end-to-end with a standalone C test that mirrors the BC_* macros: post-fix the second putfield's target resolves to the expected this pointer; pre-fix it resolved to 0x4028000000000000 (= bits of 12.0). Each cat-changing conversion now rewrites SP[-1].type to the new CN1_TYPE_*. The narrowing inverses (L2I/L2F, D2I/D2F) and the pure cat-1 conversion I2F do the same for symmetry — DUP2/DUP_X2 path can hit them in less obvious bytecode sequences. I2B/I2C/I2S already preserve CN1_TYPE_INT, so they're left alone. Regression test conversionOpcodesUpdateRuntimeTypeTag asserts each of the 12 conversion opcodes emits the corresponding SP[-1].type = CN1_TYPE_* write. Full BytecodeInstructionIntegrationTest (72 tests) passes; verified the fail-on-pre-fix-code shape too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 20 screenshots: 20 matched. |
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Collaborator
Author
|
Compared 109 screenshots: 109 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Contributor
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
I2D,I2L,F2D,F2L,L2D) updatedSP[-1].data.*while leaving the runtime type tag stale.BC_DUP2_X1/BC_DUP2_X2/BC_DUP_X2dispatch onIS_DOUBLE_WORD(SP[-1].type). AfterPUSH_INT+I2D(data updated, tag stillCN1_TYPE_INT),DUP2_X1took the cat-1 branch, shifted SP by +2 instead of +1, and reshuffled three slots as if all single-word. The first putfield happened to read the right operands; the second read the double's bit pattern as the object pointer and crashed.SP[-1].type = CN1_TYPE_*. Parallel macros incn1_globals.hget the same treatment so the runtime header is consistent.Reproducer
Reduced from ddyer's program in #3108:
Bytecode:
invokestatic Math.min(II)I→i2d→dup2_x1→ twoputfields. Pre-fix translator emits I2D without a type-tag write, so the dup macro corrupts the stack and the second putfield NPEs on iOS.Verification
BC_*macros: second putfield's target resolved to0x4028000000000000(= IEEE-754 bits of12.0) pre-fix; resolves to the correctthispointer post-fix.SP[-1].type = CN1_TYPE_DOUBLE;beforeBC_DUP2_X1.conversionOpcodesUpdateRuntimeTypeTagcovers all 12 conversion opcodes.BytecodeInstructionIntegrationTest(72 tests) passes.Test plan
mvn test -Dtest=BytecodeInstructionIntegrationTest(72/72 pass)BC_DUP2_X1cn1_globals.hto build server and confirm with ddyer that IOS miscompilation, results in nullPointerException #3108 reproducer no longer NPEs on iOS🤖 Generated with Claude Code