blob: b386cb748072086ff58b4a1bea135f9b59705528 [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
Kévin Petit0fc88042019-04-09 23:25:02 +0100386 clang::LangStandard::Kind standard;
387 if (clspv::Option::CPlusPlus()) {
388 standard = clang::LangStandard::lang_openclcpp;
389 } else {
390 standard = clang::LangStandard::lang_opencl12;
391 }
alan-bakerfec0a472018-11-08 18:09:40 -0500392
393 // We are targeting OpenCL 1.2 only
394 instance.getLangOpts().OpenCLVersion = 120;
395
396 instance.getLangOpts().C99 = true;
397 instance.getLangOpts().RTTI = false;
398 instance.getLangOpts().RTTIData = false;
399 instance.getLangOpts().MathErrno = false;
400 instance.getLangOpts().Optimize = false;
401 instance.getLangOpts().NoBuiltin = true;
402 instance.getLangOpts().ModulesSearchAll = false;
403 instance.getLangOpts().SinglePrecisionConstants = true;
404 instance.getCodeGenOpts().StackRealignment = true;
405 instance.getCodeGenOpts().SimplifyLibCalls = false;
406 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
407 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100408 instance.getDiagnosticOpts().IgnoreWarnings = IgnoreWarnings;
alan-bakerfec0a472018-11-08 18:09:40 -0500409
410 instance.getLangOpts().SinglePrecisionConstants =
411 cl_single_precision_constants;
412 // cl_denorms_are_zero ignored for now!
413 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
414 instance.getCodeGenOpts().LessPreciseFPMAD =
415 cl_mad_enable || cl_unsafe_math_optimizations;
416 // cl_no_signed_zeros ignored for now!
417 instance.getCodeGenOpts().UnsafeFPMath =
418 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
419 instance.getLangOpts().FiniteMathOnly =
420 cl_finite_math_only || cl_fast_relaxed_math;
421 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
422
423 // Preprocessor options
424 instance.getPreprocessorOpts().addMacroDef("__IMAGE_SUPPORT__");
425 if (cl_fast_relaxed_math) {
426 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
427 }
428
429 for (auto define : Defines) {
430 instance.getPreprocessorOpts().addMacroDef(define);
431 }
432
433 // Header search options
434 for (auto include : Includes) {
435 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
436 false, false);
437 }
438
439 // We always compile on opt 0 so we preserve as much debug information about
440 // the source as possible. We'll run optimization later, once we've had a
441 // chance to view the unoptimal code first
442 instance.getCodeGenOpts().OptimizationLevel = 0;
443
444// Debug information is disabled temporarily to call instruction.
445#if 0
446 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
447#endif
448
449 // We use the 32-bit pointer-width SPIR triple
450 llvm::Triple triple("spir-unknown-unknown");
451
452 instance.getInvocation().setLangDefaults(
453 instance.getLangOpts(), clang::InputKind::OpenCL, triple,
454 instance.getPreprocessorOpts(), standard);
455
456 // Override the C99 inline semantics to accommodate for more OpenCL C
457 // programs in the wild.
458 instance.getLangOpts().GNUInline = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100459
460 // Set up diagnostics
alan-bakerfec0a472018-11-08 18:09:40 -0500461 instance.createDiagnostics(
462 new clang::TextDiagnosticPrinter(*diagnosticsStream,
463 &instance.getDiagnosticOpts()),
464 true);
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100465 instance.getDiagnostics().setWarningsAsErrors(WarningsAsErrors);
466 instance.getDiagnostics().setEnableAllWarnings(true);
alan-bakerfec0a472018-11-08 18:09:40 -0500467
468 instance.getTargetOpts().Triple = triple.str();
469
470 instance.getCodeGenOpts().MainFileName = overiddenInputFilename;
471 instance.getCodeGenOpts().PreserveVec3Type = true;
472 // Disable generation of lifetime intrinsic.
473 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
474 instance.getFrontendOpts().Inputs.push_back(kernelFile);
alan-bakerf5e5f692018-11-27 08:33:24 -0500475 // instance.getPreprocessorOpts().addRemappedFile(
476 // overiddenInputFilename, errorOrInputFile.get().release());
477 instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
478 memory_buffer.release());
alan-bakerfec0a472018-11-08 18:09:40 -0500479
480 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
481 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
482 const char *dataCasted = reinterpret_cast<const char *>(data);
483 init(dataCasted, dataCasted + data_length, true);
484 }
485
486 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
487 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
488 }
489
490 virtual ~OpenCLBuiltinMemoryBuffer() override {}
491 };
492
493 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
494 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
495 opencl_builtins_header_size - 1));
496
497 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
498
499 // Add the VULKAN macro.
500 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
501
502 // Add the __OPENCL_VERSION__ macro.
503 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
504
505 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
506 instance.getDiagnostics(),
507 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
508
509 instance.createFileManager();
510 instance.createSourceManager(instance.getFileManager());
511
512#ifdef _MSC_VER
513 std::string includePrefix("include\\");
514#else
515 std::string includePrefix("include/");
516#endif
517
518 auto entry = instance.getFileManager().getVirtualFile(
519 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
520 0);
521
522 instance.getSourceManager().overrideFileContents(
523 entry, std::move(openCLBuiltinMemoryBuffer));
524
525 return 0;
526}
527
alan-bakerf5e5f692018-11-27 08:33:24 -0500528// Populates |pm| with necessary passes to optimize and legalize the IR.
529int PopulatePassManager(
530 llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
531 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
532 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
533 *SamplerMapEntries) {
alan-bakerfec0a472018-11-08 18:09:40 -0500534 llvm::PassManagerBuilder pmBuilder;
535
536 switch (OptimizationLevel) {
537 case '0':
alan-bakerf5e5f692018-11-27 08:33:24 -0500538 case '1':
539 case '2':
540 case '3':
541 case 's':
542 case 'z':
543 break;
544 default:
545 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
546 << " specified!\n";
547 return -1;
548 }
549
550 switch (OptimizationLevel) {
551 case '0':
alan-bakerfec0a472018-11-08 18:09:40 -0500552 pmBuilder.OptLevel = 0;
553 break;
554 case '1':
555 pmBuilder.OptLevel = 1;
556 break;
557 case '2':
558 pmBuilder.OptLevel = 2;
559 break;
560 case '3':
561 pmBuilder.OptLevel = 3;
562 break;
563 case 's':
564 pmBuilder.SizeLevel = 1;
565 break;
566 case 'z':
567 pmBuilder.SizeLevel = 2;
568 break;
569 default:
570 break;
571 }
572
573 pm->add(clspv::createZeroInitializeAllocasPass());
574 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
575
576 if (0 < pmBuilder.OptLevel) {
577 pm->add(clspv::createOpenCLInlinerPass());
578 }
579
580 pm->add(clspv::createUndoByvalPass());
581 pm->add(clspv::createUndoSRetPass());
582 if (cluster_non_pointer_kernel_args) {
583 pm->add(clspv::createClusterPodKernelArgumentsPass());
584 }
585 pm->add(clspv::createReplaceOpenCLBuiltinPass());
586
587 // We need to run mem2reg and inst combine early because our
588 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
589 // %1 = alloca i32 1
590 // store <something> %1
591 // %2 = bitcast float* %1
592 // %3 = load float %2
593 pm->add(llvm::createPromoteMemoryToRegisterPass());
594
595 // Hide loads from __constant address space away from instcombine.
596 // This prevents us from generating select between pointers-to-__constant.
597 // See https://github.com/google/clspv/issues/71
598 pm->add(clspv::createHideConstantLoadsPass());
599
600 pm->add(llvm::createInstructionCombiningPass());
601
602 if (clspv::Option::InlineEntryPoints()) {
603 pm->add(clspv::createInlineEntryPointsPass());
604 } else {
605 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
606 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
607 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
608 }
609
Kévin Petit0fc88042019-04-09 23:25:02 +0100610 if (clspv::Option::CPlusPlus()) {
611 pm->add(clspv::createDemangleKernelNamesPass());
612 }
613
alan-bakerfec0a472018-11-08 18:09:40 -0500614 if (0 == pmBuilder.OptLevel) {
615 // Mem2Reg pass should be run early because O0 level optimization leaves
616 // redundant alloca, load and store instructions from function arguments.
617 // clspv needs to remove them ahead of transformation.
618 pm->add(llvm::createPromoteMemoryToRegisterPass());
619
620 // SROA pass is run because it will fold structs/unions that are problematic
621 // on Vulkan SPIR-V away.
622 pm->add(llvm::createSROAPass());
623
624 // InstructionCombining pass folds bitcast and gep instructions which are
625 // not supported by Vulkan SPIR-V.
626 pm->add(llvm::createInstructionCombiningPass());
627 }
628
629 // Now we add any of the LLVM optimizations we wanted
630 pmBuilder.populateModulePassManager(*pm);
631
Alan Bakerea88c712018-12-06 11:40:49 -0500632
alan-bakerfec0a472018-11-08 18:09:40 -0500633 // Unhide loads from __constant address space. Undoes the action of
634 // HideConstantLoadsPass.
635 pm->add(clspv::createUnhideConstantLoadsPass());
636
637 pm->add(clspv::createFunctionInternalizerPass());
638 pm->add(clspv::createReplaceLLVMIntrinsicsPass());
639 pm->add(clspv::createUndoBoolPass());
640 pm->add(clspv::createUndoTruncatedSwitchConditionPass());
641 pm->add(llvm::createStructurizeCFGPass(false));
alan-baker3fa76d92018-11-12 14:54:40 -0500642 // Must be run after structurize cfg.
alan-bakerfec0a472018-11-08 18:09:40 -0500643 pm->add(clspv::createReorderBasicBlocksPass());
644 pm->add(clspv::createUndoGetElementPtrConstantExprPass());
645 pm->add(clspv::createSplatArgPass());
646 pm->add(clspv::createSimplifyPointerBitcastPass());
647 pm->add(clspv::createReplacePointerBitcastPass());
648
649 pm->add(clspv::createUndoTranslateSamplerFoldPass());
650
651 if (clspv::Option::ModuleConstantsInStorageBuffer()) {
652 pm->add(clspv::createClusterModuleScopeConstantVars());
653 }
654
655 pm->add(clspv::createShareModuleScopeVariablesPass());
alan-bakere9308012019-03-15 10:25:13 -0400656 // This should be run after LLVM and OpenCL intrinsics are replaced.
alan-bakerfec0a472018-11-08 18:09:40 -0500657 pm->add(clspv::createAllocateDescriptorsPass(*SamplerMapEntries));
658 pm->add(llvm::createVerifierPass());
659 pm->add(clspv::createDirectResourceAccessPass());
660 // Replacing pointer bitcasts can leave some trivial GEPs
661 // that are easy to remove. Also replace GEPs of GEPS
662 // left by replacing indirect buffer accesses.
663 pm->add(clspv::createSimplifyPointerBitcastPass());
alan-baker4217b322019-03-06 08:56:12 -0500664 // Run after DRA to clean up parameters and help reduce the need for variable
665 // pointers.
666 pm->add(clspv::createRemoveUnusedArgumentsPass());
alan-bakerfec0a472018-11-08 18:09:40 -0500667
668 pm->add(clspv::createSplatSelectConditionPass());
669 pm->add(clspv::createSignedCompareFixupPass());
670 // This pass generates insertions that need to be rewritten.
671 pm->add(clspv::createScalarizePass());
672 pm->add(clspv::createRewriteInsertsPass());
673 // This pass mucks with types to point where you shouldn't rely on DataLayout
674 // anymore so leave this right before SPIR-V generation.
675 pm->add(clspv::createUBOTypeTransformPass());
676 pm->add(clspv::createSPIRVProducerPass(
alan-bakerf5e5f692018-11-27 08:33:24 -0500677 *binaryStream, descriptor_map_entries, *SamplerMapEntries,
Kévin Petite4786902019-04-02 21:51:47 +0100678 false /* Output assembly */, OutputFormat == "c"));
alan-bakerf5e5f692018-11-27 08:33:24 -0500679
680 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500681}
alan-bakerfec0a472018-11-08 18:09:40 -0500682
Kévin Petitd5db2d22019-04-04 13:55:14 +0100683int ParseOptions(const int argc, const char *const argv[]) {
alan-bakerfec0a472018-11-08 18:09:40 -0500684 // We need to change how one of the called passes works by spoofing
685 // ParseCommandLineOptions with the specific option.
686 const int llvmArgc = 2;
687 const char *llvmArgv[llvmArgc] = {
alan-bakerf5e5f692018-11-27 08:33:24 -0500688 argv[0],
689 "-simplifycfg-sink-common=false",
alan-bakerfec0a472018-11-08 18:09:40 -0500690 };
691
Kévin Petitd5db2d22019-04-04 13:55:14 +0100692 llvm::cl::ResetAllOptionOccurrences();
alan-bakerfec0a472018-11-08 18:09:40 -0500693 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
alan-bakerfec0a472018-11-08 18:09:40 -0500694 llvm::cl::ParseCommandLineOptions(argc, argv);
695
Kévin Petitd5db2d22019-04-04 13:55:14 +0100696 if (clspv::Option::ConstantArgsInUniformBuffer() &&
697 !clspv::Option::InlineEntryPoints()) {
698 llvm::errs() << "clspv restriction: -constant-args-ubo requires "
699 "-inline-entry-points\n";
700 return -1;
701 }
702
Kévin Petit0fc88042019-04-09 23:25:02 +0100703 if (clspv::Option::CPlusPlus() &&
704 !clspv::Option::InlineEntryPoints()) {
705 llvm::errs() << "cannot use -c++ without -inline-entry-points\n";
706 return -1;
707 }
708
Kévin Petitd5db2d22019-04-04 13:55:14 +0100709 return 0;
710}
711} // namespace
712
713namespace clspv {
714int Compile(const int argc, const char *const argv[]) {
715
716 if (auto error = ParseOptions(argc, argv))
717 return error;
718
alan-bakerfec0a472018-11-08 18:09:40 -0500719 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500720 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500721 return error;
722
723 // if no output file was provided, use a default
724 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
725
726 // If we are reading our input file from stdin.
727 if ("-" == InputFilename) {
728 // We need to overwrite the file name we use.
729 overiddenInputFilename = "stdin.cl";
730 }
731
732 clang::CompilerInstance instance;
733 clang::FrontendInputFile kernelFile(overiddenInputFilename,
734 clang::InputKind::OpenCL);
735 std::string log;
736 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500737 if (auto error = SetCompilerInstanceOptions(
738 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500739 return error;
740
741 // Parse.
742 llvm::LLVMContext context;
743 clang::EmitLLVMOnlyAction action(&context);
744
745 // Prepare the action for processing kernelFile
746 const bool success = action.BeginSourceFile(instance, kernelFile);
747 if (!success) {
748 return -1;
749 }
750
751 action.Execute();
752 action.EndSourceFile();
753
754 clang::DiagnosticConsumer *const consumer =
755 instance.getDiagnostics().getClient();
756 consumer->finish();
757
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100758 auto num_warnings = consumer->getNumWarnings();
alan-bakerfec0a472018-11-08 18:09:40 -0500759 auto num_errors = consumer->getNumErrors();
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100760 if ((num_errors > 0) || (num_warnings > 0)) {
761 llvm::errs() << log;
762 }
alan-bakerfec0a472018-11-08 18:09:40 -0500763 if (num_errors > 0) {
alan-bakerfec0a472018-11-08 18:09:40 -0500764 return -1;
765 }
766
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100767 // Don't run the passes or produce any output in verify mode.
768 // Clang doesn't always produce a valid module.
769 if (verify) {
770 return 0;
771 }
772
alan-bakerfec0a472018-11-08 18:09:40 -0500773 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
774 llvm::initializeCore(Registry);
775 llvm::initializeScalarOpts(Registry);
776
777 std::unique_ptr<llvm::Module> module(action.takeModule());
778
779 // Optimize.
780 // Create a memory buffer for temporarily writing the result.
781 SmallVector<char, 10000> binary;
782 llvm::raw_svector_ostream binaryStream(binary);
783 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500784 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500785 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
786 if (auto error =
787 PopulatePassManager(&pm, &binaryStream,
788 &descriptor_map_entries, &SamplerMapEntries))
789 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500790 pm.run(*module);
791
792 // Write outputs
793
794 // Write the descriptor map, if requested.
795 std::error_code error;
796 if (!DescriptorMapFilename.empty()) {
alan-bakerfec0a472018-11-08 18:09:40 -0500797 llvm::raw_fd_ostream descriptor_map_out_fd(DescriptorMapFilename, error,
alan-bakerbccf62c2019-03-29 10:32:41 -0400798 llvm::sys::fs::CD_CreateAlways,
799 llvm::sys::fs::FA_Write,
800 llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500801 if (error) {
802 llvm::errs() << "Unable to open descriptor map file '"
803 << DescriptorMapFilename << "': " << error.message() << '\n';
804 return -1;
805 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500806 std::string descriptor_map_string;
807 std::ostringstream str(descriptor_map_string);
808 for (const auto &entry : descriptor_map_entries) {
809 str << entry << "\n";
810 }
811 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500812 descriptor_map_out_fd.close();
813 }
814
815 // Write the resulting binary.
816 // Wait until now to try writing the file so that we only write it on
817 // successful compilation.
818 if (OutputFilename.empty()) {
Kévin Petite4786902019-04-02 21:51:47 +0100819 if (OutputFormat == "c") {
alan-bakerfec0a472018-11-08 18:09:40 -0500820 OutputFilename = "a.spvinc";
821 } else {
822 OutputFilename = "a.spv";
823 }
824 }
alan-bakerbccf62c2019-03-29 10:32:41 -0400825 llvm::raw_fd_ostream outStream(OutputFilename, error, llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500826
827 if (error) {
828 llvm::errs() << "Unable to open output file '" << OutputFilename
829 << "': " << error.message() << '\n';
830 return -1;
831 }
832 outStream << binaryStream.str();
833
834 return 0;
835}
alan-bakerf5e5f692018-11-27 08:33:24 -0500836
837int CompileFromSourceString(const std::string &program,
838 const std::string &sampler_map,
839 const std::string &options,
840 std::vector<uint32_t> *output_binary,
841 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500842
843 llvm::SmallVector<const char *, 20> argv;
844 llvm::BumpPtrAllocator A;
845 llvm::StringSaver Saver(A);
846 argv.push_back(Saver.save("clspv").data());
847 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
848 int argc = static_cast<int>(argv.size());
Kévin Petitd5db2d22019-04-04 13:55:14 +0100849
850 if (auto error = ParseOptions(argc, &argv[0]))
851 return error;
alan-bakerf5e5f692018-11-27 08:33:24 -0500852
853 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
854 if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
855 return error;
856
857 InputFilename = "source.cl";
858 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
859
860 clang::CompilerInstance instance;
861 clang::FrontendInputFile kernelFile(overiddenInputFilename,
862 clang::InputKind::OpenCL);
863 std::string log;
864 llvm::raw_string_ostream diagnosticsStream(log);
865 if (auto error =
866 SetCompilerInstanceOptions(instance, overiddenInputFilename,
867 kernelFile, program, &diagnosticsStream))
868 return error;
869
870 // Parse.
871 llvm::LLVMContext context;
872 clang::EmitLLVMOnlyAction action(&context);
873
874 // Prepare the action for processing kernelFile
875 const bool success = action.BeginSourceFile(instance, kernelFile);
876 if (!success) {
877 return -1;
878 }
879
880 action.Execute();
881 action.EndSourceFile();
882
883 clang::DiagnosticConsumer *const consumer =
884 instance.getDiagnostics().getClient();
885 consumer->finish();
886
887 auto num_errors = consumer->getNumErrors();
888 if (num_errors > 0) {
889 llvm::errs() << log << "\n";
890 return -1;
891 }
892
alan-bakerf5e5f692018-11-27 08:33:24 -0500893 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
894 llvm::initializeCore(Registry);
895 llvm::initializeScalarOpts(Registry);
896
897 std::unique_ptr<llvm::Module> module(action.takeModule());
898
899 // Optimize.
900 // Create a memory buffer for temporarily writing the result.
901 SmallVector<char, 10000> binary;
902 llvm::raw_svector_ostream binaryStream(binary);
903 std::string descriptor_map;
904 llvm::legacy::PassManager pm;
905 if (auto error =
906 PopulatePassManager(&pm, &binaryStream,
907 descriptor_map_entries, &SamplerMapEntries))
908 return error;
909 pm.run(*module);
910
911 // Write outputs
912
913 // Write the descriptor map. This is required.
914 assert(descriptor_map_entries && "Valid descriptor map container is required.");
915 if (!DescriptorMapFilename.empty()) {
916 llvm::errs() << "Warning: -descriptormap is ignored descriptor map container is provided.\n";
917 }
918
919 // Write the resulting binary.
920 // Wait until now to try writing the file so that we only write it on
921 // successful compilation.
922 assert(output_binary && "Valid binary container is required.");
923 if (!OutputFilename.empty()) {
924 llvm::outs()
925 << "Warning: -o is ignored when binary container is provided.\n";
926 }
927 output_binary->resize(binary.size() / 4);
928 memcpy(output_binary->data(), binary.data(), binary.size());
929
930 return 0;
931}
alan-bakerfec0a472018-11-08 18:09:40 -0500932} // namespace clspv