16

6ビットASCIIをバイナリ形式に変換するための次のプログラムがあります。

ascii2bin :: Char -> B.ByteString
ascii2bin = B.reverse . fst . B.unfoldrN 6 decomp . to6BitASCII -- replace to6BitASCII with ord if you want to compile this
    where decomp n = case quotRem n 2 of (q,r) -> Just (chr r,q)

bs2bin :: B.ByteString -> B.ByteString
bs2bin = B.concatMap ascii2bin

これにより、次のコアセグメントが生成されます。

Rec {
$wa
$wa =
  \ ww ww1 ww2 w ->
    case ww2 of wild {
      __DEFAULT ->
        let {
          wild2
          wild2 = remInt# ww1 2 } in
        case leWord# (int2Word# wild2) (__word 1114111) of _ { 
          False -> (lvl2 wild2) `cast` ...;                                                                                   
          True ->
            case writeWord8OffAddr#
                   ww 0 (narrow8Word# (int2Word# (ord# (chr# wild2)))) w
            of s2 { __DEFAULT ->
            $wa (plusAddr# ww 1) (quotInt# ww1 2) (+# wild 1) s2
            }   
        };  
      6 -> (# w, (lvl, lvl1, Just (I# ww1)) #)
    }   
end Rec }

に注意してord . chr == idください。したがって、ここには冗長な操作があります。narrow8Word# (int2Word# (ord# (chr# wild2)))

GHCが不必要にInt->Char->Intから変換している理由はありますか、それともこれは不十分なコード生成の例ですか?これを最適化できますか?

編集:これはGHC 7.4.2を使用していますが、他のバージョンでコンパイルしようとしたことはありません。それ以来、問題はGHC 7.6.2に残っていることがわかりましたが、冗長な操作はgithubの現在のHEADブランチで削除されています。

4

1 に答える 1

19

GHCが不必要にから変換している理由はありInt -> Char -> Intますか、それともこれは不十分なコード生成の例ですか?これを最適化できますか?

実際にはそうではありません(両方に)。あなたが得るコア-ddump-simplは終わりではありません。その後、アセンブリコードに向かう途中で、まだいくつかの最適化と変換が行われています。ただし、ここで冗長な変換を削除することは、実際には最適化ではありません。

それらは、コアとアセンブリの間で取り外すことができ、取り外すことができます。重要なのは、これらのプリモップ(ナローイングを除く)はノーオペレーションであり、タイプされているため、コアにのみ存在するということです。それらはノーオペレーションであるため、コアにそれらの冗長チェーンがあるかどうかは関係ありません。

7.6.1がコードから生成するアセンブリ[7.4.2が生成するものよりも読みやすいので、私はそれを取ります]-のord代わりにto6BitASCII-は

ASCII.$wa_info:
_cXT:
    addq $64,%r12
    cmpq 144(%r13),%r12
    ja _cXX
    movq %rdi,%rcx
    cmpq $6,%rdi
    jne _cXZ
    movq $GHC.Types.I#_con_info,-56(%r12)
    movq %rsi,-48(%r12)
    movq $Data.Maybe.Just_con_info,-40(%r12)
    leaq -55(%r12),%rax
    movq %rax,-32(%r12)
    movq $(,,)_con_info,-24(%r12)
    movq $lvl1_rVq_closure+1,-16(%r12)
    movq $lvl_rVp_closure+1,-8(%r12)
    leaq -38(%r12),%rax
    movq %rax,0(%r12)
    leaq -23(%r12),%rbx
    jmp *0(%rbp)
_cXX:
    movq $64,192(%r13)
_cXV:
    movl $ASCII.$wa_closure,%ebx
    jmp *-8(%r13)
_cXZ:
    movl $2,%ebx
    movq %rsi,%rax
    cqto
    idivq %rbx
    movq %rax,%rsi
    cmpq $1114111,%rdx
    jbe _cY2
    movq %rdx,%r14
    addq $-64,%r12
    jmp GHC.Char.chr2_info
_cY2:
    movb %dl,(%r14)
    incq %r14
    leaq 1(%rcx),%rdi
    addq $-64,%r12
    jmp ASCII.$wa_info
    .size ASCII.$wa_info, .-ASCII.$wa_info

narrow8Word# (int2Word# (ord# (chr# wild2)))コアに表示される部分は、の後にありcmpq $1114111, %rdxます。商が範囲外でない場合、コードはジャンプし_cY2て、そのような変換は含まれなくなります。バイトが配列に書き込まれ、いくつかのポインター/カウンターがインクリメントされます。それで、先頭に戻ります。

これからGHCが現在行うよりも優れたコードを生成することは可能だと思いますが、冗長なno-op変換はすでに消えています。

于 2013-02-26T18:51:01.297 に答える