docc 0.8.3 (0d44999b)
C++ API Reference
Loading...
Searching...
No Matches
transformation_registry.h
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4#include <string>
5#include <utility>
6
11
12#include <nlohmann/json.hpp>
13
14#include <sdfg/einsum/einsum.h>
50
51namespace sdfg {
52namespace transformations {
53
68template<typename T>
71 const nlohmann::json& desc,
72 const T& transformation,
73 nlohmann::json& out
74 ) {
75 { T::from_json(builder, desc) } -> std::same_as<T>;
76 { transformation.to_json(out) } -> std::same_as<void>;
77};
78
79namespace detail {
80
87template<typename T, typename Visitor>
89decltype(auto) invoke_for(Visitor&& visitor) {
90 return std::forward<Visitor>(visitor).template operator()<T>();
91}
92
93} // namespace detail
94
115template<typename Visitor>
116decltype(auto)
117dispatch_transformation(const std::string& transformation_name, const nlohmann::json& desc, Visitor&& visitor) {
118 if (transformation_name == "LoopTiling") {
119 return detail::invoke_for<transformations::LoopTiling>(std::forward<Visitor>(visitor));
120 } else if (transformation_name == "MapCollapse") {
121 return detail::invoke_for<transformations::MapCollapse>(std::forward<Visitor>(visitor));
122 } else if (transformation_name == "MapDelinearize") {
124 } else if (transformation_name == "MultiLevelTiling") {
126 } else if (transformation_name == "LoopDistribute") {
128 } else if (transformation_name == "LoopInterchange") {
130 } else if (transformation_name == "LocalStorage") {
131 return detail::invoke_for<transformations::LocalStorage>(std::forward<Visitor>(visitor));
132 } else if (transformation_name == "SoftwarePipelining") {
134 } else if (transformation_name == "TileFusion") {
135 return detail::invoke_for<transformations::TileFusion>(std::forward<Visitor>(visitor));
136 } else if (transformation_name == "TileVectorizer") {
138 } else if (transformation_name == "LoopSkewing") {
139 return detail::invoke_for<transformations::LoopSkewing>(std::forward<Visitor>(visitor));
140 } else if (transformation_name == "LoopShift") {
141 return detail::invoke_for<transformations::LoopShift>(std::forward<Visitor>(visitor));
142 } else if (transformation_name == "LoopSplit") {
143 return detail::invoke_for<transformations::LoopSplit>(std::forward<Visitor>(visitor));
144 } else if (transformation_name == "LoopParallelization") {
146 } else if (transformation_name == "OMPTransform") {
147 return detail::invoke_for<transformations::OMPTransform>(std::forward<Visitor>(visitor));
148 } else if (transformation_name == "LoopPeeling") {
149 return detail::invoke_for<transformations::LoopPeeling>(std::forward<Visitor>(visitor));
150 } else if (transformation_name == "TrivialLoopElimination") {
152 } else if (transformation_name == "RedundantZeroInitElimination") {
154 } else if (transformation_name == "StreamK") {
155 return detail::invoke_for<transformations::StreamK>(std::forward<Visitor>(visitor));
156 } else if (transformation_name == "VectorizeTransform") {
158 } else if (transformation_name == "UnrollTransform") {
160 } else if (transformation_name == "CUDATransform") {
161 return detail::invoke_for<cuda::CUDATransform>(std::forward<Visitor>(visitor));
162 } else if (transformation_name == "CUDAOffloadTransform") {
163 return detail::invoke_for<cuda::CUDAOffloadTransform>(std::forward<Visitor>(visitor));
164 } else if (transformation_name == "ROCMOffloadTransform") {
165 return detail::invoke_for<rocm::ROCMOffloadTransform>(std::forward<Visitor>(visitor));
166 } else if (transformation_name == "CUDAParallelizeNestedMap") {
168 } else if (transformation_name == "ROCMTransform") {
169 return detail::invoke_for<rocm::ROCMTransform>(std::forward<Visitor>(visitor));
170 } else if (transformation_name == "ROCMParallelizeNestedMap") {
172 } else if (transformation_name == "GPUOffloadNestedLoop") {
173 const std::string gpu_type = desc.contains("parameters") && desc["parameters"].contains("gpu_type")
174 ? desc["parameters"]["gpu_type"].get<std::string>()
176 if (gpu_type == rocm::ScheduleType_ROCM_Offload::value()) {
177 return detail::invoke_for<
179 } else {
180 return detail::invoke_for<
182 }
183 } else if (transformation_name == "GPUConditionPropagation") {
185 } else if (transformation_name == "GPUConditionPropagationScope") {
187 } else if (transformation_name == "GPULoopReordering") {
189 } else if (transformation_name == "EinsumLift") {
190 return detail::invoke_for<einsum::EinsumLift>(std::forward<Visitor>(visitor));
191 } else if (transformation_name == "EinsumExtend") {
192 return detail::invoke_for<einsum::EinsumExtend>(std::forward<Visitor>(visitor));
193 } else if (transformation_name == "EinsumExpand") {
194 return detail::invoke_for<einsum::EinsumPromotion>(std::forward<Visitor>(visitor));
195 } else if (transformation_name == "Einsum2Dot") {
196 return detail::invoke_for<einsum::Einsum2Dot>(std::forward<Visitor>(visitor));
197 } else if (transformation_name == "Einsum2Gemm") {
198 return detail::invoke_for<einsum::Einsum2Gemm>(std::forward<Visitor>(visitor));
199 } else if (transformation_name == "FuseFMA") {
200 return detail::invoke_for<fp_math::FuseFMA>(std::forward<Visitor>(visitor));
201 } else if (transformation_name == "UnfuseFMA") {
202 return detail::invoke_for<fp_math::UnfuseFMA>(std::forward<Visitor>(visitor));
203 } else if (transformation_name == "QuantizeContraction") {
204 return detail::invoke_for<fp_math::QuantizeContraction>(std::forward<Visitor>(visitor));
205 } else if (transformation_name == "OzakiContraction") {
206 return detail::invoke_for<fp_math::OzakiContraction>(std::forward<Visitor>(visitor));
207 } else if (transformation_name == "GpuMmaEinsumTransform") {
208 return detail::invoke_for<gpu::GpuMmaEinsumTransform>(std::forward<Visitor>(visitor));
209 } else {
210 throw transformations::InvalidTransformationDescriptionException("Unknown transformation: " + transformation_name);
211 }
212}
213
235template<typename T>
236 requires replayable_transformation_concept<T>
237bool verify_round_trip(builder::StructuredSDFGBuilder& builder, const nlohmann::json& desc, std::string& error_out) {
238 // 1) Reconstruct from the (possibly enriched) description and re-serialize.
239 T first(T::from_json(builder, desc));
240 nlohmann::json canonical;
241 first.to_json(canonical);
242
243 // 2) The output of to_json must itself satisfy the shared schema.
244 if (!validate_transformation_schema(canonical, error_out)) {
245 error_out = "to_json() produced a schema-invalid description: " + error_out;
246 return false;
247 }
248
249 // 3) The reconstructed type must match the description that selected it.
250 if (desc.contains("transformation_type") && desc.at("transformation_type") != canonical.at("transformation_type")) {
251 error_out = "transformation_type changed across round-trip: description '" +
252 desc.at("transformation_type").get<std::string>() + "' vs to_json '" +
253 canonical.at("transformation_type").get<std::string>() + "'";
254 return false;
255 }
256
257 // 4) from_json/to_json must be a stable fixed point: any field written by
258 // to_json but not read back by from_json would perturb this comparison.
259 T second(T::from_json(builder, canonical));
260 nlohmann::json canonical_again;
261 second.to_json(canonical_again);
262 if (canonical != canonical_again) {
263 error_out = "from_json/to_json is not a stable round-trip: " + canonical.dump() +
264 " != " + canonical_again.dump();
265 return false;
266 }
267
268 return true;
269}
270
284 builder::StructuredSDFGBuilder& builder, const nlohmann::json& desc, std::string& error_out
285) {
286 if (!desc.contains("transformation_type") || !desc.at("transformation_type").is_string()) {
287 error_out = "description is missing a string 'transformation_type'";
288 return false;
289 }
290 const auto transformation_name = desc.at("transformation_type").get<std::string>();
291 std::string local_error;
292 const bool ok = dispatch_transformation(transformation_name, desc, [&]<typename T>() -> bool {
293 return verify_round_trip<T>(builder, desc, local_error);
294 });
295 if (!ok) {
296 error_out = std::move(local_error);
297 }
298 return ok;
299}
300
301} // namespace transformations
302} // namespace sdfg
Note: Even though the class references unique_ptr, it will never delete an SDFG it has a reference to...
Definition structured_sdfg_builder.h:44
static const std::string value()
Definition cuda.h:45
static const std::string value()
Definition rocm.h:45
Definition gpu_offload_nested_loop.h:14
Exception thrown when a transformation description is invalid.
Definition transformation.h:110
Concept for transformations that can be replayed from a JSON description.
Definition transformation_registry.h:69
Concept for types that are transformations.
Definition recorder.h:21
Definition function_builder.h:10
Definition transformation_registry.h:79
decltype(auto) invoke_for(Visitor &&visitor)
Invoke a visitor with a validated transformation type.
Definition transformation_registry.h:89
Definition loop_parallelization.h:8
bool validate_transformation_schema(const nlohmann::json &j, std::string &error_out)
Validate that a transformation description respects the serialization schema.
Definition transformation_schema.h:45
decltype(auto) dispatch_transformation(const std::string &transformation_name, const nlohmann::json &desc, Visitor &&visitor)
Central registry mapping transformation type names to their C++ types.
Definition transformation_registry.h:117
bool verify_transformation_round_trip(builder::StructuredSDFGBuilder &builder, const nlohmann::json &desc, std::string &error_out)
Verify from_json/to_json consistency for the transformation named in desc.
Definition transformation_registry.h:283
bool verify_round_trip(builder::StructuredSDFGBuilder &builder, const nlohmann::json &desc, std::string &error_out)
Verify that a transformation's from_json and to_json are consistent inverses.
Definition transformation_registry.h:237
Definition control_flow_node.h:16
Definition analysis.h:7