blob: d4dc8ce59b72eccb84be7b89a0832e6550d07113 [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
alan-bakerfec0a472018-11-08 18:09:40 -0500134static llvm::cl::opt<std::string> OutputFormat(
135 "mfmt", llvm::cl::init(""),
136 llvm::cl::desc(
137 "Specify special output format. 'c' is as a C initializer list"),
138 llvm::cl::value_desc("format"));
139
140static llvm::cl::opt<std::string>
141 SamplerMap("samplermap", llvm::cl::desc("Literal sampler map"),
142 llvm::cl::value_desc("filename"));
143
144static llvm::cl::opt<bool> cluster_non_pointer_kernel_args(
145 "cluster-pod-kernel-args", llvm::cl::init(false),
146 llvm::cl::desc("Collect plain-old-data kernel arguments into a struct in "
147 "a single storage buffer, using a binding number after "
148 "other arguments. Use this to reduce storage buffer "
149 "descriptors."));
150
151static llvm::cl::opt<bool> verify("verify", llvm::cl::init(false),
152 llvm::cl::desc("Verify diagnostic outputs"));
153
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100154static llvm::cl::opt<bool>
155 IgnoreWarnings("w", llvm::cl::init(false),
156 llvm::cl::desc("Disable all warnings"));
157
158static llvm::cl::opt<bool>
159 WarningsAsErrors("Werror", llvm::cl::init(false),
160 llvm::cl::desc("Turn warnings into errors"));
161
alan-bakerfec0a472018-11-08 18:09:40 -0500162// Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
163// if successful.
alan-bakerf5e5f692018-11-27 08:33:24 -0500164int ParseSamplerMap(const std::string &sampler_map,
165 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
166 *SamplerMapEntries) {
167 std::unique_ptr<llvm::MemoryBuffer> samplerMapBuffer(nullptr);
168 if (!sampler_map.empty()) {
169 // Parse the sampler map from the provided string.
170 samplerMapBuffer = llvm::MemoryBuffer::getMemBuffer(sampler_map);
171
172 if (!SamplerMap.empty()) {
173 llvm::outs() << "Warning: -samplermap is ignored when the sampler map is "
174 "provided through a string.\n";
175 }
176 } else if (!SamplerMap.empty()) {
177 // Parse the sampler map from the option provided file.
alan-bakerfec0a472018-11-08 18:09:40 -0500178 auto errorOrSamplerMapFile =
179 llvm::MemoryBuffer::getFile(SamplerMap.getValue());
180
181 // If there was an error in getting the sampler map file.
182 if (!errorOrSamplerMapFile) {
183 llvm::errs() << "Error: " << errorOrSamplerMapFile.getError().message()
184 << " '" << SamplerMap.getValue() << "'\n";
185 return -1;
186 }
187
alan-bakerf5e5f692018-11-27 08:33:24 -0500188 samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
alan-bakerfec0a472018-11-08 18:09:40 -0500189 if (0 == samplerMapBuffer->getBufferSize()) {
190 llvm::errs() << "Error: Sampler map was an empty file!\n";
191 return -1;
192 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500193 }
alan-bakerfec0a472018-11-08 18:09:40 -0500194
alan-bakerf5e5f692018-11-27 08:33:24 -0500195 // No sampler map to parse.
196 if (!samplerMapBuffer || 0 == samplerMapBuffer->getBufferSize())
197 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500198
alan-bakerf5e5f692018-11-27 08:33:24 -0500199 llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
alan-bakerfec0a472018-11-08 18:09:40 -0500200
alan-bakerf5e5f692018-11-27 08:33:24 -0500201 // We need to keep track of the beginning of the current entry.
202 const char *b = samplerMapBuffer->getBufferStart();
203 for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
204 // If we have a separator between declarations.
205 if ((*i == '|') || (*i == ',') || (i == e)) {
206 if (i == b) {
207 llvm::errs() << "Error: Sampler map contained an empty entry!\n";
208 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500209 }
210
alan-bakerf5e5f692018-11-27 08:33:24 -0500211 samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
alan-bakerfec0a472018-11-08 18:09:40 -0500212
alan-bakerf5e5f692018-11-27 08:33:24 -0500213 // And set b the next character after i.
214 b = i + 1;
215 }
alan-bakerfec0a472018-11-08 18:09:40 -0500216
alan-bakerf5e5f692018-11-27 08:33:24 -0500217 // If we have a separator between declarations within a single sampler.
218 if ((*i == ',') || (i == e)) {
219 enum NormalizedCoords {
220 CLK_NORMALIZED_COORDS_FALSE = 0x00,
221 CLK_NORMALIZED_COORDS_TRUE = 0x01,
222 CLK_NORMALIZED_COORDS_NOT_SET
223 } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
alan-bakerfec0a472018-11-08 18:09:40 -0500224
alan-bakerf5e5f692018-11-27 08:33:24 -0500225 enum AddressingModes {
226 CLK_ADDRESS_NONE = 0x00,
227 CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
228 CLK_ADDRESS_CLAMP = 0x04,
229 CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
230 CLK_ADDRESS_REPEAT = 0x06,
231 CLK_ADDRESS_NOT_SET
232 } AddressingMode = CLK_ADDRESS_NOT_SET;
233
234 enum FilterModes {
235 CLK_FILTER_NEAREST = 0x10,
236 CLK_FILTER_LINEAR = 0x20,
237 CLK_FILTER_NOT_SET
238 } FilterMode = CLK_FILTER_NOT_SET;
239
240 for (auto str : samplerStrings) {
241 if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
242 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
243 llvm::errs() << "Error: Sampler map normalized coordinates was "
244 "previously set!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500245 return -1;
246 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500247 NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
248 } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
249 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
250 llvm::errs() << "Error: Sampler map normalized coordinates was "
251 "previously set!\n";
252 return -1;
253 }
254 NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
255 } else if ("CLK_ADDRESS_NONE" == str) {
256 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
257 llvm::errs()
258 << "Error: Sampler map addressing mode was previously set!\n";
259 return -1;
260 }
261 AddressingMode = CLK_ADDRESS_NONE;
262 } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
263 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
264 llvm::errs()
265 << "Error: Sampler map addressing mode was previously set!\n";
266 return -1;
267 }
268 AddressingMode = CLK_ADDRESS_CLAMP_TO_EDGE;
269 } else if ("CLK_ADDRESS_CLAMP" == str) {
270 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
271 llvm::errs()
272 << "Error: Sampler map addressing mode was previously set!\n";
273 return -1;
274 }
275 AddressingMode = CLK_ADDRESS_CLAMP;
276 } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
277 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
278 llvm::errs()
279 << "Error: Sampler map addressing mode was previously set!\n";
280 return -1;
281 }
282 AddressingMode = CLK_ADDRESS_MIRRORED_REPEAT;
283 } else if ("CLK_ADDRESS_REPEAT" == str) {
284 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
285 llvm::errs()
286 << "Error: Sampler map addressing mode was previously set!\n";
287 return -1;
288 }
289 AddressingMode = CLK_ADDRESS_REPEAT;
290 } else if ("CLK_FILTER_NEAREST" == str) {
291 if (CLK_FILTER_NOT_SET != FilterMode) {
292 llvm::errs()
293 << "Error: Sampler map filtering mode was previously set!\n";
294 return -1;
295 }
296 FilterMode = CLK_FILTER_NEAREST;
297 } else if ("CLK_FILTER_LINEAR" == str) {
298 if (CLK_FILTER_NOT_SET != FilterMode) {
299 llvm::errs()
300 << "Error: Sampler map filtering mode was previously set!\n";
301 return -1;
302 }
303 FilterMode = CLK_FILTER_LINEAR;
304 } else {
305 llvm::errs() << "Error: Unknown sampler string '" << str
306 << "' found!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500307 return -1;
308 }
alan-bakerfec0a472018-11-08 18:09:40 -0500309 }
310
alan-bakerf5e5f692018-11-27 08:33:24 -0500311 if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
312 llvm::errs() << "Error: Sampler map entry did not contain normalized "
313 "coordinates entry!\n";
314 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500315 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500316
317 if (CLK_ADDRESS_NOT_SET == AddressingMode) {
318 llvm::errs() << "Error: Sampler map entry did not contain addressing "
319 "mode entry!\n";
320 return -1;
321 }
322
323 if (CLK_FILTER_NOT_SET == FilterMode) {
324 llvm::errs()
325 << "Error: Sampler map entry did not contain filer mode entry!\n";
326 return -1;
327 }
328
329 // Generate an equivalent expression in string form. Sort the
330 // strings to get a canonical ordering.
331 std::sort(samplerStrings.begin(), samplerStrings.end(),
332 std::less<StringRef>());
333 const auto samplerExpr = std::accumulate(
334 samplerStrings.begin(), samplerStrings.end(), std::string(),
335 [](std::string left, std::string right) {
336 return left + std::string(left.empty() ? "" : "|") + right;
337 });
338
339 // SamplerMapEntries->push_back(std::make_pair(
340 // NormalizedCoord | AddressingMode | FilterMode, samplerExpr));
341 SamplerMapEntries->emplace_back(
342 NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
343
344 // And reset the sampler strings for the next sampler in the map.
345 samplerStrings.clear();
346 }
347
348 // And lastly, if we are at the end of the file
349 if (i == e) {
350 break;
alan-bakerfec0a472018-11-08 18:09:40 -0500351 }
352 }
353
354 return 0;
355}
356
357// Sets |instance|'s options for compiling. Returns 0 if successful.
358int SetCompilerInstanceOptions(CompilerInstance &instance,
359 const llvm::StringRef &overiddenInputFilename,
360 const clang::FrontendInputFile &kernelFile,
alan-bakerf5e5f692018-11-27 08:33:24 -0500361 const std::string &program,
alan-bakerfec0a472018-11-08 18:09:40 -0500362 llvm::raw_string_ostream *diagnosticsStream) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500363 std::unique_ptr<llvm::MemoryBuffer> memory_buffer(nullptr);
364 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> errorOrInputFile(nullptr);
365 if (program.empty()) {
366 auto errorOrInputFile =
367 llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
alan-bakerfec0a472018-11-08 18:09:40 -0500368
alan-bakerf5e5f692018-11-27 08:33:24 -0500369 // If there was an error in getting the input file.
370 if (!errorOrInputFile) {
371 llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
372 << InputFilename.getValue() << "'\n";
373 return -1;
374 }
375 memory_buffer.reset(errorOrInputFile.get().release());
376 } else {
377 memory_buffer = llvm::MemoryBuffer::getMemBuffer(program.c_str(),
378 overiddenInputFilename);
alan-bakerfec0a472018-11-08 18:09:40 -0500379 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500380
alan-bakerfec0a472018-11-08 18:09:40 -0500381 if (verify) {
382 instance.getDiagnosticOpts().VerifyDiagnostics = true;
alan-bakerbccf62c2019-03-29 10:32:41 -0400383 instance.getDiagnosticOpts().VerifyPrefixes.push_back("expected");
alan-bakerfec0a472018-11-08 18:09:40 -0500384 }
385
386 clang::LangStandard::Kind standard = clang::LangStandard::lang_opencl12;
387
388 // We are targeting OpenCL 1.2 only
389 instance.getLangOpts().OpenCLVersion = 120;
390
391 instance.getLangOpts().C99 = true;
392 instance.getLangOpts().RTTI = false;
393 instance.getLangOpts().RTTIData = false;
394 instance.getLangOpts().MathErrno = false;
395 instance.getLangOpts().Optimize = false;
396 instance.getLangOpts().NoBuiltin = true;
397 instance.getLangOpts().ModulesSearchAll = false;
398 instance.getLangOpts().SinglePrecisionConstants = true;
399 instance.getCodeGenOpts().StackRealignment = true;
400 instance.getCodeGenOpts().SimplifyLibCalls = false;
401 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
402 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100403 instance.getDiagnosticOpts().IgnoreWarnings = IgnoreWarnings;
alan-bakerfec0a472018-11-08 18:09:40 -0500404
405 instance.getLangOpts().SinglePrecisionConstants =
406 cl_single_precision_constants;
407 // cl_denorms_are_zero ignored for now!
408 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
409 instance.getCodeGenOpts().LessPreciseFPMAD =
410 cl_mad_enable || cl_unsafe_math_optimizations;
411 // cl_no_signed_zeros ignored for now!
412 instance.getCodeGenOpts().UnsafeFPMath =
413 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
414 instance.getLangOpts().FiniteMathOnly =
415 cl_finite_math_only || cl_fast_relaxed_math;
416 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
417
418 // Preprocessor options
419 instance.getPreprocessorOpts().addMacroDef("__IMAGE_SUPPORT__");
420 if (cl_fast_relaxed_math) {
421 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
422 }
423
424 for (auto define : Defines) {
425 instance.getPreprocessorOpts().addMacroDef(define);
426 }
427
428 // Header search options
429 for (auto include : Includes) {
430 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
431 false, false);
432 }
433
434 // We always compile on opt 0 so we preserve as much debug information about
435 // the source as possible. We'll run optimization later, once we've had a
436 // chance to view the unoptimal code first
437 instance.getCodeGenOpts().OptimizationLevel = 0;
438
439// Debug information is disabled temporarily to call instruction.
440#if 0
441 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
442#endif
443
444 // We use the 32-bit pointer-width SPIR triple
445 llvm::Triple triple("spir-unknown-unknown");
446
447 instance.getInvocation().setLangDefaults(
448 instance.getLangOpts(), clang::InputKind::OpenCL, triple,
449 instance.getPreprocessorOpts(), standard);
450
451 // Override the C99 inline semantics to accommodate for more OpenCL C
452 // programs in the wild.
453 instance.getLangOpts().GNUInline = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100454
455 // Set up diagnostics
alan-bakerfec0a472018-11-08 18:09:40 -0500456 instance.createDiagnostics(
457 new clang::TextDiagnosticPrinter(*diagnosticsStream,
458 &instance.getDiagnosticOpts()),
459 true);
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100460 instance.getDiagnostics().setWarningsAsErrors(WarningsAsErrors);
461 instance.getDiagnostics().setEnableAllWarnings(true);
alan-bakerfec0a472018-11-08 18:09:40 -0500462
463 instance.getTargetOpts().Triple = triple.str();
464
465 instance.getCodeGenOpts().MainFileName = overiddenInputFilename;
466 instance.getCodeGenOpts().PreserveVec3Type = true;
467 // Disable generation of lifetime intrinsic.
468 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
469 instance.getFrontendOpts().Inputs.push_back(kernelFile);
alan-bakerf5e5f692018-11-27 08:33:24 -0500470 // instance.getPreprocessorOpts().addRemappedFile(
471 // overiddenInputFilename, errorOrInputFile.get().release());
472 instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
473 memory_buffer.release());
alan-bakerfec0a472018-11-08 18:09:40 -0500474
475 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
476 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
477 const char *dataCasted = reinterpret_cast<const char *>(data);
478 init(dataCasted, dataCasted + data_length, true);
479 }
480
481 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
482 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
483 }
484
485 virtual ~OpenCLBuiltinMemoryBuffer() override {}
486 };
487
488 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
489 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
490 opencl_builtins_header_size - 1));
491
492 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
493
494 // Add the VULKAN macro.
495 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
496
497 // Add the __OPENCL_VERSION__ macro.
498 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
499
500 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
501 instance.getDiagnostics(),
502 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
503
504 instance.createFileManager();
505 instance.createSourceManager(instance.getFileManager());
506
507#ifdef _MSC_VER
508 std::string includePrefix("include\\");
509#else
510 std::string includePrefix("include/");
511#endif
512
513 auto entry = instance.getFileManager().getVirtualFile(
514 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
515 0);
516
517 instance.getSourceManager().overrideFileContents(
518 entry, std::move(openCLBuiltinMemoryBuffer));
519
520 return 0;
521}
522
alan-bakerf5e5f692018-11-27 08:33:24 -0500523// Populates |pm| with necessary passes to optimize and legalize the IR.
524int PopulatePassManager(
525 llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
526 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
527 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
528 *SamplerMapEntries) {
alan-bakerfec0a472018-11-08 18:09:40 -0500529 llvm::PassManagerBuilder pmBuilder;
530
531 switch (OptimizationLevel) {
532 case '0':
alan-bakerf5e5f692018-11-27 08:33:24 -0500533 case '1':
534 case '2':
535 case '3':
536 case 's':
537 case 'z':
538 break;
539 default:
540 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
541 << " specified!\n";
542 return -1;
543 }
544
545 switch (OptimizationLevel) {
546 case '0':
alan-bakerfec0a472018-11-08 18:09:40 -0500547 pmBuilder.OptLevel = 0;
548 break;
549 case '1':
550 pmBuilder.OptLevel = 1;
551 break;
552 case '2':
553 pmBuilder.OptLevel = 2;
554 break;
555 case '3':
556 pmBuilder.OptLevel = 3;
557 break;
558 case 's':
559 pmBuilder.SizeLevel = 1;
560 break;
561 case 'z':
562 pmBuilder.SizeLevel = 2;
563 break;
564 default:
565 break;
566 }
567
568 pm->add(clspv::createZeroInitializeAllocasPass());
569 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
570
571 if (0 < pmBuilder.OptLevel) {
572 pm->add(clspv::createOpenCLInlinerPass());
573 }
574
575 pm->add(clspv::createUndoByvalPass());
576 pm->add(clspv::createUndoSRetPass());
577 if (cluster_non_pointer_kernel_args) {
578 pm->add(clspv::createClusterPodKernelArgumentsPass());
579 }
580 pm->add(clspv::createReplaceOpenCLBuiltinPass());
581
582 // We need to run mem2reg and inst combine early because our
583 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
584 // %1 = alloca i32 1
585 // store <something> %1
586 // %2 = bitcast float* %1
587 // %3 = load float %2
588 pm->add(llvm::createPromoteMemoryToRegisterPass());
589
590 // Hide loads from __constant address space away from instcombine.
591 // This prevents us from generating select between pointers-to-__constant.
592 // See https://github.com/google/clspv/issues/71
593 pm->add(clspv::createHideConstantLoadsPass());
594
595 pm->add(llvm::createInstructionCombiningPass());
596
597 if (clspv::Option::InlineEntryPoints()) {
598 pm->add(clspv::createInlineEntryPointsPass());
599 } else {
600 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
601 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
602 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
603 }
604
605 if (0 == pmBuilder.OptLevel) {
606 // Mem2Reg pass should be run early because O0 level optimization leaves
607 // redundant alloca, load and store instructions from function arguments.
608 // clspv needs to remove them ahead of transformation.
609 pm->add(llvm::createPromoteMemoryToRegisterPass());
610
611 // SROA pass is run because it will fold structs/unions that are problematic
612 // on Vulkan SPIR-V away.
613 pm->add(llvm::createSROAPass());
614
615 // InstructionCombining pass folds bitcast and gep instructions which are
616 // not supported by Vulkan SPIR-V.
617 pm->add(llvm::createInstructionCombiningPass());
618 }
619
620 // Now we add any of the LLVM optimizations we wanted
621 pmBuilder.populateModulePassManager(*pm);
622
Alan Bakerea88c712018-12-06 11:40:49 -0500623
alan-bakerfec0a472018-11-08 18:09:40 -0500624 // Unhide loads from __constant address space. Undoes the action of
625 // HideConstantLoadsPass.
626 pm->add(clspv::createUnhideConstantLoadsPass());
627
628 pm->add(clspv::createFunctionInternalizerPass());
629 pm->add(clspv::createReplaceLLVMIntrinsicsPass());
630 pm->add(clspv::createUndoBoolPass());
631 pm->add(clspv::createUndoTruncatedSwitchConditionPass());
632 pm->add(llvm::createStructurizeCFGPass(false));
alan-baker3fa76d92018-11-12 14:54:40 -0500633 // Must be run after structurize cfg.
alan-bakerfec0a472018-11-08 18:09:40 -0500634 pm->add(clspv::createReorderBasicBlocksPass());
635 pm->add(clspv::createUndoGetElementPtrConstantExprPass());
636 pm->add(clspv::createSplatArgPass());
637 pm->add(clspv::createSimplifyPointerBitcastPass());
638 pm->add(clspv::createReplacePointerBitcastPass());
639
640 pm->add(clspv::createUndoTranslateSamplerFoldPass());
641
642 if (clspv::Option::ModuleConstantsInStorageBuffer()) {
643 pm->add(clspv::createClusterModuleScopeConstantVars());
644 }
645
646 pm->add(clspv::createShareModuleScopeVariablesPass());
alan-bakere9308012019-03-15 10:25:13 -0400647 // This should be run after LLVM and OpenCL intrinsics are replaced.
alan-bakerfec0a472018-11-08 18:09:40 -0500648 pm->add(clspv::createAllocateDescriptorsPass(*SamplerMapEntries));
649 pm->add(llvm::createVerifierPass());
650 pm->add(clspv::createDirectResourceAccessPass());
651 // Replacing pointer bitcasts can leave some trivial GEPs
652 // that are easy to remove. Also replace GEPs of GEPS
653 // left by replacing indirect buffer accesses.
654 pm->add(clspv::createSimplifyPointerBitcastPass());
alan-baker4217b322019-03-06 08:56:12 -0500655 // Run after DRA to clean up parameters and help reduce the need for variable
656 // pointers.
657 pm->add(clspv::createRemoveUnusedArgumentsPass());
alan-bakerfec0a472018-11-08 18:09:40 -0500658
659 pm->add(clspv::createSplatSelectConditionPass());
660 pm->add(clspv::createSignedCompareFixupPass());
661 // This pass generates insertions that need to be rewritten.
662 pm->add(clspv::createScalarizePass());
663 pm->add(clspv::createRewriteInsertsPass());
664 // This pass mucks with types to point where you shouldn't rely on DataLayout
665 // anymore so leave this right before SPIR-V generation.
666 pm->add(clspv::createUBOTypeTransformPass());
667 pm->add(clspv::createSPIRVProducerPass(
alan-bakerf5e5f692018-11-27 08:33:24 -0500668 *binaryStream, descriptor_map_entries, *SamplerMapEntries,
Kévin Petite4786902019-04-02 21:51:47 +0100669 false /* Output assembly */, OutputFormat == "c"));
alan-bakerf5e5f692018-11-27 08:33:24 -0500670
671 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500672}
673} // namespace
674
675namespace clspv {
676int Compile(const int argc, const char *const argv[]) {
677 // We need to change how one of the called passes works by spoofing
678 // ParseCommandLineOptions with the specific option.
679 const int llvmArgc = 2;
680 const char *llvmArgv[llvmArgc] = {
alan-bakerf5e5f692018-11-27 08:33:24 -0500681 argv[0],
682 "-simplifycfg-sink-common=false",
alan-bakerfec0a472018-11-08 18:09:40 -0500683 };
684
685 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
686
687 llvm::cl::ParseCommandLineOptions(argc, argv);
688
alan-bakerfec0a472018-11-08 18:09:40 -0500689 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500690 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500691 return error;
692
693 // if no output file was provided, use a default
694 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
695
696 // If we are reading our input file from stdin.
697 if ("-" == InputFilename) {
698 // We need to overwrite the file name we use.
699 overiddenInputFilename = "stdin.cl";
700 }
701
702 clang::CompilerInstance instance;
703 clang::FrontendInputFile kernelFile(overiddenInputFilename,
704 clang::InputKind::OpenCL);
705 std::string log;
706 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500707 if (auto error = SetCompilerInstanceOptions(
708 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500709 return error;
710
711 // Parse.
712 llvm::LLVMContext context;
713 clang::EmitLLVMOnlyAction action(&context);
714
715 // Prepare the action for processing kernelFile
716 const bool success = action.BeginSourceFile(instance, kernelFile);
717 if (!success) {
718 return -1;
719 }
720
721 action.Execute();
722 action.EndSourceFile();
723
724 clang::DiagnosticConsumer *const consumer =
725 instance.getDiagnostics().getClient();
726 consumer->finish();
727
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100728 auto num_warnings = consumer->getNumWarnings();
alan-bakerfec0a472018-11-08 18:09:40 -0500729 auto num_errors = consumer->getNumErrors();
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100730 if ((num_errors > 0) || (num_warnings > 0)) {
731 llvm::errs() << log;
732 }
alan-bakerfec0a472018-11-08 18:09:40 -0500733 if (num_errors > 0) {
alan-bakerfec0a472018-11-08 18:09:40 -0500734 return -1;
735 }
736
737 if (clspv::Option::ConstantArgsInUniformBuffer() &&
738 !clspv::Option::InlineEntryPoints()) {
alan-bakerb39c8262019-03-08 14:03:37 -0500739 llvm::errs() << "clspv restriction: -constant-args-ubo requires "
alan-bakerfec0a472018-11-08 18:09:40 -0500740 "-inline-entry-points\n";
741 return -1;
742 }
743
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100744 // Don't run the passes or produce any output in verify mode.
745 // Clang doesn't always produce a valid module.
746 if (verify) {
747 return 0;
748 }
749
alan-bakerfec0a472018-11-08 18:09:40 -0500750 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
751 llvm::initializeCore(Registry);
752 llvm::initializeScalarOpts(Registry);
753
754 std::unique_ptr<llvm::Module> module(action.takeModule());
755
756 // Optimize.
757 // Create a memory buffer for temporarily writing the result.
758 SmallVector<char, 10000> binary;
759 llvm::raw_svector_ostream binaryStream(binary);
760 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500761 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500762 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
763 if (auto error =
764 PopulatePassManager(&pm, &binaryStream,
765 &descriptor_map_entries, &SamplerMapEntries))
766 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500767 pm.run(*module);
768
769 // Write outputs
770
771 // Write the descriptor map, if requested.
772 std::error_code error;
773 if (!DescriptorMapFilename.empty()) {
alan-bakerfec0a472018-11-08 18:09:40 -0500774 llvm::raw_fd_ostream descriptor_map_out_fd(DescriptorMapFilename, error,
alan-bakerbccf62c2019-03-29 10:32:41 -0400775 llvm::sys::fs::CD_CreateAlways,
776 llvm::sys::fs::FA_Write,
777 llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500778 if (error) {
779 llvm::errs() << "Unable to open descriptor map file '"
780 << DescriptorMapFilename << "': " << error.message() << '\n';
781 return -1;
782 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500783 std::string descriptor_map_string;
784 std::ostringstream str(descriptor_map_string);
785 for (const auto &entry : descriptor_map_entries) {
786 str << entry << "\n";
787 }
788 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500789 descriptor_map_out_fd.close();
790 }
791
792 // Write the resulting binary.
793 // Wait until now to try writing the file so that we only write it on
794 // successful compilation.
795 if (OutputFilename.empty()) {
Kévin Petite4786902019-04-02 21:51:47 +0100796 if (OutputFormat == "c") {
alan-bakerfec0a472018-11-08 18:09:40 -0500797 OutputFilename = "a.spvinc";
798 } else {
799 OutputFilename = "a.spv";
800 }
801 }
alan-bakerbccf62c2019-03-29 10:32:41 -0400802 llvm::raw_fd_ostream outStream(OutputFilename, error, llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500803
804 if (error) {
805 llvm::errs() << "Unable to open output file '" << OutputFilename
806 << "': " << error.message() << '\n';
807 return -1;
808 }
809 outStream << binaryStream.str();
810
811 return 0;
812}
alan-bakerf5e5f692018-11-27 08:33:24 -0500813
814int CompileFromSourceString(const std::string &program,
815 const std::string &sampler_map,
816 const std::string &options,
817 std::vector<uint32_t> *output_binary,
818 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
819 // We need to change how one of the called passes works by spoofing
820 // ParseCommandLineOptions with the specific option.
821 const int llvmArgc = 2;
822 const char *llvmArgv[llvmArgc] = {
823 "clspv",
824 "-simplifycfg-sink-common=false",
825 };
826
alan-baker2d606eb2019-03-14 13:16:09 -0400827 llvm::cl::ResetAllOptionOccurrences();
alan-bakerf5e5f692018-11-27 08:33:24 -0500828 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
829
830 llvm::SmallVector<const char *, 20> argv;
831 llvm::BumpPtrAllocator A;
832 llvm::StringSaver Saver(A);
833 argv.push_back(Saver.save("clspv").data());
834 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
835 int argc = static_cast<int>(argv.size());
836 llvm::cl::ParseCommandLineOptions(argc, &argv[0]);
837
838 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
839 if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
840 return error;
841
842 InputFilename = "source.cl";
843 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
844
845 clang::CompilerInstance instance;
846 clang::FrontendInputFile kernelFile(overiddenInputFilename,
847 clang::InputKind::OpenCL);
848 std::string log;
849 llvm::raw_string_ostream diagnosticsStream(log);
850 if (auto error =
851 SetCompilerInstanceOptions(instance, overiddenInputFilename,
852 kernelFile, program, &diagnosticsStream))
853 return error;
854
855 // Parse.
856 llvm::LLVMContext context;
857 clang::EmitLLVMOnlyAction action(&context);
858
859 // Prepare the action for processing kernelFile
860 const bool success = action.BeginSourceFile(instance, kernelFile);
861 if (!success) {
862 return -1;
863 }
864
865 action.Execute();
866 action.EndSourceFile();
867
868 clang::DiagnosticConsumer *const consumer =
869 instance.getDiagnostics().getClient();
870 consumer->finish();
871
872 auto num_errors = consumer->getNumErrors();
873 if (num_errors > 0) {
874 llvm::errs() << log << "\n";
875 return -1;
876 }
877
878 if (clspv::Option::ConstantArgsInUniformBuffer() &&
879 !clspv::Option::InlineEntryPoints()) {
880 llvm::errs() << "clspv restriction: -constant-arg-ubo requires "
881 "-inline-entry-points\n";
882 return -1;
883 }
884
885 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
886 llvm::initializeCore(Registry);
887 llvm::initializeScalarOpts(Registry);
888
889 std::unique_ptr<llvm::Module> module(action.takeModule());
890
891 // Optimize.
892 // Create a memory buffer for temporarily writing the result.
893 SmallVector<char, 10000> binary;
894 llvm::raw_svector_ostream binaryStream(binary);
895 std::string descriptor_map;
896 llvm::legacy::PassManager pm;
897 if (auto error =
898 PopulatePassManager(&pm, &binaryStream,
899 descriptor_map_entries, &SamplerMapEntries))
900 return error;
901 pm.run(*module);
902
903 // Write outputs
904
905 // Write the descriptor map. This is required.
906 assert(descriptor_map_entries && "Valid descriptor map container is required.");
907 if (!DescriptorMapFilename.empty()) {
908 llvm::errs() << "Warning: -descriptormap is ignored descriptor map container is provided.\n";
909 }
910
911 // Write the resulting binary.
912 // Wait until now to try writing the file so that we only write it on
913 // successful compilation.
914 assert(output_binary && "Valid binary container is required.");
915 if (!OutputFilename.empty()) {
916 llvm::outs()
917 << "Warning: -o is ignored when binary container is provided.\n";
918 }
919 output_binary->resize(binary.size() / 4);
920 memcpy(output_binary->data(), binary.data(), binary.size());
921
922 return 0;
923}
alan-bakerfec0a472018-11-08 18:09:40 -0500924} // namespace clspv