Demircan Celebi on X: "https://t.co/b5FNCbkdfw" / X<br>Post
Log inSign up
Post
Demircan Celebi
@demircancelebi
My MacBook can't divide by 20: an Apple Metal compiler bug<br>0 / 20 = 25<br>That's what a Metal shader on my MacBook returned. Some tinkering showed I had stumbled upon a bug in Apple's Metal compiler.<br>For uint inputs, Metal's built-in mulhi(a, b) returns the high 32 bits of the 64-bit product. Two 32-bit numbers can multiply into a 64-bit result, and whatever doesn't fit in the low 32 bits spills into the high 32.<br>Example: 25,769,804 × 500 = 12,884,902,000 = 3 × 2³² + 112, so the high half holds 3, the low half 112, and mulhi returns the 3. In the cases I tested, dividing mulhi's result by a non-power-of-two constant produced incorrect results: minimal_repro.swift.<br>Feed x = 1 to mulhi(x, 500u) / 20u: the product is 500, nowhere near 2³², so mulhi(1, 500) is 0, and zero divided by 20 is zero.<br>The compiled kernel wrote 25.<br>25 is not random though: it is 500 / 20. The compiled code had divided the low 32 bits of the product (500) instead of the high 32 bits (0). Every input in the linked reproducer matched that pattern. In separate tests, every non-power-of-two divisor I tried (3, 5, 10, 100, and 499) misbehaved the same way, while the power-of-two divisors I tried (2, 4, 8, 16, and 256) were all correct.<br>Across the cases I tested, I reproduced the bug only when all three conditions held:<br>At least one mulhi input was runtime-dependent. When both inputs were compile-time constants, the compiler constant-folded the expression and the faulty lowering was never emitted.<br>The divisor was a compile-time constant. Runtime divisors followed a different code-generation path and worked correctly in my tests.<br>The divisor was not a power of two. Power-of-two division lowered to a shift, bypassing the multiply-by-a-magic-constant sequence associated with the failure.<br>My Mac was on macOS 26.5.1 when I first found the bug. I also reproduced it on 15.3.2, released more than a year earlier, and confirmed that 26.6.1 no longer has it. I disassembled both binaries with applegpu. Here is mulhi(x, 500u) / 20u on each:<br>Three instructions differ:<br>The broken first imadd writes only the low half of x × 500; the fixed writes both.<br>The broken binary parks the constant in r1; the fixed parks it in r0.<br>The broken division sequence reads r0, the low half, where the fixed reads r1, the high.<br>The broken binary uses r0, containing the low half, while the fixed binary uses r1, containing the high half. The surrounding instruction and register-allocation differences follow from that choice. In the broken sequence, the high half of x × 500 is never materialized at all.<br>I filed FB24193979 with Apple on August 6, 2026. Apple released macOS 26.6.1 that same day, and the reproducer already produced correct results there.<br>macOS 26.5.1 was broken. I did not test 26.5.2 or 26.6, so I can only place the fix somewhere after 26.5.1 and no later than 26.6.1. I found no public release-note mention of this compiler miscompilation in Apple's release notes. Whatever changed was already in the 26.6.1 build before my report could have influenced it.
span:not(:empty)~span:not(:empty)]:before:content-['·'] [&>span:not(:empty)~span:not(:empty)]:before:px-1 [&>span:not(:empty)~span:not(:empty)]:before:shrink-0">10:12 AM · Aug 13, 20263Views
Log in or sign up for X<br>See what’s happening and join the conversation<br>Continue with phoneContinue with AppleContinue with Google<br>or<br>Log in with username or email
Relevant people
Demircan Celebi@demircancelebiFollow<br>https://t.co/bOiT91eSSW
Trending now