#2
by みけCAT » 6年前
確かに符号付き整数のオーバーフローは未定義動作ですが…具体的にどうしてこうなるのかはgccのコードを読まないとわからないかもしれません。
以下、参考
Compiler Explorer
x86-64 gcc 6.3 -O2
► スポイラーを表示
コード:
std::ctype<char>::do_widen(char) const:
mov eax, esi
ret
.LC0:
.string ", "
main:
push r13
push r12
xor r13d, r13d
push rbp
push rbx
xor r12d, r12d
sub rsp, 8
jmp .L6
.L12:
movsx esi, BYTE PTR [rbx+67]
.L5:
mov rdi, rbp
call std::basic_ostream<char, std::char_traits<char> >::put(char)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::flush()
.L6:
add r13d, 1
mov edi, OFFSET FLAT:std::cout
add r12d, 1000000000
mov esi, r13d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov edx, 2
mov rbx, rax
mov esi, OFFSET FLAT:.LC0
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov rdi, rbx
mov esi, r12d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov rbp, rax
mov rax, QWORD PTR [rax]
mov rax, QWORD PTR [rax-24]
mov rbx, QWORD PTR [rbp+240+rax]
test rbx, rbx
je .L11
cmp BYTE PTR [rbx+56], 0
jne .L12
mov rdi, rbx
call std::ctype<char>::_M_widen_init() const
mov rax, QWORD PTR [rbx]
mov esi, 10
mov rax, QWORD PTR [rax+48]
cmp rax, OFFSET FLAT:std::ctype<char>::do_widen(char) const
je .L5
mov rdi, rbx
call rax
movsx esi, al
jmp .L5
.L11:
call std::__throw_bad_cast()
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:std::__ioinit
call std::ios_base::Init::Init()
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:std::__ioinit
mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
add rsp, 8
jmp __cxa_atexit
確かに無限ループになっているようである。
x86-64 gcc 6.3
► スポイラーを表示
コード:
.LC0:
.string ", "
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 0
mov DWORD PTR [rbp-8], 0
.L3:
cmp DWORD PTR [rbp-8], 49
jg .L2
add DWORD PTR [rbp-4], 1000000000
mov eax, DWORD PTR [rbp-8]
add eax, 1
mov esi, eax
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov esi, OFFSET FLAT:.LC0
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov rdx, rax
mov eax, DWORD PTR [rbp-4]
mov esi, eax
mov rdi, rdx
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov esi, OFFSET FLAT:std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
add DWORD PTR [rbp-8], 1
jmp .L3
.L2:
mov eax, 0
leave
ret
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L7
cmp DWORD PTR [rbp-8], 65535
jne .L7
mov edi, OFFSET FLAT:std::__ioinit
call std::ios_base::Init::Init()
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:std::__ioinit
mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
call __cxa_atexit
.L7:
nop
leave
ret
_GLOBAL__sub_I_main:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
最適化をしない場合は、きちんとループ継続の判定をしている。
x86-64 clang 4.0.0 -O2
► スポイラーを表示
コード:
main: # @main
push rbp
push r15
push r14
push rbx
push rax
xor r15d, r15d
mov r14d, 1000000000
.LBB0_1: # =>This Inner Loop Header: Depth=1
inc r15d
mov edi, std::cout
mov esi, r15d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov rbx, rax
mov esi, .L.str
mov edx, 2
mov rdi, rbx
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov rdi, rbx
mov esi, r14d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov rbp, rax
mov rax, qword ptr [rbp]
mov rax, qword ptr [rax - 24]
mov rbx, qword ptr [rbp + rax + 240]
test rbx, rbx
je .LBB0_7
cmp byte ptr [rbx + 56], 0
je .LBB0_4
movzx eax, byte ptr [rbx + 67]
jmp .LBB0_5
.LBB0_4: # in Loop: Header=BB0_1 Depth=1
mov rdi, rbx
call std::ctype<char>::_M_widen_init() const
mov rax, qword ptr [rbx]
mov esi, 10
mov rdi, rbx
call qword ptr [rax + 48]
.LBB0_5: # in Loop: Header=BB0_1 Depth=1
movsx esi, al
mov rdi, rbp
call std::basic_ostream<char, std::char_traits<char> >::put(char)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::flush()
add r14d, 1000000000
cmp r15d, 50
jl .LBB0_1
xor eax, eax
add rsp, 8
pop rbx
pop r14
pop r15
pop rbp
ret
.LBB0_7:
call std::__throw_bad_cast()
_GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp
push rax
mov edi, std::__ioinit
call std::ios_base::Init::Init()
mov edi, std::ios_base::Init::~Init()
mov esi, std::__ioinit
mov edx, __dso_handle
pop rax
jmp __cxa_atexit # TAILCALL
.L.str:
.asciz ", "
このclangでは、最適化を有効にしてもループの終了が判定されている。
Wandboxのgcc HEAD 8.0.0 20170711 (experimental)でも最適化を有効にすると再現しました。
https://wandbox.org/permlink/GsZUhKauJJi791YR
確かに符号付き整数のオーバーフローは未定義動作ですが…具体的にどうしてこうなるのかはgccのコードを読まないとわからないかもしれません。
以下、参考
[url=https://gcc.godbolt.org/]Compiler Explorer[/url]
x86-64 gcc 6.3 -O2
[spoil][code=asm]std::ctype<char>::do_widen(char) const:
mov eax, esi
ret
.LC0:
.string ", "
main:
push r13
push r12
xor r13d, r13d
push rbp
push rbx
xor r12d, r12d
sub rsp, 8
jmp .L6
.L12:
movsx esi, BYTE PTR [rbx+67]
.L5:
mov rdi, rbp
call std::basic_ostream<char, std::char_traits<char> >::put(char)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::flush()
.L6:
add r13d, 1
mov edi, OFFSET FLAT:std::cout
add r12d, 1000000000
mov esi, r13d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov edx, 2
mov rbx, rax
mov esi, OFFSET FLAT:.LC0
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov rdi, rbx
mov esi, r12d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov rbp, rax
mov rax, QWORD PTR [rax]
mov rax, QWORD PTR [rax-24]
mov rbx, QWORD PTR [rbp+240+rax]
test rbx, rbx
je .L11
cmp BYTE PTR [rbx+56], 0
jne .L12
mov rdi, rbx
call std::ctype<char>::_M_widen_init() const
mov rax, QWORD PTR [rbx]
mov esi, 10
mov rax, QWORD PTR [rax+48]
cmp rax, OFFSET FLAT:std::ctype<char>::do_widen(char) const
je .L5
mov rdi, rbx
call rax
movsx esi, al
jmp .L5
.L11:
call std::__throw_bad_cast()
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:std::__ioinit
call std::ios_base::Init::Init()
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:std::__ioinit
mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
add rsp, 8
jmp __cxa_atexit[/code][/spoil]確かに無限ループになっているようである。
x86-64 gcc 6.3
[spoil][code=asm].LC0:
.string ", "
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 0
mov DWORD PTR [rbp-8], 0
.L3:
cmp DWORD PTR [rbp-8], 49
jg .L2
add DWORD PTR [rbp-4], 1000000000
mov eax, DWORD PTR [rbp-8]
add eax, 1
mov esi, eax
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov esi, OFFSET FLAT:.LC0
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov rdx, rax
mov eax, DWORD PTR [rbp-4]
mov esi, eax
mov rdi, rdx
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov esi, OFFSET FLAT:std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
add DWORD PTR [rbp-8], 1
jmp .L3
.L2:
mov eax, 0
leave
ret
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L7
cmp DWORD PTR [rbp-8], 65535
jne .L7
mov edi, OFFSET FLAT:std::__ioinit
call std::ios_base::Init::Init()
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:std::__ioinit
mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
call __cxa_atexit
.L7:
nop
leave
ret
_GLOBAL__sub_I_main:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret[/code][/spoil]最適化をしない場合は、きちんとループ継続の判定をしている。
x86-64 clang 4.0.0 -O2
[spoil][code=asm]main: # @main
push rbp
push r15
push r14
push rbx
push rax
xor r15d, r15d
mov r14d, 1000000000
.LBB0_1: # =>This Inner Loop Header: Depth=1
inc r15d
mov edi, std::cout
mov esi, r15d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov rbx, rax
mov esi, .L.str
mov edx, 2
mov rdi, rbx
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov rdi, rbx
mov esi, r14d
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov rbp, rax
mov rax, qword ptr [rbp]
mov rax, qword ptr [rax - 24]
mov rbx, qword ptr [rbp + rax + 240]
test rbx, rbx
je .LBB0_7
cmp byte ptr [rbx + 56], 0
je .LBB0_4
movzx eax, byte ptr [rbx + 67]
jmp .LBB0_5
.LBB0_4: # in Loop: Header=BB0_1 Depth=1
mov rdi, rbx
call std::ctype<char>::_M_widen_init() const
mov rax, qword ptr [rbx]
mov esi, 10
mov rdi, rbx
call qword ptr [rax + 48]
.LBB0_5: # in Loop: Header=BB0_1 Depth=1
movsx esi, al
mov rdi, rbp
call std::basic_ostream<char, std::char_traits<char> >::put(char)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::flush()
add r14d, 1000000000
cmp r15d, 50
jl .LBB0_1
xor eax, eax
add rsp, 8
pop rbx
pop r14
pop r15
pop rbp
ret
.LBB0_7:
call std::__throw_bad_cast()
_GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp
push rax
mov edi, std::__ioinit
call std::ios_base::Init::Init()
mov edi, std::ios_base::Init::~Init()
mov esi, std::__ioinit
mov edx, __dso_handle
pop rax
jmp __cxa_atexit # TAILCALL
.L.str:
.asciz ", "[/code][/spoil]このclangでは、最適化を有効にしてもループの終了が判定されている。
Wandboxのgcc HEAD 8.0.0 20170711 (experimental)でも最適化を有効にすると再現しました。
[url]https://wandbox.org/permlink/GsZUhKauJJi791YR[/url]