tvm
config_schema.h
Go to the documentation of this file.
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 
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 
53 class ConfigSchema {
54  public:
55  using ConfigMap = ffi::Map<ffi::String, ffi::Any>;
56  using Canonicalizer = ffi::TypedFunction<ConfigMap(ConfigMap)>;
57 
59  struct OptionEntry {
61  ffi::String key;
63  ffi::String type_str;
65  ffi::TypedFunction<ffi::Any(ffi::Any)> validator;
67  bool has_default = false;
69  bool default_from_factory = false;
71  ffi::Any default_value;
72  };
73 
86  template <typename T, typename... Traits>
87  ConfigSchema& def_option(const ffi::String& key, Traits&&... traits) {
88  std::string skey(key);
89  if (key_to_index_.count(skey)) {
90  TVM_FFI_THROW(ValueError) << "Duplicate config option key: '" << key << "'";
91  }
92  key_to_index_[skey] = options_.size();
93  options_.push_back(MakeEntry<T>(key, std::forward<Traits>(traits)...));
94  return *this;
95  }
96 
98  void set_canonicalizer(Canonicalizer f) { canonicalizer_ = std::move(f); }
99 
101  struct AttrValidator {
102  ffi::TypedFunction<ffi::Any(ffi::Any)> func;
103  explicit AttrValidator(ffi::TypedFunction<ffi::Any(ffi::Any)> f) : func(std::move(f)) {}
104  };
105 
107  void set_error_on_unknown(bool value) { error_on_unknown_ = value; }
108 
122  ConfigMap Resolve(ConfigMap config) const {
123  ConfigMap result;
124 
125  // Step 1: validate/coerce and materialize options in declaration order
126  for (const auto& e : options_) {
127  auto it = config.find(e.key);
128  if (it != config.end()) {
129  result.Set(e.key, e.validator((*it).second));
130  } else if (e.has_default) {
131  if (e.default_from_factory) {
132  result.Set(e.key, e.default_value.cast<ffi::Function>()());
133  } else {
134  result.Set(e.key, e.default_value);
135  }
136  }
137  // else: missing non-required option, stays absent
138  }
139 
140  // Step 2: unknown-key policy
141  if (error_on_unknown_) {
142  for (const auto& kv : config) {
143  if (!key_to_index_.count(std::string(kv.first))) {
144  std::ostringstream os;
145  os << "Unknown config option '" << kv.first << "'. Known options: ";
146  bool first = true;
147  for (const auto& e : options_) {
148  if (!first) os << ", ";
149  os << "'" << e.key << "'";
150  first = false;
151  }
152  TVM_FFI_THROW(ValueError) << os.str();
153  }
154  }
155  }
156 
157  // Step 3: whole-object canonicalization (last)
158  if (canonicalizer_ != nullptr) {
159  result = canonicalizer_(result);
160  }
161 
162  return result;
163  }
164 
169  const std::vector<OptionEntry>& ListOptions() const { return options_; }
170 
172  bool HasOption(const ffi::String& key) const { return key_to_index_.count(std::string(key)) > 0; }
173 
174  private:
175  template <typename T>
176  static ffi::TypedFunction<ffi::Any(ffi::Any)> MakeValidator(const ffi::String& key) {
177  return ffi::TypedFunction<ffi::Any(ffi::Any)>([key](ffi::Any val) -> ffi::Any {
178  auto opt = val.try_cast<T>();
179  if (!opt.has_value()) {
180  TVM_FFI_THROW(TypeError) << "Option '" << key << "': expected type '"
181  << ffi::TypeTraits<T>::TypeStr() << "' but got '"
182  << val.GetTypeKey() << "'";
183  }
184  return ffi::Any(opt.value());
185  });
186  }
187 
188  template <typename Trait>
189  static void ApplyTrait(OptionEntry* entry, ffi::reflection::FieldInfoBuilder* info,
190  Trait&& trait) {
191  using T = std::decay_t<Trait>;
192  if constexpr (std::is_same_v<T, AttrValidator>) {
193  entry->validator = std::move(trait.func);
194  } else if constexpr (std::is_base_of_v<ffi::reflection::InfoTrait, T>) {
195  trait.Apply(info);
196  } else if constexpr (std::is_same_v<T, const char*> || std::is_same_v<T, char*>) {
197  const char* doc = trait;
198  if (doc != nullptr && doc[0] != '\0') {
199  info->doc = TVMFFIByteArray{doc, std::char_traits<char>::length(doc)};
200  }
201  }
202  }
203 
204  template <typename T, typename... Traits>
205  OptionEntry MakeEntry(const ffi::String& key, Traits&&... traits) {
206  OptionEntry e;
207  e.key = key;
208  e.type_str = ffi::String(ffi::TypeTraits<T>::TypeStr());
209  e.validator = MakeValidator<T>(key);
210  // Apply traits through a temporary FieldInfoBuilder so existing
211  // reflection traits (notably refl::DefaultValue) are reused unchanged.
212  ffi::reflection::FieldInfoBuilder info{};
213  info.flags = 0;
214  info.default_value_or_factory = ffi::AnyView(nullptr).CopyToTVMFFIAny();
215  info.doc = TVMFFIByteArray{nullptr, 0};
216  (ApplyTrait(&e, &info, std::forward<Traits>(traits)), ...);
217  if (info.flags & kTVMFFIFieldFlagBitMaskHasDefault) {
218  e.has_default = true;
219  e.default_from_factory = (info.flags & kTVMFFIFieldFlagBitMaskDefaultFromFactory) != 0;
220  e.default_value = ffi::AnyView::CopyFromTVMFFIAny(info.default_value_or_factory);
221  // Release the extra ref created by CopyToTVMFFIAny in Apply
222  if (info.default_value_or_factory.type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
223  ffi::details::ObjectUnsafe::DecRefObjectHandle(info.default_value_or_factory.v_obj);
224  }
225  }
226  return e;
227  }
228 
230  std::vector<OptionEntry> options_;
232  std::unordered_map<std::string, size_t> key_to_index_;
234  Canonicalizer canonicalizer_{nullptr};
236  bool error_on_unknown_ = true;
237 };
238 
239 } // namespace ir
240 } // namespace tvm
241 
242 #endif // TVM_IR_CONFIG_SCHEMA_H_
Dynamic config schema for map-like options.
Definition: config_schema.h:53
bool HasOption(const ffi::String &key) const
Check if an option with the given key exists.
Definition: config_schema.h:172
ConfigMap Resolve(ConfigMap config) const
Default/validate, then canonicalize a config object.
Definition: config_schema.h:122
ffi::Map< ffi::String, ffi::Any > ConfigMap
Definition: config_schema.h:55
void set_error_on_unknown(bool value)
Set whether unknown keys trigger an error.
Definition: config_schema.h:107
ConfigSchema & def_option(const ffi::String &key, Traits &&... traits)
Declare a typed option.
Definition: config_schema.h:87
void set_canonicalizer(Canonicalizer f)
Set whole-object canonicalizer.
Definition: config_schema.h:98
ffi::TypedFunction< ConfigMap(ConfigMap)> Canonicalizer
Definition: config_schema.h:56
const std::vector< OptionEntry > & ListOptions() const
List declared options in declaration order.
Definition: config_schema.h:169
tvm::relax::Function Function
Definition: transform.h:38
An object that builds and maintains block scope and StmtSref mapping for Dependence analysis.
Definition: analyzer.h:37
Trait to set a custom validator for a config option.
Definition: config_schema.h:101
ffi::TypedFunction< ffi::Any(ffi::Any)> func
Definition: config_schema.h:102
AttrValidator(ffi::TypedFunction< ffi::Any(ffi::Any)> f)
Definition: config_schema.h:103
Schema entry for one declared option.
Definition: config_schema.h:59
ffi::String key
Option key.
Definition: config_schema.h:61
bool default_from_factory
Whether the default is produced by a factory function.
Definition: config_schema.h:69
ffi::Any default_value
Default value, or factory function () -> Any when default_from_factory is true.
Definition: config_schema.h:71
ffi::String type_str
Type string for this option.
Definition: config_schema.h:63
bool has_default
Whether this option has a default value or factory.
Definition: config_schema.h:67
ffi::TypedFunction< ffi::Any(ffi::Any)> validator
Per-option validator/coercer (Any -> Any).
Definition: config_schema.h:65