Repository navigation
[IRBuilder] Hoist input gets across none exprs #9338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ir-builder-tunnel-params
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -138,6 +138,15 @@ MaybeResult<IRBuilder::HoistedVal> IRBuilder::hoistLastValue(bool greedy) { | |
| // No need for a scratch local to hoist an unreachable. | ||
| return HoistedVal{Index(hoistIndex), nullptr}; | ||
| } | ||
| if (auto* get = expr->dynCast<LocalGet>(); | ||
| get && getScope().isInputLocal(get->index)) { | ||
| // Input scratch locals are never modified while their initial `local.get`s | ||
| // remain on the stack, so we can hoist them directly across subsequent | ||
| // none-typed expressions without allocating a new scratch local. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
(module
(func (export "test") (result i32)
i32.const 10
block (param i32) (result i32)
i32.const 99
local.set 0 ;; (or `local.set $scratch` in WAT) — accepted & passes validation!
end
)
)This should not even validate (seems like a bug) but it manages to write to a scratch local in an unexpected way. Not to the live local, but still, this looks concerning...
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yeah, we shouldn't allow accessing scratch locals (introduced for any reason) by index like that. But fixing that mostly-unrelated bug will restore this invariant.
This is all internal implementation details of the control flow input lowering, so I'm not sure why the header would be the right location for a comment.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough about a comment. Otherwise lgtm, but I think we need to fix that bug before this lands, as the fuzzer should be able to error until then.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the fuzzer would be affected. It should never be parsing modules that incorrectly access scratch locals via out-of-bounds local indices.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I guess it would take a text-level fuzzer, yeah, good point. Needs to be an invalid module. |
||
| stack.erase(stack.begin() + valIndex); | ||
| stack.push_back(get); | ||
| return HoistedVal{Index(stack.size() - 1), nullptr}; | ||
| } | ||
| // Hoist with a scratch local. Normally the scratch local is the same type as | ||
| // the hoisted expression, but we may need to adjust it given the enabled | ||
| // features. Otherwise, if the expression has a tuple type with a more refined | ||
|
|
@@ -919,24 +928,17 @@ Result<> IRBuilder::pushScope(ScopeCtx&& scope) { | |
| inputs.push_back(getScope().exprStack.back()); | ||
| getScope().exprStack.pop_back(); | ||
| } | ||
| const auto& parentLocals = getScope().inputLocals; | ||
| // The new scope may only receive a suffix of the outer scope's parameters, | ||
| // so match inputs starting from the remaining size of the outer stack | ||
| // rather than from the beginning. | ||
| size_t baseIndex = getScope().exprStack.size(); | ||
| for (size_t i = 0; i < inputs.size(); ++i) { | ||
| // Move an enclosing scope's input expression (a scratch local.get or a | ||
| // catch pop) directly into this scope rather than allocating a new | ||
| // scratch local. | ||
| auto* expr = inputs[i]; | ||
| size_t parentIndex = baseIndex + i; | ||
| bool isInputGet = expr->is<LocalGet>() && | ||
| expr->type == scope.inputType[i] && | ||
| parentIndex < parentLocals.size(); | ||
| auto* get = expr->dynCast<LocalGet>(); | ||
| bool isInputGet = get && get->type == scope.inputType[i] && | ||
| getScope().isInputLocal(get->index); | ||
| bool isPop = isUnnamedBlock && inputs.size() == 1 && expr->is<Pop>(); | ||
| if (isInputGet) { | ||
| assert(expr->cast<LocalGet>()->index == parentLocals[parentIndex]); | ||
| scope.inputLocals.push_back(parentLocals[parentIndex]); | ||
| scope.inputLocals.push_back(get->index); | ||
| } else if (!isPop) { | ||
| auto scratch = addScratchLocal(scope.inputType[i]); | ||
| CHECK_ERR(scratch); | ||
|
Comment on lines
+936
to
944
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When auto* get = expr->dynCast<LocalGet>();
Type type = i < scope.inputType.size() ? scope.inputType[i] : Type::none;
bool isInputGet = get && get->type == type &&
getScope().isInputLocal(get->index);
bool isPop = isUnnamedBlock && inputs.size() == 1 && expr->is<Pop>();
if (isInputGet) {
scope.inputLocals.push_back(get->index);
} else if (!isPop) {
auto scratch = addScratchLocal(type);
CHECK_ERR(scratch); |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
std::findin the header file introduces a dependency on<algorithm>. SinceinputLocalsis typically a very small vector, we can replace it with a simple range-based for loop to avoid this dependency and keep the header self-contained.