/src/WasmEdge/lib/executor/engine/memoryInstr.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "executor/executor.h" |
5 | | |
6 | | #include <cstring> |
7 | | |
8 | | namespace WasmEdge { |
9 | | namespace Executor { |
10 | | |
11 | | Expect<void> |
12 | | Executor::runMemorySizeOp(Runtime::StackManager &StackMgr, |
13 | 0 | Runtime::Instance::MemoryInstance &MemInst) { |
14 | | // Push SZ = page size to the stack. |
15 | 0 | const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType(); |
16 | 0 | StackMgr.push(emplaceAddr(MemInst.getPageSize(), AddrType)); |
17 | 0 | return {}; |
18 | 0 | } |
19 | | |
20 | | Expect<void> |
21 | | Executor::runMemoryGrowOp(Runtime::StackManager &StackMgr, |
22 | 0 | Runtime::Instance::MemoryInstance &MemInst) { |
23 | | // Pop N, the number of pages to grow. |
24 | 0 | const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType(); |
25 | 0 | uint64_t N = extractAddr(StackMgr.pop(), AddrType); |
26 | | |
27 | | // Grow the page and push the result. |
28 | 0 | const uint64_t CurrPageSize = MemInst.getPageSize(); |
29 | 0 | if (MemInst.growPage(N)) { |
30 | 0 | StackMgr.push(emplaceAddr(CurrPageSize, AddrType)); |
31 | 0 | } else { |
32 | 0 | StackMgr.push(emplaceAddr(static_cast<uint64_t>(-1), AddrType)); |
33 | 0 | } |
34 | 0 | return {}; |
35 | 0 | } |
36 | | |
37 | | Expect<void> Executor::runMemoryInitOp( |
38 | | Runtime::StackManager &StackMgr, Runtime::Instance::MemoryInstance &MemInst, |
39 | 0 | Runtime::Instance::DataInstance &DataInst, const AST::Instruction &Instr) { |
40 | | // Pop the length, source, and destination from the stack. |
41 | | // Currently, the length and source offset from the data instance are |
42 | | // 32-bit. |
43 | 0 | uint64_t Len = static_cast<uint64_t>(StackMgr.pop().get<uint32_t>()); |
44 | 0 | uint64_t Src = static_cast<uint64_t>(StackMgr.pop().get<uint32_t>()); |
45 | 0 | const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType(); |
46 | 0 | uint64_t Dst = extractAddr(StackMgr.pop(), AddrType); |
47 | | |
48 | | // Replace mem[Dst : Dst + Len] with data[Src : Src + Len]. |
49 | 0 | return MemInst.setBytes(DataInst.getData(), Dst, Src, Len) |
50 | 0 | .map_error([&Instr](auto E) { |
51 | 0 | spdlog::error( |
52 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
53 | 0 | return E; |
54 | 0 | }); |
55 | 0 | } |
56 | | |
57 | | Expect<void> |
58 | 0 | Executor::runDataDropOp(Runtime::Instance::DataInstance &DataInst) { |
59 | | // Clear data instance. |
60 | 0 | DataInst.clear(); |
61 | 0 | return {}; |
62 | 0 | } |
63 | | |
64 | | Expect<void> |
65 | | Executor::runMemoryCopyOp(Runtime::StackManager &StackMgr, |
66 | | Runtime::Instance::MemoryInstance &MemInstDst, |
67 | | Runtime::Instance::MemoryInstance &MemInstSrc, |
68 | 0 | const AST::Instruction &Instr) { |
69 | | // Pop the length, source, and destination from the stack. |
70 | 0 | const auto AddrType1 = MemInstSrc.getMemoryType().getLimit().getAddrType(); |
71 | 0 | const auto AddrType2 = MemInstDst.getMemoryType().getLimit().getAddrType(); |
72 | 0 | uint64_t Len = extractAddr(StackMgr.pop(), std::min(AddrType1, AddrType2)); |
73 | 0 | uint64_t Src = extractAddr(StackMgr.pop(), AddrType2); |
74 | 0 | uint64_t Dst = extractAddr(StackMgr.pop(), AddrType1); |
75 | | |
76 | | // Replace mem[Dst : Dst + Len] with mem[Src : Src + Len]. |
77 | | // When source and destination are the same memory instance, overlapping |
78 | | // regions require memmove semantics per the Wasm spec. |
79 | 0 | if (&MemInstSrc == &MemInstDst) { |
80 | | // Same memory: validate bounds, then use memmove for overlap safety. |
81 | 0 | EXPECTED_TRY(MemInstSrc.getBytes(Src, Len).map_error([&Instr](auto E) { |
82 | 0 | spdlog::error( |
83 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
84 | 0 | return E; |
85 | 0 | })); |
86 | 0 | EXPECTED_TRY(MemInstDst.getBytes(Dst, Len).map_error([&Instr](auto E) { |
87 | 0 | spdlog::error( |
88 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
89 | 0 | return E; |
90 | 0 | })); |
91 | 0 | if (likely(Len > 0)) { |
92 | 0 | std::memmove(MemInstDst.getDataPtr() + Dst, MemInstSrc.getDataPtr() + Src, |
93 | 0 | Len); |
94 | 0 | } |
95 | 0 | return {}; |
96 | 0 | } else { |
97 | | // Different memories: no overlap possible, use the existing path. |
98 | 0 | EXPECTED_TRY(auto Data, |
99 | 0 | MemInstSrc.getBytes(Src, Len).map_error([&Instr](auto E) { |
100 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), |
101 | 0 | Instr.getOffset())); |
102 | 0 | return E; |
103 | 0 | })); |
104 | 0 | return MemInstDst.setBytes(Data, Dst, 0, Len).map_error([&Instr](auto E) { |
105 | 0 | spdlog::error( |
106 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
107 | 0 | return E; |
108 | 0 | }); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | Expect<void> |
113 | | Executor::runMemoryFillOp(Runtime::StackManager &StackMgr, |
114 | | Runtime::Instance::MemoryInstance &MemInst, |
115 | 0 | const AST::Instruction &Instr) { |
116 | | // Pop the length, value, and offset from the stack. |
117 | 0 | const auto AddrType = MemInst.getMemoryType().getLimit().getAddrType(); |
118 | 0 | uint64_t Len = extractAddr(StackMgr.pop(), AddrType); |
119 | 0 | uint8_t Val = static_cast<uint8_t>(StackMgr.pop().get<uint32_t>()); |
120 | 0 | uint64_t Off = extractAddr(StackMgr.pop(), AddrType); |
121 | | |
122 | | // Fill data with Val. |
123 | 0 | return MemInst.fillBytes(Val, Off, Len).map_error([&Instr](auto E) { |
124 | 0 | spdlog::error( |
125 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
126 | 0 | return E; |
127 | 0 | }); |
128 | 0 | } |
129 | | |
130 | | } // namespace Executor |
131 | | } // namespace WasmEdge |