Skip to content

Commit 181226f

Browse files
Mousiusylc
authored andcommitted
Introduce centralised name transformation functions (apache#9088)
* Introduce centralised name transformation functions 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). * Improve error handling and error messages * Sanitize sanitise to sanitize This patch aims to sanitize uses of sanitise to the form of sanitize to be consistent with the overall codebases use of American English.
1 parent d76cf77 commit 181226f

File tree

5 files changed

+496
-0
lines changed

5 files changed

+496
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
"""
18+
Name transformation functions for use in code generation
19+
"""
20+
21+
from typing import List, Union
22+
23+
from tvm import TVMError
24+
from . import _backend
25+
26+
27+
def to_c_function_style(original_name: str):
28+
"""Transform a name to the C function style assuming it is
29+
appropriately constructed using the prefixing functions
30+
31+
Parameters
32+
----------
33+
original_name : str
34+
Original name to transform
35+
"""
36+
return _backend.ToCFunctionStyle(original_name)
37+
38+
39+
def to_c_variable_style(original_name: str):
40+
"""Transform a name to the C variable style assuming it is
41+
appropriately constructed using the prefixing functions
42+
43+
Parameters
44+
----------
45+
original_name : str
46+
Original name to transform
47+
"""
48+
return _backend.ToCVariableStyle(original_name)
49+
50+
51+
def _preprocess_names(names: Union[List[str], str]):
52+
"""Preprocesses name strings into format for C++ functions
53+
54+
Parameters
55+
----------
56+
names : Union[List[str], str]
57+
List of names to combine to form a combined name or the name itself
58+
"""
59+
if isinstance(names, str):
60+
if names == "":
61+
raise TVMError("Name is empty")
62+
return [names]
63+
return names
64+
65+
66+
def prefix_name(names: Union[List[str], str]):
67+
"""Apply TVM-specific prefix to a function name
68+
69+
Parameters
70+
----------
71+
names : Union[List[str], str]
72+
List of names to combine to form a combined name or the name itself
73+
"""
74+
75+
return _backend.PrefixName(_preprocess_names(names))
76+
77+
78+
def prefix_generated_name(names: Union[List[str], str]):
79+
"""Apply generated TVM-specific prefix to a function name
80+
81+
Parameters
82+
----------
83+
names : Union[List[str], str]
84+
List of names to combine to form a combined name or the name itself
85+
"""
86+
87+
return _backend.PrefixGeneratedName(_preprocess_names(names))
88+
89+
90+
def sanitize_name(original_name: str):
91+
"""Sanitize name for output into compiler artifacts
92+
93+
Parameters
94+
----------
95+
original_name : str
96+
Original name to sanitize
97+
"""
98+
return _backend.SanitizeName(original_name)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "name_transforms.h"
21+
22+
#include <tvm/runtime/registry.h>
23+
24+
#include <cctype>
25+
#include <string>
26+
27+
namespace tvm {
28+
namespace relay {
29+
namespace backend {
30+
31+
std::string ToCFunctionStyle(const std::string& original_name) {
32+
ICHECK(!original_name.empty()) << "Function name is empty";
33+
ICHECK_EQ(original_name.find("TVM"), 0) << "Function not TVM prefixed";
34+
35+
int tvm_prefix_length = 3;
36+
std::string function_name("TVM");
37+
38+
bool new_block = true;
39+
for (const char& symbol : original_name.substr(tvm_prefix_length)) {
40+
if (std::isalpha(symbol)) {
41+
if (new_block) {
42+
function_name.push_back(std::toupper(symbol));
43+
new_block = false;
44+
} else {
45+
function_name.push_back(std::tolower(symbol));
46+
}
47+
} else if (symbol == '_') {
48+
new_block = true;
49+
}
50+
}
51+
return function_name;
52+
}
53+
54+
std::string ToCVariableStyle(const std::string& original_name) {
55+
ICHECK(!original_name.empty()) << "Variable name is empty";
56+
ICHECK_EQ(original_name.find("TVM"), 0) << "Variable not TVM prefixed";
57+
58+
std::string variable_name;
59+
variable_name.resize(original_name.size());
60+
61+
std::transform(original_name.begin(), original_name.end(), variable_name.begin(), ::tolower);
62+
return variable_name;
63+
}
64+
65+
std::string CombineNames(const Array<String>& names) {
66+
std::stringstream combine_stream;
67+
ICHECK(!names.empty()) << "Name segments empty";
68+
69+
for (const String& name : names) {
70+
ICHECK(!name.empty()) << "Name segment is empty";
71+
combine_stream << name << "_";
72+
}
73+
74+
std::string combined_name = combine_stream.str();
75+
combined_name.pop_back();
76+
return combined_name;
77+
}
78+
79+
std::string SanitizeName(const std::string& name) {
80+
ICHECK(!name.empty()) << "Name is empty";
81+
82+
auto multipleSeparators = [](char before, char after) {
83+
return before == '_' && before == after;
84+
};
85+
auto isNotAlnum = [](char c) { return !std::isalnum(c); };
86+
std::string sanitized_input = name;
87+
std::replace_if(sanitized_input.begin(), sanitized_input.end(), isNotAlnum, '_');
88+
89+
sanitized_input.erase(
90+
std::unique(sanitized_input.begin(), sanitized_input.end(), multipleSeparators),
91+
sanitized_input.end());
92+
93+
return sanitized_input;
94+
}
95+
96+
TVM_REGISTER_GLOBAL("relay.backend.ToCFunctionStyle").set_body_typed(ToCFunctionStyle);
97+
TVM_REGISTER_GLOBAL("relay.backend.ToCVariableStyle").set_body_typed(ToCVariableStyle);
98+
TVM_REGISTER_GLOBAL("relay.backend.PrefixName").set_body_typed(PrefixName);
99+
TVM_REGISTER_GLOBAL("relay.backend.PrefixGeneratedName").set_body_typed(PrefixGeneratedName);
100+
TVM_REGISTER_GLOBAL("relay.backend.SanitizeName").set_body_typed(SanitizeName);
101+
102+
} // namespace backend
103+
} // namespace relay
104+
} // namespace tvm
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
/*!
21+
* \file relay/backend/name_transforms.h
22+
* \brief Transformations which are applied on names to generate appropriately named compiler
23+
* artifacts
24+
*
25+
* Example:
26+
* ToCFunctionStyle(PrefixName(CombineNames({"Device", "target", "Invoke"})))
27+
* // TVMDeviceTargetInvoke
28+
*
29+
* ToCFunctionStyle(PrefixGeneratedName(CombineNames({"model", "Run"})))
30+
* // TVMGenModelRun
31+
*
32+
* ToCVariableStyle(PrefixName(CombineNames({"Device", "target", "t"})))
33+
* // tvm_device_target_t
34+
*
35+
* ToCVariableStyle(PrefixGeneratedName(CombineNames({"model", "Devices"})))
36+
* // tvmgen_model_devices
37+
*
38+
*/
39+
40+
#include <tvm/runtime/container/array.h>
41+
#include <tvm/runtime/container/string.h>
42+
#include <tvm/runtime/logging.h>
43+
44+
#include <algorithm>
45+
#include <iostream>
46+
#include <string>
47+
48+
#ifndef TVM_RELAY_BACKEND_NAME_TRANSFORMS_H_
49+
#define TVM_RELAY_BACKEND_NAME_TRANSFORMS_H_
50+
51+
namespace tvm {
52+
namespace relay {
53+
namespace backend {
54+
55+
/*!
56+
* \brief Transform a name to the C variable style assuming it is
57+
* appropriately constructed using the prefixing functions
58+
* \param original_name Original name
59+
* \return Transformed function in the C function style
60+
*/
61+
std::string ToCFunctionStyle(const std::string& original_name);
62+
63+
/*!
64+
* \brief Transform a name to the C variable style assuming it is
65+
* appropriately constructed using the prefixing functions
66+
* \param name Original name
67+
* \return Transformed function in the C variable style
68+
*/
69+
std::string ToCVariableStyle(const std::string& original_name);
70+
71+
/*!
72+
* \brief Combine names together for use as a generated name
73+
* \param names Vector of strings to combine
74+
* \return Combined together names
75+
*/
76+
std::string CombineNames(const Array<String>& names);
77+
78+
/*!
79+
* \brief Apply TVM-specific prefix to a name
80+
* \param names Vector of names to combine to form a combined name
81+
* \return Name with prefix applied or prefix-only if no name passed
82+
*/
83+
inline std::string PrefixName(const Array<String>& names) { return "TVM_" + CombineNames(names); }
84+
85+
/*!
86+
* \brief Apply generated TVM-specific prefix to a name
87+
* \param names Vector of names to combine to form a combined name
88+
* \return Name with prefix applied or prefix-only if no name passed
89+
*/
90+
inline std::string PrefixGeneratedName(const Array<String>& names) {
91+
return "TVMGen_" + CombineNames(names);
92+
}
93+
94+
/*!
95+
* \brief Sanitize name for output into compiler artifacts
96+
* \param name Original name
97+
* \return Sanitized name
98+
*/
99+
std::string SanitizeName(const std::string& name);
100+
101+
} // namespace backend
102+
} // namespace relay
103+
} // namespace tvm
104+
105+
#endif // TVM_RELAY_BACKEND_NAME_TRANSFORMS_H_

tests/cpp/name_transforms_test.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "../src/relay/backend/name_transforms.h"
21+
22+
#include <gtest/gtest.h>
23+
#include <tvm/runtime/container/string.h>
24+
25+
using namespace tvm::relay::backend;
26+
using namespace tvm::runtime;
27+
28+
TEST(NameTransforms, ToCFunctionStyle) {
29+
ASSERT_EQ(ToCFunctionStyle("TVM_Woof"), "TVMWoof");
30+
ASSERT_EQ(ToCFunctionStyle("TVM_woof"), "TVMWoof");
31+
ASSERT_EQ(ToCFunctionStyle("TVM_woof_woof"), "TVMWoofWoof");
32+
ASSERT_EQ(ToCFunctionStyle("TVMGen_woof_woof"), "TVMGenWoofWoof");
33+
EXPECT_THROW(ToCVariableStyle("Cake_Bakery"), InternalError); // Incorrect prefix
34+
EXPECT_THROW(ToCFunctionStyle(""), InternalError);
35+
}
36+
37+
TEST(NameTransforms, ToCVariableStyle) {
38+
ASSERT_EQ(ToCVariableStyle("TVM_Woof"), "tvm_woof");
39+
ASSERT_EQ(ToCVariableStyle("TVM_woof"), "tvm_woof");
40+
ASSERT_EQ(ToCVariableStyle("TVM_woof_Woof"), "tvm_woof_woof");
41+
EXPECT_THROW(ToCVariableStyle("Cake_Bakery"), InternalError); // Incorrect prefix
42+
EXPECT_THROW(ToCVariableStyle(""), InternalError);
43+
}
44+
45+
TEST(NameTransforms, PrefixName) {
46+
ASSERT_EQ(PrefixName({"Woof"}), "TVM_Woof");
47+
ASSERT_EQ(PrefixName({"woof"}), "TVM_woof");
48+
ASSERT_EQ(PrefixName({"woof", "moo"}), "TVM_woof_moo");
49+
EXPECT_THROW(PrefixName({}), InternalError);
50+
EXPECT_THROW(PrefixName({""}), InternalError);
51+
}
52+
53+
TEST(NameTransforms, PrefixGeneratedName) {
54+
ASSERT_EQ(PrefixGeneratedName({"Woof"}), "TVMGen_Woof");
55+
ASSERT_EQ(PrefixGeneratedName({"woof"}), "TVMGen_woof");
56+
ASSERT_EQ(PrefixGeneratedName({"woof", "moo"}), "TVMGen_woof_moo");
57+
EXPECT_THROW(PrefixGeneratedName({}), InternalError);
58+
EXPECT_THROW(PrefixGeneratedName({""}), InternalError);
59+
}
60+
61+
TEST(NameTransforms, CombineNames) {
62+
ASSERT_EQ(CombineNames({"woof"}), "woof");
63+
ASSERT_EQ(CombineNames({"Woof", "woof"}), "Woof_woof");
64+
ASSERT_EQ(CombineNames({"Woof", "woof", "woof"}), "Woof_woof_woof");
65+
ASSERT_EQ(CombineNames({"Woof", "moo", "t"}), "Woof_moo_t");
66+
67+
EXPECT_THROW(CombineNames({}), InternalError);
68+
EXPECT_THROW(CombineNames({""}), InternalError);
69+
EXPECT_THROW(CombineNames({"Woof", ""}), InternalError);
70+
EXPECT_THROW(CombineNames({"", "Woof"}), InternalError);
71+
}
72+
73+
TEST(NameTransforms, SanitizeName) {
74+
ASSERT_EQ(SanitizeName("+_+ "), "_");
75+
ASSERT_EQ(SanitizeName("input+"), "input_");
76+
ASSERT_EQ(SanitizeName("input-"), "input_");
77+
ASSERT_EQ(SanitizeName("input++"), "input_");
78+
ASSERT_EQ(SanitizeName("woof:1"), "woof_1");
79+
EXPECT_THROW(SanitizeName(""), InternalError);
80+
}
81+
82+
TEST(NameTransforms, CombinedLogic) {
83+
ASSERT_EQ(ToCFunctionStyle(PrefixName({"Device", "target", "Invoke"})), "TVMDeviceTargetInvoke");
84+
ASSERT_EQ(ToCFunctionStyle(PrefixGeneratedName({"model", "Run"})), "TVMGenModelRun");
85+
ASSERT_EQ(ToCVariableStyle(PrefixName({"Device", "target", "t"})), "tvm_device_target_t");
86+
ASSERT_EQ(ToCVariableStyle(PrefixGeneratedName({"model", "Devices"})), "tvmgen_model_devices");
87+
}

0 commit comments

Comments
 (0)