2015-03-21 15 views
5

in JavaScript, divisione per zero con argomenti "intero" agisce come punti flottanti voglia:In che modo asm.js gestisce lo split-per-zero?

1/0; // Infinity 
-1/0; // -Infinity 
0/0; // NaN 

Le specifiche asm.js dice che la divisione con argomenti interi restituisce intish, che deve essere immediatamente costretto a con o senza segno. Se facciamo questo in javascript, divisione per zero con argomenti "integer" restituisce sempre zero dopo coercizione:

(1/0)|0; // == 0, signed case. 
(1/0) >> 0; // == 0, unsigned case. 

Tuttavia, in lingue con i tipi interi effettivi come Java e C, dividendo un numero intero da zero è un errore e l'esecuzione si arresta in qualche modo (ad esempio, genera un'eccezione, fa scattare una trappola, ecc.).

Anche questo sembra violare le firme di tipo specificate da asm.js. Il tipo di Infinity e NaN è double e di / è presumibilmente (dalla spec):

(firmato, firmato) → intish ∧ (non firmato, non firmato) → intish ∧ (doppia ?, il doppio?) → doppia ∧ (float float ?,?) → floatish

Tuttavia se uno di questi ha un denominatore zero, il risultato è double, così sembra come il tipo può essere solo:

(doppia ?, il doppio?) → doppia

Cosa ci si aspetta che accada nel codice asm.js? Segue javascript e restituisce 0 o divide per zero produce un errore di runtime? Se segue javascript, perché è corretto che la digitazione sia errata? Se produce un errore di runtime, perché le specifiche non lo menzionano?

risposta

4

asm.js è un sottoinsieme di JavaScript, quindi deve restituire ciò che JavaScript fa: Infinity|00.

Si segnala che Infinity è double, ma che mescola il sistema di tipo asm.js con la C uno (in JavaScript quelli sono number): asm.js utilizza tipo JavaScript coercizione per rendere i risultati intermedi del tipo "giusto" quando non lo sono La stessa cosa accade quando un piccolo numero intero in JavaScript si sovrappone a un double: viene ricondotto in un intero utilizzando operazioni bit a bit.

La chiave qui è che dà al compilatore un suggerimento che non ha bisogno di calcolare tutte le cose che normalmente avrebbe dovuto calcolarlo: non importa se un piccolo intero trabocca perché è forzato di nuovo in un numero intero , quindi il compilatore può omettere i controlli di overflow ed emettere l'aritmetica dei numeri interi. Si noti che deve ancora funzionare correttamente per ogni possibile valore! Il sistema di tipi in genere suggerisce al compilatore di eseguire una serie di riduzioni della forza.

Ora torna alla divisione intero: su x86 ciò provoca un'eccezione a virgola mobile (sì! La divisione integer causa SIGFPE!). Il compilatore conosce l'output è un numero intero in modo che possa eseguire una divisione intera, ma non può arrestare il programma se il denominatore era zero. Esistono due opzioni:

  • Ramo intorno alla divisione se l'input è zero e restituisce zero direttamente.
  • Effettuare la divisione con l'input fornito, ma all'inizio del programma installare un gestore di segnale, rilevando SIGFPE. Quando fallisce, cerca la posizione del codice, e se i metadati del compilatore dicono che si tratta di una divisione, modifica il valore restituito come zero e continua a eseguire.

Il primo è ciò che implementano V8 e OdinMonkey.

Su ARM l'istruzione di divisione intera viene definita per restituire sempre zero, eccetto il profilo ARMv7-R di ARM dove è guasto (l'errore è istruzione indefinita, oppure può essere modificato per tornare a zero se SCTRL.DZ == 0). ARM ha aggiunto solo le istruzioni UDIV e SDIV di recente con l'estensione ARMv7VE (estensione di virtualizzazione) e lo ha reso opzionale nei processori ARMv7-A (la maggior parte dei telefoni e dei tablet li usa). È possibile verificare le istruzioni usando /proc/cpuinfo, ma si noti che alcuni kernel non sono a conoscenza delle istruzioni! Una soluzione alternativa consiste nel verificare le istruzioni all'avvio del processo eseguendo l'istruzione e utilizzando sigsetjmp/siglongjmp per individuare casi in cui non è gestito. Questo ha un ulteriore avvertimento anche nei casi in cui il kernel è "utile" ed emula UDIV/IDIV su processori che non lo supportano! Se l'istruzione non è presente, è necessario utilizzare l'istruzione di divisione intera della libreria C (libgcc o compiler_rt contenere funzioni come __udivmoddi4). Si noti che il comportamento di questa funzione su dividere per zero può variare tra le implementazioni e deve essere gestito con un ramo su zero denominatore o controllato al momento del caricamento (come descritto sopra per UDIV/SDIV).

Ti lascio a una domanda: cosa succede in asm.js durante l'esecuzione del seguente codice C: INT_MIN/-1?

+1

Quindi il principio generale qui è che asm.js eredita tutta la semantica di JS più tipi numerici extra e il suo fino ai compilatori che prendono di mira asm.js per implementare la semantica del loro linguaggio (ad es. Di C) sopra a quelli. 'INT_MIN/-1' non è definito in C, ma in asm.js dopo il troncamento è di nuovo INT_MIN (overflow con segno) e poiché il comportamento non è definito in C è ok? –

+0

@FrancisAvila ci sono due compilatori: LLVM che genera codice asm.js da C/C++ e SpiderMonkey/V8 che genera assembly da JavaScript. SpiderMonkey ha una scorciatoia per il codice asm.js valido (OdinMonkey), ma quella scorciatoia deve ancora essere JavaScript valido al 100%. V8 fa qualcosa di simile con TurboFan, ma non convalida il sottoinsieme asm.js (solo "use asm" è sufficiente per attivarlo). LLVM deve generare il codice asm.js che segue le regole C, ed è per questo che 'INT_MIN/-1' può fare tutto ciò che vuole perché non è definito, quindi il risultato di JavaScript è buono come qualsiasi. SpiderMonkey/V8 non sanno nulla delle regole C! –

Problemi correlati