compiler_opt.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. # ===================================================================
  2. #
  3. # Copyright (c) 2018, Helder Eijs <helderijs@gmail.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in
  14. # the documentation and/or other materials provided with the
  15. # distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  27. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. # ===================================================================
  30. import os
  31. import sys
  32. import struct
  33. import distutils
  34. from distutils import ccompiler
  35. from distutils.errors import CCompilerError
  36. def test_compilation(program, extra_cc_options=None, extra_libraries=None,
  37. msg=''):
  38. """Test if a certain C program can be compiled."""
  39. # Create a temporary file with the C program
  40. if not os.path.exists("build"):
  41. os.makedirs("build")
  42. fname = os.path.join("build", "test1.c")
  43. f = open(fname, 'w')
  44. f.write(program)
  45. f.close()
  46. # Name for the temporary executable
  47. oname = os.path.join("build", "test1.out")
  48. debug = bool(os.environ.get('PYCRYPTODOME_DEBUG', None))
  49. # Mute the compiler and the linker
  50. if msg:
  51. print("Testing support for %s" % msg)
  52. if not (debug or os.name == 'nt'):
  53. old_stdout = os.dup(sys.stdout.fileno())
  54. old_stderr = os.dup(sys.stderr.fileno())
  55. dev_null = open(os.devnull, "w")
  56. os.dup2(dev_null.fileno(), sys.stdout.fileno())
  57. os.dup2(dev_null.fileno(), sys.stderr.fileno())
  58. objects = []
  59. try:
  60. compiler = ccompiler.new_compiler()
  61. distutils.sysconfig.customize_compiler(compiler)
  62. if compiler.compiler_type in ['msvc']:
  63. # Force creation of the manifest file (http://bugs.python.org/issue16296)
  64. # as needed by VS2010
  65. extra_linker_options = ["/MANIFEST"]
  66. else:
  67. extra_linker_options = []
  68. # In Unix, force the linker step to use CFLAGS and not CC alone (see GH#180)
  69. if compiler.compiler_type in ['unix']:
  70. compiler.set_executables(linker_exe=compiler.compiler)
  71. objects = compiler.compile([fname], extra_postargs=extra_cc_options)
  72. compiler.link_executable(objects, oname, libraries=extra_libraries,
  73. extra_preargs=extra_linker_options)
  74. result = True
  75. except (CCompilerError, OSError):
  76. result = False
  77. for f in objects + [fname, oname]:
  78. try:
  79. os.remove(f)
  80. except OSError:
  81. pass
  82. # Restore stdout and stderr
  83. if not (debug or os.name == 'nt'):
  84. if old_stdout is not None:
  85. os.dup2(old_stdout, sys.stdout.fileno())
  86. if old_stderr is not None:
  87. os.dup2(old_stderr, sys.stderr.fileno())
  88. if dev_null is not None:
  89. dev_null.close()
  90. if msg:
  91. if result:
  92. x = ""
  93. else:
  94. x = " not"
  95. print("Target does%s support %s" % (x, msg))
  96. return result
  97. def has_stdint_h():
  98. source = """
  99. #include <stdint.h>
  100. int main(void) {
  101. uint32_t u;
  102. u = 0;
  103. return u + 2;
  104. }
  105. """
  106. return test_compilation(source, msg="stdint.h header")
  107. def compiler_supports_uint128():
  108. source = """
  109. int main(void)
  110. {
  111. __uint128_t x;
  112. return 0;
  113. }
  114. """
  115. return test_compilation(source, msg="128-bit integer")
  116. def compiler_has_intrin_h():
  117. # Windows
  118. source = """
  119. #include <intrin.h>
  120. int main(void)
  121. {
  122. int a, b[4];
  123. __cpuid(b, a);
  124. return 0;
  125. }
  126. """
  127. return test_compilation(source, msg="intrin.h header")
  128. def compiler_has_cpuid_h():
  129. # UNIX
  130. source = """
  131. #include <cpuid.h>
  132. int main(void)
  133. {
  134. unsigned int eax, ebx, ecx, edx;
  135. __get_cpuid(1, &eax, &ebx, &ecx, &edx);
  136. return 0;
  137. }
  138. """
  139. return test_compilation(source, msg="cpuid.h header")
  140. def compiler_supports_aesni():
  141. source = """
  142. #include <wmmintrin.h>
  143. __m128i f(__m128i x, __m128i y) {
  144. return _mm_aesenc_si128(x, y);
  145. }
  146. int main(void) {
  147. return 0;
  148. }
  149. """
  150. if test_compilation(source):
  151. return {'extra_cc_options': [], 'extra_macros': []}
  152. if test_compilation(source, extra_cc_options=['-maes'], msg='AESNI intrinsics'):
  153. return {'extra_cc_options': ['-maes'], 'extra_macros': []}
  154. return False
  155. def compiler_supports_clmul():
  156. result = {'extra_cc_options': [], 'extra_macros' : ['HAVE_WMMINTRIN_H', 'HAVE_TMMINTRIN_H']}
  157. source = """
  158. #include <wmmintrin.h>
  159. #include <tmmintrin.h>
  160. __m128i f(__m128i x, __m128i y) {
  161. return _mm_clmulepi64_si128(x, y, 0x00);
  162. }
  163. __m128i g(__m128i a) {
  164. __m128i mask;
  165. mask = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
  166. return _mm_shuffle_epi8(a, mask);
  167. }
  168. int main(void) {
  169. return 0;
  170. }
  171. """
  172. if test_compilation(source):
  173. return result
  174. if test_compilation(source, extra_cc_options=['-mpclmul', '-mssse3'], msg='CLMUL intrinsics'):
  175. result['extra_cc_options'].extend(['-mpclmul', '-mssse3'])
  176. return result
  177. return False
  178. def compiler_has_posix_memalign():
  179. source = """
  180. #include <stdlib.h>
  181. int main(void) {
  182. void *new_mem;
  183. int res;
  184. res = posix_memalign((void**)&new_mem, 16, 101);
  185. return res == 0;
  186. }
  187. """
  188. return test_compilation(source, msg="posix_memalign")
  189. def compiler_has_memalign():
  190. source = """
  191. #include <malloc.h>
  192. int main(void) {
  193. void *p;
  194. p = memalign(16, 101);
  195. return p != (void*)0;
  196. }
  197. """
  198. return test_compilation(source, msg="memalign")
  199. def compiler_is_clang():
  200. source = """
  201. #if !defined(__clang__)
  202. #error Not clang
  203. #endif
  204. int main(void)
  205. {
  206. return 0;
  207. }
  208. """
  209. return test_compilation(source, msg="clang")
  210. def compiler_is_gcc():
  211. source = """
  212. #if defined(__clang__) || !defined(__GNUC__)
  213. #error Not GCC
  214. #endif
  215. int main(void)
  216. {
  217. return 0;
  218. }"""
  219. return test_compilation(source, msg="gcc")
  220. def support_gcc_realign():
  221. source = """
  222. void __attribute__((force_align_arg_pointer)) a(void) {}
  223. int main(void) { return 0; }
  224. """
  225. return test_compilation(source, msg="gcc")
  226. def compiler_supports_sse2():
  227. source = """
  228. #include <intrin.h>
  229. int main(void)
  230. {
  231. __m128i r0;
  232. int mask;
  233. r0 = _mm_set1_epi32(0);
  234. mask = _mm_movemask_epi8(r0);
  235. return mask;
  236. }
  237. """
  238. if test_compilation(source, msg="SSE2(intrin.h)"):
  239. return {'extra_cc_options': [], 'extra_macros': ['HAVE_INTRIN_H', 'USE_SSE2']}
  240. source = """
  241. #include <x86intrin.h>
  242. int main(void)
  243. {
  244. __m128i r0;
  245. int mask;
  246. r0 = _mm_set1_epi32(0);
  247. mask = _mm_movemask_epi8(r0);
  248. return mask;
  249. }
  250. """
  251. if test_compilation(source, extra_cc_options=['-msse2'], msg="SSE2(x86intrin.h)"):
  252. return {'extra_cc_options': ['-msse2'], 'extra_macros': ['HAVE_X86INTRIN_H', 'USE_SSE2']}
  253. source = """
  254. #include <xmmintrin.h>
  255. #include <emmintrin.h>
  256. int main(void)
  257. {
  258. __m128i r0;
  259. int mask;
  260. r0 = _mm_set1_epi32(0);
  261. mask = _mm_movemask_epi8(r0);
  262. return mask;
  263. }
  264. """
  265. if test_compilation(source, extra_cc_options=['-msse2'], msg="SSE2(emmintrin.h)"):
  266. return {'extra_cc_options': ['-msse2'], 'extra_macros': ['HAVE_EMMINTRIN_H', 'USE_SSE2']}
  267. return False
  268. def remove_extension(extensions, name):
  269. idxs = [i for i, x in enumerate(extensions) if x.name == name]
  270. if len(idxs) != 1:
  271. raise ValueError("There is no or there are multiple extensions named '%s'" % name)
  272. del extensions[idxs[0]]
  273. def set_compiler_options(package_root, extensions):
  274. """Environment specific settings for extension modules.
  275. This function modifies how each module gets compiled, to
  276. match the capabilities of the platform.
  277. Also, it removes existing modules when not supported, such as:
  278. - AESNI
  279. - CLMUL
  280. """
  281. extra_cc_options = []
  282. extra_macros = []
  283. clang = compiler_is_clang()
  284. gcc = compiler_is_gcc()
  285. if has_stdint_h():
  286. extra_macros.append(("HAVE_STDINT_H", None))
  287. # Endianess
  288. extra_macros.append(("PYCRYPTO_" + sys.byteorder.upper() + "_ENDIAN", None))
  289. # System
  290. system_bits = 8 * struct.calcsize("P")
  291. extra_macros.append(("SYS_BITS", str(system_bits)))
  292. # Disable any assembly in libtomcrypt files
  293. extra_macros.append(("LTC_NO_ASM", None))
  294. # Native 128-bit integer
  295. if compiler_supports_uint128():
  296. extra_macros.append(("HAVE_UINT128", None))
  297. # Auto-detecting CPU features
  298. cpuid_h_present = compiler_has_cpuid_h()
  299. if cpuid_h_present:
  300. extra_macros.append(("HAVE_CPUID_H", None))
  301. intrin_h_present = compiler_has_intrin_h()
  302. if intrin_h_present:
  303. extra_macros.append(("HAVE_INTRIN_H", None))
  304. # Platform-specific call for getting a block of aligned memory
  305. if compiler_has_posix_memalign():
  306. extra_macros.append(("HAVE_POSIX_MEMALIGN", None))
  307. elif compiler_has_memalign():
  308. extra_macros.append(("HAVE_MEMALIGN", None))
  309. # SSE2
  310. sse2_result = compiler_supports_sse2()
  311. if sse2_result:
  312. extra_cc_options.extend(sse2_result['extra_cc_options'])
  313. for macro in sse2_result['extra_macros']:
  314. extra_macros.append((macro, None))
  315. # Compiler specific settings
  316. if gcc:
  317. # On 32-bit x86 platforms, gcc assumes the stack to be aligned to 16
  318. # bytes, but the caller may actually only align it to 4 bytes, which
  319. # make functions crash if they use SSE2 intrinsics.
  320. # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838
  321. if system_bits == 32 and support_gcc_realign():
  322. extra_macros.append(("GCC_REALIGN", None))
  323. # Module-specific options
  324. # AESNI
  325. aesni_result = (cpuid_h_present or intrin_h_present) and compiler_supports_aesni()
  326. aesni_mod_name = package_root + ".Cipher._raw_aesni"
  327. if aesni_result:
  328. print("Compiling support for AESNI instructions")
  329. aes_mods = [x for x in extensions if x.name == aesni_mod_name]
  330. for x in aes_mods:
  331. x.extra_compile_args.extend(aesni_result['extra_cc_options'])
  332. for macro in aesni_result['extra_macros']:
  333. x.define_macros.append((macro, None))
  334. else:
  335. print("Warning: compiler does not support AESNI instructions")
  336. remove_extension(extensions, aesni_mod_name)
  337. # CLMUL
  338. clmul_result = (cpuid_h_present or intrin_h_present) and compiler_supports_clmul()
  339. clmul_mod_name = package_root + ".Hash._ghash_clmul"
  340. if clmul_result:
  341. print("Compiling support for CLMUL instructions")
  342. clmul_mods = [x for x in extensions if x.name == clmul_mod_name]
  343. for x in clmul_mods:
  344. x.extra_compile_args.extend(clmul_result['extra_cc_options'])
  345. for macro in clmul_result['extra_macros']:
  346. x.define_macros.append((macro, None))
  347. else:
  348. print("Warning: compiler does not support CLMUL instructions")
  349. remove_extension(extensions, clmul_mod_name)
  350. for x in extensions:
  351. x.extra_compile_args.extend(extra_cc_options)
  352. x.define_macros.extend(extra_macros)