2015-11-08 6 views
17

Ecco il codice che aggiunge due triple di Parole disimballati che rappresentano un numero 192 bit in un nuova tripla di parole unboxed, e anche restituisce qualsiasi overflow:Ottenere GHC per la produzione di "Add Con Carry (ADC)" istruzioni

{-# LANGUAGE MagicHash #-} 
{-# LANGUAGE UnboxedTuples #-} 

import GHC.Prim(plusWord2#, Word#, or#) 

longAdd :: 
    (# Word#, Word#, Word# #) -> 
    (# Word#, Word#, Word# #) -> 
    (# Word#, (# Word#, Word#, Word# #) #) 

longAdd (# xl, xm, xh #) (# yl, ym, yh #) =  
    let 
    plusWord3 x y c = 
     let 
     (# c1, r1 #) = plusWord2# x y 
     (# c2, r2 #) = plusWord2# r1 c 
     in 
     (# plusWord# c1 c2, r2 #) 
    (# cl, rl #) = plusWord2# xl yl 
    (# cm, rm #) = plusWord3 xm ym cl 
    (# ch, rh #) = plusWord3 xh yh cm  
    in 
    (# ch, (# rl, rm, rh #) #) 

Il problema è la definizione "plusWord3". Idealmente, questo è proprio come una funzione di "ADC", che prende due parole e il bit di riporto e restituisce il risultato e una nuova trasportare, in modo l'assembly risultante è simile al seguente:

add x1 y1 
adc x2 y2 
adc x3 y3 

Purtroppo GHC, sia nativo o tramite LLVM, produce un codice di assemblaggio brutto che comporta il salvataggio del bit di carry in un registro e quindi la lettura tramite un addizionale aggiuntivo separato, invece di utilizzare semplicemente adc. Non voglio chiamare una funzione C esterna per ottenere questo risultato, poiché una volta che aggiungi l'overhead della chiamata probabilmente non ne vale la pena, mi piacerebbe rimanere in Haskell in modo che il codice possa essere allineato laddove possibile. Ma voglio anche essere in grado di convincere il compilatore a produrre l'istruzione adc in modo appropriato. C'è comunque posso ottenerlo?

+1

192 parole bit (e molti altri) sono disponibili in [ 'dati.DoubleWord'] (https://hackage.haskell.org/package/data-dword-0.3/docs/Data-DoubleWord.html). Mi chiedo se è ottimizzato per generare codice efficiente, quindi potrebbe essere una fonte di conoscenza su come farlo, o un posto dove contribuire, una volta trovata la risposta. –

risposta

8

Non ho familiarità con la programmazione di basso livello, ma dopo una domanda sul canale #ghc di Freenode, ho ottenuto un puntatore a addIntC# primop, che è correlato a LLVM llvm.sadd.with.overflow.. Non sono sicuro di ciò che llvm compila.


Il codice nativo gen di GHC sembra sapere su adc istruzioni: X86/CodeGen.hs. Ma, come dice commento:

gestiamo aggiunta, ma piuttosto male


Edit: si lavora con le parole. Sembra che LLVM backend si compila MO_Add2 (che è un altro nome per plusWord2) per llvm.uadd.with.overflow in https://github.com/ghc/ghc/blob/2b7d9c2b96eb9da3cce7826df4a91c3426095528/compiler/llvmGen/LlvmCodeGen/CodeGen.hs#L737, biglietto correlati: https://ghc.haskell.org/trac/ghc/ticket/9430

11

Modo più attendibile ed efficiente sarebbe chiamando un primop direttamente nel programma.

L'utilizzo di una chiamata FFI è il modo più semplice ma, come è stato anche notato, non sarà il modo più efficiente, a causa dei costi fissi FFI.

Anche se il compilatore supporta l'istruzione desiderata e la utilizza in alcuni programmi, sarebbe fragile. Alcuni cambiamenti apparentemente innocenti nel tuo programma potrebbero finire con assemblaggi generati diversi che non usano l'istruzione che desideri.

Quindi la mia proposta è:

  1. Aggiungere le istruzioni necessarie per il codice X86 generatore di back-end, se non è già lì.
  2. Aggiungere un primop che si traduce direttamente nell'istruzione che si desidera eseguire. Innanzitutto assicurati che non esista alcun primop. Quindi attenersi alla seguente procedura: https://ghc.haskell.org/trac/ghc/wiki/AddingNewPrimitiveOperations
  3. È necessario che primop sia visibile in GHC.Prim (http://hackage.haskell.org/package/ghc-prim/docs/GHC-Prim.html), utilizzarlo nei programmi.
  4. aggiungere test, presentare il cerotto :)
Problemi correlati