blob: cce2ba443af298e295a854adf9561c49019925ae [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"
alan-bakerf5e5f692018-11-27 08:33:24 -050026#include "llvm/Support/Allocator.h"
alan-bakerfec0a472018-11-08 18:09:40 -050027#include "llvm/Support/CommandLine.h"
alan-bakerf5e5f692018-11-27 08:33:24 -050028#include "llvm/Support/ErrorOr.h"
alan-bakerfec0a472018-11-08 18:09:40 -050029#include "llvm/Support/MathExtras.h"
alan-bakerf5e5f692018-11-27 08:33:24 -050030#include "llvm/Support/StringSaver.h"
alan-bakerfec0a472018-11-08 18:09:40 -050031#include "llvm/Support/raw_ostream.h"
32#include "llvm/Transforms/IPO/PassManagerBuilder.h"
33
alan-bakerf5e5f692018-11-27 08:33:24 -050034#include "clspv/DescriptorMap.h"
alan-bakerfec0a472018-11-08 18:09:40 -050035#include "clspv/Option.h"
36#include "clspv/Passes.h"
37#include "clspv/opencl_builtins_header.h"
38
39#include "FrontendPlugin.h"
40
alan-bakerf5e5f692018-11-27 08:33:24 -050041#include <cassert>
alan-bakerfec0a472018-11-08 18:09:40 -050042#include <numeric>
43#include <string>
alan-bakerf5e5f692018-11-27 08:33:24 -050044#include <sstream>
alan-bakerfec0a472018-11-08 18:09:40 -050045
46using namespace clang;
47
48namespace {
49// This registration must be located in the same file as the execution of the
50// action.
51static FrontendPluginRegistry::Add<clspv::ExtraValidationASTAction>
52 X("extra-validation",
53 "Perform extra validation on OpenCL C when targeting Vulkan");
54
55static llvm::cl::opt<bool> cl_single_precision_constants(
56 "cl-single-precision-constant", llvm::cl::init(false),
57 llvm::cl::desc("Treat double precision floating-point constant as single "
58 "precision constant."));
59
60static llvm::cl::opt<bool> cl_denorms_are_zero(
61 "cl-denorms-are-zero", llvm::cl::init(false),
62 llvm::cl::desc("If specified, denormalized floating point numbers may be "
63 "flushed to zero."));
64
65static llvm::cl::opt<bool> cl_fp32_correctly_rounded_divide_sqrt(
66 "cl-fp32-correctly-rounded-divide-sqrt", llvm::cl::init(false),
67 llvm::cl::desc("Single precision floating-point divide (x/y and 1/x) and "
68 "sqrt used are correctly rounded."));
69
70static llvm::cl::opt<bool>
71 cl_opt_disable("cl-opt-disable", llvm::cl::init(false),
72 llvm::cl::desc("This option disables all optimizations. The "
73 "default is optimizations are enabled."));
74
75static llvm::cl::opt<bool> cl_mad_enable(
76 "cl-mad-enable", llvm::cl::init(false),
77 llvm::cl::desc("Allow a * b + c to be replaced by a mad. The mad computes "
78 "a * b + c with reduced accuracy."));
79
80static llvm::cl::opt<bool> cl_no_signed_zeros(
81 "cl-no-signed-zeros", llvm::cl::init(false),
82 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
83 "ignore the signedness of zero."));
84
85static llvm::cl::opt<bool> cl_unsafe_math_optimizations(
86 "cl-unsafe-math-optimizations", llvm::cl::init(false),
87 llvm::cl::desc("Allow optimizations for floating-point arithmetic that (a) "
88 "assume that arguments and results are valid, (b) may "
89 "violate IEEE 754 standard and (c) may violate the OpenCL "
90 "numerical compliance requirements. This option includes "
91 "the -cl-no-signed-zeros and -cl-mad-enable options."));
92
93static llvm::cl::opt<bool> cl_finite_math_only(
94 "cl-finite-math-only", llvm::cl::init(false),
95 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
96 "assume that arguments and results are not NaNs or INFs."));
97
98static llvm::cl::opt<bool> cl_fast_relaxed_math(
99 "cl-fast-relaxed-math", llvm::cl::init(false),
100 llvm::cl::desc("This option causes the preprocessor macro "
101 "__FAST_RELAXED_MATH__ to be defined. Sets the optimization "
102 "options -cl-finite-math-only and "
103 "-cl-unsafe-math-optimizations."));
104
105static llvm::cl::list<std::string>
106 Includes(llvm::cl::Prefix, "I",
107 llvm::cl::desc("Add a directory to the list of directories "
108 "to be searched for header files."),
109 llvm::cl::ZeroOrMore, llvm::cl::value_desc("include path"));
110
111static llvm::cl::list<std::string>
112 Defines(llvm::cl::Prefix, "D",
113 llvm::cl::desc("Define a #define directive."), llvm::cl::ZeroOrMore,
114 llvm::cl::value_desc("define"));
115
116static llvm::cl::opt<std::string>
117 InputFilename(llvm::cl::Positional, llvm::cl::desc("<input .cl file>"),
118 llvm::cl::init("-"));
119
120static llvm::cl::opt<std::string>
121 OutputFilename("o", llvm::cl::desc("Override output filename"),
122 llvm::cl::value_desc("filename"));
123
124static llvm::cl::opt<std::string>
125 DescriptorMapFilename("descriptormap",
126 llvm::cl::desc("Output file for descriptor map"),
127 llvm::cl::value_desc("filename"));
128
129static llvm::cl::opt<char>
130 OptimizationLevel(llvm::cl::Prefix, "O", llvm::cl::init('2'),
131 llvm::cl::desc("Optimization level to use"),
132 llvm::cl::value_desc("level"));
133
134static llvm::cl::opt<bool>
135 OutputAssembly("S", llvm::cl::init(false),
136 llvm::cl::desc("This option controls output of assembly"));
137
138static llvm::cl::opt<std::string> OutputFormat(
139 "mfmt", llvm::cl::init(""),
140 llvm::cl::desc(
141 "Specify special output format. 'c' is as a C initializer list"),
142 llvm::cl::value_desc("format"));
143
144static llvm::cl::opt<std::string>
145 SamplerMap("samplermap", llvm::cl::desc("Literal sampler map"),
146 llvm::cl::value_desc("filename"));
147
148static llvm::cl::opt<bool> cluster_non_pointer_kernel_args(
149 "cluster-pod-kernel-args", llvm::cl::init(false),
150 llvm::cl::desc("Collect plain-old-data kernel arguments into a struct in "
151 "a single storage buffer, using a binding number after "
152 "other arguments. Use this to reduce storage buffer "
153 "descriptors."));
154
155static llvm::cl::opt<bool> verify("verify", llvm::cl::init(false),
156 llvm::cl::desc("Verify diagnostic outputs"));
157
158// Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
159// if successful.
alan-bakerf5e5f692018-11-27 08:33:24 -0500160int ParseSamplerMap(const std::string &sampler_map,
161 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
162 *SamplerMapEntries) {
163 std::unique_ptr<llvm::MemoryBuffer> samplerMapBuffer(nullptr);
164 if (!sampler_map.empty()) {
165 // Parse the sampler map from the provided string.
166 samplerMapBuffer = llvm::MemoryBuffer::getMemBuffer(sampler_map);
167
168 if (!SamplerMap.empty()) {
169 llvm::outs() << "Warning: -samplermap is ignored when the sampler map is "
170 "provided through a string.\n";
171 }
172 } else if (!SamplerMap.empty()) {
173 // Parse the sampler map from the option provided file.
alan-bakerfec0a472018-11-08 18:09:40 -0500174 auto errorOrSamplerMapFile =
175 llvm::MemoryBuffer::getFile(SamplerMap.getValue());
176
177 // If there was an error in getting the sampler map file.
178 if (!errorOrSamplerMapFile) {
179 llvm::errs() << "Error: " << errorOrSamplerMapFile.getError().message()
180 << " '" << SamplerMap.getValue() << "'\n";
181 return -1;
182 }
183
alan-bakerf5e5f692018-11-27 08:33:24 -0500184 samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
alan-bakerfec0a472018-11-08 18:09:40 -0500185 if (0 == samplerMapBuffer->getBufferSize()) {
186 llvm::errs() << "Error: Sampler map was an empty file!\n";
187 return -1;
188 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500189 }
alan-bakerfec0a472018-11-08 18:09:40 -0500190
alan-bakerf5e5f692018-11-27 08:33:24 -0500191 // No sampler map to parse.
192 if (!samplerMapBuffer || 0 == samplerMapBuffer->getBufferSize())
193 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500194
alan-bakerf5e5f692018-11-27 08:33:24 -0500195 llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
alan-bakerfec0a472018-11-08 18:09:40 -0500196
alan-bakerf5e5f692018-11-27 08:33:24 -0500197 // We need to keep track of the beginning of the current entry.
198 const char *b = samplerMapBuffer->getBufferStart();
199 for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
200 // If we have a separator between declarations.
201 if ((*i == '|') || (*i == ',') || (i == e)) {
202 if (i == b) {
203 llvm::errs() << "Error: Sampler map contained an empty entry!\n";
204 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500205 }
206
alan-bakerf5e5f692018-11-27 08:33:24 -0500207 samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
alan-bakerfec0a472018-11-08 18:09:40 -0500208
alan-bakerf5e5f692018-11-27 08:33:24 -0500209 // And set b the next character after i.
210 b = i + 1;
211 }
alan-bakerfec0a472018-11-08 18:09:40 -0500212
alan-bakerf5e5f692018-11-27 08:33:24 -0500213 // If we have a separator between declarations within a single sampler.
214 if ((*i == ',') || (i == e)) {
215 enum NormalizedCoords {
216 CLK_NORMALIZED_COORDS_FALSE = 0x00,
217 CLK_NORMALIZED_COORDS_TRUE = 0x01,
218 CLK_NORMALIZED_COORDS_NOT_SET
219 } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
alan-bakerfec0a472018-11-08 18:09:40 -0500220
alan-bakerf5e5f692018-11-27 08:33:24 -0500221 enum AddressingModes {
222 CLK_ADDRESS_NONE = 0x00,
223 CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
224 CLK_ADDRESS_CLAMP = 0x04,
225 CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
226 CLK_ADDRESS_REPEAT = 0x06,
227 CLK_ADDRESS_NOT_SET
228 } AddressingMode = CLK_ADDRESS_NOT_SET;
229
230 enum FilterModes {
231 CLK_FILTER_NEAREST = 0x10,
232 CLK_FILTER_LINEAR = 0x20,
233 CLK_FILTER_NOT_SET
234 } FilterMode = CLK_FILTER_NOT_SET;
235
236 for (auto str : samplerStrings) {
237 if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
238 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
239 llvm::errs() << "Error: Sampler map normalized coordinates was "
240 "previously set!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500241 return -1;
242 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500243 NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
244 } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
245 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
246 llvm::errs() << "Error: Sampler map normalized coordinates was "
247 "previously set!\n";
248 return -1;
249 }
250 NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
251 } else if ("CLK_ADDRESS_NONE" == str) {
252 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
253 llvm::errs()
254 << "Error: Sampler map addressing mode was previously set!\n";
255 return -1;
256 }
257 AddressingMode = CLK_ADDRESS_NONE;
258 } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
259 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
260 llvm::errs()
261 << "Error: Sampler map addressing mode was previously set!\n";
262 return -1;
263 }
264 AddressingMode = CLK_ADDRESS_CLAMP_TO_EDGE;
265 } else if ("CLK_ADDRESS_CLAMP" == str) {
266 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
267 llvm::errs()
268 << "Error: Sampler map addressing mode was previously set!\n";
269 return -1;
270 }
271 AddressingMode = CLK_ADDRESS_CLAMP;
272 } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
273 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
274 llvm::errs()
275 << "Error: Sampler map addressing mode was previously set!\n";
276 return -1;
277 }
278 AddressingMode = CLK_ADDRESS_MIRRORED_REPEAT;
279 } else if ("CLK_ADDRESS_REPEAT" == str) {
280 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
281 llvm::errs()
282 << "Error: Sampler map addressing mode was previously set!\n";
283 return -1;
284 }
285 AddressingMode = CLK_ADDRESS_REPEAT;
286 } else if ("CLK_FILTER_NEAREST" == str) {
287 if (CLK_FILTER_NOT_SET != FilterMode) {
288 llvm::errs()
289 << "Error: Sampler map filtering mode was previously set!\n";
290 return -1;
291 }
292 FilterMode = CLK_FILTER_NEAREST;
293 } else if ("CLK_FILTER_LINEAR" == str) {
294 if (CLK_FILTER_NOT_SET != FilterMode) {
295 llvm::errs()
296 << "Error: Sampler map filtering mode was previously set!\n";
297 return -1;
298 }
299 FilterMode = CLK_FILTER_LINEAR;
300 } else {
301 llvm::errs() << "Error: Unknown sampler string '" << str
302 << "' found!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500303 return -1;
304 }
alan-bakerfec0a472018-11-08 18:09:40 -0500305 }
306
alan-bakerf5e5f692018-11-27 08:33:24 -0500307 if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
308 llvm::errs() << "Error: Sampler map entry did not contain normalized "
309 "coordinates entry!\n";
310 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500311 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500312
313 if (CLK_ADDRESS_NOT_SET == AddressingMode) {
314 llvm::errs() << "Error: Sampler map entry did not contain addressing "
315 "mode entry!\n";
316 return -1;
317 }
318
319 if (CLK_FILTER_NOT_SET == FilterMode) {
320 llvm::errs()
321 << "Error: Sampler map entry did not contain filer mode entry!\n";
322 return -1;
323 }
324
325 // Generate an equivalent expression in string form. Sort the
326 // strings to get a canonical ordering.
327 std::sort(samplerStrings.begin(), samplerStrings.end(),
328 std::less<StringRef>());
329 const auto samplerExpr = std::accumulate(
330 samplerStrings.begin(), samplerStrings.end(), std::string(),
331 [](std::string left, std::string right) {
332 return left + std::string(left.empty() ? "" : "|") + right;
333 });
334
335 // SamplerMapEntries->push_back(std::make_pair(
336 // NormalizedCoord | AddressingMode | FilterMode, samplerExpr));
337 SamplerMapEntries->emplace_back(
338 NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
339
340 // And reset the sampler strings for the next sampler in the map.
341 samplerStrings.clear();
342 }
343
344 // And lastly, if we are at the end of the file
345 if (i == e) {
346 break;
alan-bakerfec0a472018-11-08 18:09:40 -0500347 }
348 }
349
350 return 0;
351}
352
353// Sets |instance|'s options for compiling. Returns 0 if successful.
354int SetCompilerInstanceOptions(CompilerInstance &instance,
355 const llvm::StringRef &overiddenInputFilename,
356 const clang::FrontendInputFile &kernelFile,
alan-bakerf5e5f692018-11-27 08:33:24 -0500357 const std::string &program,
alan-bakerfec0a472018-11-08 18:09:40 -0500358 llvm::raw_string_ostream *diagnosticsStream) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500359 std::unique_ptr<llvm::MemoryBuffer> memory_buffer(nullptr);
360 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> errorOrInputFile(nullptr);
361 if (program.empty()) {
362 auto errorOrInputFile =
363 llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
alan-bakerfec0a472018-11-08 18:09:40 -0500364
alan-bakerf5e5f692018-11-27 08:33:24 -0500365 // If there was an error in getting the input file.
366 if (!errorOrInputFile) {
367 llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
368 << InputFilename.getValue() << "'\n";
369 return -1;
370 }
371 memory_buffer.reset(errorOrInputFile.get().release());
372 } else {
373 memory_buffer = llvm::MemoryBuffer::getMemBuffer(program.c_str(),
374 overiddenInputFilename);
alan-bakerfec0a472018-11-08 18:09:40 -0500375 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500376
alan-bakerfec0a472018-11-08 18:09:40 -0500377 if (verify) {
378 instance.getDiagnosticOpts().VerifyDiagnostics = true;
alan-bakerbccf62c2019-03-29 10:32:41 -0400379 instance.getDiagnosticOpts().VerifyPrefixes.push_back("expected");
alan-bakerfec0a472018-11-08 18:09:40 -0500380 }
381
382 clang::LangStandard::Kind standard = clang::LangStandard::lang_opencl12;
383
384 // We are targeting OpenCL 1.2 only
385 instance.getLangOpts().OpenCLVersion = 120;
386
387 instance.getLangOpts().C99 = true;
388 instance.getLangOpts().RTTI = false;
389 instance.getLangOpts().RTTIData = false;
390 instance.getLangOpts().MathErrno = false;
391 instance.getLangOpts().Optimize = false;
392 instance.getLangOpts().NoBuiltin = true;
393 instance.getLangOpts().ModulesSearchAll = false;
394 instance.getLangOpts().SinglePrecisionConstants = true;
395 instance.getCodeGenOpts().StackRealignment = true;
396 instance.getCodeGenOpts().SimplifyLibCalls = false;
397 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
398 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
399 instance.getDiagnosticOpts().IgnoreWarnings = false;
400
401 instance.getLangOpts().SinglePrecisionConstants =
402 cl_single_precision_constants;
403 // cl_denorms_are_zero ignored for now!
404 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
405 instance.getCodeGenOpts().LessPreciseFPMAD =
406 cl_mad_enable || cl_unsafe_math_optimizations;
407 // cl_no_signed_zeros ignored for now!
408 instance.getCodeGenOpts().UnsafeFPMath =
409 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
410 instance.getLangOpts().FiniteMathOnly =
411 cl_finite_math_only || cl_fast_relaxed_math;
412 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
413
414 // Preprocessor options
415 instance.getPreprocessorOpts().addMacroDef("__IMAGE_SUPPORT__");
416 if (cl_fast_relaxed_math) {
417 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
418 }
419
420 for (auto define : Defines) {
421 instance.getPreprocessorOpts().addMacroDef(define);
422 }
423
424 // Header search options
425 for (auto include : Includes) {
426 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
427 false, false);
428 }
429
430 // We always compile on opt 0 so we preserve as much debug information about
431 // the source as possible. We'll run optimization later, once we've had a
432 // chance to view the unoptimal code first
433 instance.getCodeGenOpts().OptimizationLevel = 0;
434
435// Debug information is disabled temporarily to call instruction.
436#if 0
437 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
438#endif
439
440 // We use the 32-bit pointer-width SPIR triple
441 llvm::Triple triple("spir-unknown-unknown");
442
443 instance.getInvocation().setLangDefaults(
444 instance.getLangOpts(), clang::InputKind::OpenCL, triple,
445 instance.getPreprocessorOpts(), standard);
446
447 // Override the C99 inline semantics to accommodate for more OpenCL C
448 // programs in the wild.
449 instance.getLangOpts().GNUInline = true;
450 instance.createDiagnostics(
451 new clang::TextDiagnosticPrinter(*diagnosticsStream,
452 &instance.getDiagnosticOpts()),
453 true);
454
455 instance.getTargetOpts().Triple = triple.str();
456
457 instance.getCodeGenOpts().MainFileName = overiddenInputFilename;
458 instance.getCodeGenOpts().PreserveVec3Type = true;
459 // Disable generation of lifetime intrinsic.
460 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
461 instance.getFrontendOpts().Inputs.push_back(kernelFile);
alan-bakerf5e5f692018-11-27 08:33:24 -0500462 // instance.getPreprocessorOpts().addRemappedFile(
463 // overiddenInputFilename, errorOrInputFile.get().release());
464 instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
465 memory_buffer.release());
alan-bakerfec0a472018-11-08 18:09:40 -0500466
467 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
468 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
469 const char *dataCasted = reinterpret_cast<const char *>(data);
470 init(dataCasted, dataCasted + data_length, true);
471 }
472
473 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
474 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
475 }
476
477 virtual ~OpenCLBuiltinMemoryBuffer() override {}
478 };
479
480 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
481 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
482 opencl_builtins_header_size - 1));
483
484 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
485
486 // Add the VULKAN macro.
487 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
488
489 // Add the __OPENCL_VERSION__ macro.
490 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
491
492 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
493 instance.getDiagnostics(),
494 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
495
496 instance.createFileManager();
497 instance.createSourceManager(instance.getFileManager());
498
499#ifdef _MSC_VER
500 std::string includePrefix("include\\");
501#else
502 std::string includePrefix("include/");
503#endif
504
505 auto entry = instance.getFileManager().getVirtualFile(
506 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
507 0);
508
509 instance.getSourceManager().overrideFileContents(
510 entry, std::move(openCLBuiltinMemoryBuffer));
511
512 return 0;
513}
514
alan-bakerf5e5f692018-11-27 08:33:24 -0500515// Populates |pm| with necessary passes to optimize and legalize the IR.
516int PopulatePassManager(
517 llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
518 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
519 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
520 *SamplerMapEntries) {
alan-bakerfec0a472018-11-08 18:09:40 -0500521 llvm::PassManagerBuilder pmBuilder;
522
523 switch (OptimizationLevel) {
524 case '0':
alan-bakerf5e5f692018-11-27 08:33:24 -0500525 case '1':
526 case '2':
527 case '3':
528 case 's':
529 case 'z':
530 break;
531 default:
532 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
533 << " specified!\n";
534 return -1;
535 }
536
537 switch (OptimizationLevel) {
538 case '0':
alan-bakerfec0a472018-11-08 18:09:40 -0500539 pmBuilder.OptLevel = 0;
540 break;
541 case '1':
542 pmBuilder.OptLevel = 1;
543 break;
544 case '2':
545 pmBuilder.OptLevel = 2;
546 break;
547 case '3':
548 pmBuilder.OptLevel = 3;
549 break;
550 case 's':
551 pmBuilder.SizeLevel = 1;
552 break;
553 case 'z':
554 pmBuilder.SizeLevel = 2;
555 break;
556 default:
557 break;
558 }
559
560 pm->add(clspv::createZeroInitializeAllocasPass());
561 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
562
563 if (0 < pmBuilder.OptLevel) {
564 pm->add(clspv::createOpenCLInlinerPass());
565 }
566
567 pm->add(clspv::createUndoByvalPass());
568 pm->add(clspv::createUndoSRetPass());
569 if (cluster_non_pointer_kernel_args) {
570 pm->add(clspv::createClusterPodKernelArgumentsPass());
571 }
572 pm->add(clspv::createReplaceOpenCLBuiltinPass());
573
574 // We need to run mem2reg and inst combine early because our
575 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
576 // %1 = alloca i32 1
577 // store <something> %1
578 // %2 = bitcast float* %1
579 // %3 = load float %2
580 pm->add(llvm::createPromoteMemoryToRegisterPass());
581
582 // Hide loads from __constant address space away from instcombine.
583 // This prevents us from generating select between pointers-to-__constant.
584 // See https://github.com/google/clspv/issues/71
585 pm->add(clspv::createHideConstantLoadsPass());
586
587 pm->add(llvm::createInstructionCombiningPass());
588
589 if (clspv::Option::InlineEntryPoints()) {
590 pm->add(clspv::createInlineEntryPointsPass());
591 } else {
592 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
593 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
594 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
595 }
596
597 if (0 == pmBuilder.OptLevel) {
598 // Mem2Reg pass should be run early because O0 level optimization leaves
599 // redundant alloca, load and store instructions from function arguments.
600 // clspv needs to remove them ahead of transformation.
601 pm->add(llvm::createPromoteMemoryToRegisterPass());
602
603 // SROA pass is run because it will fold structs/unions that are problematic
604 // on Vulkan SPIR-V away.
605 pm->add(llvm::createSROAPass());
606
607 // InstructionCombining pass folds bitcast and gep instructions which are
608 // not supported by Vulkan SPIR-V.
609 pm->add(llvm::createInstructionCombiningPass());
610 }
611
612 // Now we add any of the LLVM optimizations we wanted
613 pmBuilder.populateModulePassManager(*pm);
614
Alan Bakerea88c712018-12-06 11:40:49 -0500615
alan-bakerfec0a472018-11-08 18:09:40 -0500616 // Unhide loads from __constant address space. Undoes the action of
617 // HideConstantLoadsPass.
618 pm->add(clspv::createUnhideConstantLoadsPass());
619
620 pm->add(clspv::createFunctionInternalizerPass());
621 pm->add(clspv::createReplaceLLVMIntrinsicsPass());
622 pm->add(clspv::createUndoBoolPass());
623 pm->add(clspv::createUndoTruncatedSwitchConditionPass());
624 pm->add(llvm::createStructurizeCFGPass(false));
alan-baker3fa76d92018-11-12 14:54:40 -0500625 // Must be run after structurize cfg.
alan-bakerfec0a472018-11-08 18:09:40 -0500626 pm->add(clspv::createReorderBasicBlocksPass());
627 pm->add(clspv::createUndoGetElementPtrConstantExprPass());
628 pm->add(clspv::createSplatArgPass());
629 pm->add(clspv::createSimplifyPointerBitcastPass());
630 pm->add(clspv::createReplacePointerBitcastPass());
631
632 pm->add(clspv::createUndoTranslateSamplerFoldPass());
633
634 if (clspv::Option::ModuleConstantsInStorageBuffer()) {
635 pm->add(clspv::createClusterModuleScopeConstantVars());
636 }
637
638 pm->add(clspv::createShareModuleScopeVariablesPass());
alan-bakere9308012019-03-15 10:25:13 -0400639 // This should be run after LLVM and OpenCL intrinsics are replaced.
alan-bakerfec0a472018-11-08 18:09:40 -0500640 pm->add(clspv::createAllocateDescriptorsPass(*SamplerMapEntries));
641 pm->add(llvm::createVerifierPass());
642 pm->add(clspv::createDirectResourceAccessPass());
643 // Replacing pointer bitcasts can leave some trivial GEPs
644 // that are easy to remove. Also replace GEPs of GEPS
645 // left by replacing indirect buffer accesses.
646 pm->add(clspv::createSimplifyPointerBitcastPass());
alan-baker4217b322019-03-06 08:56:12 -0500647 // Run after DRA to clean up parameters and help reduce the need for variable
648 // pointers.
649 pm->add(clspv::createRemoveUnusedArgumentsPass());
alan-bakerfec0a472018-11-08 18:09:40 -0500650
651 pm->add(clspv::createSplatSelectConditionPass());
652 pm->add(clspv::createSignedCompareFixupPass());
653 // This pass generates insertions that need to be rewritten.
654 pm->add(clspv::createScalarizePass());
655 pm->add(clspv::createRewriteInsertsPass());
656 // This pass mucks with types to point where you shouldn't rely on DataLayout
657 // anymore so leave this right before SPIR-V generation.
658 pm->add(clspv::createUBOTypeTransformPass());
659 pm->add(clspv::createSPIRVProducerPass(
alan-bakerf5e5f692018-11-27 08:33:24 -0500660 *binaryStream, descriptor_map_entries, *SamplerMapEntries,
alan-bakerfec0a472018-11-08 18:09:40 -0500661 OutputAssembly.getValue(), OutputFormat == "c"));
alan-bakerf5e5f692018-11-27 08:33:24 -0500662
663 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500664}
665} // namespace
666
667namespace clspv {
668int Compile(const int argc, const char *const argv[]) {
669 // We need to change how one of the called passes works by spoofing
670 // ParseCommandLineOptions with the specific option.
671 const int llvmArgc = 2;
672 const char *llvmArgv[llvmArgc] = {
alan-bakerf5e5f692018-11-27 08:33:24 -0500673 argv[0],
674 "-simplifycfg-sink-common=false",
alan-bakerfec0a472018-11-08 18:09:40 -0500675 };
676
677 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
678
679 llvm::cl::ParseCommandLineOptions(argc, argv);
680
alan-bakerfec0a472018-11-08 18:09:40 -0500681 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500682 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500683 return error;
684
685 // if no output file was provided, use a default
686 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
687
688 // If we are reading our input file from stdin.
689 if ("-" == InputFilename) {
690 // We need to overwrite the file name we use.
691 overiddenInputFilename = "stdin.cl";
692 }
693
694 clang::CompilerInstance instance;
695 clang::FrontendInputFile kernelFile(overiddenInputFilename,
696 clang::InputKind::OpenCL);
697 std::string log;
698 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500699 if (auto error = SetCompilerInstanceOptions(
700 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500701 return error;
702
703 // Parse.
704 llvm::LLVMContext context;
705 clang::EmitLLVMOnlyAction action(&context);
706
707 // Prepare the action for processing kernelFile
708 const bool success = action.BeginSourceFile(instance, kernelFile);
709 if (!success) {
710 return -1;
711 }
712
713 action.Execute();
714 action.EndSourceFile();
715
716 clang::DiagnosticConsumer *const consumer =
717 instance.getDiagnostics().getClient();
718 consumer->finish();
719
720 auto num_errors = consumer->getNumErrors();
721 if (num_errors > 0) {
722 llvm::errs() << log << "\n";
723 return -1;
724 }
725
726 if (clspv::Option::ConstantArgsInUniformBuffer() &&
727 !clspv::Option::InlineEntryPoints()) {
alan-bakerb39c8262019-03-08 14:03:37 -0500728 llvm::errs() << "clspv restriction: -constant-args-ubo requires "
alan-bakerfec0a472018-11-08 18:09:40 -0500729 "-inline-entry-points\n";
730 return -1;
731 }
732
733 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
734 llvm::initializeCore(Registry);
735 llvm::initializeScalarOpts(Registry);
736
737 std::unique_ptr<llvm::Module> module(action.takeModule());
738
739 // Optimize.
740 // Create a memory buffer for temporarily writing the result.
741 SmallVector<char, 10000> binary;
742 llvm::raw_svector_ostream binaryStream(binary);
743 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500744 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500745 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
746 if (auto error =
747 PopulatePassManager(&pm, &binaryStream,
748 &descriptor_map_entries, &SamplerMapEntries))
749 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500750 pm.run(*module);
751
752 // Write outputs
753
754 // Write the descriptor map, if requested.
755 std::error_code error;
756 if (!DescriptorMapFilename.empty()) {
alan-bakerfec0a472018-11-08 18:09:40 -0500757 llvm::raw_fd_ostream descriptor_map_out_fd(DescriptorMapFilename, error,
alan-bakerbccf62c2019-03-29 10:32:41 -0400758 llvm::sys::fs::CD_CreateAlways,
759 llvm::sys::fs::FA_Write,
760 llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500761 if (error) {
762 llvm::errs() << "Unable to open descriptor map file '"
763 << DescriptorMapFilename << "': " << error.message() << '\n';
764 return -1;
765 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500766 std::string descriptor_map_string;
767 std::ostringstream str(descriptor_map_string);
768 for (const auto &entry : descriptor_map_entries) {
769 str << entry << "\n";
770 }
771 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500772 descriptor_map_out_fd.close();
773 }
774
775 // Write the resulting binary.
776 // Wait until now to try writing the file so that we only write it on
777 // successful compilation.
778 if (OutputFilename.empty()) {
779 // if we've to output assembly
780 if (OutputAssembly) {
781 OutputFilename = "a.spvasm";
782 } else if (OutputFormat == "c") {
783 OutputFilename = "a.spvinc";
784 } else {
785 OutputFilename = "a.spv";
786 }
787 }
alan-bakerbccf62c2019-03-29 10:32:41 -0400788 llvm::raw_fd_ostream outStream(OutputFilename, error, llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500789
790 if (error) {
791 llvm::errs() << "Unable to open output file '" << OutputFilename
792 << "': " << error.message() << '\n';
793 return -1;
794 }
795 outStream << binaryStream.str();
796
797 return 0;
798}
alan-bakerf5e5f692018-11-27 08:33:24 -0500799
800int CompileFromSourceString(const std::string &program,
801 const std::string &sampler_map,
802 const std::string &options,
803 std::vector<uint32_t> *output_binary,
804 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
805 // We need to change how one of the called passes works by spoofing
806 // ParseCommandLineOptions with the specific option.
807 const int llvmArgc = 2;
808 const char *llvmArgv[llvmArgc] = {
809 "clspv",
810 "-simplifycfg-sink-common=false",
811 };
812
alan-baker2d606eb2019-03-14 13:16:09 -0400813 llvm::cl::ResetAllOptionOccurrences();
alan-bakerf5e5f692018-11-27 08:33:24 -0500814 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
815
816 llvm::SmallVector<const char *, 20> argv;
817 llvm::BumpPtrAllocator A;
818 llvm::StringSaver Saver(A);
819 argv.push_back(Saver.save("clspv").data());
820 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
821 int argc = static_cast<int>(argv.size());
822 llvm::cl::ParseCommandLineOptions(argc, &argv[0]);
823
824 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
825 if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
826 return error;
827
828 InputFilename = "source.cl";
829 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
830
831 clang::CompilerInstance instance;
832 clang::FrontendInputFile kernelFile(overiddenInputFilename,
833 clang::InputKind::OpenCL);
834 std::string log;
835 llvm::raw_string_ostream diagnosticsStream(log);
836 if (auto error =
837 SetCompilerInstanceOptions(instance, overiddenInputFilename,
838 kernelFile, program, &diagnosticsStream))
839 return error;
840
841 // Parse.
842 llvm::LLVMContext context;
843 clang::EmitLLVMOnlyAction action(&context);
844
845 // Prepare the action for processing kernelFile
846 const bool success = action.BeginSourceFile(instance, kernelFile);
847 if (!success) {
848 return -1;
849 }
850
851 action.Execute();
852 action.EndSourceFile();
853
854 clang::DiagnosticConsumer *const consumer =
855 instance.getDiagnostics().getClient();
856 consumer->finish();
857
858 auto num_errors = consumer->getNumErrors();
859 if (num_errors > 0) {
860 llvm::errs() << log << "\n";
861 return -1;
862 }
863
864 if (clspv::Option::ConstantArgsInUniformBuffer() &&
865 !clspv::Option::InlineEntryPoints()) {
866 llvm::errs() << "clspv restriction: -constant-arg-ubo requires "
867 "-inline-entry-points\n";
868 return -1;
869 }
870
871 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
872 llvm::initializeCore(Registry);
873 llvm::initializeScalarOpts(Registry);
874
875 std::unique_ptr<llvm::Module> module(action.takeModule());
876
877 // Optimize.
878 // Create a memory buffer for temporarily writing the result.
879 SmallVector<char, 10000> binary;
880 llvm::raw_svector_ostream binaryStream(binary);
881 std::string descriptor_map;
882 llvm::legacy::PassManager pm;
883 if (auto error =
884 PopulatePassManager(&pm, &binaryStream,
885 descriptor_map_entries, &SamplerMapEntries))
886 return error;
887 pm.run(*module);
888
889 // Write outputs
890
891 // Write the descriptor map. This is required.
892 assert(descriptor_map_entries && "Valid descriptor map container is required.");
893 if (!DescriptorMapFilename.empty()) {
894 llvm::errs() << "Warning: -descriptormap is ignored descriptor map container is provided.\n";
895 }
896
897 // Write the resulting binary.
898 // Wait until now to try writing the file so that we only write it on
899 // successful compilation.
900 assert(output_binary && "Valid binary container is required.");
901 if (!OutputFilename.empty()) {
902 llvm::outs()
903 << "Warning: -o is ignored when binary container is provided.\n";
904 }
905 output_binary->resize(binary.size() / 4);
906 memcpy(output_binary->data(), binary.data(), binary.size());
907
908 return 0;
909}
alan-bakerfec0a472018-11-08 18:09:40 -0500910} // namespace clspv