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