|
91 | 91 | // merged, and at the end we traverse the entire merged module once to fuse
|
92 | 92 | // imports and exports.
|
93 | 93 | //
|
| 94 | +// Debugging: Set BINARYEN_PASS_DEBUG=1 in the env to get validation after each |
| 95 | +// merging of a module (like pass-debug mode for the pass runner, this does |
| 96 | +// expensive work after each incremental operation). This can take quadratic |
| 97 | +// time, so we do not do it by default. |
| 98 | +// |
94 | 99 |
|
95 | 100 | #include "ir/module-utils.h"
|
96 | 101 | #include "ir/names.h"
|
@@ -418,7 +423,7 @@ void checkLimit(bool& valid, const char* kind, T* export_, T* import) {
|
418 | 423 |
|
419 | 424 | // Find pairs of matching imports and exports, and make uses of the import refer
|
420 | 425 | // to the exported item (which has been merged into the module).
|
421 |
| -void fuseImportsAndExports() { |
| 426 | +void fuseImportsAndExports(const PassOptions& options) { |
422 | 427 | // First, scan the exports and build a map. We build a map of [module name] to
|
423 | 428 | // [export name => internal name]. For example, consider this module:
|
424 | 429 | //
|
@@ -466,80 +471,86 @@ void fuseImportsAndExports() {
|
466 | 471 | }
|
467 | 472 | });
|
468 | 473 |
|
469 |
| - // Make sure that the export types match the import types. |
470 |
| - bool valid = true; |
471 |
| - ModuleUtils::iterImportedFunctions(merged, [&](Function* import) { |
472 |
| - auto internalName = kindModuleExportMaps[ExternalKind::Function] |
473 |
| - [import->module][import->base]; |
474 |
| - if (internalName.is()) { |
475 |
| - auto* export_ = merged.getFunction(internalName); |
476 |
| - if (!HeapType::isSubType(export_->type, import->type)) { |
477 |
| - reportTypeMismatch(valid, "function", import); |
478 |
| - std::cerr << "type " << export_->type << " is not a subtype of " |
479 |
| - << import->type << ".\n"; |
480 |
| - } |
481 |
| - } |
482 |
| - }); |
483 |
| - ModuleUtils::iterImportedTables(merged, [&](Table* import) { |
484 |
| - auto internalName = |
485 |
| - kindModuleExportMaps[ExternalKind::Table][import->module][import->base]; |
486 |
| - if (internalName.is()) { |
487 |
| - auto* export_ = merged.getTable(internalName); |
488 |
| - checkLimit(valid, "table", export_, import); |
489 |
| - if (export_->type != import->type) { |
490 |
| - reportTypeMismatch(valid, "table", import); |
491 |
| - std::cerr << "export type " << export_->type |
492 |
| - << " is different from import type " << import->type << ".\n"; |
493 |
| - } |
494 |
| - } |
495 |
| - }); |
496 |
| - ModuleUtils::iterImportedMemories(merged, [&](Memory* import) { |
497 |
| - auto internalName = |
498 |
| - kindModuleExportMaps[ExternalKind::Memory][import->module][import->base]; |
499 |
| - if (internalName.is()) { |
500 |
| - auto* export_ = merged.getMemory(internalName); |
501 |
| - if (export_->is64() != import->is64()) { |
502 |
| - reportTypeMismatch(valid, "memory", import); |
503 |
| - std::cerr << "index type should match.\n"; |
| 474 | + if (options.validate) { |
| 475 | + // Make sure that the export types match the import types. |
| 476 | + bool valid = true; |
| 477 | + ModuleUtils::iterImportedFunctions(merged, [&](Function* import) { |
| 478 | + auto internalName = kindModuleExportMaps[ExternalKind::Function] |
| 479 | + [import->module][import->base]; |
| 480 | + if (internalName.is()) { |
| 481 | + auto* export_ = merged.getFunction(internalName); |
| 482 | + if (!HeapType::isSubType(export_->type, import->type)) { |
| 483 | + reportTypeMismatch(valid, "function", import); |
| 484 | + std::cerr << "type " << export_->type << " is not a subtype of " |
| 485 | + << import->type << ".\n"; |
| 486 | + } |
504 | 487 | }
|
505 |
| - checkLimit(valid, "memory", export_, import); |
506 |
| - } |
507 |
| - }); |
508 |
| - ModuleUtils::iterImportedGlobals(merged, [&](Global* import) { |
509 |
| - auto internalName = |
510 |
| - kindModuleExportMaps[ExternalKind::Global][import->module][import->base]; |
511 |
| - if (internalName.is()) { |
512 |
| - auto* export_ = merged.getGlobal(internalName); |
513 |
| - if (export_->mutable_ != import->mutable_) { |
514 |
| - reportTypeMismatch(valid, "global", import); |
515 |
| - std::cerr << "mutability should match.\n"; |
| 488 | + }); |
| 489 | + ModuleUtils::iterImportedTables(merged, [&](Table* import) { |
| 490 | + auto internalName = |
| 491 | + kindModuleExportMaps[ExternalKind::Table][import->module][import->base]; |
| 492 | + if (internalName.is()) { |
| 493 | + auto* export_ = merged.getTable(internalName); |
| 494 | + checkLimit(valid, "table", export_, import); |
| 495 | + if (export_->type != import->type) { |
| 496 | + reportTypeMismatch(valid, "table", import); |
| 497 | + std::cerr << "export type " << export_->type |
| 498 | + << " is different from import type " << import->type |
| 499 | + << ".\n"; |
| 500 | + } |
516 | 501 | }
|
517 |
| - if (export_->mutable_ && export_->type != import->type) { |
518 |
| - reportTypeMismatch(valid, "global", import); |
519 |
| - std::cerr << "export type " << export_->type |
520 |
| - << " is different from import type " << import->type << ".\n"; |
| 502 | + }); |
| 503 | + ModuleUtils::iterImportedMemories(merged, [&](Memory* import) { |
| 504 | + auto internalName = kindModuleExportMaps[ExternalKind::Memory] |
| 505 | + [import->module][import->base]; |
| 506 | + if (internalName.is()) { |
| 507 | + auto* export_ = merged.getMemory(internalName); |
| 508 | + if (export_->is64() != import->is64()) { |
| 509 | + reportTypeMismatch(valid, "memory", import); |
| 510 | + std::cerr << "index type should match.\n"; |
| 511 | + } |
| 512 | + checkLimit(valid, "memory", export_, import); |
521 | 513 | }
|
522 |
| - if (!export_->mutable_ && !Type::isSubType(export_->type, import->type)) { |
523 |
| - reportTypeMismatch(valid, "global", import); |
524 |
| - std::cerr << "type " << export_->type << " is not a subtype of " |
525 |
| - << import->type << ".\n"; |
| 514 | + }); |
| 515 | + ModuleUtils::iterImportedGlobals(merged, [&](Global* import) { |
| 516 | + auto internalName = kindModuleExportMaps[ExternalKind::Global] |
| 517 | + [import->module][import->base]; |
| 518 | + if (internalName.is()) { |
| 519 | + auto* export_ = merged.getGlobal(internalName); |
| 520 | + if (export_->mutable_ != import->mutable_) { |
| 521 | + reportTypeMismatch(valid, "global", import); |
| 522 | + std::cerr << "mutability should match.\n"; |
| 523 | + } |
| 524 | + if (export_->mutable_ && export_->type != import->type) { |
| 525 | + reportTypeMismatch(valid, "global", import); |
| 526 | + std::cerr << "export type " << export_->type |
| 527 | + << " is different from import type " << import->type |
| 528 | + << ".\n"; |
| 529 | + } |
| 530 | + if (!export_->mutable_ && |
| 531 | + !Type::isSubType(export_->type, import->type)) { |
| 532 | + reportTypeMismatch(valid, "global", import); |
| 533 | + std::cerr << "type " << export_->type << " is not a subtype of " |
| 534 | + << import->type << ".\n"; |
| 535 | + } |
526 | 536 | }
|
527 |
| - } |
528 |
| - }); |
529 |
| - ModuleUtils::iterImportedTags(merged, [&](Tag* import) { |
530 |
| - auto internalName = |
531 |
| - kindModuleExportMaps[ExternalKind::Tag][import->module][import->base]; |
532 |
| - if (internalName.is()) { |
533 |
| - auto* export_ = merged.getTag(internalName); |
534 |
| - if (export_->type != import->type) { |
535 |
| - reportTypeMismatch(valid, "tag", import); |
536 |
| - std::cerr << "export type " << export_->type |
537 |
| - << " is different from import type " << import->type << ".\n"; |
| 537 | + }); |
| 538 | + ModuleUtils::iterImportedTags(merged, [&](Tag* import) { |
| 539 | + auto internalName = |
| 540 | + kindModuleExportMaps[ExternalKind::Tag][import->module][import->base]; |
| 541 | + if (internalName.is()) { |
| 542 | + auto* export_ = merged.getTag(internalName); |
| 543 | + if (export_->type != import->type) { |
| 544 | + reportTypeMismatch(valid, "tag", import); |
| 545 | + std::cerr << "export type " << export_->type |
| 546 | + << " is different from import type " << import->type |
| 547 | + << ".\n"; |
| 548 | + } |
538 | 549 | }
|
| 550 | + }); |
| 551 | + if (!valid) { |
| 552 | + Fatal() << "import/export mismatches"; |
539 | 553 | }
|
540 |
| - }); |
541 |
| - if (!valid) { |
542 |
| - Fatal() << "import/export mismatches"; |
543 | 554 | }
|
544 | 555 |
|
545 | 556 | // Update the things we found.
|
@@ -730,18 +741,29 @@ Input source maps can be specified by adding an -ism option right after the modu
|
730 | 741 | // This is a later module: do a full merge.
|
731 | 742 | mergeInto(*currModule, inputFileName);
|
732 | 743 |
|
733 |
| - if (options.passOptions.validate) { |
734 |
| - if (!WasmValidator().validate(merged)) { |
| 744 | + // Validate after each merged module, when we are in pass-debug mode |
| 745 | + // (this can be quadratic time). |
| 746 | + if (PassRunner::getPassDebug()) { |
| 747 | + std::cerr << "[WasmMerge] merged : " << inputFile << '\n'; |
| 748 | + if (options.passOptions.validate && !WasmValidator().validate(merged)) { |
735 | 749 | std::cout << merged << '\n';
|
736 |
| - Fatal() << "error in validating merged after: " << inputFile; |
| 750 | + Fatal() << "error in validating after: " << inputFile; |
737 | 751 | }
|
738 | 752 | }
|
739 | 753 | }
|
740 | 754 | }
|
741 | 755 |
|
| 756 | + // If we didn't validate after each merged module, validate once at the very |
| 757 | + // end. This won't catch problems at the earliest point, but is still useful. |
| 758 | + if (!PassRunner::getPassDebug() && options.passOptions.validate && |
| 759 | + !WasmValidator().validate(merged)) { |
| 760 | + std::cout << merged << '\n'; |
| 761 | + Fatal() << "error in validating final merged"; |
| 762 | + } |
| 763 | + |
742 | 764 | // Fuse imports and exports now that everything is all together in the merged
|
743 | 765 | // module.
|
744 |
| - fuseImportsAndExports(); |
| 766 | + fuseImportsAndExports(options.passOptions); |
745 | 767 |
|
746 | 768 | {
|
747 | 769 | PassRunner passRunner(&merged);
|
|
0 commit comments