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