0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ARM32アセンブリで確認する符号有無 整数の違いと演算の挙動

Posted at

同じバイナリでも符号の有無で10進数は変わる

同じ0xFFFFFFFEでも符号ありと解釈するか無しと解釈するかで値が変わる。

test.c
#include <stdio.h>

int main() {
    signed int a = 0xFFFFFFFE;
    unsigned int b = 0xFFFFFFFE;

    printf("signed a = %d\n", a);
    printf("unsigned b = %u\n", b);

    return 0;
}
testuser@CasaOS:~/test$ gcc test.c -o test
testuser@CasaOS:~/test$ ./test
signed a = -2
unsigned b = 4294967294

アセンブリで二つの値に差があるかを確認

変数格納

逆アセンブルの命令は以下の通り

gcc -O0 -S test.c -o test.s
signed int a = 0xFFFFFFFE;
unsigned int b = 0xFFFFFFFE;

以下のようなコードへ変換された。
変数へ値を格納する段階ではsigned intunsigned intの差異は見られない。

mvn     r3, #1
str     r3, [r7, #4]
mvn     r3, #1
str     r3, [r7]

指定した値をビット反転(NOT)してレジスタに入れる。
#1は32ビットで00000000 00000000 00000000 00000001
反転させると11111111 11111111 11111111 11111110となる。
16進数で表すと0xFFFFFFFE

元のコード
test.c
#include <stdio.h>

int main() {
    signed int a = 0xFFFFFFFE;
    unsigned int b = 0xFFFFFFFE;
    return 0;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .section        .rodata
        .align  2
.LC0:
        .ascii  "signed a = %d\012\000"
        .align  2
.LC1:
        .ascii  "unsigned b = %u\012\000"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
        push    {r7, lr}
        sub     sp, sp, #8
        add     r7, sp, #0
        mvn     r3, #1
        str     r3, [r7, #4]
        mvn     r3, #1
        str     r3, [r7]
        ldr     r1, [r7, #4]
        ldr     r3, .L3
.LPIC0:
        add     r3, pc
        mov     r0, r3
        bl      printf(PLT)
        ldr     r1, [r7]
        ldr     r3, .L3+4
.LPIC1:
        add     r3, pc
        mov     r0, r3
        bl      printf(PLT)
        movs    r3, #0
        mov     r0, r3
        adds    r7, r7, #8
        mov     sp, r7
        @ sp needed
        pop     {r7, pc}
.L4:
        .align  2
.L3:
        .word   .LC0-(.LPIC0+4)
        .word   .LC1-(.LPIC1+4)
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

加算命令 符号有無による違い

どちらもadds命令を使っており違いはない

signed int a = 0xFFFFFFFE;   // 2
unsigned int b = 0xFFFFFFFE; // 4294967294
    
// ...省略...
    
a = a + 11;
b = b + 22;
mvn     r3, #1
str     r3, [r7, #12]
mvn     r3, #1
str     r3, [r7, #8]

@ ...省略...

ldr     r3, [r7, #12]
adds    r3, r3, #11
str     r3, [r7, #12]
ldr     r3, [r7, #8]
adds    r3, r3, #22
str     r3, [r7, #8]
元のコード
test.c
int main() {
    signed int a = 0xFFFFFFFE;   // 2
    unsigned int b = 0xFFFFFFFE; // 4294967294

    int signed_result, unsigned_result;

    a = a + 11;
    b = b + 22;
    
    return signed_result + unsigned_result;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        mvn     r3, #1
        str     r3, [r7, #12]
        mvn     r3, #1
        str     r3, [r7, #8]
        ldr     r3, [r7, #12]
        adds    r3, r3, #11
        str     r3, [r7, #12]
        ldr     r3, [r7, #8]
        adds    r3, r3, #22
        str     r3, [r7, #8]
        ldr     r2, [r7, #4]
        ldr     r3, [r7]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

減算命令 符号有無による違い

どちらもsubs命令を使っており違いはない

signed int a = 0xFFFFFFFE;   // 2
unsigned int b = 0xFFFFFFFE; // 4294967294
    
// ...省略...
    
a = a - 11;
b = b - 22;
mvn     r3, #1
str     r3, [r7, #12]
mvn     r3, #1
str     r3, [r7, #8]

@ ...省略...

ldr     r3, [r7, #12]
subs    r3, r3, #11
str     r3, [r7, #12]
ldr     r3, [r7, #8]
subs    r3, r3, #22
str     r3, [r7, #8]
元のコード
test.c
int main() {
    signed int a = 0xFFFFFFFE;   // 2
    unsigned int b = 0xFFFFFFFE; // 4294967294

    int signed_result, unsigned_result;

    a = a - 11;
    b = b - 22;

    return signed_result + unsigned_result;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        mvn     r3, #1
        str     r3, [r7, #12]
        mvn     r3, #1
        str     r3, [r7, #8]
        ldr     r3, [r7, #12]
        subs    r3, r3, #11
        str     r3, [r7, #12]
        ldr     r3, [r7, #8]
        subs    r3, r3, #22
        str     r3, [r7, #8]
        ldr     r2, [r7, #4]
        ldr     r3, [r7]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

加算命令 符号有無による違い

どちらもadds命令を使っており違いはない

signed int a = 0xFFFFFFFE;   // 2
unsigned int b = 0xFFFFFFFE; // 4294967294
    
// ...省略...
    
a = a + 11;
b = b + 22;
mvn     r3, #1
str     r3, [r7, #12]
mvn     r3, #1
str     r3, [r7, #8]

@ ...省略...

ldr     r3, [r7, #12]
adds    r3, r3, #11
str     r3, [r7, #12]
ldr     r3, [r7, #8]
adds    r3, r3, #22
str     r3, [r7, #8]
元のコード
test.c
int main() {
    signed int a = 0xFFFFFFFE;   // 2
    unsigned int b = 0xFFFFFFFE; // 4294967294

    int signed_result, unsigned_result;

    a = a + 11;
    b = b + 22;
    
    return signed_result + unsigned_result;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        mvn     r3, #1
        str     r3, [r7, #12]
        mvn     r3, #1
        str     r3, [r7, #8]
        ldr     r3, [r7, #12]
        adds    r3, r3, #11
        str     r3, [r7, #12]
        ldr     r3, [r7, #8]
        adds    r3, r3, #22
        str     r3, [r7, #8]
        ldr     r2, [r7, #4]
        ldr     r3, [r7]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

乗算命令 符号有無による違い

どちらもmul命令を使っており違いはない

signed int a = 0xFFFFFFFE;   // 2
unsigned int b = 0xFFFFFFFE; // 4294967294
    
// ...省略...
    
a = a * 11;
b = b * 22;
mvn     r3, #1
str     r3, [r7, #12]
mvn     r3, #1
str     r3, [r7, #8]

@ ...省略...

ldr     r3, [r7, #12]
movs    r2, #11
mul     r3, r2, r3
str     r3, [r7, #12]
ldr     r3, [r7, #8]
movs    r2, #22
mul     r3, r2, r3
str     r3, [r7, #8]
元のコード
test.c
int main() {
    signed int a = 0xFFFFFFFE;   // 2
    unsigned int b = 0xFFFFFFFE; // 4294967294

    int signed_result, unsigned_result;

    a = a * 11;
    b = b * 22;

    return signed_result + unsigned_result;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        mvn     r3, #1
        str     r3, [r7, #12]
        mvn     r3, #1
        str     r3, [r7, #8]
        ldr     r3, [r7, #12]
        movs    r2, #11
        mul     r3, r2, r3
        str     r3, [r7, #12]
        ldr     r3, [r7, #8]
        movs    r2, #22
        mul     r3, r2, r3
        str     r3, [r7, #8]
        ldr     r2, [r7, #4]
        ldr     r3, [r7]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

除算命令 符号有無による違い

smullumullを使い分けている。

signed int a = 0xFFFFFFFE;   // 2
unsigned int b = 0xFFFFFFFE; // 4294967294
    
// ...省略...
    
a = a / 11;
b = b / 22;
mvn     r3, #1
str     r3, [r7, #12]
mvn     r3, #1
str     r3, [r7, #8]

@ ...省略...

ldr     r2, [r7, #12]
movw    r3, #41705
movt    r3, 11915
smull   r1, r3, r3, r2
asrs    r1, r3, #1
asrs    r3, r2, #31
subs    r3, r1, r3
str     r3, [r7, #12]
ldr     r2, [r7, #8]
movw    r3, #35747
movt    r3, 47662
umull   r2, r3, r3, r2
lsrs    r3, r3, #4
str     r3, [r7, #8]
元のコード
test.c
int main() {
    signed int a = 0xFFFFFFFE;   // 2
    unsigned int b = 0xFFFFFFFE; // 4294967294

    int signed_result, unsigned_result;

    a = a / 11;
    b = b / 22;

    return signed_result + unsigned_result;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        mvn     r3, #1
        str     r3, [r7, #12]
        mvn     r3, #1
        str     r3, [r7, #8]
        ldr     r2, [r7, #12]
        movw    r3, #41705
        movt    r3, 11915
        smull   r1, r3, r3, r2
        asrs    r1, r3, #1
        asrs    r3, r2, #31
        subs    r3, r1, r3
        str     r3, [r7, #12]
        ldr     r2, [r7, #8]
        movw    r3, #35747
        movt    r3, 47662
        umull   r2, r3, r3, r2
        lsrs    r3, r3, #4
        str     r3, [r7, #8]
        ldr     r2, [r7, #4]
        ldr     r3, [r7]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

比較命令

符号有りはbge、符号無しはbcs命令に変換された。

volatile signed int a = 0xFFFFFFFF;
volatile unsigned int b = 0xFFFFFFFF;
volatile signed int x = 0;
volatile unsigned int y = 0;
    
// ...省略...
    
if (a < x)
    
// ...省略...
    
if (b < x)
cmp     r2, r3
bge     .L2

@ ...省略...

cmp     r3, r2
bcs     .L4
元のコード
test.c
int main() {
    volatile signed int a = 0xFFFFFFFF;
    volatile unsigned int b = 0xFFFFFFFF;
    volatile signed int x = 0;
    volatile unsigned int y = 0;

    // 符号あり比較
    if (a < x) {
        a = 1;
    } else {
        a = 2;
    }

    // 符号なし比較
    if (b < x) {
        b = 1;
    } else {
        b = 2;
    }

    return a + b;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        mov     r3, #-1
        str     r3, [r7, #12]
        mov     r3, #-1
        str     r3, [r7, #8]
        movs    r3, #0
        str     r3, [r7, #4]
        movs    r3, #0
        str     r3, [r7]
        ldr     r2, [r7, #12]
        ldr     r3, [r7, #4]
        cmp     r2, r3
        bge     .L2
        movs    r3, #1
        str     r3, [r7, #12]
        b       .L3
.L2:
        movs    r3, #2
        str     r3, [r7, #12]
.L3:
        ldr     r3, [r7, #8]
        ldr     r2, [r7, #4]
        cmp     r3, r2
        bcs     .L4
        movs    r3, #1
        str     r3, [r7, #8]
        b       .L5
.L4:
        movs    r3, #2
        str     r3, [r7, #8]
.L5:
        ldr     r3, [r7, #12]
        mov     r2, r3
        ldr     r3, [r7, #8]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

大きい型へのキャスト(拡張)

このように拡張される。
ldrsh : 0xFFFF0xFFFFFFFF
ldrh : 655350x0000FFFF

short s = 0xFFFF;
unsigned short us = 0xFFFF;

int a = (int)s;           //  (符号拡張)
int b = (int)us;          // 65535 → 0x0000FFFF (ゼロ拡張)
movw    r3, #65535
strh    r3, [r7, #14]   @ movhi
movw    r3, #65535
strh    r3, [r7, #12]   @ movhi
ldrsh   r3, [r7, #14]
str     r3, [r7, #8]
ldrh    r3, [r7, #12]
str     r3, [r7, #4]
元のコード
test.c
int main() {
    short s = 0xFFFF;
    unsigned short us = 0xFFFF;

    int a = (int)s;
    int b = (int)us;

    return a + b;
}
test.s
        .arch armv7-a
        .fpu vfpv3-d16
        .eabi_attribute 28, 1
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "test.c"
        .text
        .align  1
        .global main
        .syntax unified
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        movw    r3, #65535
        strh    r3, [r7, #14]   @ movhi
        movw    r3, #65535
        strh    r3, [r7, #12]   @ movhi
        ldrsh   r3, [r7, #14]
        str     r3, [r7, #8]
        ldrh    r3, [r7, #12]
        str     r3, [r7, #4]
        ldr     r2, [r7, #8]
        ldr     r3, [r7, #4]
        add     r3, r3, r2
        mov     r0, r3
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Debian 12.2.0-14+deb12u1) 12.2.0"
        .section        .note.GNU-stack,"",%progbits

加減乗除 符号有無で処理が変わらない理由

CPUは演算中の32ビット値が符号有りか符号無しかは知らない。
符号の有無は演算に関係無く、計算結果をどちらとして解釈するかによって変わる。

補数表現

例として-2を2進数で表す。
0x00000002 = 00000000 00000000 00000000 00000010

全ビット反転
00000000 00000000 00000000 0000001011111111 11111111 11111111 11111101
0x000000020xFFFFFFFD

1を足す
0xFFFFFFFD + 1 = 0xFFFFFFFE

-2を2進数で表すと0xFFFFFFFEとなる

加算

0x00000002 + 0x00000003 = 0x00000005
0xFFFFFFFE + 0x00000003 = 0x00000001

計算式 ビット列 signed 解釈 unsigned 解釈
0x00000002 + 0x00000003 0x00000005 5 5
0xFFFFFFFE + 0x00000003 0x00000001 1 1

減算

0x00000008 - 0x00000003 = 0x00000005
0xFFFFFFFE - 0x00000003 = 0xFFFFFFFB

計算式 ビット列 signed 解釈 unsigned 解釈
0x00000008 - 0x00000003 0x00000005 5 5
0xFFFFFFFE - 0x00000003 0xFFFFFFFB -5 4294967291

乗算

0x00000004 × 0x00000003 = 0x0000000C
0xFFFFFFFE × 0x00000003 = 0xFFFFFFFA

計算式 ビット列 signed 解釈 unsigned 解釈
0x00000004 × 0x00000003 0x0000000C 12 12
0xFFFFFFFE × 0x00000003 0xFFFFFFFA -6 4294967290

符号有無の差異違い

操作 ARM命令 / 実装 符号依存
加算 adds なし
減算 subs なし
乗算 mul なし
除算 sdiv / udiv あり
比較 cmp + 条件分岐 あり
型変換 / キャスト movsx(符号拡張) / movzx(ゼロ拡張) あり

動作環境(Linux / ARM 版)

OS: Armbian 23.08.0-trunk (Debian 12 Bookworm)
Kernel: Linux 6.1.38-meson (armv7l)
CPU: ARM Cortex-A5, 4 cores, Little Endian
GCC: 12.2.0
Architecture: armv7l
備考: Windows10からSSH接続して使用

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?