1 /*
2 * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package hat;
26
27 import hat.backend.Backend;
28
29 import hat.buffer.DispatchContext;
30 import optkl.util.carriers.ArenaAndLookupCarrier;
31 import optkl.ifacemapper.BufferTracker;
32 import optkl.ifacemapper.MappableIface;
33
34
35 import java.lang.foreign.Arena;
36 import java.lang.invoke.MethodHandles;
37 import java.lang.reflect.Method;
38
39 import jdk.incubator.code.Op;
40 import jdk.incubator.code.Quoted;
41 import jdk.incubator.code.dialect.java.JavaOp;
42
43 import java.util.HashMap;
44 import java.util.Map;
45 import java.util.ServiceLoader;
46 import java.util.function.Consumer;
47 import java.util.function.Predicate;
48
49 import static hat.backend.Backend.FIRST;
50 import static optkl.OpHelper.Invoke.getTargetInvoke;
51 import static optkl.OpHelper.Lambda.lambda;
52
53
54 /**
55 * This class provides the developer facing view of HAT, and wraps a <a href="backend/Backend.html">Backend</a> capable of
56 * executing <b>NDRange</b> style execution.
57 * <p/>
58 * An Accelerator is provided a <a href="java/lang/invoke/MethodHandles.Lookup.html">MethodHandles.Lookup</a> with visibility to the
59 * compute to be performed.
60 * <p/>
61 * As well we either a <a href="backend/Backend.html">Backend</a> directly
62 * <pre>
63 * Accelerator accelerator =
64 * new Accelerator(MethodHandles.lookup(),
65 * new JavaMultiThreadedBackend());
66 * </pre>
67 * or a {@code java.util.function.Predicate<Backend>} which can be used to select the required {@code Backend}
68 * loaded via Javas ServiceLoader mechanism
69 * {@code}
70 * <pre>
71 * Accelerator accelerator =
72 * new Accelerator(MethodHandles.lookup(),
73 * be -> be.name().startsWith("OpenCL));
74 * </pre>}
75 *
76 * @author Gary Frost
77 */
78 public class Accelerator implements ArenaAndLookupCarrier, BufferTracker {
79
80 private final MethodHandles.Lookup lookup;
81 @Override public MethodHandles.Lookup lookup(){return lookup;}
82 public final Backend backend;
83
84 private final Map<Method, hat.ComputeContext> cache = new HashMap<>();
85
86 // public KernelContext kernelContext(NDRange ndRange) {
87 // return new KernelContext(ndRange);
88 // }
89 public DispatchContext dispatchContext(NDRange ndRange) {
90 var dispatchContext = DispatchContext.createDefault(this);
91 throw new RuntimeException("fill me");
92 // return dispatchContext;
93 }
94
95 protected Accelerator(MethodHandles.Lookup lookup, ServiceLoader.Provider<Backend> provider) {
96 this(lookup, provider.get());
97 }
98 public Accelerator(MethodHandles.Lookup lookup) {
99 this(lookup, FIRST);
100 }
101
102 /**
103 * @param lookup
104 * @param backend
105 */
106 public Accelerator(MethodHandles.Lookup lookup, Backend backend) {
107 this.lookup = lookup;
108 this.backend = backend;
109 }
110
111 /**
112 * @param lookup
113 * @param backendPredicate
114 */
115 public Accelerator(MethodHandles.Lookup lookup, Predicate<Backend> backendPredicate) {
116 this(lookup, Backend.getBackend(backendPredicate));
117 }
118
119 @Override
120 public void preMutate(MappableIface mappableIface) {
121 if (backend instanceof BufferTracker bufferTracker) {
122 bufferTracker.preMutate(mappableIface);
123 }
124 }
125
126 @Override
127 public void postMutate(MappableIface mappableIface) {
128 if (backend instanceof BufferTracker bufferTracker) {
129 bufferTracker.postMutate(mappableIface);
130 }
131 }
132
133 @Override
134 public void preAccess(MappableIface mappableIface) {
135 if (backend instanceof BufferTracker bufferTracker) {
136 bufferTracker.preAccess(mappableIface);
137 }
138 }
139
140 @Override
141 public void postAccess(MappableIface mappableIface) {
142 if (backend instanceof BufferTracker bufferTracker) {
143 bufferTracker.postAccess(mappableIface);
144 }
145 }
146
147 @Override
148 public Arena arena() {
149 return backend.arena();
150 }
151
152 /**
153 * An interface used for wrapping the compute entrypoint of work to be performed by the Accelerator.
154 * <p/>
155 * So given a ComputeClass such as...
156 * <pre>
157 * public class MyComputeClass {
158 * @ Reflect
159 * public static void addDeltaKernel(KernelContext kc, S32Array arrayOfInt, int delta) {
160 * arrayOfInt.array(kc.x, arrayOfInt.array(kc.x)+delta);
161 * }
162 *
163 * @ Reflect
164 * static public void doSomeWork(final ComputeContext cc, S32Array arrayOfInt) {
165 * }
166 * }
167 * </pre>
168 * The accelerator will be passed the doSomeWork entrypoint, wrapped in a {@code Compute}
169 * <pre>
170 * accelerator.compute(cc ->
171 * MyCompute.doSomeWork(cc, arrayOfInt)
172 * );
173 * </pre>
174 */
175 @FunctionalInterface
176 public interface Compute extends Consumer<ComputeContext> {
177 }
178
179 // convenience
180 public Config config(){
181 return backend.config();
182 }
183
184 /**
185 * This method provides the Accelerator with the {@code Compute Entrypoint} from a Compute class.
186 * <p>
187 * The entrypoint is wrapped in a {@link Compute} lambda.
188 *
189 * <pre>
190 * accelerator.compute(cc ->
191 * MyCompute.doSomeWork(cc, intArray)
192 * )
193 * </pre>
194 */
195 public void compute(Compute compute) {
196 Quoted<JavaOp.LambdaOp> quoted = Op.ofLambda(compute).orElseThrow();
197 JavaOp.LambdaOp lambda = quoted.op();
198 Method method = getTargetInvoke(this.lookup,lambda, ComputeContext.class).resolveMethodOrThrow();
199 // Create (or get cached) a compute context which closes over compute entrypoint and reachable kernels.
200 // The models of all compute and kernel methods are passed to the backend during creation
201 // The backend may well mutate the models.
202 // It will also use this opportunity to generate ISA specific code for the kernels.
203 ComputeContext computeContext = cache.computeIfAbsent(method, _ -> new ComputeContext(this, method));
204 // Here we get the captured values from the lambda
205 Object[] args = lambda(lookup,lambda).getQuotedCapturedValues( quoted, method);
206 args[0] = computeContext;
207 // now ask the backend to execute
208 backend.dispatchCompute(computeContext, args);
209 }
210 }