2010-06-29 12 views
13

Usando el ensamblador en línea [gcc, intel, c], cómo verificar si el indicador de acarreo se establece después de una operación?compruebe si el indicador de acarreo está establecido

+0

¿Desea probar esto dentro de un bloque de asm o desea pasar el estado de la bandera de acarreo a algo en el código C en el que su asm está incrustado? –

+0

probando dentro de un bloque de asm es suficiente. repartirlo no debería ser tan difícil. – hans

Respuesta

10

Con saltos condicionales jc (jump if carry) o jnc (jump if not carry).

O puede almacenar la bandera de acarreo,

;; Intel syntax 
mov eax, 0 
adc eax, 0 ; add with carry 
+0

ah ok, no conocía los comandos jc, jnc, adc. Thx – hans

+2

Warnin, GCC usa la sintaxis de AT & T. Esta es la sintaxis de Intel ... –

+1

@Fififox, gracias. Edité mi respuesta. –

14

sbb %eax,%eax almacenará -1 en eax si se establece el indicador de acarreo, 0 si está claro. No es necesario borrar previamente eax a 0; restar eax de sí mismo hace eso por ti. Esta técnica puede ser muy poderosa ya que puede usar el resultado como máscara de bits para modificar los resultados de los cálculos en lugar de usar saltos condicionales.

Debe tener en cuenta que solo es válido para probar la bandera de acarreo si fue establecida por aritmética realizada DENTRO del bloque de asas en línea. No se puede realizar una prueba de un cálculo que se realizó en código C porque hay muchas formas en que el compilador puede optimizar/reordenar cosas que marcarían la bandera de acarreo.

5

Sin embargo, el ensamblador x86 está dedicado rápido ALU indica las instrucciones de la prueba denominadas SETcc donde se desea el cc ALU. Por lo tanto se puede escribir:

setc AL       //will set AL register to 1 or clear to 0 depend on carry flag 

or 

setc byte ptr [edx]    //will set memory byte on location edx depend on carry flag 

or even 

setc byte ptr [CarryFlagTestByte] //will set memory variable on location CarryFlagTestByte depend on carry flag 

Con SETcc de instrucciones que puede probar banderas como equipaje, cero, señal, desbordamiento o la paridad, algunos SETcc instrucciones permiten poner a prueba dos banderas a la vez.

EDIT: prueba simple Añadido hace en Delphi para desaparecer a dudas sobre plazo rápida

procedure TfrmTest.ButtonTestClick(Sender: TObject); 
    function GetCPUTimeStamp: int64; 
    asm 
    rdtsc 
    end; 
var 
ii, i: int64; 
begin 
    i := GetCPUTimeStamp; 
    asm 
    mov ecx, 1000000 
@repeat: 
    mov al, 0 
    adc al, 0 
    mov al, 0 
    adc al, 0 
    mov al, 0 
    adc al, 0 
    mov al, 0 
    adc al, 0 
    loop @repeat 
    end; 
    i := GetCPUTimeStamp - i; 

    ii := GetCPUTimeStamp; 
    asm 
    mov ecx, 1000000 
@repeat: 
    setc al 
    setc al 
    setc al 
    setc al 
    loop @repeat 
    end; 
    ii := GetCPUTimeStamp - ii; 
    caption := IntToStr(i) + ' ' + IntToStr(ii)); 
end; 

El bucle (iteraciones 1M) wich usando la instrucción fcolor es más de 5 veces más rápido que bucle con adc instriuction.

EDITAR: Se agregó una segunda prueba cuyo resultado de prueba se almacenó en el registro AL comulativo en el registro CL para ser un caso más realista.

procedure TfrmTestOtlContainers.Button1Click(Sender: TObject); 
    function GetCPUTimeStamp: int64; 
    asm 
    rdtsc 
    end; 

var 
ii, i: int64; 
begin 
    i := GetCPUTimeStamp; 
    asm 
    xor ecx, ecx 
    mov edx, $AAAAAAAA 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    shl edx, 1 
    mov al, 0 
    adc al, 0 
    add cl, al 

    end; 
    i := GetCPUTimeStamp - i; 

    ii := GetCPUTimeStamp; 
    asm 
    xor ecx, ecx 
    mov edx, $AAAAAAAA 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    shl edx, 1 
    setc al 
    add cl, al 

    end; 
    ii := GetCPUTimeStamp - ii; 
    caption := IntToStr(i) + ' ' + IntToStr(ii); 
end; 

parte Rutina con la instrucción SETcc es todavía más rápido alrededor del 20%.

+0

¿Tiene una cita para llamarlos rápido? No me he mantenido actualizado con la última versión de las últimas generaciones, pero durante mucho tiempo se consideraron lentos códigos de operación. –

+0

@R .. No hay duda: ¡estás equivocado! Verifique la prueba superior! –

+1

@R .. Sí, el SETcc es una instrucción antigua, pero es mucho más rápido que ADC o utiliza saltos cinditional como JC o JNC. –

2

La primera función realiza la suma sin signo y luego prueba el desbordamiento usando la bandera de acarreo (CF). Los volátiles deben permanecer. De lo contrario, el optimizador reorganizará las instrucciones, lo que garantiza un resultado incorrecto. He visto el optimizador cambiar el jnc a un jae (que también se basa en CF).

/* Performs r = a + b, returns 1 if the result is safe (no overflow), 0 otherwise */ 
int add_u32(uint32_t a, uint32_t b, uint32_t* r) 
{ 
    volatile int no_carry = 1; 
    volatile uint32_t result = a + b; 

    asm volatile 
    (
    "jnc 1f   ;" 
    "movl $0, %[xc] ;" 
    "1:    ;" 
    : [xc] "=m" (no_carry) 
    ); 

    if(r) 
     *r = result; 

    return no_carry; 
} 

La siguiente función es para los enteros con signo. El mismo uso de volátil se aplica. Tenga en cuenta que la matemática entera con signo salta en el indicador OF a través del jno. He visto el optimizador cambiar esto a jnb (que también se basa en OF).

/* Performs r = a + b, returns 1 if the result is safe (no overflow), 0 otherwise */ 
int add_i32(int32_t a, int32_t b, int32_t* r) 
{ 
    volatile int no_overflow = 1; 
    volatile int32_t result = a + b; 

    asm volatile 
    (
    "jno 1f   ;" 
    "movl $0, %[xo] ;" 
    "1:    ;" 
    : [xo] "=m" (no_overflow) 
    ); 

    if(r) 
     *r = result; 

    return no_overflow; 
} 

En la imagen grande, puede utilizar las funciones de la siguiente manera.En el mismo cuadro grande, muchas personas probablemente rechazar el trabajo extra y estética no-belleza hasta PWN'D por un desbordamiento/abrigo/desbordamiento

int r, a, b; 
... 

if(!add_i32(a, b, &r)) 
    abort(); // Integer overflow!!! 

... 

El montaje de GCC en línea está disponible en GCC 3.1 y superior. Consulte Assembler Instructions with C Expression Operands, o busque 'Conjunto extendido GCC'.

Por último, el mismo en Visual Studio sería el siguiente (no hay mucha diferencia en la generación de código), pero la sintaxis es mucho más fácil ya MASM le permite saltar a una etiqueta C:

/* Performs r = a + b, returns 1 if the result is safe (no overflow), 0 otherwise */ 
int add_i32(__int32 a, __int32 b, __int32* r) 
{ 
    volatile int no_overflow = 1; 
    volatile __int32 result = a + b; 

    __asm 
    { 
     jno NO_OVERFLOW; 
     mov no_overflow, 0; 
    NO_OVERFLOW: 
    } 

    if(r) 
     *r = result; 

    return no_overflow; 
} 

En la parte negativa lado, el código MASM anterior solo es aplicable para el ensamblaje x86. Para el ensamblaje x64, no hay alineación, por lo que deberá codificarlo en ensamblaje (en un archivo separado) y utilizar MASM64 para compilar.

+0

Puede usar la extensión goto para saltar a una etiqueta C usando, por ejemplo, asm volátil goto ("ja% l [clabel]"::: "memoria": clabel) ;, donde clabel es C etiqueta –

+0

Leer debajo de la respuesta por @R .. parece invalidar su función. 'Debe tener en cuenta que solo es válido para probar la bandera de acarreo si fue establecida por aritmética realizada DENTRO del bloque de asas en línea. ¿Está seguro de esto? –

+0

@DrBeco - sí, para GCC, depende. GCC garantizará la "consecutividad" de las instrucciones en su bloque, pero puede insertar/intercalar sus propias instrucciones. Si las instrucciones de GCC no modifican CC, entonces todo estará bien. El ensamblador en línea de Microsoft no sufre las limitaciones de GCC. – jww

Cuestiones relacionadas