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]); + } + } +}