github.com/grailbio/base@v0.0.11/simd/invmask_amd64.go.tpl (about) 1 // Copyright 2018 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache-2.0 3 // license that can be found in the LICENSE file. 4 5 // +build amd64,!appengine 6 7 package PACKAGE 8 9 import ( 10 "reflect" 11 "unsafe" 12 ) 13 14 // ZZUnsafeInplace sets main[pos] := arg[pos] OPCHAR main[pos] for every position 15 // in main[]. 16 // 17 // WARNING: This is a function designed to be used in inner loops, which makes 18 // assumptions about length and capacity which aren't checked at runtime. Use 19 // the safe version of this function when that's a problem. 20 // Assumptions #2-3 are always satisfied when the last 21 // potentially-size-increasing operation on arg[] is {Re}makeUnsafe(), 22 // ResizeUnsafe(), or XcapUnsafe(), and the same is true for main[]. 23 // 24 // 1. len(arg) and len(main) must be equal. 25 // 26 // 2. Capacities are at least RoundUpPow2(len(main) + 1, bytesPerVec). 27 // 28 // 3. The caller does not care if a few bytes past the end of main[] are 29 // changed. 30 func ZZUnsafeInplace(main, arg []byte) { 31 mainLen := len(main) 32 argHeader := (*reflect.SliceHeader)(unsafe.Pointer(&arg)) 33 mainHeader := (*reflect.SliceHeader)(unsafe.Pointer(&main)) 34 argWordsIter := unsafe.Pointer(argHeader.Data) 35 mainWordsIter := unsafe.Pointer(mainHeader.Data) 36 if mainLen > 2*BytesPerWord { 37 nWordMinus2 := (mainLen - BytesPerWord - 1) >> Log2BytesPerWord 38 for widx := 0; widx < nWordMinus2; widx++ { 39 mainWord := *((*uintptr)(mainWordsIter)) 40 argWord := *((*uintptr)(argWordsIter)) 41 *((*uintptr)(mainWordsIter)) = mainWord OPCHAR argWord 42 mainWordsIter = unsafe.Pointer(uintptr(mainWordsIter) + BytesPerWord) 43 argWordsIter = unsafe.Pointer(uintptr(argWordsIter) + BytesPerWord) 44 } 45 } else if mainLen <= BytesPerWord { 46 mainWord := *((*uintptr)(mainWordsIter)) 47 argWord := *((*uintptr)(argWordsIter)) 48 *((*uintptr)(mainWordsIter)) = mainWord OPCHAR argWord 49 return 50 } 51 // The last two read-and-writes to main[] usually overlap. To avoid a 52 // store-to-load forwarding slowdown, we read both words before writing 53 // either. 54 // shuffleLookupOddInplaceSSSE3Asm() uses the same strategy. 55 mainWord1 := *((*uintptr)(mainWordsIter)) 56 argWord1 := *((*uintptr)(argWordsIter)) 57 finalOffset := uintptr(mainLen - BytesPerWord) 58 mainFinalWordPtr := unsafe.Pointer(mainHeader.Data + finalOffset) 59 argFinalWordPtr := unsafe.Pointer(argHeader.Data + finalOffset) 60 mainWord2 := *((*uintptr)(mainFinalWordPtr)) 61 argWord2 := *((*uintptr)(argFinalWordPtr)) 62 *((*uintptr)(mainWordsIter)) = mainWord1 OPCHAR argWord1 63 *((*uintptr)(mainFinalWordPtr)) = mainWord2 OPCHAR argWord2 64 } 65 66 // ZZInplace sets main[pos] := arg[pos] OPCHAR main[pos] for every position in 67 // main[]. It panics if slice lengths don't match. 68 func ZZInplace(main, arg []byte) { 69 // This takes ~6-8% longer than ZZUnsafeInplace on the short-array benchmark 70 // on my Mac. 71 mainLen := len(main) 72 if len(arg) != mainLen { 73 panic("ZZInplace() requires len(arg) == len(main).") 74 } 75 if mainLen < BytesPerWord { 76 // It's probably possible to do better here (e.g. when mainLen is in 4..7, 77 // operate on uint32s), but I won't worry about it unless/until that's 78 // actually a common case. 79 for pos, argByte := range arg { 80 main[pos] = main[pos] OPCHAR argByte 81 } 82 return 83 } 84 argHeader := (*reflect.SliceHeader)(unsafe.Pointer(&arg)) 85 mainHeader := (*reflect.SliceHeader)(unsafe.Pointer(&main)) 86 argWordsIter := unsafe.Pointer(argHeader.Data) 87 mainWordsIter := unsafe.Pointer(mainHeader.Data) 88 if mainLen > 2*BytesPerWord { 89 nWordMinus2 := (mainLen - BytesPerWord - 1) >> Log2BytesPerWord 90 for widx := 0; widx < nWordMinus2; widx++ { 91 mainWord := *((*uintptr)(mainWordsIter)) 92 argWord := *((*uintptr)(argWordsIter)) 93 *((*uintptr)(mainWordsIter)) = mainWord OPCHAR argWord 94 mainWordsIter = unsafe.Pointer(uintptr(mainWordsIter) + BytesPerWord) 95 argWordsIter = unsafe.Pointer(uintptr(argWordsIter) + BytesPerWord) 96 } 97 } 98 mainWord1 := *((*uintptr)(mainWordsIter)) 99 argWord1 := *((*uintptr)(argWordsIter)) 100 finalOffset := uintptr(mainLen - BytesPerWord) 101 mainFinalWordPtr := unsafe.Pointer(mainHeader.Data + finalOffset) 102 argFinalWordPtr := unsafe.Pointer(argHeader.Data + finalOffset) 103 mainWord2 := *((*uintptr)(mainFinalWordPtr)) 104 argWord2 := *((*uintptr)(argFinalWordPtr)) 105 *((*uintptr)(mainWordsIter)) = mainWord1 OPCHAR argWord1 106 *((*uintptr)(mainFinalWordPtr)) = mainWord2 OPCHAR argWord2 107 } 108 109 // ZZUnsafe sets dst[pos] := src1[pos] OPCHAR src2[pos] for every position in dst. 110 // 111 // WARNING: This is a function designed to be used in inner loops, which makes 112 // assumptions about length and capacity which aren't checked at runtime. Use 113 // the safe version of this function when that's a problem. 114 // Assumptions #2-3 are always satisfied when the last 115 // potentially-size-increasing operation on src1[] is {Re}makeUnsafe(), 116 // ResizeUnsafe(), or XcapUnsafe(), and the same is true for src2[] and dst[]. 117 // 118 // 1. len(src1), len(src2), and len(dst) must be equal. 119 // 120 // 2. Capacities are at least RoundUpPow2(len(dst) + 1, bytesPerVec). 121 // 122 // 3. The caller does not care if a few bytes past the end of dst[] are 123 // changed. 124 func ZZUnsafe(dst, src1, src2 []byte) { 125 src1Header := (*reflect.SliceHeader)(unsafe.Pointer(&src1)) 126 src2Header := (*reflect.SliceHeader)(unsafe.Pointer(&src2)) 127 dstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dst)) 128 nWord := DivUpPow2(len(dst), BytesPerWord, Log2BytesPerWord) 129 130 src1Iter := unsafe.Pointer(src1Header.Data) 131 src2Iter := unsafe.Pointer(src2Header.Data) 132 dstIter := unsafe.Pointer(dstHeader.Data) 133 for widx := 0; widx < nWord; widx++ { 134 src1Word := *((*uintptr)(src1Iter)) 135 src2Word := *((*uintptr)(src2Iter)) 136 *((*uintptr)(dstIter)) = src1Word OPCHAR src2Word 137 src1Iter = unsafe.Pointer(uintptr(src1Iter) + BytesPerWord) 138 src2Iter = unsafe.Pointer(uintptr(src2Iter) + BytesPerWord) 139 dstIter = unsafe.Pointer(uintptr(dstIter) + BytesPerWord) 140 } 141 } 142 143 // ZZ sets dst[pos] := src1[pos] OPCHAR src2[pos] for every position in dst. It 144 // panics if slice lengths don't match. 145 func ZZ(dst, src1, src2 []byte) { 146 dstLen := len(dst) 147 if (len(src1) != dstLen) || (len(src2) != dstLen) { 148 panic("ZZ() requires len(src1) == len(src2) == len(dst).") 149 } 150 if dstLen < BytesPerWord { 151 for pos, src1Byte := range src1 { 152 dst[pos] = src1Byte OPCHAR src2[pos] 153 } 154 return 155 } 156 src1Header := (*reflect.SliceHeader)(unsafe.Pointer(&src1)) 157 src2Header := (*reflect.SliceHeader)(unsafe.Pointer(&src2)) 158 dstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dst)) 159 nWordMinus1 := (dstLen - 1) >> Log2BytesPerWord 160 161 src1Iter := unsafe.Pointer(src1Header.Data) 162 src2Iter := unsafe.Pointer(src2Header.Data) 163 dstIter := unsafe.Pointer(dstHeader.Data) 164 for widx := 0; widx < nWordMinus1; widx++ { 165 src1Word := *((*uintptr)(src1Iter)) 166 src2Word := *((*uintptr)(src2Iter)) 167 *((*uintptr)(dstIter)) = src1Word OPCHAR src2Word 168 src1Iter = unsafe.Pointer(uintptr(src1Iter) + BytesPerWord) 169 src2Iter = unsafe.Pointer(uintptr(src2Iter) + BytesPerWord) 170 dstIter = unsafe.Pointer(uintptr(dstIter) + BytesPerWord) 171 } 172 // No store-forwarding problem here. 173 finalOffset := uintptr(dstLen - BytesPerWord) 174 src1Iter = unsafe.Pointer(src1Header.Data + finalOffset) 175 src2Iter = unsafe.Pointer(src2Header.Data + finalOffset) 176 dstIter = unsafe.Pointer(dstHeader.Data + finalOffset) 177 src1Word := *((*uintptr)(src1Iter)) 178 src2Word := *((*uintptr)(src2Iter)) 179 *((*uintptr)(dstIter)) = src1Word OPCHAR src2Word 180 }