1 // Copyright 2020 Google LLC
2 //
3 // This source code is licensed under the BSD-style license found in the
4 // LICENSE file in the root directory of this source tree.
5
6 #include <math.h>
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <string.h>
10
11 #include <xnnpack.h>
12 #include <xnnpack/log.h>
13 #include <xnnpack/params.h>
14 #include <xnnpack/subgraph.h>
15
16
create_divide_operator(const struct xnn_node * node,const struct xnn_value * values,size_t num_values,struct xnn_operator_data * opdata)17 static enum xnn_status create_divide_operator(
18 const struct xnn_node* node,
19 const struct xnn_value* values,
20 size_t num_values,
21 struct xnn_operator_data* opdata)
22 {
23 assert(node->compute_type == xnn_compute_type_fp32);
24
25 assert(node->num_inputs == 2);
26 const uint32_t input1_id = node->inputs[0];
27 assert(input1_id != XNN_INVALID_VALUE_ID);
28 assert(input1_id < num_values);
29 const uint32_t input2_id = node->inputs[1];
30 assert(input2_id != XNN_INVALID_VALUE_ID);
31 assert(input2_id < num_values);
32
33 assert(node->num_outputs == 1);
34 const uint32_t output_id = node->outputs[0];
35 assert(output_id != XNN_INVALID_VALUE_ID);
36 assert(output_id < num_values);
37
38 const enum xnn_status status = xnn_create_divide_nd_f32(
39 node->activation.output_min,
40 node->activation.output_max,
41 node->flags,
42 &opdata->operator_object);
43 if (status == xnn_status_success) {
44 opdata->shape1.num_dims = values[input1_id].shape.num_dims;
45 opdata->shape2.num_dims = values[input2_id].shape.num_dims;
46 if (values[output_id].layout == xnn_layout_type_nchw) {
47 assert(values[input1_id].layout == xnn_layout_type_nchw);
48 assert(values[input2_id].layout == xnn_layout_type_nchw);
49 opdata->shape1.dim[0] = values[input1_id].shape.dim[0];
50 opdata->shape1.dim[1] = values[input1_id].shape.dim[values[input1_id].shape.num_dims - 1];
51 if (values[input1_id].shape.num_dims > 2) {
52 memcpy(&opdata->shape1.dim[2], &values[input1_id].shape.dim[1], (values[input1_id].shape.num_dims - 2) * sizeof(size_t));
53 }
54 opdata->shape2.dim[0] = values[input2_id].shape.dim[0];
55 opdata->shape2.dim[1] = values[input2_id].shape.dim[values[input2_id].shape.num_dims - 1];
56 if (values[input1_id].shape.num_dims > 2) {
57 memcpy(&opdata->shape2.dim[2], &values[input2_id].shape.dim[1], (values[input2_id].shape.num_dims - 2) * sizeof(size_t));
58 }
59 } else {
60 assert(values[output_id].layout == xnn_layout_type_nhwc);
61 assert(values[input1_id].layout == xnn_layout_type_nhwc);
62 assert(values[input2_id].layout == xnn_layout_type_nhwc);
63 memcpy(opdata->shape1.dim, values[input1_id].shape.dim, values[input1_id].shape.num_dims * sizeof(size_t));
64 memcpy(opdata->shape2.dim, values[input2_id].shape.dim, values[input2_id].shape.num_dims * sizeof(size_t));
65 }
66 opdata->inputs[0] = input1_id;
67 opdata->inputs[1] = input2_id;
68 opdata->outputs[0] = output_id;
69 }
70 return status;
71 }
72
setup_divide_operator(const struct xnn_operator_data * opdata,const struct xnn_blob * blobs,size_t num_blobs,pthreadpool_t threadpool)73 static enum xnn_status setup_divide_operator(
74 const struct xnn_operator_data* opdata,
75 const struct xnn_blob* blobs,
76 size_t num_blobs,
77 pthreadpool_t threadpool)
78 {
79 const uint32_t input1_id = opdata->inputs[0];
80 assert(input1_id != XNN_INVALID_VALUE_ID);
81 assert(input1_id < num_blobs);
82
83 const uint32_t input2_id = opdata->inputs[1];
84 assert(input2_id != XNN_INVALID_VALUE_ID);
85 assert(input2_id < num_blobs);
86
87 const uint32_t output_id = opdata->outputs[0];
88 assert(output_id != XNN_INVALID_VALUE_ID);
89 assert(output_id < num_blobs);
90
91 const struct xnn_blob* input1_blob = blobs + input1_id;
92 const void* input1_data = input1_blob->data;
93 assert(input1_data != NULL);
94
95 const struct xnn_blob* input2_blob = blobs + input2_id;
96 const void* input2_data = input2_blob->data;
97 assert(input2_data != NULL);
98
99 const struct xnn_blob* output_blob = blobs + output_id;
100 void* output_data = output_blob->data;
101 assert(output_data != NULL);
102
103 return xnn_setup_divide_nd_f32(
104 opdata->operator_object,
105 opdata->shape1.num_dims,
106 opdata->shape1.dim,
107 opdata->shape2.num_dims,
108 opdata->shape2.dim,
109 input1_data, input2_data, output_data,
110 threadpool);
111 }
112
xnn_define_divide(xnn_subgraph_t subgraph,float output_min,float output_max,uint32_t input1_id,uint32_t input2_id,uint32_t output_id,uint32_t flags)113 enum xnn_status xnn_define_divide(
114 xnn_subgraph_t subgraph,
115 float output_min,
116 float output_max,
117 uint32_t input1_id,
118 uint32_t input2_id,
119 uint32_t output_id,
120 uint32_t flags)
121 {
122 if ((xnn_params.init_flags & XNN_INIT_FLAG_XNNPACK) == 0) {
123 xnn_log_error("failed to define %s operator: XNNPACK is not initialized",
124 xnn_node_type_to_string(xnn_node_type_divide));
125 return xnn_status_uninitialized;
126 }
127
128 if (isnan(output_min)) {
129 xnn_log_error(
130 "failed to define %s operator with NaN output lower bound: lower bound must be non-NaN",
131 xnn_node_type_to_string(xnn_node_type_divide));
132 return xnn_status_invalid_parameter;
133 }
134
135 if (isnan(output_max)) {
136 xnn_log_error(
137 "failed to define %s operator with NaN output upper bound: upper bound must be non-NaN",
138 xnn_node_type_to_string(xnn_node_type_divide));
139 return xnn_status_invalid_parameter;
140 }
141
142 if (output_min >= output_max) {
143 xnn_log_error(
144 "failed to define %s operator with [%.7g, %.7g] output range: lower bound must be below upper bound",
145 xnn_node_type_to_string(xnn_node_type_divide), output_min, output_max);
146 return xnn_status_invalid_parameter;
147 }
148
149 if (input1_id >= subgraph->num_values) {
150 xnn_log_error(
151 "failed to define %s operator with the first input ID #%" PRIu32 ": invalid Value ID",
152 xnn_node_type_to_string(xnn_node_type_divide), input1_id);
153 return xnn_status_invalid_parameter;
154 }
155
156 const struct xnn_value* input1_value = &subgraph->values[input1_id];
157 if (input1_value->type != xnn_value_type_dense_tensor) {
158 xnn_log_error(
159 "failed to define %s operator with the first input ID #%" PRIu32 ": unsupported Value type %d (expected dense tensor)",
160 xnn_node_type_to_string(xnn_node_type_divide), input1_id, input1_value->type);
161 return xnn_status_invalid_parameter;
162 }
163
164 switch (input1_value->datatype) {
165 case xnn_datatype_fp32:
166 break;
167 default:
168 xnn_log_error(
169 "failed to define %s operator with the first input ID #%" PRIu32 ": unsupported Value datatype %s (%d)",
170 xnn_node_type_to_string(xnn_node_type_divide), input1_id,
171 xnn_datatype_to_string(input1_value->datatype), input1_value->datatype);
172 return xnn_status_invalid_parameter;
173 }
174
175 if (input2_id >= subgraph->num_values) {
176 xnn_log_error(
177 "failed to define %s operator with the second input ID #%" PRIu32 ": invalid Value ID",
178 xnn_node_type_to_string(xnn_node_type_divide), input2_id);
179 return xnn_status_invalid_parameter;
180 }
181
182 const struct xnn_value* input2_value = &subgraph->values[input2_id];
183 if (input2_value->type != xnn_value_type_dense_tensor) {
184 xnn_log_error(
185 "failed to define %s operator with the second input ID #%" PRIu32 ": unsupported Value type %d (expected dense tensor)",
186 xnn_node_type_to_string(xnn_node_type_divide), input2_id, input2_value->type);
187 return xnn_status_invalid_parameter;
188 }
189
190 switch (input2_value->datatype) {
191 case xnn_datatype_fp32:
192 break;
193 default:
194 xnn_log_error(
195 "failed to define %s operator with the second input ID #%" PRIu32 ": unsupported Value datatype %s (%d)",
196 xnn_node_type_to_string(xnn_node_type_divide), input2_id,
197 xnn_datatype_to_string(input2_value->datatype), input2_value->datatype);
198 return xnn_status_invalid_parameter;
199 }
200
201 if (output_id >= subgraph->num_values) {
202 xnn_log_error(
203 "failed to define %s operator with output ID #%" PRIu32 ": invalid Value ID",
204 xnn_node_type_to_string(xnn_node_type_divide), output_id);
205 return xnn_status_invalid_parameter;
206 }
207
208 const struct xnn_value* output_value = &subgraph->values[output_id];
209 if (output_value->type != xnn_value_type_dense_tensor) {
210 xnn_log_error(
211 "failed to define %s operator with output ID #%" PRIu32 ": unsupported Value type %d (expected dense tensor)",
212 xnn_node_type_to_string(xnn_node_type_divide), output_id, output_value->type);
213 return xnn_status_invalid_parameter;
214 }
215
216 switch (output_value->datatype) {
217 case xnn_datatype_fp32:
218 break;
219 default:
220 xnn_log_error(
221 "failed to define %s operator with output ID #%" PRIu32 ": unsupported Value datatype %s (%d)",
222 xnn_node_type_to_string(xnn_node_type_divide), output_id,
223 xnn_datatype_to_string(output_value->datatype), output_value->datatype);
224 return xnn_status_invalid_parameter;
225 }
226
227 struct xnn_node* node = xnn_subgraph_new_node(subgraph);
228 if (node == NULL) {
229 return xnn_status_out_of_memory;
230 }
231
232 node->type = xnn_node_type_divide;
233 node->compute_type = xnn_compute_type_fp32;
234 node->activation.output_min = output_min;
235 node->activation.output_max = output_max;
236 node->num_inputs = 2;
237 node->inputs[0] = input1_id;
238 node->inputs[1] = input2_id;
239 node->num_outputs = 1;
240 node->outputs[0] = output_id;
241 node->flags = flags;
242
243 node->create = create_divide_operator;
244 node->setup = setup_divide_operator;
245
246 return xnn_status_success;
247 }
248