crc32_amd64.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build !appengine,!gccgo
  5. // AMD64-specific hardware-assisted CRC32 algorithms. See crc32.go for a
  6. // description of the interface that each architecture-specific file
  7. // implements.
  8. package crc32
  9. import "unsafe"
  10. // This file contains the code to call the SSE 4.2 version of the Castagnoli
  11. // and IEEE CRC.
  12. // haveSSE41/haveSSE42/haveCLMUL are defined in crc_amd64.s and use
  13. // CPUID to test for SSE 4.1, 4.2 and CLMUL support.
  14. func haveSSE41() bool
  15. func haveSSE42() bool
  16. func haveCLMUL() bool
  17. // castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE4.2 CRC32
  18. // instruction.
  19. //go:noescape
  20. func castagnoliSSE42(crc uint32, p []byte) uint32
  21. // castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE4.2 CRC32
  22. // instruction.
  23. //go:noescape
  24. func castagnoliSSE42Triple(
  25. crcA, crcB, crcC uint32,
  26. a, b, c []byte,
  27. rounds uint32,
  28. ) (retA uint32, retB uint32, retC uint32)
  29. // ieeeCLMUL is defined in crc_amd64.s and uses the PCLMULQDQ
  30. // instruction as well as SSE 4.1.
  31. //go:noescape
  32. func ieeeCLMUL(crc uint32, p []byte) uint32
  33. var sse42 = haveSSE42()
  34. var useFastIEEE = haveCLMUL() && haveSSE41()
  35. const castagnoliK1 = 168
  36. const castagnoliK2 = 1344
  37. type sse42Table [4]Table
  38. var castagnoliSSE42TableK1 *sse42Table
  39. var castagnoliSSE42TableK2 *sse42Table
  40. func archAvailableCastagnoli() bool {
  41. return sse42
  42. }
  43. func archInitCastagnoli() {
  44. if !sse42 {
  45. panic("arch-specific Castagnoli not available")
  46. }
  47. castagnoliSSE42TableK1 = new(sse42Table)
  48. castagnoliSSE42TableK2 = new(sse42Table)
  49. // See description in updateCastagnoli.
  50. // t[0][i] = CRC(i000, O)
  51. // t[1][i] = CRC(0i00, O)
  52. // t[2][i] = CRC(00i0, O)
  53. // t[3][i] = CRC(000i, O)
  54. // where O is a sequence of K zeros.
  55. var tmp [castagnoliK2]byte
  56. for b := 0; b < 4; b++ {
  57. for i := 0; i < 256; i++ {
  58. val := uint32(i) << uint32(b*8)
  59. castagnoliSSE42TableK1[b][i] = castagnoliSSE42(val, tmp[:castagnoliK1])
  60. castagnoliSSE42TableK2[b][i] = castagnoliSSE42(val, tmp[:])
  61. }
  62. }
  63. }
  64. // castagnoliShift computes the CRC32-C of K1 or K2 zeroes (depending on the
  65. // table given) with the given initial crc value. This corresponds to
  66. // CRC(crc, O) in the description in updateCastagnoli.
  67. func castagnoliShift(table *sse42Table, crc uint32) uint32 {
  68. return table[3][crc>>24] ^
  69. table[2][(crc>>16)&0xFF] ^
  70. table[1][(crc>>8)&0xFF] ^
  71. table[0][crc&0xFF]
  72. }
  73. func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
  74. if !sse42 {
  75. panic("not available")
  76. }
  77. // This method is inspired from the algorithm in Intel's white paper:
  78. // "Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction"
  79. // The same strategy of splitting the buffer in three is used but the
  80. // combining calculation is different; the complete derivation is explained
  81. // below.
  82. //
  83. // -- The basic idea --
  84. //
  85. // The CRC32 instruction (available in SSE4.2) can process 8 bytes at a
  86. // time. In recent Intel architectures the instruction takes 3 cycles;
  87. // however the processor can pipeline up to three instructions if they
  88. // don't depend on each other.
  89. //
  90. // Roughly this means that we can process three buffers in about the same
  91. // time we can process one buffer.
  92. //
  93. // The idea is then to split the buffer in three, CRC the three pieces
  94. // separately and then combine the results.
  95. //
  96. // Combining the results requires precomputed tables, so we must choose a
  97. // fixed buffer length to optimize. The longer the length, the faster; but
  98. // only buffers longer than this length will use the optimization. We choose
  99. // two cutoffs and compute tables for both:
  100. // - one around 512: 168*3=504
  101. // - one around 4KB: 1344*3=4032
  102. //
  103. // -- The nitty gritty --
  104. //
  105. // Let CRC(I, X) be the non-inverted CRC32-C of the sequence X (with
  106. // initial non-inverted CRC I). This function has the following properties:
  107. // (a) CRC(I, AB) = CRC(CRC(I, A), B)
  108. // (b) CRC(I, A xor B) = CRC(I, A) xor CRC(0, B)
  109. //
  110. // Say we want to compute CRC(I, ABC) where A, B, C are three sequences of
  111. // K bytes each, where K is a fixed constant. Let O be the sequence of K zero
  112. // bytes.
  113. //
  114. // CRC(I, ABC) = CRC(I, ABO xor C)
  115. // = CRC(I, ABO) xor CRC(0, C)
  116. // = CRC(CRC(I, AB), O) xor CRC(0, C)
  117. // = CRC(CRC(I, AO xor B), O) xor CRC(0, C)
  118. // = CRC(CRC(I, AO) xor CRC(0, B), O) xor CRC(0, C)
  119. // = CRC(CRC(CRC(I, A), O) xor CRC(0, B), O) xor CRC(0, C)
  120. //
  121. // The castagnoliSSE42Triple function can compute CRC(I, A), CRC(0, B),
  122. // and CRC(0, C) efficiently. We just need to find a way to quickly compute
  123. // CRC(uvwx, O) given a 4-byte initial value uvwx. We can precompute these
  124. // values; since we can't have a 32-bit table, we break it up into four
  125. // 8-bit tables:
  126. //
  127. // CRC(uvwx, O) = CRC(u000, O) xor
  128. // CRC(0v00, O) xor
  129. // CRC(00w0, O) xor
  130. // CRC(000x, O)
  131. //
  132. // We can compute tables corresponding to the four terms for all 8-bit
  133. // values.
  134. crc = ^crc
  135. // If a buffer is long enough to use the optimization, process the first few
  136. // bytes to align the buffer to an 8 byte boundary (if necessary).
  137. if len(p) >= castagnoliK1*3 {
  138. delta := int(uintptr(unsafe.Pointer(&p[0])) & 7)
  139. if delta != 0 {
  140. delta = 8 - delta
  141. crc = castagnoliSSE42(crc, p[:delta])
  142. p = p[delta:]
  143. }
  144. }
  145. // Process 3*K2 at a time.
  146. for len(p) >= castagnoliK2*3 {
  147. // Compute CRC(I, A), CRC(0, B), and CRC(0, C).
  148. crcA, crcB, crcC := castagnoliSSE42Triple(
  149. crc, 0, 0,
  150. p, p[castagnoliK2:], p[castagnoliK2*2:],
  151. castagnoliK2/24)
  152. // CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B)
  153. crcAB := castagnoliShift(castagnoliSSE42TableK2, crcA) ^ crcB
  154. // CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C)
  155. crc = castagnoliShift(castagnoliSSE42TableK2, crcAB) ^ crcC
  156. p = p[castagnoliK2*3:]
  157. }
  158. // Process 3*K1 at a time.
  159. for len(p) >= castagnoliK1*3 {
  160. // Compute CRC(I, A), CRC(0, B), and CRC(0, C).
  161. crcA, crcB, crcC := castagnoliSSE42Triple(
  162. crc, 0, 0,
  163. p, p[castagnoliK1:], p[castagnoliK1*2:],
  164. castagnoliK1/24)
  165. // CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B)
  166. crcAB := castagnoliShift(castagnoliSSE42TableK1, crcA) ^ crcB
  167. // CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C)
  168. crc = castagnoliShift(castagnoliSSE42TableK1, crcAB) ^ crcC
  169. p = p[castagnoliK1*3:]
  170. }
  171. // Use the simple implementation for what's left.
  172. crc = castagnoliSSE42(crc, p)
  173. return ^crc
  174. }
  175. func archAvailableIEEE() bool {
  176. return useFastIEEE
  177. }
  178. var archIeeeTable8 *slicing8Table
  179. func archInitIEEE() {
  180. if !useFastIEEE {
  181. panic("not available")
  182. }
  183. // We still use slicing-by-8 for small buffers.
  184. archIeeeTable8 = slicingMakeTable(IEEE)
  185. }
  186. func archUpdateIEEE(crc uint32, p []byte) uint32 {
  187. if !useFastIEEE {
  188. panic("not available")
  189. }
  190. if len(p) >= 64 {
  191. left := len(p) & 15
  192. do := len(p) - left
  193. crc = ^ieeeCLMUL(^crc, p[:do])
  194. p = p[do:]
  195. }
  196. if len(p) == 0 {
  197. return crc
  198. }
  199. return slicingUpdate(crc, archIeeeTable8, p)
  200. }