Mã máy x86 (MMX / SSE1), 26 byte (4x int16_t)
Mã máy x86 (SSE4.1), 28 byte (4x int32_t hoặc uint32_t)
Mã máy x86 (SSE2), 24 byte (4x float32) hoặc 27B đến cvt int32
(Phiên bản cuối cùng chuyển đổi int32 thành float không hoàn toàn chính xác đối với các số nguyên lớn làm tròn thành cùng một float. Với đầu vào float, làm tròn là vấn đề của người gọi và chức năng này hoạt động chính xác nếu không có NaN, xác định số float so sánh == đến mức tối đa. Các phiên bản số nguyên hoạt động cho tất cả các đầu vào, coi chúng là phần bù 2 đã ký.)
Tất cả đều hoạt động ở chế độ 16/32/64-bit với cùng mã máy.
Một quy ước gọi stack-args sẽ cho phép lặp lại các đối số hai lần (tìm max và sau đó so sánh), có thể cho chúng ta thực hiện nhỏ hơn, nhưng tôi đã không thử cách tiếp cận đó.
x86 SIMD có vectơ-> bitmap nguyên như một lệnh đơn ( pmovmskb
hoặc movmskps
pd), do đó, điều này là tự nhiên mặc dù các lệnh MMX / SSE dài ít nhất 3 byte. SSSE3 và các hướng dẫn sau này dài hơn SSE2 và các lệnh MMX / SSE1 là ngắn nhất. Các phiên bản khác nhau của pmax*
(max-số dọc tối đa) được giới thiệu ở các thời điểm khác nhau, với SSE1 (đối với regx mm) và SSE2 (đối với regs xmm) chỉ có từ đã ký (16 bit) và byte không dấu.
( pshufw
và pmaxsw
trên các thanh ghi MMX là mới với Katmai Pentium III, vì vậy thực sự chúng yêu cầu SSE1, không chỉ là bit tính năng CPU MMX.)
Đây là callable từ C như unsigned max4_mmx(__m64)
với i386 System V ABI, mà đi một __m64
arg trong mm0
. (Không phải x86-64 System V, được truyền __m64
vào xmm0
!)
line code bytes
num addr
1 global max4_mmx
2 ;; Input 4x int16_t in mm0
3 ;; output: bitmap in EAX
4 ;; clobbers: mm1, mm2
5 max4_mmx:
6 00000000 0F70C8B1 pshufw mm1, mm0, 0b10110001 ; swap adjacent pairs
7 00000004 0FEEC8 pmaxsw mm1, mm0
8
9 00000007 0F70D14E pshufw mm2, mm1, 0b01001110 ; swap high/low halves
10 0000000B 0FEECA pmaxsw mm1, mm2
11
12 0000000E 0F75C8 pcmpeqw mm1, mm0 ; 0 / -1
13 00000011 0F63C9 packsswb mm1, mm1 ; squish word elements to bytes, preserving sign bit
14
15 00000014 0FD7C1 pmovmskb eax, mm1 ; extract the high bit of each byte
16 00000017 240F and al, 0x0F ; zero out the 2nd copy of the bitmap in the high nibble
17 00000019 C3 ret
size = 0x1A = 26 bytes
Nếu có một pmovmskw
, cái gì sẽ lưu packsswb
và and
(3 + 2 byte). Chúng tôi không cần and eax, 0x0f
vì pmovmskb
trên một thanh ghi MMX đã không có các byte trên. Các thanh ghi MMX chỉ rộng 8 byte, vì vậy AL 8 bit bao gồm tất cả các bit khác không có thể.
Nếu chúng ta biết đầu vào của chúng ta không âm, chúng ta có thểpacksswb mm1, mm0
tạo ra các byte được ký không âm ở 4 byte trên mm1
, để tránh sự cần thiết and
sau pmovmskb
. Do đó 24 byte.
Gói x86 với độ bão hòa đã ký xử lý đầu vào và đầu ra như đã ký, vì vậy nó luôn bảo toàn bit dấu. ( https://www.felixcloutier.com/x86/packsswb:packssdw ). Thực tế thú vị: gói x86 với độ bão hòa không dấu vẫn xử lý đầu vào như đã ký. Đây có thể là lý do tại sao PACKUSDW
không được giới thiệu cho đến SSE4.1, trong khi 3 kết hợp kích thước và chữ ký khác tồn tại kể từ MMX / SSE2.
Hoặc với số nguyên 32 bit trong một thanh ghi XMM (và pshufd
thay vì pshufw
), mỗi lệnh sẽ cần thêm một byte tiền tố, ngoại trừ việc movmskps
thay thế gói / và. Nhưng pmaxsd
/ pmaxud
cần thêm một byte ...
có thể gọi từ C nhưunsigned max4_sse4(__m128i);
với x86-64 System V hoặc MSVC vectorcall ( -Gv
), cả hai đều vượt qua __m128i
/ __m128d
/ __m128
args trong XMM regs bắt đầu bằng xmm0
.
20 global max4_sse4
21 ;; Input 4x int32_t in xmm0
22 ;; output: bitmap in EAX
23 ;; clobbers: xmm1, xmm2
24 max4_sse4:
25 00000020 660F70C8B1 pshufd xmm1, xmm0, 0b10110001 ; swap adjacent pairs
26 00000025 660F383DC8 pmaxsd xmm1, xmm0
27
28 0000002A 660F70D14E pshufd xmm2, xmm1, 0b01001110 ; swap high/low halves
29 0000002F 660F383DCA pmaxsd xmm1, xmm2
30
31 00000034 660F76C8 pcmpeqd xmm1, xmm0 ; 0 / -1
32
33 00000038 0F50C1 movmskps eax, xmm1 ; extract the high bit of each dword
34 0000003B C3 ret
size = 0x3C - 0x20 = 28 bytes
Hoặc nếu chúng tôi chấp nhận đầu vào là float
, chúng tôi có thể sử dụng các hướng dẫn SSE1. Các float
định dạng có thể đại diện cho một loạt các giá trị số nguyên ...
Hoặc nếu bạn nghĩ rằng việc bẻ cong các quy tắc quá xa, hãy bắt đầu với 3 byte 0F 5B C0 cvtdq2ps xmm0, xmm0
để chuyển đổi, tạo ra một hàm 27 byte hoạt động cho tất cả các số nguyên có thể biểu diễn chính xác như nhị phân IEEE32 float
và nhiều kết hợp đầu vào mà một số đầu vào nhận được làm tròn thành bội số của 2, 4, 8 hoặc bất cứ điều gì trong quá trình chuyển đổi. (Vì vậy, nó nhỏ hơn 1 byte so với phiên bản SSE4.1 và hoạt động trên mọi x86-64 chỉ với SSE2.)
Nếu bất kỳ đầu vào float nào là NaN, lưu ý rằng maxps a,b
thực hiện chính xác (a<b) ? a : b
, giữ cho phần tử từ toán hạng 2 không được sắp xếp . Vì vậy, điều này có thể trả về với bitmap khác không ngay cả khi đầu vào chứa một số NaN, tùy thuộc vào vị trí của chúng.
unsigned max4_sse2(__m128);
37 global max4_sse2
38 ;; Input 4x float32 in xmm0
39 ;; output: bitmap in EAX
40 ;; clobbers: xmm1, xmm2
41 max4_sse2:
42 ; cvtdq2ps xmm0, xmm0
43 00000040 660F70C8B1 pshufd xmm1, xmm0, 0b10110001 ; swap adjacent pairs
44 00000045 0F5FC8 maxps xmm1, xmm0
45
46 00000048 660F70D14E pshufd xmm2, xmm1, 0b01001110 ; swap high/low halves
47 0000004D 0F5FCA maxps xmm1, xmm2
48
49 00000050 0FC2C800 cmpeqps xmm1, xmm0 ; 0 / -1
50
51 00000054 0F50C1 movmskps eax, xmm1 ; extract the high bit of each dword
52 00000057 C3 ret
size = 0x58 - 0x40 = 24 bytes
sao chép và xáo trộn với pshufd
vẫn là đặt cược tốt nhất của chúng tôi: shufps dst,src,imm8
đọc đầu vào cho nửa thấp dst
từ dst
. Và chúng ta cần một bản sao và xáo trộn không phá hủy cả hai lần, vì vậy cả 3 byte movhlps
và unpckhps
/ pd đều bị loại bỏ. Nếu chúng tôi thu hẹp đến mức tối đa vô hướng, chúng tôi có thể sử dụng các giá trị đó, nhưng sẽ tốn một hướng dẫn khác để phát trước khi so sánh nếu chúng tôi chưa có tối đa trong tất cả các yếu tố.
Liên quan: SSE4.1 phminposuw
có thể tìm thấy vị trí và giá trị tối thiểu uint16_t
trong một thanh ghi XMM. Tôi không nghĩ rằng đó là một chiến thắng để trừ từ 65535 để sử dụng nó cho tối đa, nhưng hãy xem câu trả lời SO về việc sử dụng nó cho tối đa byte hoặc số nguyên đã ký.