binaryen-sys 0.12.0

Bindings to the binaryen library
Documentation
/*
 * Copyright 2017 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef wasm_ir_memory_h
#define wasm_ir_memory_h

#include <algorithm>
#include <vector>

#include "literal.h"
#include "wasm-binary.h"
#include "wasm-builder.h"
#include "wasm.h"

namespace wasm {

namespace MemoryUtils {
// Flattens memory into a single data segment, or no segment. If there is
// a segment, it starts at 0.
// If ensuredSegmentSize is provided, then a segment is always emitted,
// and of at least that size.
// Returns true if successful (e.g. relocatable segments cannot be flattened).
inline bool flatten(Memory& memory,
                    Index ensuredSegmentSize = 0,
                    Module* module = nullptr) {
  if (memory.segments.size() == 0) {
    if (ensuredSegmentSize > 0) {
      assert(module); // must provide a module if ensuring a size.
      Builder builder(*module);
      memory.segments.emplace_back(builder.makeConst(int32_t(0)));
      memory.segments[0].data.resize(ensuredSegmentSize);
    }
    return true;
  }
  std::vector<char> data;
  data.resize(ensuredSegmentSize);
  for (auto& segment : memory.segments) {
    if (segment.isPassive) {
      return false;
    }
    auto* offset = segment.offset->dynCast<Const>();
    if (!offset) {
      return false;
    }
  }
  for (auto& segment : memory.segments) {
    auto* offset = segment.offset->dynCast<Const>();
    Index start = offset->value.getInteger();
    Index end = start + segment.data.size();
    if (end > data.size()) {
      data.resize(end);
    }
    std::copy(segment.data.begin(), segment.data.end(), data.begin() + start);
  }
  memory.segments.resize(1);
  memory.segments[0].offset->cast<Const>()->value = Literal(int32_t(0));
  memory.segments[0].data.swap(data);
  return true;
}

// Ensures that the memory exists (of minimal size).
inline void ensureExists(Memory& memory) {
  if (!memory.exists) {
    memory.exists = true;
    memory.initial = memory.max = 1;
  }
}

// Try to merge segments until they fit into web limitations.
// Return true if successful.
inline bool ensureLimitedSegments(Module& module) {
  Memory& memory = module.memory;
  if (memory.segments.size() <= WebLimitations::MaxDataSegments) {
    return true;
  }

  // Conservatively refuse to change segments if there might be memory.init
  // and data.drop instructions.
  if (module.features.hasBulkMemory()) {
    return false;
  }

  auto isEmpty = [](Memory::Segment& segment) {
    return segment.data.size() == 0;
  };

  auto isConstantOffset = [](Memory::Segment& segment) {
    return segment.offset && segment.offset->is<Const>();
  };

  Index numConstant = 0, numDynamic = 0;
  bool hasPassiveSegments = false;
  for (auto& segment : memory.segments) {
    if (!isEmpty(segment)) {
      if (isConstantOffset(segment)) {
        numConstant++;
      } else {
        numDynamic++;
      }
    }
    hasPassiveSegments |= segment.isPassive;
  }

  if (hasPassiveSegments) {
    return false;
  }

  // check if we have too many dynamic data segments, which we can do nothing
  // about
  if (numDynamic + 1 >= WebLimitations::MaxDataSegments) {
    return false;
  }

  // we'll merge constant segments if we must
  if (numConstant + numDynamic >= WebLimitations::MaxDataSegments) {
    numConstant = WebLimitations::MaxDataSegments - numDynamic - 1;
    auto num = numConstant + numDynamic;
    WASM_UNUSED(num);
    assert(num == WebLimitations::MaxDataSegments - 1);
  }

  std::vector<Memory::Segment> mergedSegments;
  mergedSegments.reserve(WebLimitations::MaxDataSegments);

  // drop empty segments and pass through dynamic-offset segments
  for (auto& segment : memory.segments) {
    if (isEmpty(segment)) {
      continue;
    }
    if (isConstantOffset(segment)) {
      continue;
    }
    mergedSegments.push_back(segment);
  }

  // from here on, we concern ourselves with non-empty constant-offset
  // segments, the ones which we may need to merge
  auto isRelevant = [&](Memory::Segment& segment) {
    return !isEmpty(segment) && isConstantOffset(segment);
  };
  for (Index i = 0; i < memory.segments.size(); i++) {
    auto& segment = memory.segments[i];
    if (!isRelevant(segment)) {
      continue;
    }
    if (mergedSegments.size() + 2 < WebLimitations::MaxDataSegments) {
      mergedSegments.push_back(segment);
      continue;
    }
    // we can emit only one more segment! merge everything into one
    // start the combined segment at the bottom of them all
    auto start = segment.offset->cast<Const>()->value.getInteger();
    for (Index j = i + 1; j < memory.segments.size(); j++) {
      auto& segment = memory.segments[j];
      if (!isRelevant(segment)) {
        continue;
      }
      auto offset = segment.offset->cast<Const>()->value.getInteger();
      start = std::min(start, offset);
    }
    // create the segment and add in all the data
    auto* c = module.allocator.alloc<Const>();
    c->value = Literal(int32_t(start));
    c->type = Type::i32;

    Memory::Segment combined(c);
    for (Index j = i; j < memory.segments.size(); j++) {
      auto& segment = memory.segments[j];
      if (!isRelevant(segment)) {
        continue;
      }
      auto offset = segment.offset->cast<Const>()->value.getInteger();
      auto needed = offset + segment.data.size() - start;
      if (combined.data.size() < needed) {
        combined.data.resize(needed);
      }
      std::copy(segment.data.begin(),
                segment.data.end(),
                combined.data.begin() + (offset - start));
    }
    mergedSegments.push_back(combined);
    break;
  }

  memory.segments.swap(mergedSegments);
  return true;
}
} // namespace MemoryUtils

} // namespace wasm

#endif // wasm_ir_memory_h