Intel® Implicit SPMD Program Compiler (Intel® ISPC)  1.13.0
target_registry.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2019-2020, Intel Corporation
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are
7  met:
8 
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in the
14  documentation and/or other materials provided with the distribution.
15 
16  * Neither the name of Intel Corporation nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
25  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 /** @file target_registry.h
35  @brief Registry to handle bitcode libraries.
36 */
37 
38 #include <numeric>
39 #include <string>
40 #include <vector>
41 
42 #include "target_registry.h"
43 #include "util.h"
44 
45 // Returns number of bits required to store this value
46 static constexpr uint32_t bits_required(uint32_t x) {
47  int msb_position = 0;
48  while (x > 0) {
49  x = x >> 1;
50  msb_position++;
51  }
52  return msb_position;
53 }
54 
55 class Triple {
56  // Encode values |0000|arch|os|target|
57  static constexpr uint32_t target_width = bits_required((uint32_t)ISPCTarget::error);
58  static constexpr uint32_t os_width = bits_required((uint32_t)TargetOS::error);
59  static constexpr uint32_t arch_width = bits_required((uint32_t)Arch::error);
60  static_assert(target_width + os_width + arch_width <= 32, "Too large value to encode");
61  static constexpr uint32_t target_mask = (1 << target_width) - 1;
62  static constexpr uint32_t os_mask = ((1 << os_width) - 1) << target_width;
63  static constexpr uint32_t arch_mask = ((1 << arch_width) - 1) << (target_width + os_width);
64 
65  public:
69 
70  Triple(uint32_t encoding) {
71  m_target = (ISPCTarget)(encoding & target_mask);
72  m_os = (TargetOS)((encoding & os_mask) >> target_width);
73  m_arch = (Arch)((encoding & arch_mask) >> (target_width + os_width));
74  };
75 
76  Triple(ISPCTarget target, TargetOS os, Arch arch) : m_target(target), m_os(os), m_arch(arch){};
77 
78  uint32_t encode() const {
79  uint32_t result = (uint32_t)m_arch;
80  result = (result << os_width) + (uint32_t)m_os;
81  result = (result << target_width) + (uint32_t)m_target;
82  return result;
83  };
84 };
85 
86 std::vector<const BitcodeLib *> *TargetLibRegistry::libs = nullptr;
87 
89  // TODO: sort before adding - to canonicalize.
90  // TODO: check for conflicts / duplicates.
91  m_dispatch = NULL;
92  for (auto lib : *libs) {
93  switch (lib->getType()) {
95  m_dispatch = lib;
96  break;
98  m_builtins[Triple(lib->getISPCTarget(), lib->getOS(), lib->getArch()).encode()] = lib;
99  m_supported_oses[(int)lib->getOS()] = true;
100  // "custom_linux" target is regular "linux" target for ARM with a few tweaks.
101  // So, create it as an alias.
102  if (lib->getOS() == TargetOS::linux && (lib->getArch() == Arch::arm || lib->getArch() == Arch::aarch64)) {
103  m_builtins[Triple(lib->getISPCTarget(), TargetOS::custom_linux, lib->getArch()).encode()] = lib;
104  m_supported_oses[(int)TargetOS::custom_linux] = true;
105  }
106  break;
108  m_targets[Triple(lib->getISPCTarget(), lib->getOS(), lib->getArch()).encode()] = lib;
109  // "custom_linux" target is regular "linux" target for ARM with a few tweaks.
110  // So, create it as an alias.
111  if (lib->getOS() == TargetOS::linux && (lib->getArch() == Arch::arm || lib->getArch() == Arch::aarch64)) {
112  m_targets[Triple(lib->getISPCTarget(), TargetOS::custom_linux, lib->getArch()).encode()] = lib;
113  }
114  break;
115  }
116  }
117 }
118 
120  if (!libs) {
121  libs = new std::vector<const BitcodeLib *>();
122  }
123  libs->push_back(lib);
124 }
125 
127  static TargetLibRegistry *reg = new TargetLibRegistry();
128  return reg;
129 }
130 
131 const BitcodeLib *TargetLibRegistry::getDispatchLib() const { return m_dispatch; }
132 
134  auto result = m_builtins.find(Triple(ISPCTarget::none, os, arch).encode());
135  if (result != m_builtins.end()) {
136  return result->second;
137  }
138  return nullptr;
139 }
141  // TODO: validate parameters not to be errors or forbidden values.
142 
143  // This is an alias. It might be a good idea generalize this.
144  if (target == ISPCTarget::avx1_i32x4) {
145  target = ISPCTarget::sse4_i32x4;
146  }
147 
148  // Canonicalize OS, as for the target we only differentiate between Windows, Unix, and Web (WASM target).
149  switch (os) {
150  case TargetOS::windows:
151  case TargetOS::web:
152  // Keep these values.
153  break;
154  case TargetOS::linux:
156  case TargetOS::freebsd:
157  case TargetOS::macos:
158  case TargetOS::android:
159  case TargetOS::ios:
160  case TargetOS::ps4:
161  os = TargetOS::linux;
162  break;
163  case TargetOS::error:
164  UNREACHABLE();
165  }
166 
167  auto result = m_targets.find(Triple(target, os, arch).encode());
168  if (result != m_targets.end()) {
169  return result->second;
170  }
171  return nullptr;
172 }
173 
174 // Print user-friendly message about supported targets
176  // Vector of rows, which are vectors of cells.
177  std::vector<std::vector<std::string>> table;
178 
179  // OS names row
180  std::vector<std::string> os_names;
181  os_names.push_back("");
182  for (int j = (int)TargetOS::windows; j < (int)TargetOS::error; j++) {
183  os_names.push_back(OSToString((TargetOS)j));
184  }
185  table.push_back(os_names);
186 
187  // Fill in the name, one target per the row.
188  for (int i = (int)ISPCTarget::sse2_i32x4; i < (int)ISPCTarget::error; i++) {
189  std::vector<std::string> row;
190  ISPCTarget target = (ISPCTarget)i;
191  row.push_back(ISPCTargetToString(target));
192  std::vector<std::string> arch_list_target;
193  // Fill in cell: list of arches for the target/os.
194  for (int j = (int)TargetOS::windows; j < (int)TargetOS::error; j++) {
195  std::string arch_list_os;
196  TargetOS os = (TargetOS)j;
197  for (int k = (int)Arch::none; k < (int)Arch::error; k++) {
198  Arch arch = (Arch)k;
199  if (isSupported(target, os, arch)) {
200  if (!arch_list_os.empty()) {
201  arch_list_os += ", ";
202  }
203  arch_list_os += ArchToString(arch);
204  }
205  }
206  arch_list_target.push_back(arch_list_os);
207  row.push_back(arch_list_os);
208  }
209  table.push_back(row);
210  }
211 
212  // Collect maximum sizes for all columns
213  std::vector<int> column_sizes(table[0].size(), 7);
214  for (auto &row : table) {
215  for (int i = 0; i < row.size(); i++) {
216  column_sizes[i] = column_sizes[i] > row[i].size() ? column_sizes[i] : row[i].size();
217  }
218  }
219  int width = std::accumulate(column_sizes.begin(), column_sizes.end(), 0) + (column_sizes.size() - 1) * 3;
220 
221  // Print the table
222  for (int i = 0; i < table.size(); i++) {
223  auto row = table[i];
224  for (int j = 0; j < row.size(); j++) {
225  auto align = std::string(column_sizes[j] - row[j].size(), ' ');
226  printf("%s%s", row[j].c_str(), align.c_str());
227  if (j + 1 != row.size()) {
228  printf(" | ");
229  }
230  }
231  printf("\n");
232  if (i == 0) {
233  auto line = std::string(width, '-');
234  printf("%s\n", line.c_str());
235  }
236  }
237 }
238 
240  std::string archs;
241  for (int k = (int)Arch::none; k < (int)Arch::error; k++) {
242  Arch arch = (Arch)k;
243  for (int i = (int)ISPCTarget::sse2_i32x4; i < (int)ISPCTarget::error; i++) {
244  ISPCTarget target = (ISPCTarget)i;
245  for (int j = (int)TargetOS::windows; j < (int)TargetOS::error; j++) {
246  TargetOS os = (TargetOS)j;
247 
248  if (isSupported(target, os, arch)) {
249  if (!archs.empty()) {
250  archs += ", ";
251  }
252  archs += ArchToString(arch);
253  goto next_arch;
254  }
255  }
256  }
257  next_arch:;
258  }
259 
260  return archs;
261 }
262 
264  std::string targets;
265  for (int i = (int)ISPCTarget::sse2_i32x4; i < (int)ISPCTarget::error; i++) {
266  ISPCTarget target = (ISPCTarget)i;
267  for (int j = (int)TargetOS::windows; j < (int)TargetOS::error; j++) {
268  TargetOS os = (TargetOS)j;
269  for (int k = (int)Arch::none; k < (int)Arch::error; k++) {
270  Arch arch = (Arch)k;
271  if (isSupported(target, os, arch)) {
272  if (!targets.empty()) {
273  targets += ", ";
274  }
275  targets += ISPCTargetToString(target);
276  goto next_target;
277  }
278  }
279  }
280  next_target:;
281  }
282 
283  return targets;
284 }
285 
287  // We use pre-computed bitset, as this function is perfomance critical - it's used
288  // during arguments parsing.
289  std::string oses;
290  for (int j = (int)TargetOS::windows; j < (int)TargetOS::error; j++) {
291  TargetOS os = (TargetOS)j;
292  if (m_supported_oses[j]) {
293  if (!oses.empty()) {
294  oses += ", ";
295  }
296  oses += OSToLowerString(os);
297  }
298  }
299 
300  return oses;
301 }
302 
304  auto clib = getBuiltinsCLib(os, arch);
305  if (clib) {
306  auto lib = getISPCTargetLib(target, os, arch);
307  if (lib) {
308  return true;
309  }
310  }
311  return false;
312 }
static constexpr uint32_t os_width
std::string ArchToString(Arch arch)
ISPCTarget
Definition: target_enums.h:55
static constexpr uint32_t arch_width
TargetOS
Definition: target_enums.h:43
static constexpr uint32_t arch_mask
Registry to handle bitcode libraries.
const BitcodeLib * getISPCTargetLib(ISPCTarget target, TargetOS os, Arch arch) const
bool isSupported(ISPCTarget target, TargetOS os, Arch arch) const
std::string getSupportedTargets()
Triple(uint32_t encoding)
#define UNREACHABLE()
Definition: util.h:110
static constexpr uint32_t target_width
ISPCTarget m_target
std::string getSupportedOSes()
std::string OSToString(TargetOS os)
static std::vector< const BitcodeLib * > * libs
static TargetLibRegistry * getTargetLibRegistry()
static constexpr uint32_t target_mask
void printSupportMatrix() const
std::string ISPCTargetToString(ISPCTarget target)
const BitcodeLib * getBuiltinsCLib(TargetOS os, Arch arch) const
static void RegisterTarget(const BitcodeLib *lib)
static constexpr uint32_t os_mask
TargetOS m_os
std::string OSToLowerString(TargetOS os)
Triple(ISPCTarget target, TargetOS os, Arch arch)
uint32_t encode() const
const BitcodeLib * getDispatchLib() const
static constexpr uint32_t bits_required(uint32_t x)
Arch
Definition: target_enums.h:50
std::string getSupportedArchs()