Skip to content

[wip] Deprecate constant buffer#19007

Open
lucylq wants to merge 1 commit intomainfrom
lfq.deprecate-constant-buffer
Open

[wip] Deprecate constant buffer#19007
lucylq wants to merge 1 commit intomainfrom
lfq.deprecate-constant-buffer

Conversation

@lucylq
Copy link
Copy Markdown
Contributor

@lucylq lucylq commented Apr 20, 2026

It's been deprecated on AoT side since et 0.7. Let's try to deprecate it fully; it is also causing some security risk right now.

Import to check internal failures.

@pytorch-bot
Copy link
Copy Markdown

pytorch-bot Bot commented Apr 20, 2026

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/19007

Note: Links to docs will display an error until the docs builds have been completed.

❌ 6 New Failures

As of commit 7108ed7 with merge base 8ed6e85 (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Apr 20, 2026
@github-actions
Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@lucylq lucylq force-pushed the lfq.deprecate-constant-buffer branch from 38c53e4 to 7108ed7 Compare April 20, 2026 21:32
@lucylq
Copy link
Copy Markdown
Contributor Author

lucylq commented Apr 20, 2026

@claude review

@lucylq lucylq changed the title Deprecate constant buffer [wip] Deprecate constant buffer Apr 20, 2026
@lucylq lucylq marked this pull request as ready for review April 20, 2026 21:36
@lucylq lucylq requested a review from JacobSzwejbka as a code owner April 20, 2026 21:36
Copilot AI review requested due to automatic review settings April 20, 2026 21:36
@claude
Copy link
Copy Markdown

claude Bot commented Apr 20, 2026

Claude finished @lucylq's task in 1m 22s —— View job


Review of PR #19007: Deprecate constant buffer

  • Read the diff and changed files
  • Review runtime/executor/program.cpp changes
  • Review runtime/executor/test/method_test.cpp changes
  • Review runtime/executor/test/program_test.cpp changes
  • Post review feedback

Good cleanup. The change removes the deprecated inline constant_buffer path (deprecated since 0.7) and enforces that constants must live in a separate segment. The code is simpler and the security concern around the inline path is addressed. A few observations:

Potential edge case: offsets()->size() == 0

In program.cpp:238-245, the new code requires constant_segment != nullptr && constant_segment->offsets() != nullptr, then checks offsets()->size() == 1 as the "no constants" case. If offsets()->size() == 0, the code falls through to the segment-loading path (line 254+), where it would attempt to load a constant segment even though there are no constant offsets.

The old code handled this differently: size() > 0 was the outer guard, so size() == 0 would go to the deprecated else branch.

If the AoT serializer guarantees at least one placeholder offset entry (which the comment at line 242-244 suggests), this is likely unreachable, but it might be worth either:

  • Checking size() <= 1 instead of == 1 for the early return, or
  • Adding a comment that size() == 0 is invalid / unreachable

get_constant_buffer_data defensive null check may be unreachable

In program.cpp:391-394, the new check constant_segment_data_.data() != nullptr returns InvalidProgram. After this PR, Program::load already rejects any program without a valid constant_segment. The only way to reach this with data() == nullptr is the offsets()->size() == 1 early-return path (no constants). In that case, calling get_constant_buffer_data with buffer_index=0 would hit this check, which is correct behavior — but the error message "Program has no constant segment" is slightly misleading since the program does have a constant segment, it just has no actual constant data. Minor nit.

Docstring in program.h:104 still says "constant_buffer"

program.h:104: the docstring for get_constant_buffer_data still references constant_buffer:

@param[in] buffer_idx the index of the buffer in the constant_buffer.

Consider updating this to say "constant_segment" to match the new reality. Fix this →

Test changes look correct

  • method_test.cpp: Removal of ConstantBufferTest and the deprecated linear_constant_buffer program loading is expected since that path is now rejected.
  • program_test.cpp: RejectsDeprecatedInlineConstantBuffer properly validates that loading a PTE with inline constants returns Error::InvalidProgram. The GetConstantBufferDataRejectsOversizedRequest test now uses add_loader_ instead of the deprecated constant-buffer PTE — good, since the add model uses the segment path.

Summary

The PR is a clean removal of a deprecated code path. The main suggestion is the edge case around offsets()->size() == 0 and the stale docstring in program.h.


@meta-codesync
Copy link
Copy Markdown
Contributor

meta-codesync Bot commented Apr 20, 2026

@lucylq has imported this pull request. If you are a Meta employee, you can view this in D101701124.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to fully deprecate/reject the legacy inline constant storage (Program.constant_buffer) in the runtime loader, aligning runtime behavior with the AoT-side deprecation (since ExecuTorch 0.7) and reducing exposure to potentially unsafe legacy parsing paths.

Changes:

  • Make Program::load() require constants to come from constant_segment and reject deprecated inline constant_buffer programs.
  • Simplify Program::get_constant_buffer_data() to only read from the loaded constant segment.
  • Update executor tests to remove execution of legacy constant-buffer programs and add a load-time rejection test.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
runtime/executor/program.cpp Removes the deprecated inline-constant branch at load time and enforces constant-segment-only retrieval.
runtime/executor/test/program_test.cpp Replaces legacy constant-buffer compatibility test with a load-time rejection test; adjusts constant-data bounds test.
runtime/executor/test/method_test.cpp Stops loading/executing the deprecated constant-buffer test module.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +236 to 246
// Constant data lives in a separate segment (constant_segment).
const auto* constant_segment = flatbuffer_program->constant_segment();
if (constant_segment != nullptr && constant_segment->offsets() != nullptr &&
constant_segment->offsets()->size() > 0) {
if (constant_segment->offsets()->size() == 1) {
// No constants; the constant segment is empty and does not
// need to be loaded.
return Program(
loader,
segment_base_offset,
std::move(program_data.get()),
flatbuffer_program,
/*constant_segment_data=*/FreeableBuffer{},
std::move(pte_data_map));
}
// The constant data is inside a separate segment.
const auto* constant_buffer = flatbuffer_program->constant_buffer();
ET_CHECK_OR_RETURN_ERROR(
constant_buffer == nullptr || constant_buffer->size() == 0,
InvalidProgram,
"constant_buffer contains %zu items, "
"constant_segment.offsets contains %zu items. Only one should be used.",
static_cast<size_t>(constant_buffer->size()),
static_cast<size_t>(constant_segment->offsets()->size()));
const auto* segments = flatbuffer_program->segments();
ET_CHECK_OR_RETURN_ERROR(
segments != nullptr, InvalidProgram, "No segments in program");

// Load constant segment.
// TODO(T171839323): Add test for segment_index > num available segments.
ET_CHECK_OR_RETURN_ERROR(
constant_segment->segment_index() < segments->size(),
InvalidProgram,
"Constant segment index %zu invalid for program segments range %zu",
static_cast<size_t>(constant_segment->segment_index()),
static_cast<size_t>(segments->size()));

const executorch_flatbuffer::DataSegment* data_segment =
segments->Get(constant_segment->segment_index());
Result<FreeableBuffer> constant_segment_data = loader->load(
segment_base_offset + data_segment->offset(),
data_segment->size(),
DataLoader::SegmentInfo(
DataLoader::SegmentInfo::Type::Constant,
constant_segment->segment_index()));
if (!constant_segment_data.ok()) {
return constant_segment_data.error();
}
// The FreeableBuffer owns the data that flatbuffer_program points into.
// Also keep a pointer to the loader so it can load more segments when
// necessary.
return Program(
loader,
segment_base_offset,
std::move(program_data.get()),
flatbuffer_program,
std::move(constant_segment_data.get()),
std::move(pte_data_map));
} else {
// The constant data is stored inside the flatbuffer, so this program does
// not contain a separate segment for it.

// NOTE: This branch is deprecated from ExecuTorch 0.7 onwards.
// Please regenerate your PTE file to ensure newer ExecuTorch runtimes can
// support it. ExecuTorch deprecation policy:
// https://docs.pytorch.org/executorch/stable/api-life-cycle.html#deprecation-policy.
// For support, contact the PyTorch Edge team or make an issue in:
// https://github.com/pytorch/executorch/issues.
ET_LOG(
Error,
"!!DEPRECATED!! This branch is deprecated from ExecuTorch 0.7; re-export this PTE file to ensure support on newer runtimes.");
ET_CHECK_OR_RETURN_ERROR(
constant_segment != nullptr && constant_segment->offsets() != nullptr,
InvalidProgram,
"Program constant segment is nullptr. Expect at least one constant placeholder.");
// The offsets list will always contain a placeholder value 0 for non-const
// tensors. If this is the only offset, the constant segment is empty and
// does not need to be loaded.
if (constant_segment->offsets()->size() == 1) {
return Program(
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

In Program::load(), the early return for constant_segment->offsets()->size() == 1 happens before validating Program.constant_buffer is empty. This means a malformed/corrupt PTE could still include deprecated inline constant_buffer data and not be rejected. Also consider explicitly rejecting offsets()->size() == 0 (no placeholder) since the loader currently treats it as “has constants” and attempts to load a segment, which contradicts the placeholder invariant used elsewhere/tests.

Copilot uses AI. Check for mistakes.
Comment on lines +388 to +405
// Constant data must live in a separate segment (constant_segment_data) that
// was loaded during Program::load. The deprecated inline
// Program.constant_buffer path is rejected at load time.
ET_CHECK_OR_RETURN_ERROR(
constant_segment_data_.data() != nullptr,
InvalidProgram,
"Program has no constant segment; cannot retrieve constant data.");

const auto* constant_segment = internal_program->constant_segment();
size_t num_elems = constant_segment == nullptr
? 0
: (constant_segment->offsets() == nullptr
? 0
: constant_segment->offsets()->size());
ET_CHECK_OR_RETURN_ERROR(
buffer_index < num_elems,
InvalidArgument,
"Constant segment buffer index %zu invalid for program constant segment range %zu",
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

Program::get_constant_buffer_data() now hard-fails with InvalidProgram when constant_segment_data_.data() == nullptr. However, valid programs with an empty constant segment (segment size 0 / offsets only placeholder) will naturally have constant_segment_data_ empty/null (and Program::load() intentionally doesn’t load it). Consider using constant_segment.offsets()->size() to distinguish “no constants / invalid buffer_index” (should be InvalidArgument) from “constants expected but segment data missing” (could be InvalidProgram).

Copilot uses AI. Check for mistakes.
Comment on lines 850 to 857
TEST_F(ProgramTest, GetConstantBufferDataRejectsOversizedRequest) {
const char* path =
std::getenv("DEPRECATED_ET_MODULE_LINEAR_CONSTANT_BUFFER_PATH");
Result<FileDataLoader> loader = FileDataLoader::from(path);
ASSERT_EQ(loader.error(), Error::Ok);

Result<Program> program = Program::load(&loader.get());
Result<Program> program = Program::load(add_loader_.get());
ASSERT_EQ(program.error(), Error::Ok);

// Request way more bytes than any buffer could contain
// Request way more bytes than the constant segment could contain.
Result<const void*> data = ProgramTestFriend::get_constant_buffer_data(
&program.get(), /*buffer_index=*/0, /*nbytes=*/SIZE_MAX);

Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

This test now loads add_loader_ (ET_MODULE_ADD), which earlier tests indicate has no constants (constant segment is only a placeholder / size 0). With the new runtime behavior, get_constant_buffer_data() will return InvalidProgram (no segment data) rather than exercising the oversized-request bounds check. Use a module that actually has constant-segment data (e.g., ET_MODULE_ADD_MUL_PATH) and a non-placeholder buffer_index (typically 1) to keep this test asserting the intended behavior.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants