Repository: microsoft/onnxruntime
File: onnxruntime/core/optimizer/common_subexpression_elimination.cc (C++)

Code before:
  }

  return result;
}

template <typename Range>
bool AreRangesEqual(const Range& lhs, const Range& rhs) {
  return lhs.size() == rhs.size() &&
         std::equal(lhs.begin(), lhs.end(), rhs.begin());
}

// Check if two tensor attributes are equal scalar tensors, mainly to support ConstantOfShape Op.
// Currently support float, float16 and int64 data types, and requires the data are raw data in TensorProto.
bool AreScalarTensorAttributeEqual(const ONNX_NAMESPACE::TensorProto& lhs_t, const ONNX_NAMESPACE::TensorProto& rhs_t) {
  if (!(utils::HasDataType(lhs_t) && utils::HasDataType(rhs_t) && lhs_t.data_type() == rhs_t.data_type() &&
        (lhs_t.data_type() == onnx::TensorProto_DataType_FLOAT ||
         lhs_t.data_type() == onnx::TensorProto_DataType_FLOAT16 ||
         lhs_t.data_type() == onnx::TensorProto_DataType_INT64) &&
        lhs_t.dims_size() == 1 && rhs_t.dims_size() == 1 && lhs_t.dims()[0] == 1 && rhs_t.dims()[0] == 1 &&
        utils::HasRawData(lhs_t) && utils::HasRawData(rhs_t))) {
    return false;
  }
  const void* lhs_value = lhs_t.raw_data().data();
  const void* rhs_value = rhs_t.raw_data().data();
  switch (lhs_t.data_type()) {
    case onnx::TensorProto_DataType_FLOAT: {
      float lhs_float_value, rhs_float_value;
      std::memcpy(&lhs_float_value, lhs_value, sizeof(lhs_float_value));
      std::memcpy(&rhs_float_value, rhs_value, sizeof(rhs_float_value));
      return lhs_float_value == rhs_float_value;
    }
    case onnx::TensorProto_DataType_FLOAT16: {
      MLFloat16 lhs_float16_value, rhs_float16_value;
      std::memcpy(&lhs_float16_value, lhs_value, sizeof(lhs_float16_value));
      std::memcpy(&rhs_float16_value, rhs_value, sizeof(rhs_float16_value));
      return lhs_float16_value == rhs_float16_value;
    }
    case onnx::TensorProto_DataType_INT64: {
      int64_t lhs_int64_value, rhs_int64_value;
      std::memcpy(&lhs_int64_value, lhs_value, sizeof(lhs_int64_value));
      std::memcpy(&rhs_int64_value, rhs_value, sizeof(rhs_int64_value));
      return lhs_int64_value == rhs_int64_value;
    }
    default:
      break;
  }
  return false;
}

bool AreEqual(const ONNX_NAMESPACE::AttributeProto& lhs, const ONNX_NAMESPACE::AttributeProto& rhs) {
  if (&lhs == &rhs) {

Review comment (suggestion):
There is a number of problems with this code and the below. Let's try to make this fix complete.

To make sure the alignment and the endianess are respected on all platforms, as well as we cover more potential types, we want to use `utils::UnpackTensor<T>()` for individual types.

Another potentially better alternative for both locations here and for the hash computation would be `utils::UnpackInitializerData()` that returns `std::vector<uint8_t>`.

This way we also avoid the requirement of having raw data and cover more ground.

The functions are declared in core/framework/tensorprotoutils.h.

Code after:
  }

  return result;
}

template <typename Range>
bool AreRangesEqual(const Range& lhs, const Range& rhs) {
  return lhs.size() == rhs.size() &&
         std::equal(lhs.begin(), lhs.end(), rhs.begin());
}

// Check if two tensor attributes are equal scalar tensors, mainly to support ConstantOfShape Op.
bool AreScalarTensorAttributeEqual(const ONNX_NAMESPACE::TensorProto& lhs_t, const ONNX_NAMESPACE::TensorProto& rhs_t) {
  if (!(utils::HasDataType(lhs_t) && utils::HasDataType(rhs_t) && lhs_t.data_type() == rhs_t.data_type() &&
        lhs_t.data_type() != onnx::TensorProto_DataType_STRING &&
        lhs_t.dims_size() == 1 && rhs_t.dims_size() == 1 && lhs_t.dims()[0] == 1 && rhs_t.dims()[0] == 1)) {
    return false;
  }
  std::vector<uint8_t> unpacked_lhs_tensor, unpacked_rhs_tensor;
  if (!utils::UnpackInitializerData(lhs_t, unpacked_lhs_tensor).IsOK() ||
      !utils::UnpackInitializerData(rhs_t, unpacked_rhs_tensor).IsOK()) {
    return false;
  }
  return unpacked_lhs_tensor == unpacked_rhs_tensor;
}

bool AreEqual(const ONNX_NAMESPACE::AttributeProto& lhs, const ONNX_NAMESPACE::AttributeProto& rhs) {
  if (&lhs == &rhs) {
    return true;
  }

  if (lhs.type() != rhs.type() || lhs.name() != rhs.name()) {
    return false;
  }

  switch (lhs.type()) {
    case onnx::AttributeProto_AttributeType_FLOAT:
      return lhs.f() == rhs.f();
    case onnx::AttributeProto_AttributeType_INT:
      return lhs.i() == rhs.i();
    case onnx::AttributeProto_AttributeType_STRING:
      return lhs.s() == rhs.s();
    case onnx::AttributeProto_AttributeType_FLOATS:
      return AreRangesEqual(lhs.floats(), rhs.floats());
    case onnx::AttributeProto_AttributeType_INTS:
      return AreRangesEqual(lhs.ints(), rhs.ints());
    case onnx::AttributeProto_AttributeType_STRINGS:
      return AreRangesEqual(lhs.strings(), rhs.strings());
    case onnx::AttributeProto_AttributeType_TENSOR:
      return AreScalarTensorAttributeEqual(lhs.t(), rhs.t());
    case onnx::AttributeProto_AttributeType_GRAPH: