1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/lite/delegates/gpu/cl/cl_program.h"
17
18 #include <cstdint>
19 #include <cstring>
20 #include <vector>
21
22 #include "absl/strings/str_cat.h"
23 #include "absl/types/span.h"
24 #include "tensorflow/lite/delegates/gpu/cl/util.h"
25 #include "tensorflow/lite/delegates/gpu/common/status.h"
26
27 namespace tflite {
28 namespace gpu {
29 namespace cl {
30 namespace {
31
GetProgramBuildInfo(cl_program program,cl_device_id id,cl_program_build_info info)32 std::string GetProgramBuildInfo(cl_program program, cl_device_id id,
33 cl_program_build_info info) {
34 size_t size;
35 cl_int error_code =
36 clGetProgramBuildInfo(program, id, info, 0, nullptr, &size);
37 if (error_code != CL_SUCCESS) {
38 return absl::StrCat("Failed to GetProgramBuildInfo - ",
39 CLErrorCodeToString(error_code));
40 }
41
42 std::string result(size - 1, 0);
43 error_code =
44 clGetProgramBuildInfo(program, id, info, size, &result[0], nullptr);
45 if (error_code != CL_SUCCESS) {
46 return absl::StrCat("Failed to GetProgramBuildInfo - ",
47 CLErrorCodeToString(error_code));
48 }
49 return result;
50 }
51
GetBinarySize(cl_program program,size_t * binary_size)52 absl::Status GetBinarySize(cl_program program, size_t* binary_size) {
53 cl_int error_code = clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES,
54 sizeof(size_t), binary_size, nullptr);
55 if (error_code != CL_SUCCESS) {
56 return absl::UnknownError(
57 absl::StrCat("Failed to get program binary size - ",
58 CLErrorCodeToString(error_code)));
59 }
60 return absl::OkStatus();
61 }
62
BuildProgram(cl_program program,const CLDevice & device,const std::string & compiler_options)63 absl::Status BuildProgram(cl_program program, const CLDevice& device,
64 const std::string& compiler_options) {
65 const int error_code = clBuildProgram(
66 program, 0, nullptr, compiler_options.c_str(), nullptr, nullptr);
67 if (error_code != CL_SUCCESS) {
68 return absl::UnknownError(absl::StrCat(
69 "Failed to build program executable - ",
70 CLErrorCodeToString(error_code),
71 GetProgramBuildInfo(program, device.id(), CL_PROGRAM_BUILD_LOG)));
72 }
73
74 return absl::OkStatus();
75 }
76
CompilerOptionToString(const GpuInfo & gpu_info,CompilerOptions option)77 std::string CompilerOptionToString(const GpuInfo& gpu_info,
78 CompilerOptions option) {
79 switch (option) {
80 case CompilerOptions::kAdrenoFullSimd:
81 if (gpu_info.IsAdreno()) {
82 if (gpu_info.adreno_info.IsAdreno3xx() ||
83 gpu_info.adreno_info.IsAdreno4xx()) {
84 return "-qcom-accelerate-16-bit";
85 } else {
86 return "-qcom-accelerate-16-bit=true";
87 }
88 } else {
89 return "unsupported";
90 }
91 case CompilerOptions::kAdrenoMoreWaves:
92 if (gpu_info.IsAdreno()) {
93 if (!(gpu_info.adreno_info.IsAdreno3xx() ||
94 gpu_info.adreno_info.IsAdreno4xx())) {
95 return "-qcom-accelerate-16-bit=false";
96 } else {
97 return "";
98 }
99 } else {
100 return "unsupported";
101 }
102 case CompilerOptions::kClPowervrFp16:
103 return "-cl-fast-relaxed-math";
104 case CompilerOptions::kClDisableOptimizations:
105 return "-cl-opt-disable";
106 case CompilerOptions::kCl20:
107 return "-cl-std=CL2.0";
108 case CompilerOptions::kCl30:
109 return "-cl-std=CL3.0";
110 }
111 }
112
113 } // namespace
114
CompilerOptionsToString(const GpuInfo & gpu_info,const std::vector<CompilerOptions> & compiler_options)115 std::string CompilerOptionsToString(
116 const GpuInfo& gpu_info,
117 const std::vector<CompilerOptions>& compiler_options) {
118 std::string result;
119 for (auto option : compiler_options) {
120 absl::StrAppend(&result, CompilerOptionToString(gpu_info, option), " ");
121 }
122 return result;
123 }
124
CLProgram(cl_program program,cl_device_id device_id)125 CLProgram::CLProgram(cl_program program, cl_device_id device_id)
126 : program_(program), device_id_(device_id) {}
127
CLProgram(CLProgram && program)128 CLProgram::CLProgram(CLProgram&& program)
129 : program_(program.program_), device_id_(program.device_id_) {
130 program.program_ = nullptr;
131 }
132
operator =(CLProgram && program)133 CLProgram& CLProgram::operator=(CLProgram&& program) {
134 if (this != &program) {
135 Release();
136 std::swap(program_, program.program_);
137 std::swap(device_id_, program.device_id_);
138 }
139 return *this;
140 }
141
~CLProgram()142 CLProgram::~CLProgram() { Release(); }
143
Release()144 void CLProgram::Release() {
145 if (program_) {
146 clReleaseProgram(program_);
147 program_ = nullptr;
148 }
149 }
150
GetBinary(std::vector<uint8_t> * result) const151 absl::Status CLProgram::GetBinary(std::vector<uint8_t>* result) const {
152 size_t binary_size;
153 RETURN_IF_ERROR(GetBinarySize(program_, &binary_size));
154 result->resize(result->size() + binary_size);
155 uint8_t* binary_ptr = result->data() + result->size() - binary_size;
156 cl_int error_code = clGetProgramInfo(program_, CL_PROGRAM_BINARIES,
157 binary_size, &binary_ptr, nullptr);
158 if (error_code != CL_SUCCESS) {
159 return absl::UnknownError(absl::StrCat("Failed to get program binary - ",
160 CLErrorCodeToString(error_code)));
161 }
162 return absl::OkStatus();
163 }
164
CreateCLProgram(const std::string & code,const std::string & compiler_options,const CLContext & context,const CLDevice & device,CLProgram * result)165 absl::Status CreateCLProgram(const std::string& code,
166 const std::string& compiler_options,
167 const CLContext& context, const CLDevice& device,
168 CLProgram* result) {
169 int error_code;
170 const char* source = code.c_str();
171
172 cl_program program = clCreateProgramWithSource(context.context(), 1, &source,
173 nullptr, &error_code);
174 if (!program || error_code != CL_SUCCESS) {
175 return absl::UnknownError(
176 absl::StrCat("Failed to create compute program - ",
177 CLErrorCodeToString(error_code)));
178 }
179
180 *result = CLProgram(program, device.id());
181 RETURN_IF_ERROR(BuildProgram(program, device, compiler_options));
182 return absl::OkStatus();
183 }
184
CreateCLProgramFromBinary(const CLContext & context,const CLDevice & device,absl::Span<const uint8_t> binary,CLProgram * result)185 absl::Status CreateCLProgramFromBinary(const CLContext& context,
186 const CLDevice& device,
187 absl::Span<const uint8_t> binary,
188 CLProgram* result) {
189 cl_int binary_status;
190 cl_int error_code;
191 cl_device_id devices_list[] = {device.id()};
192 size_t binary_size = binary.size();
193 const uint8_t* binary_pointer = binary.data();
194 cl_program program = clCreateProgramWithBinary(
195 context.context(), 1, devices_list, &binary_size, &binary_pointer,
196 &binary_status, &error_code);
197 if (binary_status != CL_SUCCESS) {
198 return absl::UnknownError(absl::StrCat(
199 "Something wrong with binary after clCreateProgramWithBinary - ",
200 binary_status));
201 }
202 if (error_code != CL_SUCCESS) {
203 return absl::UnknownError(absl::StrCat("Failed to create program - ",
204 CLErrorCodeToString(error_code)));
205 }
206 *result = CLProgram(program, device.id());
207 return BuildProgram(program, device, "");
208 }
209
210 } // namespace cl
211 } // namespace gpu
212 } // namespace tflite
213