blob: c222a1e505acd8329f42debe641fa5de94877de3 [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"
Diego Novillo89500852019-04-15 08:45:10 -040031#include "llvm/Support/ToolOutputFile.h"
alan-bakerfec0a472018-11-08 18:09:40 -050032#include "llvm/Support/raw_ostream.h"
33#include "llvm/Transforms/IPO/PassManagerBuilder.h"
34
alan-bakerf5e5f692018-11-27 08:33:24 -050035#include "clspv/DescriptorMap.h"
alan-bakerfec0a472018-11-08 18:09:40 -050036#include "clspv/Option.h"
37#include "clspv/Passes.h"
38#include "clspv/opencl_builtins_header.h"
39
40#include "FrontendPlugin.h"
Diego Novilloa4c44fa2019-04-11 10:56:15 -040041#include "Passes.h"
alan-bakerfec0a472018-11-08 18:09:40 -050042
alan-bakerf5e5f692018-11-27 08:33:24 -050043#include <cassert>
alan-bakerfec0a472018-11-08 18:09:40 -050044#include <numeric>
alan-bakerf5e5f692018-11-27 08:33:24 -050045#include <sstream>
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040046#include <string>
alan-bakerfec0a472018-11-08 18:09:40 -050047
48using namespace clang;
49
50namespace {
51// This registration must be located in the same file as the execution of the
52// action.
53static FrontendPluginRegistry::Add<clspv::ExtraValidationASTAction>
54 X("extra-validation",
55 "Perform extra validation on OpenCL C when targeting Vulkan");
56
57static llvm::cl::opt<bool> cl_single_precision_constants(
58 "cl-single-precision-constant", llvm::cl::init(false),
59 llvm::cl::desc("Treat double precision floating-point constant as single "
60 "precision constant."));
61
62static llvm::cl::opt<bool> cl_denorms_are_zero(
63 "cl-denorms-are-zero", llvm::cl::init(false),
64 llvm::cl::desc("If specified, denormalized floating point numbers may be "
65 "flushed to zero."));
66
67static llvm::cl::opt<bool> cl_fp32_correctly_rounded_divide_sqrt(
68 "cl-fp32-correctly-rounded-divide-sqrt", llvm::cl::init(false),
69 llvm::cl::desc("Single precision floating-point divide (x/y and 1/x) and "
70 "sqrt used are correctly rounded."));
71
72static llvm::cl::opt<bool>
73 cl_opt_disable("cl-opt-disable", llvm::cl::init(false),
74 llvm::cl::desc("This option disables all optimizations. The "
75 "default is optimizations are enabled."));
76
77static llvm::cl::opt<bool> cl_mad_enable(
78 "cl-mad-enable", llvm::cl::init(false),
79 llvm::cl::desc("Allow a * b + c to be replaced by a mad. The mad computes "
80 "a * b + c with reduced accuracy."));
81
82static llvm::cl::opt<bool> cl_no_signed_zeros(
83 "cl-no-signed-zeros", llvm::cl::init(false),
84 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
85 "ignore the signedness of zero."));
86
87static llvm::cl::opt<bool> cl_unsafe_math_optimizations(
88 "cl-unsafe-math-optimizations", llvm::cl::init(false),
89 llvm::cl::desc("Allow optimizations for floating-point arithmetic that (a) "
90 "assume that arguments and results are valid, (b) may "
91 "violate IEEE 754 standard and (c) may violate the OpenCL "
92 "numerical compliance requirements. This option includes "
93 "the -cl-no-signed-zeros and -cl-mad-enable options."));
94
95static llvm::cl::opt<bool> cl_finite_math_only(
96 "cl-finite-math-only", llvm::cl::init(false),
97 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
98 "assume that arguments and results are not NaNs or INFs."));
99
100static llvm::cl::opt<bool> cl_fast_relaxed_math(
101 "cl-fast-relaxed-math", llvm::cl::init(false),
102 llvm::cl::desc("This option causes the preprocessor macro "
103 "__FAST_RELAXED_MATH__ to be defined. Sets the optimization "
104 "options -cl-finite-math-only and "
105 "-cl-unsafe-math-optimizations."));
106
107static llvm::cl::list<std::string>
108 Includes(llvm::cl::Prefix, "I",
109 llvm::cl::desc("Add a directory to the list of directories "
110 "to be searched for header files."),
111 llvm::cl::ZeroOrMore, llvm::cl::value_desc("include path"));
112
113static llvm::cl::list<std::string>
114 Defines(llvm::cl::Prefix, "D",
115 llvm::cl::desc("Define a #define directive."), llvm::cl::ZeroOrMore,
116 llvm::cl::value_desc("define"));
117
118static llvm::cl::opt<std::string>
119 InputFilename(llvm::cl::Positional, llvm::cl::desc("<input .cl file>"),
120 llvm::cl::init("-"));
121
122static llvm::cl::opt<std::string>
123 OutputFilename("o", llvm::cl::desc("Override output filename"),
124 llvm::cl::value_desc("filename"));
125
126static llvm::cl::opt<std::string>
127 DescriptorMapFilename("descriptormap",
128 llvm::cl::desc("Output file for descriptor map"),
129 llvm::cl::value_desc("filename"));
130
131static llvm::cl::opt<char>
132 OptimizationLevel(llvm::cl::Prefix, "O", llvm::cl::init('2'),
133 llvm::cl::desc("Optimization level to use"),
134 llvm::cl::value_desc("level"));
135
alan-bakerfec0a472018-11-08 18:09:40 -0500136static llvm::cl::opt<std::string> OutputFormat(
137 "mfmt", llvm::cl::init(""),
138 llvm::cl::desc(
139 "Specify special output format. 'c' is as a C initializer list"),
140 llvm::cl::value_desc("format"));
141
142static llvm::cl::opt<std::string>
143 SamplerMap("samplermap", llvm::cl::desc("Literal sampler map"),
144 llvm::cl::value_desc("filename"));
145
146static llvm::cl::opt<bool> cluster_non_pointer_kernel_args(
147 "cluster-pod-kernel-args", llvm::cl::init(false),
148 llvm::cl::desc("Collect plain-old-data kernel arguments into a struct in "
149 "a single storage buffer, using a binding number after "
150 "other arguments. Use this to reduce storage buffer "
151 "descriptors."));
152
153static llvm::cl::opt<bool> verify("verify", llvm::cl::init(false),
154 llvm::cl::desc("Verify diagnostic outputs"));
155
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100156static llvm::cl::opt<bool>
157 IgnoreWarnings("w", llvm::cl::init(false),
158 llvm::cl::desc("Disable all warnings"));
159
160static llvm::cl::opt<bool>
161 WarningsAsErrors("Werror", llvm::cl::init(false),
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400162 llvm::cl::desc("Turn warnings into errors"));
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100163
Diego Novillo89500852019-04-15 08:45:10 -0400164static llvm::cl::opt<std::string> IROutputFile(
165 "emit-ir",
166 llvm::cl::desc(
167 "Emit LLVM IR to the given file after parsing and stop compilation."),
168 llvm::cl::value_desc("filename"));
169
alan-bakerfec0a472018-11-08 18:09:40 -0500170// Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
171// if successful.
alan-bakerf5e5f692018-11-27 08:33:24 -0500172int ParseSamplerMap(const std::string &sampler_map,
173 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
174 *SamplerMapEntries) {
175 std::unique_ptr<llvm::MemoryBuffer> samplerMapBuffer(nullptr);
176 if (!sampler_map.empty()) {
177 // Parse the sampler map from the provided string.
178 samplerMapBuffer = llvm::MemoryBuffer::getMemBuffer(sampler_map);
179
180 if (!SamplerMap.empty()) {
181 llvm::outs() << "Warning: -samplermap is ignored when the sampler map is "
182 "provided through a string.\n";
183 }
184 } else if (!SamplerMap.empty()) {
185 // Parse the sampler map from the option provided file.
alan-bakerfec0a472018-11-08 18:09:40 -0500186 auto errorOrSamplerMapFile =
187 llvm::MemoryBuffer::getFile(SamplerMap.getValue());
188
189 // If there was an error in getting the sampler map file.
190 if (!errorOrSamplerMapFile) {
191 llvm::errs() << "Error: " << errorOrSamplerMapFile.getError().message()
192 << " '" << SamplerMap.getValue() << "'\n";
193 return -1;
194 }
195
alan-bakerf5e5f692018-11-27 08:33:24 -0500196 samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
alan-bakerfec0a472018-11-08 18:09:40 -0500197 if (0 == samplerMapBuffer->getBufferSize()) {
198 llvm::errs() << "Error: Sampler map was an empty file!\n";
199 return -1;
200 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500201 }
alan-bakerfec0a472018-11-08 18:09:40 -0500202
alan-bakerf5e5f692018-11-27 08:33:24 -0500203 // No sampler map to parse.
204 if (!samplerMapBuffer || 0 == samplerMapBuffer->getBufferSize())
205 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500206
alan-bakerf5e5f692018-11-27 08:33:24 -0500207 llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
alan-bakerfec0a472018-11-08 18:09:40 -0500208
alan-bakerf5e5f692018-11-27 08:33:24 -0500209 // We need to keep track of the beginning of the current entry.
210 const char *b = samplerMapBuffer->getBufferStart();
211 for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
212 // If we have a separator between declarations.
213 if ((*i == '|') || (*i == ',') || (i == e)) {
214 if (i == b) {
215 llvm::errs() << "Error: Sampler map contained an empty entry!\n";
216 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500217 }
218
alan-bakerf5e5f692018-11-27 08:33:24 -0500219 samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
alan-bakerfec0a472018-11-08 18:09:40 -0500220
alan-bakerf5e5f692018-11-27 08:33:24 -0500221 // And set b the next character after i.
222 b = i + 1;
223 }
alan-bakerfec0a472018-11-08 18:09:40 -0500224
alan-bakerf5e5f692018-11-27 08:33:24 -0500225 // If we have a separator between declarations within a single sampler.
226 if ((*i == ',') || (i == e)) {
227 enum NormalizedCoords {
228 CLK_NORMALIZED_COORDS_FALSE = 0x00,
229 CLK_NORMALIZED_COORDS_TRUE = 0x01,
230 CLK_NORMALIZED_COORDS_NOT_SET
231 } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
alan-bakerfec0a472018-11-08 18:09:40 -0500232
alan-bakerf5e5f692018-11-27 08:33:24 -0500233 enum AddressingModes {
234 CLK_ADDRESS_NONE = 0x00,
235 CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
236 CLK_ADDRESS_CLAMP = 0x04,
237 CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
238 CLK_ADDRESS_REPEAT = 0x06,
239 CLK_ADDRESS_NOT_SET
240 } AddressingMode = CLK_ADDRESS_NOT_SET;
241
242 enum FilterModes {
243 CLK_FILTER_NEAREST = 0x10,
244 CLK_FILTER_LINEAR = 0x20,
245 CLK_FILTER_NOT_SET
246 } FilterMode = CLK_FILTER_NOT_SET;
247
248 for (auto str : samplerStrings) {
249 if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
250 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
251 llvm::errs() << "Error: Sampler map normalized coordinates was "
252 "previously set!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500253 return -1;
254 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500255 NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
256 } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
257 if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
258 llvm::errs() << "Error: Sampler map normalized coordinates was "
259 "previously set!\n";
260 return -1;
261 }
262 NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
263 } else if ("CLK_ADDRESS_NONE" == 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_NONE;
270 } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == 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_TO_EDGE;
277 } else if ("CLK_ADDRESS_CLAMP" == 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_CLAMP;
284 } else if ("CLK_ADDRESS_MIRRORED_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_MIRRORED_REPEAT;
291 } else if ("CLK_ADDRESS_REPEAT" == str) {
292 if (CLK_ADDRESS_NOT_SET != AddressingMode) {
293 llvm::errs()
294 << "Error: Sampler map addressing mode was previously set!\n";
295 return -1;
296 }
297 AddressingMode = CLK_ADDRESS_REPEAT;
298 } else if ("CLK_FILTER_NEAREST" == 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_NEAREST;
305 } else if ("CLK_FILTER_LINEAR" == str) {
306 if (CLK_FILTER_NOT_SET != FilterMode) {
307 llvm::errs()
308 << "Error: Sampler map filtering mode was previously set!\n";
309 return -1;
310 }
311 FilterMode = CLK_FILTER_LINEAR;
312 } else {
313 llvm::errs() << "Error: Unknown sampler string '" << str
314 << "' found!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500315 return -1;
316 }
alan-bakerfec0a472018-11-08 18:09:40 -0500317 }
318
alan-bakerf5e5f692018-11-27 08:33:24 -0500319 if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
320 llvm::errs() << "Error: Sampler map entry did not contain normalized "
321 "coordinates entry!\n";
322 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500323 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500324
325 if (CLK_ADDRESS_NOT_SET == AddressingMode) {
326 llvm::errs() << "Error: Sampler map entry did not contain addressing "
327 "mode entry!\n";
328 return -1;
329 }
330
331 if (CLK_FILTER_NOT_SET == FilterMode) {
332 llvm::errs()
333 << "Error: Sampler map entry did not contain filer mode entry!\n";
334 return -1;
335 }
336
337 // Generate an equivalent expression in string form. Sort the
338 // strings to get a canonical ordering.
339 std::sort(samplerStrings.begin(), samplerStrings.end(),
340 std::less<StringRef>());
341 const auto samplerExpr = std::accumulate(
342 samplerStrings.begin(), samplerStrings.end(), std::string(),
343 [](std::string left, std::string right) {
344 return left + std::string(left.empty() ? "" : "|") + right;
345 });
346
347 // SamplerMapEntries->push_back(std::make_pair(
348 // NormalizedCoord | AddressingMode | FilterMode, samplerExpr));
349 SamplerMapEntries->emplace_back(
350 NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
351
352 // And reset the sampler strings for the next sampler in the map.
353 samplerStrings.clear();
354 }
355
356 // And lastly, if we are at the end of the file
357 if (i == e) {
358 break;
alan-bakerfec0a472018-11-08 18:09:40 -0500359 }
360 }
361
362 return 0;
363}
364
365// Sets |instance|'s options for compiling. Returns 0 if successful.
366int SetCompilerInstanceOptions(CompilerInstance &instance,
367 const llvm::StringRef &overiddenInputFilename,
368 const clang::FrontendInputFile &kernelFile,
alan-bakerf5e5f692018-11-27 08:33:24 -0500369 const std::string &program,
alan-bakerfec0a472018-11-08 18:09:40 -0500370 llvm::raw_string_ostream *diagnosticsStream) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500371 std::unique_ptr<llvm::MemoryBuffer> memory_buffer(nullptr);
372 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> errorOrInputFile(nullptr);
373 if (program.empty()) {
374 auto errorOrInputFile =
375 llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
alan-bakerfec0a472018-11-08 18:09:40 -0500376
alan-bakerf5e5f692018-11-27 08:33:24 -0500377 // If there was an error in getting the input file.
378 if (!errorOrInputFile) {
379 llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
380 << InputFilename.getValue() << "'\n";
381 return -1;
382 }
383 memory_buffer.reset(errorOrInputFile.get().release());
384 } else {
385 memory_buffer = llvm::MemoryBuffer::getMemBuffer(program.c_str(),
386 overiddenInputFilename);
alan-bakerfec0a472018-11-08 18:09:40 -0500387 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500388
alan-bakerfec0a472018-11-08 18:09:40 -0500389 if (verify) {
390 instance.getDiagnosticOpts().VerifyDiagnostics = true;
alan-bakerbccf62c2019-03-29 10:32:41 -0400391 instance.getDiagnosticOpts().VerifyPrefixes.push_back("expected");
alan-bakerfec0a472018-11-08 18:09:40 -0500392 }
393
Kévin Petit0fc88042019-04-09 23:25:02 +0100394 clang::LangStandard::Kind standard;
395 if (clspv::Option::CPlusPlus()) {
396 standard = clang::LangStandard::lang_openclcpp;
397 } else {
398 standard = clang::LangStandard::lang_opencl12;
399 }
alan-bakerfec0a472018-11-08 18:09:40 -0500400
401 // We are targeting OpenCL 1.2 only
402 instance.getLangOpts().OpenCLVersion = 120;
403
404 instance.getLangOpts().C99 = true;
405 instance.getLangOpts().RTTI = false;
406 instance.getLangOpts().RTTIData = false;
407 instance.getLangOpts().MathErrno = false;
408 instance.getLangOpts().Optimize = false;
409 instance.getLangOpts().NoBuiltin = true;
410 instance.getLangOpts().ModulesSearchAll = false;
411 instance.getLangOpts().SinglePrecisionConstants = true;
412 instance.getCodeGenOpts().StackRealignment = true;
413 instance.getCodeGenOpts().SimplifyLibCalls = false;
414 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
415 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100416 instance.getDiagnosticOpts().IgnoreWarnings = IgnoreWarnings;
alan-bakerfec0a472018-11-08 18:09:40 -0500417
418 instance.getLangOpts().SinglePrecisionConstants =
419 cl_single_precision_constants;
420 // cl_denorms_are_zero ignored for now!
421 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
422 instance.getCodeGenOpts().LessPreciseFPMAD =
423 cl_mad_enable || cl_unsafe_math_optimizations;
424 // cl_no_signed_zeros ignored for now!
425 instance.getCodeGenOpts().UnsafeFPMath =
426 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
427 instance.getLangOpts().FiniteMathOnly =
428 cl_finite_math_only || cl_fast_relaxed_math;
429 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
430
431 // Preprocessor options
Kévin Petita624c0c2019-05-07 20:27:43 +0800432 if (!clspv::Option::ImageSupport()) {
433 instance.getPreprocessorOpts().addMacroUndef("__IMAGE_SUPPORT__");
434 }
alan-bakerfec0a472018-11-08 18:09:40 -0500435 if (cl_fast_relaxed_math) {
436 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
437 }
438
439 for (auto define : Defines) {
440 instance.getPreprocessorOpts().addMacroDef(define);
441 }
442
443 // Header search options
444 for (auto include : Includes) {
445 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
446 false, false);
447 }
448
449 // We always compile on opt 0 so we preserve as much debug information about
450 // the source as possible. We'll run optimization later, once we've had a
451 // chance to view the unoptimal code first
452 instance.getCodeGenOpts().OptimizationLevel = 0;
453
454// Debug information is disabled temporarily to call instruction.
455#if 0
456 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
457#endif
458
459 // We use the 32-bit pointer-width SPIR triple
460 llvm::Triple triple("spir-unknown-unknown");
461
462 instance.getInvocation().setLangDefaults(
463 instance.getLangOpts(), clang::InputKind::OpenCL, triple,
464 instance.getPreprocessorOpts(), standard);
465
466 // Override the C99 inline semantics to accommodate for more OpenCL C
467 // programs in the wild.
468 instance.getLangOpts().GNUInline = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100469
470 // Set up diagnostics
alan-bakerfec0a472018-11-08 18:09:40 -0500471 instance.createDiagnostics(
472 new clang::TextDiagnosticPrinter(*diagnosticsStream,
473 &instance.getDiagnosticOpts()),
474 true);
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100475 instance.getDiagnostics().setWarningsAsErrors(WarningsAsErrors);
476 instance.getDiagnostics().setEnableAllWarnings(true);
alan-bakerfec0a472018-11-08 18:09:40 -0500477
478 instance.getTargetOpts().Triple = triple.str();
479
480 instance.getCodeGenOpts().MainFileName = overiddenInputFilename;
481 instance.getCodeGenOpts().PreserveVec3Type = true;
482 // Disable generation of lifetime intrinsic.
483 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
484 instance.getFrontendOpts().Inputs.push_back(kernelFile);
alan-bakerf5e5f692018-11-27 08:33:24 -0500485 // instance.getPreprocessorOpts().addRemappedFile(
486 // overiddenInputFilename, errorOrInputFile.get().release());
487 instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
488 memory_buffer.release());
alan-bakerfec0a472018-11-08 18:09:40 -0500489
490 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
491 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
492 const char *dataCasted = reinterpret_cast<const char *>(data);
493 init(dataCasted, dataCasted + data_length, true);
494 }
495
496 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
497 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
498 }
499
500 virtual ~OpenCLBuiltinMemoryBuffer() override {}
501 };
502
503 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
504 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
505 opencl_builtins_header_size - 1));
506
507 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
508
509 // Add the VULKAN macro.
510 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
511
512 // Add the __OPENCL_VERSION__ macro.
513 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
514
515 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
516 instance.getDiagnostics(),
517 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
518
519 instance.createFileManager();
520 instance.createSourceManager(instance.getFileManager());
521
522#ifdef _MSC_VER
523 std::string includePrefix("include\\");
524#else
525 std::string includePrefix("include/");
526#endif
527
528 auto entry = instance.getFileManager().getVirtualFile(
529 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
530 0);
531
532 instance.getSourceManager().overrideFileContents(
533 entry, std::move(openCLBuiltinMemoryBuffer));
534
535 return 0;
536}
537
alan-bakerf5e5f692018-11-27 08:33:24 -0500538// Populates |pm| with necessary passes to optimize and legalize the IR.
539int PopulatePassManager(
540 llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
541 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
542 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
543 *SamplerMapEntries) {
alan-bakerfec0a472018-11-08 18:09:40 -0500544 llvm::PassManagerBuilder pmBuilder;
545
546 switch (OptimizationLevel) {
547 case '0':
alan-bakerf5e5f692018-11-27 08:33:24 -0500548 case '1':
549 case '2':
550 case '3':
551 case 's':
552 case 'z':
553 break;
554 default:
555 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
556 << " specified!\n";
557 return -1;
558 }
559
560 switch (OptimizationLevel) {
561 case '0':
alan-bakerfec0a472018-11-08 18:09:40 -0500562 pmBuilder.OptLevel = 0;
563 break;
564 case '1':
565 pmBuilder.OptLevel = 1;
566 break;
567 case '2':
568 pmBuilder.OptLevel = 2;
569 break;
570 case '3':
571 pmBuilder.OptLevel = 3;
572 break;
573 case 's':
574 pmBuilder.SizeLevel = 1;
575 break;
576 case 'z':
577 pmBuilder.SizeLevel = 2;
578 break;
579 default:
580 break;
581 }
582
583 pm->add(clspv::createZeroInitializeAllocasPass());
584 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
585
586 if (0 < pmBuilder.OptLevel) {
587 pm->add(clspv::createOpenCLInlinerPass());
588 }
589
590 pm->add(clspv::createUndoByvalPass());
591 pm->add(clspv::createUndoSRetPass());
592 if (cluster_non_pointer_kernel_args) {
593 pm->add(clspv::createClusterPodKernelArgumentsPass());
594 }
595 pm->add(clspv::createReplaceOpenCLBuiltinPass());
596
597 // We need to run mem2reg and inst combine early because our
598 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
599 // %1 = alloca i32 1
600 // store <something> %1
601 // %2 = bitcast float* %1
602 // %3 = load float %2
603 pm->add(llvm::createPromoteMemoryToRegisterPass());
604
605 // Hide loads from __constant address space away from instcombine.
606 // This prevents us from generating select between pointers-to-__constant.
607 // See https://github.com/google/clspv/issues/71
608 pm->add(clspv::createHideConstantLoadsPass());
609
610 pm->add(llvm::createInstructionCombiningPass());
611
612 if (clspv::Option::InlineEntryPoints()) {
613 pm->add(clspv::createInlineEntryPointsPass());
614 } else {
615 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
616 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
617 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
618 }
619
Kévin Petit0fc88042019-04-09 23:25:02 +0100620 if (clspv::Option::CPlusPlus()) {
621 pm->add(clspv::createDemangleKernelNamesPass());
622 }
623
alan-bakerfec0a472018-11-08 18:09:40 -0500624 if (0 == pmBuilder.OptLevel) {
625 // Mem2Reg pass should be run early because O0 level optimization leaves
626 // redundant alloca, load and store instructions from function arguments.
627 // clspv needs to remove them ahead of transformation.
628 pm->add(llvm::createPromoteMemoryToRegisterPass());
629
630 // SROA pass is run because it will fold structs/unions that are problematic
631 // on Vulkan SPIR-V away.
632 pm->add(llvm::createSROAPass());
633
634 // InstructionCombining pass folds bitcast and gep instructions which are
635 // not supported by Vulkan SPIR-V.
636 pm->add(llvm::createInstructionCombiningPass());
637 }
638
639 // Now we add any of the LLVM optimizations we wanted
640 pmBuilder.populateModulePassManager(*pm);
641
642 // Unhide loads from __constant address space. Undoes the action of
643 // HideConstantLoadsPass.
644 pm->add(clspv::createUnhideConstantLoadsPass());
645
646 pm->add(clspv::createFunctionInternalizerPass());
647 pm->add(clspv::createReplaceLLVMIntrinsicsPass());
648 pm->add(clspv::createUndoBoolPass());
649 pm->add(clspv::createUndoTruncatedSwitchConditionPass());
650 pm->add(llvm::createStructurizeCFGPass(false));
alan-baker3fa76d92018-11-12 14:54:40 -0500651 // Must be run after structurize cfg.
alan-bakerfec0a472018-11-08 18:09:40 -0500652 pm->add(clspv::createReorderBasicBlocksPass());
653 pm->add(clspv::createUndoGetElementPtrConstantExprPass());
654 pm->add(clspv::createSplatArgPass());
655 pm->add(clspv::createSimplifyPointerBitcastPass());
656 pm->add(clspv::createReplacePointerBitcastPass());
657
658 pm->add(clspv::createUndoTranslateSamplerFoldPass());
659
660 if (clspv::Option::ModuleConstantsInStorageBuffer()) {
661 pm->add(clspv::createClusterModuleScopeConstantVars());
662 }
663
664 pm->add(clspv::createShareModuleScopeVariablesPass());
alan-bakere9308012019-03-15 10:25:13 -0400665 // This should be run after LLVM and OpenCL intrinsics are replaced.
alan-bakerfec0a472018-11-08 18:09:40 -0500666 pm->add(clspv::createAllocateDescriptorsPass(*SamplerMapEntries));
667 pm->add(llvm::createVerifierPass());
668 pm->add(clspv::createDirectResourceAccessPass());
669 // Replacing pointer bitcasts can leave some trivial GEPs
670 // that are easy to remove. Also replace GEPs of GEPS
671 // left by replacing indirect buffer accesses.
672 pm->add(clspv::createSimplifyPointerBitcastPass());
alan-baker4217b322019-03-06 08:56:12 -0500673 // Run after DRA to clean up parameters and help reduce the need for variable
674 // pointers.
675 pm->add(clspv::createRemoveUnusedArgumentsPass());
alan-bakerfec0a472018-11-08 18:09:40 -0500676
677 pm->add(clspv::createSplatSelectConditionPass());
678 pm->add(clspv::createSignedCompareFixupPass());
679 // This pass generates insertions that need to be rewritten.
680 pm->add(clspv::createScalarizePass());
681 pm->add(clspv::createRewriteInsertsPass());
alan-bakera71f1932019-04-11 11:04:34 -0400682 // UBO Transformations
683 if (clspv::Option::ConstantArgsInUniformBuffer() &&
684 !clspv::Option::InlineEntryPoints()) {
685 // MultiVersionUBOFunctionsPass will examine non-kernel functions with UBO
686 // arguments and either multi-version them as necessary or inline them if
687 // multi-versioning cannot be accomplished.
688 pm->add(clspv::createMultiVersionUBOFunctionsPass());
689 // Cleanup passes.
690 // Specialization can blindly generate GEP chains that are easily cleaned up
691 // by SimplifyPointerBitcastPass.
692 pm->add(clspv::createSimplifyPointerBitcastPass());
693 // RemoveUnusedArgumentsPass removes the actual UBO arguments that were
694 // problematic to begin with now that they have no uses.
695 pm->add(clspv::createRemoveUnusedArgumentsPass());
696 // DCE cleans up callers of the specialized functions.
697 pm->add(llvm::createDeadCodeEliminationPass());
698 }
alan-bakerfec0a472018-11-08 18:09:40 -0500699 // This pass mucks with types to point where you shouldn't rely on DataLayout
700 // anymore so leave this right before SPIR-V generation.
701 pm->add(clspv::createUBOTypeTransformPass());
702 pm->add(clspv::createSPIRVProducerPass(
alan-bakerf5e5f692018-11-27 08:33:24 -0500703 *binaryStream, descriptor_map_entries, *SamplerMapEntries,
Kévin Petite4786902019-04-02 21:51:47 +0100704 false /* Output assembly */, OutputFormat == "c"));
alan-bakerf5e5f692018-11-27 08:33:24 -0500705
706 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500707}
alan-bakerfec0a472018-11-08 18:09:40 -0500708
Kévin Petitd5db2d22019-04-04 13:55:14 +0100709int ParseOptions(const int argc, const char *const argv[]) {
alan-bakerfec0a472018-11-08 18:09:40 -0500710 // We need to change how one of the called passes works by spoofing
711 // ParseCommandLineOptions with the specific option.
712 const int llvmArgc = 2;
713 const char *llvmArgv[llvmArgc] = {
alan-bakerf5e5f692018-11-27 08:33:24 -0500714 argv[0],
715 "-simplifycfg-sink-common=false",
alan-bakerfec0a472018-11-08 18:09:40 -0500716 };
717
Kévin Petitd5db2d22019-04-04 13:55:14 +0100718 llvm::cl::ResetAllOptionOccurrences();
alan-bakerfec0a472018-11-08 18:09:40 -0500719 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
alan-bakerfec0a472018-11-08 18:09:40 -0500720 llvm::cl::ParseCommandLineOptions(argc, argv);
721
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400722 if (clspv::Option::CPlusPlus() && !clspv::Option::InlineEntryPoints()) {
Kévin Petit0fc88042019-04-09 23:25:02 +0100723 llvm::errs() << "cannot use -c++ without -inline-entry-points\n";
724 return -1;
725 }
726
Kévin Petitd5db2d22019-04-04 13:55:14 +0100727 return 0;
728}
Diego Novillo89500852019-04-15 08:45:10 -0400729
730int GenerateIRFile(llvm::legacy::PassManager *pm, llvm::Module &module,
731 std::string output) {
732 std::error_code ec;
733 std::unique_ptr<llvm::ToolOutputFile> out(
734 new llvm::ToolOutputFile(output, ec, llvm::sys::fs::F_None));
735 if (ec) {
736 llvm::errs() << output << ": " << ec.message() << '\n';
737 return -1;
738 }
739 pm->add(llvm::createPrintModulePass(out->os(), "", false));
740 pm->run(module);
741 out->keep();
742 return 0;
743}
744
Kévin Petitd5db2d22019-04-04 13:55:14 +0100745} // namespace
746
747namespace clspv {
748int Compile(const int argc, const char *const argv[]) {
749
750 if (auto error = ParseOptions(argc, argv))
751 return error;
752
alan-bakerfec0a472018-11-08 18:09:40 -0500753 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500754 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500755 return error;
756
757 // if no output file was provided, use a default
758 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
759
760 // If we are reading our input file from stdin.
761 if ("-" == InputFilename) {
762 // We need to overwrite the file name we use.
763 overiddenInputFilename = "stdin.cl";
764 }
765
766 clang::CompilerInstance instance;
767 clang::FrontendInputFile kernelFile(overiddenInputFilename,
768 clang::InputKind::OpenCL);
769 std::string log;
770 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500771 if (auto error = SetCompilerInstanceOptions(
772 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500773 return error;
774
775 // Parse.
776 llvm::LLVMContext context;
777 clang::EmitLLVMOnlyAction action(&context);
778
779 // Prepare the action for processing kernelFile
780 const bool success = action.BeginSourceFile(instance, kernelFile);
781 if (!success) {
782 return -1;
783 }
784
785 action.Execute();
786 action.EndSourceFile();
787
788 clang::DiagnosticConsumer *const consumer =
789 instance.getDiagnostics().getClient();
790 consumer->finish();
791
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100792 auto num_warnings = consumer->getNumWarnings();
alan-bakerfec0a472018-11-08 18:09:40 -0500793 auto num_errors = consumer->getNumErrors();
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100794 if ((num_errors > 0) || (num_warnings > 0)) {
795 llvm::errs() << log;
796 }
alan-bakerfec0a472018-11-08 18:09:40 -0500797 if (num_errors > 0) {
alan-bakerfec0a472018-11-08 18:09:40 -0500798 return -1;
799 }
800
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100801 // Don't run the passes or produce any output in verify mode.
802 // Clang doesn't always produce a valid module.
803 if (verify) {
804 return 0;
805 }
806
alan-bakerfec0a472018-11-08 18:09:40 -0500807 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
808 llvm::initializeCore(Registry);
809 llvm::initializeScalarOpts(Registry);
810
811 std::unique_ptr<llvm::Module> module(action.takeModule());
812
813 // Optimize.
814 // Create a memory buffer for temporarily writing the result.
815 SmallVector<char, 10000> binary;
816 llvm::raw_svector_ostream binaryStream(binary);
817 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500818 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500819 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
Diego Novillo89500852019-04-15 08:45:10 -0400820
821 // If --emit-ir was requested, emit the initial LLVM IR and stop compilation.
822 if (!IROutputFile.empty()) {
823 return GenerateIRFile(&pm, *module, IROutputFile);
824 }
825
826 // Otherwise, populate the pass manager and run the regular passes.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400827 if (auto error = PopulatePassManager(
828 &pm, &binaryStream, &descriptor_map_entries, &SamplerMapEntries))
alan-bakerf5e5f692018-11-27 08:33:24 -0500829 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500830 pm.run(*module);
831
832 // Write outputs
833
834 // Write the descriptor map, if requested.
835 std::error_code error;
836 if (!DescriptorMapFilename.empty()) {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400837 llvm::raw_fd_ostream descriptor_map_out_fd(
838 DescriptorMapFilename, error, llvm::sys::fs::CD_CreateAlways,
839 llvm::sys::fs::FA_Write, llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500840 if (error) {
841 llvm::errs() << "Unable to open descriptor map file '"
842 << DescriptorMapFilename << "': " << error.message() << '\n';
843 return -1;
844 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500845 std::string descriptor_map_string;
846 std::ostringstream str(descriptor_map_string);
847 for (const auto &entry : descriptor_map_entries) {
848 str << entry << "\n";
849 }
850 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500851 descriptor_map_out_fd.close();
852 }
853
854 // Write the resulting binary.
855 // Wait until now to try writing the file so that we only write it on
856 // successful compilation.
857 if (OutputFilename.empty()) {
Kévin Petite4786902019-04-02 21:51:47 +0100858 if (OutputFormat == "c") {
alan-bakerfec0a472018-11-08 18:09:40 -0500859 OutputFilename = "a.spvinc";
860 } else {
861 OutputFilename = "a.spv";
862 }
863 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400864 llvm::raw_fd_ostream outStream(OutputFilename, error,
865 llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500866
867 if (error) {
868 llvm::errs() << "Unable to open output file '" << OutputFilename
869 << "': " << error.message() << '\n';
870 return -1;
871 }
872 outStream << binaryStream.str();
873
874 return 0;
875}
alan-bakerf5e5f692018-11-27 08:33:24 -0500876
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400877int CompileFromSourceString(
878 const std::string &program, const std::string &sampler_map,
879 const std::string &options, std::vector<uint32_t> *output_binary,
880 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500881
882 llvm::SmallVector<const char *, 20> argv;
883 llvm::BumpPtrAllocator A;
884 llvm::StringSaver Saver(A);
885 argv.push_back(Saver.save("clspv").data());
886 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
887 int argc = static_cast<int>(argv.size());
Kévin Petitd5db2d22019-04-04 13:55:14 +0100888
889 if (auto error = ParseOptions(argc, &argv[0]))
890 return error;
alan-bakerf5e5f692018-11-27 08:33:24 -0500891
892 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
893 if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
894 return error;
895
896 InputFilename = "source.cl";
897 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
898
899 clang::CompilerInstance instance;
900 clang::FrontendInputFile kernelFile(overiddenInputFilename,
901 clang::InputKind::OpenCL);
902 std::string log;
903 llvm::raw_string_ostream diagnosticsStream(log);
904 if (auto error =
905 SetCompilerInstanceOptions(instance, overiddenInputFilename,
906 kernelFile, program, &diagnosticsStream))
907 return error;
908
909 // Parse.
910 llvm::LLVMContext context;
911 clang::EmitLLVMOnlyAction action(&context);
912
913 // Prepare the action for processing kernelFile
914 const bool success = action.BeginSourceFile(instance, kernelFile);
915 if (!success) {
916 return -1;
917 }
918
919 action.Execute();
920 action.EndSourceFile();
921
922 clang::DiagnosticConsumer *const consumer =
923 instance.getDiagnostics().getClient();
924 consumer->finish();
925
926 auto num_errors = consumer->getNumErrors();
927 if (num_errors > 0) {
928 llvm::errs() << log << "\n";
929 return -1;
930 }
931
alan-bakerf5e5f692018-11-27 08:33:24 -0500932 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
933 llvm::initializeCore(Registry);
934 llvm::initializeScalarOpts(Registry);
935
936 std::unique_ptr<llvm::Module> module(action.takeModule());
937
938 // Optimize.
939 // Create a memory buffer for temporarily writing the result.
940 SmallVector<char, 10000> binary;
941 llvm::raw_svector_ostream binaryStream(binary);
942 std::string descriptor_map;
943 llvm::legacy::PassManager pm;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400944 if (auto error = PopulatePassManager(
945 &pm, &binaryStream, descriptor_map_entries, &SamplerMapEntries))
alan-bakerf5e5f692018-11-27 08:33:24 -0500946 return error;
947 pm.run(*module);
948
949 // Write outputs
950
951 // Write the descriptor map. This is required.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400952 assert(descriptor_map_entries &&
953 "Valid descriptor map container is required.");
alan-bakerf5e5f692018-11-27 08:33:24 -0500954 if (!DescriptorMapFilename.empty()) {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400955 llvm::errs() << "Warning: -descriptormap is ignored descriptor map "
956 "container is provided.\n";
alan-bakerf5e5f692018-11-27 08:33:24 -0500957 }
958
959 // Write the resulting binary.
960 // Wait until now to try writing the file so that we only write it on
961 // successful compilation.
962 assert(output_binary && "Valid binary container is required.");
963 if (!OutputFilename.empty()) {
964 llvm::outs()
965 << "Warning: -o is ignored when binary container is provided.\n";
966 }
967 output_binary->resize(binary.size() / 4);
968 memcpy(output_binary->data(), binary.data(), binary.size());
969
970 return 0;
971}
alan-bakerfec0a472018-11-08 18:09:40 -0500972} // namespace clspv