Skip to content

Fix #3108: conversion opcodes must update runtime type tag#5009

Merged
shai-almog merged 1 commit into
masterfrom
fix-3108-conversion-type-tag
May 23, 2026
Merged

Fix #3108: conversion opcodes must update runtime type tag#5009
shai-almog merged 1 commit into
masterfrom
fix-3108-conversion-type-tag

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • Follow-up to Fix #3108: don't rely on C arg eval order in PUTFIELD/MULTIANEWARRAY #4985. That PR fixed the PUTFIELD emission for chained assignments but the NPE in ddyer's reproducer still reproduced because the conversion opcodes (I2D, I2L, F2D, F2L, L2D) updated SP[-1].data.* while leaving the runtime type tag stale.
  • BC_DUP2_X1 / BC_DUP2_X2 / BC_DUP_X2 dispatch on IS_DOUBLE_WORD(SP[-1].type). After PUSH_INT + I2D (data updated, tag still CN1_TYPE_INT), DUP2_X1 took 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.
  • Every cat-changing conversion (and the narrowing inverses, for symmetry) now writes SP[-1].type = CN1_TYPE_*. Parallel macros in cn1_globals.h get the same treatment so the runtime header is consistent.

Reproducer

Reduced from ddyer's program in #3108:

private double boardScaleIndex, newBoardScaleIndex;
public void init() {
    boardScaleIndex = newBoardScaleIndex = Math.min(numBoardScales - 1, (int)(isi * SCALE));
}

Bytecode: invokestatic Math.min(II)Ii2ddup2_x1 → two putfields. 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

  • Standalone C test mirroring the BC_* macros: second putfield's target resolved to 0x4028000000000000 (= IEEE-754 bits of 12.0) pre-fix; resolves to the correct this pointer post-fix.
  • Re-ran the translator on ddyer's reproducer — emitted C now contains SP[-1].type = CN1_TYPE_DOUBLE; before BC_DUP2_X1.
  • New regression test conversionOpcodesUpdateRuntimeTypeTag covers all 12 conversion opcodes.
  • Full BytecodeInstructionIntegrationTest (72 tests) passes.

Test plan

  • mvn test -Dtest=BytecodeInstructionIntegrationTest (72/72 pass)
  • Translator regenerated for reproducer; emitted C shows tag write before BC_DUP2_X1
  • Push translator + matching cn1_globals.h to build server and confirm with ddyer that IOS miscompilation, results in nullPointerException #3108 reproducer no longer NPEs on iOS

🤖 Generated with Claude Code

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>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 23, 2026

Compared 20 screenshots: 20 matched.
✅ JavaScript-port screenshot tests passed.

@github-actions
Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog shai-almog linked an issue May 23, 2026 that may be closed by this pull request
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 23, 2026

Compared 109 screenshots: 109 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 262 seconds

Build and Run Timing

Metric Duration
Simulator Boot 64000 ms
Simulator Boot (Run) 2000 ms
App Install 16000 ms
App Launch 12000 ms
Test Execution 315000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 509.000 ms
Base64 CN1 encode 1245.000 ms
Base64 encode ratio (CN1/native) 2.446x (144.6% slower)
Base64 native decode 269.000 ms
Base64 CN1 decode 855.000 ms
Base64 decode ratio (CN1/native) 3.178x (217.8% slower)
Base64 SIMD encode 478.000 ms
Base64 encode ratio (SIMD/native) 0.939x (6.1% faster)
Base64 encode ratio (SIMD/CN1) 0.384x (61.6% faster)
Base64 SIMD decode 420.000 ms
Base64 decode ratio (SIMD/native) 1.561x (56.1% slower)
Base64 decode ratio (SIMD/CN1) 0.491x (50.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 60.000 ms
Image createMask (SIMD on) 10.000 ms
Image createMask ratio (SIMD on/off) 0.167x (83.3% faster)
Image applyMask (SIMD off) 133.000 ms
Image applyMask (SIMD on) 54.000 ms
Image applyMask ratio (SIMD on/off) 0.406x (59.4% faster)
Image modifyAlpha (SIMD off) 145.000 ms
Image modifyAlpha (SIMD on) 62.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.428x (57.2% faster)
Image modifyAlpha removeColor (SIMD off) 181.000 ms
Image modifyAlpha removeColor (SIMD on) 67.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.370x (63.0% faster)
Image PNG encode (SIMD off) 1113.000 ms
Image PNG encode (SIMD on) 1849.000 ms
Image PNG encode ratio (SIMD on/off) 1.661x (66.1% slower)
Image JPEG encode 916.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 23, 2026

Compared 110 screenshots: 110 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 376 seconds

Build and Run Timing

Metric Duration
Simulator Boot 102000 ms
Simulator Boot (Run) 1000 ms
App Install 16000 ms
App Launch 12000 ms
Test Execution 304000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 824.000 ms
Base64 CN1 encode 1704.000 ms
Base64 encode ratio (CN1/native) 2.068x (106.8% slower)
Base64 native decode 543.000 ms
Base64 CN1 decode 1331.000 ms
Base64 decode ratio (CN1/native) 2.451x (145.1% slower)
Base64 SIMD encode 748.000 ms
Base64 encode ratio (SIMD/native) 0.908x (9.2% faster)
Base64 encode ratio (SIMD/CN1) 0.439x (56.1% faster)
Base64 SIMD decode 773.000 ms
Base64 decode ratio (SIMD/native) 1.424x (42.4% slower)
Base64 decode ratio (SIMD/CN1) 0.581x (41.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 61.000 ms
Image createMask (SIMD on) 20.000 ms
Image createMask ratio (SIMD on/off) 0.328x (67.2% faster)
Image applyMask (SIMD off) 365.000 ms
Image applyMask (SIMD on) 107.000 ms
Image applyMask ratio (SIMD on/off) 0.293x (70.7% faster)
Image modifyAlpha (SIMD off) 152.000 ms
Image modifyAlpha (SIMD on) 58.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.382x (61.8% faster)
Image modifyAlpha removeColor (SIMD off) 226.000 ms
Image modifyAlpha removeColor (SIMD on) 143.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.633x (36.7% faster)
Image PNG encode (SIMD off) 1398.000 ms
Image PNG encode (SIMD on) 1031.000 ms
Image PNG encode ratio (SIMD on/off) 0.737x (26.3% faster)
Image JPEG encode 538.000 ms

@github-actions
Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 648 total, 0 failed, 2 skipped

Benchmark Results

  • Execution Time: 9838 ms

  • Hotspots (Top 20 sampled methods):

    • 21.94% java.lang.String.indexOf (377 samples)
    • 21.19% com.codename1.tools.translator.Parser.isMethodUsed (364 samples)
    • 18.04% java.util.ArrayList.indexOf (310 samples)
    • 5.36% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (92 samples)
    • 3.96% java.lang.Object.hashCode (68 samples)
    • 2.68% java.lang.System.identityHashCode (46 samples)
    • 2.39% com.codename1.tools.translator.ByteCodeClass.calcUsedByNative (41 samples)
    • 1.92% com.codename1.tools.translator.ByteCodeClass.updateAllDependencies (33 samples)
    • 1.92% com.codename1.tools.translator.ByteCodeClass.markDependent (33 samples)
    • 1.46% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (25 samples)
    • 1.28% com.codename1.tools.translator.Parser.getClassByName (22 samples)
    • 1.11% com.codename1.tools.translator.Parser.cullMethods (19 samples)
    • 0.81% com.codename1.tools.translator.BytecodeMethod.appendMethodSignatureSuffixFromDesc (14 samples)
    • 0.81% java.lang.StringBuilder.append (14 samples)
    • 0.76% com.codename1.tools.translator.BytecodeMethod.isMethodUsedByNative (13 samples)
    • 0.70% java.lang.StringCoding.encode (12 samples)
    • 0.64% java.util.TreeMap.getEntry (11 samples)
    • 0.64% com.codename1.tools.translator.BytecodeMethod.optimize (11 samples)
    • 0.64% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (11 samples)
    • 0.58% java.io.FileOutputStream.writeBytes (10 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@shai-almog shai-almog merged commit a8f8671 into master May 23, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IOS miscompilation, results in nullPointerException

1 participant