In article news:e11nd7$udn$1@bluegill.lbm.go.jp
 toda@lbm.go.jp wrote:

> In article <e0usk3$11nc$1@nntp.tiki.ne.jp> hyama@mx7.tiki.ne.jp writes:
> 「検査」が目的だったら、andではなくtestを使うのが常道では?
 and 命令の影響を考察してみましたが、なんだか手段と目的が
逆転してたような感じですね。(^^ゞ

> ・自分自身でなく他者(他レジスタ・即値・メモリ値)で検査する場合と
>  ニーモニックが同一になって、意味を理解しやすくなり、
>  ニーモニック誤記によるバグが防げる
 8ビット機でもマクロアセンブラでよくやりました。
    clr r { xor r,r } とか、
    test r { and r,r }

> ・結果をレジスタに書き戻そうとしない分、速くなる可能性がある
> という利点があります。
 測ってみました。
# 単一命令の繰り返しなので、実際の傾向を反映してないかも知れません。

    6621C9     and  %cx,%cx     // 2.410
    21C9       and  %ecx,%ecx   // 2.410
    6685C9     test %cx,%cx     // 1.109
    85C9       test %ecx,%ecx   // 0.965
2.5倍も早かったです。

    6631C9     xor %cx,%cx      // 2.410
    66B90000   mov $0,%cx       // 2.410
    6629C9     sub %cx,%cx      // 2.410
    31C9       xor %ecx,%ecx    // 0.965
    B900000000 mov $0,%ecx      // 1.061
    29C9       sub %ecx,%ecx    // 0.964
こっちは、コードサイズの割には一割ほど。

    6683F9FF   cmp $-1,%cx      // 0.836
    83F9FF     cmp $-1,%ecx     // 1.109
    6641       inc %cx          // 2.410
    41         inc %ecx         // 2.410
コードサイズのと実行速度が逆転してます。
なぜか、cmp ecx より cmp cx の方が早い。

> もちろん、そもそもtestという命令が無いcpuだと話は別ですが。
 今回の場合 x86 が本題でしたから、元祖 8086 からある test命令を
使った方が正しいと思います。



 ついでなので、閏年の判定関数を二つ作って
遊んで^H^H^H速度比較してみました。

int leap_asm( unsigned int y ){
    __asm(
    "movl    8(%ebp), %edx" "\n\t"  // y

    "xor     %eax, %eax"    "\n\t"
    "test    $3, %dl"   "\n\t"
    "jnz     ret_ax"    "\n\t"

    "xchg    %eax, %edx"    "\n\t"  // eax==0, edx<->eax
    "mov     $100, %cx" "\n\t"
    "div     %cx"       "\n\t"
    "test    %dx, %dx"  "\n\t"
    "jnz     ret_1"     "\n\t"

    "xchg    %eax, %edx"    "\n\t"  // edx == 0
    "test    $3, %dl"   "\n\t"
    "jnz     ret_ax"    "\n\t"

"ret_1:"                "\n\t"
    "mov     $1, %eax"  "\n\t"

"ret_ax:"               "\n\t"
    );
}

int leap_asm2( unsigned int y ){
    __asm(
    "movl    8(%ebp), %edx" "\n\t"  // y

    "xor     %eax, %eax"    "\n\t"
    "test    $3, %dl"   "\n\t"
    "jnz     ret_ax"    "\n\t"

    "mov     $400, %cx" "\n\t"  // y = 1583 .. 4000
    ".p2align 2,,3"     "\n\t"
"leap_400:"         "\n\t"
    "sub     %cx, %dx"  "\n\t"
    "jnc     leap_400"  "\n\t"
//  "jz      ret_1"     "\n\t"

    "mov     $100, %cx" "\n\t"  // sar $2,%cx
    "add     %cx, %dx"  "\n\t"
    "jz      ret_ax"    "\n\t"
    "add     %cx, %dx"  "\n\t"
    "jz      ret_ax"    "\n\t"
    "add     %cx, %dx"  "\n\t"
    "jz      ret_ax"    "\n\t"

//"ret_1:"              "\n\t"
    "mov     $1, %eax"  "\n\t"
"ret_ax:"               "\n\t"
    );
}

int leap_std( unsigned y )
{
    return( y % 4 == 0 && y % 100 != 0 || y % 400 == 0 );
}

 前者が div ありで、後者が減算ループです、
グレゴリオ暦の閏年処理なので検査は、1583..3000までにしました。

    leap_asm   1.385
    leap_asm2  1.286
    leap_std   4.762
四分の三は同じ処理なので、ほとんど差が出ませんね。(^^;

# 8,16bit CPU の感覚で書いているので、
# 調整すべきところがあると思います。(--;

-- 
山口@福岡 <hyama@mx7.tiki.ne.jp>