DPC++ Runtime
Runtime libraries for oneAPI DPC++
online_compiler.cpp
Go to the documentation of this file.
1 //==----------- online_compiler.cpp ----------------------------------------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include <sycl/detail/pi.hpp>
12 
13 #include <cstring>
14 
15 #include "ocloc_api.h"
16 
17 namespace sycl {
18 inline namespace _V1 {
19 namespace ext::intel::experimental {
20 namespace detail {
21 
22 static std::vector<const char *>
24  bool Is64Bit, const std::string &DeviceStepping,
25  const std::string &UserArgs) {
26  std::vector<const char *> Args = {"ocloc", "-q", "-spv_only", "-device"};
27 
28  if (DeviceType == sycl::info::device_type::gpu) {
29  switch (DeviceArch) {
31  Args.push_back("skl");
32  break;
33 
35  Args.push_back("cfl");
36  break;
37 
39  Args.push_back("icllp");
40  break;
41 
43  Args.push_back("tgllp");
44  break;
45 
46  default:
47  Args.push_back("tgllp");
48  }
49  } else {
50  // TODO: change that to generic device when ocloc adds support for it.
51  // For now "tgllp" is used as the option supported on all known GPU RT.
52  Args.push_back("tgllp");
53  }
54 
55  if (DeviceStepping != "") {
56  Args.push_back("-revision_id");
57  Args.push_back(DeviceStepping.c_str());
58  }
59 
60  Args.push_back(Is64Bit ? "-64" : "-32");
61 
62  if (UserArgs != "") {
63  Args.push_back("-options");
64  Args.push_back(UserArgs.c_str());
65  }
66 
67  return Args;
68 }
69 
84 static std::vector<byte>
85 compileToSPIRV(const std::string &Source, sycl::info::device_type DeviceType,
86  device_arch DeviceArch, bool Is64Bit,
87  const std::string &DeviceStepping, void *&CompileToSPIRVHandle,
88  void *&FreeSPIRVOutputsHandle,
89  const std::vector<std::string> &UserArgs) {
90 
91  if (!CompileToSPIRVHandle) {
92 #ifdef __SYCL_RT_OS_WINDOWS
93  static const std::string OclocLibraryName = "ocloc64.dll";
94 #else
95  static const std::string OclocLibraryName = "libocloc.so";
96 #endif
97  void *OclocLibrary = sycl::detail::pi::loadOsLibrary(OclocLibraryName);
98  if (!OclocLibrary)
99  throw online_compile_error("Cannot load ocloc library: " +
100  OclocLibraryName);
101  void *OclocVersionHandle =
103  // The initial versions of ocloc library did not have the oclocVersion()
104  // function. Those versions had the same API as the first version of ocloc
105  // library having that oclocVersion() function.
106  int LoadedVersion = ocloc_version_t::OCLOC_VERSION_1_0;
107  if (OclocVersionHandle) {
108  decltype(::oclocVersion) *OclocVersionFunc =
109  reinterpret_cast<decltype(::oclocVersion) *>(OclocVersionHandle);
110  LoadedVersion = OclocVersionFunc();
111  }
112  // The loaded library with version (A.B) is compatible with expected API/ABI
113  // version (X.Y) used here if A == B and B >= Y.
114  int LoadedVersionMajor = LoadedVersion >> 16;
115  int LoadedVersionMinor = LoadedVersion & 0xffff;
116  int CurrentVersionMajor = ocloc_version_t::OCLOC_VERSION_CURRENT >> 16;
117  int CurrentVersionMinor = ocloc_version_t::OCLOC_VERSION_CURRENT & 0xffff;
118  if (LoadedVersionMajor != CurrentVersionMajor ||
119  LoadedVersionMinor < CurrentVersionMinor)
120  throw online_compile_error(
121  std::string("Found incompatible version of ocloc library: (") +
122  std::to_string(LoadedVersionMajor) + "." +
123  std::to_string(LoadedVersionMinor) +
124  "). The supported versions are (" +
125  std::to_string(CurrentVersionMajor) +
126  ".N), where (N >= " + std::to_string(CurrentVersionMinor) + ").");
127 
128  CompileToSPIRVHandle =
130  if (!CompileToSPIRVHandle)
131  throw online_compile_error("Cannot load oclocInvoke() function");
132  FreeSPIRVOutputsHandle = sycl::detail::pi::getOsLibraryFuncAddress(
133  OclocLibrary, "oclocFreeOutput");
134  if (!FreeSPIRVOutputsHandle)
135  throw online_compile_error("Cannot load oclocFreeOutput() function");
136  }
137 
138  std::string CombinedUserArgs;
139  for (auto UserArg : UserArgs) {
140  if (UserArg == "")
141  continue;
142  if (CombinedUserArgs != "")
143  CombinedUserArgs = CombinedUserArgs + " " + UserArg;
144  else
145  CombinedUserArgs = UserArg;
146  }
147  std::vector<const char *> Args = detail::prepareOclocArgs(
148  DeviceType, DeviceArch, Is64Bit, DeviceStepping, CombinedUserArgs);
149 
150  uint32_t NumOutputs = 0;
151  byte **Outputs = nullptr;
152  uint64_t *OutputLengths = nullptr;
153  char **OutputNames = nullptr;
154 
155  const byte *Sources[] = {reinterpret_cast<const byte *>(Source.c_str())};
156  const char *SourceName = "main.cl";
157  const uint64_t SourceLengths[] = {Source.length() + 1};
158 
159  Args.push_back("-file");
160  Args.push_back(SourceName);
161 
162  decltype(::oclocInvoke) *OclocInvokeFunc =
163  reinterpret_cast<decltype(::oclocInvoke) *>(CompileToSPIRVHandle);
164  int CompileError =
165  OclocInvokeFunc(Args.size(), Args.data(), 1, Sources, SourceLengths,
166  &SourceName, 0, nullptr, nullptr, nullptr, &NumOutputs,
167  &Outputs, &OutputLengths, &OutputNames);
168 
169  std::vector<byte> SpirV;
170  std::string CompileLog;
171  for (uint32_t I = 0; I < NumOutputs; I++) {
172  size_t NameLen = strlen(OutputNames[I]);
173  if (NameLen >= 4 && strstr(OutputNames[I], ".spv") != nullptr &&
174  Outputs[I] != nullptr) {
175  assert(SpirV.size() == 0 && "More than one SPIR-V output found.");
176  SpirV = std::vector<byte>(Outputs[I], Outputs[I] + OutputLengths[I]);
177  } else if (!strcmp(OutputNames[I], "stdout.log")) {
178  CompileLog = std::string(reinterpret_cast<const char *>(Outputs[I]));
179  }
180  }
181 
182  // Try to free memory before reporting possible error.
183  decltype(::oclocFreeOutput) *OclocFreeOutputFunc =
184  reinterpret_cast<decltype(::oclocFreeOutput) *>(FreeSPIRVOutputsHandle);
185  int MemFreeError =
186  OclocFreeOutputFunc(&NumOutputs, &Outputs, &OutputLengths, &OutputNames);
187 
188  if (CompileError)
189  throw online_compile_error("ocloc reported compilation errors: {\n" +
190  CompileLog + "\n}");
191  if (SpirV.empty())
192  throw online_compile_error(
193  "Unexpected output: ocloc did not return SPIR-V");
194  if (MemFreeError)
195  throw online_compile_error("ocloc cannot safely free resources");
196 
197  return SpirV;
198 }
199 } // namespace detail
200 
201 template <>
202 template <>
203 __SYCL_EXPORT std::vector<byte>
205  const std::string &Source, const std::vector<std::string> &UserArgs) {
206 
207  if (OutputFormatVersion != std::pair<int, int>{0, 0}) {
208  std::string Version = std::to_string(OutputFormatVersion.first) + ", " +
209  std::to_string(OutputFormatVersion.second);
210  throw online_compile_error(std::string("The output format version (") +
211  Version + ") is not supported yet");
212  }
213 
214  return detail::compileToSPIRV(Source, DeviceType, DeviceArch, Is64Bit,
215  DeviceStepping, CompileToSPIRVHandle,
216  FreeSPIRVOutputsHandle, UserArgs);
217 }
218 
219 template <>
220 template <>
221 __SYCL_EXPORT std::vector<byte> online_compiler<source_language::cm>::compile(
222  const std::string &Source, const std::vector<std::string> &UserArgs) {
223 
224  if (OutputFormatVersion != std::pair<int, int>{0, 0}) {
225  std::string Version = std::to_string(OutputFormatVersion.first) + ", " +
226  std::to_string(OutputFormatVersion.second);
227  throw online_compile_error(std::string("The output format version (") +
228  Version + ") is not supported yet");
229  }
230 
231  std::vector<std::string> CMUserArgs = UserArgs;
232  CMUserArgs.push_back("-cmc");
233  return detail::compileToSPIRV(Source, DeviceType, DeviceArch, Is64Bit,
234  DeviceStepping, CompileToSPIRVHandle,
235  FreeSPIRVOutputsHandle, CMUserArgs);
236 }
237 } // namespace ext::intel::experimental
238 
239 namespace ext {
240 namespace __SYCL2020_DEPRECATED(
241  "use 'ext::intel::experimental' instead") intel {
242 using namespace ext::intel::experimental;
243 }
244 } // namespace ext
245 
246 namespace __SYCL2020_DEPRECATED(
247  "use 'ext::intel::experimental' instead") INTEL {
248 using namespace ext::intel::experimental;
249 }
250 } // namespace _V1
251 } // namespace sycl
void * loadOsLibrary(const std::string &Library)
Definition: posix_pi.cpp:20
void * getOsLibraryFuncAddress(void *Library, const std::string &FunctionName)
Definition: posix_pi.cpp:47
static std::vector< byte > compileToSPIRV(const std::string &Source, sycl::info::device_type DeviceType, device_arch DeviceArch, bool Is64Bit, const std::string &DeviceStepping, void *&CompileToSPIRVHandle, void *&FreeSPIRVOutputsHandle, const std::vector< std::string > &UserArgs)
Compiles the given source Source to SPIR-V IL and returns IL as a vector of bytes.
static std::vector< const char * > prepareOclocArgs(sycl::info::device_type DeviceType, device_arch DeviceArch, bool Is64Bit, const std::string &DeviceStepping, const std::string &UserArgs)
namespace __SYCL2020_DEPRECATED("use 'ext::intel::experimental' instead") intel
kernel_bundle< bundle_state::object > compile(const kernel_bundle< bundle_state::input > &InputBundle, const std::vector< device > &Devs, const property_list &PropList={})
std::uint8_t instead
Definition: aliases.hpp:93
Definition: access.hpp:18
SIGNATURE oclocVersion()
Returns the current version of ocloc.
SIGNATURE oclocInvoke(uint32_t NumArgs, const char *Argv[], uint32_t NumSources, const uint8_t **DataSources, const uint64_t *LenSources, const char **NameSources, uint32_t NumInputHeaders, const uint8_t **DataInputHeaders, const uint64_t *LenInputHeaders, const char **NameInputHeaders, uint32_t *NumOutputs, uint8_t ***DataOutputs, uint64_t **LenOutputs, char ***NameOutputs)
Invokes ocloc API using C interface.
@ OCLOC_VERSION_CURRENT
latest known version
Definition: ocloc_api.h:22
@ OCLOC_VERSION_1_0
version 1.0
Definition: ocloc_api.h:21
SIGNATURE oclocFreeOutput(uint32_t *NumOutputs, uint8_t ***DataOutputs, uint64_t **LenOutputs, char ***NameOutputs)
Frees results of oclocInvoke.
C++ wrapper of extern "C" PI interfaces.