Open ysbaddaden opened 6 months ago
I wonder why cross type operations are primitives, they could be handled in stdlib only.
LLVM exposes cross-size operations directly, so it's quite easily available. Maybe there's some efficiency benefit? Although I would expect the optimizer to handle this as well. But maybe primitives are more performant without optimizations?
The LLVM IR looks identical to the above crystal code (cast then add), optimized or not (using unchecked math for brevity):
@[NoInline]
def add(a : Int32, other : Int8) : Int32
a &+ other
end
define i32 @"*add<Int32, Int8>:Int32"(i32 %a, i8 %other) #3 {
entry:
%0 = sext i8 %other to i32
%1 = add i32 %a, %0
ret i32 %1
}
Maybe it's more efficient compile-time wise to always generate the LLVM code in place (inlined), than creating lots of methods to be typed, then inlined :thinking:
LLVM exposes cross-size operations directly
I don't think so: https://llvm.org/docs/LangRef.html#add-instruction
Also there are no pure Crystal equivalents for certain mixed operators, e.g. Int32
+ UInt32
:
fun __crystal_raise_overflow
while true
end
end
x = 1
y = 2_u32
z = x + y
store i32 1, ptr %x, align 4
store i32 2, ptr %y, align 4
%0 = load i32, ptr %x, align 4
%1 = load i32, ptr %y, align 4
%2 = sext i32 %0 to i33
%3 = zext i32 %1 to i33
%4 = call { i33, i1 } @llvm.sadd.with.overflow.i33(i33 %2, i33 %3)
%5 = extractvalue { i33, i1 } %4, 0
%6 = extractvalue { i33, i1 } %4, 1
%7 = trunc i33 %5 to i32
%8 = sext i32 %7 to i33
%9 = icmp ne i33 %5, %8
%10 = or i1 %6, %9
%11 = call i1 @llvm.expect.i1(i1 %10, i1 false)
br i1 %11, label %overflow, label %normal
overflow: ; preds = %entry
call void @__crystal_raise_overflow()
unreachable
normal: ; preds = %entry
%12 = trunc i33 %5 to i32
store i32 %12, ptr %z, align 4
There are simply no 33-bit integers in Crystal.
What the heck is that i33 😲
I assume it's to push the sign to bit 33 so we can properly add? I wonder about the generated machine code now.
The current
primitives.cr
in crystal always define cross integer and float sizes binary operations. I'd like to disable that in nano, and only have binary operations across same type & sizes, only keeping the checked & unchecked conversions. We could then bring them back on demand.Note: I wonder why cross type operations are primitives, they could be handled in stdlib only: