From d1a624fd27c409cdc5bfa6e1a933c5c2b56bb0d4 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 15 Apr 2026 18:26:59 +0200 Subject: [PATCH] Improve integer exponentation fallback. --- lib/intrinsics/Project.toml | 2 +- lib/intrinsics/src/math.jl | 10 ++++++---- test/intrinsics.jl | 11 +++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/intrinsics/Project.toml b/lib/intrinsics/Project.toml index 48f65a77..4046a0d1 100644 --- a/lib/intrinsics/Project.toml +++ b/lib/intrinsics/Project.toml @@ -1,7 +1,7 @@ name = "SPIRVIntrinsics" uuid = "71d1d633-e7e8-4a92-83a1-de8814b09ba8" authors = ["Tim Besard "] -version = "0.5.8" +version = "0.5.9" [deps] ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04" diff --git a/lib/intrinsics/src/math.jl b/lib/intrinsics/src/math.jl index 18993055..1f9aa578 100644 --- a/lib/intrinsics/src/math.jl +++ b/lib/intrinsics/src/math.jl @@ -189,14 +189,16 @@ end # through Float64 (broken on backends without FP64) and in all cases leaves a # runtime `pown` in the generated code. Mark the override `:foldable` so # literal expressions like `Float32(2)^(-32)` const-fold to a compile-time -# constant, and recurse into the existing `::Int32` overrides for the tail. +# constant, and fall back to the float-exponent `pow` for the tail (matches +# Base's own fallback for out-of-range integer exponents since 1.12; see +# `base/special/pow.jl`). @device_override @assume_effects :foldable @inline function Base.:(^)(x::Float16, y::Int64) y == -1 && return inv(x) y == 0 && return one(x) y == 1 && return x y == 2 && return x * x y == 3 && return x * x * x - x ^ (y % Int32) + x ^ Float16(y) end @device_override @assume_effects :foldable @inline function Base.:(^)(x::Float32, y::Int64) y == -1 && return inv(x) @@ -204,7 +206,7 @@ end y == 1 && return x y == 2 && return x * x y == 3 && return x * x * x - x ^ (y % Int32) + x ^ Float32(y) end @device_override @assume_effects :foldable @inline function Base.:(^)(x::Float64, y::Int64) y == -1 && return inv(x) @@ -212,7 +214,7 @@ end y == 1 && return x y == 2 && return x * x y == 3 && return x * x * x - x ^ (y % Int32) + x ^ Float64(y) end # remquo(x::Float32{n}, y::Float32{n}, Int32{n} *quo) = @builtin_ccall("remquo", Float32{n}, (Float32{n}, Float32{n}, Int32{n} *), x, y, quo) diff --git a/test/intrinsics.jl b/test/intrinsics.jl index 57b0d447..f5d9eff7 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -126,6 +126,17 @@ end end end +# Regression test for #428: `^(::Float, ::Int64)` must not truncate the +# exponent through Int32, since `y % Int32` wraps for |y| ≥ 2^31 and would +# flip the sign of the exponent (producing 0 instead of Inf, etc.). +@testset "^(::$T, ::Int64) with out-of-Int32 exponent" for T in float_types + y = Int64(typemax(Int32)) + Int64(1) # smallest Int64 not representable as Int32 + for x in (T(1.5), T(0.5)) + @test call_on_device(^, x, y) === x ^ y + @test call_on_device(^, x, -y) === x ^ -y + end +end + @testset "OpenCL-specific unary - $T" for T in float_types @testset "$f" for f in [ OpenCL.acospi,