@@ -176,20 +176,27 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
176176 FunctionValidator (ValidationInfo* info) : info(*info) {}
177177
178178 struct BreakInfo {
179+ enum {
180+ UnsetArity = Index (-1 ),
181+ PoisonArity = Index (-2 )
182+ };
183+
179184 Type type;
180185 Index arity;
181- BreakInfo () {}
186+ BreakInfo () : arity(UnsetArity) {}
182187 BreakInfo (Type type, Index arity) : type(type), arity(arity) {}
188+
189+ bool hasBeenSet () {
190+ // Compare to the impossible value.
191+ return arity != UnsetArity;
192+ }
183193 };
184194
185- std::map<Name, Expression*> breakTargets;
186- std::map<Expression*, BreakInfo> breakInfos;
195+ std::unordered_map<Name, BreakInfo> breakInfos;
187196
188197 Type returnType = unreachable; // type used in returns
189198
190- std::set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that
191-
192- std::unordered_set<Expression*> seenExpressions; // expressions must not appear twice
199+ std::unordered_set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that
193200
194201 void noteLabelName (Name name);
195202
@@ -198,14 +205,14 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
198205
199206 static void visitPreBlock (FunctionValidator* self, Expression** currp) {
200207 auto * curr = (*currp)->cast <Block>();
201- if (curr->name .is ()) self->breakTargets [curr->name ] = curr ;
208+ if (curr->name .is ()) self->breakInfos [curr->name ];
202209 }
203210
204211 void visitBlock (Block* curr);
205212
206213 static void visitPreLoop (FunctionValidator* self, Expression** currp) {
207214 auto * curr = (*currp)->cast <Loop>();
208- if (curr->name .is ()) self->breakTargets [curr->name ] = curr ;
215+ if (curr->name .is ()) self->breakInfos [curr->name ];
209216 }
210217
211218 void visitLoop (Loop* curr);
@@ -285,16 +292,19 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
285292
286293void FunctionValidator::noteLabelName (Name name) {
287294 if (!name.is ()) return ;
288- shouldBeTrue (labelNames.find (name) == labelNames.end (), name, " names in Binaryen IR must be unique - IR generators must ensure that" );
289- labelNames.insert (name);
295+ bool inserted;
296+ std::tie (std::ignore, inserted) = labelNames.insert (name);
297+ shouldBeTrue (inserted, name, " names in Binaryen IR must be unique - IR generators must ensure that" );
290298}
291299
292300void FunctionValidator::visitBlock (Block* curr) {
293301 // if we are break'ed to, then the value must be right for us
294302 if (curr->name .is ()) {
295303 noteLabelName (curr->name );
296- if (breakInfos.count (curr) > 0 ) {
297- auto & info = breakInfos[curr];
304+ auto iter = breakInfos.find (curr->name );
305+ assert (iter != breakInfos.end ()); // we set it ourselves
306+ auto & info = iter->second ;
307+ if (info.hasBeenSet ()) {
298308 if (isConcreteType (curr->type )) {
299309 shouldBeTrue (info.arity != 0 , curr, " break arities must be > 0 if block has a value" );
300310 } else {
@@ -307,7 +317,7 @@ void FunctionValidator::visitBlock(Block* curr) {
307317 if (isConcreteType (curr->type ) && info.arity && info.type != unreachable) {
308318 shouldBeEqual (curr->type , info.type , curr, " block+breaks must have right type if breaks have arity" );
309319 }
310- shouldBeTrue (info.arity != Index (- 1 ) , curr, " break arities must match" );
320+ shouldBeTrue (info.arity != BreakInfo::PoisonArity , curr, " break arities must match" );
311321 if (curr->list .size () > 0 ) {
312322 auto last = curr->list .back ()->type ;
313323 if (isConcreteType (last) && info.type != unreachable) {
@@ -318,7 +328,7 @@ void FunctionValidator::visitBlock(Block* curr) {
318328 }
319329 }
320330 }
321- breakTargets .erase (curr-> name );
331+ breakInfos .erase (iter );
322332 }
323333 if (curr->list .size () > 1 ) {
324334 for (Index i = 0 ; i < curr->list .size () - 1 ; i++) {
@@ -347,11 +357,13 @@ void FunctionValidator::visitBlock(Block* curr) {
347357void FunctionValidator::visitLoop (Loop* curr) {
348358 if (curr->name .is ()) {
349359 noteLabelName (curr->name );
350- breakTargets.erase (curr->name );
351- if (breakInfos.count (curr) > 0 ) {
352- auto & info = breakInfos[curr];
360+ auto iter = breakInfos.find (curr->name );
361+ assert (iter != breakInfos.end ()); // we set it ourselves
362+ auto & info = iter->second ;
363+ if (info.hasBeenSet ()) {
353364 shouldBeEqual (info.arity , Index (0 ), curr, " breaks to a loop cannot pass a value" );
354365 }
366+ breakInfos.erase (iter);
355367 }
356368 if (curr->type == none) {
357369 shouldBeFalse (isConcreteType (curr->body ->type ), curr, " bad body for a loop that has no value" );
@@ -394,12 +406,12 @@ void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr
394406 shouldBeUnequal (valueType, none, curr, " breaks must have a valid value" );
395407 arity = 1 ;
396408 }
397- if (!shouldBeTrue (breakTargets.count (name) > 0 , curr, " all break targets must be valid" )) return ;
398- auto * target = breakTargets[name];
399- if (breakInfos.count (target) == 0 ) {
400- breakInfos[target] = BreakInfo (valueType, arity);
409+ auto iter = breakInfos.find (name);
410+ if (!shouldBeTrue (iter != breakInfos.end (), curr, " all break targets must be valid" )) return ;
411+ auto & info = iter->second ;
412+ if (!info.hasBeenSet ()) {
413+ info = BreakInfo (valueType, arity);
401414 } else {
402- auto & info = breakInfos[target];
403415 if (info.type == unreachable) {
404416 info.type = valueType;
405417 } else if (valueType != unreachable) {
@@ -408,7 +420,7 @@ void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr
408420 }
409421 }
410422 if (arity != info.arity ) {
411- info.arity = Index (- 1 ); // a poison value
423+ info.arity = BreakInfo::PoisonArity;
412424 }
413425 }
414426}
@@ -810,7 +822,7 @@ void FunctionValidator::visitFunction(Function* curr) {
810822 if (returnType != unreachable) {
811823 shouldBeEqual (curr->result , returnType, curr->body , " function result must match, if function has returns" );
812824 }
813- shouldBeTrue (breakTargets .empty (), curr->body , " all named break targets must exist" );
825+ shouldBeTrue (breakInfos .empty (), curr->body , " all named break targets must exist" );
814826 returnType = unreachable;
815827 labelNames.clear ();
816828 // if function has a named type, it must match up with the function's params and result
@@ -819,24 +831,6 @@ void FunctionValidator::visitFunction(Function* curr) {
819831 shouldBeTrue (ft->params == curr->params , curr->name , " function params must match its declared type" );
820832 shouldBeTrue (ft->result == curr->result , curr->name , " function result must match its declared type" );
821833 }
822- // expressions must not be seen more than once
823- struct Walker : public PostWalker <Walker, UnifiedExpressionVisitor<Walker>> {
824- std::unordered_set<Expression*>& seen;
825- std::vector<Expression*> dupes;
826-
827- Walker (std::unordered_set<Expression*>& seen) : seen(seen) {}
828-
829- void visitExpression (Expression* curr) {
830- bool inserted;
831- std::tie (std::ignore, inserted) = seen.insert (curr);
832- if (!inserted) dupes.push_back (curr);
833- }
834- };
835- Walker walker (seenExpressions);
836- walker.walk (curr->body );
837- for (auto * bad : walker.dupes ) {
838- info.fail (" expression seen more than once in the tree" , bad, getFunction ());
839- }
840834}
841835
842836static bool checkOffset (Expression* curr, Address add, Address max) {
@@ -890,9 +884,12 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
890884 struct BinaryenIRValidator : public PostWalker <BinaryenIRValidator, UnifiedExpressionVisitor<BinaryenIRValidator>> {
891885 ValidationInfo& info;
892886
887+ std::unordered_set<Expression*> seen;
888+
893889 BinaryenIRValidator (ValidationInfo& info) : info(info) {}
894890
895891 void visitExpression (Expression* curr) {
892+ auto scope = getFunction () ? getFunction ()->name : Name (" (global scope)" );
896893 // check if a node type is 'stale', i.e., we forgot to finalize() the node.
897894 auto oldType = curr->type ;
898895 ReFinalizeNode ().visit (curr);
@@ -907,11 +904,19 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
907904 // ok for it to be either i32 or unreachable.
908905 if (!(isConcreteType (oldType) && newType == unreachable)) {
909906 std::ostringstream ss;
910- ss << " stale type found in " << ( getFunction () ? getFunction ()-> name : Name ( " (global scope) " )) << " on " << curr << " \n (marked as " << printType (oldType) << " , should be " << printType (newType) << " )\n " ;
907+ ss << " stale type found in " << scope << " on " << curr << " \n (marked as " << printType (oldType) << " , should be " << printType (newType) << " )\n " ;
911908 info.fail (ss.str (), curr, getFunction ());
912909 }
913910 curr->type = oldType;
914911 }
912+ // check if a node is a duplicate - expressions must not be seen more than once
913+ bool inserted;
914+ std::tie (std::ignore, inserted) = seen.insert (curr);
915+ if (!inserted) {
916+ std::ostringstream ss;
917+ ss << " expression seen more than once in the tree in " << scope << " on " << curr << ' \n ' ;
918+ info.fail (ss.str (), curr, getFunction ());
919+ }
915920 }
916921 };
917922 BinaryenIRValidator binaryenIRValidator (info);
@@ -952,7 +957,7 @@ static void validateExports(Module& module, ValidationInfo& info) {
952957 }
953958 }
954959 }
955- std::set <Name> exportNames;
960+ std::unordered_set <Name> exportNames;
956961 for (auto & exp : module .exports ) {
957962 Name name = exp->value ;
958963 if (exp->kind == ExternalKind::Function) {
0 commit comments