1*523fa7a6SAndroid Build Coastguard Worker /*
2*523fa7a6SAndroid Build Coastguard Worker * Copyright (c) Meta Platforms, Inc. and affiliates.
3*523fa7a6SAndroid Build Coastguard Worker * All rights reserved.
4*523fa7a6SAndroid Build Coastguard Worker *
5*523fa7a6SAndroid Build Coastguard Worker * This source code is licensed under the BSD-style license found in the
6*523fa7a6SAndroid Build Coastguard Worker * LICENSE file in the root directory of this source tree.
7*523fa7a6SAndroid Build Coastguard Worker */
8*523fa7a6SAndroid Build Coastguard Worker
9*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/executor/program.h>
10*523fa7a6SAndroid Build Coastguard Worker
11*523fa7a6SAndroid Build Coastguard Worker #include <cstddef>
12*523fa7a6SAndroid Build Coastguard Worker #include <cstdint>
13*523fa7a6SAndroid Build Coastguard Worker
14*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/core/event_tracer_hooks.h>
15*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/executor/memory_manager.h>
16*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/executor/method.h>
17*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/platform/profiler.h>
18*523fa7a6SAndroid Build Coastguard Worker #include <executorch/schema/extended_header.h>
19*523fa7a6SAndroid Build Coastguard Worker #include <executorch/schema/program_generated.h>
20*523fa7a6SAndroid Build Coastguard Worker
21*523fa7a6SAndroid Build Coastguard Worker /*
22*523fa7a6SAndroid Build Coastguard Worker * Program verification can increase code size by ~30k. Targets that need to
23*523fa7a6SAndroid Build Coastguard Worker * save this space can avoid building it by passing
24*523fa7a6SAndroid Build Coastguard Worker * -DET_ENABLE_PROGRAM_VERIFICATION=0 on the compile line.
25*523fa7a6SAndroid Build Coastguard Worker */
26*523fa7a6SAndroid Build Coastguard Worker #ifndef ET_ENABLE_PROGRAM_VERIFICATION
27*523fa7a6SAndroid Build Coastguard Worker #define ET_ENABLE_PROGRAM_VERIFICATION 1
28*523fa7a6SAndroid Build Coastguard Worker #endif
29*523fa7a6SAndroid Build Coastguard Worker
30*523fa7a6SAndroid Build Coastguard Worker namespace executorch {
31*523fa7a6SAndroid Build Coastguard Worker namespace runtime {
32*523fa7a6SAndroid Build Coastguard Worker
33*523fa7a6SAndroid Build Coastguard Worker namespace {
34*523fa7a6SAndroid Build Coastguard Worker
35*523fa7a6SAndroid Build Coastguard Worker /**
36*523fa7a6SAndroid Build Coastguard Worker * Program data must be aligned to this value to properly parse it. Must be a
37*523fa7a6SAndroid Build Coastguard Worker * power of 2. Note that max_align_t is the alignment that malloc() and new
38*523fa7a6SAndroid Build Coastguard Worker * guarantee.
39*523fa7a6SAndroid Build Coastguard Worker */
40*523fa7a6SAndroid Build Coastguard Worker constexpr size_t kMinimumAlignment = alignof(std::max_align_t);
41*523fa7a6SAndroid Build Coastguard Worker
IsAligned(const void * data)42*523fa7a6SAndroid Build Coastguard Worker bool IsAligned(const void* data) {
43*523fa7a6SAndroid Build Coastguard Worker uintptr_t addr = reinterpret_cast<uintptr_t>(data);
44*523fa7a6SAndroid Build Coastguard Worker return addr % kMinimumAlignment == 0;
45*523fa7a6SAndroid Build Coastguard Worker }
46*523fa7a6SAndroid Build Coastguard Worker
get_execution_plan(const executorch_flatbuffer::Program * program,const char * method_name)47*523fa7a6SAndroid Build Coastguard Worker Result<executorch_flatbuffer::ExecutionPlan*> get_execution_plan(
48*523fa7a6SAndroid Build Coastguard Worker const executorch_flatbuffer::Program* program,
49*523fa7a6SAndroid Build Coastguard Worker const char* method_name) {
50*523fa7a6SAndroid Build Coastguard Worker auto execution_plans = program->execution_plan();
51*523fa7a6SAndroid Build Coastguard Worker for (size_t i = 0; i < execution_plans->size(); i++) {
52*523fa7a6SAndroid Build Coastguard Worker auto plan = execution_plans->GetMutableObject(i);
53*523fa7a6SAndroid Build Coastguard Worker if (std::strcmp(plan->name()->c_str(), method_name) == 0) {
54*523fa7a6SAndroid Build Coastguard Worker return plan;
55*523fa7a6SAndroid Build Coastguard Worker }
56*523fa7a6SAndroid Build Coastguard Worker }
57*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Error, "No method named '%s' in program", method_name);
58*523fa7a6SAndroid Build Coastguard Worker return Error::InvalidArgument;
59*523fa7a6SAndroid Build Coastguard Worker }
60*523fa7a6SAndroid Build Coastguard Worker
61*523fa7a6SAndroid Build Coastguard Worker } // namespace
62*523fa7a6SAndroid Build Coastguard Worker
load(DataLoader * loader,Program::Verification verification)63*523fa7a6SAndroid Build Coastguard Worker /* static */ Result<Program> Program::load(
64*523fa7a6SAndroid Build Coastguard Worker DataLoader* loader,
65*523fa7a6SAndroid Build Coastguard Worker Program::Verification verification) {
66*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_SCOPE_PROF("Program::load");
67*523fa7a6SAndroid Build Coastguard Worker
68*523fa7a6SAndroid Build Coastguard Worker // See if the program size is in the header.
69*523fa7a6SAndroid Build Coastguard Worker size_t program_size = 0;
70*523fa7a6SAndroid Build Coastguard Worker size_t segment_base_offset = 0;
71*523fa7a6SAndroid Build Coastguard Worker {
72*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_SCOPE_PROF("Program::check_header");
73*523fa7a6SAndroid Build Coastguard Worker Result<FreeableBuffer> header = loader->load(
74*523fa7a6SAndroid Build Coastguard Worker /*offset=*/0,
75*523fa7a6SAndroid Build Coastguard Worker ExtendedHeader::kNumHeadBytes,
76*523fa7a6SAndroid Build Coastguard Worker DataLoader::SegmentInfo(DataLoader::SegmentInfo::Type::Program));
77*523fa7a6SAndroid Build Coastguard Worker if (!header.ok()) {
78*523fa7a6SAndroid Build Coastguard Worker return header.error();
79*523fa7a6SAndroid Build Coastguard Worker }
80*523fa7a6SAndroid Build Coastguard Worker Result<ExtendedHeader> eh =
81*523fa7a6SAndroid Build Coastguard Worker ExtendedHeader::Parse(header->data(), header->size());
82*523fa7a6SAndroid Build Coastguard Worker if (eh.ok()) {
83*523fa7a6SAndroid Build Coastguard Worker // The header has the program size.
84*523fa7a6SAndroid Build Coastguard Worker program_size = eh->program_size;
85*523fa7a6SAndroid Build Coastguard Worker segment_base_offset = eh->segment_base_offset;
86*523fa7a6SAndroid Build Coastguard Worker } else if (eh.error() == Error::NotFound) {
87*523fa7a6SAndroid Build Coastguard Worker // No header; the program consumes the whole file, and there are no
88*523fa7a6SAndroid Build Coastguard Worker // segments.
89*523fa7a6SAndroid Build Coastguard Worker auto result = loader->size();
90*523fa7a6SAndroid Build Coastguard Worker if (!result.ok()) {
91*523fa7a6SAndroid Build Coastguard Worker return result.error();
92*523fa7a6SAndroid Build Coastguard Worker }
93*523fa7a6SAndroid Build Coastguard Worker program_size = result.get();
94*523fa7a6SAndroid Build Coastguard Worker } else {
95*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Error, "Extended header may be corrupt");
96*523fa7a6SAndroid Build Coastguard Worker return eh.error();
97*523fa7a6SAndroid Build Coastguard Worker }
98*523fa7a6SAndroid Build Coastguard Worker }
99*523fa7a6SAndroid Build Coastguard Worker
100*523fa7a6SAndroid Build Coastguard Worker // Load the flatbuffer data as a segment.
101*523fa7a6SAndroid Build Coastguard Worker uint32_t prof_tok = EXECUTORCH_BEGIN_PROF("Program::load_data");
102*523fa7a6SAndroid Build Coastguard Worker Result<FreeableBuffer> program_data = loader->load(
103*523fa7a6SAndroid Build Coastguard Worker /*offset=*/0,
104*523fa7a6SAndroid Build Coastguard Worker program_size,
105*523fa7a6SAndroid Build Coastguard Worker DataLoader::SegmentInfo(DataLoader::SegmentInfo::Type::Program));
106*523fa7a6SAndroid Build Coastguard Worker if (!program_data.ok()) {
107*523fa7a6SAndroid Build Coastguard Worker return program_data.error();
108*523fa7a6SAndroid Build Coastguard Worker }
109*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_END_PROF(prof_tok);
110*523fa7a6SAndroid Build Coastguard Worker
111*523fa7a6SAndroid Build Coastguard Worker // Make sure the magic header matches the expected version.
112*523fa7a6SAndroid Build Coastguard Worker if (!executorch_flatbuffer::ProgramBufferHasIdentifier(
113*523fa7a6SAndroid Build Coastguard Worker program_data->data())) {
114*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
115*523fa7a6SAndroid Build Coastguard Worker Error,
116*523fa7a6SAndroid Build Coastguard Worker "Program identifier '%.4s' != expected '%.4s'",
117*523fa7a6SAndroid Build Coastguard Worker flatbuffers::GetBufferIdentifier(program_data->data()),
118*523fa7a6SAndroid Build Coastguard Worker executorch_flatbuffer::ProgramIdentifier());
119*523fa7a6SAndroid Build Coastguard Worker return Error::InvalidProgram;
120*523fa7a6SAndroid Build Coastguard Worker }
121*523fa7a6SAndroid Build Coastguard Worker
122*523fa7a6SAndroid Build Coastguard Worker // Do extra verification if requested.
123*523fa7a6SAndroid Build Coastguard Worker if (verification == Verification::InternalConsistency) {
124*523fa7a6SAndroid Build Coastguard Worker #if ET_ENABLE_PROGRAM_VERIFICATION
125*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_SCOPE_PROF("Program::verify_internal_consistency");
126*523fa7a6SAndroid Build Coastguard Worker flatbuffers::Verifier verifier(
127*523fa7a6SAndroid Build Coastguard Worker reinterpret_cast<const uint8_t*>(program_data->data()),
128*523fa7a6SAndroid Build Coastguard Worker program_data->size());
129*523fa7a6SAndroid Build Coastguard Worker bool ok = executorch_flatbuffer::VerifyProgramBuffer(verifier);
130*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
131*523fa7a6SAndroid Build Coastguard Worker ok,
132*523fa7a6SAndroid Build Coastguard Worker InvalidProgram,
133*523fa7a6SAndroid Build Coastguard Worker "Verification failed; data may be truncated or corrupt");
134*523fa7a6SAndroid Build Coastguard Worker #else
135*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
136*523fa7a6SAndroid Build Coastguard Worker Info, "InternalConsistency verification requested but not available");
137*523fa7a6SAndroid Build Coastguard Worker #endif
138*523fa7a6SAndroid Build Coastguard Worker }
139*523fa7a6SAndroid Build Coastguard Worker
140*523fa7a6SAndroid Build Coastguard Worker // The flatbuffer data must start at an aligned address to ensure internal
141*523fa7a6SAndroid Build Coastguard Worker // alignment of flatbuffer fields.
142*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
143*523fa7a6SAndroid Build Coastguard Worker IsAligned(program_data->data()),
144*523fa7a6SAndroid Build Coastguard Worker InvalidArgument,
145*523fa7a6SAndroid Build Coastguard Worker "Program data 0x%p must be aligned to %zu",
146*523fa7a6SAndroid Build Coastguard Worker program_data->data(),
147*523fa7a6SAndroid Build Coastguard Worker kMinimumAlignment);
148*523fa7a6SAndroid Build Coastguard Worker
149*523fa7a6SAndroid Build Coastguard Worker // Get the pointer to the root flatbuffer table.
150*523fa7a6SAndroid Build Coastguard Worker const executorch_flatbuffer::Program* flatbuffer_program =
151*523fa7a6SAndroid Build Coastguard Worker executorch_flatbuffer::GetProgram(program_data->data());
152*523fa7a6SAndroid Build Coastguard Worker
153*523fa7a6SAndroid Build Coastguard Worker // Constant data may live inside the flatbuffer data (constant_buffer) or in a
154*523fa7a6SAndroid Build Coastguard Worker // separate segment (constant_segment). It should not be in both.
155*523fa7a6SAndroid Build Coastguard Worker // Check constant_segment->offsets()->size() > 1, as the offsets list will
156*523fa7a6SAndroid Build Coastguard Worker // always contain a placeholder value 0 for non-const tensors. If this is the
157*523fa7a6SAndroid Build Coastguard Worker // only offset, the constant segment is empty and does not need to be loaded.
158*523fa7a6SAndroid Build Coastguard Worker const auto* constant_segment = flatbuffer_program->constant_segment();
159*523fa7a6SAndroid Build Coastguard Worker if (constant_segment != nullptr && constant_segment->offsets() != nullptr &&
160*523fa7a6SAndroid Build Coastguard Worker constant_segment->offsets()->size() > 1) {
161*523fa7a6SAndroid Build Coastguard Worker // The constant data is inside a separate segment.
162*523fa7a6SAndroid Build Coastguard Worker const auto* constant_buffer = flatbuffer_program->constant_buffer();
163*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
164*523fa7a6SAndroid Build Coastguard Worker constant_buffer == nullptr || constant_buffer->size() == 0,
165*523fa7a6SAndroid Build Coastguard Worker InvalidProgram,
166*523fa7a6SAndroid Build Coastguard Worker "constant_buffer contains %u items, "
167*523fa7a6SAndroid Build Coastguard Worker "constant_segment.offsets contains %u items. Only one should be used.",
168*523fa7a6SAndroid Build Coastguard Worker constant_buffer->size(),
169*523fa7a6SAndroid Build Coastguard Worker constant_segment->offsets()->size());
170*523fa7a6SAndroid Build Coastguard Worker const auto* segments = flatbuffer_program->segments();
171*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
172*523fa7a6SAndroid Build Coastguard Worker segments != nullptr, InvalidProgram, "No segments in program");
173*523fa7a6SAndroid Build Coastguard Worker
174*523fa7a6SAndroid Build Coastguard Worker // Load constant segment.
175*523fa7a6SAndroid Build Coastguard Worker // TODO(T171839323): Add test for segment_index > num available segments.
176*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
177*523fa7a6SAndroid Build Coastguard Worker constant_segment->segment_index() < segments->size(),
178*523fa7a6SAndroid Build Coastguard Worker InvalidProgram,
179*523fa7a6SAndroid Build Coastguard Worker "Constant segment index %d invalid for program segments range %d",
180*523fa7a6SAndroid Build Coastguard Worker constant_segment->segment_index(),
181*523fa7a6SAndroid Build Coastguard Worker segments->size());
182*523fa7a6SAndroid Build Coastguard Worker
183*523fa7a6SAndroid Build Coastguard Worker const executorch_flatbuffer::DataSegment* data_segment =
184*523fa7a6SAndroid Build Coastguard Worker segments->Get(constant_segment->segment_index());
185*523fa7a6SAndroid Build Coastguard Worker Result<FreeableBuffer> constant_segment_data = loader->load(
186*523fa7a6SAndroid Build Coastguard Worker segment_base_offset + data_segment->offset(),
187*523fa7a6SAndroid Build Coastguard Worker data_segment->size(),
188*523fa7a6SAndroid Build Coastguard Worker DataLoader::SegmentInfo(
189*523fa7a6SAndroid Build Coastguard Worker DataLoader::SegmentInfo::Type::Constant,
190*523fa7a6SAndroid Build Coastguard Worker constant_segment->segment_index()));
191*523fa7a6SAndroid Build Coastguard Worker if (!constant_segment_data.ok()) {
192*523fa7a6SAndroid Build Coastguard Worker return constant_segment_data.error();
193*523fa7a6SAndroid Build Coastguard Worker }
194*523fa7a6SAndroid Build Coastguard Worker // The FreeableBuffer owns the data that flatbuffer_program points into.
195*523fa7a6SAndroid Build Coastguard Worker // Also keep a pointer to the loader so it can load more segments when
196*523fa7a6SAndroid Build Coastguard Worker // necessary.
197*523fa7a6SAndroid Build Coastguard Worker return Program(
198*523fa7a6SAndroid Build Coastguard Worker loader,
199*523fa7a6SAndroid Build Coastguard Worker segment_base_offset,
200*523fa7a6SAndroid Build Coastguard Worker std::move(program_data.get()),
201*523fa7a6SAndroid Build Coastguard Worker flatbuffer_program,
202*523fa7a6SAndroid Build Coastguard Worker std::move(constant_segment_data.get()));
203*523fa7a6SAndroid Build Coastguard Worker } else {
204*523fa7a6SAndroid Build Coastguard Worker // The constant data is stored inside the flatbuffer, so this program does
205*523fa7a6SAndroid Build Coastguard Worker // not contain a separate segment for it.
206*523fa7a6SAndroid Build Coastguard Worker return Program(
207*523fa7a6SAndroid Build Coastguard Worker loader,
208*523fa7a6SAndroid Build Coastguard Worker segment_base_offset,
209*523fa7a6SAndroid Build Coastguard Worker std::move(program_data.get()),
210*523fa7a6SAndroid Build Coastguard Worker flatbuffer_program,
211*523fa7a6SAndroid Build Coastguard Worker /*constant_segment_data=*/FreeableBuffer{});
212*523fa7a6SAndroid Build Coastguard Worker }
213*523fa7a6SAndroid Build Coastguard Worker }
214*523fa7a6SAndroid Build Coastguard Worker
num_methods() const215*523fa7a6SAndroid Build Coastguard Worker size_t Program::num_methods() const {
216*523fa7a6SAndroid Build Coastguard Worker auto internal_program =
217*523fa7a6SAndroid Build Coastguard Worker static_cast<const executorch_flatbuffer::Program*>(internal_program_);
218*523fa7a6SAndroid Build Coastguard Worker const auto execution_plan = internal_program->execution_plan();
219*523fa7a6SAndroid Build Coastguard Worker if (execution_plan != nullptr) {
220*523fa7a6SAndroid Build Coastguard Worker return execution_plan->size();
221*523fa7a6SAndroid Build Coastguard Worker } else {
222*523fa7a6SAndroid Build Coastguard Worker return 0;
223*523fa7a6SAndroid Build Coastguard Worker }
224*523fa7a6SAndroid Build Coastguard Worker }
225*523fa7a6SAndroid Build Coastguard Worker
get_method_name(size_t plan_index) const226*523fa7a6SAndroid Build Coastguard Worker Result<const char*> Program::get_method_name(size_t plan_index) const {
227*523fa7a6SAndroid Build Coastguard Worker if (plan_index >= this->num_methods()) {
228*523fa7a6SAndroid Build Coastguard Worker return Error::InvalidArgument;
229*523fa7a6SAndroid Build Coastguard Worker }
230*523fa7a6SAndroid Build Coastguard Worker auto internal_program =
231*523fa7a6SAndroid Build Coastguard Worker static_cast<const executorch_flatbuffer::Program*>(internal_program_);
232*523fa7a6SAndroid Build Coastguard Worker // We know that the execution plan exists because num_methods() returned > 0.
233*523fa7a6SAndroid Build Coastguard Worker auto name = internal_program->execution_plan()->Get(plan_index)->name();
234*523fa7a6SAndroid Build Coastguard Worker if (name == nullptr) {
235*523fa7a6SAndroid Build Coastguard Worker return Error::InvalidProgram;
236*523fa7a6SAndroid Build Coastguard Worker }
237*523fa7a6SAndroid Build Coastguard Worker return name->c_str();
238*523fa7a6SAndroid Build Coastguard Worker }
239*523fa7a6SAndroid Build Coastguard Worker
load_method(const char * method_name,MemoryManager * memory_manager,EventTracer * event_tracer) const240*523fa7a6SAndroid Build Coastguard Worker Result<Method> Program::load_method(
241*523fa7a6SAndroid Build Coastguard Worker const char* method_name,
242*523fa7a6SAndroid Build Coastguard Worker MemoryManager* memory_manager,
243*523fa7a6SAndroid Build Coastguard Worker EventTracer* event_tracer) const {
244*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_SCOPE_PROF("Program::load_method");
245*523fa7a6SAndroid Build Coastguard Worker internal::event_tracer_create_event_block(event_tracer, "Default");
246*523fa7a6SAndroid Build Coastguard Worker internal::EventTracerProfileMethodScope event_tracer_scope =
247*523fa7a6SAndroid Build Coastguard Worker internal::EventTracerProfileMethodScope(
248*523fa7a6SAndroid Build Coastguard Worker event_tracer, "Program::load_method");
249*523fa7a6SAndroid Build Coastguard Worker // If we can't create a MethodMeta for the Method, the Method is corrupt;
250*523fa7a6SAndroid Build Coastguard Worker // Method::method_meta() assumes success, so we must fail here.
251*523fa7a6SAndroid Build Coastguard Worker Result<MethodMeta> meta = method_meta(method_name);
252*523fa7a6SAndroid Build Coastguard Worker if (!meta.ok()) {
253*523fa7a6SAndroid Build Coastguard Worker return meta.error();
254*523fa7a6SAndroid Build Coastguard Worker }
255*523fa7a6SAndroid Build Coastguard Worker
256*523fa7a6SAndroid Build Coastguard Worker auto plan = get_execution_plan(internal_program_, method_name);
257*523fa7a6SAndroid Build Coastguard Worker if (!plan.ok()) {
258*523fa7a6SAndroid Build Coastguard Worker return plan.error();
259*523fa7a6SAndroid Build Coastguard Worker }
260*523fa7a6SAndroid Build Coastguard Worker return Method::load(plan.get(), this, memory_manager, event_tracer);
261*523fa7a6SAndroid Build Coastguard Worker }
262*523fa7a6SAndroid Build Coastguard Worker
method_meta(const char * method_name) const263*523fa7a6SAndroid Build Coastguard Worker Result<MethodMeta> Program::method_meta(const char* method_name) const {
264*523fa7a6SAndroid Build Coastguard Worker auto plan = get_execution_plan(internal_program_, method_name);
265*523fa7a6SAndroid Build Coastguard Worker if (!plan.ok()) {
266*523fa7a6SAndroid Build Coastguard Worker return plan.error();
267*523fa7a6SAndroid Build Coastguard Worker }
268*523fa7a6SAndroid Build Coastguard Worker // Check any fields whose accessors don't return Result<> in case they're
269*523fa7a6SAndroid Build Coastguard Worker // missing or corrupt.
270*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
271*523fa7a6SAndroid Build Coastguard Worker plan.get()->name() != nullptr, InvalidProgram, "Missing name field");
272*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
273*523fa7a6SAndroid Build Coastguard Worker plan.get()->non_const_buffer_sizes() != nullptr,
274*523fa7a6SAndroid Build Coastguard Worker InvalidProgram,
275*523fa7a6SAndroid Build Coastguard Worker "Missing non_const_buffer_sizes field");
276*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
277*523fa7a6SAndroid Build Coastguard Worker plan.get()->inputs() != nullptr, InvalidProgram, "Missing inputs field");
278*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
279*523fa7a6SAndroid Build Coastguard Worker plan.get()->outputs() != nullptr,
280*523fa7a6SAndroid Build Coastguard Worker InvalidProgram,
281*523fa7a6SAndroid Build Coastguard Worker "Missing outputs field");
282*523fa7a6SAndroid Build Coastguard Worker return MethodMeta(plan.get());
283*523fa7a6SAndroid Build Coastguard Worker }
284*523fa7a6SAndroid Build Coastguard Worker
get_constant_buffer_data(size_t buffer_index,size_t nbytes) const285*523fa7a6SAndroid Build Coastguard Worker Result<const void*> Program::get_constant_buffer_data(
286*523fa7a6SAndroid Build Coastguard Worker size_t buffer_index,
287*523fa7a6SAndroid Build Coastguard Worker size_t nbytes) const {
288*523fa7a6SAndroid Build Coastguard Worker auto internal_program =
289*523fa7a6SAndroid Build Coastguard Worker static_cast<const executorch_flatbuffer::Program*>(internal_program_);
290*523fa7a6SAndroid Build Coastguard Worker
291*523fa7a6SAndroid Build Coastguard Worker // Constant data is either in a separate segment (constant_segment_data) and
292*523fa7a6SAndroid Build Coastguard Worker // loaded during Program::load, or stored inside the flatbuffer data
293*523fa7a6SAndroid Build Coastguard Worker // (constant_buffer).
294*523fa7a6SAndroid Build Coastguard Worker if (constant_segment_data_.data() != nullptr) {
295*523fa7a6SAndroid Build Coastguard Worker size_t num_elems = internal_program->constant_segment()->offsets()->size();
296*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
297*523fa7a6SAndroid Build Coastguard Worker buffer_index < num_elems,
298*523fa7a6SAndroid Build Coastguard Worker InvalidArgument,
299*523fa7a6SAndroid Build Coastguard Worker "Constant segment buffer index %zu invalid for program constant segment range %zu",
300*523fa7a6SAndroid Build Coastguard Worker buffer_index,
301*523fa7a6SAndroid Build Coastguard Worker num_elems);
302*523fa7a6SAndroid Build Coastguard Worker
303*523fa7a6SAndroid Build Coastguard Worker // All constant data is stored in one segment, with each tensor aligned to
304*523fa7a6SAndroid Build Coastguard Worker // @executorch_tensor_alignment. Tensor offsets are stored in the flatbuffer
305*523fa7a6SAndroid Build Coastguard Worker // data in Program.constant_segment.offsets.
306*523fa7a6SAndroid Build Coastguard Worker // The constant data at buffer_index is located at: base address of the
307*523fa7a6SAndroid Build Coastguard Worker // constant segment + offset for tensor at buffer_index.
308*523fa7a6SAndroid Build Coastguard Worker uint64_t offset = static_cast<uint64_t>(
309*523fa7a6SAndroid Build Coastguard Worker (*internal_program->constant_segment()->offsets())[buffer_index]);
310*523fa7a6SAndroid Build Coastguard Worker
311*523fa7a6SAndroid Build Coastguard Worker size_t size = constant_segment_data_.size();
312*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
313*523fa7a6SAndroid Build Coastguard Worker offset + nbytes <= size,
314*523fa7a6SAndroid Build Coastguard Worker InvalidArgument,
315*523fa7a6SAndroid Build Coastguard Worker "Constant segment offset %" PRIu64
316*523fa7a6SAndroid Build Coastguard Worker " + size_bytes %zu invalid for program constant segment size %zu",
317*523fa7a6SAndroid Build Coastguard Worker offset,
318*523fa7a6SAndroid Build Coastguard Worker nbytes,
319*523fa7a6SAndroid Build Coastguard Worker size);
320*523fa7a6SAndroid Build Coastguard Worker
321*523fa7a6SAndroid Build Coastguard Worker // Offset is wrt the beginning of the constant segment.
322*523fa7a6SAndroid Build Coastguard Worker return static_cast<const void*>(
323*523fa7a6SAndroid Build Coastguard Worker static_cast<const unsigned char*>(constant_segment_data_.data()) +
324*523fa7a6SAndroid Build Coastguard Worker offset);
325*523fa7a6SAndroid Build Coastguard Worker } else {
326*523fa7a6SAndroid Build Coastguard Worker // Otherwise, the constant data is stored inside Program.constant_buffer.
327*523fa7a6SAndroid Build Coastguard Worker size_t num_elems = internal_program->constant_buffer()->size();
328*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
329*523fa7a6SAndroid Build Coastguard Worker buffer_index < num_elems,
330*523fa7a6SAndroid Build Coastguard Worker InvalidArgument,
331*523fa7a6SAndroid Build Coastguard Worker "Constant buffer index %zu invalid for program constant buffer range %zu",
332*523fa7a6SAndroid Build Coastguard Worker buffer_index,
333*523fa7a6SAndroid Build Coastguard Worker num_elems);
334*523fa7a6SAndroid Build Coastguard Worker
335*523fa7a6SAndroid Build Coastguard Worker const auto& constant_buffer = *internal_program->constant_buffer();
336*523fa7a6SAndroid Build Coastguard Worker
337*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
338*523fa7a6SAndroid Build Coastguard Worker constant_buffer[buffer_index]->storage()->size() <= nbytes,
339*523fa7a6SAndroid Build Coastguard Worker InvalidArgument,
340*523fa7a6SAndroid Build Coastguard Worker "Constant buffer size %u larger than allocated nbytes %zu",
341*523fa7a6SAndroid Build Coastguard Worker constant_buffer[buffer_index]->storage()->size(),
342*523fa7a6SAndroid Build Coastguard Worker nbytes);
343*523fa7a6SAndroid Build Coastguard Worker
344*523fa7a6SAndroid Build Coastguard Worker return static_cast<const void*>(
345*523fa7a6SAndroid Build Coastguard Worker constant_buffer[buffer_index]->storage()->data());
346*523fa7a6SAndroid Build Coastguard Worker }
347*523fa7a6SAndroid Build Coastguard Worker }
348*523fa7a6SAndroid Build Coastguard Worker
get_output_flattening_encoding(const char * method_name) const349*523fa7a6SAndroid Build Coastguard Worker Result<const char*> Program::get_output_flattening_encoding(
350*523fa7a6SAndroid Build Coastguard Worker const char* method_name) const {
351*523fa7a6SAndroid Build Coastguard Worker auto plan = get_execution_plan(internal_program_, method_name);
352*523fa7a6SAndroid Build Coastguard Worker if (!plan.ok()) {
353*523fa7a6SAndroid Build Coastguard Worker return plan.error();
354*523fa7a6SAndroid Build Coastguard Worker }
355*523fa7a6SAndroid Build Coastguard Worker return plan.get()->container_meta_type()->encoded_out_str()->c_str();
356*523fa7a6SAndroid Build Coastguard Worker }
357*523fa7a6SAndroid Build Coastguard Worker
get_backend_delegate_data(size_t index,const void ** out_data,size_t * out_size) const358*523fa7a6SAndroid Build Coastguard Worker Error Program::get_backend_delegate_data(
359*523fa7a6SAndroid Build Coastguard Worker size_t index,
360*523fa7a6SAndroid Build Coastguard Worker const void** out_data,
361*523fa7a6SAndroid Build Coastguard Worker size_t* out_size) const {
362*523fa7a6SAndroid Build Coastguard Worker const auto* data_list =
363*523fa7a6SAndroid Build Coastguard Worker static_cast<const executorch_flatbuffer::Program*>(internal_program_)
364*523fa7a6SAndroid Build Coastguard Worker ->backend_delegate_data();
365*523fa7a6SAndroid Build Coastguard Worker ET_CHECK_OR_RETURN_ERROR(
366*523fa7a6SAndroid Build Coastguard Worker index < data_list->size(),
367*523fa7a6SAndroid Build Coastguard Worker NotFound,
368*523fa7a6SAndroid Build Coastguard Worker "index %zu >= list size %" PRIu32,
369*523fa7a6SAndroid Build Coastguard Worker index,
370*523fa7a6SAndroid Build Coastguard Worker data_list->size());
371*523fa7a6SAndroid Build Coastguard Worker auto data = data_list->Get(index)->data();
372*523fa7a6SAndroid Build Coastguard Worker *out_data = data->data();
373*523fa7a6SAndroid Build Coastguard Worker *out_size = data->size();
374*523fa7a6SAndroid Build Coastguard Worker return Error::Ok;
375*523fa7a6SAndroid Build Coastguard Worker }
376*523fa7a6SAndroid Build Coastguard Worker
check_header(const void * data,size_t size)377*523fa7a6SAndroid Build Coastguard Worker /* static */ Program::HeaderStatus Program::check_header(
378*523fa7a6SAndroid Build Coastguard Worker const void* data,
379*523fa7a6SAndroid Build Coastguard Worker size_t size) {
380*523fa7a6SAndroid Build Coastguard Worker if (size < kMinHeadBytes) {
381*523fa7a6SAndroid Build Coastguard Worker return HeaderStatus::ShortData;
382*523fa7a6SAndroid Build Coastguard Worker }
383*523fa7a6SAndroid Build Coastguard Worker if (executorch_flatbuffer::ProgramBufferHasIdentifier(data)) {
384*523fa7a6SAndroid Build Coastguard Worker // The data has the same file_identifier string as the schema.fbs file
385*523fa7a6SAndroid Build Coastguard Worker // that this runtime was built with.
386*523fa7a6SAndroid Build Coastguard Worker return HeaderStatus::CompatibleVersion;
387*523fa7a6SAndroid Build Coastguard Worker }
388*523fa7a6SAndroid Build Coastguard Worker const char* id = flatbuffers::GetBufferIdentifier(data);
389*523fa7a6SAndroid Build Coastguard Worker if (id[0] == 'E' && id[1] == 'T') {
390*523fa7a6SAndroid Build Coastguard Worker // It looks like an executorch file, but not the version we expect.
391*523fa7a6SAndroid Build Coastguard Worker return HeaderStatus::IncompatibleVersion;
392*523fa7a6SAndroid Build Coastguard Worker }
393*523fa7a6SAndroid Build Coastguard Worker return HeaderStatus::NotPresent;
394*523fa7a6SAndroid Build Coastguard Worker }
395*523fa7a6SAndroid Build Coastguard Worker
LoadSegment(const DataLoader::SegmentInfo & segment_info) const396*523fa7a6SAndroid Build Coastguard Worker Result<FreeableBuffer> Program::LoadSegment(
397*523fa7a6SAndroid Build Coastguard Worker const DataLoader::SegmentInfo& segment_info) const {
398*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_SCOPE_PROF("Program::LoadSegment");
399*523fa7a6SAndroid Build Coastguard Worker size_t index = segment_info.segment_index;
400*523fa7a6SAndroid Build Coastguard Worker if (loader_ == nullptr || segment_base_offset_ == 0) {
401*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Error, "No segments in program: requested index %zu", index);
402*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
403*523fa7a6SAndroid Build Coastguard Worker }
404*523fa7a6SAndroid Build Coastguard Worker size_t num_segments = internal_program_->segments()->size();
405*523fa7a6SAndroid Build Coastguard Worker if (index >= num_segments) {
406*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
407*523fa7a6SAndroid Build Coastguard Worker Error, "Segment index %zu out of range (>= %zu)", index, num_segments);
408*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
409*523fa7a6SAndroid Build Coastguard Worker }
410*523fa7a6SAndroid Build Coastguard Worker const executorch_flatbuffer::DataSegment* segment =
411*523fa7a6SAndroid Build Coastguard Worker internal_program_->segments()->Get(index);
412*523fa7a6SAndroid Build Coastguard Worker // Could fail if offset and size are out of bound for the data, or if this
413*523fa7a6SAndroid Build Coastguard Worker // is reading from a file and fails, or for many other reasons depending on
414*523fa7a6SAndroid Build Coastguard Worker // the implementation of the loader.
415*523fa7a6SAndroid Build Coastguard Worker return loader_->load(
416*523fa7a6SAndroid Build Coastguard Worker segment_base_offset_ + segment->offset(), segment->size(), segment_info);
417*523fa7a6SAndroid Build Coastguard Worker }
418*523fa7a6SAndroid Build Coastguard Worker
load_mutable_subsegment_into(size_t mutable_data_segments_index,size_t offset_index,size_t size,void * buffer) const419*523fa7a6SAndroid Build Coastguard Worker Error Program::load_mutable_subsegment_into(
420*523fa7a6SAndroid Build Coastguard Worker size_t mutable_data_segments_index,
421*523fa7a6SAndroid Build Coastguard Worker size_t offset_index,
422*523fa7a6SAndroid Build Coastguard Worker size_t size,
423*523fa7a6SAndroid Build Coastguard Worker void* buffer) const {
424*523fa7a6SAndroid Build Coastguard Worker EXECUTORCH_SCOPE_PROF("Program::load_subsegment_into");
425*523fa7a6SAndroid Build Coastguard Worker // Check that the program has segments.
426*523fa7a6SAndroid Build Coastguard Worker if (loader_ == nullptr || segment_base_offset_ == 0) {
427*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Error, "No segments in program");
428*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
429*523fa7a6SAndroid Build Coastguard Worker }
430*523fa7a6SAndroid Build Coastguard Worker
431*523fa7a6SAndroid Build Coastguard Worker // Check that the program has mutable data segments.
432*523fa7a6SAndroid Build Coastguard Worker if (internal_program_->mutable_data_segments() == nullptr) {
433*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Error, "No mutable data segments in program");
434*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
435*523fa7a6SAndroid Build Coastguard Worker }
436*523fa7a6SAndroid Build Coastguard Worker if (mutable_data_segments_index >=
437*523fa7a6SAndroid Build Coastguard Worker internal_program_->mutable_data_segments()->size()) {
438*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
439*523fa7a6SAndroid Build Coastguard Worker Error,
440*523fa7a6SAndroid Build Coastguard Worker "mutable_data_segments_index %zu out of range >= %" PRIu64,
441*523fa7a6SAndroid Build Coastguard Worker mutable_data_segments_index,
442*523fa7a6SAndroid Build Coastguard Worker (uint64_t)internal_program_->mutable_data_segments()->size());
443*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
444*523fa7a6SAndroid Build Coastguard Worker }
445*523fa7a6SAndroid Build Coastguard Worker
446*523fa7a6SAndroid Build Coastguard Worker // Grab the mutable data segment info.
447*523fa7a6SAndroid Build Coastguard Worker const auto& segment_offsets = internal_program_->mutable_data_segments()->Get(
448*523fa7a6SAndroid Build Coastguard Worker mutable_data_segments_index);
449*523fa7a6SAndroid Build Coastguard Worker
450*523fa7a6SAndroid Build Coastguard Worker // Check that the offset is valid.
451*523fa7a6SAndroid Build Coastguard Worker if (segment_offsets->offsets() == nullptr) {
452*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Error, "No offsets in mutable data segment");
453*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
454*523fa7a6SAndroid Build Coastguard Worker }
455*523fa7a6SAndroid Build Coastguard Worker if (offset_index >= segment_offsets->offsets()->size()) {
456*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
457*523fa7a6SAndroid Build Coastguard Worker Error,
458*523fa7a6SAndroid Build Coastguard Worker "offset index %zu out of range >= %" PRIu64,
459*523fa7a6SAndroid Build Coastguard Worker offset_index,
460*523fa7a6SAndroid Build Coastguard Worker (uint64_t)segment_offsets->offsets()->size());
461*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
462*523fa7a6SAndroid Build Coastguard Worker }
463*523fa7a6SAndroid Build Coastguard Worker
464*523fa7a6SAndroid Build Coastguard Worker // Grab the offset. Note: This offset is relative to the start of the segment,
465*523fa7a6SAndroid Build Coastguard Worker // so we will need to adjust when calling the loader.
466*523fa7a6SAndroid Build Coastguard Worker size_t offset = segment_offsets->offsets()->Get(offset_index);
467*523fa7a6SAndroid Build Coastguard Worker
468*523fa7a6SAndroid Build Coastguard Worker // Grab the segment index
469*523fa7a6SAndroid Build Coastguard Worker size_t num_segments = internal_program_->segments()->size();
470*523fa7a6SAndroid Build Coastguard Worker if (segment_offsets->segment_index() >= num_segments) {
471*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
472*523fa7a6SAndroid Build Coastguard Worker Error,
473*523fa7a6SAndroid Build Coastguard Worker "Segment index %u out of range (>= %zu)",
474*523fa7a6SAndroid Build Coastguard Worker segment_offsets->segment_index(),
475*523fa7a6SAndroid Build Coastguard Worker num_segments);
476*523fa7a6SAndroid Build Coastguard Worker return Error::NotFound;
477*523fa7a6SAndroid Build Coastguard Worker }
478*523fa7a6SAndroid Build Coastguard Worker
479*523fa7a6SAndroid Build Coastguard Worker // Grab the segment
480*523fa7a6SAndroid Build Coastguard Worker auto segment =
481*523fa7a6SAndroid Build Coastguard Worker internal_program_->segments()->Get(segment_offsets->segment_index());
482*523fa7a6SAndroid Build Coastguard Worker
483*523fa7a6SAndroid Build Coastguard Worker // Check size
484*523fa7a6SAndroid Build Coastguard Worker if (offset + size > segment->size()) {
485*523fa7a6SAndroid Build Coastguard Worker ET_LOG(
486*523fa7a6SAndroid Build Coastguard Worker Error,
487*523fa7a6SAndroid Build Coastguard Worker "offset %zu + size %zu out of range > %" PRIu64,
488*523fa7a6SAndroid Build Coastguard Worker offset,
489*523fa7a6SAndroid Build Coastguard Worker size,
490*523fa7a6SAndroid Build Coastguard Worker segment->size());
491*523fa7a6SAndroid Build Coastguard Worker return Error::InvalidArgument;
492*523fa7a6SAndroid Build Coastguard Worker }
493*523fa7a6SAndroid Build Coastguard Worker
494*523fa7a6SAndroid Build Coastguard Worker DataLoader::SegmentInfo info = DataLoader::SegmentInfo(
495*523fa7a6SAndroid Build Coastguard Worker DataLoader::SegmentInfo::Type::Mutable,
496*523fa7a6SAndroid Build Coastguard Worker segment_offsets->segment_index(),
497*523fa7a6SAndroid Build Coastguard Worker nullptr);
498*523fa7a6SAndroid Build Coastguard Worker
499*523fa7a6SAndroid Build Coastguard Worker // Load the data
500*523fa7a6SAndroid Build Coastguard Worker return loader_->load_into(
501*523fa7a6SAndroid Build Coastguard Worker segment_base_offset_ + segment->offset() + offset, size, info, buffer);
502*523fa7a6SAndroid Build Coastguard Worker }
503*523fa7a6SAndroid Build Coastguard Worker
504*523fa7a6SAndroid Build Coastguard Worker } // namespace runtime
505*523fa7a6SAndroid Build Coastguard Worker } // namespace executorch
506