From f2968c10fa4545697080a055188800d8c1602220 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 9 Jun 2026 05:35:41 +0000 Subject: [PATCH 1/4] [wasm-split] Split active segments (part 1) This removes the assumption that all segments should be pinned to the primary module. This first checks memory and table usage, and if a memory/table is exclusively used in a secondary module, moves active data / elem segments that reference the table to that secondary module. We check whether a segment can trap, and if so, we pin it to the primary because they should be evaluated at the primary instantiation time. When a memory/table is used in multiple modules, it is suboptimal to just scan all segments of that memory/table and mark them as used in those modules, because as in the case of #????, this can generate unnecessary exports. For example, if table $t is used both in the primary and the secondary, and elem $e is like ```wast (elem $e (table $t) (i32.const 0) ... (global.get $g)) ``` because $e will stay in the primary module, the secondary module is not going to see $g, so it should NOT be exported and imported. This PR figures out a single owner module for each memory/table, and marks segments as "used" there. This does not currently reduces the size of primary modules of acx_gallery and essentials because every table is exported, so there is no tables exclusively used by a secondary. This may change when we remove internal exports. --- src/ir/module-splitting.cpp | 194 +++++++++++++++----- test/lit/wasm-split/elem-global-deps.wast | 55 ++++++ test/lit/wasm-split/passive-deps.wast | 38 ++++ test/lit/wasm-split/split-module-items.wast | 75 ++++++-- 4 files changed, 308 insertions(+), 54 deletions(-) create mode 100644 test/lit/wasm-split/elem-global-deps.wast create mode 100644 test/lit/wasm-split/passive-deps.wast diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 6fd5ebbd89f..e79fcf3d672 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -77,6 +77,7 @@ #include "ir/find_all.h" #include "ir/module-utils.h" #include "ir/names.h" +#include "support/stdckdint.h" #include "wasm-builder.h" #include "wasm.h" @@ -363,6 +364,8 @@ struct ModuleSplitter { std::unordered_set memories; std::unordered_set tables; std::unordered_set tags; + std::unordered_set dataSegments; + std::unordered_set elementSegments; }; using PrimarySecondaryUsedNames = std::pair>; @@ -647,23 +650,6 @@ void ModuleSplitter::thunkExportedSecondaryFunctions() { } ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { - auto walkSegments = [](auto& walker, Module* module) { - walker.setModule(module); - for (auto& curr : module->elementSegments) { - if (curr->offset) { - walker.walk(curr->offset); - } - for (auto* item : curr->data) { - walker.walk(item); - } - } - for (auto& curr : module->dataSegments) { - if (curr->offset) { - walker.walk(curr->offset); - } - } - }; - struct NameCollector : public PostWalker> { @@ -699,9 +685,13 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { case ModuleItemKind::Tag: \ used.tags.insert(cast->field); \ break; \ - case ModuleItemKind::Function: \ case ModuleItemKind::DataSegment: \ + used.dataSegments.insert(cast->field); \ + break; \ case ModuleItemKind::ElementSegment: \ + used.elementSegments.insert(cast->field); \ + break; \ + case ModuleItemKind::Function: \ case ModuleItemKind::Invalid: \ break; \ } \ @@ -726,32 +716,13 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { used.memories.insert(funcUsed.memories.begin(), funcUsed.memories.end()); used.tables.insert(funcUsed.tables.begin(), funcUsed.tables.end()); used.tags.insert(funcUsed.tags.begin(), funcUsed.tags.end()); + used.dataSegments.insert(funcUsed.dataSegments.begin(), + funcUsed.dataSegments.end()); + used.elementSegments.insert(funcUsed.elementSegments.begin(), + funcUsed.elementSegments.end()); } NameCollector collector(used); - // We shouldn't use collector.walkModuleCode here, because we don't want to - // walk global initializers. At this point, all globals are still in the - // primary module, so if we walk global initializers here, other globals - // appearing in their initializers will all be marked as used in the primary - // module, which is not what we want. - // - // For example, we have (global $a i32 (global.get $b)). Because $a is at - // this point still in the primary module, $b will be marked as "used" in - // the primary module. But $a can be moved to a secondary module later if it - // is used exclusively by that module. Then $b can be also moved, in case it - // doesn't have other uses. But if it is marked as "used" in the primary - // module, it can't. - walkSegments(collector, &module); - for (auto& segment : module.dataSegments) { - if (segment->isActive()) { - used.memories.insert(segment->memory); - } - } - for (auto& segment : module.elementSegments) { - if (segment->isActive()) { - used.tables.insert(segment->table); - } - } return used; }; @@ -849,6 +820,107 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { } } + auto mayTrap = [&](auto* segment) { + if (segment->offset && + EffectAnalyzer(config.passOptions, primary, segment->offset) + .hasUnremovableSideEffects()) { + return true; + } + if constexpr (std::is_same_v) { + if (primary.features.hasCustomDescriptors()) { + for (auto* item : segment->data) { + if (EffectAnalyzer(config.passOptions, primary, item) + .hasUnremovableSideEffects()) { + return true; + } + } + } + } + + // Check for out-of-bounds offset. This is adapted from maybeRootSegment + // function in RemoveUnusedModuleElements pass. + if (!config.passOptions.trapsNeverHappen) { + Index segmentSize; + Index parentSize; + if constexpr (std::is_same_v) { + segmentSize = segment->data.size(); + auto* memory = primary.getMemory(segment->memory); + parentSize = memory->initial << memory->pageSizeLog2; + } else { + segmentSize = segment->data.size(); + auto* table = primary.getTable(segment->table); + parentSize = table->initial * Table::kPageSize; + } + + // Check if this might trap. If it is obviously in bounds then it cannot. + auto* c = segment->offset->template dynCast(); + // Check for overflow in the largest possible space of addresses. + using AddressType = Address::address64_t; + AddressType maxWritten; + // If there is no integer, or if there is and the addition overflows, or + // if the addition leads to a too-large value, then we may trap. + if (!c || + std::ckd_add(&maxWritten, + (AddressType)segmentSize, + (AddressType)c->value.getInteger()) || + maxWritten > parentSize) { + return true; + } + } + return false; + }; + + // Iterate on active data and element segments. If its table or memory is + // used by a single secondary module, mark it "used" there. Only scan its + // 'offset' or 'data'(in case of ElementSegment) and add it to that module's + // used only when it is a sole secondary owner. If not assign it to the + // primary module and scan it there. + ModuleUtils::iterActiveDataSegments(primary, [&](DataSegment* segment) { + UsedNames* owner = getOwner(segment->memory, &UsedNames::memories); + if (mayTrap(segment)) { + owner = &primaryUsed; + } + if (!owner) { + return; + } + owner->dataSegments.insert(segment->name); + owner->memories.insert(segment->memory); + if (segment->offset) { + NameCollector(*owner).walk(segment->offset); + } + }); + + ModuleUtils::iterActiveElementSegments(primary, [&](ElementSegment* segment) { + UsedNames* owner = getOwner(segment->table, &UsedNames::tables); + if (mayTrap(segment)) { + owner = &primaryUsed; + } + if (!owner) { + return; + } + owner->elementSegments.insert(segment->name); + owner->tables.insert(segment->table); + if (segment->offset) { + NameCollector(*owner).walk(segment->offset); + } + for (auto* item : segment->data) { + NameCollector(*owner).walk(item); + } + }); + + // Passive element segments contain expressions (e.g. global.get) in their + // data arrays that must be scanned. Since currently functions referring to + // segments are forced into the primary module, passive segments always belong + // to the primary module. + for (auto& segment : primary.elementSegments) { + if (segment->isPassive() && + primaryUsed.elementSegments.contains(segment->name)) { + for (auto* item : segment->data) { + NameCollector(primaryUsed).walk(item); + } + } + } + // Compute the transitive closure of globals referenced in other globals' // initializers. WebAssembly validation requires that global initializers only // refer to previously defined globals. Therefore, `primary.globals` is @@ -995,6 +1067,46 @@ void ModuleSplitter::shareImportableItems() { for (auto& name : tagsToRemove) { primary.removeTag(name); } + + // Move segments that are exclusively used in a secondary module. If not, do + // nothing. (Segments cannot be imported / exported. They will be handled in + // indirectReferencesToSecondaryFunctions.) + + std::vector dataSegmentsToRemove; + for (auto& dataSegment : primary.dataSegments) { + auto usingSecondaries = + getUsingSecondaries(dataSegment->name, &UsedNames::dataSegments); + bool inPrimary = primaryUsed.dataSegments.contains(dataSegment->name); + + if (!inPrimary && usingSecondaries.empty()) { + dataSegmentsToRemove.push_back(dataSegment->name); + } else if (!inPrimary && usingSecondaries.size() == 1) { + auto* secondary = usingSecondaries[0]; + ModuleUtils::copyDataSegment(dataSegment.get(), *secondary); + dataSegmentsToRemove.push_back(dataSegment->name); + } + } + for (auto& name : dataSegmentsToRemove) { + primary.removeDataSegment(name); + } + + std::vector elementSegmentsToRemove; + for (auto& elementSegment : primary.elementSegments) { + auto usingSecondaries = + getUsingSecondaries(elementSegment->name, &UsedNames::elementSegments); + bool inPrimary = primaryUsed.elementSegments.contains(elementSegment->name); + + if (!inPrimary && usingSecondaries.empty()) { + elementSegmentsToRemove.push_back(elementSegment->name); + } else if (!inPrimary && usingSecondaries.size() == 1) { + auto* secondary = usingSecondaries[0]; + ModuleUtils::copyElementSegment(elementSegment.get(), *secondary); + elementSegmentsToRemove.push_back(elementSegment->name); + } + } + for (auto& name : elementSegmentsToRemove) { + primary.removeElementSegment(name); + } } void ModuleSplitter::indirectReferencesToSecondaryFunctions() { diff --git a/test/lit/wasm-split/elem-global-deps.wast b/test/lit/wasm-split/elem-global-deps.wast new file mode 100644 index 00000000000..67f3fecc03c --- /dev/null +++ b/test/lit/wasm-split/elem-global-deps.wast @@ -0,0 +1,55 @@ +;; RUN: wasm-split -all -g %s --keep-funcs=keep -o1 %t.1.wasm -o2 %t.2.wasm +;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY + +(module + (global $g i32 (i32.const 42)) + (table $t 1 1 funcref) + + ;; This elem $e stays in the primary module because table $t is used both in + ;; the primary and secondary modules. So, this elem should be marked as "used" + ;; in the secondary module, and global $g should not be exported / imported. + (elem $e (table $t) (global.get $g) funcref (item (ref.null nofunc))) + + (func $keep + (drop + (table.get $t + (i32.const 0) + ) + ) + ) + (func $split + (drop + (table.get $t + (i32.const 0) + ) + ) + ) +) + +;; PRIMARY: (module +;; PRIMARY-NEXT: (type $0 (func)) +;; PRIMARY-NEXT: (global $g i32 (i32.const 42)) +;; PRIMARY-NEXT: (table $t 1 1 funcref) +;; PRIMARY-NEXT: (elem $e (table $t) (global.get $g) funcref (item (ref.null nofunc))) +;; PRIMARY-NEXT: (export "table" (table $t)) +;; PRIMARY-NEXT: (func $keep +;; PRIMARY-NEXT: (drop +;; PRIMARY-NEXT: (table.get $t +;; PRIMARY-NEXT: (i32.const 0) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) + +;; SECONDARY: (module +;; SECONDARY-NEXT: (type $0 (func)) +;; SECONDARY-NEXT: (import "primary" "table" (table $t 1 1 funcref)) +;; SECONDARY-NEXT: (func $split +;; SECONDARY-NEXT: (drop +;; SECONDARY-NEXT: (table.get $t +;; SECONDARY-NEXT: (i32.const 0) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) diff --git a/test/lit/wasm-split/passive-deps.wast b/test/lit/wasm-split/passive-deps.wast new file mode 100644 index 00000000000..9007cb6081a --- /dev/null +++ b/test/lit/wasm-split/passive-deps.wast @@ -0,0 +1,38 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split +;; RUN: wasm-dis -all %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis -all %t.2.wasm | filecheck %s --check-prefix SECONDARY + +(module + ;; PRIMARY: (type $0 (func)) + + ;; PRIMARY: (global $g funcref (ref.null nofunc)) + (global $g funcref (ref.null nofunc)) + + ;; We should scan this passive element segment's data and correctly mark $g as + ;; used in the primary module. + ;; PRIMARY: (elem $passive-elem funcref (item (global.get $g))) + (elem $passive-elem funcref (item (global.get $g))) + + ;; PRIMARY: (export "global" (global $g)) + + ;; PRIMARY: (func $keep (type $0) + ;; PRIMARY-NEXT: (elem.drop $passive-elem) + ;; PRIMARY-NEXT: ) + (func $keep + (elem.drop $passive-elem) + ) + + ;; SECONDARY: (type $0 (func)) + + ;; SECONDARY: (import "primary" "global" (global $g funcref)) + + ;; SECONDARY: (func $split (type $0) + ;; SECONDARY-NEXT: (drop + ;; SECONDARY-NEXT: (global.get $g) + ;; SECONDARY-NEXT: ) + ;; SECONDARY-NEXT: ) + (func $split + (drop (global.get $g)) + ) +) diff --git a/test/lit/wasm-split/split-module-items.wast b/test/lit/wasm-split/split-module-items.wast index 1c91693987c..3816b50ce9b 100644 --- a/test/lit/wasm-split/split-module-items.wast +++ b/test/lit/wasm-split/split-module-items.wast @@ -9,50 +9,89 @@ ;; the secondary module (module - (import "env" "g" (global $import-global-for-table funcref)) + (rec + (type $struct (descriptor $desc) (struct)) + (type $desc (describes $struct) (struct)) + ) + (global $null-desc (ref null none) (ref.null none)) + (memory $keep-memory 1 1) + ;; This is only used in the secondary module, but segments $keep-data2 can trap + ;; so it is pinned to the primary, so this will be too. + (memory $keep-memory2 1 1) (global $keep-global i32 (i32.const 20)) (table $keep-table 1 1 funcref) + ;; This is only used in the secondary module, but segments $keep-elem2 and + ;; $keep-elem3 can trap so they are pinned to the primary, so this will be too. + (table $keep-table2 1 1 (ref null $struct)) (tag $keep-tag (param i32)) + (elem $keep-elem1 (table $keep-table) (i32.const 0) funcref (item (ref.null nofunc))) + ;; The offset is out-of-bounds and will trap, so keep it in the primary + (elem $keep-elem2 (table $keep-table2) (i32.const 10) (ref null $struct) (item (ref.null none))) + ;; The data can trap, so keep it in the primary + (elem $keep-elem3 (table $keep-table2) (i32.const 0) (ref $struct) (item (struct.new_desc $struct (global.get $null-desc)))) + (data $keep-data1 (memory $keep-memory) (i32.const 0) "a") + ;; The offset is out-of-bounds and will trap, so keep it in the primary + (data $keep-data2 (memory $keep-memory2) (i32.const 65536) "a") (memory $split-memory 1 1) (global $split-global i32 (i32.const 20)) - (table $split-table 1 1 funcref (global.get $import-global-for-table)) + (table $split-table 1 1 funcref) (tag $split-tag (param i32)) + (elem $split-elem (table $split-table) (i32.const 0) funcref (item (ref.null nofunc))) + (data $split-data (memory $split-memory) (i32.const 0) "a") (memory $shared-memory 1 1) (global $shared-global i32 (i32.const 20)) (table $shared-table 1 1 funcref) (tag $shared-tag (param i32)) + (elem $shared-elem (table $shared-table) (i32.const 0) funcref (item (ref.null nofunc))) + (data $shared-data (memory $shared-memory) (i32.const 0) "a") ;; PRIMARY: (global $keep-global i32 (i32.const 20)) ;; PRIMARY-NEXT: (global $shared-global i32 (i32.const 20)) ;; PRIMARY-NEXT: (memory $keep-memory 1 1) + ;; PRIMARY-NEXT: (memory $keep-memory2 1 1) ;; PRIMARY-NEXT: (memory $shared-memory 1 1) + ;; PRIMARY-NEXT: (data $keep-data1 (i32.const 0) "a") + ;; PRIMARY-NEXT: (data $keep-data2 (memory $keep-memory2) (i32.const 65536) "a") + ;; PRIMARY-NEXT: (data $shared-data (memory $shared-memory) (i32.const 0) "a") ;; PRIMARY-NEXT: (table $keep-table 1 1 funcref) + ;; PRIMARY-NEXT: (table $keep-table2 1 1 (ref null $struct)) ;; PRIMARY-NEXT: (table $shared-table 1 1 funcref) - ;; PRIMARY-NEXT: (table $2 1 funcref) - ;; PRIMARY: (tag $keep-tag (type $1) (param i32)) - ;; PRIMARY-NEXT: (tag $shared-tag (type $1) (param i32)) + ;; PRIMARY-NEXT: (table $3 1 funcref) + ;; PRIMARY-NEXT: (elem $keep-elem1 (table $keep-table) (i32.const 0) funcref (item (ref.null nofunc))) + ;; PRIMARY-NEXT: (elem $keep-elem2 (table $keep-table2) (i32.const 10) (ref null $struct) (item (ref.null none))) + ;; PRIMARY-NEXT: (elem $keep-elem3 (table $keep-table2) (i32.const 0) (ref $struct) (item (struct.new_default_desc $struct + ;; PRIMARY-NEXT: (global.get $null-desc) + ;; PRIMARY-NEXT: ))) + ;; PRIMARY-NEXT: (elem $shared-elem (table $shared-table) (i32.const 0) funcref (item (ref.null nofunc))) + ;; PRIMARY: (tag $keep-tag (type $3) (param i32)) + ;; PRIMARY-NEXT: (tag $shared-tag (type $3) (param i32)) - ;; PRIMARY: (export "memory" (memory $shared-memory)) - ;; PRIMARY-NEXT: (export "table" (table $shared-table)) + ;; PRIMARY: (export "memory" (memory $keep-memory2)) + ;; PRIMARY-NEXT: (export "memory_1" (memory $shared-memory)) + ;; PRIMARY-NEXT: (export "table" (table $keep-table2)) + ;; PRIMARY-NEXT: (export "table_3" (table $shared-table)) ;; PRIMARY-NEXT: (export "global" (global $shared-global)) ;; PRIMARY-NEXT: (export "tag" (tag $shared-tag)) ;; PRIMARY-NEXT: (export "keep" (func $keep)) - ;; PRIMARY-NEXT: (export "table_5" (table $2)) + ;; PRIMARY-NEXT: (export "table_7" (table $3)) - ;; SECONDARY: (import "primary" "memory" (memory $shared-memory 1 1)) - ;; SECONDARY-NEXT: (import "primary" "table" (table $shared-table 1 1 funcref)) - ;; SECONDARY-NEXT: (import "primary" "table_5" (table $timport$1 1 funcref)) - ;; SECONDARY-NEXT: (import "env" "g" (global $import-global-for-table funcref)) + ;; SECONDARY: (import "primary" "memory" (memory $keep-memory2 1 1)) + ;; SECONDARY-NEXT: (import "primary" "memory_1" (memory $shared-memory 1 1)) + ;; SECONDARY-NEXT: (import "primary" "table" (table $keep-table2 1 1 (ref null $2))) + ;; SECONDARY-NEXT: (import "primary" "table_3" (table $shared-table 1 1 funcref)) + ;; SECONDARY-NEXT: (import "primary" "table_7" (table $timport$2 1 funcref)) ;; SECONDARY-NEXT: (import "primary" "global" (global $shared-global i32)) ;; SECONDARY-NEXT: (import "primary" "keep" (func $keep (exact (param i32) (result i32)))) ;; SECONDARY-NEXT: (import "primary" "tag" (tag $shared-tag (type $1) (param i32))) ;; SECONDARY: (global $split-global i32 (i32.const 20)) ;; SECONDARY-NEXT: (memory $split-memory 1 1) - ;; SECONDARY-NEXT: (table $split-table 1 1 funcref (global.get $import-global-for-table)) + ;; SECONDARY-NEXT: (data $split-data (memory $split-memory) (i32.const 0) "a") + ;; SECONDARY-NEXT: (table $split-table 1 1 funcref) + ;; SECONDARY-NEXT: (elem $split-elem (table $split-table) (i32.const 0) funcref (item (ref.null nofunc))) ;; SECONDARY: (tag $split-tag (type $1) (param i32)) (func $keep (param i32) (result i32) @@ -110,6 +149,12 @@ (i32.const 24) ) ) + ;; Uses $keep-memory2 + (drop + (i32.load $keep-memory2 + (i32.const 24) + ) + ) ;; Uses $split-table (drop (call_indirect $split-table (param i32) (result i32) @@ -117,6 +162,10 @@ (i32.const 0) ) ) + ;; Uses $keep-table2 + (drop + (table.get $keep-table2 (i32.const 0)) + ) ;; Uses $split-global (drop (global.get $split-global) From 266750c557cc0739d51d0045af2b521d5b5d7e4e Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 16 Jun 2026 23:05:40 +0000 Subject: [PATCH 2/4] Remove offset trap checking --- src/ir/module-splitting.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index e79fcf3d672..8757d80d816 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -821,11 +821,6 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { } auto mayTrap = [&](auto* segment) { - if (segment->offset && - EffectAnalyzer(config.passOptions, primary, segment->offset) - .hasUnremovableSideEffects()) { - return true; - } if constexpr (std::is_same_v) { if (primary.features.hasCustomDescriptors()) { for (auto* item : segment->data) { From 09f81a9a034b868f5dfbc367393dea8c6dfcf712 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 16 Jun 2026 23:08:02 +0000 Subject: [PATCH 3/4] Remove Table::kPageSize --- src/ir/module-splitting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 8757d80d816..2fc56cd86c3 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -844,7 +844,7 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { } else { segmentSize = segment->data.size(); auto* table = primary.getTable(segment->table); - parentSize = table->initial * Table::kPageSize; + parentSize = table->initial; } // Check if this might trap. If it is obviously in bounds then it cannot. From bff8917078007c45ff72341abbaa848eff5ab600 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 16 Jun 2026 23:11:09 +0000 Subject: [PATCH 4/4] AddressType -> uint64_t --- src/ir/module-splitting.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 2fc56cd86c3..7acbf1000d6 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -850,14 +850,13 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { // Check if this might trap. If it is obviously in bounds then it cannot. auto* c = segment->offset->template dynCast(); // Check for overflow in the largest possible space of addresses. - using AddressType = Address::address64_t; - AddressType maxWritten; + uint64_t maxWritten; // If there is no integer, or if there is and the addition overflows, or // if the addition leads to a too-large value, then we may trap. if (!c || std::ckd_add(&maxWritten, - (AddressType)segmentSize, - (AddressType)c->value.getInteger()) || + (uint64_t)segmentSize, + (uint64_t)c->value.getInteger()) || maxWritten > parentSize) { return true; }