github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/src/hash/crc32/crc32_amd64.go (about)

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