summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-validator.cpp
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2024-08-15 15:32:32 -0400
committerGitHub <noreply@github.com>2024-08-15 12:32:32 -0700
commit033a16ec0d063cbcfb6c67adcf228f70653a1bf5 (patch)
treebaa6c82aa41b55c05a7c0c4f64110295fdfdc039 /src/wasm/wasm-validator.cpp
parentd5675780f45f4e2994787fd7dbc5283a6c0162f0 (diff)
downloadbinaryen-033a16ec0d063cbcfb6c67adcf228f70653a1bf5.tar.gz
binaryen-033a16ec0d063cbcfb6c67adcf228f70653a1bf5.tar.bz2
binaryen-033a16ec0d063cbcfb6c67adcf228f70653a1bf5.zip
Simplify validation of stale types (#6842)
The previous rules for stale types were complicated and hard to remember: in general it was ok for result types to be further refinable as long as they were not refinable all the way to `unreachable`, but control flow structures had a carve-out and it was ok for them to be refinable all the way to unreachable. Simplify the rules so that further refinable result types are always ok, no matter what they can be refined to and no matter what kind of instruction is being validated. This will be much easier to remember and reason about. This relaxation of the rules strictly increases the set of valid IR, so no passes or tests need to be updated. It does make it possible for us to miss type refinement opportunities that previously would have been validation errors, but only in cases where non-control-flow instructions could have been refined all the way to unreachable, so the risk seems small.
Diffstat (limited to 'src/wasm/wasm-validator.cpp')
-rw-r--r--src/wasm/wasm-validator.cpp33
1 files changed, 9 insertions, 24 deletions
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index b35d1b3be..b032e6bad 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -3592,31 +3592,16 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
auto oldType = curr->type;
ReFinalizeNode().visit(curr);
auto newType = curr->type;
- if (newType != oldType) {
- // We accept concrete => undefined on control flow structures:
- // e.g.
- //
- // (drop (block (result i32) (unreachable)))
- //
- // The block has a type annotated on it, which can make its unreachable
- // contents have a concrete type. Refinalize will make it unreachable,
- // so both are valid here.
- bool validControlFlowStructureChange =
- Properties::isControlFlowStructure(curr) && oldType.isConcrete() &&
- newType == Type::unreachable;
- // It's ok in general for types to get refined as long as they don't
- // become unreachable.
- bool validRefinement =
- Type::isSubType(newType, oldType) && newType != Type::unreachable;
- if (!validRefinement && !validControlFlowStructureChange) {
- std::ostringstream ss;
- ss << "stale type found in " << scope << " on " << curr
- << "\n(marked as " << oldType << ", should be " << newType
- << ")\n";
- info.fail(ss.str(), curr, getFunction());
- }
- curr->type = oldType;
+ // It's ok for types to be further refinable, but they must admit a
+ // superset of the values allowed by the most precise possible type, i.e.
+ // they must not be strict subtypes of or unrelated to the refined type.
+ if (!Type::isSubType(newType, oldType)) {
+ std::ostringstream ss;
+ ss << "stale type found in " << scope << " on " << curr
+ << "\n(marked as " << oldType << ", should be " << newType << ")\n";
+ info.fail(ss.str(), curr, getFunction());
}
+ curr->type = oldType;
// check if a node is a duplicate - expressions must not be seen more than
// once
if (!seen.insert(curr).second) {