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 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 UnknownError(absl::StrCat("Failed to get program binary size - ",
57 CLErrorCodeToString(error_code)));
58 }
59 return OkStatus();
60 }
61
BuildProgram(cl_program program,const CLDevice & device,const std::string & compiler_options)62 Status BuildProgram(cl_program program, const CLDevice& device,
63 const std::string& compiler_options) {
64 const int error_code = clBuildProgram(
65 program, 0, nullptr, compiler_options.c_str(), nullptr, nullptr);
66 if (error_code != CL_SUCCESS) {
67 return UnknownError(absl::StrCat(
68 "Failed to build program executable - ",
69 CLErrorCodeToString(error_code),
70 GetProgramBuildInfo(program, device.id(), CL_PROGRAM_BUILD_LOG)));
71 }
72
73 return OkStatus();
74 }
75
CompilerOptionToString(const CLDevice & device,CompilerOptions option)76 std::string CompilerOptionToString(const CLDevice& device,
77 CompilerOptions option) {
78 switch (option) {
79 case CompilerOptions::ADRENO_FULL_SIMD_LINE:
80 if (device.GetInfo().adreno_info.gpu_version < 500) {
81 return "-qcom-accelerate-16-bit";
82 } else {
83 return "-qcom-accelerate-16-bit=true";
84 }
85 case CompilerOptions::POWERVR_FP16:
86 return "-cl-fast-relaxed-math";
87 case CompilerOptions::CL_OPT_DISABLE:
88 return "-cl-opt-disable";
89 }
90 }
91
92 } // namespace
93
CompilerOptionsToString(const CLDevice & device,const std::vector<CompilerOptions> & compiler_options)94 std::string CompilerOptionsToString(
95 const CLDevice& device,
96 const std::vector<CompilerOptions>& compiler_options) {
97 std::string result;
98 for (auto option : compiler_options) {
99 absl::StrAppend(&result, CompilerOptionToString(device, option), " ");
100 }
101 return result;
102 }
103
CLProgram(cl_program program,cl_device_id device_id)104 CLProgram::CLProgram(cl_program program, cl_device_id device_id)
105 : program_(program), device_id_(device_id) {}
106
CLProgram(CLProgram && program)107 CLProgram::CLProgram(CLProgram&& program)
108 : program_(program.program_), device_id_(program.device_id_) {
109 program.program_ = nullptr;
110 }
111
operator =(CLProgram && program)112 CLProgram& CLProgram::operator=(CLProgram&& program) {
113 if (this != &program) {
114 Release();
115 std::swap(program_, program.program_);
116 std::swap(device_id_, program.device_id_);
117 }
118 return *this;
119 }
120
~CLProgram()121 CLProgram::~CLProgram() { Release(); }
122
Release()123 void CLProgram::Release() {
124 if (program_) {
125 clReleaseProgram(program_);
126 program_ = nullptr;
127 }
128 }
129
GetBinary(std::vector<uint8_t> * result) const130 Status CLProgram::GetBinary(std::vector<uint8_t>* result) const {
131 size_t binary_size;
132 RETURN_IF_ERROR(GetBinarySize(program_, &binary_size));
133 result->resize(result->size() + binary_size);
134 uint8_t* binary_ptr = result->data() + result->size() - binary_size;
135 cl_int error_code = clGetProgramInfo(program_, CL_PROGRAM_BINARIES,
136 binary_size, &binary_ptr, nullptr);
137 if (error_code != CL_SUCCESS) {
138 return UnknownError(absl::StrCat("Failed to get program binary - ",
139 CLErrorCodeToString(error_code)));
140 }
141 return OkStatus();
142 }
143
CreateCLProgram(const std::string & code,const std::string & compiler_options,const CLContext & context,const CLDevice & device,CLProgram * result)144 Status CreateCLProgram(const std::string& code,
145 const std::string& compiler_options,
146 const CLContext& context, const CLDevice& device,
147 CLProgram* result) {
148 int error_code;
149 const char* source = code.c_str();
150
151 cl_program program = clCreateProgramWithSource(context.context(), 1, &source,
152 nullptr, &error_code);
153 if (!program || error_code != CL_SUCCESS) {
154 return UnknownError(absl::StrCat("Failed to create compute program - ",
155 CLErrorCodeToString(error_code)));
156 }
157
158 *result = CLProgram(program, device.id());
159 RETURN_IF_ERROR(BuildProgram(program, device, compiler_options));
160 return OkStatus();
161 }
162
CreateCLProgramFromBinary(const CLContext & context,const CLDevice & device,absl::Span<const uint8_t> binary,CLProgram * result)163 Status CreateCLProgramFromBinary(const CLContext& context,
164 const CLDevice& device,
165 absl::Span<const uint8_t> binary,
166 CLProgram* result) {
167 cl_int binary_status;
168 cl_int error_code;
169 cl_device_id devices_list[] = {device.id()};
170 size_t binary_size = binary.size();
171 const uint8_t* binary_pointer = binary.data();
172 cl_program program = clCreateProgramWithBinary(
173 context.context(), 1, devices_list, &binary_size, &binary_pointer,
174 &binary_status, &error_code);
175 if (binary_status != CL_SUCCESS) {
176 return UnknownError(absl::StrCat(
177 "Something wrong with binary after clCreateProgramWithBinary - ",
178 binary_status));
179 }
180 if (error_code != CL_SUCCESS) {
181 return UnknownError(absl::StrCat("Failed to create program - ",
182 CLErrorCodeToString(error_code)));
183 }
184 *result = CLProgram(program, device.id());
185 return BuildProgram(program, device, "");
186 }
187
188 } // namespace cl
189 } // namespace gpu
190 } // namespace tflite
191