x86 32-বিট মেশিন-কোড ফাংশন, 42 41 বাইট
বর্তমানে গল্ফিং-না-উত্তর উত্তর, @ স্ট্রিস্টারের কিউ / কেডিবি + এর চেয়ে 1 বি কম ।
সত্যের জন্য 0 এবং মিথ্যা জন্য শূন্য নয়: 41 40 বাইট। (সাধারণভাবে, 32-বিটের জন্য 1 বাইট, 64-বিটের জন্য 2 বাইট সংরক্ষণ করে)।
অন্তর্নির্মিত দৈর্ঘ্যের স্ট্রিং (সি-স্টাইল 0-সমাপ্ত): 45 44 বাইট
x86-64 মেশিন-কোড (32-বিট পয়েন্টার সহ, x32 এবিআই এর মতো): 44 43 বাইট ।
অন্তর্নির্মিত দৈর্ঘ্যের স্ট্রিং সহ x86-64, এখনও 46 বাইট (শিফট / মাস্ক বিটম্যাপ কৌশলটি এখন ব্রেক-সমান)।
এটি সি স্বাক্ষর সহ একটি ফাংশন _Bool dennis_like(size_t ecx, const char *esi)
। কলিং কনভেনশনটি কিছুটা মানহীন, এমএস ভেক্টরক্যাল / ফাস্টকলের কাছাকাছি তবে বিভিন্ন আর্গ রেজিস্টরের সাথে: ইসি তে স্ট্রিং এবং ইসিএক্সের দৈর্ঘ্য। এটি কেবল তার আরগ-রেজি এবং ইডিএক্স ক্লোবার করে। AL হাই বাইটগুলি আবর্জনা ধারণ করে রিটার্নের মান রাখে (সিসভি x86 এবং x32 এবিআই দ্বারা অনুমোদিত allowed
অ্যালগরিদমের ব্যাখ্যা :
ইনপুট স্ট্রিংয়ের উপর দিয়ে লুপ করুন, স্ট্যাকের উপরে বুলিয়ান অ্যারেতে ফিল্টারিং এবং শ্রেণিবদ্ধকরণ: প্রতিটি বাইটের জন্য, এটি কোনও বর্ণমালা অক্ষর (যদি না, তবে পরবর্তী চরটিতে অবিরত থাকে) তা পরীক্ষা করে দেখুন এবং 0-25 (এজেড) থেকে পূর্ণসংখ্যায় রূপান্তর করুন । স্বর = 0 / ব্যঞ্জনবর্ণ = 1 এর বিটম্যাপটি পরীক্ষা করতে সেই 0-25 পূর্ণসংখ্যার ব্যবহার করুন। (বিটম্যাপটি 32-বিট তাত্ক্ষণিক ধ্রুবক হিসাবে একটি রেজিস্টারে লোড করা হয়)। বিটম্যাপের ফলাফল অনুসারে স্ট্যাকের উপর 0 বা 0xFF চাপুন (আসলে একটি 32-বিট উপাদানটির নিম্ন বাইটে, যার উপরের 3 বাইটে আবর্জনা থাকতে পারে)।
প্রথম লুপটি 0 বা 0xFF এর অ্যারে উত্পাদন করে (আবর্জনায় আবদ্ধ শব্দগুলির উপাদানগুলিতে)। সাধারণত প্যালিনড্রোমটি দ্বিতীয় লুপের সাহায্যে পরীক্ষা করুন যা পয়েন্টারগুলি মাঝখানে অতিক্রম করার সময় বন্ধ হয়ে যায় (বা যখন উভয় বর্ণমালার অক্ষরগুলির একটি পৃথক সংখ্যক যদি একই উপাদানকে নির্দেশ করে)। উপরের দিকে চলন্ত পয়েন্টার হ'ল স্ট্যাক পয়েন্টার এবং আমরা + ইনক্রিমেন্ট লোড করতে পপ ব্যবহার করি। এই লুপটিতে তুলনা / সেটসিটির পরিবর্তে, আমরা কেবল দুটি সম্ভাব্য মান আছে বলেই কেবল একই / আলাদা সনাক্ত করতে এক্সওআর ব্যবহার করতে পারি। আমরা কোনও অ-মিলের উপাদান খুঁজে পেয়েছি কিনা (ওআর দিয়ে) জমে উঠতে পারতাম, তবে এক্সওআর দ্বারা নির্ধারিত পতাকাগুলিতে একটি প্রাথমিক-শাখা কমপক্ষে ভাল good
লক্ষ্য করুন যে দ্বিতীয় লুপটি byte
অপরেন্দ্র-আকার ব্যবহার করে , সুতরাং এটি বিবেচ্য নয় যে প্রতিটি অ্যারের উপাদানের নিম্ন বাইটের বাইরে প্রথম লুপটি কী আবর্জনা ফেলে।
এটি সিওএফsalc
থেকে AL সেট করতে একইভাবে, অদ্বন্ধিত নির্দেশ ব্যবহার করে sbb al,al
। এটি প্রতিটি ইন্টেল সিপিইউতে সমর্থিত (64৪-বিট মোড ব্যতীত), এমনকি নাইটের ল্যান্ডিং! আগ্নার ফাগ তার জন্য সমস্ত এএমডি সিপিইউতে (রাইজেন সহ) তালিকাভুক্ত করে , তাই x86 বিক্রেতারা যদি 8086 সাল থেকে অপকোড স্পেসের বাইটটি বেঁধে দেওয়ার জন্য জোর দেয়, তবে আমরা সম্ভবত এটির সুবিধা নিতে পারি।
আকর্ষণীয় কৌশল:
- একটি সম্মিলিত ইসলফা () এবং টপার্পার () এর জন্য স্বাক্ষরযুক্ত-তুলনা কৌশল এবং শেক্সটি পূরণ করার জন্য বাইটটি প্রসারিত করে, এটির জন্য সেট আপ:
- এর জন্য একটি নিবন্ধে তাত্ক্ষণিক বিটম্যাপ
bt
, এর জন্য কিছু সুন্দর সংকলক আউটপুট দ্বারা অনুপ্রাণিতswitch
।
- একটি লুপে পুশ করে স্ট্যাকের উপর একটি পরিবর্তনশীল-আকারের অ্যারে তৈরি করা। (Asm এর জন্য স্ট্যান্ডার্ড, তবে অন্তর্নিহিত দৈর্ঘ্যের স্ট্রিং সংস্করণের জন্য আপনি সি দিয়ে কিছু করতে পারেন না)। এটি প্রতিটি ইনপুট চরিত্রের জন্য 4 বাইট স্ট্যাক স্পেস ব্যবহার করে, তবে কমপক্ষে 1 বাইট বনাম সর্বোত্তম গল্ফিং সাশ্রয় করে
stosb
।
- বুলিয়ান অ্যারেতে সিএমপি / সেটেনের পরিবর্তে এক্সওআর বুলিয়ানরা একসাথে সত্যের মান পেতে। (
cmp
/ salc
কোনও বিকল্প নয়, কারণ salc
কেবল সিএফ-এর জন্য কাজ করে, এবং 0xFF-0 সিএফ সেট করে না sete
3 বাইট হয়, তবে inc
2 বাইটের নিখরচায় (-৪-বিট মোডে 1 )) লুপের মধ্যে বনাম xor এবং এটি Inc সহ স্থির করে।
; explicit-length version: input string in ESI, byte count in ECX
08048060 <dennis_like>:
8048060: 55 push ebp
8048061: 89 e5 mov ebp,esp ; a stack frame lets us restore esp with LEAVE (1B)
8048063: ba ee be ef 03 mov edx,0x3efbeee ; consonant bitmap
08048068 <dennis_like.filter_loop>:
8048068: ac lods al,BYTE PTR ds:[esi]
8048069: 24 5f and al,0x5f ; uppercase
804806b: 2c 41 sub al,0x41 ; range-shift to 0..25
804806d: 3c 19 cmp al,0x19 ; reject non-letters
804806f: 77 05 ja 8048076 <dennis_like.non_alpha>
8048071: 0f a3 c2 bt edx,eax # AL = 0..25 = position in alphabet
8048074: d6 SALC ; set AL=0 or 0xFF from carry. Undocumented insn, but widely supported
8048075: 50 push eax
08048076 <dennis_like.non_alpha>:
8048076: e2 f0 loop 8048068 <dennis_like.filter_loop> # ecx = remaining string bytes
; end of first loop
8048078: 89 ee mov esi,ebp ; ebp = one-past-the-top of the bool array
0804807a <dennis_like.palindrome_loop>:
804807a: 58 pop eax ; read from the bottom
804807b: 83 ee 04 sub esi,0x4
804807e: 32 06 xor al,BYTE PTR [esi]
8048080: 75 04 jne 8048086 <dennis_like.non_palindrome>
8048082: 39 e6 cmp esi,esp ; until the pointers meet or cross in the middle
8048084: 77 f4 ja 804807a <dennis_like.palindrome_loop>
08048086 <dennis_like.non_palindrome>:
; jump or fall-through to here with al holding an inverted boolean
8048086: 40 inc eax
8048087: c9 leave
8048088: c3 ret
;; 0x89 - 0x60 = 41 bytes
এটি সম্ভবত দ্রুততম উত্তরগুলির মধ্যে একটি, যেহেতু গল্ফিং কেউই খুব খারাপভাবে ব্যথা করে না, অন্তত কয়েক হাজার অক্ষরের নীচে স্ট্রিংগুলির ক্ষেত্রে যেখানে 4x মেমরির ব্যবহারের ফলে অনেক বেশি ক্যাশে-মিস হয় না। (এটি এমন উত্তরগুলিও হারাতে পারে যা সমস্ত অক্ষরকে ফাঁকে ফাঁকে ফাঁকে দেওয়ার আগে নন-ডেনিস-জাতীয় স্ট্রিংগুলির প্রথম দিকে নিয়ে যায়) অনেকগুলি সিপিইউয়ের salc
তুলনায় ধীর setcc
হয় (উদাহরণস্বরূপ 3 উফ বনাম 1 স্কাইলেকে), তবে একটি বিটম্যাপ চেক bt/salc
স্ট্রিং-সন্ধান বা রেজেক্স-ম্যাচের চেয়ে এখনও দ্রুত। এবং ওভারহেডের কোনও প্রারম্ভ নেই, সুতরাং এটি সংক্ষিপ্ত স্ট্রিংগুলির জন্য অত্যন্ত সস্তা।
উড়ে যাওয়ার সময় এক পাসে করা মানে আপ এবং ডাউন দিকের জন্য শ্রেণিবদ্ধকরণ কোডটি পুনরাবৃত্তি করা। এটি দ্রুত তবে বৃহত্তর কোড-আকারের হবে। (অবশ্যই যদি আপনি দ্রুত চান, আপনি এসএসই 2 বা এভিএক্স 2 এর সাথে একসাথে 16 বা 32 টি করতে পারেন, স্বাক্ষরিত রেঞ্জের নীচে সীমা স্থানান্তর করে তুলনা কৌশলটি ব্যবহার করে)।
টেস্ট প্রোগ্রাম (ia32 বা x32 লিনাক্সের জন্য) এই ফাংশনটি একটি সেমিডলাইন আর্গ দিয়ে কল করতে এবং স্ট্যাটাস = রিটার্ন মান সহ প্রস্থান করুন। int80h.orgstrlen
থেকে বাস্তবায়ন ।
; build with the same %define macros as the source below (so this uses 32-bit regs in 32-bit mode)
global _start
_start:
;%define PTRSIZE 4 ; true for x32 and 32-bit mode.
mov esi, [rsp+4 + 4*1] ; esi = argv[1]
;mov rsi, [rsp+8 + 8*1] ; rsi = argv[1] ; For regular x86-64 (not x32)
%if IMPLICIT_LENGTH == 0
; strlen(esi)
mov rdi, rsi
mov rcx, -1
xor eax, eax
repne scasb ; rcx = -strlen - 2
not rcx
dec rcx
%endif
mov eax, 0xFFFFAEBB ; make sure the function works with garbage in EAX
call dennis_like
;; use the 32-bit ABI _exit syscall, even in x32 code for simplicity
mov ebx, eax
mov eax, 1
int 0x80 ; _exit( dennis_like(argv[1]) )
;; movzx edi, al ; actually mov edi,eax is fine here, too
;; mov eax,231 ; 64-bit ABI exit_group( same thing )
;; syscall
এই ফাংশনের একটি 64-বিট সংস্করণ ব্যবহার করতে পারে sbb eax,eax
যা 3 এর পরিবর্তে 2 বাইট setc al
। এটির জন্য dec
বা not
শেষে অতিরিক্ত বাইটেরও প্রয়োজন হবে (কারণ কেবল 32-বিটের ক্ষেত্রে 1-বাইট ইনক / ডিসি আর 32 রয়েছে)। এক্স 32 এবিআই (লং মোডে 32-বিট পয়েন্টার) ব্যবহার করে আমরা পয়েন্টারগুলি অনুলিপি এবং তুলনা করেও আমরা REX উপসর্গগুলি এড়াতে পারি।
setc [rdi]
মেমোরিতে সরাসরি লিখতে পারে, তবে স্ট্যাক স্পেসের ইসিএক্স বাইট সংরক্ষণের জন্য সাশ্রয়ের চেয়ে কোড-আকার বেশি লাগে। (এবং আমাদের আউটপুট অ্যারেটি স্থানান্তরিত করতে হবে [rdi+rcx]
the অ্যাড্রেসিং মোডের জন্য আরও একটি অতিরিক্ত বাইট লাগবে, তবে সত্যিই আমাদের এমন কাউন্টার দরকার যা ফিল্টারযুক্ত অক্ষরগুলির জন্য আপডেট হয় না যাতে এটি এর চেয়ে খারাপ হতে চলেছে))
এটি শর্তসাপেক্ষে ওয়াইএএসএম / এনএএসএম উত্স %if
। এটি -felf32
(32-বিট কোড) বা -felfx32
(x32 এবিআই সহ 64-বিট কোড) এবং অন্তর্নিহিত বা স্পষ্ট দৈর্ঘ্যের সাহায্যে নির্মিত যেতে পারে । আমি সমস্ত 4 সংস্করণ পরীক্ষা করেছি। NASM / YASM উত্স থেকে একটি স্ট্যাটিক বাইনারি তৈরি করতে স্ক্রিপ্টের জন্য এই উত্তরটি দেখুন ।
X32 এবিআইয়ের সমর্থন ছাড়াই কোনও মেশিনে -৪-বিট সংস্করণটি পরীক্ষা করতে, আপনি পয়েন্টার রেগগুলি 64৪-বিটে পরিবর্তন করতে পারেন। (তারপরে গণনা থেকে কেবল REX.W = 1 উপসর্গ (0x48 বাইট) সংখ্যা বিয়োগ করুন case এই ক্ষেত্রে, 4 টি নির্দেশাবলী EX৪-বিট রেগগুলিতে পরিচালনা করতে REX উপসর্গের প্রয়োজন। অথবা rsp
এড্রেস স্পেসের কম 4 জি ইনপুট পয়েন্টার সহ এটিকে কল করুন।
%define IMPLICIT_LENGTH 0
; This source can be built as x32, or as plain old 32-bit mode
; x32 needs to push 64-bit regs, and using them in addressing modes avoids address-size prefixes
; 32-bit code needs to use the 32-bit names everywhere
;%if __BITS__ != 32 ; NASM-only
%ifidn __OUTPUT_FORMAT__, elfx32
%define CPUMODE 64
%define STACKWIDTH 8 ; push / pop 8 bytes
%else
%define CPUMODE 32
%define STACKWIDTH 4 ; push / pop 4 bytes
%define rax eax
%define rcx ecx
%define rsi esi
%define rdi edi
%define rbp ebp
%define rsp esp
%endif
; A regular x86-64 version needs 4 REX prefixes to handle 64-bit pointers
; I haven't cluttered the source with that, but I guess stuff like %define ebp rbp would do the trick.
;; Calling convention similar to SysV x32, or to MS vectorcall, but with different arg regs
;; _Bool dennis_like_implicit(const char *esi)
;; _Bool dennis_like_explicit(size_t ecx, const char *esi)
global dennis_like
dennis_like:
; We want to restore esp later, so make a stack frame for LEAVE
push rbp
mov ebp, esp ; enter 0,0 is 4 bytes. Only saves bytes if we had a fixed-size allocation to do.
; ZYXWVUTSRQPONMLKJIHGFEDCBA
mov edx, 11111011111011111011101110b ; consonant/vowel bitmap for use with bt
;;; assume that len >= 1
%if IMPLICIT_LENGTH
lodsb ; pipelining the loop is 1B shorter than jmp .non_alpha
.filter_loop:
%else
.filter_loop:
lodsb
%endif
and al, 0x7F ^ 0x20 ; force ASCII to uppercase.
sub al, 'A' ; range-shift to 'A' = 0
cmp al, 'Z'-'A' ; if al was less than 'A', it will be a large unsigned number
ja .non_alpha
;; AL = position in alphabet (0-25)
bt edx, eax ; 3B
%if CPUMODE == 32
salc ; 1B only sets AL = 0 or 0xFF. Not available in 64-bit mode
%else
sbb eax, eax ; 2B eax = 0 or -1, according to CF.
%endif
push rax
.non_alpha:
%if IMPLICIT_LENGTH
lodsb
test al,al
jnz .filter_loop
%else
loop .filter_loop
%endif
; al = potentially garbage if the last char was non-alpha
; esp = bottom of bool array
mov esi, ebp ; ebp = one-past-the-top of the bool array
.palindrome_loop:
pop rax
sub esi, STACKWIDTH
xor al, [rsi] ; al = (arr[up] != arr[--down]). 8-bit operand-size so flags are set from the non-garbage
jnz .non_palindrome
cmp esi, esp
ja .palindrome_loop
.non_palindrome: ; we jump here with al=1 if we found a difference, or drop out of the loop with al=0 for no diff
inc eax ;; AL transforms 0 -> 1 or 0xFF -> 0.
leave
ret ; return value in AL. high bytes of EAX are allowed to contain garbage.
আমি ডিএফ (যে দিকের পতাকাটি নিয়ন্ত্রণ করে lodsd
/ scasd
এবং এমন কি) এর সাথে চারপাশে গোলযোগের দিকে তাকিয়েছিলাম , তবে এটি বিজয় বলে মনে হয় নি। সাধারণ এবিআইদের প্রয়োজন হয় যে ফাংশন এন্ট্রি এবং প্রস্থানকালে ডিএফ সাফ হয়ে যায়। এন্ট্রি-তে ক্লিয়ার হয়ে থাকলেও এটিকে প্রস্থান করার সময় সেট করা ঠকানো হবে, আইএমও। 3-বাইট এড়াতে LODSD / SCASD ব্যবহার করা ভাল লাগবে sub esi, 4
, বিশেষত যেখানে উচ্চ আবর্জনা নেই case
বিকল্প বিটম্যাপ কৌশল (x86-64 অন্তর্ভুক্ত দৈর্ঘ্যের স্ট্রিংয়ের জন্য)
দেখা যাচ্ছে এটি কোনও বাইট সংরক্ষণ করে না, কারণ bt r32,r32
এখনও বিট-ইনডেক্সে উচ্চ আবর্জনা নিয়ে কাজ করে। এটি কেবল নথিবদ্ধ নয় উপায় shr
।
bt / sbb
সিএফ-তে বিটটি প্রবেশ করার পরিবর্তে, বিটম্যাপ থেকে আমরা যা চাই তা বিচ্ছিন্ন করতে একটি শিফট / মাস্ক ব্যবহার করুন।
%if IMPLICIT_LENGTH && CPUMODE == 64
; incompatible with LOOP for explicit-length, both need ECX. In that case, bt/sbb is best
xchg eax, ecx
mov eax, 11111011111011111011101110b ; not hoisted out of the loop
shr eax, cl
and al, 1
%else
bt edx, eax
sbb eax, eax
%endif
push rax
যেহেতু এটি AL এ 0/1 উত্পাদন করে (0 / 0xFF এর পরিবর্তে), আমরা ফাংশন শেষে xor al, 1
(2 বি) দিয়ে ( dec eax
x86-64 এ 2 বি) পরিবর্তে ফাংশনটির শেষে ফেরত মানটির প্রয়োজনীয় বিপর্যয়ও করতে পারি এখনও একটি সঠিক bool
/_Bool
ফেরতের মান উত্পাদন করে ।
এটি ইএএক্স এর উচ্চ বাইট শূন্যের প্রয়োজনীয়তা এড়িয়ে, ইমপ্লিট-দৈর্ঘ্যের স্ট্রিং সহ x86-64 এর জন্য 1 বি সংরক্ষণ করতে ব্যবহৃত হয়েছিল। (আমি and eax, 0x7F ^ 0x20
উচ্চতর ক্ষেত্রে বল প্রয়োগ করতে এবং বাক্সটি 3-বাইটের সাথে শূন্য করতে and r32,imm8
ব্যবহার করে আসছি। তবে এখন আমি 2-বাইট অবিলম্বে-আল-এনকোড ব্যবহার করছি যা বেশিরভাগ 8086 নির্দেশাবলী রয়েছে, যেমন আমি ইতিমধ্যে করছিলাম এর জন্য sub
এবং cmp
।)
এটি 32-বিট মোডে bt
/ salc
এ হারিয়ে যায় এবং সুস্পষ্ট দৈর্ঘ্যের স্ট্রিংগুলিকে গণনার জন্য ইসিএক্স দরকার হয় যাতে এটি সেখানে কাজ করে না।
তবে আমি বুঝতে পেরেছিলাম যে আমি ভুল ছিলাম: bt edx, eax
তবুও ইক্সে উচ্চ আবর্জনা নিয়ে কাজ করে। এটি দৃশ্যত শিফট গণনাটিকে একইভাবেshr r32, cl
মাস্ক করে (কেবলমাত্র ক্লেলের কম 5 বিটের দিকে তাকিয়ে)। এটি এর থেকে আলাদা bt [mem], reg
, যা অ্যাড্রেসিং-মোড / আকার দ্বারা রেফারেন্স করা মেমরির বাইরে অ্যাক্সেস করতে পারে, এটি একটি বিটস্ট্রিং হিসাবে বিবেচনা করে। (ক্রেজি সিআইএসসি ...)
ইন্টেলের ইনসন সেট রেফ ম্যানুয়ালটি মাস্কিংয়ের নথি দেয় না, সুতরাং এটি সম্ভবত অননুমোদিত আচরণ যা বর্তমানে ইন্টেল সংরক্ষণ করছে। (এই ধরণের জিনিসটি অস্বাভাবিক নয় bsf dst, src
s এসআরসি = 0 দিয়ে সর্বদা ডিসস্টিমেড ছেড়ে যায়, যদিও এই ক্ষেত্রে এটি অনির্ধারিত মান রাখার নথিভুক্ত থাকে AM এএমডি আসলে src = 0 আচরণের নথি দেয়)) আমি স্কাইলেক এবং কোর 2 তে পরীক্ষা করেছি, এবং bt
সংস্করণটি AL এর বাইরে EAX- এ শূন্য-নন আবর্জনা নিয়ে কাজ করে।
সিএলে xchg eax,ecx
গণনা পেতে এখানে একটি ঝরঝরে কৌশল (1 বাইট) ব্যবহার করছে । দুর্ভাগ্যক্রমে, BMI2 shrx eax, edx, eax
হ'ল 5 বাইট, বনাম মাত্র 2 বাইট shr eax, cl
। ব্যবহারের bextr
জন্য একটি 2-বাইট প্রয়োজন mov ah,1
(বিটগুলির সংখ্যা বের করার জন্য), সুতরাং এটি আবার SHRX + AND এর মতো 5 + 2 বাইট।
%if
শর্তাদি যুক্ত করার পরে উত্স কোডটি বেশ অগোছালো হয়ে গেছে । এখানে x32 অন্তর্নিহিত দৈর্ঘ্যের স্ট্রিংগুলি বিছিন্ন করা (বিটম্যাপের জন্য বিকল্প কৌশল ব্যবহার করে, এটি এখনও 46 বাইট)।
সুস্পষ্ট দৈর্ঘ্যের সংস্করণ থেকে মূল পার্থক্যটি প্রথম লুপে। lods
লুপের শীর্ষে কেবল একটির পরিবর্তে এটির আগে এবং নীচে রয়েছে কীভাবে তা লক্ষ্য করুন ।
; 64-bit implicit-length version using the alternate bitmap strategy
00400060 <dennis_like>:
400060: 55 push rbp
400061: 89 e5 mov ebp,esp
400063: ac lods al,BYTE PTR ds:[rsi]
00400064 <dennis_like.filter_loop>:
400064: 24 5f and al,0x5f
400066: 2c 41 sub al,0x41
400068: 3c 19 cmp al,0x19
40006a: 77 0b ja 400077 <dennis_like.non_alpha>
40006c: 91 xchg ecx,eax
40006d: b8 ee be ef 03 mov eax,0x3efbeee ; inside the loop since SHR destroys it
400072: d3 e8 shr eax,cl
400074: 24 01 and al,0x1
400076: 50 push rax
00400077 <dennis_like.non_alpha>:
400077: ac lods al,BYTE PTR ds:[rsi]
400078: 84 c0 test al,al
40007a: 75 e8 jne 400064 <dennis_like.filter_loop>
40007c: 89 ee mov esi,ebp
0040007e <dennis_like.palindrome_loop>:
40007e: 58 pop rax
40007f: 83 ee 08 sub esi,0x8
400082: 32 06 xor al,BYTE PTR [rsi]
400084: 75 04 jne 40008a <dennis_like.non_palindrome>
400086: 39 e6 cmp esi,esp
400088: 77 f4 ja 40007e <dennis_like.palindrome_loop>
0040008a <dennis_like.non_palindrome>:
40008a: ff c8 dec eax ; invert the 0 / non-zero status of AL. xor al,1 works too, and produces a proper bool.
40008c: c9 leave
40008d: c3 ret
0x8e - 0x60 = 0x2e = 46 bytes