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