zig
zig copied to clipboard
Zig generates truncate for intCast on MIPS
Zig Version
0.12.0-dev.1753+a98d4a66e
Steps to Reproduce and Observed Behavior
Compile the following using zig build-obj -target mipsel-freestanding -O ReleaseFast -femit-asm
export fn int_cast(x: i32) i16 {
return @intCast(x);
}
Note that the generated assembly:
int_cast:
sll $1, $4, 16 ; $1.u32 = $4.u32 << 16
jr $ra ; (delayed)
sra $2, $1, 16 ; $2.i32 = $1.i32 >> 16
;; return @intCast($2.i32)
truncates.
Expected Behavior
A faster equivalent where $4 is in the range -32768 to 32787 would be:
int_cast:
jr $ra ; (delayed)
move $2, $4 ; $2 = $4
;; return $2
The issue seems to lie somewhere between Zig and LLVM, LLVM IR doesn't seem to have an equivalent to intCast:
; Function Attrs: mustprogress nofree noredzone nosync nounwind willreturn memory(inaccessiblemem: readwrite)
define dso_local signext i16 @int_cast(i32 %0) local_unnamed_addr #0 !dbg !133 {
Entry:
call void @llvm.dbg.value(metadata i32 %0, metadata !140, metadata !DIExpression()), !dbg !141
%1 = trunc i32 %0 to i16, !dbg !142
ret i16 %1, !dbg !144
}
LLVM is missing the information that x can never be out of the specified range, and as such, the truncation is generated, despite being unnecessary.
Even when adding an unreachable in manually, the LLVM IR does not reflect the assumption:
export fn int_cast(x: i32) i16 {
if (x < -32768 or x > 32767) unreachable;
return @intCast(x);
}
; Function Attrs: mustprogress nofree noredzone nosync nounwind willreturn memory(inaccessiblemem: readwrite)
define dso_local signext i16 @int_cast(i32 %0) local_unnamed_addr #0 !dbg !133 {
Entry:
call void @llvm.dbg.value(metadata i32 %0, metadata !140, metadata !DIExpression()), !dbg !141
%1 = trunc i32 %0 to i16, !dbg !142
ret i16 %1, !dbg !144
}
However, llvm.assume is declared:
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
declare void @llvm.assume(i1 noundef) #2
I am unsure why the unreachable is swept under the rug.