Files
TheRock/rocm-libraries/projects/hipdnn/tests/frontend/IntegrationOverrideValidation.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

581 lines
24 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
// Frontend integration tests for overridable tensor shape validation.
// Detailed rule coverage lives in frontend unit tests for the detail helper.
// This file checks that invalid override payloads reject before backend
// dispatch and that graph flag round-trips survive backend pack/unpack paths.
//
// These integration tests use backend graph lowering and plugin-backed handles;
// GPU-less environments skip them through the common test utility.
#include <gtest/gtest.h>
#include <hip/hip_runtime.h>
#include "OverrideTestUtils.hpp"
#include <hipdnn_data_sdk/utilities/ShapeUtilities.hpp>
#include <hipdnn_frontend.hpp>
#include <hipdnn_test_sdk/utilities/TestUtilities.hpp>
#include <test_plugins/TestPluginCommon.hpp>
#include <test_plugins/TestPluginConstants.hpp>
#include <array>
#include <cstdint>
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#ifdef HIPDNN_ENABLE_SDPA
using namespace hipdnn_frontend;
using namespace hipdnn_frontend::graph;
namespace
{
using ScopedHipdnnHandle
= std::unique_ptr<std::remove_pointer_t<hipdnnHandle_t>, decltype(&hipdnnDestroy)>;
/// Build a minimal pointwise (RELU) graph with the supplied declared strides
/// and `set_override_shape_enabled(true)`. This is the standard "valid graph
/// to override against" used by every validation test below. Thin wrapper
/// over the shared `buildPointwiseReluGraph` helper.
std::shared_ptr<Graph> createOverridableGraph(const std::string& graphName,
const std::vector<int64_t>& declaredDims,
const std::vector<int64_t>& declaredStrides)
{
return hipdnn_tests::override_test_utils::buildPointwiseReluGraph(
graphName, declaredDims, declaredStrides, /*overrideShapeEnabled=*/true);
}
// Bring the shared `compileGraph(graph, handle)` helper into the file's
// anonymous namespace so existing call sites resolve unqualified.
using hipdnn_tests::override_test_utils::compileGraph;
/// Common fixture: load the override-implementing fake (so plan creation
/// succeeds for an override-flag graph) and reset TLS state per test.
class IntegrationOverrideValidationBase : public ::testing::Test
{
protected:
void SetUp() override
{
SKIP_IF_NO_DEVICES();
ASSERT_EQ(hipInit(0), hipSuccess);
int deviceId = 0;
ASSERT_EQ(hipGetDevice(&deviceId), hipSuccess);
// Reset the TLS LastCallRecord across every override-execute fake
// plugin that may be loaded. Plugins not loaded in this test process
// are silently skipped by the no-load symbol lookup.
hipdnn_tests::override_test_utils::resetAllOverrideFakePluginRecords();
_implementingPlugin = std::make_unique<ScopedTestPluginLibrary>(
hipdnn_tests::plugin_constants::testOverrideImplementingPluginPath());
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testOverrideImplementingPluginPath().c_str()};
ASSERT_EQ(hipdnnSetEnginePluginPaths_ext(
paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnCreate(&_handle), HIPDNN_STATUS_SUCCESS);
}
void TearDown() override
{
if(_handle != nullptr)
{
ASSERT_EQ(hipdnnDestroy(_handle), HIPDNN_STATUS_SUCCESS);
_handle = nullptr;
}
_implementingPlugin.reset();
}
/// Convenience: build a 4-D tensor with NCHW packed strides and a tiny
/// device-side allocation so `variantPack` pointer values are valid.
static std::vector<int64_t> packedStrides(const std::vector<int64_t>& dims)
{
return hipdnn_data_sdk::utilities::generateStrides(dims);
}
hipdnnHandle_t _handle = nullptr;
std::unique_ptr<ScopedTestPluginLibrary> _implementingPlugin;
};
/// Helper: invoke the parallel-array overload and assert validation rejects
/// with `ErrorCode::INVALID_VALUE`. The override-implementing fake plugin
/// must NOT have been called (fixture loads only that plugin). When
/// `expectedRuleSubstring` is non-empty, the returned error message must
/// contain it.
void expectArrayRejected(std::shared_ptr<Graph>& graph,
[[maybe_unused]] hipdnnHandle_t handle,
const ScopedTestPluginLibrary& implementingPlugin,
std::unordered_map<int64_t, void*>& variantPack,
const std::vector<int64_t>& uids,
const std::vector<std::vector<int64_t>>& shapes,
const std::vector<std::vector<int64_t>>& strides,
const std::string& expectedRuleSubstring = "")
{
resetLastCallRecord(implementingPlugin, "OverrideImplementing");
auto result = graph->execute(handle, variantPack, nullptr, uids, shapes, strides);
EXPECT_EQ(result.code, ErrorCode::INVALID_VALUE)
<< "Parallel-array overload should have rejected: " << result.err_msg;
if(!expectedRuleSubstring.empty())
{
EXPECT_NE(result.err_msg.find(expectedRuleSubstring), std::string::npos)
<< "Rejection message must identify the failing rule explicitly: " << result.err_msg;
}
const auto* record = getLastCallRecord(implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::NONE)
<< "Validation must reject before any backend call.";
}
/// Helper: invoke the map-keyed overload with the same logical payload and
/// assert it rejects with the same `ErrorCode::INVALID_VALUE`.
void expectMapRejected(std::shared_ptr<Graph>& graph,
[[maybe_unused]] hipdnnHandle_t handle,
const ScopedTestPluginLibrary& implementingPlugin,
std::unordered_map<int64_t, void*>& variantPack,
const std::unordered_map<int64_t, OverrideEntry>& overrides)
{
resetLastCallRecord(implementingPlugin, "OverrideImplementing");
auto result = graph->execute(handle, variantPack, nullptr, overrides);
EXPECT_EQ(result.code, ErrorCode::INVALID_VALUE)
<< "Map overload should have rejected: " << result.err_msg;
const auto* record = getLastCallRecord(implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::NONE)
<< "Validation must reject before any backend call.";
}
} // namespace
// ============================================================================
// Override validation integration smoke tests. Detailed rule coverage lives in
// frontend unit tests for detail/GraphOverrideValidation.hpp.
// ============================================================================
class IntegrationOverrideValidation : public IntegrationOverrideValidationBase
{
};
TEST_F(IntegrationOverrideValidation, ArrayFormInvalidOverrideRejectedBeforeBackend)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = createOverridableGraph("Array_InvalidOverride", dims, packedStrides(dims));
compileGraph(graph, _handle);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
variantPack[2] = nullptr;
const std::vector<int64_t> uids = {999};
const std::vector<std::vector<int64_t>> shapes = {dims};
const std::vector<std::vector<int64_t>> strides = {packedStrides(dims)};
expectArrayRejected(graph, _handle, *_implementingPlugin, variantPack, uids, shapes, strides);
}
TEST_F(IntegrationOverrideValidation, MapFormInvalidOverrideRejectedBeforeBackend)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = createOverridableGraph("Map_InvalidOverride", dims, packedStrides(dims));
compileGraph(graph, _handle);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
variantPack[2] = nullptr;
std::unordered_map<int64_t, OverrideEntry> overrides;
overrides[999] = OverrideEntry{dims, packedStrides(dims)};
expectMapRejected(graph, _handle, *_implementingPlugin, variantPack, overrides);
}
TEST_F(IntegrationOverrideValidation, ValidOverrideReachesPlugin)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = createOverridableGraph("ValidOverride", dims, packedStrides(dims));
compileGraph(graph, _handle);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
variantPack[2] = nullptr;
const std::vector<int64_t> uids = {1};
const std::vector<std::vector<int64_t>> shapes = {dims};
const std::vector<std::vector<int64_t>> strides = {packedStrides(dims)};
resetLastCallRecord(*_implementingPlugin, "OverrideImplementing");
auto result = graph->execute(_handle, variantPack, nullptr, uids, shapes, strides);
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
const auto* record = getLastCallRecord(*_implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::OP_GRAPH_WITH_OVERRIDES);
}
TEST_F(IntegrationOverrideValidation, OverrideUidMissingFromVariantPackRejectedBeforeDispatch)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = createOverridableGraph("MissingVariantPackUid", dims, packedStrides(dims));
compileGraph(graph, _handle);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
const std::vector<int64_t> uids = {2};
const std::vector<std::vector<int64_t>> shapes = {dims};
const std::vector<std::vector<int64_t>> strides = {packedStrides(dims)};
resetLastCallRecord(*_implementingPlugin, "OverrideImplementing");
auto result = graph->execute(_handle, variantPack, nullptr, uids, shapes, strides);
EXPECT_EQ(result.code, ErrorCode::INVALID_VALUE)
<< "Override UID 2 is a graph tensor, but it is absent from the variant pack.";
EXPECT_NE(result.err_msg.find("variant pack"), std::string::npos);
const auto* record = getLastCallRecord(*_implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::NONE);
}
// ============================================================================
// Reject overrides when the graph did not opt in.
// ============================================================================
class IntegrationOverrideValidationFlagAbsent : public ::testing::Test
{
protected:
void SetUp() override
{
SKIP_IF_NO_DEVICES();
ASSERT_EQ(hipInit(0), hipSuccess);
int deviceId = 0;
ASSERT_EQ(hipGetDevice(&deviceId), hipSuccess);
hipdnn_tests::override_test_utils::resetAllOverrideFakePluginRecords();
_implementingPlugin = std::make_unique<ScopedTestPluginLibrary>(
hipdnn_tests::plugin_constants::testOverrideImplementingPluginPath());
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testOverrideImplementingPluginPath().c_str()};
ASSERT_EQ(hipdnnSetEnginePluginPaths_ext(
paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnCreate(&_handle), HIPDNN_STATUS_SUCCESS);
}
void TearDown() override
{
if(_handle != nullptr)
{
ASSERT_EQ(hipdnnDestroy(_handle), HIPDNN_STATUS_SUCCESS);
_handle = nullptr;
}
_implementingPlugin.reset();
}
hipdnnHandle_t _handle = nullptr;
std::unique_ptr<ScopedTestPluginLibrary> _implementingPlugin;
};
/// RFC §7.1: calling the override-execute overload on a graph that did not
/// `set_override_shape_enabled(true)` must reject with `INVALID_VALUE` and
/// must NOT invoke the backend.
TEST_F(IntegrationOverrideValidationFlagAbsent, ArrayFormRejectedWhenFlagAbsent)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
// Build a graph WITHOUT set_override_shape_enabled.
auto graph = hipdnn_tests::override_test_utils::buildPointwiseReluGraph(
"FlagAbsent_Array", dims, /*strides=*/{}, /*overrideShapeEnabled=*/false);
compileGraph(graph, _handle);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
variantPack[2] = nullptr;
const std::vector<int64_t> uids = {1};
const std::vector<std::vector<int64_t>> shapes = {dims};
const std::vector<std::vector<int64_t>> strides
= {hipdnn_data_sdk::utilities::generateStrides(dims)};
resetLastCallRecord(*_implementingPlugin, "OverrideImplementing");
auto result = graph->execute(_handle, variantPack, nullptr, uids, shapes, strides);
EXPECT_EQ(result.code, ErrorCode::INVALID_VALUE)
<< "Override-execute on a graph that did not opt in must reject "
"(RFC §7.1): "
<< result.err_msg;
const auto* record = getLastCallRecord(*_implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::NONE);
}
TEST_F(IntegrationOverrideValidationFlagAbsent, PostBuildToggleDoesNotEnableExistingPlan)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = hipdnn_tests::override_test_utils::buildPointwiseReluGraph(
"FlagAbsent_PostBuildToggle", dims, /*strides=*/{}, /*overrideShapeEnabled=*/false);
compileGraph(graph, _handle);
graph->set_override_shape_enabled(true);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
variantPack[2] = nullptr;
const std::vector<int64_t> uids = {1};
const std::vector<std::vector<int64_t>> shapes = {dims};
const std::vector<std::vector<int64_t>> strides
= {hipdnn_data_sdk::utilities::generateStrides(dims)};
resetLastCallRecord(*_implementingPlugin, "OverrideImplementing");
auto result = graph->execute(_handle, variantPack, nullptr, uids, shapes, strides);
EXPECT_NE(result.code, ErrorCode::OK)
<< "Toggling override shape support after build must not make the existing plan "
"override-capable.";
EXPECT_NE(result.err_msg.find("override"), std::string::npos);
const auto* record = getLastCallRecord(*_implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::NONE);
}
TEST_F(IntegrationOverrideValidationFlagAbsent, MapFormRejectedWhenFlagAbsent)
{
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = hipdnn_tests::override_test_utils::buildPointwiseReluGraph(
"FlagAbsent_Map", dims, /*strides=*/{}, /*overrideShapeEnabled=*/false);
compileGraph(graph, _handle);
std::unordered_map<int64_t, void*> variantPack;
variantPack[1] = nullptr;
variantPack[2] = nullptr;
std::unordered_map<int64_t, OverrideEntry> overrides;
overrides[1] = OverrideEntry{dims, hipdnn_data_sdk::utilities::generateStrides(dims)};
resetLastCallRecord(*_implementingPlugin, "OverrideImplementing");
auto result = graph->execute(_handle, variantPack, nullptr, overrides);
EXPECT_EQ(result.code, ErrorCode::INVALID_VALUE) << result.err_msg;
const auto* record = getLastCallRecord(*_implementingPlugin, "OverrideImplementing");
ASSERT_NE(record, nullptr);
EXPECT_EQ(record->whichEntry, TestPluginExecuteEntry::NONE);
}
// ============================================================================
// Frontend pack/unpack round-trip of `is_override_shape_enabled`.
// ============================================================================
/// Build a graph with `set_override_shape_enabled(true)`, serialize,
/// deserialize, and assert the flag survived.
///
/// Uses `to_binary()` which auto-lowers via the backend descriptor, mirroring
/// the existing `IntegrationGraphLifting` round-trip pattern. Deserialization
/// uses no handle so restored graph finalization is not required.
TEST(IntegrationOverrideRoundTrip, OverrideShapeEnabledFlagSurvivesSerialization)
{
SKIP_IF_NO_DEVICES();
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testGoodPluginPath().c_str()};
ASSERT_EQ(
hipdnnSetEnginePluginPaths_ext(paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
hipdnnHandle_t rawHandle = nullptr;
ASSERT_EQ(hipdnnCreate(&rawHandle), HIPDNN_STATUS_SUCCESS);
const ScopedHipdnnHandle handle(rawHandle, hipdnnDestroy);
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = std::make_shared<Graph>();
graph->set_name("RoundTrip_OverrideShapeEnabled")
.set_io_data_type(DataType::FLOAT)
.set_intermediate_data_type(DataType::FLOAT)
.set_compute_data_type(DataType::FLOAT)
.set_override_shape_enabled(true);
EXPECT_TRUE(graph->is_override_shape_enabled())
<< "Setter must round-trip via the in-memory getter immediately.";
auto x = std::make_shared<TensorAttributes>();
x->set_uid(1).set_dim(dims).set_stride({48, 16, 4, 1}).set_data_type(DataType::FLOAT);
PointwiseAttributes attrs;
attrs.set_mode(PointwiseMode::RELU_FWD);
auto y = graph->pointwise(x, attrs);
y->set_uid(2)
.set_dim(dims)
.set_stride({48, 16, 4, 1})
.set_data_type(DataType::FLOAT)
.set_output(true);
auto result = graph->validate();
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
// Lower to the backend operation graph so `to_binary()` exercises
// `assembleGraphDescriptor()` — without this step the wire round-trip is
// not actually performed.
result = graph->build_operation_graph(handle.get());
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
auto [data, serErr] = graph->to_binary();
ASSERT_TRUE(serErr.is_good()) << serErr.get_message();
auto restored = std::make_shared<Graph>();
auto deserResult = restored->deserialize(nullptr, data);
ASSERT_EQ(deserResult.code, ErrorCode::OK) << deserResult.err_msg;
EXPECT_TRUE(restored->is_override_shape_enabled())
<< "is_override_shape_enabled must survive a serialize/deserialize round-trip.";
}
TEST(IntegrationOverrideRoundTrip, OverrideShapeEnabledFlagSurvivesJsonRoundTrip)
{
SKIP_IF_NO_DEVICES();
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testGoodPluginPath().c_str()};
ASSERT_EQ(
hipdnnSetEnginePluginPaths_ext(paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
hipdnnHandle_t rawHandle = nullptr;
ASSERT_EQ(hipdnnCreate(&rawHandle), HIPDNN_STATUS_SUCCESS);
const ScopedHipdnnHandle handle(rawHandle, hipdnnDestroy);
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = hipdnn_tests::override_test_utils::buildPointwiseReluGraph(
"RoundTrip_OverrideShapeEnabledJson",
dims,
/*strides=*/{},
/*overrideShapeEnabled=*/true);
auto result = graph->validate();
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
result = graph->build_operation_graph(handle.get());
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
auto [json, serErr] = graph->to_json();
ASSERT_TRUE(serErr.is_good()) << serErr.get_message();
auto restored = std::make_shared<Graph>();
auto deserResult = restored->from_json(json);
ASSERT_EQ(deserResult.code, ErrorCode::OK) << deserResult.err_msg;
EXPECT_TRUE(restored->is_override_shape_enabled())
<< "is_override_shape_enabled must survive a JSON serialize/deserialize round-trip.";
}
/// End-to-end check that a graph with the default-false
/// `is_override_shape_enabled` value survives lowering through
/// `build_operation_graph()` plus a full serialize/deserialize round-trip
/// without flipping to true.
TEST(IntegrationOverrideRoundTrip, OverrideShapeEnabledDefaultFalseSurvivesBuildOperationGraph)
{
SKIP_IF_NO_DEVICES();
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testGoodPluginPath().c_str()};
ASSERT_EQ(
hipdnnSetEnginePluginPaths_ext(paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
hipdnnHandle_t rawHandle = nullptr;
ASSERT_EQ(hipdnnCreate(&rawHandle), HIPDNN_STATUS_SUCCESS);
const ScopedHipdnnHandle handle(rawHandle, hipdnnDestroy);
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = std::make_shared<Graph>();
// Intentionally do NOT call set_override_shape_enabled; the default is false.
graph->set_name("BuildOpGraph_DefaultFalse")
.set_io_data_type(DataType::FLOAT)
.set_intermediate_data_type(DataType::FLOAT)
.set_compute_data_type(DataType::FLOAT);
auto x = std::make_shared<TensorAttributes>();
x->set_uid(1).set_dim(dims).set_stride({48, 16, 4, 1}).set_data_type(DataType::FLOAT);
PointwiseAttributes attrs;
attrs.set_mode(PointwiseMode::RELU_FWD);
auto y = graph->pointwise(x, attrs);
y->set_uid(2)
.set_dim(dims)
.set_stride({48, 16, 4, 1})
.set_data_type(DataType::FLOAT)
.set_output(true);
EXPECT_FALSE(graph->is_override_shape_enabled())
<< "Pre-validate: default (unset) must read as false.";
auto result = graph->validate();
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
EXPECT_FALSE(graph->is_override_shape_enabled())
<< "Post-validate: default-false must not flip to true during validation.";
// Critical step the existing default-false round-trip test omits: the
// backend lowering. `assembleGraphDescriptor()` is the path that
// propagates the flag into the backend graph descriptor; a regression
// there is invisible without this call.
result = graph->build_operation_graph(handle.get());
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
EXPECT_FALSE(graph->is_override_shape_enabled())
<< "Post-build_operation_graph: default-false must survive backend "
"lowering.";
auto [data, serErr] = graph->to_binary();
ASSERT_TRUE(serErr.is_good()) << serErr.get_message();
auto restored = std::make_shared<Graph>();
auto deserResult = restored->deserialize(nullptr, data);
ASSERT_EQ(deserResult.code, ErrorCode::OK) << deserResult.err_msg;
EXPECT_FALSE(restored->is_override_shape_enabled())
<< "Default-false must survive build_operation_graph + "
"serialize/deserialize end-to-end.";
}
/// Round-trip the default (unset) case: a graph that never called
/// `set_override_shape_enabled` must deserialize to `false` via the
/// in-memory getter (matches the wire default for legacy graphs).
TEST(IntegrationOverrideRoundTrip, OverrideShapeEnabledDefaultFalseSurvivesSerialization)
{
SKIP_IF_NO_DEVICES();
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testGoodPluginPath().c_str()};
ASSERT_EQ(
hipdnnSetEnginePluginPaths_ext(paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
hipdnnHandle_t rawHandle = nullptr;
ASSERT_EQ(hipdnnCreate(&rawHandle), HIPDNN_STATUS_SUCCESS);
const ScopedHipdnnHandle handle(rawHandle, hipdnnDestroy);
const std::vector<int64_t> dims = {1, 3, 4, 4};
auto graph = std::make_shared<Graph>();
graph->set_name("RoundTrip_DefaultFalse")
.set_io_data_type(DataType::FLOAT)
.set_intermediate_data_type(DataType::FLOAT)
.set_compute_data_type(DataType::FLOAT);
auto x = std::make_shared<TensorAttributes>();
x->set_uid(1).set_dim(dims).set_stride({48, 16, 4, 1}).set_data_type(DataType::FLOAT);
PointwiseAttributes attrs;
attrs.set_mode(PointwiseMode::RELU_FWD);
auto y = graph->pointwise(x, attrs);
y->set_uid(2)
.set_dim(dims)
.set_stride({48, 16, 4, 1})
.set_data_type(DataType::FLOAT)
.set_output(true);
EXPECT_FALSE(graph->is_override_shape_enabled())
<< "Default (unset) must read as false from the in-memory getter.";
auto result = graph->validate();
ASSERT_EQ(result.code, ErrorCode::OK) << result.err_msg;
auto [data, serErr] = graph->to_binary();
ASSERT_TRUE(serErr.is_good()) << serErr.get_message();
auto restored = std::make_shared<Graph>();
auto deserResult = restored->deserialize(nullptr, data);
ASSERT_EQ(deserResult.code, ErrorCode::OK) << deserResult.err_msg;
EXPECT_FALSE(restored->is_override_shape_enabled())
<< "Default-false must survive serialize/deserialize as false.";
}
#endif // HIPDNN_ENABLE_SDPA