Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/microsoft/clc/clc_helpers.cpp
4560 views
1
//
2
// Copyright 2012-2016 Francisco Jerez
3
// Copyright 2012-2016 Advanced Micro Devices, Inc.
4
// Copyright 2014-2016 Jan Vesely
5
// Copyright 2014-2015 Serge Martin
6
// Copyright 2015 Zoltan Gilian
7
//
8
// Permission is hereby granted, free of charge, to any person obtaining a
9
// copy of this software and associated documentation files (the "Software"),
10
// to deal in the Software without restriction, including without limitation
11
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
// and/or sell copies of the Software, and to permit persons to whom the
13
// Software is furnished to do so, subject to the following conditions:
14
//
15
// The above copyright notice and this permission notice shall be included in
16
// all copies or substantial portions of the Software.
17
//
18
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
// OTHER DEALINGS IN THE SOFTWARE.
25
26
#include <sstream>
27
28
#include <llvm/ADT/ArrayRef.h>
29
#include <llvm/IR/DiagnosticPrinter.h>
30
#include <llvm/IR/DiagnosticInfo.h>
31
#include <llvm/IR/LLVMContext.h>
32
#include <llvm/IR/Type.h>
33
#include <llvm/Support/raw_ostream.h>
34
#include <llvm-c/Core.h>
35
#include <llvm-c/Target.h>
36
#include <LLVMSPIRVLib/LLVMSPIRVLib.h>
37
38
#include <clang/CodeGen/CodeGenAction.h>
39
#include <clang/Lex/PreprocessorOptions.h>
40
#include <clang/Frontend/CompilerInstance.h>
41
#include <clang/Frontend/TextDiagnosticBuffer.h>
42
#include <clang/Frontend/TextDiagnosticPrinter.h>
43
#include <clang/Basic/TargetInfo.h>
44
45
#include <spirv-tools/libspirv.hpp>
46
#include <spirv-tools/linker.hpp>
47
48
#include "util/macros.h"
49
#include "glsl_types.h"
50
#include "nir.h"
51
#include "nir_types.h"
52
53
#include "clc_helpers.h"
54
#include "spirv.h"
55
56
#include "opencl-c.h.h"
57
#include "opencl-c-base.h.h"
58
59
using ::llvm::Function;
60
using ::llvm::LLVMContext;
61
using ::llvm::Module;
62
using ::llvm::raw_string_ostream;
63
64
static void
65
llvm_log_handler(const ::llvm::DiagnosticInfo &di, void *data) {
66
raw_string_ostream os { *reinterpret_cast<std::string *>(data) };
67
::llvm::DiagnosticPrinterRawOStream printer { os };
68
di.print(printer);
69
}
70
71
class SPIRVKernelArg {
72
public:
73
SPIRVKernelArg(uint32_t id, uint32_t typeId) : id(id), typeId(typeId),
74
addrQualifier(CLC_KERNEL_ARG_ADDRESS_PRIVATE),
75
accessQualifier(0),
76
typeQualifier(0) { }
77
~SPIRVKernelArg() { }
78
79
uint32_t id;
80
uint32_t typeId;
81
std::string name;
82
std::string typeName;
83
enum clc_kernel_arg_address_qualifier addrQualifier;
84
unsigned accessQualifier;
85
unsigned typeQualifier;
86
};
87
88
class SPIRVKernelInfo {
89
public:
90
SPIRVKernelInfo(uint32_t fid, const char *nm) : funcId(fid), name(nm), vecHint(0) { }
91
~SPIRVKernelInfo() { }
92
93
uint32_t funcId;
94
std::string name;
95
std::vector<SPIRVKernelArg> args;
96
unsigned vecHint;
97
};
98
99
class SPIRVKernelParser {
100
public:
101
SPIRVKernelParser() : curKernel(NULL)
102
{
103
ctx = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);
104
}
105
106
~SPIRVKernelParser()
107
{
108
spvContextDestroy(ctx);
109
}
110
111
void parseEntryPoint(const spv_parsed_instruction_t *ins)
112
{
113
assert(ins->num_operands >= 3);
114
115
const spv_parsed_operand_t *op = &ins->operands[1];
116
117
assert(op->type == SPV_OPERAND_TYPE_ID);
118
119
uint32_t funcId = ins->words[op->offset];
120
121
for (auto &iter : kernels) {
122
if (funcId == iter.funcId)
123
return;
124
}
125
126
op = &ins->operands[2];
127
assert(op->type == SPV_OPERAND_TYPE_LITERAL_STRING);
128
const char *name = reinterpret_cast<const char *>(ins->words + op->offset);
129
130
kernels.push_back(SPIRVKernelInfo(funcId, name));
131
}
132
133
void parseFunction(const spv_parsed_instruction_t *ins)
134
{
135
assert(ins->num_operands == 4);
136
137
const spv_parsed_operand_t *op = &ins->operands[1];
138
139
assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);
140
141
uint32_t funcId = ins->words[op->offset];
142
143
SPIRVKernelInfo *kernel = NULL;
144
145
for (auto &kernel : kernels) {
146
if (funcId == kernel.funcId && !kernel.args.size()) {
147
curKernel = &kernel;
148
return;
149
}
150
}
151
}
152
153
void parseFunctionParam(const spv_parsed_instruction_t *ins)
154
{
155
const spv_parsed_operand_t *op;
156
uint32_t id, typeId;
157
158
if (!curKernel)
159
return;
160
161
assert(ins->num_operands == 2);
162
op = &ins->operands[0];
163
assert(op->type == SPV_OPERAND_TYPE_TYPE_ID);
164
typeId = ins->words[op->offset];
165
op = &ins->operands[1];
166
assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);
167
id = ins->words[op->offset];
168
curKernel->args.push_back(SPIRVKernelArg(id, typeId));
169
}
170
171
void parseName(const spv_parsed_instruction_t *ins)
172
{
173
const spv_parsed_operand_t *op;
174
const char *name;
175
uint32_t id;
176
177
assert(ins->num_operands == 2);
178
179
op = &ins->operands[0];
180
assert(op->type == SPV_OPERAND_TYPE_ID);
181
id = ins->words[op->offset];
182
op = &ins->operands[1];
183
assert(op->type == SPV_OPERAND_TYPE_LITERAL_STRING);
184
name = reinterpret_cast<const char *>(ins->words + op->offset);
185
186
for (auto &kernel : kernels) {
187
for (auto &arg : kernel.args) {
188
if (arg.id == id && arg.name.empty()) {
189
arg.name = name;
190
break;
191
}
192
}
193
}
194
}
195
196
void parseTypePointer(const spv_parsed_instruction_t *ins)
197
{
198
enum clc_kernel_arg_address_qualifier addrQualifier;
199
uint32_t typeId, storageClass;
200
const spv_parsed_operand_t *op;
201
202
assert(ins->num_operands == 3);
203
204
op = &ins->operands[0];
205
assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);
206
typeId = ins->words[op->offset];
207
208
op = &ins->operands[1];
209
assert(op->type == SPV_OPERAND_TYPE_STORAGE_CLASS);
210
storageClass = ins->words[op->offset];
211
switch (storageClass) {
212
case SpvStorageClassCrossWorkgroup:
213
addrQualifier = CLC_KERNEL_ARG_ADDRESS_GLOBAL;
214
break;
215
case SpvStorageClassWorkgroup:
216
addrQualifier = CLC_KERNEL_ARG_ADDRESS_LOCAL;
217
break;
218
case SpvStorageClassUniformConstant:
219
addrQualifier = CLC_KERNEL_ARG_ADDRESS_CONSTANT;
220
break;
221
default:
222
addrQualifier = CLC_KERNEL_ARG_ADDRESS_PRIVATE;
223
break;
224
}
225
226
for (auto &kernel : kernels) {
227
for (auto &arg : kernel.args) {
228
if (arg.typeId == typeId)
229
arg.addrQualifier = addrQualifier;
230
}
231
}
232
}
233
234
void parseOpString(const spv_parsed_instruction_t *ins)
235
{
236
const spv_parsed_operand_t *op;
237
std::string str;
238
239
assert(ins->num_operands == 2);
240
241
op = &ins->operands[1];
242
assert(op->type == SPV_OPERAND_TYPE_LITERAL_STRING);
243
str = reinterpret_cast<const char *>(ins->words + op->offset);
244
245
if (str.find("kernel_arg_type.") != 0)
246
return;
247
248
size_t start = sizeof("kernel_arg_type.") - 1;
249
250
for (auto &kernel : kernels) {
251
size_t pos;
252
253
pos = str.find(kernel.name, start);
254
if (pos == std::string::npos ||
255
pos != start || str[start + kernel.name.size()] != '.')
256
continue;
257
258
pos = start + kernel.name.size();
259
if (str[pos++] != '.')
260
continue;
261
262
for (auto &arg : kernel.args) {
263
if (arg.name.empty())
264
break;
265
266
size_t typeEnd = str.find(',', pos);
267
if (typeEnd == std::string::npos)
268
break;
269
270
arg.typeName = str.substr(pos, typeEnd - pos);
271
pos = typeEnd + 1;
272
}
273
}
274
}
275
276
void applyDecoration(uint32_t id, const spv_parsed_instruction_t *ins)
277
{
278
auto iter = decorationGroups.find(id);
279
if (iter != decorationGroups.end()) {
280
for (uint32_t entry : iter->second)
281
applyDecoration(entry, ins);
282
return;
283
}
284
285
const spv_parsed_operand_t *op;
286
uint32_t decoration;
287
288
assert(ins->num_operands >= 2);
289
290
op = &ins->operands[1];
291
assert(op->type == SPV_OPERAND_TYPE_DECORATION);
292
decoration = ins->words[op->offset];
293
294
for (auto &kernel : kernels) {
295
for (auto &arg : kernel.args) {
296
if (arg.id == id) {
297
switch (decoration) {
298
case SpvDecorationVolatile:
299
arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_VOLATILE;
300
break;
301
case SpvDecorationConstant:
302
arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_CONST;
303
break;
304
case SpvDecorationRestrict:
305
arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_RESTRICT;
306
break;
307
case SpvDecorationFuncParamAttr:
308
op = &ins->operands[2];
309
assert(op->type == SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE);
310
switch (ins->words[op->offset]) {
311
case SpvFunctionParameterAttributeNoAlias:
312
arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_RESTRICT;
313
break;
314
case SpvFunctionParameterAttributeNoWrite:
315
arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_CONST;
316
break;
317
}
318
break;
319
}
320
}
321
322
}
323
}
324
}
325
326
void parseOpDecorate(const spv_parsed_instruction_t *ins)
327
{
328
const spv_parsed_operand_t *op;
329
uint32_t id;
330
331
assert(ins->num_operands >= 2);
332
333
op = &ins->operands[0];
334
assert(op->type == SPV_OPERAND_TYPE_ID);
335
id = ins->words[op->offset];
336
337
applyDecoration(id, ins);
338
}
339
340
void parseOpGroupDecorate(const spv_parsed_instruction_t *ins)
341
{
342
assert(ins->num_operands >= 2);
343
344
const spv_parsed_operand_t *op = &ins->operands[0];
345
assert(op->type == SPV_OPERAND_TYPE_ID);
346
uint32_t groupId = ins->words[op->offset];
347
348
auto lowerBound = decorationGroups.lower_bound(groupId);
349
if (lowerBound != decorationGroups.end() &&
350
lowerBound->first == groupId)
351
// Group already filled out
352
return;
353
354
auto iter = decorationGroups.emplace_hint(lowerBound, groupId, std::vector<uint32_t>{});
355
auto& vec = iter->second;
356
vec.reserve(ins->num_operands - 1);
357
for (uint32_t i = 1; i < ins->num_operands; ++i) {
358
op = &ins->operands[i];
359
assert(op->type == SPV_OPERAND_TYPE_ID);
360
vec.push_back(ins->words[op->offset]);
361
}
362
}
363
364
void parseOpTypeImage(const spv_parsed_instruction_t *ins)
365
{
366
const spv_parsed_operand_t *op;
367
uint32_t typeId;
368
unsigned accessQualifier = CLC_KERNEL_ARG_ACCESS_READ;
369
370
op = &ins->operands[0];
371
assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);
372
typeId = ins->words[op->offset];
373
374
if (ins->num_operands >= 9) {
375
op = &ins->operands[8];
376
assert(op->type == SPV_OPERAND_TYPE_ACCESS_QUALIFIER);
377
switch (ins->words[op->offset]) {
378
case SpvAccessQualifierReadOnly:
379
accessQualifier = CLC_KERNEL_ARG_ACCESS_READ;
380
break;
381
case SpvAccessQualifierWriteOnly:
382
accessQualifier = CLC_KERNEL_ARG_ACCESS_WRITE;
383
break;
384
case SpvAccessQualifierReadWrite:
385
accessQualifier = CLC_KERNEL_ARG_ACCESS_WRITE |
386
CLC_KERNEL_ARG_ACCESS_READ;
387
break;
388
}
389
}
390
391
for (auto &kernel : kernels) {
392
for (auto &arg : kernel.args) {
393
if (arg.typeId == typeId) {
394
arg.accessQualifier = accessQualifier;
395
arg.addrQualifier = CLC_KERNEL_ARG_ADDRESS_GLOBAL;
396
}
397
}
398
}
399
}
400
401
void parseExecutionMode(const spv_parsed_instruction_t *ins)
402
{
403
uint32_t executionMode = ins->words[ins->operands[1].offset];
404
if (executionMode != SpvExecutionModeVecTypeHint)
405
return;
406
407
uint32_t funcId = ins->words[ins->operands[0].offset];
408
uint32_t vecHint = ins->words[ins->operands[2].offset];
409
for (auto& kernel : kernels) {
410
if (kernel.funcId == funcId)
411
kernel.vecHint = vecHint;
412
}
413
}
414
415
static spv_result_t
416
parseInstruction(void *data, const spv_parsed_instruction_t *ins)
417
{
418
SPIRVKernelParser *parser = reinterpret_cast<SPIRVKernelParser *>(data);
419
420
switch (ins->opcode) {
421
case SpvOpName:
422
parser->parseName(ins);
423
break;
424
case SpvOpEntryPoint:
425
parser->parseEntryPoint(ins);
426
break;
427
case SpvOpFunction:
428
parser->parseFunction(ins);
429
break;
430
case SpvOpFunctionParameter:
431
parser->parseFunctionParam(ins);
432
break;
433
case SpvOpFunctionEnd:
434
case SpvOpLabel:
435
parser->curKernel = NULL;
436
break;
437
case SpvOpTypePointer:
438
parser->parseTypePointer(ins);
439
break;
440
case SpvOpTypeImage:
441
parser->parseOpTypeImage(ins);
442
break;
443
case SpvOpString:
444
parser->parseOpString(ins);
445
break;
446
case SpvOpDecorate:
447
parser->parseOpDecorate(ins);
448
break;
449
case SpvOpGroupDecorate:
450
parser->parseOpGroupDecorate(ins);
451
break;
452
case SpvOpExecutionMode:
453
parser->parseExecutionMode(ins);
454
break;
455
default:
456
break;
457
}
458
459
return SPV_SUCCESS;
460
}
461
462
bool parsingComplete()
463
{
464
for (auto &kernel : kernels) {
465
if (kernel.name.empty())
466
return false;
467
468
for (auto &arg : kernel.args) {
469
if (arg.name.empty() || arg.typeName.empty())
470
return false;
471
}
472
}
473
474
return true;
475
}
476
477
void parseBinary(const struct spirv_binary &spvbin)
478
{
479
/* 3 passes should be enough to retrieve all kernel information:
480
* 1st pass: all entry point name and number of args
481
* 2nd pass: argument names and type names
482
* 3rd pass: pointer type names
483
*/
484
for (unsigned pass = 0; pass < 3; pass++) {
485
spvBinaryParse(ctx, reinterpret_cast<void *>(this),
486
spvbin.data, spvbin.size / 4,
487
NULL, parseInstruction, NULL);
488
489
if (parsingComplete())
490
return;
491
}
492
493
assert(0);
494
}
495
496
std::vector<SPIRVKernelInfo> kernels;
497
std::map<uint32_t, std::vector<uint32_t>> decorationGroups;
498
SPIRVKernelInfo *curKernel;
499
spv_context ctx;
500
};
501
502
const struct clc_kernel_info *
503
clc_spirv_get_kernels_info(const struct spirv_binary *spvbin,
504
unsigned *num_kernels)
505
{
506
struct clc_kernel_info *kernels;
507
508
SPIRVKernelParser parser;
509
510
parser.parseBinary(*spvbin);
511
*num_kernels = parser.kernels.size();
512
if (!*num_kernels)
513
return NULL;
514
515
kernels = reinterpret_cast<struct clc_kernel_info *>(calloc(*num_kernels,
516
sizeof(*kernels)));
517
assert(kernels);
518
for (unsigned i = 0; i < parser.kernels.size(); i++) {
519
kernels[i].name = strdup(parser.kernels[i].name.c_str());
520
kernels[i].num_args = parser.kernels[i].args.size();
521
kernels[i].vec_hint_size = parser.kernels[i].vecHint >> 16;
522
kernels[i].vec_hint_type = (enum clc_vec_hint_type)(parser.kernels[i].vecHint & 0xFFFF);
523
if (!kernels[i].num_args)
524
continue;
525
526
struct clc_kernel_arg *args;
527
528
args = reinterpret_cast<struct clc_kernel_arg *>(calloc(kernels[i].num_args,
529
sizeof(*kernels->args)));
530
kernels[i].args = args;
531
assert(args);
532
for (unsigned j = 0; j < kernels[i].num_args; j++) {
533
if (!parser.kernels[i].args[j].name.empty())
534
args[j].name = strdup(parser.kernels[i].args[j].name.c_str());
535
args[j].type_name = strdup(parser.kernels[i].args[j].typeName.c_str());
536
args[j].address_qualifier = parser.kernels[i].args[j].addrQualifier;
537
args[j].type_qualifier = parser.kernels[i].args[j].typeQualifier;
538
args[j].access_qualifier = parser.kernels[i].args[j].accessQualifier;
539
}
540
}
541
542
return kernels;
543
}
544
545
void
546
clc_free_kernels_info(const struct clc_kernel_info *kernels,
547
unsigned num_kernels)
548
{
549
if (!kernels)
550
return;
551
552
for (unsigned i = 0; i < num_kernels; i++) {
553
if (kernels[i].args) {
554
for (unsigned j = 0; j < kernels[i].num_args; j++) {
555
free((void *)kernels[i].args[j].name);
556
free((void *)kernels[i].args[j].type_name);
557
}
558
}
559
free((void *)kernels[i].name);
560
}
561
562
free((void *)kernels);
563
}
564
565
int
566
clc_to_spirv(const struct clc_compile_args *args,
567
struct spirv_binary *spvbin,
568
const struct clc_logger *logger)
569
{
570
LLVMInitializeAllTargets();
571
LLVMInitializeAllTargetInfos();
572
LLVMInitializeAllTargetMCs();
573
LLVMInitializeAllAsmPrinters();
574
575
std::string log;
576
std::unique_ptr<LLVMContext> llvm_ctx { new LLVMContext };
577
llvm_ctx->setDiagnosticHandlerCallBack(llvm_log_handler, &log);
578
579
std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance };
580
clang::DiagnosticsEngine diag { new clang::DiagnosticIDs,
581
new clang::DiagnosticOptions,
582
new clang::TextDiagnosticPrinter(*new raw_string_ostream(log),
583
&c->getDiagnosticOpts(), true)};
584
585
std::vector<const char *> clang_opts = {
586
args->source.name,
587
"-triple", "spir64-unknown-unknown",
588
// By default, clang prefers to use modules to pull in the default headers,
589
// which doesn't work with our technique of embedding the headers in our binary
590
"-finclude-default-header",
591
// Add a default CL compiler version. Clang will pick the last one specified
592
// on the command line, so the app can override this one.
593
"-cl-std=cl1.2",
594
// The LLVM-SPIRV-Translator doesn't support memset with variable size
595
"-fno-builtin-memset",
596
// LLVM's optimizations can produce code that the translator can't translate
597
"-O0",
598
// Ensure inline functions are actually emitted
599
"-fgnu89-inline"
600
};
601
// We assume there's appropriate defines for __OPENCL_VERSION__ and __IMAGE_SUPPORT__
602
// being provided by the caller here.
603
clang_opts.insert(clang_opts.end(), args->args, args->args + args->num_args);
604
605
if (!clang::CompilerInvocation::CreateFromArgs(c->getInvocation(),
606
#if LLVM_VERSION_MAJOR >= 10
607
clang_opts,
608
#else
609
clang_opts.data(),
610
clang_opts.data() + clang_opts.size(),
611
#endif
612
diag)) {
613
log += "Couldn't create Clang invocation.\n";
614
clc_error(logger, log.c_str());
615
return -1;
616
}
617
618
if (diag.hasErrorOccurred()) {
619
log += "Errors occurred during Clang invocation.\n";
620
clc_error(logger, log.c_str());
621
return -1;
622
}
623
624
// This is a workaround for a Clang bug which causes the number
625
// of warnings and errors to be printed to stderr.
626
// http://www.llvm.org/bugs/show_bug.cgi?id=19735
627
c->getDiagnosticOpts().ShowCarets = false;
628
629
c->createDiagnostics(new clang::TextDiagnosticPrinter(
630
*new raw_string_ostream(log),
631
&c->getDiagnosticOpts(), true));
632
633
c->setTarget(clang::TargetInfo::CreateTargetInfo(
634
c->getDiagnostics(), c->getInvocation().TargetOpts));
635
636
c->getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
637
c->getHeaderSearchOpts().UseBuiltinIncludes = false;
638
c->getHeaderSearchOpts().UseStandardSystemIncludes = false;
639
640
// Add opencl-c generic search path
641
{
642
::llvm::SmallString<128> system_header_path;
643
::llvm::sys::path::system_temp_directory(true, system_header_path);
644
::llvm::sys::path::append(system_header_path, "openclon12");
645
c->getHeaderSearchOpts().AddPath(system_header_path.str(),
646
clang::frontend::Angled,
647
false, false);
648
649
::llvm::sys::path::append(system_header_path, "opencl-c.h");
650
c->getPreprocessorOpts().addRemappedFile(system_header_path.str(),
651
::llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(opencl_c_source, _countof(opencl_c_source) - 1)).release());
652
653
::llvm::sys::path::remove_filename(system_header_path);
654
::llvm::sys::path::append(system_header_path, "opencl-c-base.h");
655
c->getPreprocessorOpts().addRemappedFile(system_header_path.str(),
656
::llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(opencl_c_base_source, _countof(opencl_c_base_source) - 1)).release());
657
}
658
659
if (args->num_headers) {
660
::llvm::SmallString<128> tmp_header_path;
661
::llvm::sys::path::system_temp_directory(true, tmp_header_path);
662
::llvm::sys::path::append(tmp_header_path, "openclon12");
663
664
c->getHeaderSearchOpts().AddPath(tmp_header_path.str(),
665
clang::frontend::Quoted,
666
false, false);
667
668
for (size_t i = 0; i < args->num_headers; i++) {
669
auto path_copy = tmp_header_path;
670
::llvm::sys::path::append(path_copy, ::llvm::sys::path::convert_to_slash(args->headers[i].name));
671
c->getPreprocessorOpts().addRemappedFile(path_copy.str(),
672
::llvm::MemoryBuffer::getMemBufferCopy(args->headers[i].value).release());
673
}
674
}
675
676
c->getPreprocessorOpts().addRemappedFile(
677
args->source.name,
678
::llvm::MemoryBuffer::getMemBufferCopy(std::string(args->source.value)).release());
679
680
// Compile the code
681
clang::EmitLLVMOnlyAction act(llvm_ctx.get());
682
if (!c->ExecuteAction(act)) {
683
log += "Error executing LLVM compilation action.\n";
684
clc_error(logger, log.c_str());
685
return -1;
686
}
687
688
auto mod = act.takeModule();
689
std::ostringstream spv_stream;
690
if (!::llvm::writeSpirv(mod.get(), spv_stream, log)) {
691
log += "Translation from LLVM IR to SPIR-V failed.\n";
692
clc_error(logger, log.c_str());
693
return -1;
694
}
695
696
const std::string spv_out = spv_stream.str();
697
spvbin->size = spv_out.size();
698
spvbin->data = static_cast<uint32_t *>(malloc(spvbin->size));
699
memcpy(spvbin->data, spv_out.data(), spvbin->size);
700
701
return 0;
702
}
703
704
static const char *
705
spv_result_to_str(spv_result_t res)
706
{
707
switch (res) {
708
case SPV_SUCCESS: return "success";
709
case SPV_UNSUPPORTED: return "unsupported";
710
case SPV_END_OF_STREAM: return "end of stream";
711
case SPV_WARNING: return "warning";
712
case SPV_FAILED_MATCH: return "failed match";
713
case SPV_REQUESTED_TERMINATION: return "requested termination";
714
case SPV_ERROR_INTERNAL: return "internal error";
715
case SPV_ERROR_OUT_OF_MEMORY: return "out of memory";
716
case SPV_ERROR_INVALID_POINTER: return "invalid pointer";
717
case SPV_ERROR_INVALID_BINARY: return "invalid binary";
718
case SPV_ERROR_INVALID_TEXT: return "invalid text";
719
case SPV_ERROR_INVALID_TABLE: return "invalid table";
720
case SPV_ERROR_INVALID_VALUE: return "invalid value";
721
case SPV_ERROR_INVALID_DIAGNOSTIC: return "invalid diagnostic";
722
case SPV_ERROR_INVALID_LOOKUP: return "invalid lookup";
723
case SPV_ERROR_INVALID_ID: return "invalid id";
724
case SPV_ERROR_INVALID_CFG: return "invalid config";
725
case SPV_ERROR_INVALID_LAYOUT: return "invalid layout";
726
case SPV_ERROR_INVALID_CAPABILITY: return "invalid capability";
727
case SPV_ERROR_INVALID_DATA: return "invalid data";
728
case SPV_ERROR_MISSING_EXTENSION: return "missing extension";
729
case SPV_ERROR_WRONG_VERSION: return "wrong version";
730
default: return "unknown error";
731
}
732
}
733
734
class SPIRVMessageConsumer {
735
public:
736
SPIRVMessageConsumer(const struct clc_logger *logger): logger(logger) {}
737
738
void operator()(spv_message_level_t level, const char *src,
739
const spv_position_t &pos, const char *msg)
740
{
741
switch(level) {
742
case SPV_MSG_FATAL:
743
case SPV_MSG_INTERNAL_ERROR:
744
case SPV_MSG_ERROR:
745
clc_error(logger, "(file=%s,line=%ld,column=%ld,index=%ld): %s",
746
src, pos.line, pos.column, pos.index, msg);
747
break;
748
749
case SPV_MSG_WARNING:
750
clc_warning(logger, "(file=%s,line=%ld,column=%ld,index=%ld): %s",
751
src, pos.line, pos.column, pos.index, msg);
752
break;
753
754
default:
755
break;
756
}
757
}
758
759
private:
760
const struct clc_logger *logger;
761
};
762
763
int
764
clc_link_spirv_binaries(const struct clc_linker_args *args,
765
struct spirv_binary *dst_bin,
766
const struct clc_logger *logger)
767
{
768
std::vector<std::vector<uint32_t>> binaries;
769
770
for (unsigned i = 0; i < args->num_in_objs; i++) {
771
std::vector<uint32_t> bin(args->in_objs[i]->spvbin.data,
772
args->in_objs[i]->spvbin.data +
773
(args->in_objs[i]->spvbin.size / 4));
774
binaries.push_back(bin);
775
}
776
777
SPIRVMessageConsumer msgconsumer(logger);
778
spvtools::Context context(SPV_ENV_UNIVERSAL_1_0);
779
context.SetMessageConsumer(msgconsumer);
780
spvtools::LinkerOptions options;
781
options.SetAllowPartialLinkage(args->create_library);
782
options.SetCreateLibrary(args->create_library);
783
std::vector<uint32_t> linkingResult;
784
spv_result_t status = spvtools::Link(context, binaries, &linkingResult, options);
785
if (status != SPV_SUCCESS) {
786
return -1;
787
}
788
789
dst_bin->size = linkingResult.size() * 4;
790
dst_bin->data = static_cast<uint32_t *>(malloc(dst_bin->size));
791
memcpy(dst_bin->data, linkingResult.data(), dst_bin->size);
792
793
return 0;
794
}
795
796
void
797
clc_dump_spirv(const struct spirv_binary *spvbin, FILE *f)
798
{
799
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
800
std::vector<uint32_t> bin(spvbin->data, spvbin->data + (spvbin->size / 4));
801
std::string out;
802
tools.Disassemble(bin, &out,
803
SPV_BINARY_TO_TEXT_OPTION_INDENT |
804
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
805
fwrite(out.c_str(), out.size(), 1, f);
806
}
807
808
void
809
clc_free_spirv_binary(struct spirv_binary *spvbin)
810
{
811
free(spvbin->data);
812
}
813
814