blob: 2ce13cdce31086f901005acea8ab3f938c7b592c [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}
alan-bakerfec0a472018-11-08 18:09:40 -0500673
Kévin Petitd5db2d22019-04-04 13:55:14 +0100674int ParseOptions(const int argc, const char *const argv[]) {
alan-bakerfec0a472018-11-08 18:09:40 -0500675 // We need to change how one of the called passes works by spoofing
676 // ParseCommandLineOptions with the specific option.
677 const int llvmArgc = 2;
678 const char *llvmArgv[llvmArgc] = {
alan-bakerf5e5f692018-11-27 08:33:24 -0500679 argv[0],
680 "-simplifycfg-sink-common=false",
alan-bakerfec0a472018-11-08 18:09:40 -0500681 };
682
Kévin Petitd5db2d22019-04-04 13:55:14 +0100683 llvm::cl::ResetAllOptionOccurrences();
alan-bakerfec0a472018-11-08 18:09:40 -0500684 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
alan-bakerfec0a472018-11-08 18:09:40 -0500685 llvm::cl::ParseCommandLineOptions(argc, argv);
686
Kévin Petitd5db2d22019-04-04 13:55:14 +0100687 if (clspv::Option::ConstantArgsInUniformBuffer() &&
688 !clspv::Option::InlineEntryPoints()) {
689 llvm::errs() << "clspv restriction: -constant-args-ubo requires "
690 "-inline-entry-points\n";
691 return -1;
692 }
693
694 return 0;
695}
696} // namespace
697
698namespace clspv {
699int Compile(const int argc, const char *const argv[]) {
700
701 if (auto error = ParseOptions(argc, argv))
702 return error;
703
alan-bakerfec0a472018-11-08 18:09:40 -0500704 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500705 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500706 return error;
707
708 // if no output file was provided, use a default
709 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
710
711 // If we are reading our input file from stdin.
712 if ("-" == InputFilename) {
713 // We need to overwrite the file name we use.
714 overiddenInputFilename = "stdin.cl";
715 }
716
717 clang::CompilerInstance instance;
718 clang::FrontendInputFile kernelFile(overiddenInputFilename,
719 clang::InputKind::OpenCL);
720 std::string log;
721 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500722 if (auto error = SetCompilerInstanceOptions(
723 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500724 return error;
725
726 // Parse.
727 llvm::LLVMContext context;
728 clang::EmitLLVMOnlyAction action(&context);
729
730 // Prepare the action for processing kernelFile
731 const bool success = action.BeginSourceFile(instance, kernelFile);
732 if (!success) {
733 return -1;
734 }
735
736 action.Execute();
737 action.EndSourceFile();
738
739 clang::DiagnosticConsumer *const consumer =
740 instance.getDiagnostics().getClient();
741 consumer->finish();
742
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100743 auto num_warnings = consumer->getNumWarnings();
alan-bakerfec0a472018-11-08 18:09:40 -0500744 auto num_errors = consumer->getNumErrors();
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100745 if ((num_errors > 0) || (num_warnings > 0)) {
746 llvm::errs() << log;
747 }
alan-bakerfec0a472018-11-08 18:09:40 -0500748 if (num_errors > 0) {
alan-bakerfec0a472018-11-08 18:09:40 -0500749 return -1;
750 }
751
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100752 // Don't run the passes or produce any output in verify mode.
753 // Clang doesn't always produce a valid module.
754 if (verify) {
755 return 0;
756 }
757
alan-bakerfec0a472018-11-08 18:09:40 -0500758 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
759 llvm::initializeCore(Registry);
760 llvm::initializeScalarOpts(Registry);
761
762 std::unique_ptr<llvm::Module> module(action.takeModule());
763
764 // Optimize.
765 // Create a memory buffer for temporarily writing the result.
766 SmallVector<char, 10000> binary;
767 llvm::raw_svector_ostream binaryStream(binary);
768 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500769 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500770 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
771 if (auto error =
772 PopulatePassManager(&pm, &binaryStream,
773 &descriptor_map_entries, &SamplerMapEntries))
774 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500775 pm.run(*module);
776
777 // Write outputs
778
779 // Write the descriptor map, if requested.
780 std::error_code error;
781 if (!DescriptorMapFilename.empty()) {
alan-bakerfec0a472018-11-08 18:09:40 -0500782 llvm::raw_fd_ostream descriptor_map_out_fd(DescriptorMapFilename, error,
alan-bakerbccf62c2019-03-29 10:32:41 -0400783 llvm::sys::fs::CD_CreateAlways,
784 llvm::sys::fs::FA_Write,
785 llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500786 if (error) {
787 llvm::errs() << "Unable to open descriptor map file '"
788 << DescriptorMapFilename << "': " << error.message() << '\n';
789 return -1;
790 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500791 std::string descriptor_map_string;
792 std::ostringstream str(descriptor_map_string);
793 for (const auto &entry : descriptor_map_entries) {
794 str << entry << "\n";
795 }
796 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500797 descriptor_map_out_fd.close();
798 }
799
800 // Write the resulting binary.
801 // Wait until now to try writing the file so that we only write it on
802 // successful compilation.
803 if (OutputFilename.empty()) {
Kévin Petite4786902019-04-02 21:51:47 +0100804 if (OutputFormat == "c") {
alan-bakerfec0a472018-11-08 18:09:40 -0500805 OutputFilename = "a.spvinc";
806 } else {
807 OutputFilename = "a.spv";
808 }
809 }
alan-bakerbccf62c2019-03-29 10:32:41 -0400810 llvm::raw_fd_ostream outStream(OutputFilename, error, llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500811
812 if (error) {
813 llvm::errs() << "Unable to open output file '" << OutputFilename
814 << "': " << error.message() << '\n';
815 return -1;
816 }
817 outStream << binaryStream.str();
818
819 return 0;
820}
alan-bakerf5e5f692018-11-27 08:33:24 -0500821
822int CompileFromSourceString(const std::string &program,
823 const std::string &sampler_map,
824 const std::string &options,
825 std::vector<uint32_t> *output_binary,
826 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500827
828 llvm::SmallVector<const char *, 20> argv;
829 llvm::BumpPtrAllocator A;
830 llvm::StringSaver Saver(A);
831 argv.push_back(Saver.save("clspv").data());
832 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
833 int argc = static_cast<int>(argv.size());
Kévin Petitd5db2d22019-04-04 13:55:14 +0100834
835 if (auto error = ParseOptions(argc, &argv[0]))
836 return error;
alan-bakerf5e5f692018-11-27 08:33:24 -0500837
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
alan-bakerf5e5f692018-11-27 08:33:24 -0500878 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
879 llvm::initializeCore(Registry);
880 llvm::initializeScalarOpts(Registry);
881
882 std::unique_ptr<llvm::Module> module(action.takeModule());
883
884 // Optimize.
885 // Create a memory buffer for temporarily writing the result.
886 SmallVector<char, 10000> binary;
887 llvm::raw_svector_ostream binaryStream(binary);
888 std::string descriptor_map;
889 llvm::legacy::PassManager pm;
890 if (auto error =
891 PopulatePassManager(&pm, &binaryStream,
892 descriptor_map_entries, &SamplerMapEntries))
893 return error;
894 pm.run(*module);
895
896 // Write outputs
897
898 // Write the descriptor map. This is required.
899 assert(descriptor_map_entries && "Valid descriptor map container is required.");
900 if (!DescriptorMapFilename.empty()) {
901 llvm::errs() << "Warning: -descriptormap is ignored descriptor map container is provided.\n";
902 }
903
904 // Write the resulting binary.
905 // Wait until now to try writing the file so that we only write it on
906 // successful compilation.
907 assert(output_binary && "Valid binary container is required.");
908 if (!OutputFilename.empty()) {
909 llvm::outs()
910 << "Warning: -o is ignored when binary container is provided.\n";
911 }
912 output_binary->resize(binary.size() / 4);
913 memcpy(output_binary->data(), binary.data(), binary.size());
914
915 return 0;
916}
alan-bakerfec0a472018-11-08 18:09:40 -0500917} // namespace clspv