Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d652298
Refine SIMD docs with real loop example and anti-pattern FAQ
liannacasper Apr 7, 2026
f94afc9
Add SIMD hint readiness diagnostics for verbose translator runs
liannacasper Apr 7, 2026
2b12085
Validate SIMD width hints before storing translator metadata
liannacasper Apr 7, 2026
cdb33b7
Emit SIMD width hint in generated C and test it
liannacasper Apr 7, 2026
b589082
Enforce SIMD annotation validity in translator
liannacasper Apr 8, 2026
27777a7
Emit NEON target pragmas for SIMD-eligible methods
liannacasper Apr 8, 2026
e53638f
Revert NEON pragma emission to fix benchmark crash
liannacasper Apr 8, 2026
44a33ba
Reintroduce SIMD pragmas behind opt-in guard
liannacasper Apr 8, 2026
cde14f2
Add weak SIMD hook dispatch for eligible translated methods
liannacasper Apr 8, 2026
7b2e32f
Add explicit SIMD Java API with scalar fallback
liannacasper Apr 9, 2026
50489dc
Expand explicit SIMD API and wire Base64 SIMD path
liannacasper Apr 9, 2026
521529e
Always enable SIMD API path and fold isSupported in translator
liannacasper Apr 9, 2026
d94c9c0
Simplify SIMD support probe and annotate translated SIMD API invokes
liannacasper Apr 9, 2026
976d86d
Remove SIMD hint annotation pipeline and hook emission
liannacasper Apr 9, 2026
9873e22
Fix Base64 SIMD dead store and reduce lane-call overhead
liannacasper Apr 10, 2026
6477697
Print translated Base64 symbols in integration benchmark
liannacasper Apr 10, 2026
816511c
Persist translated Base64 dump as test artifact file
liannacasper Apr 10, 2026
237e6e3
Add dedicated Base64 translation dump workflow
liannacasper Apr 10, 2026
2941646
Fix Base64 dump workflow dependency resolution
liannacasper Apr 10, 2026
321c9c2
Run Base64 dump workflow on hosted runner JDK 8
liannacasper Apr 10, 2026
5d86ccb
Allow reactor modules without matching test filter
liannacasper Apr 10, 2026
1c6f4fd
Use local core artifacts for Base64 translation dump
liannacasper Apr 10, 2026
4859c50
Inline SIMD.isSupported in CustomInvoke paths
liannacasper Apr 10, 2026
669b2a7
Force translated SIMD.isSupported method to return true
liannacasper Apr 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/base64-translation-dump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Base64 Translation Dump

on:
workflow_dispatch:
pull_request:
branches:
- master
paths:
- 'vm/ByteCodeTranslator/**'
- 'vm/tests/**'
- '.github/workflows/base64-translation-dump.yml'

permissions:
contents: read

jobs:
base64-translation-dump:
runs-on: ubuntu-latest
defaults:
run:
shell: bash

steps:
- uses: actions/checkout@v4

- name: Set up Java 8
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '8'

- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2

- name: Build ByteCodeTranslator
run: mvn -B -f vm/pom.xml -pl ByteCodeTranslator -am -DskipTests=true install

- name: Build Codename One core artifact
run: mvn -B -f maven/pom.xml -pl core -am -DunitTests=true -DskipTests=true -Dspotbugs.skip=true -Dmaven.javadoc.skip=true install

- name: Generate Base64 translation dump
run: mvn -B -f vm/pom.xml -pl tests -am -Dcodenameone.core.version=8.0-SNAPSHOT -Dtest=Base64PerformanceIntegrationTest -Dsurefire.failIfNoSpecifiedTests=false test

- name: Upload Base64 translation dump artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: base64-translation-dump
path: |
vm/tests/target/base64-translated-snippets.txt
vm/tests/target/surefire-reports/**
if-no-files-found: warn
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
216 changes: 216 additions & 0 deletions CodenameOne/src/com/codename1/simd/SIMD.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.simd;

/**
* Explicit SIMD helper API.
* <p>
* This API is intentionally tiny and designed to be bytecode-recognition friendly for
* platform translators that can lower these calls into native SIMD instructions.
* </p>
* <p>
* The default Java implementation is scalar and fully functional so code remains portable.
* </p>
*/
public final class SIMD {

private SIMD() {
throw new AssertionError("SIMD should not be instantiated");
}

/**
* Returns true when the current runtime+translator combo can map this API to a native SIMD backend.
* The default implementation returns false.
*
* @return true if SIMD backend support is available.
*/
public static boolean isSupported() {
return false;
}

/**
* 4-lane float vector value type.
*/
public static final class Float4 {
public final float x;
public final float y;
public final float z;
public final float w;

public Float4(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}

public static Float4 makeFloat4(float x, float y, float z, float w) {
return new Float4(x, y, z, w);
}

public static Float4 load(float[] values, int offset) {
return new Float4(
values[offset],
values[offset + 1],
values[offset + 2],
values[offset + 3]
);
}

public static void store(float[] values, int offset, Float4 value) {
values[offset] = value.x;
values[offset + 1] = value.y;
values[offset + 2] = value.z;
values[offset + 3] = value.w;
}

public static Float4 add(Float4 a, Float4 b) {
return new Float4(
a.x + b.x,
a.y + b.y,
a.z + b.z,
a.w + b.w
);
}

public static Float4 mul(Float4 a, Float4 b) {
return new Float4(
a.x * b.x,
a.y * b.y,
a.z * b.z,
a.w * b.w
);
}

public static Float4 fma(Float4 a, Float4 b, Float4 c) {
return add(mul(a, b), c);
}

/**
* 4-lane integer vector.
*/
public static final class Int4 {
public final int x;
public final int y;
public final int z;
public final int w;

public Int4(int x, int y, int z, int w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}

public static Int4 makeInt4(int x, int y, int z, int w) {
return new Int4(x, y, z, w);
}

public static Int4 add(Int4 a, Int4 b) {
return new Int4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}

public static Int4 sub(Int4 a, Int4 b) {
return new Int4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}

public static Int4 and(Int4 a, Int4 b) {
return new Int4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
}

public static Int4 or(Int4 a, Int4 b) {
return new Int4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
}

public static Int4 shl(Int4 a, int bits) {
return new Int4(a.x << bits, a.y << bits, a.z << bits, a.w << bits);
}

public static Int4 ushr(Int4 a, int bits) {
return new Int4(a.x >>> bits, a.y >>> bits, a.z >>> bits, a.w >>> bits);
}

/**
* Unsigned byte 16-lane vector.
*/
public static final class U8x16 {
private final int[] lanes = new int[16];

private U8x16() {
}
}

public static U8x16 loadU8(byte[] values, int offset) {
U8x16 out = new U8x16();
for (int i = 0; i < 16; i++) {
out.lanes[i] = values[offset + i] & 0xff;
}
return out;
}

public static int laneU8(U8x16 value, int lane) {
return value.lanes[lane] & 0xff;
}

public static U8x16 and(U8x16 a, U8x16 b) {
U8x16 out = new U8x16();
for (int i = 0; i < 16; i++) {
out.lanes[i] = (a.lanes[i] & b.lanes[i]) & 0xff;
}
return out;
}

public static U8x16 or(U8x16 a, U8x16 b) {
U8x16 out = new U8x16();
for (int i = 0; i < 16; i++) {
out.lanes[i] = (a.lanes[i] | b.lanes[i]) & 0xff;
}
return out;
}

public static U8x16 xor(U8x16 a, U8x16 b) {
U8x16 out = new U8x16();
for (int i = 0; i < 16; i++) {
out.lanes[i] = (a.lanes[i] ^ b.lanes[i]) & 0xff;
}
return out;
}

public static U8x16 shl(U8x16 a, int bits) {
U8x16 out = new U8x16();
for (int i = 0; i < 16; i++) {
out.lanes[i] = ((a.lanes[i] << bits) & 0xff);
}
return out;
}

public static U8x16 ushr(U8x16 a, int bits) {
U8x16 out = new U8x16();
for (int i = 0; i < 16; i++) {
out.lanes[i] = (a.lanes[i] >>> bits) & 0xff;
}
return out;
}
}
80 changes: 80 additions & 0 deletions CodenameOne/src/com/codename1/util/Base64.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

package com.codename1.util;

import com.codename1.simd.SIMD;

/// This class implements Base64 encoding/decoding functionality
/// as specified in RFC 2045 (http://www.ietf.org/rfc/rfc2045.txt).
public abstract class Base64 {
Expand Down Expand Up @@ -343,6 +345,9 @@ public static int encodeNoNewline(byte[] in, byte[] out) {
if (inputLength == 0) {
return 0;
}
if (SIMD.isSupported() && inputLength >= 64) {
return encodeNoNewlineSimdApi(in, out);
}
byte[] mapLocal = map;
int end = inputLength - (inputLength % 3);
int outIndex = 0;
Expand Down Expand Up @@ -416,4 +421,79 @@ public static int encodeNoNewline(byte[] in, byte[] out) {
}
return outIndex;
}

private static int encodeNoNewlineSimdApi(byte[] in, byte[] out) {
int inputLength = in.length;
byte[] mapLocal = map;
int end = inputLength - (inputLength % 3);
int outIndex = 0;
int i = 0;
for (; i + 16 <= end; i += 12) {
SIMD.U8x16 v = SIMD.loadU8(in, i);
int b0 = SIMD.laneU8(v, 0);
int b1 = SIMD.laneU8(v, 1);
int b2 = SIMD.laneU8(v, 2);
int b3 = SIMD.laneU8(v, 3);
int b4 = SIMD.laneU8(v, 4);
int b5 = SIMD.laneU8(v, 5);
int b6 = SIMD.laneU8(v, 6);
int b7 = SIMD.laneU8(v, 7);
int b8 = SIMD.laneU8(v, 8);
int b9 = SIMD.laneU8(v, 9);
int b10 = SIMD.laneU8(v, 10);
int b11 = SIMD.laneU8(v, 11);

out[outIndex++] = mapLocal[b0 >> 2];
out[outIndex++] = mapLocal[((b0 & 0x03) << 4) | (b1 >> 4)];
out[outIndex++] = mapLocal[((b1 & 0x0f) << 2) | (b2 >> 6)];
out[outIndex++] = mapLocal[b2 & 0x3f];

out[outIndex++] = mapLocal[b3 >> 2];
out[outIndex++] = mapLocal[((b3 & 0x03) << 4) | (b4 >> 4)];
out[outIndex++] = mapLocal[((b4 & 0x0f) << 2) | (b5 >> 6)];
out[outIndex++] = mapLocal[b5 & 0x3f];

out[outIndex++] = mapLocal[b6 >> 2];
out[outIndex++] = mapLocal[((b6 & 0x03) << 4) | (b7 >> 4)];
out[outIndex++] = mapLocal[((b7 & 0x0f) << 2) | (b8 >> 6)];
out[outIndex++] = mapLocal[b8 & 0x3f];

out[outIndex++] = mapLocal[b9 >> 2];
out[outIndex++] = mapLocal[((b9 & 0x03) << 4) | (b10 >> 4)];
out[outIndex++] = mapLocal[((b10 & 0x0f) << 2) | (b11 >> 6)];
out[outIndex++] = mapLocal[b11 & 0x3f];
}
for (; i < end; i += 3) {
int b0 = in[i] & 0xff;
int b1 = in[i + 1] & 0xff;
int b2 = in[i + 2] & 0xff;

out[outIndex++] = mapLocal[b0 >> 2];
out[outIndex++] = mapLocal[((b0 & 0x03) << 4) | (b1 >> 4)];
out[outIndex++] = mapLocal[((b1 & 0x0f) << 2) | (b2 >> 6)];
out[outIndex++] = mapLocal[b2 & 0x3f];
}
switch (inputLength - end) {
case 1: {
int b0 = in[end] & 0xff;
out[outIndex++] = mapLocal[b0 >> 2];
out[outIndex++] = mapLocal[(b0 & 0x03) << 4];
out[outIndex++] = '=';
out[outIndex++] = '=';
break;
}
case 2: {
int b0 = in[end] & 0xff;
int b1 = in[end + 1] & 0xff;
out[outIndex++] = mapLocal[b0 >> 2];
out[outIndex++] = mapLocal[((b0 & 0x03) << 4) | (b1 >> 4)];
out[outIndex++] = mapLocal[(b1 & 0x0f) << 2];
out[outIndex++] = '=';
break;
}
default:
break;
}
return outIndex;
}
}
Loading
Loading