# Revisiting saturating intrinsics

**URL:** <https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917>\
**Category:** Internals & Design\
**Tags:** llvm, arithmetic\
**Created:** [April 13, 2024, 11:50pm UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917 "2024-04-13T23:50:53Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [April 13, 2024, 11:50pm UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/1 "2024-04-13T23:50:53Z")

</div>

Hi all.

Here is the origin of the discussion I want to have.

> [@The performance of saturating operations or adding intrinsics](https://discourse.julialang.org/t/the-performance-of-saturating-operations-or-adding-intrinsics/48575):
>
> I (we) will support saturating arithmetic in FixedPointNumbers.jl as an experimental feature until the API design is mature. (If you’re interested in the background and API design, you can also see: ) Since the addition and subtraction of fixed-point numbers is essentially the “same” as the addition and subtraction of integers, we will discuss just integer arithmetic below. Saturating addition and subtraction can be implemented simply as follows: using Base.Checked function saturating\_add1…

I had to leave the development a few years ago due to somewhat misfortunes. I apologize for the inconvenience.

Unfortunately, no progress has been made in `FixedPointNumbers` or `CheckedArithmetic` over the last few years regarding arithmetic.  
Rather, the `LoopVectorization` (`VectorizationBase`) glow of hope is about to fade.

> [@Why is LoopVectorization deprecated?](https://discourse.julialang.org/t/why-is-loopvectorization-deprecated/109547):
>
> In the README file of [JuliaSIMD/LoopVectorization.jl: Macro(s) for vectorizing loops. (github.com)](https://github.com/JuliaSIMD/LoopVectorization.jl) it is clearly stated that the package is deprecated for Julia 1.11 and newer versions, without any explanation. Anyone knows what’s going on here?

Given this situation, I beleave that functions like `saturating_add` (or `saturated_add`) are better defined in julia’s `Base`.  
The reason they should be under `Base` is that they should correspond closely to LLVM’s saturating intrinsics.

> [@The performance of saturating operations or adding intrinsics](https://discourse.julialang.org/t/the-performance-of-saturating-operations-or-adding-intrinsics/48575/5):
>
> Isn’t that already possible using llvmcall?

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [April 14, 2024, 12:17am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/2 "2024-04-14T00:17:55Z")

</div>

```julia
function saturating_add(x::T, y::T) where {T <: Integer}
    clamp(widen(x) + widen(y), T)
end

```

seems to optimize to the correct LLVM saturating intrinsics.

```julia
julia> @code_llvm debuginfo=:none saturating_add(1,2)
define i64 @julia_saturating_add_486(i64 signext %0, i64 signext %1) #0 {
top:
  %2 = call i64 @llvm.sadd.sat.i64(i64 %0, i64 %1)
  ret i64 %2
}

```

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [April 14, 2024, 12:29am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/3 "2024-04-14T00:29:59Z")

</div>

That’s right.  
The essence of this issue is not the implementation of `saturating_*` but where the definition should be.

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [April 14, 2024, 12:40am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/4 "2024-04-14T00:40:46Z")

</div>

More to the point, underlying my idea is the hope that if julia or the package maintainers are missing, LLVM will do it for good. 😆

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [April 14, 2024, 1:27am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/5 "2024-04-14T01:27:42Z")

</div>

Yeah, I don’t know if it is easier in the long run to add new intrinsics to Base or somehow ensure the `clamp` `widen` optimization gets applied correctly in future LLVM/Julia versions.

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [April 14, 2024, 1:53am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/6 "2024-04-14T01:53:05Z")

</div>

No matter how smart julia or LLVM gets, there should always be definitions of what to call them.  
For example, the function (not functionality) of saturating arithmetic for types in `Dates` should be the same as the function of saturating arithmetic for `Integer`s.  
(I doubt that saturating arithmetic should be implemented in `Dates`, though.)

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [April 14, 2024, 2:07am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/7 "2024-04-14T02:07:52Z")

</div>

Imho `saturating_add` would be a good fit for Base.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [April 14, 2024, 3:47am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/8 "2024-04-14T03:47:51Z")

</div>

Saturating versions of the basic arithmetic functions for floating-point types becomes increasingly important to machine learning.

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [April 14, 2024, 3:54am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/9 "2024-04-14T03:54:55Z")

</div>

Interestingly `clamp(widen(x) + widen(y), T)` fails to optimize broadcasting `UInt` vectors.

```julia
function bar(x::T, y::T) where T
    r, f = add_with_overflow(x, y)
    f ? (signbit(y) ? typemin(T) : typemax(T)) : r
end

```

is 2x faster when broadcasting `UInt` vectors in Julia 1.10.2.

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [April 25, 2024, 5:15am UTC](https://discourse.julialang.org/t/revisiting-saturating-intrinsics/112917/10 "2024-04-25T05:15:37Z")

</div>

Even if julia supports saturating arithmetic in the future, an external package is needed to supplement its functionality to support versions prior to v1.11.

VectorizationBase.jl was one of the candidates, but as noted above, we now consider it inappropriate.  
I had CheckedArithmeticCore.jl as a candidate for that, but now there is another candidate [OverflowContexts.jl](https://github.com/BioTurboNick/OverflowContexts.jl)

> <https://github.com/JuliaMath/CheckedArithmetic.jl/issues/12>
>
> Thought I would formally link to my variant in this area, \[OverflowContexts.jl\](…https://github.com/BioTurboNick/OverflowContexts.jl), in part to explore if they could/should be merged.
