diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ff92765ccaaefa57e986ce4810a2fdac569c888..b312ab1a854533b52ce69b82a6f230e0fe45a4a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
* Java execution mode now provides detailed backtraces of failed Kernel threads including passId, groupIds, globalIds and localIds
* Internal translation of bytecode is now facilitated by the BCEL library
* Scala support has been added (see unit tests).
+* Fix arrays of AtomicInteger stored on local variables no longer fail with type cast exception while generating OpenCL (support for I_ALOAD_0,1,2,3 bytecode instructions)
## 1.9.0
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 07babbf4de7efeab2df0cec5c26522ab7b372c29..7f4eef9480bced902a861278aebc7f6ff5cc53ae 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -58,3 +58,4 @@ Below are some of the specific details of various contributions.
* Luis Mendes submited PR to Fix Java execution mode to fail-fast when Kernel execution fails
* Luis Mendes submited PR to Java execution mode now provides detailed backtraces of failed Kernel threads including passId, groupIds, globalIds and localIds
* Saurabh Rawat contributed apache BCEL bytecode parsing and Scala support.
+* Luis Mendes submited PR #139 to Fix arrays of AtomicInteger stored on local variables no longer fail with type cast exception - Partial fix for #138
\ No newline at end of file
diff --git a/src/main/java/com/aparapi/internal/writer/BlockWriter.java b/src/main/java/com/aparapi/internal/writer/BlockWriter.java
index 22789c60d42ddd4e56c079f75f0b87d8e51cb389..013239d05513c2e6025191a5af2eac73cd9be252 100644
--- a/src/main/java/com/aparapi/internal/writer/BlockWriter.java
+++ b/src/main/java/com/aparapi/internal/writer/BlockWriter.java
@@ -790,12 +790,20 @@ public abstract class BlockWriter{
}
private boolean isMultiDimensionalArray(final AccessArrayElement arrayLoadInstruction) {
- AccessField accessInstanceField = getUltimateInstanceFieldAccess(arrayLoadInstruction);
- return isMultiDimensionalArray(accessInstanceField.getConstantPoolFieldEntry().getNameAndTypeEntry());
+ AccessField accessInstanceField = getUltimateInstanceFieldAccess(arrayLoadInstruction);
+ if (accessInstanceField != null) {
+ return isMultiDimensionalArray(accessInstanceField.getConstantPoolFieldEntry().getNameAndTypeEntry());
+ } else {
+ //Arrays can be accessed through local variables instead of instance fields, thus, AccessField instruction
+ //can be null.
+ AccessLocalVariable accessLocalVariable = getUltimateInstanceLocalVarAccess(arrayLoadInstruction);
+ //Directly check for multi-dimensional array...
+ return accessLocalVariable.getLocalVariableInfo().getVariableDescriptor().startsWith("[[");
+ }
}
private boolean isObjectArray(final AccessArrayElement arrayLoadInstruction) {
- AccessField accessInstanceField = getUltimateInstanceFieldAccess(arrayLoadInstruction);
+ AccessField accessInstanceField = getUltimateInstanceFieldAccess(arrayLoadInstruction);
return isObjectArray(accessInstanceField.getConstantPoolFieldEntry().getNameAndTypeEntry());
}
@@ -804,10 +812,25 @@ public abstract class BlockWriter{
while (load instanceof I_AALOAD) {
load = load.getFirstChild();
}
+
+ if (load instanceof I_ALOAD ||
+ load instanceof I_ALOAD_0 || load instanceof I_ALOAD_1 || load instanceof I_ALOAD_2 || load instanceof I_ALOAD_3) {
+ //It is not a Field Access, it is either a constant index local variable (0..3), or is a variable indexed local variable (>3)
+ return null;
+ }
return (AccessField) load;
}
+ private AccessLocalVariable getUltimateInstanceLocalVarAccess(AccessArrayElement arrayLoadInstruction) {
+ Instruction load = arrayLoadInstruction.getArrayRef();
+ while (load instanceof I_AALOAD) {
+ load = load.getFirstChild();
+ }
+
+ return (AccessLocalVariable)load;
+ }
+
public void writeMethod(MethodCall _methodCall, MethodEntry _methodEntry) throws CodeGenException {
boolean noCL = _methodEntry.getOwnerClassModel().getNoCLMethods()
.contains(_methodEntry.getNameAndTypeEntry().getNameUTF8Entry().getUTF8());
diff --git a/src/test/java/com/aparapi/runtime/LocalAtomicVariableArrayTest.java b/src/test/java/com/aparapi/runtime/LocalAtomicVariableArrayTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f70f16cbe7fdd4057d3d272430892743331d46f
--- /dev/null
+++ b/src/test/java/com/aparapi/runtime/LocalAtomicVariableArrayTest.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2016 - 2018 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.aparapi.runtime;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.aparapi.Kernel;
+import com.aparapi.Range;
+import com.aparapi.device.Device;
+import com.aparapi.device.JavaDevice;
+import com.aparapi.device.OpenCLDevice;
+import com.aparapi.internal.kernel.KernelManager;
+
+public class LocalAtomicVariableArrayTest {
+ private static final int SIZE = 10;
+
+ private static OpenCLDevice openCLDevice = null;
+
+ private class CLKernelManager extends KernelManager {
+ @Override
+ protected List<Device.TYPE> getPreferredDeviceTypes() {
+ return Arrays.asList(Device.TYPE.ACC, Device.TYPE.GPU, Device.TYPE.CPU);
+ }
+ }
+
+ private class JTPKernelManager extends KernelManager {
+ private JTPKernelManager() {
+ LinkedHashSet<Device> preferredDevices = new LinkedHashSet<Device>(1);
+ preferredDevices.add(JavaDevice.THREAD_POOL);
+ setDefaultPreferredDevices(preferredDevices);
+ }
+ @Override
+ protected List<Device.TYPE> getPreferredDeviceTypes() {
+ return Arrays.asList(Device.TYPE.JTP);
+ }
+ }
+
+ @Before
+ public void setUpBeforeClass() throws Exception {
+ KernelManager.setKernelManager(new CLKernelManager());
+ Device device = KernelManager.instance().bestDevice();
+ assumeTrue (device != null && device instanceof OpenCLDevice);
+ openCLDevice = (OpenCLDevice) device;
+ }
+
+ @After
+ public void classTeardown() {
+ Util.resetKernelManager();
+ }
+
+ @Test
+ public void simpleConstIndexOpenCLTest() {
+ SimpleConstIndexLocalVarKernel myKernel = new SimpleConstIndexLocalVarKernel();
+ Range range = openCLDevice.createRange(SIZE, SIZE);
+ try {
+ myKernel.execute(range);
+ assertEquals("Atomic increment doesn't match, index 1", SIZE, myKernel.atomics[1].get());
+ assertEquals("Atomic increment doesn't match, index 2", SIZE, myKernel.atomics[2].get());
+ assertEquals("Atomic increment doesn't match, index 3", SIZE, myKernel.atomics[3].get());
+ } finally {
+ myKernel.dispose();
+ }
+ }
+
+ @Test
+ public void simpleVarIndexOpenCLTest() {
+ SimpleVarIndexLocalVarKernel myKernel = new SimpleVarIndexLocalVarKernel();
+ Range range = openCLDevice.createRange(SIZE, SIZE);
+ try {
+ myKernel.execute(range);
+ assertEquals("Atomic increment doesn't match", SIZE, myKernel.atomics[4].get());
+ } finally {
+ myKernel.dispose();
+ }
+ }
+
+ public class SimpleConstIndexLocalVarKernel extends Kernel {
+ private AtomicInteger[] atomics = new AtomicInteger[SIZE];
+
+ public SimpleConstIndexLocalVarKernel() {
+ for (int i = 0; i < atomics.length; i++) {
+ atomics[i] = new AtomicInteger(0);
+ }
+ }
+
+ @Override
+ public void run() {
+ atomicUpdate1(atomics, 1);
+ atomicUpdate2(2, atomics);
+ atomicUpdate3(3, 0, atomics);
+ }
+
+ public int atomicUpdate1(AtomicInteger[] arr, int index) {
+ //Exercises I_ALOAD_1
+ return atomicInc(arr[index]);
+ }
+
+ public int atomicUpdate2(int index, AtomicInteger[] arr) {
+ //Exercises I_ALOAD_2
+ return atomicInc(arr[index]);
+ }
+
+ public int atomicUpdate3(int index, int indexB, AtomicInteger[] arr) {
+ //Exercises I_ALOAD_3
+ return atomicInc(arr[index+indexB]);
+ }
+ }
+
+ public class SimpleVarIndexLocalVarKernel extends Kernel {
+ private AtomicInteger[] atomics = new AtomicInteger[SIZE];
+
+ public SimpleVarIndexLocalVarKernel() {
+ for (int i = 0; i < atomics.length; i++) {
+ atomics[i] = new AtomicInteger(0);
+ }
+ }
+
+ @Override
+ public void run() {
+ atomicUpdate4(4, 0, 0, atomics);
+ }
+
+ public int atomicUpdate4(int index, int indexB, int indexC, AtomicInteger[] arr) {
+ //Exercises I_ALOAD - when index is greater than 3
+ return atomicInc(arr[index+indexB+indexC]);
+ }
+ }
+}