jckarter / clay

The Clay programming language
http://claylabs.com/clay
Other
404 stars 34 forks source link

LLVM bug causes 64-bit multiply with overflow to fail erroneously on x86.32 #377

Closed jckarter closed 12 years ago

jckarter commented 12 years ago
main() {
    9ul * 8;
}
stepancheg commented 12 years ago

Also fails on OSX when compiled with -arch i386.

stepancheg commented 12 years ago

Problem is not in digest module. Code that also crashes when compiled with -arch i386:

main() {
    9ul * 8;
}
jckarter commented 12 years ago

Thanks @stepancheg . I updated the bug title.

jckarter commented 12 years ago

Looks like a bug in overflow checking.


% ./build/compiler/src/clay -Ilib-clay -arch i386 -O0 foo.clay
% ./foo                                                       
error: invalid integer math: integerMultiplyWithOverflow(9, 8)
0   foo                                 0x000680f4 core.errors.backtrace.showBacktrace() clay + 532
1   foo                                 0x00068a56 core.errors.errorWithPrintfNoThrow(Static[#invalidc32integerc32mathc58c32integerMultiplyWithOverflowc40c37lluc44c32c37lluc41], UInt64, UInt64) clay + 182
2   foo                                 0x000688cf __main__.main() clay + 191
3   foo                                 0x00068576 __operators__.callMain(Static[main], Int32, Pointer[Pointer[Int8]]) Int32 clay + 54
4   foo                                 0x00068512 main + 50
5   libdyld.dylib                       0x988a2725 start + 0
zsh: abort      ./foo
stepancheg commented 12 years ago

Looks like LLVM bug. Code:

import printer.*;

foo() --> returned: Int32 __llvm__ {
    %result = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 9, i64 8)
    %ov = extractvalue { i64, i1 } %result, 1
    %ove = zext i1 %ov to i32
    store i32 %ove, i32* %returned
    ret i8* null
}

main() {
    println(foo());
}

prints 0 when compiled for 64-bit and 1 when compiled with -arch i386.

jckarter commented 12 years ago

Yeah, that's what I'm thinking. Here's the i386 assembly:


    .align  4, 0x90
"_core.numbers.overflow.overflowIntrinsic(Static[#umul], UInt64, UInt64) UInt64, Bool clay1": ## @"core.numbers.overflow.overflowIntrinsic(Static[#umul], UInt64, UInt64) UInt64, Bool clay1"
    .cfi_startproc
## BB#0:
    pushl   %ebp
Ltmp74:
    .cfi_def_cfa_offset 8
Ltmp75:
    .cfi_offset %ebp, -8
    movl    %esp, %ebp
Ltmp76:
    .cfi_def_cfa_register %ebp
    pushl   %ebx
    pushl   %edi
    pushl   %esi
    subl    $28, %esp
Ltmp77:
    .cfi_offset %esi, -20
Ltmp78:
    .cfi_offset %edi, -16
Ltmp79:
    .cfi_offset %ebx, -12
    movl    12(%ebp), %eax
    movl    (%eax), %esi
    movl    4(%eax), %eax
    movl    %eax, -16(%ebp)         ## 4-byte Spill
    movl    16(%ebp), %eax
    movl    (%eax), %ecx
    movl    4(%eax), %edi
    movl    %esi, %eax
    mull    %ecx
    movl    %eax, (%esp)
    movl    20(%ebp), %ebx
    movl    %eax, (%ebx)
    xorl    %eax, %eax
    movl    %ecx, %ebx
    orl %edi, %ebx
    movl    %edi, %ebx
    cmovnel %eax, %ebx
    movl    %ebx, 12(%esp)
    movl    $1, %eax
    cmovel  %ecx, %eax
    movl    %eax, 8(%esp)
    imull   %esi, %edi
    addl    %edx, %edi
    movl    -16(%ebp), %ebx         ## 4-byte Reload
    imull   %ebx, %ecx
    addl    %edi, %ecx
    movl    %ecx, 4(%esp)
    movl    20(%ebp), %eax
    movl    %ecx, 4(%eax)
    calll   ___udivdi3
    xorl    %ebx, %edx
    xorl    %esi, %eax
    movl    24(%ebp), %ecx
    orl %edx, %eax
    setne   (%ecx)
    xorl    %eax, %eax
    addl    $28, %esp
    popl    %esi
    popl    %edi
    popl    %ebx
    popl    %ebp
    ret
    .cfi_endproc

The logic at the end where it divides then tests the results to get the overflow condition looks fishy.

jckarter commented 12 years ago

Filed LLVM PR13839: http://llvm.org/bugs/show_bug.cgi?id=13839

jckarter commented 12 years ago

There's a workaround in place, and the bug is fixed upstream for whenever 3.2/4.0 comes out, so I'll close this.