Skip to content

Commit

Permalink
Introduce centralised name transformation functions
Browse files Browse the repository at this point in the history
To address some of the concerns raised in apache#8280 and apache#8720 I've put together a series of functions to combine together names to re-use between these areas. These are meant to be a starting point to fix up the name generation to use the TVM C conventions and port the interface API header to C++.

These functions will also be used for constructing the names in the C Device API (apache/tvm-rfcs#31).
  • Loading branch information
Mousius committed Sep 24, 2021
1 parent d3d7e8e commit cca7176
Show file tree
Hide file tree
Showing 5 changed files with 474 additions and 0 deletions.
86 changes: 86 additions & 0 deletions python/tvm/relay/backend/name_transforms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""
Name transformation functions for use in code generation
"""

from typing import List, Union

from . import _backend


def to_c_function_style(original_name: str):
"""Transform a name to the C function style assuming it is
appropriately constructed using the prefixing functions
Parameters
----------
original_name : str
Original name to transform
"""
return _backend.ToCFunctionStyle(original_name)


def to_c_variable_style(original_name: str):
"""Transform a name to the C variable style assuming it is
appropriately constructed using the prefixing functions
Parameters
----------
original_name : str
Original name to transform
"""
return _backend.ToCVariableStyle(original_name)


def prefix_name(names: Union[List[str], str]):
"""Apply TVM-specific prefix to a function name
Parameters
----------
names : Union[List[str], str]
List of names to combine to form a combined name or the name itself
"""
if isinstance(names, str):
names = [names]

return _backend.PrefixName(names)


def prefix_generated_name(names: Union[List[str], str]):
"""Apply generated TVM-specific prefix to a function name
Parameters
----------
names : Union[List[str], str]
List of names to combine to form a combined name or the name itself
"""
if isinstance(names, str):
names = [names]

return _backend.PrefixGeneratedName(names)


def sanitise_name(original_name: str):
"""Sanitise name for output into compiler artifacts
Parameters
----------
original_name : str
Original name to sanitise
"""
return _backend.SanitiseName(original_name)
102 changes: 102 additions & 0 deletions src/relay/backend/name_transforms.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#include "name_transforms.h"

#include <tvm/runtime/registry.h>

#include <cctype>
#include <string>

namespace tvm {
namespace relay {
namespace backend {

std::string ToCFunctionStyle(const std::string& original_name) {
ICHECK(!original_name.empty());

int tvm_prefix_length = 3;
std::string function_name("TVM");

bool new_block = true;
for (const char& symbol : original_name.substr(tvm_prefix_length)) {
if (std::isalpha(symbol)) {
if (new_block) {
function_name.push_back(std::toupper(symbol));
new_block = false;
} else {
function_name.push_back(std::tolower(symbol));
}
} else if (symbol == '_') {
new_block = true;
}
}
return function_name;
}

std::string ToCVariableStyle(const std::string& original_name) {
ICHECK(!original_name.empty());

std::string variable_name;
variable_name.resize(original_name.size());

std::transform(original_name.begin(), original_name.end(), variable_name.begin(), ::tolower);
return variable_name;
}

std::string CombineNames(const Array<String>& names) {
std::stringstream combine_stream;
ICHECK(!names.empty());

for (const String& name : names) {
ICHECK(!name.empty());
combine_stream << name << "_";
}

std::string combined_name = combine_stream.str();
combined_name.pop_back();
return combined_name;
}

std::string SanitiseName(const std::string& name) {
ICHECK(!name.empty());

auto multipleSeparators = [](char before, char after) {
return before == '_' && before == after;
};
auto isNotAlnum = [](char c) { return !std::isalnum(c); };
std::string sanitised_input = name;
std::replace_if(sanitised_input.begin(), sanitised_input.end(), isNotAlnum, '_');

sanitised_input.erase(
std::unique(sanitised_input.begin(), sanitised_input.end(), multipleSeparators),
sanitised_input.end());

return sanitised_input;
}

TVM_REGISTER_GLOBAL("relay.backend.ToCFunctionStyle").set_body_typed(ToCFunctionStyle);
TVM_REGISTER_GLOBAL("relay.backend.ToCVariableStyle").set_body_typed(ToCVariableStyle);
TVM_REGISTER_GLOBAL("relay.backend.PrefixName").set_body_typed(PrefixName);
TVM_REGISTER_GLOBAL("relay.backend.PrefixGeneratedName").set_body_typed(PrefixGeneratedName);
TVM_REGISTER_GLOBAL("relay.backend.SanitiseName").set_body_typed(SanitiseName);

} // namespace backend
} // namespace relay
} // namespace tvm
105 changes: 105 additions & 0 deletions src/relay/backend/name_transforms.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*!
* \file relay/backend/name_transforms.h
* \brief Transformations which are applied on names to generate appropriately named compiler
* artifacts
*
* Example:
* ToCFunctionStyle(PrefixName(CombineNames({"Device", "target", "Invoke"})))
* // TVMDeviceTargetInvoke
*
* ToCFunctionStyle(PrefixGeneratedName(CombineNames({"model", "Run"})))
* // TVMGenModelRun
*
* ToCVariableStyle(PrefixName(CombineNames({"Device", "target", "t"})))
* // tvm_device_target_t
*
* ToCVariableStyle(PrefixGeneratedName(CombineNames({"model", "Devices"})))
* // tvmgen_model_devices
*
*/

#include <tvm/runtime/container/array.h>
#include <tvm/runtime/container/string.h>
#include <tvm/runtime/logging.h>

#include <algorithm>
#include <iostream>
#include <string>

#ifndef TVM_RELAY_BACKEND_NAME_TRANSFORMS_H_
#define TVM_RELAY_BACKEND_NAME_TRANSFORMS_H_

namespace tvm {
namespace relay {
namespace backend {

/*!
* \brief Transform a name to the C variable style assuming it is
* appropriately constructed using the prefixing functions
* \param original_name Original name
* \return Transformed function in the C function style
*/
std::string ToCFunctionStyle(const std::string& original_name);

/*!
* \brief Transform a name to the C variable style assuming it is
* appropriately constructed using the prefixing functions
* \param name Original name
* \return Transformed function in the C variable style
*/
std::string ToCVariableStyle(const std::string& original_name);

/*!
* \brief Combine names together for use as a generated name
* \param names Vector of strings to combine
* \return Combined together names
*/
std::string CombineNames(const Array<String>& names);

/*!
* \brief Apply TVM-specific prefix to a name
* \param names Vector of names to combine to form a combined name
* \return Name with prefix applied or prefix-only if no name passed
*/
inline std::string PrefixName(const Array<String>& names) { return "TVM_" + CombineNames(names); }

/*!
* \brief Apply generated TVM-specific prefix to a name
* \param names Vector of names to combine to form a combined name
* \return Name with prefix applied or prefix-only if no name passed
*/
inline std::string PrefixGeneratedName(const Array<String>& names) {
return "TVMGen_" + CombineNames(names);
}

/*!
* \brief Sanitise name for output into compiler artifacts
* \param name Original name
* \return Sanitised name
*/
std::string SanitiseName(const std::string& name);

} // namespace backend
} // namespace relay
} // namespace tvm

#endif // TVM_RELAY_BACKEND_NAME_TRANSFORMS_H_
85 changes: 85 additions & 0 deletions tests/cpp/name_transforms_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#include "../src/relay/backend/name_transforms.h"

#include <gtest/gtest.h>
#include <tvm/runtime/container/string.h>

using namespace tvm::relay::backend;
using namespace tvm::runtime;

TEST(NameTransforms, ToCFunctionStyle) {
ASSERT_EQ(ToCFunctionStyle("TVM_Woof"), "TVMWoof");
ASSERT_EQ(ToCFunctionStyle("TVM_woof"), "TVMWoof");
ASSERT_EQ(ToCFunctionStyle("TVM_woof_woof"), "TVMWoofWoof");
ASSERT_EQ(ToCFunctionStyle("TVMGen_woof_woof"), "TVMGenWoofWoof");
EXPECT_THROW(ToCFunctionStyle(""), InternalError);
}

TEST(NameTransforms, ToCVariableStyle) {
ASSERT_EQ(ToCVariableStyle("TVM_Woof"), "tvm_woof");
ASSERT_EQ(ToCVariableStyle("TVM_woof"), "tvm_woof");
ASSERT_EQ(ToCVariableStyle("TVM_woof_Woof"), "tvm_woof_woof");
EXPECT_THROW(ToCVariableStyle(""), InternalError);
}

TEST(NameTransforms, PrefixName) {
ASSERT_EQ(PrefixName({"Woof"}), "TVM_Woof");
ASSERT_EQ(PrefixName({"woof"}), "TVM_woof");
ASSERT_EQ(PrefixName({"woof", "moo"}), "TVM_woof_moo");
EXPECT_THROW(PrefixName({}), InternalError);
EXPECT_THROW(PrefixName({""}), InternalError);
}

TEST(NameTransforms, PrefixGeneratedName) {
ASSERT_EQ(PrefixGeneratedName({"Woof"}), "TVMGen_Woof");
ASSERT_EQ(PrefixGeneratedName({"woof"}), "TVMGen_woof");
ASSERT_EQ(PrefixGeneratedName({"woof", "moo"}), "TVMGen_woof_moo");
EXPECT_THROW(PrefixGeneratedName({}), InternalError);
EXPECT_THROW(PrefixGeneratedName({""}), InternalError);
}

TEST(NameTransforms, CombineNames) {
ASSERT_EQ(CombineNames({"woof"}), "woof");
ASSERT_EQ(CombineNames({"Woof", "woof"}), "Woof_woof");
ASSERT_EQ(CombineNames({"Woof", "woof", "woof"}), "Woof_woof_woof");
ASSERT_EQ(CombineNames({"Woof", "moo", "t"}), "Woof_moo_t");

EXPECT_THROW(CombineNames({}), InternalError);
EXPECT_THROW(CombineNames({""}), InternalError);
EXPECT_THROW(CombineNames({"Woof", ""}), InternalError);
EXPECT_THROW(CombineNames({"", "Woof"}), InternalError);
}

TEST(NameTransforms, SanitiseName) {
ASSERT_EQ(SanitiseName("+_+ "), "_");
ASSERT_EQ(SanitiseName("input+"), "input_");
ASSERT_EQ(SanitiseName("input-"), "input_");
ASSERT_EQ(SanitiseName("input++"), "input_");
ASSERT_EQ(SanitiseName("woof:1"), "woof_1");
EXPECT_THROW(SanitiseName(""), InternalError);
}

TEST(NameTransforms, CombinedLogic) {
ASSERT_EQ(ToCFunctionStyle(PrefixName({"Device", "target", "Invoke"})), "TVMDeviceTargetInvoke");
ASSERT_EQ(ToCFunctionStyle(PrefixGeneratedName({"model", "Run"})), "TVMGenModelRun");
ASSERT_EQ(ToCVariableStyle(PrefixName({"Device", "target", "t"})), "tvm_device_target_t");
ASSERT_EQ(ToCVariableStyle(PrefixGeneratedName({"model", "Devices"})), "tvmgen_model_devices");
}
Loading

0 comments on commit cca7176

Please sign in to comment.