There seems to be a miscompilation bug in parallel assignments, where the assignments are executed in sequence, with the effects of one assignment being visible in the RHS.
Known cases:
Using the geometric algebra from stdlib and a dynamic array type
setup:
local g = ga(float, dimension)
local struct particle {
pos: g.vector_t
vel: g.vector_t
mass: g.scalar_t
}
local struct spring {
conns: tuple(uint, uint)
strength: g.scalar_t -- [0, 1]
length: g.scalar_t -- [0, oo)
}
local struct system {
gravity: g.scalar_t -- [0, oo)
drag: g.scalar_t -- [0, 1]
particles: dynarray(particle)
springs: dynarray(spring)
}
Reference implementation with no parallel assignment:
terra system:update()
-- position based dynamics
-- initial velocity, gravity, and drag application.
for i = 0, self.particles.size do
var part = &self.particles(i)
var vel = part.vel
part.vel = part.pos
part.pos = part.pos + vel * (1 - self.drag) --reuse velocity to store old position; will be recomputed at end
part.pos = part.pos - self.gravity * part.pos:normalize()
end
-- rest of the physics omitted, the presence/absence doesn't affect the bug
-- compute final velocity
for i = 0, self.particles.size do
var part = &self.particles(i)
part.vel = part.pos - part.vel --velocity field stored old position during updates; recover velocity from position change
end
end
This variant using parallel assignment should behave identically to the reference implementation, but triggers a miscompilation bug and so instead completes the first part of the assignment updating the position before reading the position to update the velocity, resulting in all velocities carried between ticks being zeroed every tick
terra system:update()
-- position based dynamics
-- initial velocity, gravity, and drag application.
for i = 0, self.particles.size do
var part = &self.particles(i)
part.pos, part.vel = part.pos + part.vel * (1 - self.drag), part.pos --reuse velocity to store old position; will be recomputed at end
part.pos = part.pos - self.gravity * part.pos:normalize()
end
-- rest of the physics omitted, the presence/absence doesn't affect the bug
-- compute final velocity
for i = 0, self.particles.size do
var part = &self.particles(i)
part.vel = part.pos - part.vel --velocity field stored old position during updates; recover velocity from position change
end
end
This variant using parallel assignment should behave identically to both the reference implementation and to how the other variant should behave. If it triggered the bug, the expected behavior would be to incorrectly use the old position as the velocity, resulting in everything flying away from the origin at high speed. This variant does not appear to trigger the bug, and so works identically to the reference implementation
terra system:update()
-- position based dynamics
-- initial velocity, gravity, and drag application.
for i = 0, self.particles.size do
var part = &self.particles(i)
part.vel, part.pos = part.pos, part.pos + part.vel * (1 - self.drag) --reuse velocity to store old position; will be recomputed at end
part.pos = part.pos - self.gravity * part.pos:normalize()
end
-- rest of the physics omitted, the presence/absence doesn't affect the bug
-- compute final velocity
for i = 0, self.particles.size do
var part = &self.particles(i)
part.vel = part.pos - part.vel --velocity field stored old position during updates; recover velocity from position change
end
end
Some additional cases which don't trigger the bug:
local struct foo{
a: int
b: int
}
local function printfoo(f)
print(f.a, f.b)
end
local terra test1(a: int, b: int)
a, b = b, a
return [foo]{a, b}
end
printfoo(test1(1, 2))
local terra test2(a: int, b: int)
var f = [foo]{a, b}
f.a, f.b = f.b, f.a
return f
end
printfoo(test2(1, 2))
local terra test3(a: int, b: int)
var f = [foo]{a, b}
var fp = &f
fp.a, fp.b = fp.a + fp.b, fp.a
fp.a = fp.a + 1
fp.b = fp.a - fp.b
return f
end
printfoo(test3(1, 2))
local terra test4(a: int, b: int, c: int)
var f = [foo]{a, b}
var fp = &f
fp.a, fp.b = fp.a + fp.b * (1 - c), fp.a
fp.a = fp.a + 1
fp.b = fp.a - fp.b
return f
end
printfoo(test4(1, 2, 0))
There seems to be a miscompilation bug in parallel assignments, where the assignments are executed in sequence, with the effects of one assignment being visible in the RHS.
Known cases:
Using the geometric algebra from stdlib and a dynamic array type setup:
Reference implementation with no parallel assignment:
This variant using parallel assignment should behave identically to the reference implementation, but triggers a miscompilation bug and so instead completes the first part of the assignment updating the position before reading the position to update the velocity, resulting in all velocities carried between ticks being zeroed every tick
This variant using parallel assignment should behave identically to both the reference implementation and to how the other variant should behave. If it triggered the bug, the expected behavior would be to incorrectly use the old position as the velocity, resulting in everything flying away from the origin at high speed. This variant does not appear to trigger the bug, and so works identically to the reference implementation
Some additional cases which don't trigger the bug: