Hey,
I wanted to propose adding a C++ function call emitter that takes as input a FuncOp and emits C++ function calls matching the names of the Ops inside the function. E.g.,
func @test_foo_print() {
%0 = "foo.constant"() {value = dense<[0, 1]> : tensor<2xi32>} : () -> (i32)
"foo.print"(%0) : (i32) -> ()
return
}
func @test_single_return(%arg0 : i32) -> i32 {
return %arg0 : i32
}
func @test_multiple_return() -> (i32, i32) {
%0:2 = "foo.blah"() : () -> (i32, i32)
return %0#0, %0#1 : i32, i32
}
And produces [modulo formatting and customization for emitting function/op names]
static void test_foo_print() {
int32_t v1;
v1 = foo::constant(/*value=*/{0, 1});
foo::print(v1);
}
static int32_t test_single_return(int32_t v2) { return v2; }
static std::tuple<int32_t, int32_t> test_multiple_return() {
int32_t v3;
int32_t v4;
std::tie(v3, v4) = foo::blah();
return std::make_tuple(v3, v4);
}
This is a utility class parameterized on Ops, Types, attributes & function/op name emitting (e.g., dialect emitted as namespace or something else) so that the default lowering of an Op is a call to a similarly named function with first its operands, then a list of attributes (in alphabetical order), then regions (potentially emitted as lambdas). The above would the default behavior, but the intention is to make it parameterizable with the defaults, say ordering, a helper method with which to parametrize the output. Multiple result types modelled using std::tuple.
Goal
Create utility class/method to make producing C++-call like output emitters simpler
Non-goals
No verification from the function as to the semantics of the region (e.g., the emitter is just an emitter and doesn't understand that actual semantics of the ops or the regions, merely walks over the ops printing ops as calls);
No C++ function registrations known (e.g., whether foo::constant in the above example exists is unknown)
Not a C dialect (e.g., this does not attempt to model C or C++ as a dialect, just lowering to C++ calls to match a FuncOp);
The emitter could be rebased on such a dialect, then that dialect could also be optimized on etc. Here the goal is more a textual output of a function that is amenable to export as simple calls, and so much smaller in scope than trying to model C.
The proposal is to have the base emitter and then also register a translation to C++ calls where compile time registered specializations (similar to how registered for translate functions) could be used.
Conceptually this is similar to AsmPrinter except focussed/specialized on generating calls. The initial use case I had was testing & shape functions, but I saw similar emitters being written and having some common base to make writing these simpler seemed good to have. Even if it is narrow in its claims.
-- Jacques
[...]
Conceptually this is similar to AsmPrinter except focussed/specialized on generating calls.
What about something simpler:
static std::tuple<int32_t, int32_t> test_multiple_return() {
auto [ v3, v4 ] = foo::blah();
return { v3, v4 };
}