blob: 4f5ccf693601e29ce42b50baa43f072d2502dbfb [file] [log] [blame]
alan-bakerfec0a472018-11-08 18:09:40 -05001// Copyright 2018 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "clang/Basic/TargetInfo.h"
16#include "clang/CodeGen/CodeGenAction.h"
17#include "clang/Frontend/CompilerInstance.h"
18#include "clang/Frontend/FrontendPluginRegistry.h"
19#include "clang/Frontend/TextDiagnosticPrinter.h"
20#include "clang/Lex/PreprocessorOptions.h"
21#include "llvm/IR/LLVMContext.h"
22#include "llvm/IR/LegacyPassManager.h"
23#include "llvm/IR/Module.h"
24#include "llvm/IR/Verifier.h"
25#include "llvm/LinkAllPasses.h"
26#include "llvm/Support/CommandLine.h"
27#include "llvm/Support/MathExtras.h"
28#include "llvm/Support/raw_ostream.h"
29#include "llvm/Transforms/IPO/PassManagerBuilder.h"
30
31#include "clspv/Option.h"
32#include "clspv/Passes.h"
33#include "clspv/opencl_builtins_header.h"
34
35#include "FrontendPlugin.h"
36
37#include <numeric>
38#include <string>
39
40using namespace clang;
41
42namespace {
43// This registration must be located in the same file as the execution of the
44// action.
45static FrontendPluginRegistry::Add<clspv::ExtraValidationASTAction>
46 X("extra-validation",
47 "Perform extra validation on OpenCL C when targeting Vulkan");
48
49static llvm::cl::opt<bool> cl_single_precision_constants(
50 "cl-single-precision-constant", llvm::cl::init(false),
51 llvm::cl::desc("Treat double precision floating-point constant as single "
52 "precision constant."));
53
54static llvm::cl::opt<bool> cl_denorms_are_zero(
55 "cl-denorms-are-zero", llvm::cl::init(false),
56 llvm::cl::desc("If specified, denormalized floating point numbers may be "
57 "flushed to zero."));
58
59static llvm::cl::opt<bool> cl_fp32_correctly_rounded_divide_sqrt(
60 "cl-fp32-correctly-rounded-divide-sqrt", llvm::cl::init(false),
61 llvm::cl::desc("Single precision floating-point divide (x/y and 1/x) and "
62 "sqrt used are correctly rounded."));
63
64static llvm::cl::opt<bool>
65 cl_opt_disable("cl-opt-disable", llvm::cl::init(false),
66 llvm::cl::desc("This option disables all optimizations. The "
67 "default is optimizations are enabled."));
68
69static llvm::cl::opt<bool> cl_mad_enable(
70 "cl-mad-enable", llvm::cl::init(false),
71 llvm::cl::desc("Allow a * b + c to be replaced by a mad. The mad computes "
72 "a * b + c with reduced accuracy."));
73
74static llvm::cl::opt<bool> cl_no_signed_zeros(
75 "cl-no-signed-zeros", llvm::cl::init(false),
76 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
77 "ignore the signedness of zero."));
78
79static llvm::cl::opt<bool> cl_unsafe_math_optimizations(
80 "cl-unsafe-math-optimizations", llvm::cl::init(false),
81 llvm::cl::desc("Allow optimizations for floating-point arithmetic that (a) "
82 "assume that arguments and results are valid, (b) may "
83 "violate IEEE 754 standard and (c) may violate the OpenCL "
84 "numerical compliance requirements. This option includes "
85 "the -cl-no-signed-zeros and -cl-mad-enable options."));
86
87static llvm::cl::opt<bool> cl_finite_math_only(
88 "cl-finite-math-only", llvm::cl::init(false),
89 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
90 "assume that arguments and results are not NaNs or INFs."));
91
92static llvm::cl::opt<bool> cl_fast_relaxed_math(
93 "cl-fast-relaxed-math", llvm::cl::init(false),
94 llvm::cl::desc("This option causes the preprocessor macro "
95 "__FAST_RELAXED_MATH__ to be defined. Sets the optimization "
96 "options -cl-finite-math-only and "
97 "-cl-unsafe-math-optimizations."));
98
99static llvm::cl::list<std::string>
100 Includes(llvm::cl::Prefix, "I",
101 llvm::cl::desc("Add a directory to the list of directories "
102 "to be searched for header files."),
103 llvm::cl::ZeroOrMore, llvm::cl::value_desc("include path"));
104
105static llvm::cl::list<std::string>
106 Defines(llvm::cl::Prefix, "D",
107 llvm::cl::desc("Define a #define directive."), llvm::cl::ZeroOrMore,
108 llvm::cl::value_desc("define"));
109
110static llvm::cl::opt<std::string>
111 InputFilename(llvm::cl::Positional, llvm::cl::desc("<input .cl file>"),
112 llvm::cl::init("-"));
113
114static llvm::cl::opt<std::string>
115 OutputFilename("o", llvm::cl::desc("Override output filename"),
116 llvm::cl::value_desc("filename"));
117
118static llvm::cl::opt<std::string>
119 DescriptorMapFilename("descriptormap",
120 llvm::cl::desc("Output file for descriptor map"),
121 llvm::cl::value_desc("filename"));
122
123static llvm::cl::opt<char>
124 OptimizationLevel(llvm::cl::Prefix, "O", llvm::cl::init('2'),
125 llvm::cl::desc("Optimization level to use"),
126 llvm::cl::value_desc("level"));
127
128static llvm::cl::opt<bool>
129 OutputAssembly("S", llvm::cl::init(false),
130 llvm::cl::desc("This option controls output of assembly"));
131
132static llvm::cl::opt<std::string> OutputFormat(
133 "mfmt", llvm::cl::init(""),
134 llvm::cl::desc(
135 "Specify special output format. 'c' is as a C initializer list"),
136 llvm::cl::value_desc("format"));
137
138static llvm::cl::opt<std::string>
139 SamplerMap("samplermap", llvm::cl::desc("Literal sampler map"),
140 llvm::cl::value_desc("filename"));
141
142static llvm::cl::opt<bool> cluster_non_pointer_kernel_args(
143 "cluster-pod-kernel-args", llvm::cl::init(false),
144 llvm::cl::desc("Collect plain-old-data kernel arguments into a struct in "
145 "a single storage buffer, using a binding number after "
146 "other arguments. Use this to reduce storage buffer "
147 "descriptors."));
148
149static llvm::cl::opt<bool> verify("verify", llvm::cl::init(false),
150 llvm::cl::desc("Verify diagnostic outputs"));
151
152// Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
153// if successful.
154int ParseSamplerMap(llvm::SmallVectorImpl<std::pair<unsigned, std::string>> *SamplerMapEntries) {
155 if (!SamplerMap.empty()) {
156 auto errorOrSamplerMapFile =
157 llvm::MemoryBuffer::getFile(SamplerMap.getValue());
158
159 // If there was an error in getting the sampler map file.
160 if (!errorOrSamplerMapFile) {
161 llvm::errs() << "Error: " << errorOrSamplerMapFile.getError().message()
162 << " '" << SamplerMap.getValue() << "'\n";
163 return -1;
164 }
165
166 auto samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
167
168 if (0 == samplerMapBuffer->getBufferSize()) {
169 llvm::errs() << "Error: Sampler map was an empty file!\n";
170 return -1;
171 }
172
173 llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
174
175 // We need to keep track of the beginning of the current entry.
176 const char *b = samplerMapBuffer->getBufferStart();
177 for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
178 // If we have a separator between declarations.
179 if ((*i == '|') || (*i == ',') || (i == e)) {
180 if (i == b) {
181 llvm::errs() << "Error: Sampler map contained an empty entry!\n";
182 return -1;
183 }
184
185 samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
186
187 // And set b the next character after i.
188 b = i + 1;
189 }
190
191 // If we have a separator between declarations within a single sampler.
192 if ((*i == ',') || (i == e)) {
193 enum NormalizedCoords {
194 CLK_NORMALIZED_COORDS_FALSE = 0x00,
195 CLK_NORMALIZED_COORDS_TRUE = 0x01,
196 CLK_NORMALIZED_COORDS_NOT_SET
197 } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
198
199 enum AddressingModes {
200 CLK_ADDRESS_NONE = 0x00,
201 CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
202 CLK_ADDRESS_CLAMP = 0x04,
203 CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
204 CLK_ADDRESS_REPEAT = 0x06,
205 CLK_ADDRESS_NOT_SET
206 } AddressingMode = CLK_ADDRESS_NOT_SET;
207
208 enum FilterModes {
209 CLK_FILTER_NEAREST = 0x10,
210 CLK_FILTER_LINEAR = 0x20,
211 CLK_FILTER_NOT_SET
212 } FilterMode = CLK_FILTER_NOT_SET;
213
214 for (auto str : samplerStrings) {
215 if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
216 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
217 llvm::errs() << "Error: Sampler map normalized coordinates was "
218 "previously set!\n";
219 return -1;
220 }
221 NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
222 } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
223 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
224 llvm::errs() << "Error: Sampler map normalized coordinates was "
225 "previously set!\n";
226 return -1;
227 }
228 NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
229 } else if ("CLK_ADDRESS_NONE" == str) {
230 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
231 llvm::errs()
232 << "Error: Sampler map addressing mode was previously set!\n";
233 return -1;
234 }
235 AddressingMode = CLK_ADDRESS_NONE;
236 } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
237 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
238 llvm::errs()
239 << "Error: Sampler map addressing mode was previously set!\n";
240 return -1;
241 }
242 AddressingMode = CLK_ADDRESS_CLAMP_TO_EDGE;
243 } else if ("CLK_ADDRESS_CLAMP" == str) {
244 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
245 llvm::errs()
246 << "Error: Sampler map addressing mode was previously set!\n";
247 return -1;
248 }
249 AddressingMode = CLK_ADDRESS_CLAMP;
250 } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
251 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
252 llvm::errs()
253 << "Error: Sampler map addressing mode was previously set!\n";
254 return -1;
255 }
256 AddressingMode = CLK_ADDRESS_MIRRORED_REPEAT;
257 } else if ("CLK_ADDRESS_REPEAT" == str) {
258 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
259 llvm::errs()
260 << "Error: Sampler map addressing mode was previously set!\n";
261 return -1;
262 }
263 AddressingMode = CLK_ADDRESS_REPEAT;
264 } else if ("CLK_FILTER_NEAREST" == str) {
265 if (CLK_FILTER_NOT_SET != FilterMode) {
266 llvm::errs()
267 << "Error: Sampler map filtering mode was previously set!\n";
268 return -1;
269 }
270 FilterMode = CLK_FILTER_NEAREST;
271 } else if ("CLK_FILTER_LINEAR" == str) {
272 if (CLK_FILTER_NOT_SET != FilterMode) {
273 llvm::errs()
274 << "Error: Sampler map filtering mode was previously set!\n";
275 return -1;
276 }
277 FilterMode = CLK_FILTER_LINEAR;
278 } else {
279 llvm::errs() << "Error: Unknown sampler string '" << str
280 << "' found!\n";
281 return -1;
282 }
283 }
284
285 if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
286 llvm::errs() << "Error: Sampler map entry did not contain normalized "
287 "coordinates entry!\n";
288 return -1;
289 }
290
291 if (CLK_ADDRESS_NOT_SET == AddressingMode) {
292 llvm::errs() << "Error: Sampler map entry did not contain addressing "
293 "mode entry!\n";
294 return -1;
295 }
296
297 if (CLK_FILTER_NOT_SET == FilterMode) {
298 llvm::errs()
299 << "Error: Sampler map entry did not contain filer mode entry!\n";
300 return -1;
301 }
302
303 // Generate an equivalent expression in string form. Sort the
304 // strings to get a canonical ordering.
305 std::sort(samplerStrings.begin(), samplerStrings.end(),
306 std::less<StringRef>());
307 const auto samplerExpr = std::accumulate(
308 samplerStrings.begin(), samplerStrings.end(), std::string(),
309 [](std::string left, std::string right) {
310 return left + std::string(left.empty() ? "" : "|") + right;
311 });
312
313 SamplerMapEntries->emplace_back(
314 NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
315
316 // And reset the sampler strings for the next sampler in the map.
317 samplerStrings.clear();
318 }
319
320 // And lastly, if we are at the end of the file
321 if (i == e) {
322 break;
323 }
324 }
325 }
326
327 return 0;
328}
329
330// Sets |instance|'s options for compiling. Returns 0 if successful.
331int SetCompilerInstanceOptions(CompilerInstance &instance,
332 const llvm::StringRef &overiddenInputFilename,
333 const clang::FrontendInputFile &kernelFile,
334 llvm::raw_string_ostream *diagnosticsStream) {
335 auto errorOrInputFile =
336 llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
337
338 // If there was an error in getting the input file.
339 if (!errorOrInputFile) {
340 llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
341 << InputFilename.getValue() << "'\n";
342 return -1;
343 }
344 if (verify) {
345 instance.getDiagnosticOpts().VerifyDiagnostics = true;
346 }
347
348 clang::LangStandard::Kind standard = clang::LangStandard::lang_opencl12;
349
350 // We are targeting OpenCL 1.2 only
351 instance.getLangOpts().OpenCLVersion = 120;
352
353 instance.getLangOpts().C99 = true;
354 instance.getLangOpts().RTTI = false;
355 instance.getLangOpts().RTTIData = false;
356 instance.getLangOpts().MathErrno = false;
357 instance.getLangOpts().Optimize = false;
358 instance.getLangOpts().NoBuiltin = true;
359 instance.getLangOpts().ModulesSearchAll = false;
360 instance.getLangOpts().SinglePrecisionConstants = true;
361 instance.getCodeGenOpts().StackRealignment = true;
362 instance.getCodeGenOpts().SimplifyLibCalls = false;
363 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
364 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
365 instance.getDiagnosticOpts().IgnoreWarnings = false;
366
367 instance.getLangOpts().SinglePrecisionConstants =
368 cl_single_precision_constants;
369 // cl_denorms_are_zero ignored for now!
370 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
371 instance.getCodeGenOpts().LessPreciseFPMAD =
372 cl_mad_enable || cl_unsafe_math_optimizations;
373 // cl_no_signed_zeros ignored for now!
374 instance.getCodeGenOpts().UnsafeFPMath =
375 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
376 instance.getLangOpts().FiniteMathOnly =
377 cl_finite_math_only || cl_fast_relaxed_math;
378 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
379
380 // Preprocessor options
381 instance.getPreprocessorOpts().addMacroDef("__IMAGE_SUPPORT__");
382 if (cl_fast_relaxed_math) {
383 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
384 }
385
386 for (auto define : Defines) {
387 instance.getPreprocessorOpts().addMacroDef(define);
388 }
389
390 // Header search options
391 for (auto include : Includes) {
392 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
393 false, false);
394 }
395
396 // We always compile on opt 0 so we preserve as much debug information about
397 // the source as possible. We'll run optimization later, once we've had a
398 // chance to view the unoptimal code first
399 instance.getCodeGenOpts().OptimizationLevel = 0;
400
401// Debug information is disabled temporarily to call instruction.
402#if 0
403 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
404#endif
405
406 // We use the 32-bit pointer-width SPIR triple
407 llvm::Triple triple("spir-unknown-unknown");
408
409 instance.getInvocation().setLangDefaults(
410 instance.getLangOpts(), clang::InputKind::OpenCL, triple,
411 instance.getPreprocessorOpts(), standard);
412
413 // Override the C99 inline semantics to accommodate for more OpenCL C
414 // programs in the wild.
415 instance.getLangOpts().GNUInline = true;
416 instance.createDiagnostics(
417 new clang::TextDiagnosticPrinter(*diagnosticsStream,
418 &instance.getDiagnosticOpts()),
419 true);
420
421 instance.getTargetOpts().Triple = triple.str();
422
423 instance.getCodeGenOpts().MainFileName = overiddenInputFilename;
424 instance.getCodeGenOpts().PreserveVec3Type = true;
425 // Disable generation of lifetime intrinsic.
426 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
427 instance.getFrontendOpts().Inputs.push_back(kernelFile);
428 instance.getPreprocessorOpts().addRemappedFile(
429 overiddenInputFilename, errorOrInputFile.get().release());
430
431 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
432 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
433 const char *dataCasted = reinterpret_cast<const char *>(data);
434 init(dataCasted, dataCasted + data_length, true);
435 }
436
437 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
438 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
439 }
440
441 virtual ~OpenCLBuiltinMemoryBuffer() override {}
442 };
443
444 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
445 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
446 opencl_builtins_header_size - 1));
447
448 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
449
450 // Add the VULKAN macro.
451 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
452
453 // Add the __OPENCL_VERSION__ macro.
454 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
455
456 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
457 instance.getDiagnostics(),
458 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
459
460 instance.createFileManager();
461 instance.createSourceManager(instance.getFileManager());
462
463#ifdef _MSC_VER
464 std::string includePrefix("include\\");
465#else
466 std::string includePrefix("include/");
467#endif
468
469 auto entry = instance.getFileManager().getVirtualFile(
470 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
471 0);
472
473 instance.getSourceManager().overrideFileContents(
474 entry, std::move(openCLBuiltinMemoryBuffer));
475
476 return 0;
477}
478
479// Populates |pm| with necessary passes to optimize and legalize the IR.
480void PopulatePassManager(llvm::legacy::PassManager *pm,
481 llvm::raw_svector_ostream *binaryStream,
482 llvm::raw_string_ostream *descriptor_map_out,
483 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
484 *SamplerMapEntries) {
485 llvm::PassManagerBuilder pmBuilder;
486
487 switch (OptimizationLevel) {
488 case '0':
489 pmBuilder.OptLevel = 0;
490 break;
491 case '1':
492 pmBuilder.OptLevel = 1;
493 break;
494 case '2':
495 pmBuilder.OptLevel = 2;
496 break;
497 case '3':
498 pmBuilder.OptLevel = 3;
499 break;
500 case 's':
501 pmBuilder.SizeLevel = 1;
502 break;
503 case 'z':
504 pmBuilder.SizeLevel = 2;
505 break;
506 default:
507 break;
508 }
509
510 pm->add(clspv::createZeroInitializeAllocasPass());
511 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
512
513 if (0 < pmBuilder.OptLevel) {
514 pm->add(clspv::createOpenCLInlinerPass());
515 }
516
517 pm->add(clspv::createUndoByvalPass());
518 pm->add(clspv::createUndoSRetPass());
519 if (cluster_non_pointer_kernel_args) {
520 pm->add(clspv::createClusterPodKernelArgumentsPass());
521 }
522 pm->add(clspv::createReplaceOpenCLBuiltinPass());
523
524 // We need to run mem2reg and inst combine early because our
525 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
526 // %1 = alloca i32 1
527 // store <something> %1
528 // %2 = bitcast float* %1
529 // %3 = load float %2
530 pm->add(llvm::createPromoteMemoryToRegisterPass());
531
532 // Hide loads from __constant address space away from instcombine.
533 // This prevents us from generating select between pointers-to-__constant.
534 // See https://github.com/google/clspv/issues/71
535 pm->add(clspv::createHideConstantLoadsPass());
536
537 pm->add(llvm::createInstructionCombiningPass());
538
539 if (clspv::Option::InlineEntryPoints()) {
540 pm->add(clspv::createInlineEntryPointsPass());
541 } else {
542 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
543 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
544 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
545 }
546
547 if (0 == pmBuilder.OptLevel) {
548 // Mem2Reg pass should be run early because O0 level optimization leaves
549 // redundant alloca, load and store instructions from function arguments.
550 // clspv needs to remove them ahead of transformation.
551 pm->add(llvm::createPromoteMemoryToRegisterPass());
552
553 // SROA pass is run because it will fold structs/unions that are problematic
554 // on Vulkan SPIR-V away.
555 pm->add(llvm::createSROAPass());
556
557 // InstructionCombining pass folds bitcast and gep instructions which are
558 // not supported by Vulkan SPIR-V.
559 pm->add(llvm::createInstructionCombiningPass());
560 }
561
562 // Now we add any of the LLVM optimizations we wanted
563 pmBuilder.populateModulePassManager(*pm);
564
565 // Unhide loads from __constant address space. Undoes the action of
566 // HideConstantLoadsPass.
567 pm->add(clspv::createUnhideConstantLoadsPass());
568
569 pm->add(clspv::createFunctionInternalizerPass());
570 pm->add(clspv::createReplaceLLVMIntrinsicsPass());
571 pm->add(clspv::createUndoBoolPass());
572 pm->add(clspv::createUndoTruncatedSwitchConditionPass());
573 pm->add(llvm::createStructurizeCFGPass(false));
574 pm->add(clspv::createReorderBasicBlocksPass());
575 pm->add(clspv::createUndoGetElementPtrConstantExprPass());
576 pm->add(clspv::createSplatArgPass());
577 pm->add(clspv::createSimplifyPointerBitcastPass());
578 pm->add(clspv::createReplacePointerBitcastPass());
579
580 pm->add(clspv::createUndoTranslateSamplerFoldPass());
581
582 if (clspv::Option::ModuleConstantsInStorageBuffer()) {
583 pm->add(clspv::createClusterModuleScopeConstantVars());
584 }
585
586 pm->add(clspv::createShareModuleScopeVariablesPass());
587 pm->add(clspv::createAllocateDescriptorsPass(*SamplerMapEntries));
588 pm->add(llvm::createVerifierPass());
589 pm->add(clspv::createDirectResourceAccessPass());
590 // Replacing pointer bitcasts can leave some trivial GEPs
591 // that are easy to remove. Also replace GEPs of GEPS
592 // left by replacing indirect buffer accesses.
593 pm->add(clspv::createSimplifyPointerBitcastPass());
594
595 pm->add(clspv::createSplatSelectConditionPass());
596 pm->add(clspv::createSignedCompareFixupPass());
597 // This pass generates insertions that need to be rewritten.
598 pm->add(clspv::createScalarizePass());
599 pm->add(clspv::createRewriteInsertsPass());
600 // This pass mucks with types to point where you shouldn't rely on DataLayout
601 // anymore so leave this right before SPIR-V generation.
602 pm->add(clspv::createUBOTypeTransformPass());
603 pm->add(clspv::createSPIRVProducerPass(
604 *binaryStream, *descriptor_map_out, *SamplerMapEntries,
605 OutputAssembly.getValue(), OutputFormat == "c"));
606}
607} // namespace
608
609namespace clspv {
610int Compile(const int argc, const char *const argv[]) {
611 // We need to change how one of the called passes works by spoofing
612 // ParseCommandLineOptions with the specific option.
613 const int llvmArgc = 2;
614 const char *llvmArgv[llvmArgc] = {
615 argv[0], "-simplifycfg-sink-common=false",
616 };
617
618 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
619
620 llvm::cl::ParseCommandLineOptions(argc, argv);
621
622 switch (OptimizationLevel) {
623 case '0':
624 case '1':
625 case '2':
626 case '3':
627 case 's':
628 case 'z':
629 break;
630 default:
631 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
632 << " specified!\n";
633 return -1;
634 }
635
636 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
637 if (auto error = ParseSamplerMap(&SamplerMapEntries))
638 return error;
639
640 // if no output file was provided, use a default
641 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
642
643 // If we are reading our input file from stdin.
644 if ("-" == InputFilename) {
645 // We need to overwrite the file name we use.
646 overiddenInputFilename = "stdin.cl";
647 }
648
649 clang::CompilerInstance instance;
650 clang::FrontendInputFile kernelFile(overiddenInputFilename,
651 clang::InputKind::OpenCL);
652 std::string log;
653 llvm::raw_string_ostream diagnosticsStream(log);
654 if (auto error = SetCompilerInstanceOptions(instance, overiddenInputFilename,
655 kernelFile, &diagnosticsStream))
656 return error;
657
658 // Parse.
659 llvm::LLVMContext context;
660 clang::EmitLLVMOnlyAction action(&context);
661
662 // Prepare the action for processing kernelFile
663 const bool success = action.BeginSourceFile(instance, kernelFile);
664 if (!success) {
665 return -1;
666 }
667
668 action.Execute();
669 action.EndSourceFile();
670
671 clang::DiagnosticConsumer *const consumer =
672 instance.getDiagnostics().getClient();
673 consumer->finish();
674
675 auto num_errors = consumer->getNumErrors();
676 if (num_errors > 0) {
677 llvm::errs() << log << "\n";
678 return -1;
679 }
680
681 if (clspv::Option::ConstantArgsInUniformBuffer() &&
682 !clspv::Option::InlineEntryPoints()) {
683 llvm::errs() << "clspv restriction: -constant-arg-ubo requires "
684 "-inline-entry-points\n";
685 return -1;
686 }
687
688 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
689 llvm::initializeCore(Registry);
690 llvm::initializeScalarOpts(Registry);
691
692 std::unique_ptr<llvm::Module> module(action.takeModule());
693
694 // Optimize.
695 // Create a memory buffer for temporarily writing the result.
696 SmallVector<char, 10000> binary;
697 llvm::raw_svector_ostream binaryStream(binary);
698 std::string descriptor_map;
699 llvm::raw_string_ostream descriptor_map_out(descriptor_map);
700 llvm::legacy::PassManager pm;
701 PopulatePassManager(&pm, &binaryStream, &descriptor_map_out,
702 &SamplerMapEntries);
703 pm.run(*module);
704
705 // Write outputs
706
707 // Write the descriptor map, if requested.
708 std::error_code error;
709 if (!DescriptorMapFilename.empty()) {
710 descriptor_map_out.flush();
711
712 llvm::raw_fd_ostream descriptor_map_out_fd(DescriptorMapFilename, error,
713 llvm::sys::fs::F_RW |
714 llvm::sys::fs::F_Text);
715 if (error) {
716 llvm::errs() << "Unable to open descriptor map file '"
717 << DescriptorMapFilename << "': " << error.message() << '\n';
718 return -1;
719 }
720 descriptor_map_out_fd << descriptor_map;
721 descriptor_map_out_fd.close();
722 }
723
724 // Write the resulting binary.
725 // Wait until now to try writing the file so that we only write it on
726 // successful compilation.
727 if (OutputFilename.empty()) {
728 // if we've to output assembly
729 if (OutputAssembly) {
730 OutputFilename = "a.spvasm";
731 } else if (OutputFormat == "c") {
732 OutputFilename = "a.spvinc";
733 } else {
734 OutputFilename = "a.spv";
735 }
736 }
737 llvm::raw_fd_ostream outStream(OutputFilename, error, llvm::sys::fs::F_RW);
738
739 if (error) {
740 llvm::errs() << "Unable to open output file '" << OutputFilename
741 << "': " << error.message() << '\n';
742 return -1;
743 }
744 outStream << binaryStream.str();
745
746 return 0;
747}
748} // namespace clspv