Stream: t-compiler/major changes

Topic: MCP: More Cranelift-friendly portable SIM… compiler-team#381

triagebot (Nov 04 2020 at 11:36, on Zulip):

A new proposal has been announced: MCP: More Cranelift-friendly portable SIMD intrinsics #381. It will be announced at the next meeting to try and draw attention to it, but usually MCPs are not discussed during triage meetings. If you think this would benefit from discussion amongst the team, consider proposing a design meeting.

Jonas Schievink (Nov 04 2020 at 12:47, on Zulip):

Seems like these should go in compiler_builtins, not miri/the const eval engine, otherwise you can only execute them at compile time.

Jonas Schievink (Nov 04 2020 at 12:48, on Zulip):

But then any backend needs support for SIMD as part of the calling convention, and any use of an intrinsic incurs a function call.

bjorn3 (Nov 04 2020 at 13:51, on Zulip):

You can have the compiler_builtins implementation as fallback for when there is no native implementation for a platform-intrinsic. SIMD supports as part of the calling convention is not necessary. You can just pass the arguments using the regular struct calling convention. A disadvantage of having it in compiler_builtins is that platform-intrinsics work for any combination of lane type and vector size, while regular functions can't.

nikomatsakis (Nov 04 2020 at 14:02, on Zulip):

So in reference to this part of the MCP:

We want to write reference implementations in plain Rust rather than in MIR, but also want backends to be able to use them to emit code. Is this something that's possible in the compiler? Is compiler-builtins the right place for them?

This is probably the same as what @bjorn3 said, but what I would imagine is that we would write "plain Rust" functions but give them some kind of special attribute

nikomatsakis (Nov 04 2020 at 14:04, on Zulip):

such that if there is a direct call to the function (in a backend that can handle it) we generate special SIMD code

nikomatsakis (Nov 04 2020 at 14:04, on Zulip):

but otherwise we can treat it like a normal function call

nikomatsakis (Nov 04 2020 at 14:05, on Zulip):

I think this is how we handle intrinsics anyway?

nikomatsakis (Nov 04 2020 at 14:05, on Zulip):

I know that @eddyb had proposed this model in particular to handle things like indirect function calls

nikomatsakis (Nov 04 2020 at 14:05, on Zulip):

though I think in that original proposal you'd see something

nikomatsakis (Nov 04 2020 at 14:05, on Zulip):
fn some_intrinsic() {
nikomatsakis (Nov 04 2020 at 14:05, on Zulip):

the idea being that an indirect call invokes the some_intrinsic function and the function body itself contains a direct call, which gets generated with the special behavior

bjorn3 (Nov 04 2020 at 14:06, on Zulip):

That is basically what drop_in_place does I think.

nikomatsakis (Nov 04 2020 at 14:08, on Zulip):
pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
    // Code here does not matter - this is replaced by the
    // real drop glue by the compiler.

    // SAFETY: see comment above
    unsafe { drop_in_place(to_drop) }
nikomatsakis (Nov 04 2020 at 14:08, on Zulip):

sounds right

nikomatsakis (Nov 04 2020 at 14:08, on Zulip):

anyway, I think this would work, right? the idea being that we give the "reference impl" semantics as plain rust

bjorn3 (Nov 04 2020 at 14:10, on Zulip):

How would the "reference impl" be written for say simd_add though? The full signature is extern "platform-intrinsic { fn simd_add<T>(a: T, b: T) -> T; }. This accepts vector types with any lane size and lane count.

Lokathor (Nov 04 2020 at 14:20, on Zulip):

Well, the final version would probably need some trait bounds, or just compile error if unacceptable types are put in.

However, being allowed to use types without direct hardware support is by design, so that much at least has to be allowed.

bjorn3 (Nov 04 2020 at 14:21, on Zulip):

I am not talking about the interface. I am talking about the implementation. What would you write in the function body?

nikomatsakis (Nov 04 2020 at 17:45, on Zulip):

@bjorn3 yeah that wouldn't work :) I agree it would need some kind of trait bounds to make it writeable

bjorn3 (Nov 04 2020 at 17:47, on Zulip):

Currently all intrinsics in compiler_builtins are non-generic. I don't know if it is possible to introduce generics given that the public interface is through unmangled symbols and there is a lot of special casing of compiler_builtins in rustc.

scottmcm (Nov 04 2020 at 22:49, on Zulip):

Hmm, I guess it could be done like doc items or something -- write everything as all normal rust functions but with a special attribute on them so the backend can swap them out for whatever the IR has if they have support for the operation.

Ashley Mannix (Nov 04 2020 at 23:23, on Zulip):

It sounds like our first job is to figure out how to write these reference implementations and we’re generally ok with using extern “platform-intrinsic”?

Ashley Mannix (Nov 05 2020 at 04:06, on Zulip):

The reason I was pushing for plain Rust reference implementations that we can directly call was that I was expecting an API that hands backends MIR would obscure the contract of these portable methods and make them harder to work on. But if coming up with a few macros to make “I have a simd_add, what code should I emit?” easier to understand for us non-compiler folk has fewer unknowns than trying to come up with a pure Rust approach then maybe we should try go that way

nikomatsakis (Nov 05 2020 at 15:02, on Zulip):

I have to bring some of those back into cache obviously but

nikomatsakis (Nov 05 2020 at 15:03, on Zulip):

I remember that way back in Ye Olde Days huonw had these special intrinsics that kind of hard-coded simd over some set of types but that set was ad-hoc and not defined via traits

nikomatsakis (Nov 05 2020 at 15:03, on Zulip):

(probably this is still true?)

nikomatsakis (Nov 05 2020 at 15:04, on Zulip):

I guess I didn't get a clear picture from the MCP what kind of data types these intrinsics need to operate over

nikomatsakis (Nov 05 2020 at 15:04, on Zulip):

I think it'd be nice if we can write "plain old Rust" and not hard-coded MIR etc

bjorn3 (Nov 05 2020 at 15:04, on Zulip):

The data types they work on are all #[repr(simd)] types with the correct lane types.

nikomatsakis (Nov 05 2020 at 15:21, on Zulip):

yes ok this is roughly what I remember

Lokathor (Nov 05 2020 at 19:09, on Zulip):

So specifically the types would be signed and unsigned integer types (all widths) and float types (all widths) for 128-bit simd initially, but also 64, 256, 512 eventually, and in the far future doubtlessly more. These would need to be supported even if they're not hardware supported, so on some targets a particular op might need a fallback even if most ops for that type don't.

Lokathor (Nov 05 2020 at 19:10, on Zulip):

when i say "integer" i include usize/isize but i do not include char or bool.

Lokathor (Nov 05 2020 at 19:11, on Zulip):

and the operations available for floats would be not be the exact same operations as for integers (eg: float has sqrt, integer does not)

Ashley Mannix (Nov 07 2020 at 03:23, on Zulip):

Ok, so what if we wrote our fallback implementations in pure Rust in terms of [T; N], and then manually monomorphized a bunch of them as compiler_builtins using a naming convention we can easily generate from the inputs, like LLVM’s intrinsics. Kind of like this.

Ashley Mannix (Nov 07 2020 at 03:25, on Zulip):

We could easily use macros to generate simd_add_i32x4-style functions from our single generic simd_add

Ashley Mannix (Nov 07 2020 at 05:41, on Zulip):

Or we could use some traits to specialize behavior, while still generating the same monomorphized intrinsics

Ashley Mannix (Nov 08 2020 at 05:51, on Zulip):

I’ve updated the MCP text to include some more details on how these fallbacks could be written.

Ashley Mannix (Nov 15 2020 at 06:53, on Zulip):

I've updated this MCP again. It's now got a conceptual, but working, example of how this could all hang together:

It would be great to get some more :eyes: on it!

bjorn3 (Nov 15 2020 at 08:47, on Zulip):

I assume the codegen_support and codegen_cranelift modules are what the compiler would do and not included directly in compiler_builtins, right?

Ashley Mannix (Nov 15 2020 at 09:17, on Zulip):

@bjorn3 I think so, yeh. The idea being Cranelift only has to come up with an implementation of that codegen_cranelift::simd_fallback that can pack and unpack #[repr(simd)] types as slices so they can be passed to the builtin it names by convention

Ashley Mannix (Nov 15 2020 at 09:18, on Zulip):

So I think the difference between this and the API you were originally talking about is instead of being handed some MIR you get handed a function to call

triagebot (Apr 01 2021 at 14:43, on Zulip):

@T-compiler: Proposal #381 has been seconded, and will be approved in 10 days if no objections are raised.

Jubilee (Apr 21 2021 at 15:48, on Zulip):

@Ashley Mannix Thank you so much.

triagebot (May 06 2021 at 17:10, on Zulip):

This proposal has been accepted: #381.

Last update: May 07 2021 at 06:15UTC