Дано: EAX - 32-битное число ECX - число в диапазоне 0..7 включительно Задача: Написать функцию возвращающую EAX-1 если EAX<0 и ECX mod 4 == 0, и EAX во всех других случаях. При этом не использовать переходы, таблицы и предикатные команды SETcc. Скажу сразу - у меня пока решения нет и я вообще не уверен что это возможно. Функция оптимизируется по скорости, т.е. желательно обойтись логическими операциями. Вот код на Си кто словами не понял Код (Text): int func(int x,int i) { if(x<0 && (i & 3)==0) return x-1; return x; } Немного подумав пришёл к такому выражению: f(x)=x+((x>>31) & k); где k должно быть -1 для i=0 или i=4, и 0 в других случаях. Вот такую функцию я придумать что-то не могу... Точнее могу, но мне не нравится. f(x)=x+((x & (~i << 30) & (~i << 31))>>31); Может кто-нибудь предложит вариант с меньшим числом операций?
cppasm Здесь разве не f(x)=x-... должно быть? На одну операцию меньше: f(x) = x - (x>>31 & ~(i | (i>>1)) & 1);
Нет, именно плюс - число знаковое. При SAR REG32, 31 в REG32 будет -1 для отрицательных и 0 для положительных. Операций столько же. В моём варианте инверсия i выполняется дважды - можно вынести за выражение. Т.е. сделать инверсию i и дальше везде использовать инвертированное значение. Вот ещё вариация, операций столько же: f(x)=x+((x & (~((i >> 1) | i) << 31)) >> 31);
cppasm mov ebx,ecx neg ebx and ebx,ecx; выделяем самый левый единичный бит cmp ebx,1 sbb esi,esi; если число в ebx = 0 тогда esi = -1 иначе esi = 0 cmp ebx,4 sbb ebx,ebx; если число в ebx <4 тогда ebx = -1 иначе ebx =0 xor ebx,esi; если число в ecx не кратно 4 тогда ebx=-1 иначе ebx=0 cmp eax,80000000h sbb edx,edx; если число в eax > 0 тогда CF=1 и edx=-1 иначе edx=0 or ebx,edx not ebx lea eax,[eax+ebx];EAX-1 если EAX<0 и ECX mod 4 == 0, и EAX во всех других случаях. ;При этом не использованы переходы, таблицы и предикатные команды SETcc. но у murder код короче
murder - прикольно leo - и как ты такое только придумываеш У меня такой вариант есть: f(x)=x+((x & ((i & 3)-1)) >> 31); или то же самое на ASM: Код (Text): and ecx,3 dec ecx and ecx,eax sar ecx,31 add eax,ecx Вопрос теперь что быстрее. Насколько cdq медленная команда? По документации она не спаривается, значит не самый лучший выбор...
leo Круто! Но там наверное sbb eax,0 вместо adc eax,edx. cppasm Вроде бы проблема не в cdq, а в adc. Ждём Black_mirror.
murder Не-а. Перенос возникает при (eax < 0)&&(ecx & 3 != 0), т.е. когда eax изменять не нужно - в этом сл. adc eax,edx == (eax-1)+1 == eax cppasm cdq на современных компах - однотактовая и нормально "спариваемая" (по сути это расширенный аналог movsx) А вот adc\sbb - на нормальных процах не более 2-х тактов, на дебильных P4 до 8-ми (?!). Но т.к. это команда последняя, то в итоге все зависит от того как дальше используется рез-т из eax (например, если крутить этот код в цикле, то задержка adc никакой роли играть не будет). Кстати в первых моделях P4 (Northwood, которые еще живут и здравствуют) все сдвиги выполняются за 4 такта... Твой вариант не слишком хорош тем, что все операции в нем зависимы, т.е. как ни крути, а не менее 5 тактов. Наверняка можно что-то распараллелить
leo О великий гуру, ты открыл мне глаза! sub eax,1 это не то же самое, что add eax,-1. Буду медитировать над этим. Добавлено: Ага - логично, всё понял.
А твой с murder не очень хорош тем, что заставить HLL компилятор использовать ADC/SBB это надо постараться. А писать ассемблерной вставкой - ставить палки в колёса оптимизатору. К тому же до и после этого кода идут расчёты на fpu, так что команды эти всё равно идут не подряд, а разбавлены командами от предыдущих и следующих вычислений. murder, я кстати тоже поначалу подумал что в конце должно быть не adc eax,edx а sbb eax,edx. Но после минуты смотрения на код понял что всё-таки написано правильно