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