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 #include <fstream>
26 #include <functional>
27 #define shared_cpp
28
29 #include "shared.h"
30
31 #define INFO 0
32
33 #include <cstring>
34
35 void Hex::ascii(std::ostream &s, char c) {
36 if (::iscntrl(c)) {
37 if (c == '\a') {
38 s << "\\a ";
39 } else if (c == '\r') {
40 s << "\\r ";
41 } else if (c == '\n') {
42 s << "\\n ";
43 } else if (c == '\t') {
44 s << "\\t ";
45 } else {
46 s << "?? ";
47 }
48 } else {
49 s << c << " ";
50 }
51 }
52
53 void Hex::hex(std::ostream &s, char c) {
54 s << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (c & 0xff) << " ";
55 }
56
57 void Hex::bytes(std::ostream &s, char *p, size_t len, std::function<void(std::ostream &)> prefix) {
58 for (int i = 0; i < len; i++) {
59 if ((i % 16) == 0) {
60 if (i > 0) {
61 s << " ";
62 for (int c = i - 16; c < i; c++) {
63 ascii(s, p[c]);
64 }
65 }
66 s << std::endl;
67 prefix(s);
68 s << std::hex << std::setw(6) << std::setfill('0') << i << " ";
69 }
70 hex(s, p[i]);
71 }
72
73 if ((len % 16) == 0) {
74 s << " ";
75 for (int c = len - 16; c < len; c++) {
76 ascii(s, p[c]);
77 }
78 } else {
79 for (int v = len % 16; v < 16; v++) {
80 s << " ";
81 }
82 s << " ";
83 for (int c = len - (len % 16); c < len; c++) {
84 ascii(s, p[c]);
85 }
86 }
87 }
88
89 void strutil::replaceInPlace(std::string &subject, const std::string &search,
90 const std::string &replace) {
91 size_t pos = 0;
92 while ((pos = subject.find(search, pos)) != std::string::npos) {
93 subject.replace(pos, search.length(), replace);
94 pos += replace.length();
95 }
96 }
97
98
99 bool strutil::endsWith(const std::string &str, const std::string &suffix) {
100 return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
101 }
102
103 char *strutil::clone(char *name) {
104 size_t len = ::strlen(name);
105 char *buf = new char[len + 1];
106 memcpy(buf, name, len);
107 buf[len] = '\0';
108 return buf;
109 }
110
111 void hexdump(void *ptr, int buflen) {
112 auto *buf = static_cast<unsigned char *>(ptr);
113 int i, j;
114 for (i = 0; i < buflen; i += 16) {
115 printf("%06x: ", i);
116 for (j = 0; j < 16; j++)
117 if (i + j < buflen)
118 printf("%02x ", buf[i + j]);
119 else
120 printf(" ");
121 printf(" ");
122 for (j = 0; j < 16; j++)
123 if (i + j < buflen)
124 printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
125 printf("\n");
126 }
127 }
128
129 void Sled::show(std::ostream &out, void *argArray) {
130 ArgSled argSled(static_cast<ArgArray_s *>(argArray));
131 for (int i = 0; i < argSled.argc(); i++) {
132 KernelArg *arg = argSled.arg(i);
133 switch (arg->variant) {
134 case '&': {
135 out << "Buf: of " << arg->value.buffer.sizeInBytes << " bytes " << std::endl;
136 break;
137 }
138 case 'B': {
139 out << "S8:" << arg->value.s8 << std::endl;
140 break;
141 }
142 case 'Z': {
143 out << "Z:" << arg->value.z1 << std::endl;
144 break;
145 }
146 case 'C': {
147 out << "U16:" << arg->value.u16 << std::endl;
148 break;
149 }
150 case 'S': {
151 out << "S16:" << arg->value.s16 << std::endl;
152 break;
153 }
154 case 'I': {
155 out << "S32:" << arg->value.s32 << std::endl;
156 break;
157 }
158 case 'F': {
159 out << "F32:" << arg->value.f32 << std::endl;
160 break;
161 }
162 case 'J': {
163 out << "S64:" << arg->value.s64 << std::endl;
164 break;
165 }
166 case 'D': {
167 out << "F64:" << arg->value.f64 << std::endl;
168 break;
169 }
170 default: {
171 std::cerr << "unexpected variant (shared.cpp) '" << static_cast<char>(arg->variant) << "'" << std::endl;
172 exit(1);
173 }
174 }
175 }
176 out << "schema len = " << argSled.schemaLen() << std::endl;
177
178 out << "schema = " << argSled.schema() << std::endl;
179 }
180
181
182 extern "C" void showDeviceInfo(long backendHandle) {
183 std::cout << "DEBUG through backendHandle to backend.showDeviceInfo()" << std::endl;
184 if (INFO) {
185 std::cout << "trampolining through backendHandle to backend.showDeviceInfo()" << std::endl;
186 }
187 auto *backend = reinterpret_cast<Backend *>(backendHandle);
188 backend->showDeviceInfo();
189 }
190
191 extern "C" void computeStart(long backendHandle) {
192 if (INFO) {
193 std::cout << "trampolining through backendHandle to backend.computeStart()" << std::endl;
194 }
195 auto *backend = reinterpret_cast<Backend *>(backendHandle);
196 backend->computeStart();
197 }
198
199 extern "C" void computeEnd(long backendHandle) {
200 if (INFO) {
201 std::cout << "trampolining through backendHandle to backend.computeEnd()" << std::endl;
202 }
203 auto *backend = reinterpret_cast<Backend *>(backendHandle);
204 backend->computeEnd();
205 }
206
207 extern "C" void releaseBackend(long backendHandle) {
208 auto *backend = reinterpret_cast<Backend *>(backendHandle);
209 delete backend;
210 }
211
212 extern "C" long compile(long backendHandle, int len, char *source) {
213 if (INFO) {
214 std::cout << "trampolining through backendHandle to backend.compile() "
215 << std::hex << backendHandle << std::dec << std::endl;
216 }
217 auto *backend = reinterpret_cast<Backend *>(backendHandle);
218 long compilationUnitHandle = reinterpret_cast<long>(backend->compile(len, source));
219 if (INFO) {
220 std::cout << "compilationUnitHandle = " << std::hex << compilationUnitHandle << std::dec << std::endl;
221 }
222 return compilationUnitHandle;
223 }
224
225 extern "C" long getKernel(long compilationUnitHandle, int nameLen, char *name) {
226 if (INFO) {
227 std::cout << "trampolining through programHandle to compilationUnit.getKernel()"
228 << std::hex << compilationUnitHandle << std::dec << std::endl;
229 }
230 auto compilationUnit = reinterpret_cast<Backend::CompilationUnit *>(compilationUnitHandle);
231 return reinterpret_cast<long>(compilationUnit->getKernel(nameLen, name));
232 }
233
234 extern "C" long ndrange(long kernelHandle, void *argArray) {
235 if (INFO) {
236 std::cout << "trampolining through kernelHandle to kernel.ndrange(...) " << std::endl;
237 }
238 auto kernel = reinterpret_cast<Backend::CompilationUnit::Kernel *>(kernelHandle);
239 kernel->ndrange(argArray);
240 return (long) 0;
241 }
242
243 extern "C" void releaseKernel(long kernelHandle) {
244 if (INFO) {
245 std::cout << "trampolining through to releaseKernel " << std::endl;
246 }
247 auto kernel = reinterpret_cast<Backend::CompilationUnit::Kernel *>(kernelHandle);
248 delete kernel;
249 }
250
251 extern "C" void releaseCompilationUnit(long compilationUnitHandle) {
252 if (INFO) {
253 std::cout << "trampolining through to releaseCompilationUnit " << std::endl;
254 }
255 auto compilationUnit = reinterpret_cast<Backend::CompilationUnit *>(compilationUnitHandle);
256 delete compilationUnit;
257 }
258
259 extern "C" bool compilationUnitOK(long compilationUnitHandle) {
260 if (INFO) {
261 std::cout << "trampolining through to compilationUnitHandleOK " << std::endl;
262 }
263 auto compilationUnit = reinterpret_cast<Backend::CompilationUnit *>(compilationUnitHandle);
264 return compilationUnit->compilationUnitOK();
265 }
266
267 extern "C" bool getBufferFromDeviceIfDirty(long backendHandle, long memorySegmentHandle, long memorySegmentLength) {
268 if (INFO) {
269 std::cout << "trampolining through to getBuffer " << std::endl;
270 }
271 auto backend = reinterpret_cast<Backend *>(backendHandle);
272 auto memorySegment = reinterpret_cast<void *>(memorySegmentHandle);
273 return backend->getBufferFromDeviceIfDirty(memorySegment, memorySegmentLength);
274 }
275
276
277 Backend::Config::Config(int configBits):BasicConfig(configBits) {
278
279 }
280
281 Backend::Config::~Config() = default;
282
283 Backend::Queue::Queue(Backend *backend)
284 : backend(backend) {
285 }
286
287 Backend::Queue::~Queue() = default;
288
289 Text::Text(size_t len, char *text, bool isCopy)
290 : len(len), text(text), isCopy(isCopy) {
291 // std::cout << "in Text len="<<len<<" isCopy="<<isCopy << std::endl;
292 }
293
294 Text::Text(char *text, bool isCopy)
295 : len(std::strlen(text)), text(text), isCopy(isCopy) {
296 // std::cout << "in Text len="<<len<<" isCopy="<<isCopy << std::endl;
297 }
298
299 Text::Text(size_t len)
300 : len(len), text(len > 0 ? new char[len] : nullptr), isCopy(true) {
301 // std::cout << "in Text len="<<len<<" isCopy="<<isCopy << std::endl;
302 }
303
304 void Text::write(const std::string &filename) const {
305 std::ofstream out;
306 out.open(filename, std::ofstream::trunc);
307 out.write(text, len);
308 out.close();
309 }
310
311 void Text::read(const std::string &filename) {
312 if (isCopy && text) {
313 delete[] text;
314 }
315 text = nullptr;
316 isCopy = false;
317 // std::cout << "reading from " << filename << std::endl;
318
319 std::ifstream ptxStream;
320 ptxStream.open(filename);
321
322
323 ptxStream.seekg(0, std::ios::end);
324 len = ptxStream.tellg();
325 ptxStream.seekg(0, std::ios::beg);
326
327 if (len > 0) {
328 text = new char[len];
329 isCopy = true;
330 //std::cerr << "about to read " << len << std::endl;
331 ptxStream.read(text, len);
332 ptxStream.close();
333 //std::cerr << "read " << len << std::endl;
334 text[len - 1] = '\0';
335 //std::cerr << "read text " << text << std::endl;
336 }
337 }
338
339 Text::~Text() {
340 if (isCopy && text) {
341 delete[] text;
342 }
343 text = nullptr;
344 isCopy = false;
345 len = 0;
346 }
347
348 Log::Log(const size_t len)
349 : Text(len) {
350 }
351
352 Log::Log(char *text)
353 : Text(text, false) {
354 }
355
356 long Backend::CompilationUnit::Kernel::ndrange(void *argArray) {
357 if (compilationUnit->backend->config->traceCalls) {
358 std::cout << "dispatchContext(\"" << name << "\"){" << std::endl;
359 }
360 ArgSled argSled(static_cast<ArgArray_s *>(argArray));
361 auto *profilableQueue = dynamic_cast<ProfilableQueue *>(compilationUnit->backend->queue);
362 if (profilableQueue != nullptr) {
363 profilableQueue->marker(ProfilableQueue::EnterKernelDispatchBits, name);
364 }
365 if (compilationUnit->backend->config->trace) {
366 Sled::show(std::cout, argArray);
367 }
368 KernelArg *argDispatchContext = argSled.arg(0);
369 DispatchContext *dispatchContext = static_cast<DispatchContext *>(argDispatchContext->value.buffer.memorySegment);
370 // Now arg[0] is the dispatchContext so we will extract it immediately
371 for (int i = 1; i < argSled.argc(); i++) {
372
373 KernelArg *arg = argSled.arg(i);
374 // std::cout << "in argsled loop id = "<< i<< " and arg->idx = " << arg->idx << std::endl;
375 switch (arg->variant) {
376 case '&': {
377 bool readAccessor = arg->value.buffer.access == RO_BYTE || arg->value.buffer.access == RW_BYTE || arg->value.buffer.access == UNKNOWN_BYTE;
378 if (compilationUnit->backend->config->trace) {
379 std::cout << "arg[" << i << "] = " << std::hex << (int) (arg->value.buffer.access);
380 switch (arg->value.buffer.access) {
381 case RO_BYTE:
382 std::cout << " RO";
383 break;
384 case WO_BYTE:
385 std::cout << " WO";
386 break;
387 case RW_BYTE:
388 std::cout << " RW";
389 break;
390 }
391 std::cout << std::endl;
392 }
393
394 BufferState *bufferState = BufferState::of(arg);
395
396 Buffer *buffer = compilationUnit->backend->getOrCreateBuffer(bufferState);
397
398 bool kernelReadsFromThisArg = arg->value.buffer.access == RW_BYTE
399 || arg->value.buffer.access == RO_BYTE;
400
401 bool copyToDevice = readAccessor;
402 if (!compilationUnit->backend->config->alwaysCopy) {
403 copyToDevice = (bufferState->state == BufferState::NEW_STATE)
404 || ((bufferState->state == BufferState::HOST_OWNED));
405 }
406
407 if (compilationUnit->backend->config->showWhy) {
408 std::cout << "config.alwaysCopy=" << compilationUnit->backend->config->alwaysCopy
409 << " | arg.RW=" << (arg->value.buffer.access == RW_BYTE)
410 << " | arg.RO=" << (arg->value.buffer.access == RO_BYTE)
411 << " | kernel.needsToRead=" << kernelReadsFromThisArg
412 << " | Buffer state = " << BufferState::stateNames[bufferState->state]
413 << " so "
414 << std::endl;
415 }
416 if (copyToDevice) {
417 compilationUnit->backend->queue->copyToDevice(buffer);
418 bufferState->state = BufferState::DEVICE_OWNED;
419 if (compilationUnit->backend->config->traceCopies) {
420 std::cout << "copying arg " << arg->idx-1 << " host->device " << std::endl;
421 }
422 } else {
423 if (compilationUnit->backend->config->traceSkippedCopies) {
424 std::cout << "NOT copying arg " << arg->idx-1 << " host->device " << std::endl;
425 }
426 }
427 setArg(arg, buffer);
428 if (compilationUnit->backend->config->trace) {
429 std::cout << "set buffer arg " << arg->idx-1 << std::endl;
430 }
431 break;
432 }
433 case 'B':
434 case 'S':
435 case 'C':
436 case 'I':
437 case 'F':
438 case 'J':
439 case 'D': {
440 setArg(arg);
441 if (compilationUnit->backend->config->trace) {
442 std::cerr << "set " << arg->variant << " " << arg->idx-1 << std::endl;
443 }
444 break;
445 }
446 default: {
447 std::cerr << "unexpected variant setting args in OpenCLKernel::dispatchContext " << (char) arg->variant <<
448 std::endl;
449 exit(1);
450 }
451 }
452 }
453
454 if (dispatchContext == nullptr) {
455 std::cerr << "Looks like we received a kernel dispatch with zero args kernel='" << name << "'" << std::endl;
456 exit(1);
457 }
458
459 if (compilationUnit->backend->config->trace) {
460 std::cout << "dispatchContext = <" << dispatchContext->gsx << "," << dispatchContext->gsy << "," << dispatchContext->gsz << ">" << std::endl;
461 }
462
463 compilationUnit->backend->queue->dispatch(dispatchContext, this);
464
465 for (int i = 1; i < argSled.argc(); i++) {
466 // note i above = 1... we never need to copy back the KernelContext fix this for DispatchContext
467 KernelArg *arg = argSled.arg(i);
468 // std::cout << "out argsled loop id = "<< i<< " and arg->idx = " << arg->idx << std::endl;
469 if (arg->variant == '&') {
470 BufferState *bufferState = BufferState::of(arg);
471
472 bool kernelWroteToThisArg = (arg->value.buffer.access == WO_BYTE) || (arg->value.buffer.access == RW_BYTE);
473 if (compilationUnit->backend->config->showWhy) {
474 std::cout <<
475 "config.alwaysCopy=" << compilationUnit->backend->config->alwaysCopy
476 << " | arg.WO=" << (arg->value.buffer.access == WO_BYTE)
477 << " | arg.RW=" << (arg->value.buffer.access == RW_BYTE)
478 << " | kernel.wroteToThisArg=" << kernelWroteToThisArg
479 << "Buffer state = " << BufferState::stateNames[bufferState->state]
480 << " so "
481 << std::endl;
482 }
483
484 auto *buffer = static_cast<Buffer *>(bufferState->vendorPtr);
485 if (kernelWroteToThisArg && compilationUnit->backend->config->alwaysCopy) {
486 compilationUnit->backend->queue->copyFromDevice(buffer);
487 bufferState->state = BufferState::HOST_OWNED;
488 if (compilationUnit->backend->config->traceCopies || compilationUnit->backend->config->traceEnqueues) {
489 std::cout << "copying arg " << arg->idx-1 << " device->host " << std::endl;
490 }
491 } else {
492 if (compilationUnit->backend->config->traceSkippedCopies) {
493 std::cout << "NOT copying arg " << arg->idx-1 << " device->host " << std::endl;
494 }
495 }
496 }
497 }
498 if (profilableQueue != nullptr) {
499 profilableQueue->marker(Backend::ProfilableQueue::LeaveKernelDispatchBits, name);
500 }
501 compilationUnit->backend->queue->wait();
502 compilationUnit->backend->queue->release();
503 if (compilationUnit->backend->config->traceCalls) {
504 std::cout << "\"" << name << "\"}" << std::endl;
505 }
506 return 0;
507 }