I have identified a very subtle memory management error that took me weeks to 
characterize.

I'm directly translating a working C++ program to Nim. The problem seems to be 
that the Nim code erroneously addresses the **seg** array beyonds its bounds, 
because the **k** values used to update the **nextp** array are thrown off, 
which causes incorrect values in **seg** to accumulate as the segment 
iterations increase.

In **Listing 1** I show the corresponding code where the problem occurs. In it 
I output the values shown to see the difference in values that start to occur 
between the C++ and Nim code. Here I output the values for **k** and 
**seg[ki]** before they are updated in the loop. Both versions output the same 
values for the first complete segment iteration process. Starting within the 
second iteration those values start to become different, as **nextp** is not 
getting the correct values in the Nim version.
    
    
    Listing 1.
    
    C++
        
        for (uint j=0; j < pcnt; ++j){        // for each prime r1..sqrt(N)
          if (nextp[row+j] < Kn) {            // if 1st mult resgroup is within 
'seg'
                uint k = nextp[row+j];        // starting from this resgroup in 
'seg'
                uint ki = k*bprg + byti;      // convert it to a byte address 
in seg[]
                uint prime = primes[j];       // get prime, convert it to 
number of
                uint prmstep = prime * bprg;  // bytes to nextp primenth 
resgroup byte
                for (; k < Kn; k += prime){   // for each primenth byte to end 
of 'seg'
                  
                  cout << "(" << r << "," << prime << "," << k << "," << 
(seg[ki] & 255) << "," << biti << ") ";
                  
                  seg[ki] |= biti;            // mark restrack bit in byte as 
nonprime
                  ki += prmstep;              // byte in next prime multiple 
resgroup
                }
            nextp[row+j] = k - Kn;        // save 1st resgroup in next eligible 
seg
          }
          else nextp[row+j] -= Kn;        // when 1st mult resgroup > seg 
resgroup range
        }
    
    
-------------------------------------------------------------------------------
    Nim
        
        for j, prime in primes:          # for each prime r1..sqrt(N)
          if nextp[row + j] < uint(Kn):  # if 1st mult resgroup is within 'seg'
            var k = int(nextp[row + j])  # starting from this resgroup in 'seg'
            var ki = k*bprg + byti       # convert it to a byte addres in 'seg'
            let prmstep = prime * bprg   # set number of bytes to next prime 
mult
            while k < Kn:                # for each primenth byte to end of 
'seg'
              
              write(stdout, "(",r, ",", prime, ",", k,",",seg[ki], ",", biti,") 
")
              
              seg[ki] = seg[ki] or biti  # mark restrack bit in byte as nonprime
              ki += prmstep              # byte in next prime multiple resgroup
              k  += prime                # set resgroup of next prime multiple
            nextp[row + j] = uint(k-Kn)  # save 1st resgroup in next eligible 
seg
          else: nextp[row+j] -= uint(Kn) # when 1st mult resgroup > seg 
resgroup range
    

In **Listing 2** below, instead of outputting the values before they are 
updated I do it afterwards. This should throw a **Segfault** error, and did in 
the C++ code when it tried to output **seg[ki]** at an out-of-bound address. 
However, the Nim code keeps outputting out-of-bounds values for **seg[ki]** 
until the loop finished.

After hitting the docs I saw that compiling using **\--d:release** turns off 
array bounds checking. So I compiled the **Listing 2** code as: **$ nim c -r 
file.nim** and now the Nim output also gave a segfault error, but it didn't 
occur at the same spot (**r** and **prime** values) as the C++ compiled code.

So then I compiled the **Listing 1** Nim code similarly, and it gave better 
results, but it still produced errors.

I'm convinced now that I can't rewrite the Nim code to produce the correct 
results because something is happening at the compile(r) level that I don't 
understand and can't control.

Is there some compile flag to be set, or something that can be done, to make 
the Nim code work correctly, and/or is this a bug in the compile process?

This was run with Nim 0.17.0, but I also got it to run under 0.12.0 (with a VB 
VM Linux distros) with same results.
    
    
    Listing 2
    
    C++
        
        for (uint j=0; j < pcnt; ++j){        // for each prime r1..sqrt(N)
          if (nextp[row+j] < Kn) {            // if 1st mult resgroup is within 
'seg'
                uint k = nextp[row+j];        // starting from this resgroup in 
'seg'
                uint ki = k*bprg + byti;      // convert it to a byte address 
in seg[]
                uint prime = primes[j];       // get prime, convert it to 
number of
                uint prmstep = prime * bprg;  // bytes to nextp primenth 
resgroup byte
                for (; k < Kn; k += prime){   // for each primenth byte to end 
of 'seg
                  seg[ki] |= biti;            // mark restrack bit in byte as 
nonprime
                  ki += prmstep;              // byte in next prime multiple 
resgroup
                  
                  cout << "(" << r << "," << prime << "," << k << "," << 
(seg[ki] & 255) << "," << biti << ") ";
              }
            nextp[row+j] = k - Kn;        // save 1st resgroup in next eligible 
seg
          }
          else nextp[row+j] -= Kn;        // when 1st mult resgroup > seg 
resgroup range
        }
    
    
-------------------------------------------------------------------------------
    Nim
        
        for j, prime in primes:          # for each prime r1..sqrt(N)
          if nextp[row + j] < uint(Kn):  # if 1st mult resgroup is within 'seg'
            var k = int(nextp[row + j])  # starting from this resgroup in 'seg'
            var ki = k*bprg + byti       # convert it to a byte addres in 'seg'
            let prmstep = prime * bprg   # set number of bytes to next prime 
mult
            while k < Kn:                # for each primenth byte to end of 
'seg'
              seg[ki] = seg[ki] or biti  # mark restrack bit in byte as nonprime
              ki += prmstep              # byte in next prime multiple resgroup
              k  += prime                # set resgroup of next prime multiple
              
              write(stdout, "(",r, ",", prime, ",", k,",",seg[ki], ",", biti,") 
")
            
            nextp[row + j] = uint(k-Kn)  # save 1st resgroup in next eligible 
seg
          else: nextp[row+j] -= uint(Kn) # when 1st mult resgroup > seg 
resgroup range
    

Reply via email to