@@ -173,7 +173,9 @@ class TypeConverter {
173173 // / conversion has finished.
174174 // /
175175 // / Note: Target materializations may optionally accept an additional Type
176- // / parameter, which is the original type of the SSA value.
176+ // / parameter, which is the original type of the SSA value. Furthermore, `T`
177+ // / can be a TypeRange; in that case, the function must return a
178+ // / SmallVector<Value>.
177179
178180 // / This method registers a materialization that will be called when
179181 // / converting (potentially multiple) block arguments that were the result of
@@ -210,6 +212,9 @@ class TypeConverter {
210212 // / will be invoked with: outputType = "t3", inputs = "v2",
211213 // originalType = "t1". Note that the original type "t1" cannot be recovered
212214 // / from just "t3" and "v2"; that's why the originalType parameter exists.
215+ // /
216+ // / Note: During a 1:N conversion, the result types can be a TypeRange. In
217+ // / that case the materialization produces a SmallVector<Value>.
213218 template <typename FnT, typename T = typename llvm::function_traits<
214219 std::decay_t <FnT>>::template arg_t <1 >>
215220 void addTargetMaterialization (FnT &&callback) {
@@ -316,6 +321,11 @@ class TypeConverter {
316321 Value materializeTargetConversion (OpBuilder &builder, Location loc,
317322 Type resultType, ValueRange inputs,
318323 Type originalType = {}) const ;
324+ SmallVector<Value> materializeTargetConversion (OpBuilder &builder,
325+ Location loc,
326+ TypeRange resultType,
327+ ValueRange inputs,
328+ Type originalType = {}) const ;
319329
320330 // / Convert an attribute present `attr` from within the type `type` using
321331 // / the registered conversion functions. If no applicable conversion has been
@@ -340,9 +350,9 @@ class TypeConverter {
340350
341351 // / The signature of the callback used to materialize a target conversion.
342352 // /
343- // / Arguments: builder, result type , inputs, location, original type
344- using TargetMaterializationCallbackFn =
345- std::function<Value( OpBuilder &, Type , ValueRange, Location, Type)>;
353+ // / Arguments: builder, result types , inputs, location, original type
354+ using TargetMaterializationCallbackFn = std::function<SmallVector<Value>(
355+ OpBuilder &, TypeRange , ValueRange, Location, Type)>;
346356
347357 // / The signature of the callback used to convert a type attribute.
348358 using TypeAttributeConversionCallbackFn =
@@ -409,32 +419,56 @@ class TypeConverter {
409419 // / callback.
410420 // /
411421 // / With callback of form:
412- // / `Value(OpBuilder &, T, ValueRange, Location, Type)`
422+ // / - Value(OpBuilder &, T, ValueRange, Location, Type)
423+ // / - SmallVector<Value>(OpBuilder &, TypeRange, ValueRange, Location, Type)
413424 template <typename T, typename FnT>
414425 std::enable_if_t <
415426 std::is_invocable_v<FnT, OpBuilder &, T, ValueRange, Location, Type>,
416427 TargetMaterializationCallbackFn>
417428 wrapTargetMaterialization (FnT &&callback) const {
418429 return [callback = std::forward<FnT>(callback)](
419- OpBuilder &builder, Type resultType, ValueRange inputs,
420- Location loc, Type originalType) -> Value {
421- if (T derivedType = dyn_cast<T>(resultType))
422- return callback (builder, derivedType, inputs, loc, originalType);
423- return Value ();
430+ OpBuilder &builder, TypeRange resultTypes, ValueRange inputs,
431+ Location loc, Type originalType) -> SmallVector<Value> {
432+ SmallVector<Value> result;
433+ if constexpr (std::is_same<T, TypeRange>::value) {
434+ // This is a 1:N target materialization. Return the produces values
435+ // directly.
436+ result = callback (builder, resultTypes, inputs, loc, originalType);
437+ } else if constexpr (std::is_assignable<Type, T>::value) {
438+ // This is a 1:1 target materialization. Invoke the callback only if a
439+ // single SSA value is requested.
440+ if (resultTypes.size () == 1 ) {
441+ // Invoke the callback only if the type class of the callback matches
442+ // the requested result type.
443+ if (T derivedType = dyn_cast<T>(resultTypes.front ())) {
444+ // 1:1 materializations produce single values, but we store 1:N
445+ // target materialization functions in the type converter. Wrap the
446+ // result value in a SmallVector<Value>.
447+ Value val =
448+ callback (builder, derivedType, inputs, loc, originalType);
449+ if (val)
450+ result.push_back (val);
451+ }
452+ }
453+ } else {
454+ static_assert (sizeof (T) == 0 , " T must be a Type or a TypeRange" );
455+ }
456+ return result;
424457 };
425458 }
426459 // / With callback of form:
427- // / `Value(OpBuilder &, T, ValueRange, Location)`
460+ // / - Value(OpBuilder &, T, ValueRange, Location)
461+ // / - SmallVector<Value>(OpBuilder &, TypeRange, ValueRange, Location)
428462 template <typename T, typename FnT>
429463 std::enable_if_t <
430464 std::is_invocable_v<FnT, OpBuilder &, T, ValueRange, Location>,
431465 TargetMaterializationCallbackFn>
432466 wrapTargetMaterialization (FnT &&callback) const {
433467 return wrapTargetMaterialization<T>(
434468 [callback = std::forward<FnT>(callback)](
435- OpBuilder &builder, T resultType , ValueRange inputs, Location loc,
436- Type originalType) -> Value {
437- return callback (builder, resultType , inputs, loc);
469+ OpBuilder &builder, T resultTypes , ValueRange inputs, Location loc,
470+ Type originalType) {
471+ return callback (builder, resultTypes , inputs, loc);
438472 });
439473 }
440474
0 commit comments