xref: /aosp_15_r20/external/executorch/runtime/executor/program.cpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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