| // generated by go run gen.go; DO NOT EDIT |
| |
| // +build !appengine |
| // +build gc |
| // +build !noasm |
| |
| #include "textflag.h" |
| |
| // fl is short for floating point math. fx is short for fixed point math. |
| |
| DATA flAlmost65536<>+0x00(SB)/8, $0x477fffff477fffff |
| DATA flAlmost65536<>+0x08(SB)/8, $0x477fffff477fffff |
| DATA flOne<>+0x00(SB)/8, $0x3f8000003f800000 |
| DATA flOne<>+0x08(SB)/8, $0x3f8000003f800000 |
| DATA flSignMask<>+0x00(SB)/8, $0x7fffffff7fffffff |
| DATA flSignMask<>+0x08(SB)/8, $0x7fffffff7fffffff |
| |
| // scatterAndMulBy0x101 is a PSHUFB mask that brings the low four bytes of an |
| // XMM register to the low byte of that register's four uint32 values. It |
| // duplicates those bytes, effectively multiplying each uint32 by 0x101. |
| // |
| // It transforms a little-endian 16-byte XMM value from |
| // ijkl???????????? |
| // to |
| // ii00jj00kk00ll00 |
| DATA scatterAndMulBy0x101<>+0x00(SB)/8, $0x8080010180800000 |
| DATA scatterAndMulBy0x101<>+0x08(SB)/8, $0x8080030380800202 |
| |
| // gather is a PSHUFB mask that brings the second-lowest byte of the XMM |
| // register's four uint32 values to the low four bytes of that register. |
| // |
| // It transforms a little-endian 16-byte XMM value from |
| // ?i???j???k???l?? |
| // to |
| // ijkl000000000000 |
| DATA gather<>+0x00(SB)/8, $0x808080800d090501 |
| DATA gather<>+0x08(SB)/8, $0x8080808080808080 |
| |
| DATA fxAlmost65536<>+0x00(SB)/8, $0x0000ffff0000ffff |
| DATA fxAlmost65536<>+0x08(SB)/8, $0x0000ffff0000ffff |
| DATA inverseFFFF<>+0x00(SB)/8, $0x8000800180008001 |
| DATA inverseFFFF<>+0x08(SB)/8, $0x8000800180008001 |
| |
| GLOBL flAlmost65536<>(SB), (NOPTR+RODATA), $16 |
| GLOBL flOne<>(SB), (NOPTR+RODATA), $16 |
| GLOBL flSignMask<>(SB), (NOPTR+RODATA), $16 |
| GLOBL scatterAndMulBy0x101<>(SB), (NOPTR+RODATA), $16 |
| GLOBL gather<>(SB), (NOPTR+RODATA), $16 |
| GLOBL fxAlmost65536<>(SB), (NOPTR+RODATA), $16 |
| GLOBL inverseFFFF<>(SB), (NOPTR+RODATA), $16 |
| |
| // func haveSSE4_1() bool |
| TEXT ·haveSSE4_1(SB), NOSPLIT, $0 |
| MOVQ $1, AX |
| CPUID |
| SHRQ $19, CX |
| ANDQ $1, CX |
| MOVB CX, ret+0(FP) |
| RET |
| |
| // ---------------------------------------------------------------------------- |
| |
| // func fixedAccumulateOpOverSIMD(dst []uint8, src []uint32) |
| // |
| // XMM registers. Variable names are per |
| // https://github.com/google/font-rs/blob/master/src/accumulate.c |
| // |
| // xmm0 scratch |
| // xmm1 x |
| // xmm2 y, z |
| // xmm3 - |
| // xmm4 - |
| // xmm5 fxAlmost65536 |
| // xmm6 gather |
| // xmm7 offset |
| // xmm8 scatterAndMulBy0x101 |
| // xmm9 fxAlmost65536 |
| // xmm10 inverseFFFF |
| TEXT ·fixedAccumulateOpOverSIMD(SB), NOSPLIT, $0-48 |
| |
| MOVQ dst_base+0(FP), DI |
| MOVQ dst_len+8(FP), BX |
| MOVQ src_base+24(FP), SI |
| MOVQ src_len+32(FP), R10 |
| |
| // Sanity check that len(dst) >= len(src). |
| CMPQ BX, R10 |
| JLT fxAccOpOverEnd |
| |
| // R10 = len(src) &^ 3 |
| // R11 = len(src) |
| MOVQ R10, R11 |
| ANDQ $-4, R10 |
| |
| // fxAlmost65536 := XMM(0x0000ffff repeated four times) // Maximum of an uint16. |
| MOVOU fxAlmost65536<>(SB), X5 |
| |
| // gather := XMM(see above) // PSHUFB shuffle mask. |
| // scatterAndMulBy0x101 := XMM(see above) // PSHUFB shuffle mask. |
| // fxAlmost65536 := XMM(0x0000ffff repeated four times) // 0xffff. |
| // inverseFFFF := XMM(0x80008001 repeated four times) // Magic constant for dividing by 0xffff. |
| MOVOU gather<>(SB), X6 |
| MOVOU scatterAndMulBy0x101<>(SB), X8 |
| MOVOU fxAlmost65536<>(SB), X9 |
| MOVOU inverseFFFF<>(SB), X10 |
| |
| // offset := XMM(0x00000000 repeated four times) // Cumulative sum. |
| XORPS X7, X7 |
| |
| // i := 0 |
| MOVQ $0, R9 |
| |
| fxAccOpOverLoop4: |
| // for i < (len(src) &^ 3) |
| CMPQ R9, R10 |
| JAE fxAccOpOverLoop1 |
| |
| // x = XMM(s0, s1, s2, s3) |
| // |
| // Where s0 is src[i+0], s1 is src[i+1], etc. |
| MOVOU (SI), X1 |
| |
| // scratch = XMM(0, s0, s1, s2) |
| // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) |
| MOVOU X1, X0 |
| PSLLO $4, X0 |
| PADDD X0, X1 |
| |
| // scratch = XMM(0, 0, 0, 0) |
| // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) |
| // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) |
| XORPS X0, X0 |
| SHUFPS $0x40, X1, X0 |
| PADDD X0, X1 |
| |
| // x += offset |
| PADDD X7, X1 |
| |
| // y = abs(x) |
| // y >>= 2 // Shift by 2*ϕ - 16. |
| // y = min(y, fxAlmost65536) |
| // |
| // pabsd %xmm1,%xmm2 |
| // psrld $0x2,%xmm2 |
| // pminud %xmm5,%xmm2 |
| // |
| // Hopefully we'll get these opcode mnemonics into the assembler for Go |
| // 1.8. https://golang.org/issue/16007 isn't exactly the same thing, but |
| // it's similar. |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x1e; BYTE $0xd1 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x72; BYTE $0xd2; BYTE $0x02 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x3b; BYTE $0xd5 |
| |
| // z = convertToInt32(y) |
| // No-op. |
| |
| // Blend over the dst's prior value. SIMD for i in 0..3: |
| // |
| // dstA := uint32(dst[i]) * 0x101 |
| // maskA := z@i |
| // outA := dstA*(0xffff-maskA)/0xffff + maskA |
| // dst[i] = uint8(outA >> 8) |
| // |
| // First, set X0 to dstA*(0xfff-maskA). |
| MOVL (DI), X0 |
| PSHUFB X8, X0 |
| MOVOU X9, X11 |
| PSUBL X2, X11 |
| PMULLD X11, X0 |
| |
| // We implement uint32 division by 0xffff as multiplication by a magic |
| // constant (0x800080001) and then a shift by a magic constant (47). |
| // See TestDivideByFFFF for a justification. |
| // |
| // That multiplication widens from uint32 to uint64, so we have to |
| // duplicate and shift our four uint32s from one XMM register (X0) to |
| // two XMM registers (X0 and X11). |
| // |
| // Move the second and fourth uint32s in X0 to be the first and third |
| // uint32s in X11. |
| MOVOU X0, X11 |
| PSRLQ $32, X11 |
| |
| // Multiply by magic, shift by magic. |
| // |
| // pmuludq %xmm10,%xmm0 |
| // pmuludq %xmm10,%xmm11 |
| BYTE $0x66; BYTE $0x41; BYTE $0x0f; BYTE $0xf4; BYTE $0xc2 |
| BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0xf4; BYTE $0xda |
| PSRLQ $47, X0 |
| PSRLQ $47, X11 |
| |
| // Merge the two registers back to one, X11, and add maskA. |
| PSLLQ $32, X11 |
| XORPS X0, X11 |
| PADDD X11, X2 |
| |
| // As per opSrcStore4, shuffle and copy the 4 second-lowest bytes. |
| PSHUFB X6, X2 |
| MOVL X2, (DI) |
| |
| // offset = XMM(x@3, x@3, x@3, x@3) |
| MOVOU X1, X7 |
| SHUFPS $0xff, X1, X7 |
| |
| // i += 4 |
| // dst = dst[4:] |
| // src = src[4:] |
| ADDQ $4, R9 |
| ADDQ $4, DI |
| ADDQ $16, SI |
| JMP fxAccOpOverLoop4 |
| |
| fxAccOpOverLoop1: |
| // for i < len(src) |
| CMPQ R9, R11 |
| JAE fxAccOpOverEnd |
| |
| // x = src[i] + offset |
| MOVL (SI), X1 |
| PADDD X7, X1 |
| |
| // y = abs(x) |
| // y >>= 2 // Shift by 2*ϕ - 16. |
| // y = min(y, fxAlmost65536) |
| // |
| // pabsd %xmm1,%xmm2 |
| // psrld $0x2,%xmm2 |
| // pminud %xmm5,%xmm2 |
| // |
| // Hopefully we'll get these opcode mnemonics into the assembler for Go |
| // 1.8. https://golang.org/issue/16007 isn't exactly the same thing, but |
| // it's similar. |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x1e; BYTE $0xd1 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x72; BYTE $0xd2; BYTE $0x02 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x3b; BYTE $0xd5 |
| |
| // z = convertToInt32(y) |
| // No-op. |
| |
| // Blend over the dst's prior value. |
| // |
| // dstA := uint32(dst[0]) * 0x101 |
| // maskA := z |
| // outA := dstA*(0xffff-maskA)/0xffff + maskA |
| // dst[0] = uint8(outA >> 8) |
| MOVBLZX (DI), R12 |
| IMULL $0x101, R12 |
| MOVL X2, R13 |
| MOVL $0xffff, AX |
| SUBL R13, AX |
| MULL R12 // MULL's implicit arg is AX, and the result is stored in DX:AX. |
| MOVL $0x80008001, BX // Divide by 0xffff is to first multiply by a magic constant... |
| MULL BX // MULL's implicit arg is AX, and the result is stored in DX:AX. |
| SHRL $15, DX // ...and then shift by another magic constant (47 - 32 = 15). |
| ADDL DX, R13 |
| SHRL $8, R13 |
| MOVB R13, (DI) |
| |
| // offset = x |
| MOVOU X1, X7 |
| |
| // i += 1 |
| // dst = dst[1:] |
| // src = src[1:] |
| ADDQ $1, R9 |
| ADDQ $1, DI |
| ADDQ $4, SI |
| JMP fxAccOpOverLoop1 |
| |
| fxAccOpOverEnd: |
| RET |
| |
| // ---------------------------------------------------------------------------- |
| |
| // func fixedAccumulateOpSrcSIMD(dst []uint8, src []uint32) |
| // |
| // XMM registers. Variable names are per |
| // https://github.com/google/font-rs/blob/master/src/accumulate.c |
| // |
| // xmm0 scratch |
| // xmm1 x |
| // xmm2 y, z |
| // xmm3 - |
| // xmm4 - |
| // xmm5 fxAlmost65536 |
| // xmm6 gather |
| // xmm7 offset |
| // xmm8 - |
| // xmm9 - |
| // xmm10 - |
| TEXT ·fixedAccumulateOpSrcSIMD(SB), NOSPLIT, $0-48 |
| |
| MOVQ dst_base+0(FP), DI |
| MOVQ dst_len+8(FP), BX |
| MOVQ src_base+24(FP), SI |
| MOVQ src_len+32(FP), R10 |
| |
| // Sanity check that len(dst) >= len(src). |
| CMPQ BX, R10 |
| JLT fxAccOpSrcEnd |
| |
| // R10 = len(src) &^ 3 |
| // R11 = len(src) |
| MOVQ R10, R11 |
| ANDQ $-4, R10 |
| |
| // fxAlmost65536 := XMM(0x0000ffff repeated four times) // Maximum of an uint16. |
| MOVOU fxAlmost65536<>(SB), X5 |
| |
| // gather := XMM(see above) // PSHUFB shuffle mask. |
| MOVOU gather<>(SB), X6 |
| |
| // offset := XMM(0x00000000 repeated four times) // Cumulative sum. |
| XORPS X7, X7 |
| |
| // i := 0 |
| MOVQ $0, R9 |
| |
| fxAccOpSrcLoop4: |
| // for i < (len(src) &^ 3) |
| CMPQ R9, R10 |
| JAE fxAccOpSrcLoop1 |
| |
| // x = XMM(s0, s1, s2, s3) |
| // |
| // Where s0 is src[i+0], s1 is src[i+1], etc. |
| MOVOU (SI), X1 |
| |
| // scratch = XMM(0, s0, s1, s2) |
| // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) |
| MOVOU X1, X0 |
| PSLLO $4, X0 |
| PADDD X0, X1 |
| |
| // scratch = XMM(0, 0, 0, 0) |
| // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) |
| // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) |
| XORPS X0, X0 |
| SHUFPS $0x40, X1, X0 |
| PADDD X0, X1 |
| |
| // x += offset |
| PADDD X7, X1 |
| |
| // y = abs(x) |
| // y >>= 2 // Shift by 2*ϕ - 16. |
| // y = min(y, fxAlmost65536) |
| // |
| // pabsd %xmm1,%xmm2 |
| // psrld $0x2,%xmm2 |
| // pminud %xmm5,%xmm2 |
| // |
| // Hopefully we'll get these opcode mnemonics into the assembler for Go |
| // 1.8. https://golang.org/issue/16007 isn't exactly the same thing, but |
| // it's similar. |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x1e; BYTE $0xd1 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x72; BYTE $0xd2; BYTE $0x02 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x3b; BYTE $0xd5 |
| |
| // z = convertToInt32(y) |
| // No-op. |
| |
| // z = shuffleTheSecondLowestBytesOfEach4ByteElement(z) |
| // copy(dst[:4], low4BytesOf(z)) |
| PSHUFB X6, X2 |
| MOVL X2, (DI) |
| |
| // offset = XMM(x@3, x@3, x@3, x@3) |
| MOVOU X1, X7 |
| SHUFPS $0xff, X1, X7 |
| |
| // i += 4 |
| // dst = dst[4:] |
| // src = src[4:] |
| ADDQ $4, R9 |
| ADDQ $4, DI |
| ADDQ $16, SI |
| JMP fxAccOpSrcLoop4 |
| |
| fxAccOpSrcLoop1: |
| // for i < len(src) |
| CMPQ R9, R11 |
| JAE fxAccOpSrcEnd |
| |
| // x = src[i] + offset |
| MOVL (SI), X1 |
| PADDD X7, X1 |
| |
| // y = abs(x) |
| // y >>= 2 // Shift by 2*ϕ - 16. |
| // y = min(y, fxAlmost65536) |
| // |
| // pabsd %xmm1,%xmm2 |
| // psrld $0x2,%xmm2 |
| // pminud %xmm5,%xmm2 |
| // |
| // Hopefully we'll get these opcode mnemonics into the assembler for Go |
| // 1.8. https://golang.org/issue/16007 isn't exactly the same thing, but |
| // it's similar. |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x1e; BYTE $0xd1 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x72; BYTE $0xd2; BYTE $0x02 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x3b; BYTE $0xd5 |
| |
| // z = convertToInt32(y) |
| // No-op. |
| |
| // dst[0] = uint8(z>>8) |
| MOVL X2, BX |
| SHRL $8, BX |
| MOVB BX, (DI) |
| |
| // offset = x |
| MOVOU X1, X7 |
| |
| // i += 1 |
| // dst = dst[1:] |
| // src = src[1:] |
| ADDQ $1, R9 |
| ADDQ $1, DI |
| ADDQ $4, SI |
| JMP fxAccOpSrcLoop1 |
| |
| fxAccOpSrcEnd: |
| RET |
| |
| // ---------------------------------------------------------------------------- |
| |
| // func fixedAccumulateMaskSIMD(buf []uint32) |
| // |
| // XMM registers. Variable names are per |
| // https://github.com/google/font-rs/blob/master/src/accumulate.c |
| // |
| // xmm0 scratch |
| // xmm1 x |
| // xmm2 y, z |
| // xmm3 - |
| // xmm4 - |
| // xmm5 fxAlmost65536 |
| // xmm6 - |
| // xmm7 offset |
| // xmm8 - |
| // xmm9 - |
| // xmm10 - |
| TEXT ·fixedAccumulateMaskSIMD(SB), NOSPLIT, $0-24 |
| |
| MOVQ buf_base+0(FP), DI |
| MOVQ buf_len+8(FP), BX |
| MOVQ buf_base+0(FP), SI |
| MOVQ buf_len+8(FP), R10 |
| |
| // R10 = len(src) &^ 3 |
| // R11 = len(src) |
| MOVQ R10, R11 |
| ANDQ $-4, R10 |
| |
| // fxAlmost65536 := XMM(0x0000ffff repeated four times) // Maximum of an uint16. |
| MOVOU fxAlmost65536<>(SB), X5 |
| |
| // offset := XMM(0x00000000 repeated four times) // Cumulative sum. |
| XORPS X7, X7 |
| |
| // i := 0 |
| MOVQ $0, R9 |
| |
| fxAccMaskLoop4: |
| // for i < (len(src) &^ 3) |
| CMPQ R9, R10 |
| JAE fxAccMaskLoop1 |
| |
| // x = XMM(s0, s1, s2, s3) |
| // |
| // Where s0 is src[i+0], s1 is src[i+1], etc. |
| MOVOU (SI), X1 |
| |
| // scratch = XMM(0, s0, s1, s2) |
| // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) |
| MOVOU X1, X0 |
| PSLLO $4, X0 |
| PADDD X0, X1 |
| |
| // scratch = XMM(0, 0, 0, 0) |
| // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) |
| // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) |
| XORPS X0, X0 |
| SHUFPS $0x40, X1, X0 |
| PADDD X0, X1 |
| |
| // x += offset |
| PADDD X7, X1 |
| |
| // y = abs(x) |
| // y >>= 2 // Shift by 2*ϕ - 16. |
| // y = min(y, fxAlmost65536) |
| // |
| // pabsd %xmm1,%xmm2 |
| // psrld $0x2,%xmm2 |
| // pminud %xmm5,%xmm2 |
| // |
| // Hopefully we'll get these opcode mnemonics into the assembler for Go |
| // 1.8. https://golang.org/issue/16007 isn't exactly the same thing, but |
| // it's similar. |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x1e; BYTE $0xd1 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x72; BYTE $0xd2; BYTE $0x02 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x3b; BYTE $0xd5 |
| |
| // z = convertToInt32(y) |
| // No-op. |
| |
| // copy(dst[:4], z) |
| MOVOU X2, (DI) |
| |
| // offset = XMM(x@3, x@3, x@3, x@3) |
| MOVOU X1, X7 |
| SHUFPS $0xff, X1, X7 |
| |
| // i += 4 |
| // dst = dst[4:] |
| // src = src[4:] |
| ADDQ $4, R9 |
| ADDQ $16, DI |
| ADDQ $16, SI |
| JMP fxAccMaskLoop4 |
| |
| fxAccMaskLoop1: |
| // for i < len(src) |
| CMPQ R9, R11 |
| JAE fxAccMaskEnd |
| |
| // x = src[i] + offset |
| MOVL (SI), X1 |
| PADDD X7, X1 |
| |
| // y = abs(x) |
| // y >>= 2 // Shift by 2*ϕ - 16. |
| // y = min(y, fxAlmost65536) |
| // |
| // pabsd %xmm1,%xmm2 |
| // psrld $0x2,%xmm2 |
| // pminud %xmm5,%xmm2 |
| // |
| // Hopefully we'll get these opcode mnemonics into the assembler for Go |
| // 1.8. https://golang.org/issue/16007 isn't exactly the same thing, but |
| // it's similar. |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x1e; BYTE $0xd1 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x72; BYTE $0xd2; BYTE $0x02 |
| BYTE $0x66; BYTE $0x0f; BYTE $0x38; BYTE $0x3b; BYTE $0xd5 |
| |
| // z = convertToInt32(y) |
| // No-op. |
| |
| // dst[0] = uint32(z) |
| MOVL X2, (DI) |
| |
| // offset = x |
| MOVOU X1, X7 |
| |
| // i += 1 |
| // dst = dst[1:] |
| // src = src[1:] |
| ADDQ $1, R9 |
| ADDQ $4, DI |
| ADDQ $4, SI |
| JMP fxAccMaskLoop1 |
| |
| fxAccMaskEnd: |
| RET |
| |
| // ---------------------------------------------------------------------------- |
| |
| // func floatingAccumulateOpOverSIMD(dst []uint8, src []float32) |
| // |
| // XMM registers. Variable names are per |
| // https://github.com/google/font-rs/blob/master/src/accumulate.c |
| // |
| // xmm0 scratch |
| // xmm1 x |
| // xmm2 y, z |
| // xmm3 flSignMask |
| // xmm4 flOne |
| // xmm5 flAlmost65536 |
| // xmm6 gather |
| // xmm7 offset |
| // xmm8 scatterAndMulBy0x101 |
| // xmm9 fxAlmost65536 |
| // xmm10 inverseFFFF |
| TEXT ·floatingAccumulateOpOverSIMD(SB), NOSPLIT, $8-48 |
| |
| MOVQ dst_base+0(FP), DI |
| MOVQ dst_len+8(FP), BX |
| MOVQ src_base+24(FP), SI |
| MOVQ src_len+32(FP), R10 |
| |
| // Sanity check that len(dst) >= len(src). |
| CMPQ BX, R10 |
| JLT flAccOpOverEnd |
| |
| // R10 = len(src) &^ 3 |
| // R11 = len(src) |
| MOVQ R10, R11 |
| ANDQ $-4, R10 |
| |
| // Prepare to set MXCSR bits 13 and 14, so that the CVTPS2PL below is |
| // "Round To Zero". |
| STMXCSR mxcsrOrig-8(SP) |
| MOVL mxcsrOrig-8(SP), AX |
| ORL $0x6000, AX |
| MOVL AX, mxcsrNew-4(SP) |
| |
| // flSignMask := XMM(0x7fffffff repeated four times) // All but the sign bit of a float32. |
| // flOne := XMM(0x3f800000 repeated four times) // 1 as a float32. |
| // flAlmost65536 := XMM(0x477fffff repeated four times) // 255.99998 * 256 as a float32. |
| MOVOU flSignMask<>(SB), X3 |
| MOVOU flOne<>(SB), X4 |
| MOVOU flAlmost65536<>(SB), X5 |
| |
| // gather := XMM(see above) // PSHUFB shuffle mask. |
| // scatterAndMulBy0x101 := XMM(see above) // PSHUFB shuffle mask. |
| // fxAlmost65536 := XMM(0x0000ffff repeated four times) // 0xffff. |
| // inverseFFFF := XMM(0x80008001 repeated four times) // Magic constant for dividing by 0xffff. |
| MOVOU gather<>(SB), X6 |
| MOVOU scatterAndMulBy0x101<>(SB), X8 |
| MOVOU fxAlmost65536<>(SB), X9 |
| MOVOU inverseFFFF<>(SB), X10 |
| |
| // offset := XMM(0x00000000 repeated four times) // Cumulative sum. |
| XORPS X7, X7 |
| |
| // i := 0 |
| MOVQ $0, R9 |
| |
| flAccOpOverLoop4: |
| // for i < (len(src) &^ 3) |
| CMPQ R9, R10 |
| JAE flAccOpOverLoop1 |
| |
| // x = XMM(s0, s1, s2, s3) |
| // |
| // Where s0 is src[i+0], s1 is src[i+1], etc. |
| MOVOU (SI), X1 |
| |
| // scratch = XMM(0, s0, s1, s2) |
| // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) |
| MOVOU X1, X0 |
| PSLLO $4, X0 |
| ADDPS X0, X1 |
| |
| // scratch = XMM(0, 0, 0, 0) |
| // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) |
| // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) |
| XORPS X0, X0 |
| SHUFPS $0x40, X1, X0 |
| ADDPS X0, X1 |
| |
| // x += offset |
| ADDPS X7, X1 |
| |
| // y = x & flSignMask |
| // y = min(y, flOne) |
| // y = mul(y, flAlmost65536) |
| MOVOU X3, X2 |
| ANDPS X1, X2 |
| MINPS X4, X2 |
| MULPS X5, X2 |
| |
| // z = convertToInt32(y) |
| LDMXCSR mxcsrNew-4(SP) |
| CVTPS2PL X2, X2 |
| LDMXCSR mxcsrOrig-8(SP) |
| |
| // Blend over the dst's prior value. SIMD for i in 0..3: |
| // |
| // dstA := uint32(dst[i]) * 0x101 |
| // maskA := z@i |
| // outA := dstA*(0xffff-maskA)/0xffff + maskA |
| // dst[i] = uint8(outA >> 8) |
| // |
| // First, set X0 to dstA*(0xfff-maskA). |
| MOVL (DI), X0 |
| PSHUFB X8, X0 |
| MOVOU X9, X11 |
| PSUBL X2, X11 |
| PMULLD X11, X0 |
| |
| // We implement uint32 division by 0xffff as multiplication by a magic |
| // constant (0x800080001) and then a shift by a magic constant (47). |
| // See TestDivideByFFFF for a justification. |
| // |
| // That multiplication widens from uint32 to uint64, so we have to |
| // duplicate and shift our four uint32s from one XMM register (X0) to |
| // two XMM registers (X0 and X11). |
| // |
| // Move the second and fourth uint32s in X0 to be the first and third |
| // uint32s in X11. |
| MOVOU X0, X11 |
| PSRLQ $32, X11 |
| |
| // Multiply by magic, shift by magic. |
| // |
| // pmuludq %xmm10,%xmm0 |
| // pmuludq %xmm10,%xmm11 |
| BYTE $0x66; BYTE $0x41; BYTE $0x0f; BYTE $0xf4; BYTE $0xc2 |
| BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0xf4; BYTE $0xda |
| PSRLQ $47, X0 |
| PSRLQ $47, X11 |
| |
| // Merge the two registers back to one, X11, and add maskA. |
| PSLLQ $32, X11 |
| XORPS X0, X11 |
| PADDD X11, X2 |
| |
| // As per opSrcStore4, shuffle and copy the 4 second-lowest bytes. |
| PSHUFB X6, X2 |
| MOVL X2, (DI) |
| |
| // offset = XMM(x@3, x@3, x@3, x@3) |
| MOVOU X1, X7 |
| SHUFPS $0xff, X1, X7 |
| |
| // i += 4 |
| // dst = dst[4:] |
| // src = src[4:] |
| ADDQ $4, R9 |
| ADDQ $4, DI |
| ADDQ $16, SI |
| JMP flAccOpOverLoop4 |
| |
| flAccOpOverLoop1: |
| // for i < len(src) |
| CMPQ R9, R11 |
| JAE flAccOpOverEnd |
| |
| // x = src[i] + offset |
| MOVL (SI), X1 |
| ADDPS X7, X1 |
| |
| // y = x & flSignMask |
| // y = min(y, flOne) |
| // y = mul(y, flAlmost65536) |
| MOVOU X3, X2 |
| ANDPS X1, X2 |
| MINPS X4, X2 |
| MULPS X5, X2 |
| |
| // z = convertToInt32(y) |
| LDMXCSR mxcsrNew-4(SP) |
| CVTPS2PL X2, X2 |
| LDMXCSR mxcsrOrig-8(SP) |
| |
| // Blend over the dst's prior value. |
| // |
| // dstA := uint32(dst[0]) * 0x101 |
| // maskA := z |
| // outA := dstA*(0xffff-maskA)/0xffff + maskA |
| // dst[0] = uint8(outA >> 8) |
| MOVBLZX (DI), R12 |
| IMULL $0x101, R12 |
| MOVL X2, R13 |
| MOVL $0xffff, AX |
| SUBL R13, AX |
| MULL R12 // MULL's implicit arg is AX, and the result is stored in DX:AX. |
| MOVL $0x80008001, BX // Divide by 0xffff is to first multiply by a magic constant... |
| MULL BX // MULL's implicit arg is AX, and the result is stored in DX:AX. |
| SHRL $15, DX // ...and then shift by another magic constant (47 - 32 = 15). |
| ADDL DX, R13 |
| SHRL $8, R13 |
| MOVB R13, (DI) |
| |
| // offset = x |
| MOVOU X1, X7 |
| |
| // i += 1 |
| // dst = dst[1:] |
| // src = src[1:] |
| ADDQ $1, R9 |
| ADDQ $1, DI |
| ADDQ $4, SI |
| JMP flAccOpOverLoop1 |
| |
| flAccOpOverEnd: |
| RET |
| |
| // ---------------------------------------------------------------------------- |
| |
| // func floatingAccumulateOpSrcSIMD(dst []uint8, src []float32) |
| // |
| // XMM registers. Variable names are per |
| // https://github.com/google/font-rs/blob/master/src/accumulate.c |
| // |
| // xmm0 scratch |
| // xmm1 x |
| // xmm2 y, z |
| // xmm3 flSignMask |
| // xmm4 flOne |
| // xmm5 flAlmost65536 |
| // xmm6 gather |
| // xmm7 offset |
| // xmm8 - |
| // xmm9 - |
| // xmm10 - |
| TEXT ·floatingAccumulateOpSrcSIMD(SB), NOSPLIT, $8-48 |
| |
| MOVQ dst_base+0(FP), DI |
| MOVQ dst_len+8(FP), BX |
| MOVQ src_base+24(FP), SI |
| MOVQ src_len+32(FP), R10 |
| |
| // Sanity check that len(dst) >= len(src). |
| CMPQ BX, R10 |
| JLT flAccOpSrcEnd |
| |
| // R10 = len(src) &^ 3 |
| // R11 = len(src) |
| MOVQ R10, R11 |
| ANDQ $-4, R10 |
| |
| // Prepare to set MXCSR bits 13 and 14, so that the CVTPS2PL below is |
| // "Round To Zero". |
| STMXCSR mxcsrOrig-8(SP) |
| MOVL mxcsrOrig-8(SP), AX |
| ORL $0x6000, AX |
| MOVL AX, mxcsrNew-4(SP) |
| |
| // flSignMask := XMM(0x7fffffff repeated four times) // All but the sign bit of a float32. |
| // flOne := XMM(0x3f800000 repeated four times) // 1 as a float32. |
| // flAlmost65536 := XMM(0x477fffff repeated four times) // 255.99998 * 256 as a float32. |
| MOVOU flSignMask<>(SB), X3 |
| MOVOU flOne<>(SB), X4 |
| MOVOU flAlmost65536<>(SB), X5 |
| |
| // gather := XMM(see above) // PSHUFB shuffle mask. |
| MOVOU gather<>(SB), X6 |
| |
| // offset := XMM(0x00000000 repeated four times) // Cumulative sum. |
| XORPS X7, X7 |
| |
| // i := 0 |
| MOVQ $0, R9 |
| |
| flAccOpSrcLoop4: |
| // for i < (len(src) &^ 3) |
| CMPQ R9, R10 |
| JAE flAccOpSrcLoop1 |
| |
| // x = XMM(s0, s1, s2, s3) |
| // |
| // Where s0 is src[i+0], s1 is src[i+1], etc. |
| MOVOU (SI), X1 |
| |
| // scratch = XMM(0, s0, s1, s2) |
| // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) |
| MOVOU X1, X0 |
| PSLLO $4, X0 |
| ADDPS X0, X1 |
| |
| // scratch = XMM(0, 0, 0, 0) |
| // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) |
| // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) |
| XORPS X0, X0 |
| SHUFPS $0x40, X1, X0 |
| ADDPS X0, X1 |
| |
| // x += offset |
| ADDPS X7, X1 |
| |
| // y = x & flSignMask |
| // y = min(y, flOne) |
| // y = mul(y, flAlmost65536) |
| MOVOU X3, X2 |
| ANDPS X1, X2 |
| MINPS X4, X2 |
| MULPS X5, X2 |
| |
| // z = convertToInt32(y) |
| LDMXCSR mxcsrNew-4(SP) |
| CVTPS2PL X2, X2 |
| LDMXCSR mxcsrOrig-8(SP) |
| |
| // z = shuffleTheSecondLowestBytesOfEach4ByteElement(z) |
| // copy(dst[:4], low4BytesOf(z)) |
| PSHUFB X6, X2 |
| MOVL X2, (DI) |
| |
| // offset = XMM(x@3, x@3, x@3, x@3) |
| MOVOU X1, X7 |
| SHUFPS $0xff, X1, X7 |
| |
| // i += 4 |
| // dst = dst[4:] |
| // src = src[4:] |
| ADDQ $4, R9 |
| ADDQ $4, DI |
| ADDQ $16, SI |
| JMP flAccOpSrcLoop4 |
| |
| flAccOpSrcLoop1: |
| // for i < len(src) |
| CMPQ R9, R11 |
| JAE flAccOpSrcEnd |
| |
| // x = src[i] + offset |
| MOVL (SI), X1 |
| ADDPS X7, X1 |
| |
| // y = x & flSignMask |
| // y = min(y, flOne) |
| // y = mul(y, flAlmost65536) |
| MOVOU X3, X2 |
| ANDPS X1, X2 |
| MINPS X4, X2 |
| MULPS X5, X2 |
| |
| // z = convertToInt32(y) |
| LDMXCSR mxcsrNew-4(SP) |
| CVTPS2PL X2, X2 |
| LDMXCSR mxcsrOrig-8(SP) |
| |
| // dst[0] = uint8(z>>8) |
| MOVL X2, BX |
| SHRL $8, BX |
| MOVB BX, (DI) |
| |
| // offset = x |
| MOVOU X1, X7 |
| |
| // i += 1 |
| // dst = dst[1:] |
| // src = src[1:] |
| ADDQ $1, R9 |
| ADDQ $1, DI |
| ADDQ $4, SI |
| JMP flAccOpSrcLoop1 |
| |
| flAccOpSrcEnd: |
| RET |
| |
| // ---------------------------------------------------------------------------- |
| |
| // func floatingAccumulateMaskSIMD(dst []uint32, src []float32) |
| // |
| // XMM registers. Variable names are per |
| // https://github.com/google/font-rs/blob/master/src/accumulate.c |
| // |
| // xmm0 scratch |
| // xmm1 x |
| // xmm2 y, z |
| // xmm3 flSignMask |
| // xmm4 flOne |
| // xmm5 flAlmost65536 |
| // xmm6 - |
| // xmm7 offset |
| // xmm8 - |
| // xmm9 - |
| // xmm10 - |
| TEXT ·floatingAccumulateMaskSIMD(SB), NOSPLIT, $8-48 |
| |
| MOVQ dst_base+0(FP), DI |
| MOVQ dst_len+8(FP), BX |
| MOVQ src_base+24(FP), SI |
| MOVQ src_len+32(FP), R10 |
| |
| // Sanity check that len(dst) >= len(src). |
| CMPQ BX, R10 |
| JLT flAccMaskEnd |
| |
| // R10 = len(src) &^ 3 |
| // R11 = len(src) |
| MOVQ R10, R11 |
| ANDQ $-4, R10 |
| |
| // Prepare to set MXCSR bits 13 and 14, so that the CVTPS2PL below is |
| // "Round To Zero". |
| STMXCSR mxcsrOrig-8(SP) |
| MOVL mxcsrOrig-8(SP), AX |
| ORL $0x6000, AX |
| MOVL AX, mxcsrNew-4(SP) |
| |
| // flSignMask := XMM(0x7fffffff repeated four times) // All but the sign bit of a float32. |
| // flOne := XMM(0x3f800000 repeated four times) // 1 as a float32. |
| // flAlmost65536 := XMM(0x477fffff repeated four times) // 255.99998 * 256 as a float32. |
| MOVOU flSignMask<>(SB), X3 |
| MOVOU flOne<>(SB), X4 |
| MOVOU flAlmost65536<>(SB), X5 |
| |
| // offset := XMM(0x00000000 repeated four times) // Cumulative sum. |
| XORPS X7, X7 |
| |
| // i := 0 |
| MOVQ $0, R9 |
| |
| flAccMaskLoop4: |
| // for i < (len(src) &^ 3) |
| CMPQ R9, R10 |
| JAE flAccMaskLoop1 |
| |
| // x = XMM(s0, s1, s2, s3) |
| // |
| // Where s0 is src[i+0], s1 is src[i+1], etc. |
| MOVOU (SI), X1 |
| |
| // scratch = XMM(0, s0, s1, s2) |
| // x += scratch // yields x == XMM(s0, s0+s1, s1+s2, s2+s3) |
| MOVOU X1, X0 |
| PSLLO $4, X0 |
| ADDPS X0, X1 |
| |
| // scratch = XMM(0, 0, 0, 0) |
| // scratch = XMM(scratch@0, scratch@0, x@0, x@1) // yields scratch == XMM(0, 0, s0, s0+s1) |
| // x += scratch // yields x == XMM(s0, s0+s1, s0+s1+s2, s0+s1+s2+s3) |
| XORPS X0, X0 |
| SHUFPS $0x40, X1, X0 |
| ADDPS X0, X1 |
| |
| // x += offset |
| ADDPS X7, X1 |
| |
| // y = x & flSignMask |
| // y = min(y, flOne) |
| // y = mul(y, flAlmost65536) |
| MOVOU X3, X2 |
| ANDPS X1, X2 |
| MINPS X4, X2 |
| MULPS X5, X2 |
| |
| // z = convertToInt32(y) |
| LDMXCSR mxcsrNew-4(SP) |
| CVTPS2PL X2, X2 |
| LDMXCSR mxcsrOrig-8(SP) |
| |
| // copy(dst[:4], z) |
| MOVOU X2, (DI) |
| |
| // offset = XMM(x@3, x@3, x@3, x@3) |
| MOVOU X1, X7 |
| SHUFPS $0xff, X1, X7 |
| |
| // i += 4 |
| // dst = dst[4:] |
| // src = src[4:] |
| ADDQ $4, R9 |
| ADDQ $16, DI |
| ADDQ $16, SI |
| JMP flAccMaskLoop4 |
| |
| flAccMaskLoop1: |
| // for i < len(src) |
| CMPQ R9, R11 |
| JAE flAccMaskEnd |
| |
| // x = src[i] + offset |
| MOVL (SI), X1 |
| ADDPS X7, X1 |
| |
| // y = x & flSignMask |
| // y = min(y, flOne) |
| // y = mul(y, flAlmost65536) |
| MOVOU X3, X2 |
| ANDPS X1, X2 |
| MINPS X4, X2 |
| MULPS X5, X2 |
| |
| // z = convertToInt32(y) |
| LDMXCSR mxcsrNew-4(SP) |
| CVTPS2PL X2, X2 |
| LDMXCSR mxcsrOrig-8(SP) |
| |
| // dst[0] = uint32(z) |
| MOVL X2, (DI) |
| |
| // offset = x |
| MOVOU X1, X7 |
| |
| // i += 1 |
| // dst = dst[1:] |
| // src = src[1:] |
| ADDQ $1, R9 |
| ADDQ $4, DI |
| ADDQ $4, SI |
| JMP flAccMaskLoop1 |
| |
| flAccMaskEnd: |
| RET |