checksum_amd64.s 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #include "textflag.h"
  2. // func checksumAVX2(buf []byte, initial uint16) uint16
  3. //
  4. // Computes the RFC 1071 ones-complement sum of buf, seeded with initial.
  5. //
  6. // Algorithm: sum the buffer treating it as a stream of uint32s in machine
  7. // (little-endian) byte order, accumulating into 64-bit lanes (top 32 bits
  8. // hold cross-add carries — at 1 byte / lane / iter we have 32 bits of
  9. // headroom which is far more than the 16 KB/64 KB max practical inputs).
  10. // At the end we fold to 16 bits and byte-swap once to recover the on-wire
  11. // (big-endian) result. RFC 1071 §1.2.B byte-order independence makes this
  12. // equivalent to summing as 16-bit big-endian words.
  13. //
  14. // The ymm accumulators (Y4..Y7) hold 4 uint64 lanes each = 16 parallel
  15. // partial sums. The main loop loads 64 bytes per iter as four 16-byte
  16. // chunks, zero-extending each chunk's four uint32s into a ymm via
  17. // VPMOVZXDQ-from-memory, then VPADDQ into a separate accumulator per
  18. // chunk to break the dep chain. After the vector loop the lane sums are
  19. // horizontally reduced and merged with a scalar accumulator that handles
  20. // the trailing 0..63 bytes plus the (byte-swapped) initial seed.
  21. TEXT ·checksumAVX2(SB), NOSPLIT, $0-34
  22. MOVQ buf_base+0(FP), SI
  23. MOVQ buf_len+8(FP), CX
  24. MOVWQZX initial+24(FP), AX
  25. // Pre-byteswap initial into the LE-summing space so it merges directly
  26. // with the rest of the accumulator. The final fold's bswap16 will undo
  27. // this and convert the whole result back to BE.
  28. XCHGB AH, AL
  29. CMPQ CX, $32
  30. JLT scalar_tail
  31. VPXOR Y4, Y4, Y4
  32. VPXOR Y5, Y5, Y5
  33. VPXOR Y6, Y6, Y6
  34. VPXOR Y7, Y7, Y7
  35. CMPQ CX, $64
  36. JLT loop32
  37. loop64:
  38. VPMOVZXDQ (SI), Y0
  39. VPMOVZXDQ 16(SI), Y1
  40. VPMOVZXDQ 32(SI), Y2
  41. VPMOVZXDQ 48(SI), Y3
  42. VPADDQ Y0, Y4, Y4
  43. VPADDQ Y1, Y5, Y5
  44. VPADDQ Y2, Y6, Y6
  45. VPADDQ Y3, Y7, Y7
  46. ADDQ $64, SI
  47. SUBQ $64, CX
  48. CMPQ CX, $64
  49. JGE loop64
  50. loop32:
  51. CMPQ CX, $32
  52. JLT reduce_vec
  53. VPMOVZXDQ (SI), Y0
  54. VPMOVZXDQ 16(SI), Y1
  55. VPADDQ Y0, Y4, Y4
  56. VPADDQ Y1, Y5, Y5
  57. ADDQ $32, SI
  58. SUBQ $32, CX
  59. JMP loop32
  60. reduce_vec:
  61. // Combine the four ymm accumulators into Y4.
  62. VPADDQ Y5, Y4, Y4
  63. VPADDQ Y7, Y6, Y6
  64. VPADDQ Y6, Y4, Y4
  65. // Horizontally reduce Y4's four uint64 lanes to a single scalar.
  66. VEXTRACTI128 $1, Y4, X5
  67. VPADDQ X5, X4, X4
  68. VPSHUFD $0x4e, X4, X5
  69. VPADDQ X5, X4, X4
  70. VMOVQ X4, R8
  71. VZEROUPPER
  72. ADDQ R8, AX
  73. ADCQ $0, AX
  74. scalar_tail:
  75. // Handle remaining 0..63 bytes (or the entire buffer if it was < 32).
  76. CMPQ CX, $8
  77. JLT tail4
  78. loop8:
  79. ADDQ (SI), AX
  80. ADCQ $0, AX
  81. ADDQ $8, SI
  82. SUBQ $8, CX
  83. CMPQ CX, $8
  84. JGE loop8
  85. tail4:
  86. CMPQ CX, $4
  87. JLT tail2
  88. MOVL (SI), R8
  89. ADDQ R8, AX
  90. ADCQ $0, AX
  91. ADDQ $4, SI
  92. SUBQ $4, CX
  93. tail2:
  94. CMPQ CX, $2
  95. JLT tail1
  96. MOVWQZX (SI), R8
  97. ADDQ R8, AX
  98. ADCQ $0, AX
  99. ADDQ $2, SI
  100. SUBQ $2, CX
  101. tail1:
  102. TESTQ CX, CX
  103. JZ fold
  104. MOVBQZX (SI), R8
  105. ADDQ R8, AX
  106. ADCQ $0, AX
  107. fold:
  108. // Fold the 64-bit accumulator to 16 bits via four rounds, mirroring
  109. // gvisor's reduce(). Each pair (split, add) halves the live width;
  110. // the truncation steps absorb the single bit that may be left over
  111. // after each add so the next round's bound holds.
  112. // 64 → 33 bits.
  113. MOVQ AX, R8
  114. SHRQ $32, R8
  115. MOVL AX, AX
  116. ADDQ R8, AX
  117. // 33 → 32 bits. AX += (AX>>32); truncate to 32. AX is now ≤ 0xFFFF_FFFF.
  118. MOVQ AX, R8
  119. SHRQ $32, R8
  120. ADDQ R8, AX
  121. MOVL AX, AX
  122. // 32 → 17 bits.
  123. MOVQ AX, R8
  124. SHRQ $16, R8
  125. MOVWQZX AX, AX
  126. ADDQ R8, AX
  127. // 17 → 16 bits. AX += (AX>>16); the trailing MOVW truncates bit 16.
  128. MOVQ AX, R8
  129. SHRQ $16, R8
  130. ADDQ R8, AX
  131. // AX low 16 bits hold the 16-bit sum in machine (LE) byte order; flip
  132. // to big-endian to match the gvisor API contract.
  133. XCHGB AH, AL
  134. MOVW AX, ret+32(FP)
  135. RET