--- title: "Lang/RfL meeting 2024-10-09" tags: ["T-lang", "design-meeting", "minutes"] date: 2024-10-09 discussion: https://rust-lang.zulipchat.com/#narrow/stream/410673-t-lang.2Fmeetings/topic/RfL.20meeting.202024-10-09 url: https://hackmd.io/AAEd5yPxRJOJKE-ZtP2yOw --- # Lang/RfL meeting 2024-10-09 [Tracking issue](https://github.com/rust-lang/rust-project-goals/issues/116) ## Off script agenda Gary Guo would like to go through the design doc for asm-goto. Alice has a very incomplete target modifiers RFC. NM: Name for derive smart pointer in FCP as `CoercePointee`. Benno: I created a use-cases document for field projections. Zulip topic: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Field.20Projections Document: https://hackmd.io/@y86-dev/SkSB48hCR ## Target modifiers Alice: Had thoughts about build-std. Do we really need to block stabilization of these sorts of flags on building the standard library? NM: I didn't realize they were blocked Alice: Target modifiers require a new std NM: Sure, so you can't productively use it, but why does that prevent stabilizing the flag? Seems like an unnecessary dependency. There is precedent, e.g., [when we stabilized `#![no_std]`](https://rust-lang.github.io/rfcs/1184-stabilize-no_std.html) I recall people making the argument that we should stabilize this, even though it can't be used off nightly in practice, to move us forward. GG: In practice people are building libcore/liballoc and libs team makes decisions based on that (e.g., avoiding feature flags). AR: Libs team has been avoiding breaking these users....can we just *say* you can compile libcore with rustc and be done with it? NM: I feel like "maybe" but I'd still rather we not block on that. MO: Some folks have wanted to add some sort of build-std flag in `rustc` itself (not Cargo). NM: I strongly suspect that will be the way we do this. Some flag that says "give the default flags you need to build core", even if that's currently the empty set, so that we can evolve it. ## Gary Guo's doc on asm-goto https://hackmd.io/@nbdd0121/BJlVrepa0 (https://hackmd.io/FFw6vk6cRFSBLQRh1Jf04Q?both for source) (Background on `asm goto` in C: https://gcc.gnu.org/onlinedocs/gcc/extensions-to-the-c-language-family/how-to-use-inline-assembly-language-in-c-code.html#goto-labels) Niko's takeaways from doc * Definitely should remove the safe/unsafe * Directly supporting values out of asm-goto feels elegant, even if low priority * "This adds complexity to a rarely (in terms of code appearance frequency not runtime frequency) used asm feature" -- how much complexity? I'm curious to hear from Amanieu on this. * Extending const to CTFE constants seems like a no-brainer...is it backwards compatible? * Const arguments would be useful for SIMD as well; I love the idea of it but it's a fairly large-ish change Alice: * Const accepting pointers seems like a good way to replace 'i' arguments. Notes on conversation: AR: from Rust perspective, it looks like this basically means "if a page fault happens, you jump to the default label, but otherwise you drop through the end of the block". GG: yes, the only thing that affects the design is whether the block is safe or not. If we want to say that `unsafe` is narrowly scoped then we want to make the block safe. I think the argument has been that the block should be safe by default. TC: Does this tie into a larger language discussion over "goto-labeled blocks"? This was happening over on Zulip recently, I didn't follow the whole discussion, something about labeled block arguments, computed goto...feels like the way that this works in C is reliant on the fact that you can jump via goto in C *anyway*. We don't have that in Rust so I'm struggling to see how we make asm-goto work without the more general goto? GG: asm-goto is not unstructured control flow in Rust. In C it goes to a label. But in Rust it goes to a block, control is transferred to that block, which is scoped. ```rust 'out: { 'fault: { unsafe { asm!( "/* do some op, jump to {fault} on fail */", fault = label { break 'fault; } ); } break 'out; } // Fault handling code. } ``` NM: Seems like even if we did want to add labels in the future, it'd be backwards compatible to support it... ```rust 'out: { 'fault: { unsafe { asm!( "/* do some op, jump to {fault} on fail */", fault = break 'fault, ); } break 'out; } // Fault handling code. } // Always comes here eventually ``` AR: Right, it seems like you could just say "you can give an inline block or a label". GG: In this case the fallthrough block has no use. NM: Currently fallthrough block is not a concept, right? NM: So, allowing safe code is really perfect here: ```rust unsafe { asm!( "/* do some op, jump to {fault} on fail */", fault = label { // safe fault handling code here } ); } ``` ### Breaking out from loop in asm-implemented computation Example: ```rust asm!( "/* ... */ beq, ..., {end} /* ... */", end = label { return ... // or break }, ) ``` Could be mildly shorter but "meh". ### Static branch Modeling "results" from the `asm!` expression: ```rust 'outer: { unsafe { asm!(/* use label */, label { break 'outer true; }); false } } ``` ...used like... ```rust if arch_static_branch!(KEY, BRANCH) { /* */ } ``` ...could be nicer if we have had... ```rust unsafe { asm!( "/* do some op, maybe jump to {0}, maybe not */", label { true }, fallthrough { false }, ) } ``` GG: Easy to implement for the type checker. Already have to do unification because blocks can diverge and what not. We are currently unifying them to unit. NM: This appeals to me because it feels Rust-y to have asm blocks yield values. GG: Kind of weird to not permit result values for the more common case of e.g. writing to a register. NM: :shrug: True? Still nice. ### `i` constraint GG: This is technically a distinct thing, not specifiying to asm-goto. In C: ```c int x; static inline void foo(int *ptr, int value) { int y; asm volatile ("/* ... */"::"i"(100)); // OKAY asm volatile ("/* ... */"::"i"(1+1)); // OKAY asm volatile ("/* ... */"::"i"(&x)); // OKAY asm volatile ("/* ... */"::"i"(&y)); // NOT OKAY asm volatile ("/* ... */"::"i"(ptr)); // OKAY if caller uses a constant `ptr`. asm volatile ("/* ... */"::"i"(value)); // OKAY if caller uses a constant `value`. } ``` Rust permits `const` like so: ```rust static X: (i32, i32) = 1; #[inline] unsafe fn foo<const N: i32>(ptr: *const i32, value: i32) { let y: i32 = 2; asm!("/* ... */", const 100); // OKAY asm!("/* ... */", const 1+1); // OKAY asm!("/* ... */", const &X); // NOT OKAY asm!("/* ... */", const &X.1); // NOT OKAY asm!("/* ... */", sym X); // but this is OKAY asm!("/* ... */", const &y); // NOT OKAY asm!("/* ... */", const ptr); // NOT OKAY asm!("/* ... */", const value); // NOT OKAY asm!("/* ... */", const N); // but this is OKAY } ``` But it doesn't work with pointers. There's a workaround to use `sym` which inserts the address of the global. Inserts a symbol name as textual replacement. Prevents you from writing a function, must use macros. If we could accept "pointers" with const...could work around this. GG: There was some discussion. People wanted to say that `const FOO: &str` would substitute the string into the assembly, but I think it should be an address. AR: So there are these other points, ref to str, where you might want the actual string value... ```asm mov "foo", x ``` Currently, if you have ```rust asm!("mov rax, {}", const 100); ``` LLVM gets ```asm mov rax, 100 ``` there's suggestion to make ```rust asm!("mov rax, {}", const "foo"); ``` LLVM gets ```asm mov rax, foo ``` NM: that seems like a distinct keyword AR: maybe we should have interpolate or something ```rust asm!("mov rax, {}", interpolate "foo"); ``` GG: alternative would be to insert the address of the pointee. Some concern about how to get that to work for global-asm. Currently works for numerical numbers only. For non-global though can generate similar to `i` constraints. NM: Is it a problem to just document that it can't be used in global-asm? GG: Not particularly, it'd just be nice for them to be the same. NM: in Rust, "foo" is a wide-pointer.......what should that do? GG: Prob only want thin pointers. Just accept integers or thin pointers. NM: Basically any pointer to something whose metadata is unit. NM: Alice the things you want to insert, what type are they? AR: I want to insert a pointer to a field of a struct. Specifically not at the beginning. So `&X.my_field`. ## THE DREAM: constant function arguments What we'd be able to do with better `const` is to use `const` generic arguments... ```rust static X: (i32, i32) = 1; #[inline] unsafe fn foo<const N: i32>(ptr: *const i32, value: i32) { let y: i32 = 2; asm!("/* ... */", const 100); // OKAY asm!("/* ... */", const 1+1); // OKAY asm!("/* ... */", const &X); // NOT OKAY asm!("/* ... */", const &X.1); // NOT OKAY asm!("/* ... */", sym X); // but this is OKAY asm!("/* ... */", const &y); // NOT OKAY asm!("/* ... */", const ptr); // NOT OKAY asm!("/* ... */", const value); // NOT OKAY asm!("/* ... */", const N); // but this is OKAY } ``` ```rust unsafe fn foo(const ptr: *const i32, const value: i32) { asm!("/* ... */", const ptr); asm!("/* ... */", const value); } ```