blob: 7eb8339a47c87da0dac49e6604656174aca23200 [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"
Diego Novilloa4c44fa2019-04-11 10:56:15 -040040#include "Passes.h"
alan-bakerfec0a472018-11-08 18:09:40 -050041
alan-bakerf5e5f692018-11-27 08:33:24 -050042#include <cassert>
alan-bakerfec0a472018-11-08 18:09:40 -050043#include <numeric>
alan-bakerf5e5f692018-11-27 08:33:24 -050044#include <sstream>
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040045#include <string>
alan-bakerfec0a472018-11-08 18:09:40 -050046
47using namespace clang;
48
49namespace {
50// This registration must be located in the same file as the execution of the
51// action.
52static FrontendPluginRegistry::Add<clspv::ExtraValidationASTAction>
53 X("extra-validation",
54 "Perform extra validation on OpenCL C when targeting Vulkan");
55
56static llvm::cl::opt<bool> cl_single_precision_constants(
57 "cl-single-precision-constant", llvm::cl::init(false),
58 llvm::cl::desc("Treat double precision floating-point constant as single "
59 "precision constant."));
60
61static llvm::cl::opt<bool> cl_denorms_are_zero(
62 "cl-denorms-are-zero", llvm::cl::init(false),
63 llvm::cl::desc("If specified, denormalized floating point numbers may be "
64 "flushed to zero."));
65
66static llvm::cl::opt<bool> cl_fp32_correctly_rounded_divide_sqrt(
67 "cl-fp32-correctly-rounded-divide-sqrt", llvm::cl::init(false),
68 llvm::cl::desc("Single precision floating-point divide (x/y and 1/x) and "
69 "sqrt used are correctly rounded."));
70
71static llvm::cl::opt<bool>
72 cl_opt_disable("cl-opt-disable", llvm::cl::init(false),
73 llvm::cl::desc("This option disables all optimizations. The "
74 "default is optimizations are enabled."));
75
76static llvm::cl::opt<bool> cl_mad_enable(
77 "cl-mad-enable", llvm::cl::init(false),
78 llvm::cl::desc("Allow a * b + c to be replaced by a mad. The mad computes "
79 "a * b + c with reduced accuracy."));
80
81static llvm::cl::opt<bool> cl_no_signed_zeros(
82 "cl-no-signed-zeros", llvm::cl::init(false),
83 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
84 "ignore the signedness of zero."));
85
86static llvm::cl::opt<bool> cl_unsafe_math_optimizations(
87 "cl-unsafe-math-optimizations", llvm::cl::init(false),
88 llvm::cl::desc("Allow optimizations for floating-point arithmetic that (a) "
89 "assume that arguments and results are valid, (b) may "
90 "violate IEEE 754 standard and (c) may violate the OpenCL "
91 "numerical compliance requirements. This option includes "
92 "the -cl-no-signed-zeros and -cl-mad-enable options."));
93
94static llvm::cl::opt<bool> cl_finite_math_only(
95 "cl-finite-math-only", llvm::cl::init(false),
96 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
97 "assume that arguments and results are not NaNs or INFs."));
98
99static llvm::cl::opt<bool> cl_fast_relaxed_math(
100 "cl-fast-relaxed-math", llvm::cl::init(false),
101 llvm::cl::desc("This option causes the preprocessor macro "
102 "__FAST_RELAXED_MATH__ to be defined. Sets the optimization "
103 "options -cl-finite-math-only and "
104 "-cl-unsafe-math-optimizations."));
105
106static llvm::cl::list<std::string>
107 Includes(llvm::cl::Prefix, "I",
108 llvm::cl::desc("Add a directory to the list of directories "
109 "to be searched for header files."),
110 llvm::cl::ZeroOrMore, llvm::cl::value_desc("include path"));
111
112static llvm::cl::list<std::string>
113 Defines(llvm::cl::Prefix, "D",
114 llvm::cl::desc("Define a #define directive."), llvm::cl::ZeroOrMore,
115 llvm::cl::value_desc("define"));
116
117static llvm::cl::opt<std::string>
118 InputFilename(llvm::cl::Positional, llvm::cl::desc("<input .cl file>"),
119 llvm::cl::init("-"));
120
121static llvm::cl::opt<std::string>
122 OutputFilename("o", llvm::cl::desc("Override output filename"),
123 llvm::cl::value_desc("filename"));
124
125static llvm::cl::opt<std::string>
126 DescriptorMapFilename("descriptormap",
127 llvm::cl::desc("Output file for descriptor map"),
128 llvm::cl::value_desc("filename"));
129
130static llvm::cl::opt<char>
131 OptimizationLevel(llvm::cl::Prefix, "O", llvm::cl::init('2'),
132 llvm::cl::desc("Optimization level to use"),
133 llvm::cl::value_desc("level"));
134
alan-bakerfec0a472018-11-08 18:09:40 -0500135static llvm::cl::opt<std::string> OutputFormat(
136 "mfmt", llvm::cl::init(""),
137 llvm::cl::desc(
138 "Specify special output format. 'c' is as a C initializer list"),
139 llvm::cl::value_desc("format"));
140
141static llvm::cl::opt<std::string>
142 SamplerMap("samplermap", llvm::cl::desc("Literal sampler map"),
143 llvm::cl::value_desc("filename"));
144
145static llvm::cl::opt<bool> cluster_non_pointer_kernel_args(
146 "cluster-pod-kernel-args", llvm::cl::init(false),
147 llvm::cl::desc("Collect plain-old-data kernel arguments into a struct in "
148 "a single storage buffer, using a binding number after "
149 "other arguments. Use this to reduce storage buffer "
150 "descriptors."));
151
152static llvm::cl::opt<bool> verify("verify", llvm::cl::init(false),
153 llvm::cl::desc("Verify diagnostic outputs"));
154
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100155static llvm::cl::opt<bool>
156 IgnoreWarnings("w", llvm::cl::init(false),
157 llvm::cl::desc("Disable all warnings"));
158
159static llvm::cl::opt<bool>
160 WarningsAsErrors("Werror", llvm::cl::init(false),
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400161 llvm::cl::desc("Turn warnings into errors"));
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100162
alan-bakerfec0a472018-11-08 18:09:40 -0500163// Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
164// if successful.
alan-bakerf5e5f692018-11-27 08:33:24 -0500165int ParseSamplerMap(const std::string &sampler_map,
166 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
167 *SamplerMapEntries) {
168 std::unique_ptr<llvm::MemoryBuffer> samplerMapBuffer(nullptr);
169 if (!sampler_map.empty()) {
170 // Parse the sampler map from the provided string.
171 samplerMapBuffer = llvm::MemoryBuffer::getMemBuffer(sampler_map);
172
173 if (!SamplerMap.empty()) {
174 llvm::outs() << "Warning: -samplermap is ignored when the sampler map is "
175 "provided through a string.\n";
176 }
177 } else if (!SamplerMap.empty()) {
178 // Parse the sampler map from the option provided file.
alan-bakerfec0a472018-11-08 18:09:40 -0500179 auto errorOrSamplerMapFile =
180 llvm::MemoryBuffer::getFile(SamplerMap.getValue());
181
182 // If there was an error in getting the sampler map file.
183 if (!errorOrSamplerMapFile) {
184 llvm::errs() << "Error: " << errorOrSamplerMapFile.getError().message()
185 << " '" << SamplerMap.getValue() << "'\n";
186 return -1;
187 }
188
alan-bakerf5e5f692018-11-27 08:33:24 -0500189 samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
alan-bakerfec0a472018-11-08 18:09:40 -0500190 if (0 == samplerMapBuffer->getBufferSize()) {
191 llvm::errs() << "Error: Sampler map was an empty file!\n";
192 return -1;
193 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500194 }
alan-bakerfec0a472018-11-08 18:09:40 -0500195
alan-bakerf5e5f692018-11-27 08:33:24 -0500196 // No sampler map to parse.
197 if (!samplerMapBuffer || 0 == samplerMapBuffer->getBufferSize())
198 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500199
alan-bakerf5e5f692018-11-27 08:33:24 -0500200 llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
alan-bakerfec0a472018-11-08 18:09:40 -0500201
alan-bakerf5e5f692018-11-27 08:33:24 -0500202 // We need to keep track of the beginning of the current entry.
203 const char *b = samplerMapBuffer->getBufferStart();
204 for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
205 // If we have a separator between declarations.
206 if ((*i == '|') || (*i == ',') || (i == e)) {
207 if (i == b) {
208 llvm::errs() << "Error: Sampler map contained an empty entry!\n";
209 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500210 }
211
alan-bakerf5e5f692018-11-27 08:33:24 -0500212 samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
alan-bakerfec0a472018-11-08 18:09:40 -0500213
alan-bakerf5e5f692018-11-27 08:33:24 -0500214 // And set b the next character after i.
215 b = i + 1;
216 }
alan-bakerfec0a472018-11-08 18:09:40 -0500217
alan-bakerf5e5f692018-11-27 08:33:24 -0500218 // If we have a separator between declarations within a single sampler.
219 if ((*i == ',') || (i == e)) {
220 enum NormalizedCoords {
221 CLK_NORMALIZED_COORDS_FALSE = 0x00,
222 CLK_NORMALIZED_COORDS_TRUE = 0x01,
223 CLK_NORMALIZED_COORDS_NOT_SET
224 } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
alan-bakerfec0a472018-11-08 18:09:40 -0500225
alan-bakerf5e5f692018-11-27 08:33:24 -0500226 enum AddressingModes {
227 CLK_ADDRESS_NONE = 0x00,
228 CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
229 CLK_ADDRESS_CLAMP = 0x04,
230 CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
231 CLK_ADDRESS_REPEAT = 0x06,
232 CLK_ADDRESS_NOT_SET
233 } AddressingMode = CLK_ADDRESS_NOT_SET;
234
235 enum FilterModes {
236 CLK_FILTER_NEAREST = 0x10,
237 CLK_FILTER_LINEAR = 0x20,
238 CLK_FILTER_NOT_SET
239 } FilterMode = CLK_FILTER_NOT_SET;
240
241 for (auto str : samplerStrings) {
242 if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
243 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
244 llvm::errs() << "Error: Sampler map normalized coordinates was "
245 "previously set!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500246 return -1;
247 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500248 NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
249 } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
250 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
251 llvm::errs() << "Error: Sampler map normalized coordinates was "
252 "previously set!\n";
253 return -1;
254 }
255 NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
256 } else if ("CLK_ADDRESS_NONE" == str) {
257 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
258 llvm::errs()
259 << "Error: Sampler map addressing mode was previously set!\n";
260 return -1;
261 }
262 AddressingMode = CLK_ADDRESS_NONE;
263 } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
264 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
265 llvm::errs()
266 << "Error: Sampler map addressing mode was previously set!\n";
267 return -1;
268 }
269 AddressingMode = CLK_ADDRESS_CLAMP_TO_EDGE;
270 } else if ("CLK_ADDRESS_CLAMP" == str) {
271 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
272 llvm::errs()
273 << "Error: Sampler map addressing mode was previously set!\n";
274 return -1;
275 }
276 AddressingMode = CLK_ADDRESS_CLAMP;
277 } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
278 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
279 llvm::errs()
280 << "Error: Sampler map addressing mode was previously set!\n";
281 return -1;
282 }
283 AddressingMode = CLK_ADDRESS_MIRRORED_REPEAT;
284 } else if ("CLK_ADDRESS_REPEAT" == str) {
285 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
286 llvm::errs()
287 << "Error: Sampler map addressing mode was previously set!\n";
288 return -1;
289 }
290 AddressingMode = CLK_ADDRESS_REPEAT;
291 } else if ("CLK_FILTER_NEAREST" == str) {
292 if (CLK_FILTER_NOT_SET != FilterMode) {
293 llvm::errs()
294 << "Error: Sampler map filtering mode was previously set!\n";
295 return -1;
296 }
297 FilterMode = CLK_FILTER_NEAREST;
298 } else if ("CLK_FILTER_LINEAR" == str) {
299 if (CLK_FILTER_NOT_SET != FilterMode) {
300 llvm::errs()
301 << "Error: Sampler map filtering mode was previously set!\n";
302 return -1;
303 }
304 FilterMode = CLK_FILTER_LINEAR;
305 } else {
306 llvm::errs() << "Error: Unknown sampler string '" << str
307 << "' found!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500308 return -1;
309 }
alan-bakerfec0a472018-11-08 18:09:40 -0500310 }
311
alan-bakerf5e5f692018-11-27 08:33:24 -0500312 if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
313 llvm::errs() << "Error: Sampler map entry did not contain normalized "
314 "coordinates entry!\n";
315 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500316 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500317
318 if (CLK_ADDRESS_NOT_SET == AddressingMode) {
319 llvm::errs() << "Error: Sampler map entry did not contain addressing "
320 "mode entry!\n";
321 return -1;
322 }
323
324 if (CLK_FILTER_NOT_SET == FilterMode) {
325 llvm::errs()
326 << "Error: Sampler map entry did not contain filer mode entry!\n";
327 return -1;
328 }
329
330 // Generate an equivalent expression in string form. Sort the
331 // strings to get a canonical ordering.
332 std::sort(samplerStrings.begin(), samplerStrings.end(),
333 std::less<StringRef>());
334 const auto samplerExpr = std::accumulate(
335 samplerStrings.begin(), samplerStrings.end(), std::string(),
336 [](std::string left, std::string right) {
337 return left + std::string(left.empty() ? "" : "|") + right;
338 });
339
340 // SamplerMapEntries->push_back(std::make_pair(
341 // NormalizedCoord | AddressingMode | FilterMode, samplerExpr));
342 SamplerMapEntries->emplace_back(
343 NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
344
345 // And reset the sampler strings for the next sampler in the map.
346 samplerStrings.clear();
347 }
348
349 // And lastly, if we are at the end of the file
350 if (i == e) {
351 break;
alan-bakerfec0a472018-11-08 18:09:40 -0500352 }
353 }
354
355 return 0;
356}
357
358// Sets |instance|'s options for compiling. Returns 0 if successful.
359int SetCompilerInstanceOptions(CompilerInstance &instance,
360 const llvm::StringRef &overiddenInputFilename,
361 const clang::FrontendInputFile &kernelFile,
alan-bakerf5e5f692018-11-27 08:33:24 -0500362 const std::string &program,
alan-bakerfec0a472018-11-08 18:09:40 -0500363 llvm::raw_string_ostream *diagnosticsStream) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500364 std::unique_ptr<llvm::MemoryBuffer> memory_buffer(nullptr);
365 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> errorOrInputFile(nullptr);
366 if (program.empty()) {
367 auto errorOrInputFile =
368 llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
alan-bakerfec0a472018-11-08 18:09:40 -0500369
alan-bakerf5e5f692018-11-27 08:33:24 -0500370 // If there was an error in getting the input file.
371 if (!errorOrInputFile) {
372 llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
373 << InputFilename.getValue() << "'\n";
374 return -1;
375 }
376 memory_buffer.reset(errorOrInputFile.get().release());
377 } else {
378 memory_buffer = llvm::MemoryBuffer::getMemBuffer(program.c_str(),
379 overiddenInputFilename);
alan-bakerfec0a472018-11-08 18:09:40 -0500380 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500381
alan-bakerfec0a472018-11-08 18:09:40 -0500382 if (verify) {
383 instance.getDiagnosticOpts().VerifyDiagnostics = true;
alan-bakerbccf62c2019-03-29 10:32:41 -0400384 instance.getDiagnosticOpts().VerifyPrefixes.push_back("expected");
alan-bakerfec0a472018-11-08 18:09:40 -0500385 }
386
Kévin Petit0fc88042019-04-09 23:25:02 +0100387 clang::LangStandard::Kind standard;
388 if (clspv::Option::CPlusPlus()) {
389 standard = clang::LangStandard::lang_openclcpp;
390 } else {
391 standard = clang::LangStandard::lang_opencl12;
392 }
alan-bakerfec0a472018-11-08 18:09:40 -0500393
394 // We are targeting OpenCL 1.2 only
395 instance.getLangOpts().OpenCLVersion = 120;
396
397 instance.getLangOpts().C99 = true;
398 instance.getLangOpts().RTTI = false;
399 instance.getLangOpts().RTTIData = false;
400 instance.getLangOpts().MathErrno = false;
401 instance.getLangOpts().Optimize = false;
402 instance.getLangOpts().NoBuiltin = true;
403 instance.getLangOpts().ModulesSearchAll = false;
404 instance.getLangOpts().SinglePrecisionConstants = true;
405 instance.getCodeGenOpts().StackRealignment = true;
406 instance.getCodeGenOpts().SimplifyLibCalls = false;
407 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
408 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100409 instance.getDiagnosticOpts().IgnoreWarnings = IgnoreWarnings;
alan-bakerfec0a472018-11-08 18:09:40 -0500410
411 instance.getLangOpts().SinglePrecisionConstants =
412 cl_single_precision_constants;
413 // cl_denorms_are_zero ignored for now!
414 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
415 instance.getCodeGenOpts().LessPreciseFPMAD =
416 cl_mad_enable || cl_unsafe_math_optimizations;
417 // cl_no_signed_zeros ignored for now!
418 instance.getCodeGenOpts().UnsafeFPMath =
419 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
420 instance.getLangOpts().FiniteMathOnly =
421 cl_finite_math_only || cl_fast_relaxed_math;
422 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
423
424 // Preprocessor options
425 instance.getPreprocessorOpts().addMacroDef("__IMAGE_SUPPORT__");
426 if (cl_fast_relaxed_math) {
427 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
428 }
429
430 for (auto define : Defines) {
431 instance.getPreprocessorOpts().addMacroDef(define);
432 }
433
434 // Header search options
435 for (auto include : Includes) {
436 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
437 false, false);
438 }
439
440 // We always compile on opt 0 so we preserve as much debug information about
441 // the source as possible. We'll run optimization later, once we've had a
442 // chance to view the unoptimal code first
443 instance.getCodeGenOpts().OptimizationLevel = 0;
444
445// Debug information is disabled temporarily to call instruction.
446#if 0
447 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
448#endif
449
450 // We use the 32-bit pointer-width SPIR triple
451 llvm::Triple triple("spir-unknown-unknown");
452
453 instance.getInvocation().setLangDefaults(
454 instance.getLangOpts(), clang::InputKind::OpenCL, triple,
455 instance.getPreprocessorOpts(), standard);
456
457 // Override the C99 inline semantics to accommodate for more OpenCL C
458 // programs in the wild.
459 instance.getLangOpts().GNUInline = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100460
461 // Set up diagnostics
alan-bakerfec0a472018-11-08 18:09:40 -0500462 instance.createDiagnostics(
463 new clang::TextDiagnosticPrinter(*diagnosticsStream,
464 &instance.getDiagnosticOpts()),
465 true);
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100466 instance.getDiagnostics().setWarningsAsErrors(WarningsAsErrors);
467 instance.getDiagnostics().setEnableAllWarnings(true);
alan-bakerfec0a472018-11-08 18:09:40 -0500468
469 instance.getTargetOpts().Triple = triple.str();
470
471 instance.getCodeGenOpts().MainFileName = overiddenInputFilename;
472 instance.getCodeGenOpts().PreserveVec3Type = true;
473 // Disable generation of lifetime intrinsic.
474 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
475 instance.getFrontendOpts().Inputs.push_back(kernelFile);
alan-bakerf5e5f692018-11-27 08:33:24 -0500476 // instance.getPreprocessorOpts().addRemappedFile(
477 // overiddenInputFilename, errorOrInputFile.get().release());
478 instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
479 memory_buffer.release());
alan-bakerfec0a472018-11-08 18:09:40 -0500480
481 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
482 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
483 const char *dataCasted = reinterpret_cast<const char *>(data);
484 init(dataCasted, dataCasted + data_length, true);
485 }
486
487 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
488 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
489 }
490
491 virtual ~OpenCLBuiltinMemoryBuffer() override {}
492 };
493
494 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
495 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
496 opencl_builtins_header_size - 1));
497
498 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
499
500 // Add the VULKAN macro.
501 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
502
503 // Add the __OPENCL_VERSION__ macro.
504 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
505
506 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
507 instance.getDiagnostics(),
508 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
509
510 instance.createFileManager();
511 instance.createSourceManager(instance.getFileManager());
512
513#ifdef _MSC_VER
514 std::string includePrefix("include\\");
515#else
516 std::string includePrefix("include/");
517#endif
518
519 auto entry = instance.getFileManager().getVirtualFile(
520 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
521 0);
522
523 instance.getSourceManager().overrideFileContents(
524 entry, std::move(openCLBuiltinMemoryBuffer));
525
526 return 0;
527}
528
alan-bakerf5e5f692018-11-27 08:33:24 -0500529// Populates |pm| with necessary passes to optimize and legalize the IR.
530int PopulatePassManager(
531 llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
532 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
533 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
534 *SamplerMapEntries) {
alan-bakerfec0a472018-11-08 18:09:40 -0500535 llvm::PassManagerBuilder pmBuilder;
536
537 switch (OptimizationLevel) {
538 case '0':
alan-bakerf5e5f692018-11-27 08:33:24 -0500539 case '1':
540 case '2':
541 case '3':
542 case 's':
543 case 'z':
544 break;
545 default:
546 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
547 << " specified!\n";
548 return -1;
549 }
550
551 switch (OptimizationLevel) {
552 case '0':
alan-bakerfec0a472018-11-08 18:09:40 -0500553 pmBuilder.OptLevel = 0;
554 break;
555 case '1':
556 pmBuilder.OptLevel = 1;
557 break;
558 case '2':
559 pmBuilder.OptLevel = 2;
560 break;
561 case '3':
562 pmBuilder.OptLevel = 3;
563 break;
564 case 's':
565 pmBuilder.SizeLevel = 1;
566 break;
567 case 'z':
568 pmBuilder.SizeLevel = 2;
569 break;
570 default:
571 break;
572 }
573
574 pm->add(clspv::createZeroInitializeAllocasPass());
575 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
576
577 if (0 < pmBuilder.OptLevel) {
578 pm->add(clspv::createOpenCLInlinerPass());
579 }
580
581 pm->add(clspv::createUndoByvalPass());
582 pm->add(clspv::createUndoSRetPass());
583 if (cluster_non_pointer_kernel_args) {
584 pm->add(clspv::createClusterPodKernelArgumentsPass());
585 }
586 pm->add(clspv::createReplaceOpenCLBuiltinPass());
587
588 // We need to run mem2reg and inst combine early because our
589 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
590 // %1 = alloca i32 1
591 // store <something> %1
592 // %2 = bitcast float* %1
593 // %3 = load float %2
594 pm->add(llvm::createPromoteMemoryToRegisterPass());
595
596 // Hide loads from __constant address space away from instcombine.
597 // This prevents us from generating select between pointers-to-__constant.
598 // See https://github.com/google/clspv/issues/71
599 pm->add(clspv::createHideConstantLoadsPass());
600
601 pm->add(llvm::createInstructionCombiningPass());
602
603 if (clspv::Option::InlineEntryPoints()) {
604 pm->add(clspv::createInlineEntryPointsPass());
605 } else {
606 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
607 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
608 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
609 }
610
Kévin Petit0fc88042019-04-09 23:25:02 +0100611 if (clspv::Option::CPlusPlus()) {
612 pm->add(clspv::createDemangleKernelNamesPass());
613 }
614
alan-bakerfec0a472018-11-08 18:09:40 -0500615 if (0 == pmBuilder.OptLevel) {
616 // Mem2Reg pass should be run early because O0 level optimization leaves
617 // redundant alloca, load and store instructions from function arguments.
618 // clspv needs to remove them ahead of transformation.
619 pm->add(llvm::createPromoteMemoryToRegisterPass());
620
621 // SROA pass is run because it will fold structs/unions that are problematic
622 // on Vulkan SPIR-V away.
623 pm->add(llvm::createSROAPass());
624
625 // InstructionCombining pass folds bitcast and gep instructions which are
626 // not supported by Vulkan SPIR-V.
627 pm->add(llvm::createInstructionCombiningPass());
628 }
629
630 // Now we add any of the LLVM optimizations we wanted
631 pmBuilder.populateModulePassManager(*pm);
632
633 // 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
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400703 if (clspv::Option::CPlusPlus() && !clspv::Option::InlineEntryPoints()) {
Kévin Petit0fc88042019-04-09 23:25:02 +0100704 llvm::errs() << "cannot use -c++ without -inline-entry-points\n";
705 return -1;
706 }
707
Kévin Petitd5db2d22019-04-04 13:55:14 +0100708 return 0;
709}
710} // namespace
711
712namespace clspv {
713int Compile(const int argc, const char *const argv[]) {
714
715 if (auto error = ParseOptions(argc, argv))
716 return error;
717
alan-bakerfec0a472018-11-08 18:09:40 -0500718 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500719 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500720 return error;
721
722 // if no output file was provided, use a default
723 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
724
725 // If we are reading our input file from stdin.
726 if ("-" == InputFilename) {
727 // We need to overwrite the file name we use.
728 overiddenInputFilename = "stdin.cl";
729 }
730
731 clang::CompilerInstance instance;
732 clang::FrontendInputFile kernelFile(overiddenInputFilename,
733 clang::InputKind::OpenCL);
734 std::string log;
735 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500736 if (auto error = SetCompilerInstanceOptions(
737 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500738 return error;
739
740 // Parse.
741 llvm::LLVMContext context;
742 clang::EmitLLVMOnlyAction action(&context);
743
744 // Prepare the action for processing kernelFile
745 const bool success = action.BeginSourceFile(instance, kernelFile);
746 if (!success) {
747 return -1;
748 }
749
750 action.Execute();
751 action.EndSourceFile();
752
753 clang::DiagnosticConsumer *const consumer =
754 instance.getDiagnostics().getClient();
755 consumer->finish();
756
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100757 auto num_warnings = consumer->getNumWarnings();
alan-bakerfec0a472018-11-08 18:09:40 -0500758 auto num_errors = consumer->getNumErrors();
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100759 if ((num_errors > 0) || (num_warnings > 0)) {
760 llvm::errs() << log;
761 }
alan-bakerfec0a472018-11-08 18:09:40 -0500762 if (num_errors > 0) {
alan-bakerfec0a472018-11-08 18:09:40 -0500763 return -1;
764 }
765
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100766 // Don't run the passes or produce any output in verify mode.
767 // Clang doesn't always produce a valid module.
768 if (verify) {
769 return 0;
770 }
771
alan-bakerfec0a472018-11-08 18:09:40 -0500772 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
773 llvm::initializeCore(Registry);
774 llvm::initializeScalarOpts(Registry);
775
776 std::unique_ptr<llvm::Module> module(action.takeModule());
777
778 // Optimize.
779 // Create a memory buffer for temporarily writing the result.
780 SmallVector<char, 10000> binary;
781 llvm::raw_svector_ostream binaryStream(binary);
782 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500783 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500784 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400785 if (auto error = PopulatePassManager(
786 &pm, &binaryStream, &descriptor_map_entries, &SamplerMapEntries))
alan-bakerf5e5f692018-11-27 08:33:24 -0500787 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500788 pm.run(*module);
789
790 // Write outputs
791
792 // Write the descriptor map, if requested.
793 std::error_code error;
794 if (!DescriptorMapFilename.empty()) {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400795 llvm::raw_fd_ostream descriptor_map_out_fd(
796 DescriptorMapFilename, error, llvm::sys::fs::CD_CreateAlways,
797 llvm::sys::fs::FA_Write, llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500798 if (error) {
799 llvm::errs() << "Unable to open descriptor map file '"
800 << DescriptorMapFilename << "': " << error.message() << '\n';
801 return -1;
802 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500803 std::string descriptor_map_string;
804 std::ostringstream str(descriptor_map_string);
805 for (const auto &entry : descriptor_map_entries) {
806 str << entry << "\n";
807 }
808 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500809 descriptor_map_out_fd.close();
810 }
811
812 // Write the resulting binary.
813 // Wait until now to try writing the file so that we only write it on
814 // successful compilation.
815 if (OutputFilename.empty()) {
Kévin Petite4786902019-04-02 21:51:47 +0100816 if (OutputFormat == "c") {
alan-bakerfec0a472018-11-08 18:09:40 -0500817 OutputFilename = "a.spvinc";
818 } else {
819 OutputFilename = "a.spv";
820 }
821 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400822 llvm::raw_fd_ostream outStream(OutputFilename, error,
823 llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500824
825 if (error) {
826 llvm::errs() << "Unable to open output file '" << OutputFilename
827 << "': " << error.message() << '\n';
828 return -1;
829 }
830 outStream << binaryStream.str();
831
832 return 0;
833}
alan-bakerf5e5f692018-11-27 08:33:24 -0500834
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400835int CompileFromSourceString(
836 const std::string &program, const std::string &sampler_map,
837 const std::string &options, std::vector<uint32_t> *output_binary,
838 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500839
840 llvm::SmallVector<const char *, 20> argv;
841 llvm::BumpPtrAllocator A;
842 llvm::StringSaver Saver(A);
843 argv.push_back(Saver.save("clspv").data());
844 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
845 int argc = static_cast<int>(argv.size());
Kévin Petitd5db2d22019-04-04 13:55:14 +0100846
847 if (auto error = ParseOptions(argc, &argv[0]))
848 return error;
alan-bakerf5e5f692018-11-27 08:33:24 -0500849
850 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
851 if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
852 return error;
853
854 InputFilename = "source.cl";
855 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
856
857 clang::CompilerInstance instance;
858 clang::FrontendInputFile kernelFile(overiddenInputFilename,
859 clang::InputKind::OpenCL);
860 std::string log;
861 llvm::raw_string_ostream diagnosticsStream(log);
862 if (auto error =
863 SetCompilerInstanceOptions(instance, overiddenInputFilename,
864 kernelFile, program, &diagnosticsStream))
865 return error;
866
867 // Parse.
868 llvm::LLVMContext context;
869 clang::EmitLLVMOnlyAction action(&context);
870
871 // Prepare the action for processing kernelFile
872 const bool success = action.BeginSourceFile(instance, kernelFile);
873 if (!success) {
874 return -1;
875 }
876
877 action.Execute();
878 action.EndSourceFile();
879
880 clang::DiagnosticConsumer *const consumer =
881 instance.getDiagnostics().getClient();
882 consumer->finish();
883
884 auto num_errors = consumer->getNumErrors();
885 if (num_errors > 0) {
886 llvm::errs() << log << "\n";
887 return -1;
888 }
889
alan-bakerf5e5f692018-11-27 08:33:24 -0500890 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
891 llvm::initializeCore(Registry);
892 llvm::initializeScalarOpts(Registry);
893
894 std::unique_ptr<llvm::Module> module(action.takeModule());
895
896 // Optimize.
897 // Create a memory buffer for temporarily writing the result.
898 SmallVector<char, 10000> binary;
899 llvm::raw_svector_ostream binaryStream(binary);
900 std::string descriptor_map;
901 llvm::legacy::PassManager pm;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400902 if (auto error = PopulatePassManager(
903 &pm, &binaryStream, descriptor_map_entries, &SamplerMapEntries))
alan-bakerf5e5f692018-11-27 08:33:24 -0500904 return error;
905 pm.run(*module);
906
907 // Write outputs
908
909 // Write the descriptor map. This is required.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400910 assert(descriptor_map_entries &&
911 "Valid descriptor map container is required.");
alan-bakerf5e5f692018-11-27 08:33:24 -0500912 if (!DescriptorMapFilename.empty()) {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400913 llvm::errs() << "Warning: -descriptormap is ignored descriptor map "
914 "container is provided.\n";
alan-bakerf5e5f692018-11-27 08:33:24 -0500915 }
916
917 // Write the resulting binary.
918 // Wait until now to try writing the file so that we only write it on
919 // successful compilation.
920 assert(output_binary && "Valid binary container is required.");
921 if (!OutputFilename.empty()) {
922 llvm::outs()
923 << "Warning: -o is ignored when binary container is provided.\n";
924 }
925 output_binary->resize(binary.size() / 4);
926 memcpy(output_binary->data(), binary.data(), binary.size());
927
928 return 0;
929}
alan-bakerfec0a472018-11-08 18:09:40 -0500930} // namespace clspv