Skip to content

Commit 2e5ec2e

Browse files
HerrCai0907atc-github
authored andcommitted
feat(ImmutableLoadEliminating): implement ImmutableLoadEliminating optimization (#124)
Some of the data in the data segment generated by AS is immutable, but binaryen assumes it is mutable during optimization. As a result, some optimization passes are missed. This PR passes the immutable data segment range from AS front-end to binaryen through an additional object in AsModule, and uses an optimization pass to simplify the load instruction to const instruction.
1 parent 07df963 commit 2e5ec2e

File tree

14 files changed

+491
-24
lines changed

14 files changed

+491
-24
lines changed

assemblyscript/src/compiler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ import {
219219
liftRequiresExportRuntime,
220220
lowerRequiresExportRuntime
221221
} from "./bindings/js";
222+
import { markDataElementImmutable } from "./warpo";
222223

223224
/** Features enabled by default. */
224225
export const defaultFeatures = Feature.MutableGlobals
@@ -2073,6 +2074,7 @@ export class Compiler extends DiagnosticEmitter {
20732074
assert(rtInstance.writeField("_index", index, buf));
20742075
assert(rtInstance.writeField("_env", 0, buf));
20752076
instance.memorySegment = memorySegment = this.addRuntimeMemorySegment(buf);
2077+
markDataElementImmutable(i64_add(memorySegment.offset, i64_new(program.totalOverhead)), rtInstance.nextMemoryOffset);
20762078
}
20772079
return i64_add(memorySegment.offset, i64_new(program.totalOverhead));
20782080
}

assemblyscript/src/warpo.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
declare function _WarpoMarkDataElementImmutable(begin: u32, size: u32): void;
2+
3+
export function markDataElementImmutable(begin: i64, size: i32): void {
4+
_WarpoMarkDataElementImmutable(i64_low(begin), <u32>size);
5+
}

common/AsModule.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (C) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#include "warpo/common/AsModule.hpp"
18+
19+
namespace warpo {
20+
21+
bool ImmutableDataElementRanges::contains(uint32_t offset, uint32_t size) const {
22+
for (DataElementRange const &range : *this) {
23+
if (offset >= range.begin_ && (offset + size) <= range.end_)
24+
return true;
25+
}
26+
return false;
27+
}
28+
29+
} // namespace warpo

frontend/CompilerImpl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class FrontendCompiler {
3030
std::map<std::string, std::filesystem::path> packageRootMap_{};
3131
size_t errorCount_ = 0;
3232
std::string errorMessage_;
33-
AsModule asModule_;
3433

3534
int32_t allocString(std::string_view str);
3635
std::u16string utf8ToUtf16(std::string const &utf8Str);
@@ -67,6 +66,7 @@ class FrontendCompiler {
6766
CompilationResult compile(std::vector<std::string> const &entryFilePaths, Config const &config);
6867

6968
std::set<void *> allocedPtrs_;
69+
AsModule asModule_;
7070
};
7171

7272
} // namespace warpo::frontend

frontend/LinkedAPI.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ std::vector<vb::NativeSymbol> createLinkedAPI() {
1616
append(linkedAPI, createAssemblyscriptAPI());
1717
append(linkedAPI, createCppWrapperAPI());
1818
append(linkedAPI, createBinaryenLinkedAPI());
19+
append(linkedAPI, createOptAPI());
1920
return linkedAPI;
2021
}
2122

frontend/LinkedAPI.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
namespace warpo::frontend {
88

99
std::vector<vb::NativeSymbol> createAssemblyscriptAPI();
10-
std::vector<vb::NativeSymbol> createCppWrapperAPI();
1110
std::vector<vb::NativeSymbol> createBinaryenLinkedAPI();
11+
std::vector<vb::NativeSymbol> createCppWrapperAPI();
12+
std::vector<vb::NativeSymbol> createOptAPI();
1213

1314
std::vector<vb::NativeSymbol> const &getLinkedAPI();
1415

frontend/LinkedAPIAssemblyscript.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <bit>
12
#include <cassert>
23
#include <cstddef>
34
#include <cstdint>
@@ -47,7 +48,7 @@ void traceForLink(uint32_t ptr, uint32_t n, double d1, double d2, double d3, dou
4748
vb::WasmModule *ctx) {
4849
std::stringstream ss{};
4950
ss << getAsString(ptr, ctx);
50-
for (size_t i = 0; i < n; i++) {
51+
for (size_t i = 1U; i <= n; i++) {
5152
switch (i) {
5253
case 1:
5354
ss << " " << d1;

frontend/LinkedAPIOpt.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <cassert>
2+
#include <iostream>
3+
4+
#include "CompilerImpl.hpp"
5+
#include "LinkedAPI.hpp"
6+
#include "warpo/common/AsModule.hpp"
7+
8+
#include "src/core/common/function_traits.hpp"
9+
10+
namespace warpo::frontend {
11+
12+
namespace {
13+
14+
FrontendCompiler *getFrontendCompiler(vb::WasmModule *ctx) {
15+
return static_cast<FrontendCompiler *>(ctx->getContext());
16+
}
17+
18+
void markDataElementImmutableForLink(uint32_t offset, uint32_t size, [[maybe_unused]] vb::WasmModule *ctx) {
19+
AsModule *const m = &getFrontendCompiler(ctx)->asModule_;
20+
if (m->immutableRanges_ == nullptr)
21+
m->immutableRanges_ = std::make_shared<ImmutableDataElementRanges>();
22+
m->immutableRanges_->insert({offset, offset + size});
23+
}
24+
25+
} // namespace
26+
27+
std::vector<vb::NativeSymbol> createOptAPI() {
28+
return std::vector<vb::NativeSymbol>{
29+
STATIC_LINK("warpo", "_WarpoMarkDataElementImmutable", markDataElementImmutableForLink),
30+
};
31+
}
32+
33+
} // namespace warpo::frontend

include/warpo/common/AsModule.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <binaryen/src/binaryen-c.h>
2020
#include <compare>
2121
#include <cstddef>
22+
#include <memory>
2223
#include <set>
2324
#include <utility>
2425

@@ -44,7 +45,7 @@ class BinaryenModule {
4445
}
4546

4647
BinaryenModule() noexcept : BinaryenModule(nullptr) {}
47-
BinaryenModule(BinaryenModuleRef ref) noexcept : ref_(ref) {}
48+
explicit BinaryenModule(BinaryenModuleRef ref) noexcept : ref_(ref) {}
4849
~BinaryenModule() noexcept {
4950
if (ref_ != nullptr)
5051
BinaryenModuleDispose(ref_);
@@ -74,14 +75,21 @@ class DataElementRange {
7475
}
7576
};
7677

78+
struct ImmutableDataElementRanges : private std::set<DataElementRange> {
79+
using std::set<DataElementRange>::insert;
80+
bool contains(uint32_t offset, uint32_t size) const;
81+
};
82+
7783
class AsModule {
7884
BinaryenModule raw_;
7985

8086
public:
81-
std::set<DataElementRange> immutableRanges_;
87+
std::shared_ptr<ImmutableDataElementRanges> immutableRanges_;
8288
// TODO: more information fields
8389

8490
AsModule() = default;
91+
explicit AsModule(BinaryenModuleRef ref) : raw_{ref}, immutableRanges_{nullptr} {}
92+
8593
void set(BinaryenModule raw) { raw_ = std::move(raw); }
8694
BinaryenModuleRef get() const { return raw_.get(); }
8795
bool valid() const { return raw_ != nullptr; }

include/warpo/passes/Runner.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <string>
66
#include <vector>
77

8+
#include "warpo/common/AsModule.hpp"
9+
810
namespace warpo::passes {
911

1012
struct Output {
@@ -20,9 +22,9 @@ struct Config {
2022
void init();
2123

2224
Output runOnWat(std::string const &input, Config const &config);
23-
Output runOnModule(BinaryenModuleRef const m, Config const &config);
25+
Output runOnModule(AsModule const &m, Config const &config);
2426

25-
void runAndEmit(BinaryenModuleRef const m, std::string const &outputPath);
27+
void runAndEmit(AsModule const &m, std::string const &outputPath);
2628
void runAndEmit(std::string const &inputPath, std::string const &outputPath);
2729

2830
} // namespace warpo::passes

0 commit comments

Comments
 (0)