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