1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4 
5 /////////////////////////////////////////////////////////////////////////
6 // \file
7 // platform.h is where we put all the platform-specific macros.
8 // Things like:
9 //
10 //   * Detecting which compiler is being used.
11 //   * Detecting which C++ standard is being used and what features are
12 //     available.
13 //   * Various helpers that need to be defined differently per compiler,
14 //     language version, OS, etc.
15 /////////////////////////////////////////////////////////////////////////
16 
17 // clang-format off
18 
19 #pragma once
20 
21 #include <memory>
22 
23 #include <OSL/oslversion.h>
24 
25 
26 /////////////////////////////////////////////////////////////////////////
27 // Detect which compiler and version we're using
28 
29 
30 // Define OSL_GNUC_VERSION to hold an encoded gcc version (e.g. 40802 for
31 // 4.8.2), or 0 if not a GCC release. N.B.: This will be 0 for clang.
32 #if defined(__GNUC__) && !defined(__clang__)
33 #  define OSL_GNUC_VERSION (10000*__GNUC__ + 100*__GNUC_MINOR__ + __GNUC_PATCHLEVEL__)
34 #else
35 #  define OSL_GNUC_VERSION 0
36 #endif
37 
38 // Define OSL_CLANG_VERSION to hold an encoded generic Clang version (e.g.
39 // 30402 for clang 3.4.2), or 0 if not a generic Clang release.
40 // N.B. This will be 0 for the clang Apple distributes (which has different
41 // version numbers entirely).
42 #if defined(__clang__) && !defined(__apple_build_version__)
43 #  define OSL_CLANG_VERSION (10000*__clang_major__ + 100*__clang_minor__ + __clang_patchlevel__)
44 #else
45 #  define OSL_CLANG_VERSION 0
46 #endif
47 
48 // Define OSL_APPLE_CLANG_VERSION to hold an encoded Apple Clang version
49 // (e.g. 70002 for clang 7.0.2), or 0 if not an Apple Clang release.
50 #if defined(__clang__) && defined(__apple_build_version__)
51 #  define OSL_APPLE_CLANG_VERSION (10000*__clang_major__ + 100*__clang_minor__ + __clang_patchlevel__)
52 #else
53 #  define OSL_APPLE_CLANG_VERSION 0
54 #endif
55 
56 // Define OSL_INTEL_COMPILER to hold an encoded Intel compiler version
57 // (e.g. 1900), or 0 if not an Intel compiler.
58 #if defined(__INTEL_COMPILER)
59 #  define OSL_INTEL_COMPILER __INTEL_COMPILER
60 #else
61 #  define OSL_INTEL_COMPILER 0
62 #endif
63 
64 // Intel's compiler on OSX may still define __clang__
65 // and we have need to know when using a true clang compiler
66 #if !defined(__INTEL_COMPILER) && defined(__clang__)
67     #define OSL_NON_INTEL_CLANG  __clang__
68 #else
69 	#define OSL_NON_INTEL_CLANG  0
70 #endif
71 
72 // Tests for MSVS versions, always 0 if not MSVS at all.
73 #if defined(_MSC_VER)
74 #  if _MSC_VER < 1900
75 #    error "This version of OSL is meant to work only with Visual Studio 2015 or later"
76 #  endif
77 #  define OSL_MSVS_AT_LEAST_2013 (_MSC_VER >= 1800)
78 #  define OSL_MSVS_BEFORE_2013   (_MSC_VER <  1800)
79 #  define OSL_MSVS_AT_LEAST_2015 (_MSC_VER >= 1900)
80 #  define OSL_MSVS_BEFORE_2015   (_MSC_VER <  1900)
81 #  define OSL_MSVS_AT_LEAST_2017 (_MSC_VER >= 1910)
82 #  define OSL_MSVS_BEFORE_2017   (_MSC_VER <  1910)
83 #else
84 #  define OSL_MSVS_AT_LEAST_2013 0
85 #  define OSL_MSVS_BEFORE_2013   0
86 #  define OSL_MSVS_AT_LEAST_2015 0
87 #  define OSL_MSVS_BEFORE_2015   0
88 #  define OSL_MSVS_AT_LEAST_2017 0
89 #  define OSL_MSVS_BEFORE_2017   0
90 #endif
91 
92 
93 // Note about __CUDACC__ versus __CUDA_ARCH__:
94 //
95 // __CUDACC__     defined any time we are compiling a module for Cuda
96 //                (both for the host pass and the device pass). "Do this
97 //                when using nvcc or clang with ptx target."
98 // __CUDA_ARCH__  is only defined when doing the device pass. "Do this only
99 //                for code that will actually run on the GPU."
100 
101 
102 /////////////////////////////////////////////////////////////////////////
103 // Detect which C++ standard we're using, and handy macros.
104 // See https://en.cppreference.com/w/cpp/compiler_support
105 //
106 // Note: oslversion.h defines OSL_BUILD_CPP11 to be 1 if OSL was built
107 // using C++11. In contrast, OSL_CPLUSPLUS_VERSION defined below will be set
108 // to the right number for the C++ standard being compiled RIGHT NOW. These
109 // two things may be the same when compiling OSL, but they may not be the
110 // same if another package is compiling against OSL and using these headers
111 // (OSL may be C++11 but the client package may be newer, or vice versa --
112 // use these two symbols to differentiate these cases, when important).
113 #if (__cplusplus >= 202001L)
114 #    define OSL_CPLUSPLUS_VERSION 20
115 #    define OSL_CONSTEXPR14 constexpr
116 #    define OSL_CONSTEXPR17 constexpr
117 #    define OSL_CONSTEXPR20 constexpr
118 #elif (__cplusplus >= 201703L)
119 #    define OSL_CPLUSPLUS_VERSION 17
120 #    define OSL_CONSTEXPR14 constexpr
121 #    define OSL_CONSTEXPR17 constexpr
122 #    define OSL_CONSTEXPR20 /* not constexpr before C++20 */
123 #elif (__cplusplus >= 201402L) || (defined(_MSC_VER) && _MSC_VER >= 1914)
124 #    define OSL_CPLUSPLUS_VERSION 14
125 #    define OSL_CONSTEXPR14 constexpr
126 #    define OSL_CONSTEXPR17 /* not constexpr before C++17 */
127 #    define OSL_CONSTEXPR20 /* not constexpr before C++20 */
128 #elif (__cplusplus >= 201103L) || _MSC_VER >= 1900
129 #    define OSL_CPLUSPLUS_VERSION 11
130 #    define OSL_CONSTEXPR14 /* not constexpr before C++14 */
131 #    define OSL_CONSTEXPR17 /* not constexpr before C++17 */
132 #    define OSL_CONSTEXPR20 /* not constexpr before C++20 */
133 #else
134 #    error "This version of OSL requires C++11"
135 #endif
136 
137 
138 
139 // In C++20 (and some compilers before that), __has_cpp_attribute can
140 // test for understand of [[attr]] tests.
141 #ifndef __has_cpp_attribute
142 #    define __has_cpp_attribute(x) 0
143 #endif
144 
145 // On gcc & clang, __has_attribute can test for __attribute__((attr))
146 #ifndef __has_attribute
147 #    define __has_attribute(x) 0
148 #endif
149 
150 // In C++17 (and some compilers before that), __has_include("blah.h") or
151 // __has_include(<blah.h>) can test for presence of an include file.
152 #ifndef __has_include
153 #    define __has_include(x) 0
154 #endif
155 
156 
157 
158 /////////////////////////////////////////////////////////////////////////
159 // Pragmas and attributes that vary per platform
160 
161 // Generic pragma definition
162 #if defined(_MSC_VER)
163     // Of course MS does it in a quirky way
164     #define OSL_PRAGMA(UnQuotedPragma) __pragma(UnQuotedPragma)
165 #else
166     // All other compilers seem to support C99 _Pragma
167     #define OSL_PRAGMA(UnQuotedPragma) _Pragma(#UnQuotedPragma)
168 #endif
169 
170 // Compiler-specific pragmas
171 #if defined(__GNUC__) /* gcc, clang, icc */
172 #    define OSL_PRAGMA_WARNING_PUSH    OSL_PRAGMA(GCC diagnostic push)
173 #    define OSL_PRAGMA_WARNING_POP     OSL_PRAGMA(GCC diagnostic pop)
174 #    define OSL_PRAGMA_VISIBILITY_PUSH OSL_PRAGMA(GCC visibility push(default))
175 #    define OSL_PRAGMA_VISIBILITY_POP  OSL_PRAGMA(GCC visibility pop)
176 #    define OSL_GCC_PRAGMA(UnQuotedPragma) OSL_PRAGMA(UnQuotedPragma)
177 #    if defined(__clang__)
178 #        define OSL_CLANG_PRAGMA(UnQuotedPragma) OSL_PRAGMA(UnQuotedPragma)
179 #    else
180 #        define OSL_CLANG_PRAGMA(UnQuotedPragma)
181 #    endif
182 #    if defined(__INTEL_COMPILER)
183 #        define OSL_INTEL_PRAGMA(UnQuotedPragma) OSL_PRAGMA(UnQuotedPragma)
184 #    else
185 #        define OSL_INTEL_PRAGMA(UnQuotedPragma)
186 #    endif
187 #    define OSL_MSVS_PRAGMA(UnQuotedPragma)
188 #elif defined(_MSC_VER)
189 #    define OSL_PRAGMA_WARNING_PUSH __pragma(warning(push))
190 #    define OSL_PRAGMA_WARNING_POP  __pragma(warning(pop))
191 #    define OSL_PRAGMA_VISIBILITY_PUSH /* N/A on MSVS */
192 #    define OSL_PRAGMA_VISIBILITY_POP  /* N/A on MSVS */
193 #    define OSL_GCC_PRAGMA(UnQuotedPragma)
194 #    define OSL_CLANG_PRAGMA(UnQuotedPragma)
195 #    define OSL_INTEL_PRAGMA(UnQuotedPragma)
196 #    define OSL_MSVS_PRAGMA(UnQuotedPragma) OSL_PRAGMA(UnQuotedPragma)
197 #else
198 #    define OSL_PRAGMA_WARNING_PUSH
199 #    define OSL_PRAGMA_WARNING_POP
200 #    define OSL_PRAGMA_VISIBILITY_PUSH
201 #    define OSL_PRAGMA_VISIBILITY_POP
202 #    define OSL_GCC_PRAGMA(UnQuotedPragma)
203 #    define OSL_CLANG_PRAGMA(UnQuotedPragma)
204 #    define OSL_INTEL_PRAGMA(UnQuotedPragma)
205 #    define OSL_MSVS_PRAGMA(UnQuotedPragma)
206 #endif
207 
208 #ifdef __clang__
209     #define OSL_CLANG_ATTRIBUTE(value) __attribute__((value))
210 #else
211     #define OSL_CLANG_ATTRIBUTE(value)
212 #endif
213 
214 
215 
216 #ifndef OSL_DEBUG
217     #ifdef NDEBUG
218         #define OSL_DEBUG 0
219     #else
220         #ifdef _DEBUG
221             #define OSL_DEBUG _DEBUG
222         #else
223             #define OSL_DEBUG 1
224         #endif
225     #endif
226 #endif // OSL_DEBUG
227 
228 
229 #if OSL_DEBUG
230 	// Disable OPENMP_SIMD when debugging
231 	#undef OSL_OPENMP_SIMD
232 #endif
233 
234 #ifdef OSL_OPENMP_SIMD
235     #define OSL_OMP_PRAGMA(aUnQuotedPragma) OSL_PRAGMA(aUnQuotedPragma)
236 #else
237     #define OSL_OMP_PRAGMA(aUnQuotedPragma)
238 #endif
239 
240 // OSL_FORCEINLINE is a function attribute that attempts to make the
241 // function always inline. On many compilers regular 'inline' is only
242 // advisory. Put this attribute before the function return type, just like
243 // you would use 'inline'. Note that if OSL_DEBUG is true, it just becomes
244 // ordinary inline.
245 #if OSL_DEBUG
246 #    define OSL_FORCEINLINE inline
247 #elif defined(__CUDACC__)
248 #    define OSL_FORCEINLINE __inline__
249 #elif defined(__GNUC__) || defined(__clang__) || __has_attribute(always_inline)
250 #    define OSL_FORCEINLINE inline __attribute__((always_inline))
251 #elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
252 #    define OSL_FORCEINLINE __forceinline
253 #else
254 #    define OSL_FORCEINLINE inline
255 #endif
256 
257 
258 // OSL_NOINLINE hints to the compiler that the functions should never be
259 // inlined.
260 #if defined(__GNUC__) || defined(__clang__) || __has_attribute(noinline)
261 #    define OSL_NOINLINE __attribute__((noinline))
262 #elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
263 #    define OSL_NOINLINE __declspec(noinline)
264 #else
265 #    define OSL_NOINLINE
266 #endif
267 
268 // OSL_FORCEINLINE_BLOCK tells the compiler that any calls in the next
269 // statement (or compound statement/code block)should be inlined whenever
270 // the compiler is capable of doing so.  Furthermore, any calls made by those
271 // calls should be inlined, and so on recursively.  Has no affect on function calls to
272 // whose definition exists in different translation units or calls that are not
273 // legal to inline (possibly by OSL_NOINLINE).  Intent is to ensure that entire
274 // contents of a SIMD loop body is are fully inlined avoiding compiler heuristics.
275 // However if any debugging code exists inside the code block, then the amount
276 // of code inlined could explode.  So great care should be used when placing
277 // code inside one of these forced inline blocks.  Note that if OSL_DEBUG is true,
278 // it does nothing to avoid over-inlining.
279 // NOTE:  only supported by Intel C++ compiler.
280 // Unsupported compilers may instead use command line flags to increase their inlining
281 // limits/thresholds
282 #if OSL_DEBUG
283 	#define OSL_FORCEINLINE_BLOCK
284 #else
285 	#define OSL_FORCEINLINE_BLOCK OSL_INTEL_PRAGMA(forceinline recursive)
286 #endif
287 
288 
289 // OSL_MAYBE_UNUSED is a function or variable attribute that assures the
290 // compiler that it's fine for the item to appear to be unused.
291 #if OSL_CPLUSPLUS_VERSION >= 17 || __has_cpp_attribute(maybe_unused)
292 #    define OSL_MAYBE_UNUSED [[maybe_unused]]
293 #elif defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) || __has_attribute(unused)
294 #    define OSL_MAYBE_UNUSED __attribute__((unused))
295 #else
296 #    define OSL_MAYBE_UNUSED
297 #endif
298 
299 
300 // Some compilers define a special intrinsic to use in conditionals that can
301 // speed up extremely performance-critical spots if the conditional is
302 // usually (or rarely) is true.  You use it by replacing
303 //     if (x) ...
304 // with
305 //     if (OSL_LIKELY(x)) ...     // if you think x will usually be true
306 // or
307 //     if (OSL_UNLIKELY(x)) ...   // if you think x will rarely be true
308 // Caveat: Programmers are notoriously bad at guessing this, so it
309 // should be used only with thorough benchmarking.
310 #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)
311 #    define OSL_LIKELY(x) (__builtin_expect(bool(x), true))
312 #    define OSL_UNLIKELY(x) (__builtin_expect(bool(x), false))
313 #else
314 #    define OSL_LIKELY(x) (x)
315 #    define OSL_UNLIKELY(x) (x)
316 #endif
317 
318 
319 #ifndef OSL_HOSTDEVICE
320 #  ifdef __CUDACC__
321 #    define OSL_HOSTDEVICE __host__ __device__
322 #  else
323 #    define OSL_HOSTDEVICE
324 #  endif
325 #endif
326 
327 #ifndef OSL_DEVICE
328 #  ifdef __CUDACC__
329 #    define OSL_DEVICE __device__
330 #  else
331 #    define OSL_DEVICE
332 #  endif
333 #endif
334 
335 #ifndef OSL_CONSTANT_DATA
336 #  ifdef __CUDACC__
337 #    define OSL_CONSTANT_DATA __constant__
338 #  else
339 #    define OSL_CONSTANT_DATA
340 #  endif
341 #endif
342 
343 
344 // OSL_DEPRECATED before a function declaration marks it as deprecated in
345 // a way that will generate compile warnings if it is called (but will
346 // preserve linkage compatibility).
347 #if OSL_CPLUSPLUS_VERSION >= 14 || __has_cpp_attribute(deprecated)
348 #  define OSL_DEPRECATED(msg) [[deprecated(msg)]]
349 #elif defined(__GNUC__) || defined(__clang__) || __has_attribute(deprecated)
350 #  define OSL_DEPRECATED(msg) __attribute__((deprecated(msg)))
351 #elif defined(_MSC_VER)
352 #  define OSL_DEPRECATED(msg) __declspec(deprecated(msg))
353 #else
354 #  define OSL_DEPRECATED(msg)
355 #endif
356 
357 // OSL_FALLTHROUGH at the end of a `case` label's statements documents that
358 // the switch statement case is intentionally falling through to the code for
359 // the next case.
360 #if OSL_CPLUSPLUS_VERSION >= 17 || __has_cpp_attribute(fallthrough)
361 #    define OSL_FALLTHROUGH [[fallthrough]]
362 #else
363 #    define OSL_FALLTHROUGH
364 #endif
365 
366 
367 // OSL_NODISCARD following a function declaration documents that the
368 // function's return value should never be ignored.
369 #if OSL_CPLUSPLUS_VERSION >= 17 || __has_cpp_attribute(nodiscard)
370 #    define OSL_NODISCARD [[nodiscard]]
371 #else
372 #    define OSL_NODISCARD
373 #endif
374 
375 
376 /// Work around bug in GCC with mixed __attribute__ and alignas parsing
377 /// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585
378 #ifdef __GNUC__
379 #  define OSL_ALIGNAS(size) __attribute__((aligned(size)))
380 #else
381 #  define OSL_ALIGNAS(size) alignas(size)
382 #endif
383 
384 
385 // OSL_PRETTY_FUNCTION gives a text string of the current function
386 // declaration.
387 #if defined(__PRETTY_FUNCTION__)
388 #    define OSL_PRETTY_FUNCTION __PRETTY_FUNCTION__ /* gcc, clang */
389 #elif defined(__FUNCSIG__)
390 #    define OSL_PRETTY_FUNCTION __FUNCSIG__ /* MS gotta be different */
391 #else
392 #    define OSL_PRETTY_FUNCTION __FUNCTION__
393 #endif
394 
395 
396 /// OSL_ABORT_IF_DEBUG is a call to abort() for debug builds, but does
397 /// nothing for release builds.
398 #ifndef NDEBUG
399 #    define OSL_ABORT_IF_DEBUG abort()
400 #else
401 #    define OSL_ABORT_IF_DEBUG (void)0
402 #endif
403 
404 
405 /// OSL_ASSERT(condition) checks if the condition is met, and if not,
406 /// prints an error message indicating the module and line where the error
407 /// occurred, and additionally aborts if in debug mode. When in release
408 /// mode, it prints the error message if the condition fails, but does not
409 /// abort.
410 ///
411 /// OSL_ASSERT_MSG(condition,msg,...) lets you add formatted output (a la
412 /// printf) to the failure message.
413 #ifndef __CUDA_ARCH__
414 #    define OSL_ASSERT_PRINT(...) (std::fprintf(stderr, __VA_ARGS__))
415 #else
416 #    define OSL_ASSERT_PRINT(...) (printf(__VA_ARGS__))
417 #endif
418 
419 #define OSL_ASSERT(x)                                                          \
420     (OSL_LIKELY(x)                                                             \
421          ? ((void)0)                                                           \
422          : (OSL_ASSERT_PRINT("%s:%u: %s: Assertion '%s' failed.\n",            \
423                              __FILE__, __LINE__, OSL_PRETTY_FUNCTION, #x),     \
424             OSL_ABORT_IF_DEBUG))
425 #define OSL_ASSERT_MSG(x, msg, ...)                                             \
426     (OSL_LIKELY(x)                                                              \
427          ? ((void)0)                                                            \
428          : (std::fprintf(stderr, "%s:%u: %s: Assertion '%s' failed: " msg "\n", \
429                          __FILE__, __LINE__, OSL_PRETTY_FUNCTION, #x,           \
430                          __VA_ARGS__),                                          \
431             OSL_ABORT_IF_DEBUG))
432 
433 /// OSL_DASSERT and OSL_DASSERT_MSG are the same as OSL_ASSERT for debug
434 /// builds (test, print error, abort), but do nothing at all in release
435 /// builds (not even perform the test). This is similar to C/C++ assert(),
436 /// but gives us flexibility in improving our error messages. It is also ok
437 /// to use regular assert() for this purpose if you need to eliminate the
438 /// dependency on this header from a particular place (and don't mind that
439 /// assert won't format identically on all platforms).
440 #ifndef NDEBUG
441 #    define OSL_DASSERT OSL_ASSERT
442 #    define OSL_DASSERT_MSG OSL_ASSERT_MSG
443 #else
444 #    define OSL_DASSERT(x) ((void)sizeof(x))          /*NOLINT*/
445 #    define OSL_DASSERT_MSG(x, ...) ((void)sizeof(x)) /*NOLINT*/
446 #endif
447 
448 
449 OSL_NAMESPACE_ENTER
450 
451 #if OSL_CPLUSPLUS_VERSION >= 20
452 using std::assume_aligned;
453 #else
454 
455 // Until we have c++20, we can provide our own version of std::assume_aligned
456 // http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1007r0.pdf
457 template<int Alignment, class DataT>
458 OSL_NODISCARD
459 #ifndef __INTEL_COMPILER // constexpr not compatible with ICC's __assume_aligned
460     constexpr
461 #endif
462 DataT* assume_aligned(DataT* ptr)
463 {
464 #ifdef __INTEL_COMPILER
465     __assume_aligned(ptr, Alignment);
466     return ptr;
467 #elif defined(__clang__) || defined(__GNUC__)
468     return reinterpret_cast<DataT*>(__builtin_assume_aligned(ptr, Alignment));
469 #elif defined(_MSC_VER)
470     constexpr std::uintptr_t unaligned_bit_mask = (Alignment - 1);
471     if ((reinterpret_cast<std::uintptr_t>(ptr) & unaligned_bit_mask) == 0)
472         return ptr;
473     else
474         __assume(0); // let compiler know we should never reach this branch
475 #else
476     #error unknown compiler, update assume_aligned to handle (or explicitly ignore) emitting a pointer alignment hint
477     return ptr;
478 #endif
479 }
480 
481 #endif
482 
483 OSL_NAMESPACE_EXIT
484