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 <string>
21 #include <vector>
22
23 #include "absl/strings/str_cat.h"
24 #include "absl/types/span.h"
25 #include "tensorflow/lite/delegates/gpu/cl/util.h"
26 #include "tensorflow/lite/delegates/gpu/common/status.h"
27
28 namespace tflite {
29 namespace gpu {
30 namespace cl {
31 namespace {
32
GetProgramBuildInfo(cl_program program,cl_device_id id,cl_program_build_info info)33 std::string GetProgramBuildInfo(cl_program program, cl_device_id id,
34 cl_program_build_info info) {
35 size_t size;
36 cl_int error_code =
37 clGetProgramBuildInfo(program, id, info, 0, nullptr, &size);
38 if (error_code != CL_SUCCESS) {
39 return absl::StrCat("Failed to GetProgramBuildInfo - ",
40 CLErrorCodeToString(error_code));
41 }
42
43 std::string result(size - 1, 0);
44 error_code =
45 clGetProgramBuildInfo(program, id, info, size, &result[0], nullptr);
46 if (error_code != CL_SUCCESS) {
47 return absl::StrCat("Failed to GetProgramBuildInfo - ",
48 CLErrorCodeToString(error_code));
49 }
50 return result;
51 }
52
GetBinarySize(cl_program program,size_t * binary_size)53 absl::Status GetBinarySize(cl_program program, size_t* binary_size) {
54 cl_int error_code = clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES,
55 sizeof(size_t), binary_size, nullptr);
56 if (error_code != CL_SUCCESS) {
57 return absl::UnknownError(
58 absl::StrCat("Failed to get program binary size - ",
59 CLErrorCodeToString(error_code)));
60 }
61 return absl::OkStatus();
62 }
63
BuildProgram(cl_program program,const CLDevice & device,const std::string & compiler_options)64 absl::Status BuildProgram(cl_program program, const CLDevice& device,
65 const std::string& compiler_options) {
66 const int error_code = clBuildProgram(
67 program, 0, nullptr, compiler_options.c_str(), nullptr, nullptr);
68 if (error_code != CL_SUCCESS) {
69 return absl::UnknownError(absl::StrCat(
70 "Failed to build program executable - ",
71 CLErrorCodeToString(error_code),
72 GetProgramBuildInfo(program, device.id(), CL_PROGRAM_BUILD_LOG)));
73 }
74
75 return absl::OkStatus();
76 }
77
CompilerOptionToString(const GpuInfo & gpu_info,CompilerOptions option)78 std::string CompilerOptionToString(const GpuInfo& gpu_info,
79 CompilerOptions option) {
80 switch (option) {
81 case CompilerOptions::kAdrenoFullSimd:
82 if (gpu_info.IsAdreno()) {
83 if (gpu_info.adreno_info.IsAdreno3xx() ||
84 gpu_info.adreno_info.IsAdreno4xx()) {
85 return "-qcom-accelerate-16-bit";
86 } else {
87 return "-qcom-accelerate-16-bit=true";
88 }
89 } else {
90 return "unsupported";
91 }
92 case CompilerOptions::kAdrenoMoreWaves:
93 if (gpu_info.IsAdreno()) {
94 if (!(gpu_info.adreno_info.IsAdreno3xx() ||
95 gpu_info.adreno_info.IsAdreno4xx())) {
96 return "-qcom-accelerate-16-bit=false";
97 } else {
98 return "";
99 }
100 } else {
101 return "unsupported";
102 }
103 case CompilerOptions::kClFastRelaxedMath:
104 return "-cl-fast-relaxed-math";
105 case CompilerOptions::kClDisableOptimizations:
106 return "-cl-opt-disable";
107 case CompilerOptions::kCl20:
108 return "-cl-std=CL2.0";
109 case CompilerOptions::kCl30:
110 return "-cl-std=CL3.0";
111 }
112 }
113
114 } // namespace
115
CompilerOptionsToString(const GpuInfo & gpu_info,const std::vector<CompilerOptions> & compiler_options)116 std::string CompilerOptionsToString(
117 const GpuInfo& gpu_info,
118 const std::vector<CompilerOptions>& compiler_options) {
119 std::string result;
120 for (auto option : compiler_options) {
121 absl::StrAppend(&result, CompilerOptionToString(gpu_info, option), " ");
122 }
123 return result;
124 }
125
CLProgram(cl_program program,cl_device_id device_id)126 CLProgram::CLProgram(cl_program program, cl_device_id device_id)
127 : program_(program), device_id_(device_id) {}
128
CLProgram(CLProgram && program)129 CLProgram::CLProgram(CLProgram&& program)
130 : program_(program.program_), device_id_(program.device_id_) {
131 program.program_ = nullptr;
132 }
133
operator =(CLProgram && program)134 CLProgram& CLProgram::operator=(CLProgram&& program) {
135 if (this != &program) {
136 Release();
137 std::swap(program_, program.program_);
138 std::swap(device_id_, program.device_id_);
139 }
140 return *this;
141 }
142
~CLProgram()143 CLProgram::~CLProgram() { Release(); }
144
Release()145 void CLProgram::Release() {
146 if (program_) {
147 clReleaseProgram(program_);
148 program_ = nullptr;
149 }
150 }
151
GetBinary(std::vector<uint8_t> * result) const152 absl::Status CLProgram::GetBinary(std::vector<uint8_t>* result) const {
153 size_t binary_size;
154 RETURN_IF_ERROR(GetBinarySize(program_, &binary_size));
155 result->resize(result->size() + binary_size);
156 uint8_t* binary_ptr = result->data() + result->size() - binary_size;
157 cl_int error_code =
158 clGetProgramInfo(program_, CL_PROGRAM_BINARIES, sizeof(unsigned char*),
159 &binary_ptr, nullptr);
160 if (error_code != CL_SUCCESS) {
161 return absl::UnknownError(absl::StrCat("Failed to get program binary - ",
162 CLErrorCodeToString(error_code)));
163 }
164 return absl::OkStatus();
165 }
166
CreateCLProgram(const std::string & code,const std::string & compiler_options,const CLContext & context,const CLDevice & device,CLProgram * result)167 absl::Status CreateCLProgram(const std::string& code,
168 const std::string& compiler_options,
169 const CLContext& context, const CLDevice& device,
170 CLProgram* result) {
171 int error_code;
172 const char* source = code.c_str();
173
174 cl_program program = clCreateProgramWithSource(context.context(), 1, &source,
175 nullptr, &error_code);
176 if (!program || error_code != CL_SUCCESS) {
177 return absl::UnknownError(
178 absl::StrCat("Failed to create compute program - ",
179 CLErrorCodeToString(error_code)));
180 }
181
182 *result = CLProgram(program, device.id());
183 RETURN_IF_ERROR(BuildProgram(program, device, compiler_options));
184 return absl::OkStatus();
185 }
186
CreateCLProgramFromBinary(const CLContext & context,const CLDevice & device,absl::Span<const uint8_t> binary,CLProgram * result)187 absl::Status CreateCLProgramFromBinary(const CLContext& context,
188 const CLDevice& device,
189 absl::Span<const uint8_t> binary,
190 CLProgram* result) {
191 cl_int binary_status;
192 cl_int error_code;
193 cl_device_id devices_list[] = {device.id()};
194 size_t binary_size = binary.size();
195 const uint8_t* binary_pointer = binary.data();
196 cl_program program = clCreateProgramWithBinary(
197 context.context(), 1, devices_list, &binary_size, &binary_pointer,
198 &binary_status, &error_code);
199 if (binary_status != CL_SUCCESS) {
200 return absl::UnknownError(absl::StrCat(
201 "Something wrong with binary after clCreateProgramWithBinary - ",
202 binary_status));
203 }
204 if (error_code != CL_SUCCESS) {
205 return absl::UnknownError(absl::StrCat("Failed to create program - ",
206 CLErrorCodeToString(error_code)));
207 }
208 *result = CLProgram(program, device.id());
209 return BuildProgram(program, device, "");
210 }
211
212 } // namespace cl
213 } // namespace gpu
214 } // namespace tflite
215