@@ -791,6 +791,32 @@ Error defineConvertNode(
791791
792792 return Error::Ok;
793793};
794+ /*
795+ Look up a serialized tensor value (plain or quantized wrapper) by its
796+ output id. Returns nullptr if not found.
797+ */
798+ const fb_xnnpack::XNNTensorValue* getSerializedTensorValue (
799+ const fb_xnnpack::XNNGraph* graph,
800+ uint32_t id) noexcept {
801+ if (graph == nullptr || graph->xvalues () == nullptr ) {
802+ return nullptr ;
803+ }
804+ for (auto value : *graph->xvalues ()) {
805+ const fb_xnnpack::XNNTensorValue* tv = nullptr ;
806+ if (value->xvalue_union_type () == fb_xnnpack::XValueUnion::XNNTensorValue) {
807+ tv = value->xvalue_union_as_XNNTensorValue ();
808+ } else if (
809+ value->xvalue_union_type () ==
810+ fb_xnnpack::XValueUnion::XNNQuantizedTensorValue) {
811+ tv = value->xvalue_union_as_XNNQuantizedTensorValue ()->tensor_value ();
812+ }
813+ if (tv != nullptr && tv->id_out () == id) {
814+ return tv;
815+ }
816+ }
817+ return nullptr ;
818+ }
819+
794820/*
795821Define serialized linear(fully-connected) node into the subgraph using
796822the remapped ids to map the serialized ids, to the new ids generated
@@ -810,14 +836,52 @@ Error defineFullyConnectedNode(
810836 REMAP_ID (remapped_ids, graph_node->bias_id (), fc_bias);
811837 REMAP_ID (remapped_ids, graph_node->output_id (), fc_output);
812838
839+ // XNNPACK only provides a bf16 fully-connected of type bf16_bf16_f32:
840+ // bf16 activation x bf16 weight -> fp32 output. When the serialized graph
841+ // asks for a bf16 output (e.g. a fully bf16 model), define the FC with an
842+ // fp32 intermediate output and append a convert (fp32 -> bf16) so the
843+ // delegate boundary stays bf16.
844+ const auto * in_tv = getSerializedTensorValue (graph, graph_node->input1_id ());
845+ const auto * filt_tv =
846+ getSerializedTensorValue (graph, graph_node->filter_id ());
847+ const auto * out_tv = getSerializedTensorValue (graph, graph_node->output_id ());
848+ const bool needs_bf16_output_convert = in_tv != nullptr &&
849+ filt_tv != nullptr && out_tv != nullptr &&
850+ in_tv->datatype () == DataType::xnn_datatype_bf16 &&
851+ filt_tv->datatype () == DataType::xnn_datatype_bf16 &&
852+ out_tv->datatype () == DataType::xnn_datatype_bf16;
853+
854+ uint32_t fc_compute_output = fc_output;
855+ if (needs_bf16_output_convert) {
856+ std::vector<size_t > out_dims =
857+ flatbufferDimsToVector<size_t >(out_tv->dims ());
858+ uint32_t intermediate_id = XNN_INVALID_VALUE_ID ;
859+ xnn_status ts = xnn_define_tensor_value (
860+ subgraph_ptr,
861+ xnn_datatype_fp32,
862+ out_dims.size (),
863+ out_dims.data (),
864+ /* data=*/ nullptr ,
865+ /* external_id=*/ XNN_INVALID_VALUE_ID ,
866+ /* flags=*/ 0 ,
867+ &intermediate_id);
868+ ET_CHECK_OR_RETURN_ERROR (
869+ ts == xnn_status_success,
870+ Internal,
871+ " Failed to define fp32 intermediate for bf16 linear node %i: %s" ,
872+ node->debug_handle (),
873+ xnn_status_to_string (ts));
874+ fc_compute_output = intermediate_id;
875+ }
876+
813877 xnn_status status = xnn_define_fully_connected (
814878 subgraph_ptr,
815879 min_max.first ,
816880 min_max.second ,
817881 fc_input1,
818882 fc_filter,
819883 fc_bias,
820- fc_output ,
884+ fc_compute_output ,
821885 graph_node->flags ());
822886 ET_CHECK_OR_RETURN_ERROR (
823887 status == xnn_status_success,
@@ -826,6 +890,17 @@ Error defineFullyConnectedNode(
826890 node->debug_handle (),
827891 xnn_status_to_string (status));
828892
893+ if (needs_bf16_output_convert) {
894+ xnn_status cs = xnn_define_convert (
895+ subgraph_ptr, fc_compute_output, fc_output, /* flags=*/ 0 );
896+ ET_CHECK_OR_RETURN_ERROR (
897+ cs == xnn_status_success,
898+ Internal,
899+ " Failed to define bf16 output convert for linear node %i: %s" ,
900+ node->debug_handle (),
901+ xnn_status_to_string (cs));
902+ }
903+
829904 return Error::Ok;
830905};
831906
0 commit comments