// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // AMD64-specific hardware-assisted CRC32 algorithms. See crc32.go for a // description of the interface that each architecture-specific file // implements. package crc32 import ( "internal/cpu" "unsafe" ) // This file contains the code to call the SSE 4.2 version of the Castagnoli // and IEEE CRC. // castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE 4.2 CRC32 // instruction. // //go:noescape func castagnoliSSE42(crc uint32, p []byte) uint32 // castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE 4.2 CRC32 // instruction. // //go:noescape func castagnoliSSE42Triple( crcA, crcB, crcC uint32, a, b, c []byte, rounds uint32, ) (retA uint32, retB uint32, retC uint32) // ieeeCLMUL is defined in crc_amd64.s and uses the PCLMULQDQ // instruction as well as SSE 4.1. // //go:noescape func ieeeCLMUL(crc uint32, p []byte) uint32 const castagnoliK1 = 168 const castagnoliK2 = 1344 type sse42Table [4]Table var castagnoliSSE42TableK1 *sse42Table var castagnoliSSE42TableK2 *sse42Table func archAvailableCastagnoli() bool { return cpu.X86.HasSSE42 } func archInitCastagnoli() { if !cpu.X86.HasSSE42 { panic("arch-specific Castagnoli not available") } castagnoliSSE42TableK1 = new(sse42Table) castagnoliSSE42TableK2 = new(sse42Table) // See description in updateCastagnoli. // t[0][i] = CRC(i000, O) // t[1][i] = CRC(0i00, O) // t[2][i] = CRC(00i0, O) // t[3][i] = CRC(000i, O) // where O is a sequence of K zeros. var tmp [castagnoliK2]byte for b := 0; b < 4; b++ { for i := 0; i < 256; i++ { val := uint32(i) << uint32(b*8) castagnoliSSE42TableK1[b][i] = castagnoliSSE42(val, tmp[:castagnoliK1]) castagnoliSSE42TableK2[b][i] = castagnoliSSE42(val, tmp[:]) } } } // castagnoliShift computes the CRC32-C of K1 or K2 zeroes (depending on the // table given) with the given initial crc value. This corresponds to // CRC(crc, O) in the description in updateCastagnoli. func castagnoliShift(table *sse42Table, crc uint32) uint32 { return table[3][crc>>24] ^ table[2][(crc>>16)&0xFF] ^ table[1][(crc>>8)&0xFF] ^ table[0][crc&0xFF] } func archUpdateCastagnoli(crc uint32, p []byte) uint32 { if !cpu.X86.HasSSE42 { panic("not available") } // This method is inspired from the algorithm in Intel's white paper: // "Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction" // The same strategy of splitting the buffer in three is used but the // combining calculation is different; the complete derivation is explained // below. // // -- The basic idea -- // // The CRC32 instruction (available in SSE4.2) can process 8 bytes at a // time. In recent Intel architectures the instruction takes 3 cycles; // however the processor can pipeline up to three instructions if they // don't depend on each other. // // Roughly this means that we can process three buffers in about the same // time we can process one buffer. // // The idea is then to split the buffer in three, CRC the three pieces // separately and then combine the results. // // Combining the results requires precomputed tables, so we must choose a // fixed buffer length to optimize. The longer the length, the faster; but // only buffers longer than this length will use the optimization. We choose // two cutoffs and compute tables for both: // - one around 512: 168*3=504 // - one around 4KB: 1344*3=4032 // // -- The nitty gritty -- // // Let CRC(I, X) be the non-inverted CRC32-C of the sequence X (with // initial non-inverted CRC I). This function has the following properties: // (a) CRC(I, AB) = CRC(CRC(I, A), B) // (b) CRC(I, A xor B) = CRC(I, A) xor CRC(0, B) // // Say we want to compute CRC(I, ABC) where A, B, C are three sequences of // K bytes each, where K is a fixed constant. Let O be the sequence of K zero // bytes. // // CRC(I, ABC) = CRC(I, ABO xor C) // = CRC(I, ABO) xor CRC(0, C) // = CRC(CRC(I, AB), O) xor CRC(0, C) // = CRC(CRC(I, AO xor B), O) xor CRC(0, C) // = CRC(CRC(I, AO) xor CRC(0, B), O) xor CRC(0, C) // = CRC(CRC(CRC(I, A), O) xor CRC(0, B), O) xor CRC(0, C) // // The castagnoliSSE42Triple function can compute CRC(I, A), CRC(0, B), // and CRC(0, C) efficiently. We just need to find a way to quickly compute // CRC(uvwx, O) given a 4-byte initial value uvwx. We can precompute these // values; since we can't have a 32-bit table, we break it up into four // 8-bit tables: // // CRC(uvwx, O) = CRC(u000, O) xor // CRC(0v00, O) xor // CRC(00w0, O) xor // CRC(000x, O) // // We can compute tables corresponding to the four terms for all 8-bit // values. crc = ^crc // If a buffer is long enough to use the optimization, process the first few // bytes to align the buffer to an 8 byte boundary (if necessary). if len(p) >= castagnoliK1*3 { delta := int(uintptr(unsafe.Pointer(&p[0])) & 7) if delta != 0 { delta = 8 - delta crc = castagnoliSSE42(crc, p[:delta]) p = p[delta:] } } // Process 3*K2 at a time. for len(p) >= castagnoliK2*3 { // Compute CRC(I, A), CRC(0, B), and CRC(0, C). crcA, crcB, crcC := castagnoliSSE42Triple( crc, 0, 0, p, p[castagnoliK2:], p[castagnoliK2*2:], castagnoliK2/24) // CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B) crcAB := castagnoliShift(castagnoliSSE42TableK2, crcA) ^ crcB // CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C) crc = castagnoliShift(castagnoliSSE42TableK2, crcAB) ^ crcC p = p[castagnoliK2*3:] } // Process 3*K1 at a time. for len(p) >= castagnoliK1*3 { // Compute CRC(I, A), CRC(0, B), and CRC(0, C). crcA, crcB, crcC := castagnoliSSE42Triple( crc, 0, 0, p, p[castagnoliK1:], p[castagnoliK1*2:], castagnoliK1/24) // CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B) crcAB := castagnoliShift(castagnoliSSE42TableK1, crcA) ^ crcB // CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C) crc = castagnoliShift(castagnoliSSE42TableK1, crcAB) ^ crcC p = p[castagnoliK1*3:] } // Use the simple implementation for what's left. crc = castagnoliSSE42(crc, p) return ^crc } func archAvailableIEEE() bool { return cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 } var archIeeeTable8 *slicing8Table func archInitIEEE() { if !cpu.X86.HasPCLMULQDQ || !cpu.X86.HasSSE41 { panic("not available") } // We still use slicing-by-8 for small buffers. archIeeeTable8 = slicingMakeTable(IEEE) } func archUpdateIEEE(crc uint32, p []byte) uint32 { if !cpu.X86.HasPCLMULQDQ || !cpu.X86.HasSSE41 { panic("not available") } if len(p) >= 64 { left := len(p) & 15 do := len(p) - left crc = ^ieeeCLMUL(^crc, p[:do]) p = p[do:] } if len(p) == 0 { return crc } return slicingUpdate(crc, archIeeeTable8, p) }