Skip to content

Implement all core_float_math functions using core::intrinsics - #668

Open
mikwielgus wants to merge 1 commit into
Rust-GPU:mainfrom
mikwielgus:core-float-math
Open

mikwielgus wants to merge 1 commit into
Rust-GPU:mainfrom
mikwielgus:core-float-math

Conversation

@mikwielgus

Copy link
Copy Markdown
Contributor

This implements all the methods listed in

rust-lang/rust#137578

for f32 and f64 in a no-std environment using functions from core::intrinsics and with the help of the #[rustc_allow_incoherent_impl] internal rustc attribute.

For test coverage, I have updated the math_ops difftest to use the new functions (for that I used Cursor Auto).

powi test output again marginally changed, so I blessed it again.

These are not all missing functions yet. There is also log, log10, exp, and a few more, which are not part of core_float_math. These will probably follow afterwards.

Follow-up from c087f99.

This implements all the methods listed in

rust-lang/rust#137578

for `f32` and `f64` in a no-`std` environment using
functions from `core::intrinsics` and with the help of the
`#[rustc_allow_incoherent_impl]` internal rustc attribute.

For test coverage, I have updated the math_ops difftest to use the new
functions (for that I used Cursor Auto).

`powi` test output again marginally changed, so I blessed it again.

These are not all missing functions yet. There is also `log`, `log10`,
`exp`, and a few more, which are not part of `core_float_math`. These
will probably follow afterwards.

Follow-up from c087f99.
@Firestar99

Firestar99 commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Any reason to limit this to core_float_math? I'd be willing to expand this to all f32 ops with #[rustc_allow_incoherent_impl] and remove our hacky implementation overwriting libm functions?
(Also our difftests are a mess that originated as AI code so no worries)

@mikwielgus

Copy link
Copy Markdown
Contributor Author

Any reason to limit this to core_float_math?

No reason, I'm only splitting these changes into multiple PRs for easier review.

My goal is to replace all of these functions, so that I can remove num_traits from all tests and mark it as deprecated. I intend to finish this in the next PR.

@Firestar99

Copy link
Copy Markdown
Member

Feel free to just expand this PR into adding all of them, makes it easier to review since I can just scan core's impl f32 for missing impls.

@mikwielgus

mikwielgus commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor Author

I've re-read your message.

remove our hacky implementation overwriting libm functions?

Just to be sure: in this PR, you don't want me to just implement the remaining mathematical functions using libm, but you want me to remove the libm overwrite located in libm_intrinsics.rs, and instead add implementations of the remaining functions to float.rs using inline assembly code?

If yes, then I'd still prefer doing this in another PR, because that's going to be more advanced and as such could merit a separate review.

@Firestar99

Firestar99 commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

I'd also be fine merging this as is and take the rest as a later PR. I'll delay the actual merge a tiny bit as I want to make a release today / tomorrow.


While I look over rust-gpu maintenance, I'm not too familiar with how our libm functions work, so feel free to correct me on this.

So we've got our table of libm overwrites here containing functions such as sin, cos, sqrt etc. This table ends up being used here to overwrite any external function declarations with our custom intrinsics. At the same time, within codegen_intrinsic_call we implement a bunch of math intrinsics, including sym::sinf32, sym::cosf32 and sym::sqrtf32.

My brain immediately screams DRY and wants to get rid of one of them. So why shouldn't we get rid of the libm path and just do this for the remaining functions?

#[cfg(target_arch = "spirv")]
impl f32 {
    pub fn sin(self) -> f32 {
        intrinsics::sin(self)
    }
}

This should end up calling our sym::sinf32, right?

However, when you look at glam::Vec4::length(), it calls math::sqrt(), which has two implementations depending on features:

  • std calls f32::sqrt
  • libm calls libm::sqrt

So I'd assume if we impl this and get rid of our libm overwrites, f32::sqrt() will codegen the GLSL sqrt intrinsic but Vec4::length() will fall back to a software implementation. I assume num-traits may have the same issue.

I can prep some compiletest to make sure our codegen doesn't worsen.


On inline assembly: We could very much also use inline SPIR-V asm to implement these functions instead of going via the codegen backend. Here's a quick (tested) mockup, you'll find the IDs (13 for sin) in https://registry.khronos.org/SPIR-V/specs/unified1/GLSL.std.450.html

#[inline]
pub fn sin(a: f32) -> f32 {
    let mut result;
    unsafe {
        asm!(
            "%glsl = OpExtInstImport \"GLSL.std.450\"",
            "{result} = OpExtInst typeof*{result} %glsl 13 {a}",
            a = in(reg) a,
            result = out(reg) result,
        );
    }
    result
}

@mikwielgus

Copy link
Copy Markdown
Contributor Author

This should end up calling our sym::sinf32, right?

Yes (but I haven't tested this). But tan, asin, atan2, sinh, and some more, exist in std and have their GLOp variants but are not present in core::intrinsics. For these, inline assembly code might be be inevitable if we are to remove libm_intrinsics.rs.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants