Skip to content

Commit 0460d82

Browse files
authored
[REFACTOR][TARGET] Cleanup target config (#18788)
1 parent bcd716b commit 0460d82

30 files changed

+1467
-1301
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ tvm_file_glob(GLOB_RECURSE COMPILER_SRCS
306306
tvm_file_glob(GLOB CODEGEN_SRCS
307307
src/target/*.cc
308308
src/target/source/*.cc
309-
src/target/parsers/*.cc
309+
src/target/canonicalizer/*.cc
310+
src/target/canonicalizer/llvm/*.cc
310311
)
311312

312313
list(APPEND COMPILER_SRCS ${CODEGEN_SRCS})

include/tvm/ir/config_schema.h

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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 tvm/ir/config_schema.h
22+
* \brief Minimal schema for dynamic config canonicalization and validation.
23+
*
24+
* This utility is intended for dynamic map-like configs (e.g. Target options),
25+
* where we still want type checking, optional defaulting, and canonicalization.
26+
*/
27+
#ifndef TVM_IR_CONFIG_SCHEMA_H_
28+
#define TVM_IR_CONFIG_SCHEMA_H_
29+
30+
#include <tvm/ffi/container/map.h>
31+
#include <tvm/ffi/function.h>
32+
#include <tvm/ffi/reflection/registry.h>
33+
34+
#include <sstream>
35+
#include <string>
36+
#include <type_traits>
37+
#include <unordered_map>
38+
#include <utility>
39+
#include <vector>
40+
41+
namespace tvm {
42+
namespace ir {
43+
44+
/*!
45+
* \brief Dynamic config schema for map-like options.
46+
*
47+
* The schema supports:
48+
* - Option declaration (`def_option<T>`)
49+
* - Optional canonicalizer (`set_canonicalizer`)
50+
* - Resolution (`Resolve`) that performs validation/defaulting, unknown-key policy,
51+
* and canonicalization (last).
52+
*/
53+
class ConfigSchema {
54+
public:
55+
using ConfigMap = ffi::Map<ffi::String, ffi::Any>;
56+
using Canonicalizer = ffi::TypedFunction<ConfigMap(ConfigMap)>;
57+
58+
/*! \brief Schema entry for one declared option. */
59+
struct OptionEntry {
60+
/*! \brief Option key. */
61+
ffi::String key;
62+
/*! \brief Type string for this option. */
63+
ffi::String type_str;
64+
/*! \brief Per-option validator/coercer (Any -> Any). */
65+
ffi::TypedFunction<ffi::Any(ffi::Any)> validator;
66+
/*! \brief Whether this option has a default value. */
67+
bool has_default = false;
68+
/*! \brief Default value (valid when has_default is true). */
69+
ffi::Any default_value;
70+
};
71+
72+
/*!
73+
* \brief Declare a typed option.
74+
*
75+
* Validation/coercion is implicitly generated from `T`.
76+
* Additional optional traits may be supplied (e.g. `refl::DefaultValue`, `const char*` doc).
77+
*
78+
* \tparam T The canonical value type of this option.
79+
* \tparam Traits Optional metadata/traits.
80+
* \param key Option key.
81+
* \param traits Optional traits.
82+
* \return Reference to `*this` for chaining.
83+
*/
84+
template <typename T, typename... Traits>
85+
ConfigSchema& def_option(const ffi::String& key, Traits&&... traits) {
86+
std::string skey(key);
87+
if (key_to_index_.count(skey)) {
88+
TVM_FFI_THROW(ValueError) << "Duplicate config option key: '" << key << "'";
89+
}
90+
key_to_index_[skey] = options_.size();
91+
options_.push_back(MakeEntry<T>(key, std::forward<Traits>(traits)...));
92+
return *this;
93+
}
94+
95+
/*! \brief Set whole-object canonicalizer. */
96+
void set_canonicalizer(Canonicalizer f) { canonicalizer_ = std::move(f); }
97+
98+
/*! \brief Trait to set a custom validator for a config option. */
99+
struct AttrValidator {
100+
ffi::TypedFunction<ffi::Any(ffi::Any)> func;
101+
explicit AttrValidator(ffi::TypedFunction<ffi::Any(ffi::Any)> f) : func(std::move(f)) {}
102+
};
103+
104+
/*! \brief Set whether unknown keys trigger an error. */
105+
void set_error_on_unknown(bool value) { error_on_unknown_ = value; }
106+
107+
/*!
108+
* \brief Default/validate, then canonicalize a config object.
109+
*
110+
* Resolve flow:
111+
* 1) Validate/coerce declared options in declaration order.
112+
* 2) Materialize defaults and enforce required options.
113+
* 3) Apply unknown-key policy.
114+
* 4) Run canonicalizer as final step.
115+
*
116+
* \param config Input config object.
117+
* \return Canonical validated config object.
118+
* \throws ValueError/TypeError with option context.
119+
*/
120+
ConfigMap Resolve(ConfigMap config) const {
121+
ConfigMap result;
122+
123+
// Step 1: validate/coerce and materialize options in declaration order
124+
for (const auto& e : options_) {
125+
auto it = config.find(e.key);
126+
if (it != config.end()) {
127+
result.Set(e.key, e.validator((*it).second));
128+
} else if (e.has_default) {
129+
result.Set(e.key, e.default_value);
130+
}
131+
// else: missing non-required option, stays absent
132+
}
133+
134+
// Step 2: unknown-key policy
135+
if (error_on_unknown_) {
136+
for (const auto& kv : config) {
137+
if (!key_to_index_.count(std::string(kv.first))) {
138+
std::ostringstream os;
139+
os << "Unknown config option '" << kv.first << "'. Known options: ";
140+
bool first = true;
141+
for (const auto& e : options_) {
142+
if (!first) os << ", ";
143+
os << "'" << e.key << "'";
144+
first = false;
145+
}
146+
TVM_FFI_THROW(ValueError) << os.str();
147+
}
148+
}
149+
}
150+
151+
// Step 3: whole-object canonicalization (last)
152+
if (canonicalizer_ != nullptr) {
153+
result = canonicalizer_(result);
154+
}
155+
156+
return result;
157+
}
158+
159+
/*!
160+
* \brief List declared options in declaration order.
161+
* \return Const reference to the option entries vector.
162+
*/
163+
const std::vector<OptionEntry>& ListOptions() const { return options_; }
164+
165+
/*! \brief Check if an option with the given key exists. */
166+
bool HasOption(const ffi::String& key) const { return key_to_index_.count(std::string(key)) > 0; }
167+
168+
private:
169+
template <typename T>
170+
static ffi::TypedFunction<ffi::Any(ffi::Any)> MakeValidator(const ffi::String& key) {
171+
return ffi::TypedFunction<ffi::Any(ffi::Any)>([key](ffi::Any val) -> ffi::Any {
172+
auto opt = val.try_cast<T>();
173+
if (!opt.has_value()) {
174+
TVM_FFI_THROW(TypeError) << "Option '" << key << "': expected type '"
175+
<< ffi::TypeTraits<T>::TypeStr() << "' but got '"
176+
<< val.GetTypeKey() << "'";
177+
}
178+
return ffi::Any(opt.value());
179+
});
180+
}
181+
182+
template <typename Trait>
183+
static void ApplyTrait(OptionEntry* entry, ffi::reflection::FieldInfoBuilder* info,
184+
Trait&& trait) {
185+
using T = std::decay_t<Trait>;
186+
if constexpr (std::is_same_v<T, AttrValidator>) {
187+
entry->validator = std::move(trait.func);
188+
} else if constexpr (std::is_base_of_v<ffi::reflection::InfoTrait, T>) {
189+
trait.Apply(info);
190+
} else if constexpr (std::is_same_v<T, const char*> || std::is_same_v<T, char*>) {
191+
const char* doc = trait;
192+
if (doc != nullptr && doc[0] != '\0') {
193+
info->doc = TVMFFIByteArray{doc, std::char_traits<char>::length(doc)};
194+
}
195+
}
196+
}
197+
198+
template <typename T, typename... Traits>
199+
OptionEntry MakeEntry(const ffi::String& key, Traits&&... traits) {
200+
OptionEntry e;
201+
e.key = key;
202+
e.type_str = ffi::String(ffi::TypeTraits<T>::TypeStr());
203+
e.validator = MakeValidator<T>(key);
204+
// Apply traits through a temporary FieldInfoBuilder so existing
205+
// reflection traits (notably refl::DefaultValue) are reused unchanged.
206+
ffi::reflection::FieldInfoBuilder info{};
207+
info.flags = 0;
208+
info.default_value = ffi::AnyView(nullptr).CopyToTVMFFIAny();
209+
info.doc = TVMFFIByteArray{nullptr, 0};
210+
(ApplyTrait(&e, &info, std::forward<Traits>(traits)), ...);
211+
if (info.flags & kTVMFFIFieldFlagBitMaskHasDefault) {
212+
e.has_default = true;
213+
e.default_value = ffi::AnyView::CopyFromTVMFFIAny(info.default_value);
214+
// Release the extra ref created by CopyToTVMFFIAny in Apply
215+
if (info.default_value.type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
216+
ffi::details::ObjectUnsafe::DecRefObjectHandle(info.default_value.v_obj);
217+
}
218+
}
219+
return e;
220+
}
221+
222+
/*! \brief Declared options in declaration order. */
223+
std::vector<OptionEntry> options_;
224+
/*! \brief Map from key string to index in options_. */
225+
std::unordered_map<std::string, size_t> key_to_index_;
226+
/*! \brief Optional whole-config canonicalizer. */
227+
Canonicalizer canonicalizer_{nullptr};
228+
/*! \brief Whether unknown keys trigger an error. */
229+
bool error_on_unknown_ = true;
230+
};
231+
232+
} // namespace ir
233+
} // namespace tvm
234+
235+
#endif // TVM_IR_CONFIG_SCHEMA_H_

include/tvm/target/target.h

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,12 @@
2727
#include <tvm/ffi/reflection/registry.h>
2828
#include <tvm/ir/expr.h>
2929
#include <tvm/ir/function.h>
30-
#include <tvm/node/attr_registry_map.h>
3130
#include <tvm/node/node.h>
3231
#include <tvm/runtime/device_api.h>
3332
#include <tvm/support/with.h>
3433
#include <tvm/target/target_kind.h>
3534

3635
#include <string>
37-
#include <unordered_set>
38-
#include <vector>
3936

4037
namespace tvm {
4138

@@ -56,18 +53,16 @@ class TargetNode : public Object {
5653
ffi::String tag;
5754
/*! \brief Keys for this target */
5855
ffi::Array<ffi::String> keys;
59-
/*! \brief Collection of attributes */
56+
/*! \brief Collection of attributes (includes feature.* keys set by canonicalizer) */
6057
ffi::Map<ffi::String, Any> attrs;
61-
/*! \brief Target features */
62-
ffi::Map<ffi::String, Any> features;
6358

6459
/*!
6560
* \brief The JSON string representation of the target
6661
* \return JSON string of the target configuration (e.g. {"kind": "llvm", "mcpu": "cortex-a53"})
6762
*/
6863
TVM_DLL const std::string& str() const;
6964
/*! \return Export target to JSON-like configuration */
70-
TVM_DLL ffi::Map<ffi::String, ffi::Any> Export() const;
65+
TVM_DLL ffi::Map<ffi::String, ffi::Any> ToConfig() const;
7166
/*! \return The ffi::Optional<Target> typed target host of the TargetNode */
7267
TVM_DLL ffi::Optional<Target> GetHost() const;
7368
/*! \return The device type for this target */
@@ -83,23 +78,13 @@ class TargetNode : public Object {
8378
*/
8479
TVM_DLL bool HasKey(const std::string& query_key) const;
8580

86-
/*!
87-
* \brief Returns a human readable representation of \p Target which includes all fields,
88-
* especially the host. Useful for diagnostic messages and debugging.
89-
*
90-
* TODO(mbs): The ReprPrinter version should perhaps switch to this form, however currently
91-
* code depends on str() and << being the same.
92-
*/
93-
ffi::String ToDebugString() const;
94-
9581
static void RegisterReflection() {
9682
namespace refl = tvm::ffi::reflection;
9783
refl::ObjectDef<TargetNode>()
9884
.def_ro("kind", &TargetNode::kind)
9985
.def_ro("tag", &TargetNode::tag)
10086
.def_ro("keys", &TargetNode::keys)
10187
.def_ro("attrs", &TargetNode::attrs)
102-
.def_ro("features", &TargetNode::features)
10388
.def_ro("host", &TargetNode::host);
10489
}
10590

@@ -133,47 +118,6 @@ class TargetNode : public Object {
133118
return GetAttr<TObjectRef>(attr_key, ffi::Optional<TObjectRef>(default_value));
134119
}
135120

136-
/*!
137-
* \brief Get a Target feature
138-
*
139-
* \param feature_key The feature key.
140-
* \param default_value The default value if the key does not exist, defaults to nullptr.
141-
*
142-
* \return The result
143-
*
144-
* \tparam TOBjectRef the expected object type.
145-
* \throw Error if the key exists but the value does not match TObjectRef
146-
*
147-
* \code
148-
*
149-
* void GetTargetFeature(const Target& target) {
150-
* Bool has_feature = target->GetFeature<Bool>("has_feature", false).value();
151-
* }
152-
*
153-
* \endcode
154-
*/
155-
template <typename TObjectRef>
156-
ffi::Optional<TObjectRef> GetFeature(
157-
const std::string& feature_key,
158-
ffi::Optional<TObjectRef> default_value = std::nullopt) const {
159-
if (auto feature = features.Get(feature_key)) {
160-
return Downcast<TObjectRef>(feature.value());
161-
} else {
162-
return default_value;
163-
}
164-
}
165-
// variant that uses TObjectRef to enable implicit conversion to default value.
166-
template <typename TObjectRef>
167-
ffi::Optional<TObjectRef> GetFeature(const std::string& attr_key,
168-
TObjectRef default_value) const {
169-
return GetFeature<TObjectRef>(attr_key, ffi::Optional<TObjectRef>(default_value));
170-
}
171-
172-
/*! \brief Get the keys for this target as a vector of string */
173-
TVM_DLL std::vector<std::string> GetKeys() const;
174-
/*! \brief Get the keys for this target as an unordered_set of string */
175-
TVM_DLL std::unordered_set<std::string> GetLibs() const;
176-
177121
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
178122
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("target.Target", TargetNode, Object);
179123

@@ -218,12 +162,7 @@ class Target : public ObjectRef {
218162
*/
219163
TVM_DLL explicit Target(Target target, Target host);
220164
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(Target, ObjectRef, TargetNode);
221-
/*!
222-
* \brief Create a new Target object with given target (w.o host) and target host.
223-
* \param target The current Target typed object target, with or without host field.
224-
* \param host The given Target typed object target host
225-
* \return The new Target object with the given target and host field of given host.
226-
*/
165+
227166
static Target WithHost(const Target& target, const Target& host);
228167

229168
/*! \return The target with the host stripped out */

0 commit comments

Comments
 (0)