Yesterday, I am testing nim destructor thing, and find a strange bug.

Look at the code modified from tests/destrcutor/tcustomstrings.nim 
    
    
    type
       mystring = object
          len, cap: int
          data: ptr UncheckedArray[char]
    
    var
       allocCount, deallocCount: int
    
    proc `=destroy`*(s: var mystring) =
       if s.data != nil:
          dealloc(s.data)
          inc deallocCount
          s.data = nil
          s.len = 0
          s.cap = 0
    
    proc `=sink`*(a: var mystring, b: mystring) =
       if a.data != nil and a.data != b.data:
          dealloc(a.data)
          inc deallocCount
       a.len = b.len
       a.cap = b.cap
       a.data = b.data
    
    proc ensure(self: var mystring; newLen: int) =
       if newLen >= self.cap:
          self.cap = max((self.cap * 3) shr 1, newLen)
          if self.cap > 0:
             if self.data == nil: inc allocCount
             self.data = cast[type(self.data)](realloc(self.data, self.cap + 1))
    
    proc create*(lit: string): mystring =
       let newLen = lit.len
       ensure(result, newLen)
       copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1)
       result.len = newLen
    
    proc `=`*(a: var mystring; b: mystring) =
       if a.data != nil and a.data != b.data:
          echo "dealloc ", cast[int](a.data)              # the a.data is not 
nil !!!!
          dealloc(a.data)
          inc deallocCount
          a.data = nil
       a.len = b.len
       a.cap = b.cap
       if b.data != nil:
          a.data = cast[type(a.data)](alloc(a.cap + 1))
          inc allocCount
          copyMem(a.data, b.data, a.cap+1)
    
    proc `&`*(a, b: mystring): mystring =
       if result.data == nil:
          echo "nil"                                       # here we check it 
is nil
       result = a
    
    proc main() =
       for i in 0..<1:
          let b = create" to append"
          let c = b & create"more here"
    
    main()
    
    
    Run

When I compile with -d:release, it give SIGSEGV.
    
    
    nil
    dealloc 1791187200
    SIGSEGV: Illegal storage access. (Attempt to read from nil?)
    
    
    Run

The signal is generated in `dealloc(a.data)` in function `=`, but before call 
this function, the a.data is nil !!!! Very weird.

I thought it is a nim compiler bug, but found it is the vc compiler bug finally.

I start a debugger to check the call to `=`, to find that the argument is 
passed wrong way. Function `=` is defined as `__fastcall`, but the c compiler 
generated an `__stdcall` to `=`.

Here is the c code generated by nim. 
    
    
    # function `=`
    
    N_LIB_PRIVATE N_NIMCALL(void, 
eq__qx39cr0PtF4OjyizSYR9bNoA_2)(tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg* a, 
tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg b) {
            {
                    NIM_BOOL T3_;
                    tyArray_Re75IspeoxXy2oCZHwcRrA T7_;
                    T3_ = (NIM_BOOL)0;
                    T3_ = !(((*a).data == NIM_NIL));
                    if (!(T3_)) goto LA4_;
                    T3_ = !(((*a).data == b.data));
                    LA4_: ;
                    if (!T3_) goto LA5_;
                    nimZeroMem((void*)T7_, 
sizeof(tyArray_Re75IspeoxXy2oCZHwcRrA));
                    T7_[0] = copyString(((NimStringDesc*) 
&TM_59a5wK6mGRY2v9bEMYO8KsFQ_6));
                    T7_[1] = nimIntToStr(((NI) (ptrdiff_t) ((*a).data)));
                    echoBinSafe(T7_, 2);
                    dealloc_RCjNtRnHdRYntrcE7YtwWw(((void*) ((*a).data)));
                    deallocCount_dV8zMfA1D9bZ5xF49boNsMlw += ((NI) 1);
                    (*a).data = NIM_NIL;
            }
            LA5_: ;
            (*a).len = b.len;
            (*a).cap = b.cap;
            {
                    void* T12_;
                    if (!!((b.data == NIM_NIL))) goto LA10_;
                    T12_ = (void*)0;
                    T12_ = alloc_4cubgSerkjpuLcj5MXjiLw_2(((NI) ((NI)((*a).cap 
+ ((NI) 1)))));
                    (*a).data = ((NIM_CHAR*) (T12_));
                    allocCount_LvnLtTHe5aJKq8hYDBw7tg += ((NI) 1);
                    copyMem_fPlwH3r9agN9aEHB6yCPMh0wsystem(((void*) 
((*a).data)), ((void*) (b.data)), ((NI) ((NI)((*a).cap + ((NI) 1)))));
            }
            LA10_: ;
    }
    
    
    # function `&`
    N_LIB_PRIVATE N_NIMCALL(tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg, 
amp__fgQgIaHVxaWqgQAbb9a3gsw)(tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg a, 
tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg b) {
            tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg result;
            nimZeroMem((void*)(&result), 
sizeof(tyObject_mystring_ZsWUolR2hPINlsA3QxLwCg));
            {
                    if (!(result.data == NIM_NIL)) goto LA3_;
                    echoBinSafe(TM_59a5wK6mGRY2v9bEMYO8KsFQ_4, 1);
            }
            LA3_: ;
            eq__qx39cr0PtF4OjyizSYR9bNoA_2((&result), a);
            return result;
    }
    
    
    
    
    Run

The problem is: the c compiler inline the `&` function to main and generated 
wrong code:
    
    
    
    # function `=` using __fastcall, get the first argument from ecx
    
    @eq__qx39cr0PtF4OjyizSYR9bNoA_2@16 PROC NEAR
            sub     esp, 8                                  ; 0170 _ 83. EC, 08
            push    esi                                     ; 0173 _ 56
            mov     esi, ecx                                ; 0174 _ 8B. F1
            mov     eax, dword ptr [esi+8H]                 ; 0176 _ 8B. 46, 08
            test    eax, eax                                ; 0179 _ 85. C0
            setne   cl                                      ; 017B _ 0F 95. C1
            test    cl, cl                                  ; 017E _ 84. C9
            jz      ?_010                                   ; 0180 _ 74, 56
            cmp     eax, dword ptr [esp+18H]                ; 0182 _ 3B. 44 24, 
18
            setne   al                                      ; 0186 _ 0F 95. C0
            test    al, al                                  ; 0189 _ 84. C0
            jz      ?_010                                   ; 018B _ 74, 4B
            xor     eax, eax                                ; 018D _ 33. C0
            mov     ecx, offset _TM_59a5wK6mGRY2v9bEMYO8KsFQ_6; 018F _ B9, 
00000000(d)
            mov     dword ptr [esp+4H], eax                 ; 0194 _ 89. 44 24, 
04
            mov     dword ptr [esp+8H], eax                 ; 0198 _ 89. 44 24, 
08
            call    @copyString@4                           ; 019C _ E8, 
00000000(rel)
            mov     ecx, dword ptr [esi+8H]                 ; 01A1 _ 8B. 4E, 08
            mov     dword ptr [esp+4H], eax                 ; 01A4 _ 89. 44 24, 
04
            call    @nimIntToStr@4                          ; 01A8 _ E8, 
00000000(rel)
            mov     edx, 2                                  ; 01AD _ BA, 
00000002
            lea     ecx, [esp+4H]                           ; 01B2 _ 8D. 4C 24, 
04
            mov     dword ptr [esp+8H], eax                 ; 01B6 _ 89. 44 24, 
08
            call    @echoBinSafe@8                          ; 01BA _ E8, 
00000000(rel)
            mov     eax, dword ptr [esi+8H]                 ; 01BF _ 8B. 46, 08
            push    eax                                     ; 01C2 _ 50
            call    _dealloc_RCjNtRnHdRYntrcE7YtwWw         ; 01C3 _ E8, 
00000000(rel)
            add     esp, 4                                  ; 01C8 _ 83. C4, 04
            inc     dword ptr [_deallocCount_dV8zMfA1D9bZ5xF49boNsMlw]; 01CB _ 
FF. 05, 00000000(d)
            mov     dword ptr [esi+8H], 0                   ; 01D1 _ C7. 46, 
08, 00000000
    ?_010:  cmp     dword ptr [esp+18H], 0                  ; 01D8 _ 83. 7C 24, 
18, 00
            mov     ecx, dword ptr [esp+10H]                ; 01DD _ 8B. 4C 24, 
10
            mov     eax, dword ptr [esp+14H]                ; 01E1 _ 8B. 44 24, 
14
            mov     dword ptr [esi], ecx                    ; 01E5 _ 89. 0E
            mov     dword ptr [esi+4H], eax                 ; 01E7 _ 89. 46, 04
            jz      ?_011                                   ; 01EA _ 74, 26
            inc     eax                                     ; 01EC _ 40
            push    eax                                     ; 01ED _ 50
            call    _alloc_4cubgSerkjpuLcj5MXjiLw_2         ; 01EE _ E8, 
00000000(rel)
            mov     dword ptr [esi+8H], eax                 ; 01F3 _ 89. 46, 08
            inc     dword ptr [_allocCount_LvnLtTHe5aJKq8hYDBw7tg]; 01F6 _ FF. 
05, 00000000(d)
            mov     edx, dword ptr [esi+4H]                 ; 01FC _ 8B. 56, 04
            mov     eax, dword ptr [esp+1CH]                ; 01FF _ 8B. 44 24, 
1C
            mov     ecx, dword ptr [esi+8H]                 ; 0203 _ 8B. 4E, 08
            inc     edx                                     ; 0206 _ 42
            push    edx                                     ; 0207 _ 52
            push    eax                                     ; 0208 _ 50
            push    ecx                                     ; 0209 _ 51
            call    _memcpy                                 ; 020A _ E8, 
00000000(rel)
            add     esp, 16                                 ; 020F _ 83. C4, 10
    ?_011:  pop     esi                                     ; 0212 _ 5E
            add     esp, 8                                  ; 0213 _ 83. C4, 08
            ret     12                                      ; 0216 _ C2, 000C
    @eq__qx39cr0PtF4OjyizSYR9bNoA_2@16 ENDP
    
    
    # inlined code from `main`, look at the call to function `=`, it use 
`__stdcall` to push the argument
            
            xor     eax, eax                                ; 0395 _ 33. C0
            test    eax, eax                                ; 03AF _ 85. C0
            jnz     ?_016                                   ; 03B1 _ 75, 0D
            lea     edx, [eax+1H]                           ; 03B3 _ 8D. 50, 01
            mov     ecx, offset _TM_59a5wK6mGRY2v9bEMYO8KsFQ_4; 03B6 _ B9, 
00000000(d)
            call    @echoBinSafe@8                          ; 03BB _ E8, 
00000000(rel)
    ?_016:  sub     esp, 12                                 ; 03C0 _ 83. EC, 0C
            mov     eax, esp                                ; 03C3 _ 8B. C4
            mov     dword ptr [eax], esi                    ; 03C5 _ 89. 30
            mov     dword ptr [eax+4H], edi                 ; 03C7 _ 89. 78, 04
            mov     dword ptr [eax+8H], ebx                 ; 03CA _ 89. 58, 08
            lea     eax, [ebp+48H]                          ; 03CD _ 8D. 45, 48
            push    eax                                     ; 03D0 _ 50
            call    @eq__qx39cr0PtF4OjyizSYR9bNoA_2@16      ; 03D1 _ E8, 
00000000(rel)
            mov     ecx, dword ptr [ebp+68H]                ; 03D6 _ 8B. 4D, 68
            mov     esi, dword ptr [ebp+50H]                ; 03D9 _ 8B. 75, 50
            test    ecx, ecx                                ; 03DC _ 85. C9
    
    
    
    Run

This problem only exist vc compiler 2005, 2008, 2010 x86 version with `/O2` or 
`/Ox`.

We can add compiler flat `/Ob1` to disable greed inline to avoid the problem. 

Reply via email to