blob: 0e8c346af7889133579c36910a4ba439d380650d [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
alan-baker0d095d62020-03-12 14:59:47 -040015#include "clang/Basic/FileManager.h"
alan-bakerfec0a472018-11-08 18:09:40 -050016#include "clang/Basic/TargetInfo.h"
17#include "clang/CodeGen/CodeGenAction.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Frontend/FrontendPluginRegistry.h"
20#include "clang/Frontend/TextDiagnosticPrinter.h"
21#include "clang/Lex/PreprocessorOptions.h"
22#include "llvm/IR/LLVMContext.h"
23#include "llvm/IR/LegacyPassManager.h"
24#include "llvm/IR/Module.h"
25#include "llvm/IR/Verifier.h"
alan-baker0e64a592019-11-18 13:36:25 -050026#include "llvm/InitializePasses.h"
alan-bakerfec0a472018-11-08 18:09:40 -050027#include "llvm/LinkAllPasses.h"
alan-bakerf5e5f692018-11-27 08:33:24 -050028#include "llvm/Support/Allocator.h"
alan-bakerfec0a472018-11-08 18:09:40 -050029#include "llvm/Support/CommandLine.h"
alan-bakerf5e5f692018-11-27 08:33:24 -050030#include "llvm/Support/ErrorOr.h"
alan-bakerfec0a472018-11-08 18:09:40 -050031#include "llvm/Support/MathExtras.h"
alan-bakerf5e5f692018-11-27 08:33:24 -050032#include "llvm/Support/StringSaver.h"
Diego Novillo89500852019-04-15 08:45:10 -040033#include "llvm/Support/ToolOutputFile.h"
alan-bakerfec0a472018-11-08 18:09:40 -050034#include "llvm/Support/raw_ostream.h"
35#include "llvm/Transforms/IPO/PassManagerBuilder.h"
36
Kévin Petit38c52482019-05-07 20:28:00 +080037#include "clspv/AddressSpace.h"
alan-bakerf5e5f692018-11-27 08:33:24 -050038#include "clspv/DescriptorMap.h"
alan-bakerfec0a472018-11-08 18:09:40 -050039#include "clspv/Option.h"
40#include "clspv/Passes.h"
41#include "clspv/opencl_builtins_header.h"
42
43#include "FrontendPlugin.h"
Diego Novilloa4c44fa2019-04-11 10:56:15 -040044#include "Passes.h"
alan-bakerfec0a472018-11-08 18:09:40 -050045
alan-bakerf5e5f692018-11-27 08:33:24 -050046#include <cassert>
alan-bakerfec0a472018-11-08 18:09:40 -050047#include <numeric>
alan-bakerf5e5f692018-11-27 08:33:24 -050048#include <sstream>
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040049#include <string>
alan-bakerfec0a472018-11-08 18:09:40 -050050
51using namespace clang;
52
53namespace {
54// This registration must be located in the same file as the execution of the
55// action.
56static FrontendPluginRegistry::Add<clspv::ExtraValidationASTAction>
57 X("extra-validation",
58 "Perform extra validation on OpenCL C when targeting Vulkan");
59
60static llvm::cl::opt<bool> cl_single_precision_constants(
61 "cl-single-precision-constant", llvm::cl::init(false),
62 llvm::cl::desc("Treat double precision floating-point constant as single "
63 "precision constant."));
64
65static llvm::cl::opt<bool> cl_denorms_are_zero(
66 "cl-denorms-are-zero", llvm::cl::init(false),
67 llvm::cl::desc("If specified, denormalized floating point numbers may be "
68 "flushed to zero."));
69
70static llvm::cl::opt<bool> cl_fp32_correctly_rounded_divide_sqrt(
71 "cl-fp32-correctly-rounded-divide-sqrt", llvm::cl::init(false),
72 llvm::cl::desc("Single precision floating-point divide (x/y and 1/x) and "
73 "sqrt used are correctly rounded."));
74
75static llvm::cl::opt<bool>
76 cl_opt_disable("cl-opt-disable", llvm::cl::init(false),
77 llvm::cl::desc("This option disables all optimizations. The "
78 "default is optimizations are enabled."));
79
80static llvm::cl::opt<bool> cl_mad_enable(
81 "cl-mad-enable", llvm::cl::init(false),
82 llvm::cl::desc("Allow a * b + c to be replaced by a mad. The mad computes "
83 "a * b + c with reduced accuracy."));
84
85static llvm::cl::opt<bool> cl_no_signed_zeros(
86 "cl-no-signed-zeros", llvm::cl::init(false),
87 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
88 "ignore the signedness of zero."));
89
90static llvm::cl::opt<bool> cl_unsafe_math_optimizations(
91 "cl-unsafe-math-optimizations", llvm::cl::init(false),
92 llvm::cl::desc("Allow optimizations for floating-point arithmetic that (a) "
93 "assume that arguments and results are valid, (b) may "
94 "violate IEEE 754 standard and (c) may violate the OpenCL "
95 "numerical compliance requirements. This option includes "
96 "the -cl-no-signed-zeros and -cl-mad-enable options."));
97
98static llvm::cl::opt<bool> cl_finite_math_only(
99 "cl-finite-math-only", llvm::cl::init(false),
100 llvm::cl::desc("Allow optimizations for floating-point arithmetic that "
101 "assume that arguments and results are not NaNs or INFs."));
102
103static llvm::cl::opt<bool> cl_fast_relaxed_math(
104 "cl-fast-relaxed-math", llvm::cl::init(false),
105 llvm::cl::desc("This option causes the preprocessor macro "
106 "__FAST_RELAXED_MATH__ to be defined. Sets the optimization "
107 "options -cl-finite-math-only and "
108 "-cl-unsafe-math-optimizations."));
109
110static llvm::cl::list<std::string>
111 Includes(llvm::cl::Prefix, "I",
112 llvm::cl::desc("Add a directory to the list of directories "
113 "to be searched for header files."),
114 llvm::cl::ZeroOrMore, llvm::cl::value_desc("include path"));
115
116static llvm::cl::list<std::string>
117 Defines(llvm::cl::Prefix, "D",
118 llvm::cl::desc("Define a #define directive."), llvm::cl::ZeroOrMore,
119 llvm::cl::value_desc("define"));
120
121static llvm::cl::opt<std::string>
122 InputFilename(llvm::cl::Positional, llvm::cl::desc("<input .cl file>"),
123 llvm::cl::init("-"));
124
Kévin Petitddad8f42019-09-30 15:12:08 +0100125static llvm::cl::opt<clang::Language> InputLanguage(
126 "x", llvm::cl::desc("Select input type"),
127 llvm::cl::init(clang::Language::OpenCL),
128 llvm::cl::values(clEnumValN(clang::Language::OpenCL, "cl", "OpenCL source"),
129 clEnumValN(clang::Language::LLVM_IR, "ir", "LLVM IR")));
130
alan-bakerfec0a472018-11-08 18:09:40 -0500131static llvm::cl::opt<std::string>
132 OutputFilename("o", llvm::cl::desc("Override output filename"),
133 llvm::cl::value_desc("filename"));
134
135static llvm::cl::opt<std::string>
136 DescriptorMapFilename("descriptormap",
137 llvm::cl::desc("Output file for descriptor map"),
138 llvm::cl::value_desc("filename"));
139
140static llvm::cl::opt<char>
141 OptimizationLevel(llvm::cl::Prefix, "O", llvm::cl::init('2'),
142 llvm::cl::desc("Optimization level to use"),
143 llvm::cl::value_desc("level"));
144
alan-bakerfec0a472018-11-08 18:09:40 -0500145static llvm::cl::opt<std::string> OutputFormat(
146 "mfmt", llvm::cl::init(""),
147 llvm::cl::desc(
148 "Specify special output format. 'c' is as a C initializer list"),
149 llvm::cl::value_desc("format"));
150
151static llvm::cl::opt<std::string>
alan-baker09cb9802019-12-10 13:16:27 -0500152 SamplerMap("samplermap", llvm::cl::desc("DEPRECATED - Literal sampler map"),
alan-bakerfec0a472018-11-08 18:09:40 -0500153 llvm::cl::value_desc("filename"));
154
155static llvm::cl::opt<bool> cluster_non_pointer_kernel_args(
156 "cluster-pod-kernel-args", llvm::cl::init(false),
157 llvm::cl::desc("Collect plain-old-data kernel arguments into a struct in "
158 "a single storage buffer, using a binding number after "
159 "other arguments. Use this to reduce storage buffer "
160 "descriptors."));
161
162static llvm::cl::opt<bool> verify("verify", llvm::cl::init(false),
163 llvm::cl::desc("Verify diagnostic outputs"));
164
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100165static llvm::cl::opt<bool>
166 IgnoreWarnings("w", llvm::cl::init(false),
167 llvm::cl::desc("Disable all warnings"));
168
169static llvm::cl::opt<bool>
170 WarningsAsErrors("Werror", llvm::cl::init(false),
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400171 llvm::cl::desc("Turn warnings into errors"));
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100172
Diego Novillo89500852019-04-15 08:45:10 -0400173static llvm::cl::opt<std::string> IROutputFile(
174 "emit-ir",
175 llvm::cl::desc(
176 "Emit LLVM IR to the given file after parsing and stop compilation."),
177 llvm::cl::value_desc("filename"));
178
alan-bakerfec0a472018-11-08 18:09:40 -0500179// Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
180// if successful.
alan-bakerf5e5f692018-11-27 08:33:24 -0500181int ParseSamplerMap(const std::string &sampler_map,
182 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
183 *SamplerMapEntries) {
184 std::unique_ptr<llvm::MemoryBuffer> samplerMapBuffer(nullptr);
185 if (!sampler_map.empty()) {
186 // Parse the sampler map from the provided string.
187 samplerMapBuffer = llvm::MemoryBuffer::getMemBuffer(sampler_map);
188
alan-baker09cb9802019-12-10 13:16:27 -0500189 clspv::Option::SetUseSamplerMap(true);
alan-bakerf5e5f692018-11-27 08:33:24 -0500190 if (!SamplerMap.empty()) {
191 llvm::outs() << "Warning: -samplermap is ignored when the sampler map is "
192 "provided through a string.\n";
193 }
194 } else if (!SamplerMap.empty()) {
195 // Parse the sampler map from the option provided file.
alan-bakerfec0a472018-11-08 18:09:40 -0500196 auto errorOrSamplerMapFile =
197 llvm::MemoryBuffer::getFile(SamplerMap.getValue());
198
199 // If there was an error in getting the sampler map file.
200 if (!errorOrSamplerMapFile) {
201 llvm::errs() << "Error: " << errorOrSamplerMapFile.getError().message()
202 << " '" << SamplerMap.getValue() << "'\n";
203 return -1;
204 }
205
alan-baker09cb9802019-12-10 13:16:27 -0500206 clspv::Option::SetUseSamplerMap(true);
alan-bakerf5e5f692018-11-27 08:33:24 -0500207 samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
alan-bakerfec0a472018-11-08 18:09:40 -0500208 if (0 == samplerMapBuffer->getBufferSize()) {
209 llvm::errs() << "Error: Sampler map was an empty file!\n";
210 return -1;
211 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500212 }
alan-bakerfec0a472018-11-08 18:09:40 -0500213
alan-baker09cb9802019-12-10 13:16:27 -0500214 if (clspv::Option::UseSamplerMap()) {
215 llvm::outs()
216 << "Warning: use of the sampler map is deprecated and unnecessary\n";
217 }
218
alan-bakerf5e5f692018-11-27 08:33:24 -0500219 // No sampler map to parse.
220 if (!samplerMapBuffer || 0 == samplerMapBuffer->getBufferSize())
221 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500222
alan-bakerf5e5f692018-11-27 08:33:24 -0500223 llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
alan-bakerfec0a472018-11-08 18:09:40 -0500224
alan-bakerf5e5f692018-11-27 08:33:24 -0500225 // We need to keep track of the beginning of the current entry.
226 const char *b = samplerMapBuffer->getBufferStart();
227 for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
228 // If we have a separator between declarations.
229 if ((*i == '|') || (*i == ',') || (i == e)) {
230 if (i == b) {
231 llvm::errs() << "Error: Sampler map contained an empty entry!\n";
232 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500233 }
234
alan-bakerf5e5f692018-11-27 08:33:24 -0500235 samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
alan-bakerfec0a472018-11-08 18:09:40 -0500236
alan-bakerf5e5f692018-11-27 08:33:24 -0500237 // And set b the next character after i.
238 b = i + 1;
239 }
alan-bakerfec0a472018-11-08 18:09:40 -0500240
alan-bakerf5e5f692018-11-27 08:33:24 -0500241 // If we have a separator between declarations within a single sampler.
242 if ((*i == ',') || (i == e)) {
alan-bakerfec0a472018-11-08 18:09:40 -0500243
James Pricec05f6052020-01-14 13:37:20 -0500244 clspv::version0::SamplerNormalizedCoords NormalizedCoord =
245 clspv::version0::CLK_NORMALIZED_COORDS_NOT_SET;
246 clspv::version0::SamplerAddressingMode AddressingMode =
247 clspv::version0::CLK_ADDRESS_NOT_SET;
248 clspv::version0::SamplerFilterMode FilterMode =
249 clspv::version0::CLK_FILTER_NOT_SET;
alan-bakerf5e5f692018-11-27 08:33:24 -0500250
251 for (auto str : samplerStrings) {
252 if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500253 if (clspv::version0::CLK_NORMALIZED_COORDS_NOT_SET !=
254 NormalizedCoord) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500255 llvm::errs() << "Error: Sampler map normalized coordinates was "
256 "previously set!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500257 return -1;
258 }
James Pricec05f6052020-01-14 13:37:20 -0500259 NormalizedCoord = clspv::version0::CLK_NORMALIZED_COORDS_FALSE;
alan-bakerf5e5f692018-11-27 08:33:24 -0500260 } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500261 if (clspv::version0::CLK_NORMALIZED_COORDS_NOT_SET !=
262 NormalizedCoord) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500263 llvm::errs() << "Error: Sampler map normalized coordinates was "
264 "previously set!\n";
265 return -1;
266 }
James Pricec05f6052020-01-14 13:37:20 -0500267 NormalizedCoord = clspv::version0::CLK_NORMALIZED_COORDS_TRUE;
alan-bakerf5e5f692018-11-27 08:33:24 -0500268 } else if ("CLK_ADDRESS_NONE" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500269 if (clspv::version0::CLK_ADDRESS_NOT_SET != AddressingMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500270 llvm::errs()
271 << "Error: Sampler map addressing mode was previously set!\n";
272 return -1;
273 }
James Pricec05f6052020-01-14 13:37:20 -0500274 AddressingMode = clspv::version0::CLK_ADDRESS_NONE;
alan-bakerf5e5f692018-11-27 08:33:24 -0500275 } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500276 if (clspv::version0::CLK_ADDRESS_NOT_SET != AddressingMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500277 llvm::errs()
278 << "Error: Sampler map addressing mode was previously set!\n";
279 return -1;
280 }
James Pricec05f6052020-01-14 13:37:20 -0500281 AddressingMode = clspv::version0::CLK_ADDRESS_CLAMP_TO_EDGE;
alan-bakerf5e5f692018-11-27 08:33:24 -0500282 } else if ("CLK_ADDRESS_CLAMP" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500283 if (clspv::version0::CLK_ADDRESS_NOT_SET != AddressingMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500284 llvm::errs()
285 << "Error: Sampler map addressing mode was previously set!\n";
286 return -1;
287 }
James Pricec05f6052020-01-14 13:37:20 -0500288 AddressingMode = clspv::version0::CLK_ADDRESS_CLAMP;
alan-bakerf5e5f692018-11-27 08:33:24 -0500289 } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500290 if (clspv::version0::CLK_ADDRESS_NOT_SET != AddressingMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500291 llvm::errs()
292 << "Error: Sampler map addressing mode was previously set!\n";
293 return -1;
294 }
James Pricec05f6052020-01-14 13:37:20 -0500295 AddressingMode = clspv::version0::CLK_ADDRESS_MIRRORED_REPEAT;
alan-bakerf5e5f692018-11-27 08:33:24 -0500296 } else if ("CLK_ADDRESS_REPEAT" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500297 if (clspv::version0::CLK_ADDRESS_NOT_SET != AddressingMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500298 llvm::errs()
299 << "Error: Sampler map addressing mode was previously set!\n";
300 return -1;
301 }
James Pricec05f6052020-01-14 13:37:20 -0500302 AddressingMode = clspv::version0::CLK_ADDRESS_REPEAT;
alan-bakerf5e5f692018-11-27 08:33:24 -0500303 } else if ("CLK_FILTER_NEAREST" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500304 if (clspv::version0::CLK_FILTER_NOT_SET != FilterMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500305 llvm::errs()
306 << "Error: Sampler map filtering mode was previously set!\n";
307 return -1;
308 }
James Pricec05f6052020-01-14 13:37:20 -0500309 FilterMode = clspv::version0::CLK_FILTER_NEAREST;
alan-bakerf5e5f692018-11-27 08:33:24 -0500310 } else if ("CLK_FILTER_LINEAR" == str) {
James Pricec05f6052020-01-14 13:37:20 -0500311 if (clspv::version0::CLK_FILTER_NOT_SET != FilterMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500312 llvm::errs()
313 << "Error: Sampler map filtering mode was previously set!\n";
314 return -1;
315 }
James Pricec05f6052020-01-14 13:37:20 -0500316 FilterMode = clspv::version0::CLK_FILTER_LINEAR;
alan-bakerf5e5f692018-11-27 08:33:24 -0500317 } else {
318 llvm::errs() << "Error: Unknown sampler string '" << str
319 << "' found!\n";
alan-bakerfec0a472018-11-08 18:09:40 -0500320 return -1;
321 }
alan-bakerfec0a472018-11-08 18:09:40 -0500322 }
323
James Pricec05f6052020-01-14 13:37:20 -0500324 if (clspv::version0::CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500325 llvm::errs() << "Error: Sampler map entry did not contain normalized "
326 "coordinates entry!\n";
327 return -1;
alan-bakerfec0a472018-11-08 18:09:40 -0500328 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500329
James Pricec05f6052020-01-14 13:37:20 -0500330 if (clspv::version0::CLK_ADDRESS_NOT_SET == AddressingMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500331 llvm::errs() << "Error: Sampler map entry did not contain addressing "
332 "mode entry!\n";
333 return -1;
334 }
335
James Pricec05f6052020-01-14 13:37:20 -0500336 if (clspv::version0::CLK_FILTER_NOT_SET == FilterMode) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500337 llvm::errs()
338 << "Error: Sampler map entry did not contain filer mode entry!\n";
339 return -1;
340 }
341
342 // Generate an equivalent expression in string form. Sort the
343 // strings to get a canonical ordering.
344 std::sort(samplerStrings.begin(), samplerStrings.end(),
345 std::less<StringRef>());
346 const auto samplerExpr = std::accumulate(
347 samplerStrings.begin(), samplerStrings.end(), std::string(),
alan-baker21574d32020-01-29 16:00:31 -0500348 [](llvm::StringRef left, llvm::StringRef right) {
349 return left.str() + std::string(left.empty() ? "" : "|") +
350 right.str();
alan-bakerf5e5f692018-11-27 08:33:24 -0500351 });
352
353 // SamplerMapEntries->push_back(std::make_pair(
354 // NormalizedCoord | AddressingMode | FilterMode, samplerExpr));
355 SamplerMapEntries->emplace_back(
356 NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
357
358 // And reset the sampler strings for the next sampler in the map.
359 samplerStrings.clear();
360 }
361
362 // And lastly, if we are at the end of the file
363 if (i == e) {
364 break;
alan-bakerfec0a472018-11-08 18:09:40 -0500365 }
366 }
367
368 return 0;
369}
370
371// Sets |instance|'s options for compiling. Returns 0 if successful.
372int SetCompilerInstanceOptions(CompilerInstance &instance,
373 const llvm::StringRef &overiddenInputFilename,
374 const clang::FrontendInputFile &kernelFile,
alan-bakerf5e5f692018-11-27 08:33:24 -0500375 const std::string &program,
alan-bakerfec0a472018-11-08 18:09:40 -0500376 llvm::raw_string_ostream *diagnosticsStream) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500377 std::unique_ptr<llvm::MemoryBuffer> memory_buffer(nullptr);
378 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> errorOrInputFile(nullptr);
379 if (program.empty()) {
380 auto errorOrInputFile =
381 llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
alan-bakerfec0a472018-11-08 18:09:40 -0500382
alan-bakerf5e5f692018-11-27 08:33:24 -0500383 // If there was an error in getting the input file.
384 if (!errorOrInputFile) {
385 llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
386 << InputFilename.getValue() << "'\n";
387 return -1;
388 }
389 memory_buffer.reset(errorOrInputFile.get().release());
390 } else {
391 memory_buffer = llvm::MemoryBuffer::getMemBuffer(program.c_str(),
392 overiddenInputFilename);
alan-bakerfec0a472018-11-08 18:09:40 -0500393 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500394
alan-bakerfec0a472018-11-08 18:09:40 -0500395 if (verify) {
396 instance.getDiagnosticOpts().VerifyDiagnostics = true;
alan-bakerbccf62c2019-03-29 10:32:41 -0400397 instance.getDiagnosticOpts().VerifyPrefixes.push_back("expected");
alan-bakerfec0a472018-11-08 18:09:40 -0500398 }
399
Kévin Petit0fc88042019-04-09 23:25:02 +0100400 clang::LangStandard::Kind standard;
Kévin Petitf0515712020-01-07 18:29:20 +0000401 switch (clspv::Option::Language()) {
402 case clspv::Option::SourceLanguage::OpenCL_C_10:
403 standard = clang::LangStandard::lang_opencl10;
404 break;
405 case clspv::Option::SourceLanguage::OpenCL_C_11:
406 standard = clang::LangStandard::lang_opencl11;
407 break;
408 case clspv::Option::SourceLanguage::OpenCL_C_12:
Kévin Petit0fc88042019-04-09 23:25:02 +0100409 standard = clang::LangStandard::lang_opencl12;
Kévin Petitf0515712020-01-07 18:29:20 +0000410 break;
411 case clspv::Option::SourceLanguage::OpenCL_C_20:
412 standard = clang::LangStandard::lang_opencl20;
413 break;
414 case clspv::Option::SourceLanguage::OpenCL_CPP:
415 standard = clang::LangStandard::lang_openclcpp;
416 break;
417 default:
418 llvm_unreachable("Unknown source language");
Kévin Petit0fc88042019-04-09 23:25:02 +0100419 }
alan-bakerfec0a472018-11-08 18:09:40 -0500420
alan-bakerfec0a472018-11-08 18:09:40 -0500421 instance.getLangOpts().C99 = true;
422 instance.getLangOpts().RTTI = false;
423 instance.getLangOpts().RTTIData = false;
424 instance.getLangOpts().MathErrno = false;
425 instance.getLangOpts().Optimize = false;
426 instance.getLangOpts().NoBuiltin = true;
427 instance.getLangOpts().ModulesSearchAll = false;
428 instance.getLangOpts().SinglePrecisionConstants = true;
429 instance.getCodeGenOpts().StackRealignment = true;
430 instance.getCodeGenOpts().SimplifyLibCalls = false;
431 instance.getCodeGenOpts().EmitOpenCLArgMetadata = false;
432 instance.getCodeGenOpts().DisableO0ImplyOptNone = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100433 instance.getDiagnosticOpts().IgnoreWarnings = IgnoreWarnings;
alan-bakerfec0a472018-11-08 18:09:40 -0500434
435 instance.getLangOpts().SinglePrecisionConstants =
436 cl_single_precision_constants;
437 // cl_denorms_are_zero ignored for now!
438 // cl_fp32_correctly_rounded_divide_sqrt ignored for now!
439 instance.getCodeGenOpts().LessPreciseFPMAD =
440 cl_mad_enable || cl_unsafe_math_optimizations;
441 // cl_no_signed_zeros ignored for now!
442 instance.getCodeGenOpts().UnsafeFPMath =
443 cl_unsafe_math_optimizations || cl_fast_relaxed_math;
444 instance.getLangOpts().FiniteMathOnly =
445 cl_finite_math_only || cl_fast_relaxed_math;
446 instance.getLangOpts().FastRelaxedMath = cl_fast_relaxed_math;
447
448 // Preprocessor options
Kévin Petita624c0c2019-05-07 20:27:43 +0800449 if (!clspv::Option::ImageSupport()) {
450 instance.getPreprocessorOpts().addMacroUndef("__IMAGE_SUPPORT__");
451 }
alan-bakerfec0a472018-11-08 18:09:40 -0500452 if (cl_fast_relaxed_math) {
453 instance.getPreprocessorOpts().addMacroDef("__FAST_RELAXED_MATH__");
454 }
455
456 for (auto define : Defines) {
457 instance.getPreprocessorOpts().addMacroDef(define);
458 }
459
460 // Header search options
461 for (auto include : Includes) {
462 instance.getHeaderSearchOpts().AddPath(include, clang::frontend::After,
463 false, false);
464 }
465
466 // We always compile on opt 0 so we preserve as much debug information about
467 // the source as possible. We'll run optimization later, once we've had a
468 // chance to view the unoptimal code first
469 instance.getCodeGenOpts().OptimizationLevel = 0;
470
471// Debug information is disabled temporarily to call instruction.
472#if 0
473 instance.getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo);
474#endif
475
476 // We use the 32-bit pointer-width SPIR triple
477 llvm::Triple triple("spir-unknown-unknown");
478
479 instance.getInvocation().setLangDefaults(
alan-bakerd354f1a2019-08-06 15:41:55 -0400480 instance.getLangOpts(), clang::InputKind(clang::Language::OpenCL), triple,
alan-bakerfec0a472018-11-08 18:09:40 -0500481 instance.getPreprocessorOpts(), standard);
482
483 // Override the C99 inline semantics to accommodate for more OpenCL C
484 // programs in the wild.
485 instance.getLangOpts().GNUInline = true;
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100486
487 // Set up diagnostics
alan-bakerfec0a472018-11-08 18:09:40 -0500488 instance.createDiagnostics(
489 new clang::TextDiagnosticPrinter(*diagnosticsStream,
490 &instance.getDiagnosticOpts()),
491 true);
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100492 instance.getDiagnostics().setWarningsAsErrors(WarningsAsErrors);
493 instance.getDiagnostics().setEnableAllWarnings(true);
alan-bakerfec0a472018-11-08 18:09:40 -0500494
495 instance.getTargetOpts().Triple = triple.str();
496
alan-baker21574d32020-01-29 16:00:31 -0500497 instance.getCodeGenOpts().MainFileName = overiddenInputFilename.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500498 instance.getCodeGenOpts().PreserveVec3Type = true;
499 // Disable generation of lifetime intrinsic.
500 instance.getCodeGenOpts().DisableLifetimeMarkers = true;
501 instance.getFrontendOpts().Inputs.push_back(kernelFile);
alan-bakerf5e5f692018-11-27 08:33:24 -0500502 instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
503 memory_buffer.release());
alan-bakerfec0a472018-11-08 18:09:40 -0500504
505 struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
506 OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
507 const char *dataCasted = reinterpret_cast<const char *>(data);
508 init(dataCasted, dataCasted + data_length, true);
509 }
510
511 virtual llvm::MemoryBuffer::BufferKind getBufferKind() const override {
512 return llvm::MemoryBuffer::MemoryBuffer_Malloc;
513 }
514
515 virtual ~OpenCLBuiltinMemoryBuffer() override {}
516 };
517
518 std::unique_ptr<llvm::MemoryBuffer> openCLBuiltinMemoryBuffer(
519 new OpenCLBuiltinMemoryBuffer(opencl_builtins_header_data,
520 opencl_builtins_header_size - 1));
521
522 instance.getPreprocessorOpts().Includes.push_back("openclc.h");
523
alan-bakerf3bce4a2019-06-28 16:01:15 -0400524 std::unique_ptr<llvm::MemoryBuffer> openCLBaseBuiltinMemoryBuffer(
525 new OpenCLBuiltinMemoryBuffer(opencl_base_builtins_header_data,
526 opencl_base_builtins_header_size - 1));
527
528 instance.getPreprocessorOpts().Includes.push_back("opencl-c-base.h");
529
alan-bakerfec0a472018-11-08 18:09:40 -0500530 // Add the VULKAN macro.
531 instance.getPreprocessorOpts().addMacroDef("VULKAN=100");
532
533 // Add the __OPENCL_VERSION__ macro.
534 instance.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=120");
535
536 instance.setTarget(clang::TargetInfo::CreateTargetInfo(
537 instance.getDiagnostics(),
538 std::make_shared<clang::TargetOptions>(instance.getTargetOpts())));
539
540 instance.createFileManager();
541 instance.createSourceManager(instance.getFileManager());
542
543#ifdef _MSC_VER
544 std::string includePrefix("include\\");
545#else
546 std::string includePrefix("include/");
547#endif
548
549 auto entry = instance.getFileManager().getVirtualFile(
550 includePrefix + "openclc.h", openCLBuiltinMemoryBuffer->getBufferSize(),
551 0);
552
553 instance.getSourceManager().overrideFileContents(
554 entry, std::move(openCLBuiltinMemoryBuffer));
555
alan-bakerf3bce4a2019-06-28 16:01:15 -0400556 auto base_entry = instance.getFileManager().getVirtualFile(
557 includePrefix + "opencl-c-base.h",
558 openCLBaseBuiltinMemoryBuffer->getBufferSize(), 0);
559
560 instance.getSourceManager().overrideFileContents(
561 base_entry, std::move(openCLBaseBuiltinMemoryBuffer));
562
alan-bakerfec0a472018-11-08 18:09:40 -0500563 return 0;
564}
565
alan-bakerf5e5f692018-11-27 08:33:24 -0500566// Populates |pm| with necessary passes to optimize and legalize the IR.
567int PopulatePassManager(
568 llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
569 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
570 llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
571 *SamplerMapEntries) {
alan-bakerfec0a472018-11-08 18:09:40 -0500572 llvm::PassManagerBuilder pmBuilder;
573
574 switch (OptimizationLevel) {
575 case '0':
alan-bakerf5e5f692018-11-27 08:33:24 -0500576 case '1':
577 case '2':
578 case '3':
579 case 's':
580 case 'z':
581 break;
582 default:
583 llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
584 << " specified!\n";
585 return -1;
586 }
587
588 switch (OptimizationLevel) {
589 case '0':
alan-bakerfec0a472018-11-08 18:09:40 -0500590 pmBuilder.OptLevel = 0;
591 break;
592 case '1':
593 pmBuilder.OptLevel = 1;
594 break;
595 case '2':
596 pmBuilder.OptLevel = 2;
597 break;
598 case '3':
599 pmBuilder.OptLevel = 3;
600 break;
601 case 's':
602 pmBuilder.SizeLevel = 1;
603 break;
604 case 'z':
605 pmBuilder.SizeLevel = 2;
606 break;
607 default:
608 break;
609 }
610
611 pm->add(clspv::createZeroInitializeAllocasPass());
alan-baker04f3a952020-03-24 10:39:53 -0400612 pm->add(clspv::createAddFunctionAttributesPass());
Kévin Petitbbbda972020-03-03 19:16:31 +0000613 pm->add(clspv::createDeclarePushConstantsPass());
alan-bakerfec0a472018-11-08 18:09:40 -0500614 pm->add(clspv::createDefineOpenCLWorkItemBuiltinsPass());
615
616 if (0 < pmBuilder.OptLevel) {
617 pm->add(clspv::createOpenCLInlinerPass());
618 }
619
620 pm->add(clspv::createUndoByvalPass());
621 pm->add(clspv::createUndoSRetPass());
622 if (cluster_non_pointer_kernel_args) {
623 pm->add(clspv::createClusterPodKernelArgumentsPass());
624 }
625 pm->add(clspv::createReplaceOpenCLBuiltinPass());
626
627 // We need to run mem2reg and inst combine early because our
628 // createInlineFuncWithPointerBitCastArgPass pass cannot handle the pattern
629 // %1 = alloca i32 1
630 // store <something> %1
631 // %2 = bitcast float* %1
632 // %3 = load float %2
633 pm->add(llvm::createPromoteMemoryToRegisterPass());
634
alan-baker1b13e8f2019-08-08 17:56:51 -0400635 // Try to deal with pointer bitcasts early. This can prevent problems like
636 // issue #409 where LLVM is looser about access chain addressing than SPIR-V.
637 // This needs to happen before instcombine and after replacing OpenCL
638 // builtins. This run of the pass will not handle all pointer bitcasts that
639 // could be handled. It should be run again after other optimizations (e.g
640 // InlineFuncWithPointerBitCastArgPass).
641 pm->add(clspv::createSimplifyPointerBitcastPass());
642 pm->add(clspv::createReplacePointerBitcastPass());
643 pm->add(llvm::createDeadCodeEliminationPass());
644
alan-bakerfec0a472018-11-08 18:09:40 -0500645 // Hide loads from __constant address space away from instcombine.
646 // This prevents us from generating select between pointers-to-__constant.
647 // See https://github.com/google/clspv/issues/71
648 pm->add(clspv::createHideConstantLoadsPass());
649
650 pm->add(llvm::createInstructionCombiningPass());
651
652 if (clspv::Option::InlineEntryPoints()) {
653 pm->add(clspv::createInlineEntryPointsPass());
654 } else {
655 pm->add(clspv::createInlineFuncWithPointerBitCastArgPass());
656 pm->add(clspv::createInlineFuncWithPointerToFunctionArgPass());
657 pm->add(clspv::createInlineFuncWithSingleCallSitePass());
658 }
659
Kévin Petitf0515712020-01-07 18:29:20 +0000660 if (clspv::Option::LanguageUsesGenericAddressSpace()) {
Kévin Petit38c52482019-05-07 20:28:00 +0800661 pm->add(llvm::createInferAddressSpacesPass(clspv::AddressSpace::Generic));
Kévin Petit0fc88042019-04-09 23:25:02 +0100662 }
663
alan-bakerfec0a472018-11-08 18:09:40 -0500664 if (0 == pmBuilder.OptLevel) {
665 // Mem2Reg pass should be run early because O0 level optimization leaves
666 // redundant alloca, load and store instructions from function arguments.
667 // clspv needs to remove them ahead of transformation.
668 pm->add(llvm::createPromoteMemoryToRegisterPass());
669
670 // SROA pass is run because it will fold structs/unions that are problematic
671 // on Vulkan SPIR-V away.
672 pm->add(llvm::createSROAPass());
673
674 // InstructionCombining pass folds bitcast and gep instructions which are
675 // not supported by Vulkan SPIR-V.
676 pm->add(llvm::createInstructionCombiningPass());
677 }
678
679 // Now we add any of the LLVM optimizations we wanted
680 pmBuilder.populateModulePassManager(*pm);
681
682 // Unhide loads from __constant address space. Undoes the action of
683 // HideConstantLoadsPass.
684 pm->add(clspv::createUnhideConstantLoadsPass());
685
alan-baker13568382020-04-02 17:29:27 -0400686 pm->add(clspv::createUndoInstCombinePass());
alan-bakerfec0a472018-11-08 18:09:40 -0500687 pm->add(clspv::createFunctionInternalizerPass());
688 pm->add(clspv::createReplaceLLVMIntrinsicsPass());
689 pm->add(clspv::createUndoBoolPass());
690 pm->add(clspv::createUndoTruncatedSwitchConditionPass());
691 pm->add(llvm::createStructurizeCFGPass(false));
alan-baker3fa76d92018-11-12 14:54:40 -0500692 // Must be run after structurize cfg.
alan-baker9580aef2020-01-07 22:31:48 -0500693 pm->add(clspv::createFixupStructuredCFGPass());
694 // Must be run after structured cfg fixup.
alan-bakerfec0a472018-11-08 18:09:40 -0500695 pm->add(clspv::createReorderBasicBlocksPass());
696 pm->add(clspv::createUndoGetElementPtrConstantExprPass());
697 pm->add(clspv::createSplatArgPass());
698 pm->add(clspv::createSimplifyPointerBitcastPass());
699 pm->add(clspv::createReplacePointerBitcastPass());
700
701 pm->add(clspv::createUndoTranslateSamplerFoldPass());
702
703 if (clspv::Option::ModuleConstantsInStorageBuffer()) {
704 pm->add(clspv::createClusterModuleScopeConstantVars());
705 }
706
707 pm->add(clspv::createShareModuleScopeVariablesPass());
alan-bakerf67468c2019-11-25 15:51:49 -0500708 // Specialize images before assigning descriptors to disambiguate the various
709 // types.
710 pm->add(clspv::createSpecializeImageTypesPass());
alan-bakere9308012019-03-15 10:25:13 -0400711 // This should be run after LLVM and OpenCL intrinsics are replaced.
alan-bakerfec0a472018-11-08 18:09:40 -0500712 pm->add(clspv::createAllocateDescriptorsPass(*SamplerMapEntries));
713 pm->add(llvm::createVerifierPass());
714 pm->add(clspv::createDirectResourceAccessPass());
715 // Replacing pointer bitcasts can leave some trivial GEPs
716 // that are easy to remove. Also replace GEPs of GEPS
717 // left by replacing indirect buffer accesses.
718 pm->add(clspv::createSimplifyPointerBitcastPass());
alan-baker4217b322019-03-06 08:56:12 -0500719 // Run after DRA to clean up parameters and help reduce the need for variable
720 // pointers.
721 pm->add(clspv::createRemoveUnusedArgumentsPass());
alan-bakerfec0a472018-11-08 18:09:40 -0500722
723 pm->add(clspv::createSplatSelectConditionPass());
724 pm->add(clspv::createSignedCompareFixupPass());
725 // This pass generates insertions that need to be rewritten.
726 pm->add(clspv::createScalarizePass());
727 pm->add(clspv::createRewriteInsertsPass());
alan-bakera71f1932019-04-11 11:04:34 -0400728 // UBO Transformations
729 if (clspv::Option::ConstantArgsInUniformBuffer() &&
730 !clspv::Option::InlineEntryPoints()) {
731 // MultiVersionUBOFunctionsPass will examine non-kernel functions with UBO
732 // arguments and either multi-version them as necessary or inline them if
733 // multi-versioning cannot be accomplished.
734 pm->add(clspv::createMultiVersionUBOFunctionsPass());
735 // Cleanup passes.
736 // Specialization can blindly generate GEP chains that are easily cleaned up
737 // by SimplifyPointerBitcastPass.
738 pm->add(clspv::createSimplifyPointerBitcastPass());
739 // RemoveUnusedArgumentsPass removes the actual UBO arguments that were
740 // problematic to begin with now that they have no uses.
741 pm->add(clspv::createRemoveUnusedArgumentsPass());
742 // DCE cleans up callers of the specialized functions.
743 pm->add(llvm::createDeadCodeEliminationPass());
744 }
alan-bakerfec0a472018-11-08 18:09:40 -0500745 // This pass mucks with types to point where you shouldn't rely on DataLayout
746 // anymore so leave this right before SPIR-V generation.
747 pm->add(clspv::createUBOTypeTransformPass());
alan-baker00e7a582019-06-07 12:54:21 -0400748 pm->add(clspv::createSPIRVProducerPass(*binaryStream, descriptor_map_entries,
749 *SamplerMapEntries,
750 OutputFormat == "c"));
alan-bakerf5e5f692018-11-27 08:33:24 -0500751
752 return 0;
alan-bakerfec0a472018-11-08 18:09:40 -0500753}
alan-bakerfec0a472018-11-08 18:09:40 -0500754
Kévin Petitd5db2d22019-04-04 13:55:14 +0100755int ParseOptions(const int argc, const char *const argv[]) {
alan-bakerfec0a472018-11-08 18:09:40 -0500756 // We need to change how one of the called passes works by spoofing
757 // ParseCommandLineOptions with the specific option.
758 const int llvmArgc = 2;
759 const char *llvmArgv[llvmArgc] = {
alan-bakerf5e5f692018-11-27 08:33:24 -0500760 argv[0],
761 "-simplifycfg-sink-common=false",
alan-bakerfec0a472018-11-08 18:09:40 -0500762 };
763
Kévin Petitd5db2d22019-04-04 13:55:14 +0100764 llvm::cl::ResetAllOptionOccurrences();
alan-bakerfec0a472018-11-08 18:09:40 -0500765 llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
alan-bakerfec0a472018-11-08 18:09:40 -0500766 llvm::cl::ParseCommandLineOptions(argc, argv);
767
Kévin Petitf0515712020-01-07 18:29:20 +0000768 if (clspv::Option::LanguageUsesGenericAddressSpace() &&
769 !clspv::Option::InlineEntryPoints()) {
770 llvm::errs() << "cannot compile languages that use the generic address "
771 "space (e.g. CLC++, CL2.0) without -inline-entry-points\n";
Kévin Petit0fc88042019-04-09 23:25:02 +0100772 return -1;
773 }
774
Kévin Petitbbbda972020-03-03 19:16:31 +0000775 if (clspv::Option::ScalarBlockLayout()) {
776 llvm::errs() << "scalar block layout support unimplemented\n";
777 return -1;
778 }
779
Kévin Petitd5db2d22019-04-04 13:55:14 +0100780 return 0;
781}
Diego Novillo89500852019-04-15 08:45:10 -0400782
783int GenerateIRFile(llvm::legacy::PassManager *pm, llvm::Module &module,
784 std::string output) {
785 std::error_code ec;
786 std::unique_ptr<llvm::ToolOutputFile> out(
787 new llvm::ToolOutputFile(output, ec, llvm::sys::fs::F_None));
788 if (ec) {
789 llvm::errs() << output << ": " << ec.message() << '\n';
790 return -1;
791 }
792 pm->add(llvm::createPrintModulePass(out->os(), "", false));
793 pm->run(module);
794 out->keep();
795 return 0;
796}
797
Kévin Petitd5db2d22019-04-04 13:55:14 +0100798} // namespace
799
800namespace clspv {
801int Compile(const int argc, const char *const argv[]) {
802
803 if (auto error = ParseOptions(argc, argv))
804 return error;
805
alan-bakerfec0a472018-11-08 18:09:40 -0500806 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
alan-bakerf5e5f692018-11-27 08:33:24 -0500807 if (auto error = ParseSamplerMap("", &SamplerMapEntries))
alan-bakerfec0a472018-11-08 18:09:40 -0500808 return error;
809
810 // if no output file was provided, use a default
811 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
812
813 // If we are reading our input file from stdin.
814 if ("-" == InputFilename) {
815 // We need to overwrite the file name we use.
Kévin Petitddad8f42019-09-30 15:12:08 +0100816 switch (InputLanguage) {
817 case clang::Language::OpenCL:
818 overiddenInputFilename = "stdin.cl";
819 break;
820 case clang::Language::LLVM_IR:
821 overiddenInputFilename = "stdin.ll";
822 break;
alan-baker31298a62019-10-07 13:24:30 -0400823 default:
824 // Default to fix compiler warnings/errors. Option parsing will reject a
825 // bad enum value for the option so there is no need for a message.
826 return -1;
Kévin Petitddad8f42019-09-30 15:12:08 +0100827 }
alan-bakerfec0a472018-11-08 18:09:40 -0500828 }
829
830 clang::CompilerInstance instance;
Kévin Petitddad8f42019-09-30 15:12:08 +0100831 clang::FrontendInputFile kernelFile(overiddenInputFilename,
832 clang::InputKind(InputLanguage));
alan-bakerfec0a472018-11-08 18:09:40 -0500833 std::string log;
834 llvm::raw_string_ostream diagnosticsStream(log);
alan-bakerf5e5f692018-11-27 08:33:24 -0500835 if (auto error = SetCompilerInstanceOptions(
836 instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
alan-bakerfec0a472018-11-08 18:09:40 -0500837 return error;
838
839 // Parse.
840 llvm::LLVMContext context;
841 clang::EmitLLVMOnlyAction action(&context);
842
843 // Prepare the action for processing kernelFile
844 const bool success = action.BeginSourceFile(instance, kernelFile);
845 if (!success) {
846 return -1;
847 }
848
alan-bakerf3bce4a2019-06-28 16:01:15 -0400849 auto result = action.Execute();
alan-bakerfec0a472018-11-08 18:09:40 -0500850 action.EndSourceFile();
851
852 clang::DiagnosticConsumer *const consumer =
853 instance.getDiagnostics().getClient();
854 consumer->finish();
855
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100856 auto num_warnings = consumer->getNumWarnings();
alan-bakerfec0a472018-11-08 18:09:40 -0500857 auto num_errors = consumer->getNumErrors();
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100858 if ((num_errors > 0) || (num_warnings > 0)) {
859 llvm::errs() << log;
860 }
alan-bakerf3bce4a2019-06-28 16:01:15 -0400861 if (result || num_errors > 0) {
alan-bakerfec0a472018-11-08 18:09:40 -0500862 return -1;
863 }
864
Kévin Petit6b07cbe2019-04-02 21:52:16 +0100865 // Don't run the passes or produce any output in verify mode.
866 // Clang doesn't always produce a valid module.
867 if (verify) {
868 return 0;
869 }
870
alan-bakerfec0a472018-11-08 18:09:40 -0500871 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
872 llvm::initializeCore(Registry);
873 llvm::initializeScalarOpts(Registry);
Diego Novillo1fcff722019-05-07 13:45:53 -0400874 llvm::initializeClspvPasses(Registry);
alan-bakerfec0a472018-11-08 18:09:40 -0500875
876 std::unique_ptr<llvm::Module> module(action.takeModule());
877
878 // Optimize.
879 // Create a memory buffer for temporarily writing the result.
880 SmallVector<char, 10000> binary;
881 llvm::raw_svector_ostream binaryStream(binary);
882 std::string descriptor_map;
alan-bakerfec0a472018-11-08 18:09:40 -0500883 llvm::legacy::PassManager pm;
alan-bakerf5e5f692018-11-27 08:33:24 -0500884 std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
Diego Novillo89500852019-04-15 08:45:10 -0400885
886 // If --emit-ir was requested, emit the initial LLVM IR and stop compilation.
887 if (!IROutputFile.empty()) {
888 return GenerateIRFile(&pm, *module, IROutputFile);
889 }
890
891 // Otherwise, populate the pass manager and run the regular passes.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400892 if (auto error = PopulatePassManager(
893 &pm, &binaryStream, &descriptor_map_entries, &SamplerMapEntries))
alan-bakerf5e5f692018-11-27 08:33:24 -0500894 return error;
alan-bakerfec0a472018-11-08 18:09:40 -0500895 pm.run(*module);
896
897 // Write outputs
898
899 // Write the descriptor map, if requested.
900 std::error_code error;
901 if (!DescriptorMapFilename.empty()) {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400902 llvm::raw_fd_ostream descriptor_map_out_fd(
903 DescriptorMapFilename, error, llvm::sys::fs::CD_CreateAlways,
904 llvm::sys::fs::FA_Write, llvm::sys::fs::F_Text);
alan-bakerfec0a472018-11-08 18:09:40 -0500905 if (error) {
906 llvm::errs() << "Unable to open descriptor map file '"
907 << DescriptorMapFilename << "': " << error.message() << '\n';
908 return -1;
909 }
alan-bakerf5e5f692018-11-27 08:33:24 -0500910 std::string descriptor_map_string;
911 std::ostringstream str(descriptor_map_string);
912 for (const auto &entry : descriptor_map_entries) {
913 str << entry << "\n";
914 }
915 descriptor_map_out_fd << str.str();
alan-bakerfec0a472018-11-08 18:09:40 -0500916 descriptor_map_out_fd.close();
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 if (OutputFilename.empty()) {
Kévin Petite4786902019-04-02 21:51:47 +0100923 if (OutputFormat == "c") {
alan-bakerfec0a472018-11-08 18:09:40 -0500924 OutputFilename = "a.spvinc";
925 } else {
926 OutputFilename = "a.spv";
927 }
928 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400929 llvm::raw_fd_ostream outStream(OutputFilename, error,
930 llvm::sys::fs::FA_Write);
alan-bakerfec0a472018-11-08 18:09:40 -0500931
932 if (error) {
933 llvm::errs() << "Unable to open output file '" << OutputFilename
934 << "': " << error.message() << '\n';
935 return -1;
936 }
937 outStream << binaryStream.str();
938
939 return 0;
940}
alan-bakerf5e5f692018-11-27 08:33:24 -0500941
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400942int CompileFromSourceString(
943 const std::string &program, const std::string &sampler_map,
944 const std::string &options, std::vector<uint32_t> *output_binary,
945 std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500946
947 llvm::SmallVector<const char *, 20> argv;
948 llvm::BumpPtrAllocator A;
949 llvm::StringSaver Saver(A);
950 argv.push_back(Saver.save("clspv").data());
951 llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
952 int argc = static_cast<int>(argv.size());
Kévin Petitd5db2d22019-04-04 13:55:14 +0100953
954 if (auto error = ParseOptions(argc, &argv[0]))
955 return error;
alan-bakerf5e5f692018-11-27 08:33:24 -0500956
957 llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
958 if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
959 return error;
960
961 InputFilename = "source.cl";
962 llvm::StringRef overiddenInputFilename = InputFilename.getValue();
963
964 clang::CompilerInstance instance;
alan-bakerd354f1a2019-08-06 15:41:55 -0400965 clang::FrontendInputFile kernelFile(
966 overiddenInputFilename, clang::InputKind(clang::Language::OpenCL));
alan-bakerf5e5f692018-11-27 08:33:24 -0500967 std::string log;
968 llvm::raw_string_ostream diagnosticsStream(log);
969 if (auto error =
970 SetCompilerInstanceOptions(instance, overiddenInputFilename,
971 kernelFile, program, &diagnosticsStream))
972 return error;
973
974 // Parse.
975 llvm::LLVMContext context;
976 clang::EmitLLVMOnlyAction action(&context);
977
978 // Prepare the action for processing kernelFile
979 const bool success = action.BeginSourceFile(instance, kernelFile);
980 if (!success) {
981 return -1;
982 }
983
alan-bakerf3bce4a2019-06-28 16:01:15 -0400984 auto result = action.Execute();
alan-bakerf5e5f692018-11-27 08:33:24 -0500985 action.EndSourceFile();
986
987 clang::DiagnosticConsumer *const consumer =
988 instance.getDiagnostics().getClient();
989 consumer->finish();
990
991 auto num_errors = consumer->getNumErrors();
alan-bakerf3bce4a2019-06-28 16:01:15 -0400992 if (result || num_errors > 0) {
alan-bakerf5e5f692018-11-27 08:33:24 -0500993 llvm::errs() << log << "\n";
994 return -1;
995 }
996
alan-bakerf5e5f692018-11-27 08:33:24 -0500997 llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
998 llvm::initializeCore(Registry);
999 llvm::initializeScalarOpts(Registry);
Diego Novillo1fcff722019-05-07 13:45:53 -04001000 llvm::initializeClspvPasses(Registry);
alan-bakerf5e5f692018-11-27 08:33:24 -05001001
1002 std::unique_ptr<llvm::Module> module(action.takeModule());
1003
1004 // Optimize.
1005 // Create a memory buffer for temporarily writing the result.
1006 SmallVector<char, 10000> binary;
1007 llvm::raw_svector_ostream binaryStream(binary);
1008 std::string descriptor_map;
1009 llvm::legacy::PassManager pm;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -04001010 if (auto error = PopulatePassManager(
1011 &pm, &binaryStream, descriptor_map_entries, &SamplerMapEntries))
alan-bakerf5e5f692018-11-27 08:33:24 -05001012 return error;
1013 pm.run(*module);
1014
1015 // Write outputs
1016
1017 // Write the descriptor map. This is required.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -04001018 assert(descriptor_map_entries &&
1019 "Valid descriptor map container is required.");
alan-bakerf5e5f692018-11-27 08:33:24 -05001020 if (!DescriptorMapFilename.empty()) {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -04001021 llvm::errs() << "Warning: -descriptormap is ignored descriptor map "
1022 "container is provided.\n";
alan-bakerf5e5f692018-11-27 08:33:24 -05001023 }
1024
1025 // Write the resulting binary.
1026 // Wait until now to try writing the file so that we only write it on
1027 // successful compilation.
1028 assert(output_binary && "Valid binary container is required.");
1029 if (!OutputFilename.empty()) {
1030 llvm::outs()
1031 << "Warning: -o is ignored when binary container is provided.\n";
1032 }
1033 output_binary->resize(binary.size() / 4);
1034 memcpy(output_binary->data(), binary.data(), binary.size());
1035
1036 return 0;
1037}
alan-bakerfec0a472018-11-08 18:09:40 -05001038} // namespace clspv