Skip to content

TypeAnalysis: seed extractvalue from LLVM type when operand unknown#2819

Open
kimjune01 wants to merge 8 commits into
EnzymeAD:mainfrom
kimjune01:fix/2630-extractvalue-type-deduction
Open

TypeAnalysis: seed extractvalue from LLVM type when operand unknown#2819
kimjune01 wants to merge 8 commits into
EnzymeAD:mainfrom
kimjune01:fix/2630-extractvalue-type-deduction

Conversation

@kimjune01
Copy link
Copy Markdown
Contributor

Fixes #2630, also addresses #2463.

When extractvalue operates on an opaque struct (e.g., from an unknown function call), and the operand has no type info, use the LLVM IR type as ground truth. Unlike memory loads, extractvalue field types are unambiguous.

Tested with extractvalue chains on float arrays nested in structs returned from opaque calls.

June Kim and others added 2 commits May 10, 2026 21:07
Fixes EnzymeAD#2630, EnzymeAD#2463.

When TypeAnalysis encounters extractvalue on an aggregate whose source
has no prior type info (e.g. an opaque external call return), it left
the result with empty TypeTree, which caused AdjointGenerator to emit
"Cannot deduce type of extract" during reverse-mode AD.

extractvalue is fully type-checked at the IR level, so the LLVM type of
the result is authoritative about leaf float types -- there is no
type-punning possible. When the result type is a uniform-FP aggregate
(every leaf is the same FP type) or a single FP, seed Float@<FP> into
the result TypeTree before the existing DOWN/UP propagation. UP
propagation then back-fills the source aggregate with float entries at
every leaf offset, recovering type info even when the source originated
from an opaque call.

Test: enzyme/test/TypeAnalysis/extractvalue_aggregate.ll mirrors the IR
pattern from EnzymeAD#2630 (a function returning struct { [2 x float], [2 x [3
x float]] } and the caller chain of extractvalues). Pre-fix all
extractvalues had {} type; post-fix every leaf is correctly typed
{[-1]:Float@float} and the source call result is typed at all 8 byte
offsets.
…LeafType

Address codex review findings:
- Add VectorType case: extractvalue can return vector-typed aggregate
  elements (e.g. { <2 x float> }), treat like scalar FP via element type.
- Guard zero-length arrays: [0 x float] has no scalar leaves, return
  nullptr to avoid seeding type info onto zero-sized results.
- Add negative lit test: mixed aggregate (float + i32) must not seed
  Float on the i32 field.
@wsmoses
Copy link
Copy Markdown
Member

wsmoses commented May 11, 2026

So this is not necessarily the case, but I'm potentially willing to allow this with a warning if looseTypeAnalysis is on

@@ -0,0 +1,71 @@
; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -print-type-analysis -type-analysis-func=compute -o /dev/null | FileCheck %s; fi
; RUN: %opt < %s %newLoadEnzyme -passes="print-type-analysis" -type-analysis-func=compute -S -o /dev/null | FileCheck %s

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test isn't necessarily helpful here because by default float input types will be assumed to be the correct float

Copy link
Copy Markdown
Contributor Author

@kimjune01 kimjune01 May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that float types from the struct are already propagated. The end-to-end test (ENZYME check) exercises the full pipeline path through AdjointGenerator, which is where the original crash happened (#2630). The WARN check might be redundant if the seeding fires after the type is already known from struct propagation. Happy to drop the WARN check and keep just the end-to-end test if that's cleaner.

Per wsmoses: LLVM type is not always authoritative about semantic
float-ness, so gate the extractvalue seeding behind looseTypeAnalysis.

Fix the test to use i64 input (not float) so TypeAnalysis cannot infer
float from the function signature — the only source of float info is
the extractvalue LLVM type seeding via looseTypeAnalysis.
@kimjune01
Copy link
Copy Markdown
Contributor Author

Addressed both points:

  1. Gated the seeding behind looseTypeAnalysis — you're right that LLVM type isn't always authoritative about semantic float-ness.

  2. Rewrote the test to take i64 input (not float) so TypeAnalysis can't infer float from the function signature. The only source of float info is now the extractvalue LLVM type seeding, which exercises the fix path directly.

Also added -enzyme-loose-types to the RUN lines.

Previous test only extracted %a and %a0, so the CHECK line claiming
all 8 offsets on %r was speculative. Now extracts both %a and %b
sub-fields so UP propagation definitively populates all offsets.

NOTE: CHECK lines need verification against actual enzymemlir-opt output.
@kimjune01
Copy link
Copy Markdown
Contributor Author

Verifying fix now

June Kim added 2 commits May 10, 2026 22:04
Adds a third RUN line that runs the full enzyme reverse-mode pass and
asserts (a) a `diffead_compute` is generated and (b) the output never
contains the "Cannot deduce type of extract" diagnostic. This proves
the fix removes the original failure mode end-to-end, not just at the
TypeAnalysis layer.

The new ad_compute function takes a float seed, calls pre_work (opaque,
returns the same { [2 x float], [2 x [3 x float]] } struct), extracts a
[2 x float] aggregate and a single float via chained extractvalues, and
multiplies them with the seed. Pre-fix, the [2 x float] extract trips
the AdjointGenerator's diagnostic because the looseTypeAnalysis fallback
at AdjointGenerator.h:1894 only handles primitive FP/int and not
aggregates. Post-fix, the TypeAnalysis seeding fills the source struct
with all 8 float offsets, all extracts type cleanly, and the reverse
pass succeeds.

Both run lines verified on Ubuntu 26.04 / clang-19 via:
  python3 /usr/lib/llvm-19/build/utils/lit/lit.py test/TypeAnalysis/extractvalue_aggregate.ll
  PASS: Enzyme :: TypeAnalysis/extractvalue_aggregate.ll (1 of 1)

Non-regression: full TypeAnalysis lit suite shows identical pass/fail
counts (13/96/1) with and without the fix; the 96 pre-existing failures
are LLVM 19 opaque-pointer text format mismatches, not caused by this
change.
Address review feedback from @wsmoses on PR EnzymeAD#2819:
"potentially willing to allow this with a warning if looseTypeAnalysis
is on".

The seeding from LLVM type at extractvalue is not always semantically
authoritative (e.g. type-punning through unions), even though it's IR-
verifier-checked. When the fallback fires under -enzyme-loose-types,
emit a warning gated on the existing -enzyme-type-warning flag (default
on). Routes through CustomErrorHandler when set, matches the existing
TypeDepthExceeded warning shape.

Test extended:
- existing CHECK / ENZYME RUN lines now pass -enzyme-type-warning=0 to
  keep their FileCheck output clean.
- new WARN RUN line asserts the warning text fires for the %a extract.

All four RUN lines verified via:
  python3 /usr/lib/llvm-19/build/utils/lit/lit.py test/TypeAnalysis/extractvalue_aggregate.ll
  PASS: Enzyme :: TypeAnalysis/extractvalue_aggregate.ll (1 of 1)
@kimjune01
Copy link
Copy Markdown
Contributor Author

kimjune01 commented May 11, 2026

Verified on WSL/LLVM-19

ErrorType choice used IllegalTypeAnalysis as the closest existing category, maybe a new enum?

Warning fires per-iteration TypeAnalysis iterates to fixpoint, triggers twice. Could be noisy. Dedup? It's gated behind -enzyme-loose-types, I say it's a TODO

Default-on warning: -enzyme-loose-types users go from "compile fails" to "compile succeeds with warnings." Maybe -enzyme-type-warning=0?

Scope: stayed away from AdjointGenerator.h:1894 primitive-only fallback, also no fix for insertvalue

kimjune01 added 2 commits May 10, 2026 22:31
The @compute function doesn't meaningfully test the LLVM-type seeding
fallback because float types are already assumed to be the correct float
by default. Keeping only the @ad_compute end-to-end test which exercises
the fix in the context it was designed for: preventing "Cannot deduce
type of extract" errors when loose-types is enabled.
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.

Enzyme: Cannot deduce type of extract %29 = extractvalue [2 x float] %28, 0, !dbg !1270{} start: 0 size: 4 extractSize: 4

2 participants