@@ -21,8 +21,8 @@ limitations under the License.
2121// IMPORTANT: explicit STL includes (this .cc is included via tfj_gradients.h in some builds,
2222// so we must not rely on transitive includes from other headers).
2323#include < string>
24- #include < vector>
2524#include < unordered_map>
25+ #include < vector>
2626
2727#include " tfj_graph.h"
2828#include " tsl/platform/errors.h"
@@ -31,141 +31,136 @@ limitations under the License.
3131#include " tensorflow/cc/framework/grad_op_registry.h"
3232
3333namespace tensorflow {
34- namespace java {
35- using namespace tsl ;
36- using namespace std ;
37-
38- unordered_map<string, TFJ_GradFuncAdapter> g_grad_func_adapters;
39-
40- // / This method can be used to cast a pointer to/from a C struct that contains only that pointer. It is a bit
41- // / It has been "inspired" by the TensorFlow C API code, as found at this location when time of writing:
42- // / https://github.com/tensorflow/tensorflow/blob/9d637f69f699c0c422716b56153a8b27b681891a/tensorflow/c/c_api.cc#L658
43- template <typename T, typename U> T* struct_cast (U* ptr) {
44- return static_cast <T*>(static_cast <void *>(ptr));
45- }
46-
47- // / This function is called by the TensorFlow runtime when it is time to add gradient operations of `op` to the
48- // / graph using the given `scope`.
49- // / We use it as a bridge between the C++ signature in TensorFlow (tensorflow::op::GradFunc) and our custom
50- // / "C" version (TFJ_GradFuncAdapter).
51- Status CustomGradFunc (const Scope& scope,
52- const Operation& op,
53- const vector<Output>& grad_inputs,
54- vector<Output>* grad_outputs)
55- {
56- const string& op_type = op.node ()->type_string ();
57- auto found_adapter = g_grad_func_adapters.find (op_type);
58- if (found_adapter == g_grad_func_adapters.end ()) {
59- return errors::NotFound (" No gradient adapter found for operation " , op_type);
60- }
61-
62- const int num_inputs = static_cast <int >(grad_inputs.size ());
63-
64- TF_Output* inputs = nullptr ;
65- if (num_inputs > 0 ) {
66- inputs = static_cast <TF_Output*>(malloc (num_inputs * sizeof (TF_Output)));
67- if (inputs == nullptr ) {
68- return errors::ResourceExhausted (
69- " Out of memory allocating inputs for custom gradient of op " , op_type);
70- }
71- }
72-
73- for (int i = 0 ; i < num_inputs; ++i) {
74- const Output& grad_input = grad_inputs[i];
75- inputs[i].oper = struct_cast<TF_Operation>(grad_input.node ());
76- inputs[i].index = grad_input.index ();
77- }
78-
79- TF_Output* outputs = nullptr ;
80- LOG (INFO ) << " Calling Java gradient function for operation of type " << op_type;
81-
82- TFJ_GradFuncAdapter adapter = found_adapter->second ;
83- if (adapter == nullptr ) {
84- if (inputs != nullptr ) free (inputs);
85- return errors::Unknown (" Null Java gradient adapter for op " , op_type);
86- }
87- LOG (INFO ) << " Adapter ptr for " << op_type << " = " << reinterpret_cast <void *>(found_adapter->second );
88- const int num_outputs = adapter (
89- static_cast <TFJ_GraphId>(scope.graph ()),
90- struct_cast<TFJ_Scope>(const_cast <Scope*>(&scope)),
91- struct_cast<TF_Operation>(op.node ()),
92- inputs,
93- num_inputs,
94- &outputs
95- );
96-
97- // Always free inputs, even on error paths.
98- if (inputs != nullptr ) free (inputs);
99-
100- // Adapter contract hardening:
101- // - On Java exception / failure, adapter should return negative or outputs==nullptr.
102- if (num_outputs < 0 ) {
103- if (outputs != nullptr ) free (outputs);
104- return errors::Unknown (" Java custom gradient adapter failed for op " , op_type,
105- " (num_outputs=" , num_outputs, " )" );
106- }
107- if (num_outputs > 0 && outputs == nullptr ) {
108- return errors::Unknown (" Java custom gradient adapter returned null outputs for op " ,
109- op_type, " with num_outputs=" , num_outputs);
110- }
111-
112- grad_outputs->reserve (grad_outputs->size () + static_cast <size_t >(num_outputs));
113-
114- for (int i = 0 ; i < num_outputs; ++i) {
115- const TF_Output out = outputs[i];
116-
117- // "NoGradient" sentinel from Java: TF_Output.oper == nullptr
118- if (out.oper == nullptr ) {
119- // Represent "no gradient" as an empty Output.
120- // TF's gradient builder should tolerate missing gradients for non-differentiable inputs.
121- grad_outputs->push_back (Output ());
122- continue ;
123- }
124-
125- grad_outputs->push_back (Output (struct_cast<Node>(out.oper ), out.index ));
126- }
127-
128- if (outputs != nullptr ) free (outputs);
129- return OkStatus ();
130- }
34+ namespace java {
35+
36+ using namespace tsl ;
37+ using namespace std ;
38+
39+ unordered_map<string, TFJ_GradFuncAdapter> g_grad_func_adapters;
40+
41+ // Cast helper (inspired by TF C-API)
42+ template <typename T, typename U>
43+ T* struct_cast (U* ptr) {
44+ return static_cast <T*>(static_cast <void *>(ptr));
45+ }
46+
47+ // Bridge called by TF runtime when building gradients for op
48+ Status CustomGradFunc (const Scope& scope,
49+ const Operation& op,
50+ const vector<Output>& grad_inputs,
51+ vector<Output>* grad_outputs) {
52+ const string& op_type = op.node ()->type_string ();
53+ auto found_adapter = g_grad_func_adapters.find (op_type);
54+ if (found_adapter == g_grad_func_adapters.end ()) {
55+ return errors::NotFound (" No gradient adapter found for operation " , op_type);
56+ }
57+
58+ TFJ_GradFuncAdapter adapter = found_adapter->second ;
59+ if (adapter == nullptr ) {
60+ return errors::Unknown (" Null Java gradient adapter for op " , op_type);
61+ }
62+
63+ const int num_inputs = static_cast <int >(grad_inputs.size ());
64+
65+ TF_Output* inputs = nullptr ;
66+ if (num_inputs > 0 ) {
67+ inputs = static_cast <TF_Output*>(malloc (num_inputs * sizeof (TF_Output)));
68+ if (inputs == nullptr ) {
69+ return errors::ResourceExhausted (
70+ " Out of memory allocating inputs for custom gradient of op " , op_type);
71+ }
72+ }
73+
74+ for (int i = 0 ; i < num_inputs; ++i) {
75+ const Output& grad_input = grad_inputs[i];
76+ inputs[i].oper = struct_cast<TF_Operation>(grad_input.node ());
77+ inputs[i].index = grad_input.index ();
78+ }
79+
80+ TF_Output* outputs = nullptr ;
81+
82+ LOG (INFO ) << " Calling Java gradient function for operation of type " << op_type;
83+ const int num_outputs = adapter (
84+ static_cast <TFJ_GraphId>(scope.graph ()),
85+ struct_cast<TFJ_Scope>(const_cast <Scope*>(&scope)),
86+ struct_cast<TF_Operation>(op.node ()),
87+ inputs,
88+ num_inputs,
89+ &outputs);
90+
91+ if (inputs != nullptr ) free (inputs);
92+
93+ // Adapter contract:
94+ // - num_outputs < 0 indicates failure
95+ // - num_outputs == 0: OK, outputs may be nullptr
96+ // - num_outputs > 0: outputs must be non-null
97+ if (num_outputs < 0 ) {
98+ if (outputs != nullptr ) free (outputs);
99+ return errors::Unknown (" Java custom gradient adapter failed for op " , op_type,
100+ " (num_outputs=" , num_outputs, " )" );
101+ }
102+ if (num_outputs > 0 && outputs == nullptr ) {
103+ return errors::Unknown (" Java custom gradient adapter returned null outputs for op " ,
104+ op_type, " with num_outputs=" , num_outputs);
105+ }
106+
107+ grad_outputs->reserve (grad_outputs->size () + static_cast <size_t >(num_outputs));
108+
109+ for (int i = 0 ; i < num_outputs; ++i) {
110+ const TF_Output out = outputs[i];
111+
112+ // Convention: out.oper == nullptr => NoGradient
113+ if (out.oper == nullptr ) {
114+ grad_outputs->push_back (Output ()); // TF interprets empty Output as "no grad"
115+ continue ;
131116 }
117+
118+ grad_outputs->push_back (Output (struct_cast<Node>(out.oper ), out.index ));
119+ }
120+
121+ if (outputs != nullptr ) free (outputs); // allocated from Java via malloc
122+ return OkStatus ();
132123}
133124
125+ } // namespace java
126+ } // namespace tensorflow
127+
134128using namespace tensorflow ::ops;
135129using namespace tensorflow ::java;
136130
137131bool TFJ_HasGradient (const char * op_type) {
138- GradFunc dummy;
139- tsl::Status status = GradOpRegistry::Global ()->Lookup (op_type, &dummy);
140- return status.ok ();
132+ GradFunc dummy;
133+ tsl::Status status = GradOpRegistry::Global ()->Lookup (op_type, &dummy);
134+ return status.ok ();
141135}
142136
143137bool TFJ_RegisterCustomGradient (const char * op_type, TFJ_GradFuncAdapter grad_func_adapter) {
144- LOG (INFO ) << " TFJ_RegisterCustomGradient(" << op_type << " ) adapter_ptr="
145- << reinterpret_cast <void *>(grad_func_adapter);
146-
147- if (grad_func_adapter == nullptr ) {
148- LOG (ERROR ) << " Refusing to register NULL Java gradient adapter for op " << op_type;
149- return false ;
150- }
151-
152- if (TFJ_HasGradient (op_type)) { // Check if gradient already exists otherwise the JVM might abort/crash
153- LOG (WARNING ) << " Tried to register Java gradient function for operation " << op_type
154- << " , which has already a registered function" ;
155- return false ;
156- }
157- bool registered = GradOpRegistry::Global ()->Register (op_type, CustomGradFunc);
158- if (registered) {
159- g_grad_func_adapters.insert ({op_type, grad_func_adapter});
160- }
161- return registered;
138+ LOG (INFO ) << " TFJ_RegisterCustomGradient(" << op_type << " ) adapter_ptr="
139+ << reinterpret_cast <void *>(grad_func_adapter);
140+
141+ if (grad_func_adapter == nullptr ) {
142+ LOG (ERROR ) << " Refusing to register NULL Java gradient adapter for op " << op_type;
143+ return false ;
144+ }
145+
146+ if (TFJ_HasGradient (op_type)) {
147+ LOG (WARNING ) << " Tried to register Java gradient function for operation " << op_type
148+ << " , which has already a registered function" ;
149+ return false ;
150+ }
151+
152+ bool registered = GradOpRegistry::Global ()->Register (op_type, CustomGradFunc);
153+ if (registered) {
154+ g_grad_func_adapters.insert ({op_type, grad_func_adapter});
155+ }
156+ return registered;
162157}
163158
164- #else // #ifndef _WIN32
165-
166- /* This extension is not available on Windows */
159+ #else // _WIN32
167160
168161bool TFJ_HasGradient (const char * op_type) { return true ; }
169- bool TFJ_RegisterCustomGradient (const char * op_type, TFJ_GradFuncAdapter grad_func_adapter) { return false ; }
162+ bool TFJ_RegisterCustomGradient (const char * op_type, TFJ_GradFuncAdapter grad_func_adapter) {
163+ return false ;
164+ }
170165
171- #endif // #ifndef _WIN32
166+ #endif // _WIN32
0 commit comments