รหัสเครื่อง x86 (MMX / SSE1), 26 ไบต์ (4x int16_t)
รหัสเครื่อง x86 (SSE4.1), 28 ไบต์ (4x int32_t หรือ uint32_t)
รหัสเครื่อง x86 (SSE2), 24 ไบต์ (4x float32) หรือ 27B ถึง cvt int32
(เวอร์ชันล่าสุดที่แปลง int32 เป็น float ไม่ถูกต้องสมบูรณ์สำหรับจำนวนเต็มขนาดใหญ่ที่ปัดเป็น float เดียวกันด้วยอินพุตลอยการปัดเศษเป็นปัญหาของผู้โทรและฟังก์ชันนี้ทำงานอย่างถูกต้องหากไม่มี NaNs ระบุลอยที่เปรียบเทียบ == ถึงจำนวนสูงสุดรุ่นจำนวนเต็มใช้งานได้กับอินพุตทั้งหมดโดยถือว่าเป็นส่วนเติมเต็มของ 2 ที่ได้รับการรับรอง)
ทั้งหมดนี้ทำงานในโหมด 16/32/64 บิตพร้อมกับรหัสเครื่องเดียวกัน
รูปแบบการเรียก stack-args จะทำให้สามารถวนรอบ args สองครั้ง (การหาค่าสูงสุดและการเปรียบเทียบ) อาจทำให้เรามีการใช้งานที่น้อยลง แต่ฉันไม่ได้ลองวิธีนั้น
x86 SIMD มี vector-> bitmap จำนวนเต็มเป็นคำสั่งเดียว ( pmovmskb
หรือmovmskps
หรือ pd) ดังนั้นจึงเป็นเรื่องธรรมดาสำหรับเรื่องนี้แม้ว่าคำสั่ง MMX / SSE จะมีความยาวอย่างน้อย 3 ไบต์ SSSE3 และคำแนะนำในภายหลังนั้นยาวกว่า SSE2 และคำสั่ง MMX / SSE1 นั้นสั้นที่สุด รุ่นที่แตกต่างกันของpmax*
(maxed vertical-integer ในแนวตั้งเต็มจำนวน) ถูกนำมาใช้ในเวลาที่ต่างกันด้วย SSE1 (สำหรับ mmx regs) และ SSE2 (สำหรับ xmm regs) มีเพียงคำที่ลงนาม (16 บิต) และไบต์ที่ไม่ได้ลงนาม
( pshufw
และpmaxsw
ในการลงทะเบียน MMX นั้นใหม่กับ Katmai Pentium III ดังนั้นจริงๆแล้วพวกเขาต้องการ SSE1 ไม่ใช่บิตของ CPU MMX เท่านั้น)
นี่คือ callable จาก C รวมunsigned max4_mmx(__m64)
กับ i386 ระบบวี ABI ซึ่งผ่านหาเรื่องใน__m64
mm0
(ไม่ x86-64 System V ซึ่งผ่าน__m64
ใน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
หากมี a pmovmskw
สิ่งที่จะบันทึกpacksswb
และand
(3 + 2 ไบต์) เราไม่ต้องการand eax, 0x0f
เพราะpmovmskb
ในการลงทะเบียน MMX แล้วจำนวนศูนย์บนไบต์ การลงทะเบียน MMX มีความกว้างเพียง 8 ไบต์ดังนั้น 8-bit AL จะครอบคลุมบิตที่ไม่เป็นศูนย์ทั้งหมด
หากเรารู้ว่าอินพุตของเราไม่เป็นลบเราสามารถpacksswb mm1, mm0
สร้างไบต์ที่เซ็นชื่อแบบไม่ลบใน 4 ไบต์บนของmm1
โดยหลีกเลี่ยงความต้องการand
หลังจากpmovmskb
นั้น ดังนั้น 24 ไบต์
x86 แพ็คที่มีความอิ่มตัวที่ลงนามแล้วจะถือว่าอินพุตและเอาต์พุตเป็นสัญญาณดังนั้นมันจึงสงวนบิตของสัญญาณไว้เสมอ ( https://www.felixcloutier.com/x86/packsswb:packssdw ) ข้อเท็จจริงที่น่าสนุก: x86 แพ็คที่มีความอิ่มตัวที่ไม่ได้ลงชื่อยังคงถือว่าอินพุตเป็นสัญญาณ นี่อาจเป็นสาเหตุที่PACKUSDW
ยังไม่ถูกนำมาใช้จนกระทั่ง SSE4.1 ในขณะที่มีการรวมกันของขนาดและการเซ็นชื่ออีก 3 รายการตั้งแต่ MMX / SSE2
หรือด้วยจำนวนเต็ม 32 บิตในการลงทะเบียน XMM (และpshufd
แทนpshufw
) คำสั่งทุกคำสั่งจะต้องมีไบต์นำหน้าเพิ่มอีกหนึ่งตัวยกเว้นการmovmskps
แทนที่แพ็ค / และ แต่pmaxsd
/pmaxud
ต้องเพิ่มไบต์พิเศษ ...
callable จาก C รวมunsigned max4_sse4(__m128i);
กับ x86-64 System V หรือ MSVC vectorcall ( -Gv
) ซึ่งทั้งสองผ่าน__m128i
/ __m128d
/ __m128
args ใน XMM regs 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
หรือถ้าเรายอมรับอินพุตเป็นfloat
เราสามารถใช้คำแนะนำ SSE1 float
รูปแบบสามารถเป็นตัวแทนของความหลากหลายของค่าจำนวนเต็ม ...
หรือถ้าคุณคิดว่ามันก้มกฏมากเกินไปเริ่มต้นด้วย 3-byte 0F 5B C0 cvtdq2ps xmm0, xmm0
เพื่อทำการแปลงทำให้ฟังก์ชั่น 27- ไบต์ที่ทำงานกับจำนวนเต็มทั้งหมดที่สามารถแทนได้อย่างแน่นอนเช่น IEEE binary32float
และชุดอินพุตจำนวนมากที่อินพุตบางตัวได้รับ ปัดเศษเป็นทวีคูณของ 2, 4, 8 หรืออะไรก็ตามในระหว่างการแปลง (ดังนั้นมันจึงเล็กกว่ารุ่น SSE4.1 1 ไบต์และใช้งานได้กับ x86-64 ใด ๆ ด้วย SSE2 เท่านั้น)
ถ้าใด ๆ ของปัจจัยการผลิตที่ลอยอยู่น่านทราบว่าmaxps a,b
ว่าการดำเนินการ(a<b) ? a : b
, การรักษาองค์ประกอบจากตัวถูกดำเนินการครั้งที่ 2 เรียงลำดับ ดังนั้นจึงอาจเป็นไปได้ที่สิ่งนี้จะกลับมาพร้อมกับบิตแมปที่ไม่เป็นศูนย์แม้ว่าอินพุตจะมี NaN บางตัวขึ้นอยู่กับว่าพวกเขาอยู่ที่ไหน
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
คัดลอกและสับเปลี่ยนกับpshufd
ยังคงเป็นทางออกที่ดีที่สุดของเรา: shufps dst,src,imm8
อ่านการป้อนข้อมูลสำหรับครึ่งต่ำจากdst
dst
และเราต้องการการคัดลอกและสับเปลี่ยนแบบไม่ทำลายทั้งสองครั้งดังนั้น 3 ไบต์movhlps
และunpckhps
/ pd จึงออกมาทั้งคู่ ถ้าเราแคบลงถึงค่าสเกลาร์แม็กซ์เราสามารถใช้มัน แต่มันมีค่าใช้จ่ายคำสั่งอื่นในการออกอากาศก่อนที่จะเปรียบเทียบถ้าเราไม่มีค่าสูงสุดในองค์ประกอบทั้งหมดแล้ว
ที่เกี่ยวข้อง: SSE4.1 phminposuw
สามารถค้นหาตำแหน่งและมูลค่าขั้นต่ำuint16_t
ในการลงทะเบียน XMM ฉันไม่คิดว่ามันจะชนะการลบจาก 65535 เพื่อใช้สำหรับสูงสุด แต่ดูคำตอบดังนั้นเกี่ยวกับการใช้มันสำหรับสูงสุดไบต์หรือจำนวนเต็มเซ็น