TODO(impjdi): Check number of input/output tensors and their dimensions. TODO(impjdi): Check ops' parameters.
| 2359 | // TODO(impjdi): Check number of input/output tensors and their dimensions. |
| 2360 | // TODO(impjdi): Check ops' parameters. |
| 2361 | TfLiteIntArray* GetOpsToReplace(TfLiteContext* context) { |
| 2362 | TfLiteIntArray* execution_plan = nullptr; |
| 2363 | if (context->GetExecutionPlan(context, &execution_plan) != kTfLiteOk) { |
| 2364 | context->ReportError(context, "Unable to get graph execution plan."); |
| 2365 | return nullptr; |
| 2366 | } |
| 2367 | |
| 2368 | // Dispatch to another function if graph has Dequantize nodes. |
| 2369 | for (int i = 0; i < execution_plan->size; ++i) { |
| 2370 | const int node_id = execution_plan->data[i]; |
| 2371 | TfLiteNode* node = nullptr; |
| 2372 | TfLiteRegistration* registration = nullptr; |
| 2373 | auto status = |
| 2374 | GetNodeAndRegistration(context, node_id, &node, ®istration); |
| 2375 | if (!status.ok()) { |
| 2376 | context->ReportError(context, status.error_message().c_str()); |
| 2377 | return nullptr; |
| 2378 | } |
| 2379 | if (registration->builtin_code == kTfLiteBuiltinDequantize && |
| 2380 | context->tensors[node->inputs->data[0]].type == |
| 2381 | TfLiteType::kTfLiteFloat16) { |
| 2382 | return GetOpsToReplaceFromGraphWithDequantize(context); |
| 2383 | } |
| 2384 | } |
| 2385 | |
| 2386 | // No Dequantize nodes. Iterate through graph and find ops to replace. |
| 2387 | TfLiteIntArray* subgraph = TfLiteIntArrayCreate(execution_plan->size); |
| 2388 | subgraph->size = 0; |
| 2389 | std::set<std::string> errors; |
| 2390 | for (int i = 0; i < execution_plan->size; ++i) { |
| 2391 | const int node_id = execution_plan->data[i]; |
| 2392 | TfLiteNode* node; |
| 2393 | TfLiteRegistration* registration; |
| 2394 | auto status = |
| 2395 | GetNodeAndRegistration(context, node_id, &node, ®istration); |
| 2396 | if (!status.ok()) { |
| 2397 | context->ReportError(context, status.error_message().c_str()); |
| 2398 | return nullptr; |
| 2399 | } |
| 2400 | status = IsSupported(context, node, registration); |
| 2401 | if (status.ok() && |
| 2402 | // TODO(eignasheva): resolve sub operation support for metal delegate |
| 2403 | // registration->builtin_code != kTfLiteBuiltinSub && |
| 2404 | IsAllFloatTensors(context, node->inputs) && |
| 2405 | IsAllFloatTensors(context, node->outputs)) { |
| 2406 | if (errors.empty()) subgraph->data[subgraph->size++] = node_id; |
| 2407 | } else { |
| 2408 | errors.insert(absl::StrCat(GetOpNameByRegistration(registration), ": ", |
| 2409 | status.error_message())); |
| 2410 | } |
| 2411 | } |
| 2412 | if (!errors.empty()) { |
| 2413 | std::string unsupported = absl::StrJoin(errors, "\n"); |
| 2414 | std::string error_message = |
| 2415 | "Next operations are not supported by GPU delegate:\n" + unsupported + |
| 2416 | "\nFirst " + std::to_string(subgraph->size) + |
| 2417 | " operations will run on the GPU, and the remaining " + |
| 2418 | std::to_string(execution_plan->size - subgraph->size) + " on the CPU."; |