JIT: fix value-probing schema index collision with handle histogram#127959
JIT: fix value-probing schema index collision with handle histogram#127959EgorBo merged 1 commit intodotnet:mainfrom
Conversation
Both `HandleHistogramProbeInstrumentor` and `ValueInstrumentor` were writing to the same `BasicBlock::bbHistogramSchemaIndex` field. When a single block had both `BBF_HAS_HISTOGRAM_PROFILE` (e.g. a virtual call) and `BBF_HAS_VALUE_PROFILE` (`SpanHelpers.Memmove`/`SpanHelpers.SequenceEqual`) the value instrumentor's `BuildSchemaElements` overwrote the handle index, causing the handle inserter to read the wrong schema entries and either hit the DEBUG assert at fgprofile.cpp:2077 or produce malformed IR (a `GT_COMMA` with a `nullptr` child) in retail. Split the field: rename `bbHistogramSchemaIndex` to `bbHandleHistogramSchemaIndex` and add a dedicated `bbValueHistogramSchemaIndex`. The new field is placed so that it consumes the existing 4-byte tail padding before `bbTryIndex` -- `sizeof(BasicBlock)` is unchanged on x64 (verified at 280 bytes before/after). Also fix a copy-paste typo in `ValueInstrumentor::Prepare` that was clearing `bbCountSchemaIndex` instead of its own field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR fixes a correctness issue in CoreCLR JIT PGO instrumentation where handle-histogram probing and value-probing could overwrite the same per-block schema index, causing the handle-histogram instrumentor to read the wrong schema entry for blocks that contain both probe kinds.
Changes:
- Split the shared
BasicBlockschema index intobbHandleHistogramSchemaIndex(handle histogram) and a newbbValueHistogramSchemaIndex(value histogram). - Update
HandleHistogramProbeInstrumentorandValueInstrumentorto write/read their dedicated schema index fields. - Fix
ValueInstrumentor::Prepareto reset its own schema index field (rather than the unrelated count schema index).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/coreclr/jit/fgprofile.cpp | Switch handle/value instrumentors to use distinct per-block schema index fields and correct the Prepare reset target. |
| src/coreclr/jit/block.h | Rename the handle-histogram schema index field and add a dedicated value-histogram schema index field on BasicBlock. |
|
I wonder if this fixes #123282 |
I think it's very likely fixing it, yes, at least that assert can fail today because of the issue I'm fixing. |
|
Slightly more allocated on 32-bit where the new field in BasicBlock slightly increased its layout (no impact on 64bit) I played locally with multiple value probes and class probes in the same block to test it |
Both
HandleHistogramProbeInstrumentorandValueInstrumentorwere writing to the sameBasicBlock::bbHistogramSchemaIndexfield. The value instrumentor was essentially copy-pasted from the type histogram one and inherited the same field without adjusting for the fact that a block can be processed by both.The bug
When a single basic block has both
BBF_HAS_HISTOGRAM_PROFILE(e.g. a virtual call) andBBF_HAS_VALUE_PROFILE(aSpanHelpers.Memmove/SpanHelpers.SequenceEqualspecial intrinsic), schema build runs in this order:HandleHistogramProbeInstrumentor::BuildSchemaElementswritesbbHistogramSchemaIndex = Nand appendsHhandle-histogram entries.ValueInstrumentor::BuildSchemaElementsoverwrites withbbHistogramSchemaIndex = N + Hand appends value entries.Then
HandleHistogramProbeInstrumentor::Instrumentruns first for the block, readsN + H, andReadHistogramAndAdvancelands on aValueHistogramIntCount/ValueHistogramLongCountkind. It returns early without settingtypeHistogram/methodHistogram:"Expected at least one handle histogram when inserting probes") fires, and so doesInstrCount() == compHandleHistogramProbeCountlater.helperCallNodestaysnullptrandgtNewOperNode(GT_COMMA, TYP_REF, helperCallNode, tmpNode2)is built with anullptrchild -- malformed IR, not just a missed probe.The pattern is common: any method that has a virtual call followed by
Span<T>.CopyTo/MemoryExtensions.SequenceEqual/Buffer.Memmovein the same block can hit it once those calls inline down to the recognizedSpanHelpersintrinsics.While at it I also noticed a copy-paste typo in
ValueInstrumentor::Preparethat clearedbbCountSchemaIndexinstead of its own field. Benign today (count instrumentor'sPreparecleared it earlier), but indicative of the shaky copy-paste.The fix
Split the per-block schema index into two dedicated fields:
bbHistogramSchemaIndex->bbHandleHistogramSchemaIndex(used byHandleHistogramProbeInstrumentor)bbValueHistogramSchemaIndex(used byValueInstrumentor)The new field is placed right after the existing union so it consumes the pre-existing 4-byte tail padding before
bbTryIndex/bbHndIndex.sizeof(BasicBlock)is unchanged on x64 (280 bytes before and after, verified with a temporaryPrintSize<sizeof(BasicBlock)>probe).