autodiff: Normalize typetree struct field types - #160701
Conversation
FieldDef::ty returns Unnormalized, so skipping normalization left anon-const array lengths unevaluated. That ICEd in struct_tail_for_codegen and emptied TypeTrees for plain [T; N] fields. Normalize with TypingEnv::fully_monomorphized before recursing.
Cover both the ICE path through *mut [T; N] struct fields and the silent empty-TypeTree case for plain [T; N] fields.
|
r? @davidtwco rustbot has assigned @davidtwco. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
r? me |
| INLINE-NOT: define | ||
| INLINE: call void @llvm.memcpy{{.*}}"enzyme_type"="{[0]:Pointer, [0,0]:Float@float, [0,32]:Float@float}" | ||
|
|
||
| ; Enzyme accepts the ICE-shaped PtrArray TypeTree (not just rustc emission). |
There was a problem hiding this comment.
What does ICE-shaped mean? Same for " (not just rustc emission)"
There was a problem hiding this comment.
yeah fair, "ICE-shaped" was me being cute and it just confused things lol. dropped that whole comment. imo if the FileCheck already proves the TypeTree shape we don't need a little essay next to it. ltm if the new wording still feels off.
| *b = *a; | ||
| } | ||
|
|
||
| // Run Enzyme over the ICE-shaped type so metadata is not only emitted but accepted. |
There was a problem hiding this comment.
This comment doesn't give relevant info (All metadata we emit should be accepted)
There was a problem hiding this comment.
agree, that comment was kinda pointless. if we emit it, Enzyme should take it; no reason to special-case that in the test notes. deleted it.
|
|
||
| // Regression test for #160635: non-literal array lengths (anon consts) in struct | ||
| // fields must be normalized before typetree recursion. Without that, `*mut [f32; N]` | ||
| // ICEs in `struct_tail_for_codegen`, and a plain `[f32; N]` field silently yields |
There was a problem hiding this comment.
Ah this. Can you please update the comments to something that briefly explains what a struct_tail_for_codegen is? (E.g. along the first two sentences from the fn def).
There was a problem hiding this comment.
updated. fyi I basically stole the "deepest trailing field / unsizing tail" bit from the fn docs, since that's the part that actually bites us here when the anon const isn't normalized yet. hope that reads clearer.
| PTR-NOT: define | ||
| PTR: call void @llvm.memcpy{{.*}}"enzyme_type"="{[0]:Pointer, [0,0]:Pointer, [0,0,-1]:Float@float, [0,8]:Float@float, [0,12]:Float@float}" | ||
|
|
||
| ; Silent case: plain [T; N] field produces float metadata (not an empty subtree). |
There was a problem hiding this comment.
"Silent case: ", "Known limitation" etc read very llm-y. can you shorten the comments a bit so it reads more natural? I curently find them a bit confusing.
There was a problem hiding this comment.
yeah those comments were doing too much. shortened them a bunch. btw I kept one short note that we still only classify the first inline element rn, mainly so nobody thinks the test is claiming full array coverage. ltm if even that feels like too much.
|
|
||
| #[derive(Copy, Clone)] | ||
| #[repr(C)] | ||
| pub struct InlineArray { |
There was a problem hiding this comment.
@wsmoses what typetree do you expect to be generated here, especially for larger N, of say 2048? Currently we only generate it for offset 0 of data and for scale.
Generating 2048 offsets seems extremely wasteful and -1 would be wrong, in the general case were scale and data have different base types (e.g. scale were int).
Afaik Enzyme doesn't directly let frontend generate typetree ranges?
There was a problem hiding this comment.
same place I'm stuck tbh. dense offsets for N=2048 feel pretty wasteful, and -1 is wrong once scale isn't float. afaik Enzyme still doesn't give frontends a clean range form, so I'm not trying to "solve" that in this PR.
imo we keep the normalize fix + the i32 scale regression here, and wait for @wsmoses on what rustc should emit for big inline arrays. ltm what you'd want that follow-up to look like.
| #[repr(C)] | ||
| pub struct InlineArray { | ||
| pub data: [f32; N], | ||
| pub scale: f32, |
There was a problem hiding this comment.
Would the test still work if scale were another type? That makes it a bit more clear why we can't just simplify it to -1 (float everywhere)
There was a problem hiding this comment.
good call. flipped scale to i32 and the FileCheck now wants Integer at byte 32. imo that makes the "can't just slap [-1]:Float on the whole struct" point way more obvious irl.
| ; Silent case: plain [T; N] field produces float metadata (not an empty subtree). | ||
| ; Known limitation: the struct arm collapses array element offset -1 to the field | ||
| ; byte offset, so only byte 0 of `data: [f32; 8]` is classified as Float; bytes | ||
| ; 4..32 stay unclassified (unlike pointer-to-array, which preserves [-1]). |
There was a problem hiding this comment.
That sounds like a bad footgun, let's see what the enzyme author suggests.
There was a problem hiding this comment.
yeah it's a real footgun. for this PR I left the current first-element behavior alone and just made the test honest about it. idk what the right compact encoding is yet, so I'd rather wait for William than invent something weird on our side. can chase the full-array case in a follow-up asap once that's clearer.
|
Thanks! Seems like quite an easy fix, mostly left document nits. But let's also see if we can fix the weird case of not generating typetrees for all following array elements, that feels like it would otherwise cause headaches down the road. Not really sure what we want to generate though, so let's ask the author. |
Drop the unhelpful acceptance wording, briefly explain the struct_tail_for_codegen ICE path, and make scale an i32 so the inline-array case cannot be hand-waved as floats everywhere.
Fixes #160635
-Zautodiff=Enablewas ICEing on structs with fields like*mut [f32; N].FieldDef::tyreturnsUnnormalized, and we were just callingskip_norm_wip, so recursion hitstruct_tail_for_codegenwith an unevaluated anon const length. Same root cause also quietly emptied TypeTrees for plain[T; N]fields, fyi.Now we normalize with
TypingEnv::fully_monomorphizedbefore walking struct fields. Imo that's the right call here: we're already post-mono in codegen, and it lines up with the Unnormalized WIP migration instead of spreading moreskip_norm_wiparound. Btw I added a run-make regression covering both faces of the bug.Idk if Enzyme will still choke on murkier shadow-pointer cases irl, but at least rustc stops panicking. Ltm if the FileCheck strings look too layout-locked for other targets and I can loosen them asap.