3131#ifdef TVM_GRAPH_EXECUTOR_ARM_COMPUTE_LIB
3232#include < arm_compute/core/Types.h>
3333#include < arm_compute/runtime/NEON/functions/NEArithmeticAddition.h>
34+ #include < arm_compute/runtime/NEON/functions/NEConcatenateLayer.h>
3435#include < arm_compute/runtime/NEON/functions/NEConvolutionLayer.h>
3536#include < arm_compute/runtime/NEON/functions/NEDepthwiseConvolutionLayer.h>
3637#include < arm_compute/runtime/NEON/functions/NEElementwiseOperations.h>
@@ -91,12 +92,21 @@ class ACLRuntime : public JSONRuntimeBase {
9192 * \return Status of inference.
9293 */
9394 void Run () override {
94- for (size_t i = 0 ; i < input_nodes_.size (); ++i) {
95- auto nid = input_nodes_[i];
96- uint32_t eid = EntryID (nid, 0 );
95+ for (size_t nid_idx = 0 ; nid_idx < input_nodes_.size (); ++nid_idx) {
96+ auto nid = input_nodes_[nid_idx];
9797 if (nodes_[nid].GetOpType () == " input" ) {
98- void * data = data_entry_[eid]->data ;
99- CheckACLError (layer_.inputs [i].allocator ()->import_memory (data));
98+ for (uint32_t eid_idx = 0 ; eid_idx < nodes_[nid].GetNumOutput (); eid_idx++) {
99+ uint32_t eid = EntryID (nid, eid_idx);
100+ void * data = data_entry_[eid]->data ;
101+ auto key = std::pair<uint32_t , uint32_t >(nid, eid_idx);
102+ if (layer_.json_inputid_to_layer_inputid .count (key) > 0 ) {
103+ CheckACLError (
104+ layer_.inputs [layer_.json_inputid_to_layer_inputid [key]].allocator ()->import_memory (
105+ data));
106+ } else {
107+ CheckACLError (layer_.inputs [nid_idx].allocator ()->import_memory (data));
108+ }
109+ }
100110 }
101111 }
102112
@@ -149,6 +159,8 @@ class ACLRuntime : public JSONRuntimeBase {
149159 CreateMaximumLayer (&layer_, node);
150160 } else if (" add" == op_name || " qnn.add" == op_name) {
151161 CreateAddLayer (&layer_, node);
162+ } else if (" concatenate" == op_name) {
163+ CreateConcatenateLayer (&layer_, node);
152164 } else {
153165 LOG (FATAL) << " Unsupported op: " << op_name;
154166 }
@@ -166,6 +178,9 @@ class ACLRuntime : public JSONRuntimeBase {
166178 std::shared_ptr<arm_compute::IFunction> function;
167179 std::vector<arm_compute::Tensor> inputs;
168180 std::vector<arm_compute::Tensor> outputs;
181+ // maps the input index of JSON node to the index of the ACL layer's inputs
182+ // this is optional (i.e.only when an operator uses the eid index)
183+ std::map<std::pair<uint32_t , uint32_t >, uint32_t > json_inputid_to_layer_inputid;
169184 };
170185
171186 /* !
@@ -175,17 +190,25 @@ class ACLRuntime : public JSONRuntimeBase {
175190 * \param tensor The tensor to represent.
176191 * \param scale (optional) The scale of the tensor as an input.
177192 * \param offset (optional) The offset of the tensor as an input.
193+ * \param apply_dim_correction (Optional) Flag to state whether apply dimension correction after
194+ * setting one dimension. E.g. when permuting NCHW -> NHWC, 1x1x2 would become 2x1x1, but
195+ * _num_dimensions should be 3 rather than 1.
196+ * \param increase_dim_unit (Optional) Set to true if new unit dimensions increase the number of
197+ * dimensions of the shape.
178198 * \return ACL Tensor.
179199 */
180200 arm_compute::Tensor MakeACLTensorFromJSONEntry (const JSONGraphNodeEntry& tensor,
181201 JSONGraphNodeEntry* scale = nullptr ,
182- JSONGraphNodeEntry* offset = nullptr ) {
202+ JSONGraphNodeEntry* offset = nullptr ,
203+ bool apply_dim_correction = true ,
204+ bool increase_dim_unit = true ) {
183205 JSONGraphNode node = nodes_[tensor.id_ ];
184206 void * node_data = nullptr ;
185207 if (node.GetOpType () == " const" ) {
186208 node_data = data_entry_[EntryID (tensor)]->data ;
187209 }
188- return MakeACLTensorFromJSONNode (node, scale, offset, node_data);
210+ return MakeACLTensorFromJSONNode (node, scale, offset, node_data, apply_dim_correction,
211+ increase_dim_unit, tensor.index_ );
189212 }
190213
191214 /* !
@@ -196,19 +219,26 @@ class ACLRuntime : public JSONRuntimeBase {
196219 * \param scale (optional) The scale of the tensor as an input.
197220 * \param offset (optional) The offset of the tensor as an input.
198221 * \param data (optional) Constant data of input node.
222+ * \param apply_dim_correction (Optional) Flag to state whether apply dimension correction after
223+ * setting one dimension. E.g. when permuting NCHW -> NHWC, 1x1x2 would become 2x1x1, but
224+ * _num_dimensions should be 3 rather than 1.
225+ * \param increase_dim_unit (Optional) Set to true if new unit dimensions increase the number of
226+ * dimensions of the shape.
227+ * \param entry_index The entry index.
199228 * \return ACL Tensor.
200229 */
201- arm_compute::Tensor MakeACLTensorFromJSONNode (const JSONGraphNode& node,
202- JSONGraphNodeEntry* scale = nullptr ,
203- JSONGraphNodeEntry* offset = nullptr ,
204- void * data = nullptr ) {
230+ arm_compute::Tensor MakeACLTensorFromJSONNode (
231+ const JSONGraphNode& node, JSONGraphNodeEntry* scale = nullptr ,
232+ JSONGraphNodeEntry* offset = nullptr , void * data = nullptr , bool apply_dim_correction = true ,
233+ bool increase_dim_unit = true , uint32_t entry_index = 0 ) {
205234 const DLTensor* scale_data = nullptr ;
206235 const DLTensor* offset_data = nullptr ;
207236 if (scale && offset) {
208237 scale_data = data_entry_[EntryID (*scale)];
209238 offset_data = data_entry_[EntryID (*offset)];
210239 }
211- return MakeACLTensor (node, data, scale_data, offset_data);
240+ return MakeACLTensor (node, data, scale_data, offset_data, apply_dim_correction,
241+ increase_dim_unit, entry_index);
212242 }
213243
214244 /* !
@@ -510,6 +540,34 @@ class ACLRuntime : public JSONRuntimeBase {
510540 layer->function = f;
511541 }
512542
543+ /* !
544+ * \brief Create a Concatenate layer.
545+ *
546+ * \param layer The ACL layer to build. Containing inputs, outputs and the ACL function.c
547+ * \param node The JSON representation of the operator.
548+ */
549+ void CreateConcatenateLayer (CachedLayer* layer, const JSONGraphNode& node) {
550+ std::vector<std::string> axis = node.GetAttr <std::vector<std::string>>(" axis" );
551+ std::vector<const arm_compute::ITensor*> inputs;
552+ for (auto input : node.GetInputs ()) {
553+ layer->inputs .push_back (MakeACLTensorFromJSONEntry (input, nullptr , nullptr , false ));
554+ layer->json_inputid_to_layer_inputid [std::pair<uint32_t , uint32_t >(input.id_ , input.index_ )] =
555+ layer->inputs .size () - 1 ;
556+ }
557+ for (size_t i = 0 ; i < layer->inputs .size (); i++) {
558+ inputs.push_back (&layer->inputs [i]);
559+ }
560+ layer->outputs .push_back (MakeACLTensorFromJSONNode (node));
561+ int dimNum = layer->inputs [0 ].info ()->num_dimensions ();
562+ auto function = std::make_shared<arm_compute::NEConcatenateLayer>();
563+ // the shape of input tensor will be reversed after passing to ACL
564+ // for example a tensor with shape [1, 2, 3, 4] will be changed to
565+ // [4, 3, 2, 1] at ACL side. So the axis here should be preprocessed.
566+ auto a = std::stoi (axis[0 ]);
567+ function->configure (inputs, &layer->outputs [0 ], a < 0 ? -a - 1 : dimNum - a - 1 );
568+ layer->function = function;
569+ }
570+
513571 /* ! \brief Allow ACL functions to request auxiliary memory from TVM. */
514572 ACLAllocator allocator_;
515573 /* !
0 commit comments