Consider this example program:
actor Main
new create(env: Env) =>
let array = Array[String]
array.append(env.args)
while array.size() < 32 do
array.push("")
end
(let index, let used) = try
env.args(1)?.read_int[U32]()?
else
env.err.print("error: invalid index")
return
end
env.out.print("info: index: " + index.string())
env.out.print("info: used: " + used.string())
try
if array.size() != 32 then
env.err.print("error: array has wrong size")
return
end
env.out.print(array(magic(index).usize())?)
env.out.print("info: magic: " + magic(index).string())
env.out.print("info: no error thrown")
else
env.out.print("info: error thrown")
end
fun magic(x: U32): U32 =>
let y = x.clz_unsafe()
if y < 32 then
y
else
0
end
(This can undoubtedly be reduced further, but it will be really brittle.)
When I run it, I get this:
./unsafe 0
Segmentation fault
I believe the reason is this: LLVM treats undefined as really undefined. The bounds check y < 32 is optimized away, and on some CPUs (mine is a Core m7-6Y75), when compiling natively, clz_unsafe is implemented as lzcnt, which yields 32 for a zero input value. The subsequent code gets confused by this because according to LLVM semantics, the value 32 is impossible here. However, this is not an LLVM bug—it does what the Pony compiler instructs it to do.
My compiler:
0.24.4-fff531cf [release]
compiled with: llvm 3.9.1 -- cc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Defaults: pic=true ssl=openssl_1.1.0
The reproducer is probably brittle, so unless you have the same environment, it will not work.
The same issue applies to all unsafe arithmetic operations, with different reproducers. (clz_unsafe was literally the first one I tried.) I think it would be reasonable to simply remove all the unsafe arithmetic methods because the performance benefits will be extremely narrow.
Consider this example program:
(This can undoubtedly be reduced further, but it will be really brittle.)
When I run it, I get this:
I believe the reason is this: LLVM treats undefined as really undefined. The bounds check
y < 32is optimized away, and on some CPUs (mine is a Core m7-6Y75), when compiling natively,clz_unsafeis implemented aslzcnt, which yields 32 for a zero input value. The subsequent code gets confused by this because according to LLVM semantics, the value 32 is impossible here. However, this is not an LLVM bug—it does what the Pony compiler instructs it to do.My compiler:
The reproducer is probably brittle, so unless you have the same environment, it will not work.
The same issue applies to all unsafe arithmetic operations, with different reproducers. (
clz_unsafewas literally the first one I tried.) I think it would be reasonable to simply remove all the unsafe arithmetic methods because the performance benefits will be extremely narrow.