รหัสเครื่อง x86-64 ขนาด 44 ไบต์
(รหัสเครื่องเดียวกันทำงานในโหมด 32 บิตได้เช่นกัน)
คำตอบของ @Daniel Scheplerเป็นจุดเริ่มต้นสำหรับสิ่งนี้ แต่มีแนวคิดอัลกอริทึมใหม่อย่างน้อยหนึ่งแนวคิด (ไม่ใช่แค่การเล่นกอล์ฟที่ดีกว่าของความคิดเดียวกัน): รหัส ASCII สำหรับ'B'
( 1000010
) และ'X'
( 1011000
) ให้ 16 และ 2 หลังจากปิดบัง0b0010010
หลังจากที่กำบังด้วย
ดังนั้นหลังจากไม่รวมทศนิยม (หลักนำที่ไม่ใช่ศูนย์) และฐานแปด (ถ่านหลังจาก'0'
น้อยกว่า'B'
) เราก็สามารถตั้งค่าฐาน =c & 0b0010010
และกระโดดเข้าไปในวงหลัก
Callable กับ x86-64 System V เป็นunsigned __int128 parse_cxx14_int(int dummy, const char*rsi);
แยก EDX ค่าตอบแทนจากครึ่งสูงของผลกับunsigned __int128
tmp>>64
.globl parse_cxx14_int
## Input: pointer to 0-terminated string in RSI
## output: integer in EDX
## clobbers: RAX, RCX (base), RSI (points to terminator on return)
parse_cxx14_int:
xor %eax,%eax # initialize high bits of digit reader
cdq # also initialize result accumulator edx to 0
lea 10(%rax), %ecx # base 10 default
lodsb # fetch first character
cmp $'0', %al
jne .Lentry2
# leading zero. Legal 2nd characters are b/B (base 2), x/X (base 16)
# Or NUL terminator = 0 in base 10
# or any digit or ' separator (octal). These have ASCII codes below the alphabetic ranges
lodsb
mov $8, %cl # after '0' have either digit, apostrophe, or terminator,
cmp $'B', %al # or 'b'/'B' or 'x'/'X' (set a new base)
jb .Lentry2 # enter the parse loop with base=8 and an already-loaded character
# else hex or binary. The bit patterns for those letters are very convenient
and $0b0010010, %al # b/B -> 2, x/X -> 16
xchg %eax, %ecx
jmp .Lentry
.Lprocessdigit:
sub $'0' & (~32), %al
jb .Lentry # chars below '0' are treated as a separator, including '
cmp $10, %al
jb .Lnum
add $('0'&~32) - 'A' + 10, %al # digit value = c-'A' + 10. we have al = c - '0'&~32.
# c = al + '0'&~32. val = m+'0'&~32 - 'A' + 10
.Lnum:
imul %ecx, %edx
add %eax, %edx # accum = accum * base + newdigit
.Lentry:
lodsb # fetch next character
.Lentry2:
and $~32, %al # uppercase letters (and as side effect,
# digits are translated to N+16)
jnz .Lprocessdigit # space also counts as a terminator
.Lend:
ret
บล็อกที่มีการเปลี่ยนแปลงเทียบกับเวอร์ชั่นของแดเนียล (ส่วนใหญ่) เยื้องน้อยกว่าคำสั่งอื่น ๆ นอกจากนี้ห่วงหลักมีสาขาตามเงื่อนไขที่ด้านล่าง สิ่งนี้กลายเป็นการเปลี่ยนแปลงที่เป็นกลางเพราะไม่มีเส้นทางใดสามารถตกลงบนจุดสูงสุดของมันและdec ecx / loop .Lentry
ความคิดในการเข้าสู่วงกลับกลายเป็นว่าไม่ชนะหลังจากจัดการแปดด้านต่างกัน แต่มันมีคำแนะนำน้อยกว่าภายในลูปโดยมีลูปในรูปแบบของสำนวนทำ {} ในขณะที่โครงสร้างดังนั้นฉันจึงเก็บมันไว้
ชุดควบคุมการทดสอบ C ++ ของ Daniel ทำงานไม่เปลี่ยนแปลงในโหมด 64 บิตด้วยรหัสนี้ซึ่งใช้หลักการเรียกที่เหมือนกันกับคำตอบ 32- บิตของเขา
g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good
ถอดชิ้นส่วนรวมถึงไบต์รหัสเครื่องที่เป็นคำตอบที่แท้จริง
0000000000000000 <parse_cxx14_int>:
0: 31 c0 xor %eax,%eax
2: 99 cltd
3: 8d 48 0a lea 0xa(%rax),%ecx
6: ac lods %ds:(%rsi),%al
7: 3c 30 cmp $0x30,%al
9: 75 1c jne 27 <parse_cxx14_int+0x27>
b: ac lods %ds:(%rsi),%al
c: b1 08 mov $0x8,%cl
e: 3c 42 cmp $0x42,%al
10: 72 15 jb 27 <parse_cxx14_int+0x27>
12: 24 12 and $0x12,%al
14: 91 xchg %eax,%ecx
15: eb 0f jmp 26 <parse_cxx14_int+0x26>
17: 2c 10 sub $0x10,%al
19: 72 0b jb 26 <parse_cxx14_int+0x26>
1b: 3c 0a cmp $0xa,%al
1d: 72 02 jb 21 <parse_cxx14_int+0x21>
1f: 04 d9 add $0xd9,%al
21: 0f af d1 imul %ecx,%edx
24: 01 c2 add %eax,%edx
26: ac lods %ds:(%rsi),%al
27: 24 df and $0xdf,%al
29: 75 ec jne 17 <parse_cxx14_int+0x17>
2b: c3 retq
การเปลี่ยนแปลงอื่น ๆ จากเวอร์ชันของ Daniel รวมถึงการบันทึกsub $16, %al
จากภายในวงหลักโดยใช้มากกว่าsub
แทนtest
เป็นส่วนหนึ่งของการตรวจจับตัวคั่นและตัวเลขกับตัวอักษรและตัวอักษร
ซึ่งแตกต่างจากแดเนียลเป็นทุกตัวอักษรด้านล่างจะถือว่าเป็นตัวคั่นไม่เพียง'0'
'\''
(ยกเว้น' '
: and $~32, %al
/ jnz
ในลูปทั้งสองของเราใช้พื้นที่เป็นเทอร์มิเนเตอร์ซึ่งอาจสะดวกในการทดสอบด้วยจำนวนเต็มที่จุดเริ่มต้นของบรรทัด)
การดำเนินการทุกอย่างที่แก้ไข%al
ภายในลูปจะมีแฟลกการแบรนช์ย่อยที่กำหนดโดยผลลัพธ์และแต่ละสาขาจะไป (หรือผ่าน) ไปยังตำแหน่งอื่น