The IfOp builder takes a resultTypes up front. My problem is I have the thenBuilder and elseBuilder which have some user code getting added to blocks, each returns a Value and only after I have executed those functions can I get what value to yield and return out of the IfOp.
Is there a way to modify the resultTypes? or my choice is only to create the IfOp, get the returnTypes and then create a new one, this time setting the resultTypes?
Could this build function below have got resultTypes returned by thenBuider and elseBuilder (also performing some validation on both to ensure they are same number with same elementTypes)?
void IfOp::build(OpBuilder &builder, OperationState &result,
TypeRange resultTypes, Value cond,
function_ref<void(OpBuilder &, Location)> thenBuilder,
function_ref<void(OpBuilder &, Location)> elseBuilder) {
assert(thenBuilder && "the builder callback for 'then' must be present");
result.addOperands(cond);
result.addTypes(resultTypes);
OpBuilder::InsertionGuard guard(builder);
Region *thenRegion = result.addRegion();
builder.createBlock(thenRegion);
thenBuilder(builder, result.location);
Region *elseRegion = result.addRegion();
if (!elseBuilder)
return;
builder.createBlock(elseRegion);
elseBuilder(builder, result.location);
}
I’ve run into the same thing before but only when using the C and python builder APIs, which do not have the same restriction. I don’t know if it’s the best way, but it is reasonable to build invalid ops, so long as you correct them before verification. I suspect it is fine if you construct it with an empty range for result types, then when you know more, just setResultTypes on the underlying operation.
The verifier enforces that the terminator operand types match the if op’s result types, and so long as you arrange for that to happen, it is less important how you get there.
Yes, but if you already built the body/Region for each branch, the constructor that takes thenBuilder and elseBuilder can be invoked with lambdas that will splice your bodies in place.
This seems to do the trick, thanks, is this what you meant as well?
Although long term do we see value in having an ifOp builder in scf which handles this for the user (as a convenience)?
Instead of having to create the op twice and moving things around? I am sure I will run into the same thing for whileOp too, but I can use the same trick I think.
It isn’t clear to me why the creation of the first IfOp is necessary here?
I don’t know what is the type of trueResult, but can’t you do just something that looks like this:
You are absolutely correct, I am able to use this and then I don’t have to create 2 IfOps, I can splice ops from these blocks.
That resolves my dilemma I think, thanks all!
Alternatively, we could add a builder that derives the result types from the terminators of the regions, but that would require the body-builder callbacks to always create the proper terminator (and fail an assertion otherwise).