Repository: microsoft/onnxruntime
File: onnxruntime/core/optimizer/qdq_transformer/selectors_actions/qdq_selector_action_transformer.cc (C++)

Code before:
#if !defined(ORT_MINIMAL_BUILD)
  // TODO: Enable 16-bit types in selector when ArgMax supports 16-bit integer input tensors.
  std::vector<const char*> providers = {kCpuExecutionProvider, kDmlExecutionProvider};
  std::unique_ptr<NodeSelector> selector = std::make_unique<QDQ::DropDQNodesSelector>(false, false, providers);
  qdq_selector_action_registry.RegisterSelectorAndAction(action_name,
                                                         {{"ArgMax", {}}},
                                                         std::move(selector),
                                                         std::move(action));
#else
  qdq_selector_action_registry.RegisterAction(action_name, std::move(action));
#endif
}

void UnaryOpQDQRules(SelectorActionRegistry& qdq_selector_action_registry) {
  // 3 nodes. DQ, target, Q
  // Replace with internal QLinear version of operator. Delete all original nodes.
  const std::string action_name{"1DQ"};
  std::unique_ptr<Action> action = std::make_unique<QDQ::UnaryReplaceWithQLinear>(kMSDomain);

#if !defined(ORT_MINIMAL_BUILD)
  // Operators without DML QLinear kernel support (CPU only).
  // DML EP does not have QLinearLeakyRelu or QLinearSoftmax kernels.
  // See https://github.com/microsoft/onnxruntime/issues/26531
  std::vector<const char*> cpu_only_providers = {kCpuExecutionProvider};
  std::unique_ptr<NodeSelector> cpu_selector = std::make_unique<QDQ::UnarySelector>(cpu_only_providers);
  qdq_selector_action_registry.RegisterSelectorAndAction("1DQ_CPU",
                                                         {{"LeakyRelu", {}},
                                                          {"Softmax", {}}},
                                                         std::move(cpu_selector),
                                                         std::make_unique<QDQ::UnaryReplaceWithQLinear>(kMSDomain));

  // Operators with DML QLinear kernel support (QLinearAveragePool, QLinearGlobalAveragePool, QLinearSigmoid)
  std::vector<const char*> providers = {kCpuExecutionProvider, kDmlExecutionProvider};
  std::unique_ptr<NodeSelector> selector = std::make_unique<QDQ::UnarySelector>(providers);
  qdq_selector_action_registry.RegisterSelectorAndAction(action_name,
                                                         {{"AveragePool", {}},
                                                          {"GlobalAveragePool", {}},
                                                          {"Sigmoid", {}}},
                                                         std::move(selector),
                                                         std::move(action));
#else
  qdq_selector_action_registry.RegisterAction(action_name, std::move(action));
#endif
}

void BinaryOpQDQRules(SelectorActionRegistry& qdq_selector_action_registry) {
  // 4 nodes. 2 x DQ for inputs, target, Q
  // Replace with internal QLinear version of operator. Delete all original nodes.
  {
    const std::string action_name{"2DQ"};
    std::unique_ptr<Action> action = std::make_unique<QDQ::BinaryReplaceWithQLinear>(kMSDomain);

Review comment (performance):
a call to `qdq_selector_action_registry.RegisterAction()` with the same action name needs to be made in the minimal build path, otherwise it won't be able to look up the corresponding action in a minimal build.

here's an example:
https://github.com/microsoft/onnxruntime/blob/07724d5c3277d2b2bdaad7687272a9beb6bd2ef7/onnxruntime/core/optimizer/qdq_transformer/selectors_actions/qdq_selector_action_transformer.cc#L61-L116

Code after:
#if !defined(ORT_MINIMAL_BUILD)
  // TODO: Enable 16-bit types in selector when ArgMax supports 16-bit integer input tensors.
  std::vector<const char*> providers = {kCpuExecutionProvider, kDmlExecutionProvider};
  std::unique_ptr<NodeSelector> selector = std::make_unique<QDQ::DropDQNodesSelector>(false, false, providers);
  qdq_selector_action_registry.RegisterSelectorAndAction(action_name,
                                                         {{"ArgMax", {}}},
                                                         std::move(selector),
                                                         std::move(action));
#else
  qdq_selector_action_registry.RegisterAction(action_name, std::move(action));
#endif
}

void UnaryOpQDQRules(SelectorActionRegistry& qdq_selector_action_registry) {
  // 3 nodes. DQ, target, Q
  // Replace with internal QLinear version of operator. Delete all original nodes.
  const std::string action_name{"1DQ"};
  const std::string cpu_action_name{"1DQ_CPU"};
  std::unique_ptr<Action> action = std::make_unique<QDQ::UnaryReplaceWithQLinear>(kMSDomain);
  std::unique_ptr<Action> cpu_action = std::make_unique<QDQ::UnaryReplaceWithQLinear>(kMSDomain);

#if !defined(ORT_MINIMAL_BUILD)
  // Operators with DML QLinear kernel support (QLinearAveragePool, QLinearGlobalAveragePool, QLinearSigmoid)
  std::vector<const char*> providers = {kCpuExecutionProvider, kDmlExecutionProvider};
  std::unique_ptr<NodeSelector> selector = std::make_unique<QDQ::UnarySelector>(providers);
  qdq_selector_action_registry.RegisterSelectorAndAction(action_name,
                                                         {{"AveragePool", {}},
                                                          {"GlobalAveragePool", {}},
                                                          {"Sigmoid", {}}},
                                                         std::move(selector),
                                                         std::move(action));

  // Operators without DML QLinear kernel support (CPU only).
  // DML EP does not have QLinearLeakyRelu or QLinearSoftmax kernels.
  // See https://github.com/microsoft/onnxruntime/issues/26531
  std::vector<const char*> cpu_only_provider = {kCpuExecutionProvider};
  std::unique_ptr<NodeSelector> cpu_selector = std::make_unique<QDQ::UnarySelector>(cpu_only_provider);
  qdq_selector_action_registry.RegisterSelectorAndAction(cpu_action_name,
                                                         {{"LeakyRelu", {}},
                                                          {"Softmax", {}}},
                                                         std::move(cpu_selector),
                                                         std::move(cpu_action));
#else
  qdq_selector_action_registry.RegisterAction(action_name, std::move(action));
  qdq_selector_action_registry.RegisterAction(cpu_action_name, std::move(cpu_action));
#endif
}

void BinaryOpQDQRules(SelectorActionRegistry& qdq_selector_action_registry) {
  // 4 nodes. 2 x DQ for inputs, target, Q
  // Replace with internal QLinear version of operator. Delete all original nodes.