Open straight-shoota opened 1 month ago
There is a second issue here. #unsafe_as
can trigger dynamic dispatch: (contrast with #12846)
x = 123 || nil
alias T = UInt8[4]
x.unsafe_as(T) # => StaticArray[123, 0, 0, 0]
pointerof(x).as(T*).value # => StaticArray[183, 0, 0, 0]
x.crystal_type_id # => 183
It seems we actually want the second behavior, which can be done by simply rewriting Object#unsafe_as
as a class method:
class Object
def self.unsafe_cast(obj) : self
pointerof(obj).as(self*).value
end
end
T.unsafe_cast(x) # => StaticArray[183, 0, 0, 0]
Still, this formally involves copying obj
, which is undesirable for large structs. So maybe pseudo-method is the way to go?
On the other hand, if we are keeping the dynamic dispatch then I don't know what benefits a pseudo-method or primitive would bring.
Yet another issue is that T
may have a larger alignment than the object. Since Pointer(T)#value
assumes the pointer to have T
's alignment, code like below can in general lead to bad codegen:
# `x` has byte alignment
x = uninitialized UInt8[16]
# `y` assumes `pointerof(x).address.divisible_by?(alignof(UInt128))`
y = pointerof(x).as(UInt128*).value
%x = alloca [16 x i8], align 1
%y = alloca i128, align 16
; ...
%0 = load i128, ptr %x, align 16
store i128 %0, ptr %y, align 16
If #unsafe_as
codegens to a fresh call stack, then everything will happen to work because stack frames are 16-byte-aligned for most targets, but this is not guaranteed for release builds. Instead one must write %0 = load i128, ptr %x, align 1
, and at the moment there is no language capability that could achieve this.
@HertzDevil but then we'd read the i128 unaligned, right? would it work on ARM?
Could the compiler detect this and directly align x
as if it was an UInt128? maybe through a C union? Hum, I'm probably being very naive, here :sweat_smile:
If LLVM cannot infer any stricter alignment it will generate individual byte reads for the load i128
.
Object#unsafe_as
is implemented as a regular method, which uses a pointer to impose a type cast. Related methods#as
and#as?
on the other hand are pseudo-methods implemented in the compiler.I believe
#unsafe_as
should also be a compiler primitive. This would bring it closer to its siblings,#as
and#as?
.