Skip to content

common: attr: include binary select src2 in post-op identity - #5970

Open
MaxFreedomPollard wants to merge 1 commit into
uxlfoundation:mainfrom
MaxFreedomPollard:common-binary-select-po-identity
Open

MaxFreedomPollard wants to merge 1 commit into
uxlfoundation:mainfrom
MaxFreedomPollard:common-binary-select-po-identity

Conversation

@MaxFreedomPollard

@MaxFreedomPollard MaxFreedomPollard commented Sep 6, 2026

Copy link
Copy Markdown

Description

The identity of a binary post-op ignores the third operand, so the primitive cache hands back a primitive that was built for a different src2 memory descriptor.

post_ops_t::entry_t::operator== (src/common/primitive_attr.hpp:430) compares only binary.alg and binary.user_src1_desc, and get_attr_hash (src/common/primitive_hashing.cpp:312) hashes only those same two fields. serialize for post-ops (src/common/primitive_serialization.cpp:197) writes only those two as well. For binary_select the third operand is a real input to the post-op, not a spare field, so two attributes that differ only in user_src2_desc hash to the same value and compare equal. The primitive cache key compares and hashes attributes with those functions, so the second create returns the primitive built for the first src2 descriptor. get_desc_hash(const binary_desc_t &) at src/common/primitive_hashing.cpp:393 already special-cases binary_select and hashes src_desc[2]; the post-op path was never given the same treatment.

The fix compares, hashes and serializes user_src2_desc when entry.is_binary_with_ternary_op() is true. Non-ternary binary post-ops keep ignoring user_src2_desc, which is what dnnl_post_ops_append_binary_v2 documents: "If the specified algorithm is not one that requires a ternary input, src2_desc will be ignored."

#5520 carries the same three hunks (its commits 17a324a and 0ff511a) inside the avx512_core select-fusion work, but without a test for the identity itself. This PR isolates the cache-key fix and adds that regression test so it can land on its own; #5520 then rebases onto it with no conflict.

Reproducing

Create an eltwise_forward with a binary_select post-op whose third operand is s8, then create the same primitive with a u8 third operand:

auto make = [&](memory::data_type cond_dt) {
    post_ops ops;
    ops.append_binary(algorithm::binary_select, src1,
            memory::desc({2, 3}, cond_dt, memory::format_tag::ab));
    primitive_attr attr;
    attr.set_post_ops(ops);
    return eltwise_forward(eltwise_forward::primitive_desc(eng,
            prop_kind::forward_inference, algorithm::eltwise_relu, md, md,
            0.f, 0.f, attr));
};
auto p1 = make(memory::data_type::s8);
auto p2 = make(memory::data_type::u8);

dnnl_test_get_primitive_cache_size reports 1 after both creates on main, so p2 is the primitive built for the s8 operand. With this change it reports 2.

Tests

comparison_operators_t.TestBinarySelectPostOp in tests/gtests/internals/test_comparison_operators.cpp is the regression test. It fails on unmodified main with compare(...) Which is: true and passes with the change.

Everything below was run on macOS 15 arm64 with Apple clang 17, configured as cmake -DCMAKE_BUILD_TYPE=Release -DDNNL_AARCH64_USE_ACL=OFF -DONEDNN_BUILD_GRAPH=OFF -DDNNL_BUILD_EXAMPLES=OFF -DDNNL_CPU_RUNTIME=SEQ -DDNNL_BUILD_FOR_CI=ON -DONEDNN_TEST_SET=SMOKE, on top of 6b7f1fb.

Target Result
tests/gtests/internals/test_internals 155 passed, 0 failed
tests/gtests/test_iface_attr 26 passed, 2 skipped (DepthwiseFusion, InnerProdBlockedWeights, skipped on main too)
tests/gtests/test_iface_primitive_cache 8 passed

.github/automation/commit-msg-check.py and scripts/fix_header_guards.py both pass on the commit. I ran clang-format on the four touched files; the only version I have locally is 22, so I kept its output for the new test code and reverted the two hunks it wanted in pre-existing lines of primitive_hashing.cpp and primitive_serialization.cpp that clang-format 18 leaves alone. benchdnn was not run.

Checklist

General

  • Have you formatted the code using clang-format?

Bug fixes

  • Have you included information on how to reproduce the issue (either in a github issue or in this PR)?
  • Have you added relevant regression tests?

post_ops_t::entry_t::operator== and get_attr_hash only looked at the
algorithm and user_src1_desc of a binary post-op, and serialize() wrote
only those two. For binary_select the third operand is a real input, so
two post-ops that differ only in user_src2_desc were treated as the same
attribute. The primitive cache key compares and hashes attributes, so
creating a primitive with a binary_select post-op and then another one
that differs only in the third operand returned the first cached
primitive, built for the wrong src2 memory descriptor.

Compare, hash and serialize user_src2_desc when the entry is a binary
with a ternary operator. Non-ternary binary post-ops keep ignoring
user_src2_desc, as dnnl_post_ops_append_binary_v2 documents.
@MaxFreedomPollard
MaxFreedomPollard requested a review from a team as a code owner September 6, 2026 18:43
@github-actions github-actions Bot added component:tests Codeowner: @oneapi-src/onednn-arch component:common labels Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:common component:tests Codeowner: @oneapi-src/onednn-arch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant