3434#include " ir/utils.h"
3535#include " pass.h"
3636#include " support/space.h"
37+ #include " support/stdckdint.h"
3738#include " wasm-binary.h"
3839#include " wasm-builder.h"
3940#include " wasm.h"
@@ -46,6 +47,7 @@ namespace {
4647// will be used instead of memory.init for this range.
4748struct Range {
4849 bool isZero;
50+ // The range [start, end) - that is, start is included while end is not.
4951 size_t start;
5052 size_t end;
5153};
@@ -109,7 +111,8 @@ struct MemoryPacking : public Pass {
109111 ReferrersMap& referrers);
110112 bool canSplit (const std::unique_ptr<DataSegment>& segment,
111113 const Referrers& referrers);
112- void calculateRanges (const std::unique_ptr<DataSegment>& segment,
114+ void calculateRanges (Module* module ,
115+ const std::unique_ptr<DataSegment>& segment,
113116 const Referrers& referrers,
114117 std::vector<Range>& ranges);
115118 void createSplitSegments (Builder& builder,
@@ -162,7 +165,7 @@ void MemoryPacking::run(Module* module) {
162165 std::vector<Range> ranges;
163166
164167 if (canSplit (segment, currReferrers)) {
165- calculateRanges (segment, currReferrers, ranges);
168+ calculateRanges (module , segment, currReferrers, ranges);
166169 } else {
167170 // A single range covers the entire segment. Set isZero to false so the
168171 // original memory.init will be used even if segment is all zeroes.
@@ -295,14 +298,33 @@ bool MemoryPacking::canSplit(const std::unique_ptr<DataSegment>& segment,
295298 return segment->isPassive || segment->offset ->is <Const>();
296299}
297300
298- void MemoryPacking::calculateRanges (const std::unique_ptr<DataSegment>& segment,
301+ void MemoryPacking::calculateRanges (Module* module ,
302+ const std::unique_ptr<DataSegment>& segment,
299303 const Referrers& referrers,
300304 std::vector<Range>& ranges) {
301305 auto & data = segment->data ;
302306 if (data.size () == 0 ) {
303307 return ;
304308 }
305309
310+ // A segment that might trap during startup must be handled carefully, as we
311+ // do not want to remove that trap (unless we are allowed to by TNH).
312+ auto preserveTrap = !getPassOptions ().trapsNeverHappen && segment->offset ;
313+ if (preserveTrap) {
314+ // Check if we can rule out a trap by it being in bounds.
315+ if (auto * c = segment->offset ->dynCast <Const>()) {
316+ auto * memory = module ->getMemory (segment->memory );
317+ auto memorySize = memory->initial * Memory::kPageSize ;
318+ Index start = c->value .getUnsigned ();
319+ Index size = segment->data .size ();
320+ Index end;
321+ if (!std::ckd_add (&end, start, size) && end <= memorySize) {
322+ // This is in bounds.
323+ preserveTrap = false ;
324+ }
325+ }
326+ }
327+
306328 // Calculate initial zero and nonzero ranges
307329 size_t start = 0 ;
308330 while (start < data.size ()) {
@@ -346,7 +368,10 @@ void MemoryPacking::calculateRanges(const std::unique_ptr<DataSegment>& segment,
346368 }
347369 }
348370
349- // Merge edge zeroes if they are not worth splitting
371+ // Merge edge zeroes if they are not worth splitting. Note that we must not
372+ // have a trap we must preserve here (if we did, we'd need to handle that in
373+ // the code below).
374+ assert (!preserveTrap);
350375 if (ranges.size () >= 2 ) {
351376 auto last = ranges.end () - 1 ;
352377 auto penultimate = ranges.end () - 2 ;
@@ -364,12 +389,12 @@ void MemoryPacking::calculateRanges(const std::unique_ptr<DataSegment>& segment,
364389 }
365390 }
366391 } else {
367- // Legacy ballpark overhead of active segment with offset
392+ // Legacy ballpark overhead of active segment with offset.
368393 // TODO: Tune this
369394 threshold = 8 ;
370395 }
371396
372- // Merge ranges across small spans of zeroes
397+ // Merge ranges across small spans of zeroes.
373398 std::vector<Range> mergedRanges = {ranges.front ()};
374399 size_t i;
375400 for (i = 1 ; i < ranges.size () - 1 ; ++i) {
@@ -383,10 +408,28 @@ void MemoryPacking::calculateRanges(const std::unique_ptr<DataSegment>& segment,
383408 mergedRanges.push_back (*curr);
384409 }
385410 }
386- // Add the final range if it hasn't already been merged in
411+ // Add the final range if it hasn't already been merged in.
387412 if (i < ranges.size ()) {
388413 mergedRanges.push_back (ranges.back ());
389414 }
415+ // If we need to preserve a trap then we must keep the topmost byte of the
416+ // segment, which is enough to cause a trap if we do in fact trap.
417+ if (preserveTrap) {
418+ // Check if the last byte is in a zero range. Such a range will be dropped
419+ // later, so add a non-zero range with that byte. (It is slightly odd to
420+ // add a range with a zero marked as non-zero, but that is how we ensure it
421+ // is preserved later in the output.)
422+ auto & back = mergedRanges.back ();
423+ if (back.isZero ) {
424+ // Remove the last byte from |back|. Decrementing this prevents it from
425+ // overlapping with the new segment we are about to add. Note that this
426+ // might make |back| have size 0, but that is not a problem as it will be
427+ // dropped later anyhow, since it contains zeroes.
428+ back.end --;
429+ auto lastByte = data.size () - 1 ;
430+ mergedRanges.push_back ({false , lastByte, lastByte + 1 });
431+ }
432+ }
390433 std::swap (ranges, mergedRanges);
391434}
392435
@@ -551,6 +594,20 @@ void MemoryPacking::dropUnusedSegments(
551594 module ->updateDataSegmentsMap ();
552595}
553596
597+ // Given the start of a segment and an offset into it, compute the sum of the
598+ // two to get the absolute address the data should be at. This takes into
599+ // account overflows in that we saturate to UINT_MAX: we do not want an overflow
600+ // to take us down into a small address; in the invalid case of an overflow we
601+ // stay at the largest possible unsigned value, which will keep us trapping.
602+ template <typename T>
603+ Expression* addStartAndOffset (T start, T offset, Builder& builder) {
604+ T total;
605+ if (std::ckd_add (&total, start, offset)) {
606+ total = std::numeric_limits<T>::max ();
607+ }
608+ return builder.makeConst (T (total));
609+ }
610+
554611void MemoryPacking::createSplitSegments (
555612 Builder& builder,
556613 const DataSegment* segment,
@@ -568,10 +625,12 @@ void MemoryPacking::createSplitSegments(
568625 if (!segment->isPassive ) {
569626 if (auto * c = segment->offset ->dynCast <Const>()) {
570627 if (c->value .type == Type::i32 ) {
571- offset = builder.makeConst (int32_t (c->value .geti32 () + range.start ));
628+ offset = addStartAndOffset<uint32_t >(
629+ range.start , c->value .geti32 (), builder);
572630 } else {
573631 assert (c->value .type == Type::i64 );
574- offset = builder.makeConst (int64_t (c->value .geti64 () + range.start ));
632+ offset = addStartAndOffset<uint64_t >(
633+ range.start , c->value .geti64 (), builder);
575634 }
576635 } else {
577636 assert (ranges.size () == 1 );
0 commit comments