cpuid.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. /*
  6. * MSVC wants us to use the non-portable _dupenv_s instead; since we need
  7. * to remain portable, tell MSVC to suppress this warning.
  8. */
  9. #define _CRT_SECURE_NO_WARNINGS
  10. #include <aws/common/cpuid.h>
  11. #include <stdlib.h>
  12. extern void aws_run_cpuid(uint32_t eax, uint32_t ecx, uint32_t *abcd);
  13. typedef bool(has_feature_fn)(void);
  14. static bool s_has_clmul(void) {
  15. uint32_t abcd[4];
  16. uint32_t clmul_mask = 0x00000002;
  17. aws_run_cpuid(1, 0, abcd);
  18. if ((abcd[2] & clmul_mask) != clmul_mask)
  19. return false;
  20. return true;
  21. }
  22. static bool s_has_sse41(void) {
  23. uint32_t abcd[4];
  24. uint32_t sse41_mask = 0x00080000;
  25. aws_run_cpuid(1, 0, abcd);
  26. if ((abcd[2] & sse41_mask) != sse41_mask)
  27. return false;
  28. return true;
  29. }
  30. static bool s_has_sse42(void) {
  31. uint32_t abcd[4];
  32. uint32_t sse42_mask = 0x00100000;
  33. aws_run_cpuid(1, 0, abcd);
  34. if ((abcd[2] & sse42_mask) != sse42_mask)
  35. return false;
  36. return true;
  37. }
  38. static bool s_has_avx2(void) {
  39. uint32_t abcd[4];
  40. /* Check AVX2:
  41. * CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]==1 */
  42. uint32_t avx2_mask = (1 << 5);
  43. aws_run_cpuid(7, 0, abcd);
  44. if ((abcd[1] & avx2_mask) != avx2_mask) {
  45. return false;
  46. }
  47. /* Also check AVX:
  48. * CPUID.(EAX=01H, ECX=0H):ECX.AVX[bit 28]==1
  49. *
  50. * NOTE: It SHOULD be impossible for a CPU to support AVX2 without supporting AVX.
  51. * But we've received crash reports where the AVX2 feature check passed
  52. * and then an AVX instruction caused an "invalid instruction" crash.
  53. *
  54. * We diagnosed these machines by asking users to run the sample program from:
  55. * https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-160
  56. * and observed the following results:
  57. *
  58. * AVX not supported
  59. * AVX2 supported
  60. *
  61. * We don't know for sure what was up with those machines, but this extra
  62. * check should stop them from running our AVX/AVX2 code paths. */
  63. uint32_t avx1_mask = (1 << 28);
  64. aws_run_cpuid(1, 0, abcd);
  65. if ((abcd[2] & avx1_mask) != avx1_mask) {
  66. return false;
  67. }
  68. return true;
  69. }
  70. has_feature_fn *s_check_cpu_feature[AWS_CPU_FEATURE_COUNT] = {
  71. [AWS_CPU_FEATURE_CLMUL] = s_has_clmul,
  72. [AWS_CPU_FEATURE_SSE_4_1] = s_has_sse41,
  73. [AWS_CPU_FEATURE_SSE_4_2] = s_has_sse42,
  74. [AWS_CPU_FEATURE_AVX2] = s_has_avx2,
  75. };
  76. bool aws_cpu_has_feature(enum aws_cpu_feature_name feature_name) {
  77. if (s_check_cpu_feature[feature_name])
  78. return s_check_cpu_feature[feature_name]();
  79. return false;
  80. }
  81. #define CPUID_AVAILABLE 0
  82. #define CPUID_UNAVAILABLE 1
  83. static int cpuid_state = 2;
  84. bool aws_common_private_has_avx2(void) {
  85. if (AWS_LIKELY(cpuid_state == 0)) {
  86. return true;
  87. }
  88. if (AWS_LIKELY(cpuid_state == 1)) {
  89. return false;
  90. }
  91. /* Provide a hook for testing fallbacks and benchmarking */
  92. const char *env_avx2_enabled = getenv("AWS_COMMON_AVX2");
  93. if (env_avx2_enabled) {
  94. int is_enabled = atoi(env_avx2_enabled);
  95. cpuid_state = !is_enabled;
  96. return is_enabled;
  97. }
  98. bool available = aws_cpu_has_feature(AWS_CPU_FEATURE_AVX2);
  99. cpuid_state = available ? CPUID_AVAILABLE : CPUID_UNAVAILABLE;
  100. return available;
  101. }