1 /*
  2  * Copyright (c) 2024, 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 
 26 #include "opencl_backend.h"
 27 
 28 OpenCLBackend::OpenCLBuffer *OpenCLBackend::getOrCreateBuffer(BufferState *bufferState) {
 29     OpenCLBuffer *openclBuffer;
 30     if (bufferState->vendorPtr == nullptr || bufferState->state == BufferState::NEW_STATE) {
 31         openclBuffer = new OpenCLBuffer(this, bufferState);
 32         if (config->trace) {
 33             std::cout << "[native] Device Buffer with size " << bufferState->length << " allocated" << std::endl;
 34         }
 35         bufferState->state = BufferState::NEW_STATE;
 36     } else {
 37         if (config->trace) {
 38             std::cout << "[native] Reusing a device-buffer " << std::hex << bufferState->vendorPtr << std::dec << std::endl;
 39         }
 40         openclBuffer = static_cast<OpenCLBuffer *>(bufferState->vendorPtr);
 41     }
 42     return openclBuffer;
 43 }
 44 
 45 bool OpenCLBackend::getBufferFromDeviceIfDirty(void *memorySegment, long memorySegmentLength) {
 46     if (config->traceCalls) {
 47         std::cout << "getBufferFromDeviceIfDirty(" << std::hex << (long) memorySegment << "," << std::dec <<
 48                 memorySegmentLength << "){" << std::endl;
 49     }
 50     if (config->minimizeCopies) {
 51         const BufferState *bufferState = BufferState::of(memorySegment, memorySegmentLength);
 52         if (bufferState->state == BufferState::DEVICE_OWNED) {
 53             queue->copyFromDevice(static_cast<Buffer *>(bufferState->vendorPtr));
 54             if (config->traceEnqueues | config->traceCopies) {
 55                 std::cout << "copying buffer from device (from java access) " << std::endl;
 56             }
 57             queue->wait();
 58             queue->release();
 59             bufferState->state = BufferState::HOST_OWNED;
 60         } else {
 61             std::cout << "HOW DID WE GET HERE 1 attempting  to get buffer but buffer is not device dirty" << std::endl;
 62             std::exit(1);
 63         }
 64     } else {
 65         std::cerr <<
 66                 "HOW DID WE GET HERE ? java side should avoid calling getBufferFromDeviceIfDirty as we are not minimising buffers!"
 67                 << std::endl;
 68         std::exit(1);
 69     }
 70     if (config->traceCalls) {
 71         std::cout << "}getBufferFromDeviceIfDirty()" << std::endl;
 72     }
 73     return true;
 74 }
 75 
 76 OpenCLBackend::OpenCLBackend(int configBits)
 77     : Backend(new Config(configBits), new OpenCLQueue(this)) {
 78 
 79     if (config->info) {
 80         std::cerr << "[INFO] Config Bits = " << std::hex << configBits << std::dec << std::endl;
 81     }
 82 
 83     cl_int status;
 84     cl_uint platformc = 0;
 85     OPENCL_CHECK(clGetPlatformIDs(0, nullptr, &platformc), "clGetPlatformIDs");
 86 
 87     if (config->platform >= platformc) {
 88         std::cerr << "We only have " << platformc << " platform" << ((platformc > 1) ? "s" : "") <<
 89                 " (platform[0]-platform[" << (platformc - 1) << "] inclusive) you requested platform[" << config->
 90                 platform << "]" << std::endl;
 91         std::exit(1);
 92     }
 93     auto *platforms = new cl_platform_id[platformc];
 94     OPENCL_CHECK(clGetPlatformIDs(platformc, platforms, nullptr), "clGetPlatformIDs");
 95 
 96     cl_uint numDevices = 0;
 97     platform_id = platforms[config->platform];
 98     status = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL, 0, nullptr, &numDevices);
 99     if (status != CL_SUCCESS) {
100         std::cerr << "clGetDeviceIDs (to get count) failed " << errorMsg(status) << std::endl;
101         delete[] platforms;
102         return;
103     }
104     if (config->device >= numDevices) {
105         std::cerr << "Platform[" << config->platform << "] only has " << numDevices << " device" << (
106                     (numDevices > 1) ? "s" : "") << " (device[0]-device[" << (numDevices - 1) <<
107                 "] inclusive) and you requested device[" << config->device << "]" << std::endl;
108         std::cerr << "No device available " << errorMsg(CL_DEVICE_NOT_AVAILABLE) << std::endl;
109         delete[] platforms;
110         std::exit(1);
111     }
112 
113     if (numDevices == 0) {
114         status = CL_DEVICE_NOT_AVAILABLE;
115         std::cerr << "No device available " << errorMsg(status) << std::endl;
116         delete[] platforms;
117         return;
118     }
119     auto *device_ids = new cl_device_id[numDevices]; // compute device id
120     if ((status = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL, numDevices, device_ids, nullptr)) != CL_SUCCESS) {
121         std::cerr << "clGetDeviceIDs failed " << errorMsg(status) << std::endl;
122         delete[] platforms;
123         delete[] device_ids;
124         return;
125     }
126     if ((context = clCreateContext(nullptr, 1, &device_ids[config->device], nullptr, nullptr, &status)) == nullptr ||
127         status != CL_SUCCESS) {
128         std::cerr << "clCreateContext failed " << errorMsg(status) << std::endl;
129         delete[] platforms;
130         delete[] device_ids;
131         return;
132     }
133 
134     cl_command_queue_properties queue_props = 0;
135     char vendor[256] = {0};
136     status = clGetDeviceInfo(device_ids[config->device], CL_DEVICE_VENDOR, sizeof(vendor), &vendor, nullptr);
137     if (status != CL_SUCCESS) {
138         std::cerr << "clGetDeviceInfo failed " << errorMsg(status) << std::endl;
139         delete[] platforms;
140         delete[] device_ids;
141         return;
142     }
143 
144     // We set the profile on if platform is Apple
145     if (std::strstr(vendor, "Apple") != nullptr) {
146         isPlatformProfileOn = true;
147     } else {
148         isPlatformProfileOn = false;
149 
150     }
151     if (config->profile || isPlatformProfileOn) {
152         queue_props = CL_QUEUE_PROFILING_ENABLE;
153     }
154     if (config->outOfOrder) {
155         // Check if the device supports Out Or Order first
156         cl_command_queue_properties queue_properties = 0;
157         status = clGetDeviceInfo(device_ids[config->device],
158             CL_DEVICE_QUEUE_PROPERTIES,
159             sizeof(queue_properties), &queue_properties,
160             nullptr);
161         if (queue_properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) {
162             queue_props |= CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE;
163         }
164     }
165     const auto openCLQueue = dynamic_cast<OpenCLQueue *>(queue);
166     if ((openCLQueue->command_queue = clCreateCommandQueue(context, device_ids[config->device], queue_props, &status))
167         == nullptr || status != CL_SUCCESS) {
168         std::cerr << "clCreateCommandQueue failed " << errorMsg(status) << std::endl;
169         clReleaseContext(context);
170         delete[] platforms;
171         delete[] device_ids;
172         return;
173     }
174     device_id = device_ids[config->device];
175     _platform_info = std::make_unique<PlatformInfo>(this);
176     delete[] device_ids;
177     delete[] platforms;
178 }
179 
180 OpenCLBackend::~OpenCLBackend() {
181     clReleaseContext(context);
182 }
183 
184 void OpenCLBackend::computeStart() {
185     if (config->trace) {
186         std::cout << "compute start" << std::endl;
187     }
188     queue->computeStart();
189 }
190 
191 void OpenCLBackend::computeEnd() {
192     queue->computeEnd();
193     queue->wait();
194 
195     if (config->profile) {
196         const auto openCLQueue = dynamic_cast<OpenCLQueue *>(queue);
197         openCLQueue->showEvents(100);
198     }
199     queue->release();
200     if (config->trace) {
201         std::cout << "compute end" << std::endl;
202     }
203 }
204 
205 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(OpenCLSource &openclSource) {
206     return compileProgram(&openclSource);
207 }
208 
209 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(const OpenCLSource *openclSource) {
210     return compileProgram(openclSource->len, openclSource->text);
211 }
212 
213 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(int len, char *text) {
214     return dynamic_cast<OpenCLProgram *>(compile(len, text, 0));
215 }
216 
217 Backend::CompilationUnit *OpenCLBackend::compile(int len, char *source, int typeModel) {
218     const size_t srcLen = ::strlen(source);
219     auto src = new char[srcLen + 1];
220     strncpy(src, source, srcLen);
221     src[srcLen] = '\0';
222     if (config->trace) {
223         std::cout << "native compiling " << src << std::endl;
224     }
225     cl_int status;
226     cl_program program;
227     if ((program = clCreateProgramWithSource(context, 1, (const char **) &src, nullptr, &status)) == nullptr ||
228         status != CL_SUCCESS) {
229         std::cerr << "clCreateProgramWithSource failed" << std::endl;
230         delete[] src;
231         return nullptr;
232     }
233 
234     cl_int buildStatus = clBuildProgram(program, 0, nullptr, nullptr, nullptr, nullptr);
235     if (buildStatus != CL_SUCCESS) {
236         std::cerr << "buildStatus =failed" << std::endl;
237     }
238     size_t logLen = 0;
239     OpenCLProgram *openclProgram = nullptr;
240     if ((status = clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, 0, nullptr, &logLen)) != CL_SUCCESS) {
241         std::cerr << "clGetBuildInfo (getting log size) failed" << std::endl;
242         openclProgram = new OpenCLProgram(this, src, nullptr, buildStatus == CL_SUCCESS, program);
243     } else {
244         clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_STATUS, sizeof(buildStatus), &buildStatus, nullptr);
245         if (config -> info && logLen > 0) {
246             char *log = new char[logLen + 1];
247             if ((status = clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, logLen + 1, log, nullptr)) != CL_SUCCESS) {
248                 std::cerr << "clGetBuildInfo (getting log) failed" << std::endl;
249                 delete[] log;
250                 log = nullptr;
251             } else {
252                 log[logLen] = '\0';
253                 if (logLen > 2) {
254                     std::cerr << "logLen = " << logLen << " log  = " << log << std::endl;
255                 }
256             }
257             openclProgram = new OpenCLProgram(this, src, log, buildStatus == CL_SUCCESS, program);
258         } else {
259             openclProgram = new OpenCLProgram(this, src, nullptr, buildStatus == CL_SUCCESS, program);
260         }
261     }
262     return openclProgram;
263 }
264 
265 const char *OpenCLBackend::errorMsg(cl_int status) {
266     static struct {
267         cl_int code;
268         const char *msg;
269     } error_table[] = {
270         // @formatter:off
271                 {CL_SUCCESS, "success"},
272                 {CL_DEVICE_NOT_FOUND, "device not found",},
273                 {CL_DEVICE_NOT_AVAILABLE, "device not available",},
274                 {CL_COMPILER_NOT_AVAILABLE, "compiler not available",},
275                 {CL_MEM_OBJECT_ALLOCATION_FAILURE, "mem object allocation failure",},
276                 {CL_OUT_OF_RESOURCES, "out of resources",},
277                 {CL_OUT_OF_HOST_MEMORY, "out of host memory",},
278                 {CL_PROFILING_INFO_NOT_AVAILABLE, "profiling not available",},
279                 {CL_MEM_COPY_OVERLAP, "memcopy overlaps",},
280                 {CL_IMAGE_FORMAT_MISMATCH, "image format mismatch",},
281                 {CL_IMAGE_FORMAT_NOT_SUPPORTED, "image format not supported",},
282                 {CL_BUILD_PROGRAM_FAILURE, "build program failed",},
283                 {CL_MAP_FAILURE, "map failed",},
284                 {CL_INVALID_VALUE, "invalid value",},
285                 {CL_INVALID_DEVICE_TYPE, "invalid device type",},
286                 {CL_INVALID_PLATFORM, "invlaid platform",},
287                 {CL_INVALID_DEVICE, "invalid device",},
288                 {CL_INVALID_CONTEXT, "invalid context",},
289                 {CL_INVALID_QUEUE_PROPERTIES, "invalid queue properties",},
290                 {CL_INVALID_COMMAND_QUEUE, "invalid command queue",},
291                 {CL_INVALID_HOST_PTR, "invalid host ptr",},
292                 {CL_INVALID_MEM_OBJECT, "invalid mem object",},
293                 {CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, "invalid image format descriptor ",},
294                 {CL_INVALID_IMAGE_SIZE, "invalid image size",},
295                 {CL_INVALID_SAMPLER, "invalid sampler",},
296                 {CL_INVALID_BINARY, "invalid binary",},
297                 {CL_INVALID_BUILD_OPTIONS, "invalid build options",},
298                 {CL_INVALID_PROGRAM, "invalid program ",},
299                 {CL_INVALID_PROGRAM_EXECUTABLE, "invalid program executable",},
300                 {CL_INVALID_KERNEL_NAME, "invalid kernel name",},
301                 {CL_INVALID_KERNEL_DEFINITION, "invalid definition",},
302                 {CL_INVALID_KERNEL, "invalid kernel",},
303                 {CL_INVALID_ARG_INDEX, "invalid arg index",},
304                 {CL_INVALID_ARG_VALUE, "invalid arg value",},
305                 {CL_INVALID_ARG_SIZE, "invalid arg size",},
306                 {CL_INVALID_KERNEL_ARGS, "invalid kernel args",},
307                 {CL_INVALID_WORK_DIMENSION, "invalid work dimension",},
308                 {CL_INVALID_WORK_GROUP_SIZE, "invalid work group size",},
309                 {CL_INVALID_WORK_ITEM_SIZE, "invalid work item size",},
310                 {CL_INVALID_GLOBAL_OFFSET, "invalid global offset",},
311                 {CL_INVALID_EVENT_WAIT_LIST, "invalid event wait list",},
312                 {CL_INVALID_EVENT, "invalid event",},
313                 {CL_INVALID_OPERATION, "invalid operation",},
314                 {CL_INVALID_GL_OBJECT, "invalid gl object",},
315                 {CL_INVALID_BUFFER_SIZE, "invalid buffer size",},
316                 {CL_INVALID_MIP_LEVEL, "invalid mip level",},
317                 {CL_INVALID_GLOBAL_WORK_SIZE, "invalid global work size",},
318                 {-9999, "enqueueNdRangeKernel Illegal read or write to a buffer",},
319                 {0, nullptr},
320                 // @formatter:on
321             };
322     for (int i = 0; error_table[i].msg != nullptr; i++) {
323         if (error_table[i].code == status) {
324             //std::cerr << " clerror '" << error_table[i].msg << "'" << std::endl;
325             return error_table[i].msg;
326         }
327     }
328     static char unknown[256];
329 #if defined (_WIN32)
330         _snprintf
331 #else
332     snprintf
333 #endif
334             (unknown, sizeof(unknown), "unmapped string for  error %d", status);
335     return unknown;
336 }
337 
338 extern "C" long getBackend(int configBits) {
339     return reinterpret_cast<long>(new OpenCLBackend(configBits));
340 }
341 
342 void __checkOpenclErrors(cl_int status, const char *functionName, const char *file, const int line) {
343     if (CL_SUCCESS != status) {
344         std::cerr << "Opencl Error ( " << functionName << ") with error code: " << status << " from file " << file <<
345                 " line " << line << std::endl;
346         exit(-1);
347     }
348 }
349 
350 OpenCLSource::OpenCLSource()
351     : Text(0L) {
352 }
353 
354 OpenCLSource::OpenCLSource(const size_t len)
355     : Text(len) {
356 }
357 
358 OpenCLSource::OpenCLSource(char *text)
359     : Text(text, false) {
360 }