Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/llvm/compiler/memoryInstr.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "compiler/function_compiler.h"
5
6
#include <limits>
7
8
namespace WasmEdge {
9
10
Expect<void>
11
642k
FunctionCompiler::compileMemoryOp(const AST::Instruction &Instr) noexcept {
12
642k
  switch (Instr.getOpCode()) {
13
1.20k
  case OpCode::I32__load:
14
1.20k
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
15
1.20k
                  Instr.getMemoryAlign(), Context.Int32Ty);
16
1.20k
    break;
17
2.70k
  case OpCode::I64__load:
18
2.70k
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
19
2.70k
                  Instr.getMemoryAlign(), Context.Int64Ty);
20
2.70k
    break;
21
107
  case OpCode::F32__load:
22
107
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
23
107
                  Instr.getMemoryAlign(), Context.FloatTy);
24
107
    break;
25
225
  case OpCode::F64__load:
26
225
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
27
225
                  Instr.getMemoryAlign(), Context.DoubleTy);
28
225
    break;
29
674
  case OpCode::I32__load8_s:
30
674
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
31
674
                  Instr.getMemoryAlign(), Context.Int8Ty, Context.Int32Ty,
32
674
                  true);
33
674
    break;
34
438
  case OpCode::I32__load8_u:
35
438
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
36
438
                  Instr.getMemoryAlign(), Context.Int8Ty, Context.Int32Ty,
37
438
                  false);
38
438
    break;
39
325
  case OpCode::I32__load16_s:
40
325
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
41
325
                  Instr.getMemoryAlign(), Context.Int16Ty, Context.Int32Ty,
42
325
                  true);
43
325
    break;
44
1.56k
  case OpCode::I32__load16_u:
45
1.56k
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
46
1.56k
                  Instr.getMemoryAlign(), Context.Int16Ty, Context.Int32Ty,
47
1.56k
                  false);
48
1.56k
    break;
49
750
  case OpCode::I64__load8_s:
50
750
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
51
750
                  Instr.getMemoryAlign(), Context.Int8Ty, Context.Int64Ty,
52
750
                  true);
53
750
    break;
54
451
  case OpCode::I64__load8_u:
55
451
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
56
451
                  Instr.getMemoryAlign(), Context.Int8Ty, Context.Int64Ty,
57
451
                  false);
58
451
    break;
59
375
  case OpCode::I64__load16_s:
60
375
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
61
375
                  Instr.getMemoryAlign(), Context.Int16Ty, Context.Int64Ty,
62
375
                  true);
63
375
    break;
64
591
  case OpCode::I64__load16_u:
65
591
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
66
591
                  Instr.getMemoryAlign(), Context.Int16Ty, Context.Int64Ty,
67
591
                  false);
68
591
    break;
69
404
  case OpCode::I64__load32_s:
70
404
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
71
404
                  Instr.getMemoryAlign(), Context.Int32Ty, Context.Int64Ty,
72
404
                  true);
73
404
    break;
74
500
  case OpCode::I64__load32_u:
75
500
    compileLoadOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
76
500
                  Instr.getMemoryAlign(), Context.Int32Ty, Context.Int64Ty,
77
500
                  false);
78
500
    break;
79
396
  case OpCode::I32__store:
80
396
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
81
396
                   Instr.getMemoryAlign(), Context.Int32Ty);
82
396
    break;
83
1.34k
  case OpCode::I64__store:
84
1.34k
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
85
1.34k
                   Instr.getMemoryAlign(), Context.Int64Ty);
86
1.34k
    break;
87
64
  case OpCode::F32__store:
88
64
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
89
64
                   Instr.getMemoryAlign(), Context.FloatTy);
90
64
    break;
91
43
  case OpCode::F64__store:
92
43
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
93
43
                   Instr.getMemoryAlign(), Context.DoubleTy);
94
43
    break;
95
269
  case OpCode::I32__store8:
96
285
  case OpCode::I64__store8:
97
285
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
98
285
                   Instr.getMemoryAlign(), Context.Int8Ty, true);
99
285
    break;
100
231
  case OpCode::I32__store16:
101
351
  case OpCode::I64__store16:
102
351
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
103
351
                   Instr.getMemoryAlign(), Context.Int16Ty, true);
104
351
    break;
105
27
  case OpCode::I64__store32:
106
27
    compileStoreOp(Instr.getTargetIndex(), Instr.getMemoryOffset(),
107
27
                   Instr.getMemoryAlign(), Context.Int32Ty, true);
108
27
    break;
109
697
  case OpCode::Memory__size:
110
697
    stackPush(Builder.createTrunc(
111
697
        Context.getMemorySize(Builder, ModCtx, Instr.getTargetIndex()),
112
697
        Context.MemoryAddrTypes[Instr.getTargetIndex()]));
113
697
    break;
114
868
  case OpCode::Memory__grow: {
115
868
    auto NewPageSize = Builder.createZExt(stackPop(), Context.Int64Ty);
116
868
    stackPush(Builder.createTrunc(
117
868
        Builder.createCall(
118
868
            Context.getIntrinsic(
119
868
                Builder, Executable::Intrinsics::kMemGrow,
120
868
                LLVM::Type::getFunctionType(
121
868
                    Context.Int64Ty,
122
868
                    {Context.Int8PtrTy, Context.Int32Ty, Context.Int64Ty},
123
868
                    false)),
124
868
            {Context.getModuleInst(Builder, ModCtx),
125
868
             LLContext.getInt32(Instr.getTargetIndex()), NewPageSize}),
126
868
        Context.MemoryAddrTypes[Instr.getTargetIndex()]));
127
868
    break;
128
231
  }
129
23
  case OpCode::Memory__init: {
130
23
    auto Len = stackPop();
131
23
    auto Src = stackPop();
132
23
    auto Dst = Builder.createZExt(stackPop(), Context.Int64Ty);
133
23
    Builder.createCall(
134
23
        Context.getIntrinsic(
135
23
            Builder, Executable::Intrinsics::kMemInit,
136
23
            LLVM::Type::getFunctionType(Context.VoidTy,
137
23
                                        {Context.Int8PtrTy, Context.Int32Ty,
138
23
                                         Context.Int32Ty, Context.Int64Ty,
139
23
                                         Context.Int32Ty, Context.Int32Ty},
140
23
                                        false)),
141
23
        {Context.getModuleInst(Builder, ModCtx),
142
23
         LLContext.getInt32(Instr.getTargetIndex()),
143
23
         LLContext.getInt32(Instr.getSourceIndex()), Dst, Src, Len});
144
23
    break;
145
231
  }
146
22
  case OpCode::Data__drop: {
147
22
    Builder.createCall(
148
22
        Context.getIntrinsic(
149
22
            Builder, Executable::Intrinsics::kDataDrop,
150
22
            LLVM::Type::getFunctionType(
151
22
                Context.VoidTy, {Context.Int8PtrTy, Context.Int32Ty}, false)),
152
22
        {Context.getModuleInst(Builder, ModCtx),
153
22
         LLContext.getInt32(Instr.getTargetIndex())});
154
22
    break;
155
231
  }
156
249
  case OpCode::Memory__copy: {
157
249
    auto Len = Builder.createZExt(stackPop(), Context.Int64Ty);
158
249
    auto Src = Builder.createZExt(stackPop(), Context.Int64Ty);
159
249
    auto Dst = Builder.createZExt(stackPop(), Context.Int64Ty);
160
249
    Builder.createCall(
161
249
        Context.getIntrinsic(
162
249
            Builder, Executable::Intrinsics::kMemCopy,
163
249
            LLVM::Type::getFunctionType(Context.VoidTy,
164
249
                                        {Context.Int8PtrTy, Context.Int32Ty,
165
249
                                         Context.Int32Ty, Context.Int64Ty,
166
249
                                         Context.Int64Ty, Context.Int64Ty},
167
249
                                        false)),
168
249
        {Context.getModuleInst(Builder, ModCtx),
169
249
         LLContext.getInt32(Instr.getTargetIndex()),
170
249
         LLContext.getInt32(Instr.getSourceIndex()), Dst, Src, Len});
171
249
    break;
172
231
  }
173
634
  case OpCode::Memory__fill: {
174
634
    auto Len = Builder.createZExt(stackPop(), Context.Int64Ty);
175
634
    auto Val = Builder.createTrunc(stackPop(), Context.Int8Ty);
176
634
    auto Off = Builder.createZExt(stackPop(), Context.Int64Ty);
177
634
    Builder.createCall(
178
634
        Context.getIntrinsic(
179
634
            Builder, Executable::Intrinsics::kMemFill,
180
634
            LLVM::Type::getFunctionType(Context.VoidTy,
181
634
                                        {Context.Int8PtrTy, Context.Int32Ty,
182
634
                                         Context.Int64Ty, Context.Int8Ty,
183
634
                                         Context.Int64Ty},
184
634
                                        false)),
185
634
        {Context.getModuleInst(Builder, ModCtx),
186
634
         LLContext.getInt32(Instr.getTargetIndex()), Off, Val, Len});
187
634
    break;
188
231
  }
189
190
  // Const Numeric Instructions
191
535k
  case OpCode::I32__const:
192
535k
    stackPush(LLContext.getInt32(Instr.getNum().get<uint32_t>()));
193
535k
    break;
194
70.4k
  case OpCode::I64__const:
195
70.4k
    stackPush(LLContext.getInt64(Instr.getNum().get<uint64_t>()));
196
70.4k
    break;
197
14.3k
  case OpCode::F32__const:
198
14.3k
    stackPush(LLContext.getFloat(Instr.getNum().get<float>()));
199
14.3k
    break;
200
6.69k
  case OpCode::F64__const:
201
6.69k
    stackPush(LLContext.getDouble(Instr.getNum().get<double>()));
202
6.69k
    break;
203
204
0
  default:
205
0
    assumingUnreachable();
206
642k
  }
207
642k
  return {};
208
642k
}
209
210
// Memory32 cannot escape the reservation's guard region, so it keeps the
211
// guard-page fast path. Memory64 addresses are guest-controlled 64-bit values.
212
void FunctionCompiler::boundsCheckMemory64(unsigned MemoryIndex,
213
                                           LLVM::Value Addr, uint64_t Offset,
214
20.8k
                                           uint64_t AccessSize) noexcept {
215
20.8k
  if (Context.MemoryAddrTypes[MemoryIndex].getIntegerBitWidth() != 64) {
216
20.8k
    return;
217
20.8k
  }
218
1
  if (Offset > std::numeric_limits<uint64_t>::max() - AccessSize) {
219
    // No address can satisfy the access.
220
0
    Builder.createBr(getTrapBB(ErrCode::Value::MemoryOutOfBounds));
221
0
    Builder.positionAtEnd(
222
0
        LLVM::BasicBlock::create(LLContext, F.Fn, "mem64.ok"));
223
0
    return;
224
0
  }
225
  // Addr < usub_sat(SizeBytes, Offset + AccessSize - 1) is exactly
226
  // Addr + Offset + AccessSize <= SizeBytes with every term kept in 64 bits.
227
1
  const uint64_t OffsetAndSize = Offset + AccessSize;
228
1
  auto SizeBytes =
229
1
      Builder.createShl(Context.getMemorySize(Builder, ModCtx, MemoryIndex),
230
1
                        LLContext.getInt64(16));
231
1
  auto Limit = Builder.createIntrinsic(
232
1
      LLVM::Core::USubSat, {Context.Int64Ty},
233
1
      {SizeBytes, LLContext.getInt64(OffsetAndSize - 1)});
234
1
  auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "mem64.ok");
235
1
  Builder.createCondBr(Builder.createLikely(Builder.createICmpULT(Addr, Limit)),
236
1
                       OkBB, getTrapBB(ErrCode::Value::MemoryOutOfBounds));
237
1
  Builder.positionAtEnd(OkBB);
238
1
}
239
240
void FunctionCompiler::compileLoadOp(unsigned MemoryIndex, uint64_t Offset,
241
                                     unsigned Alignment,
242
17.6k
                                     LLVM::Type LoadTy) noexcept {
243
17.6k
  if constexpr (LLVM::kForceUnalignment) {
244
17.6k
    Alignment = 0;
245
17.6k
  }
246
17.6k
  auto Off = Builder.createZExt(stackPop(), Context.Int64Ty);
247
17.6k
  boundsCheckMemory64(MemoryIndex, Off, Offset,
248
17.6k
                      LoadTy.getPrimitiveSizeInBits() / 8);
249
17.6k
  if (Offset != 0) {
250
11.2k
    Off = Builder.createAdd(Off, LLContext.getInt64(Offset));
251
11.2k
  }
252
253
17.6k
  auto VPtr = Builder.createInBoundsGEP1(
254
17.6k
      Context.Int8Ty, Context.getMemory(Builder, ModCtx, MemoryIndex), Off);
255
17.6k
  auto Ptr = Builder.createBitCast(VPtr, LoadTy.getPointerTo());
256
17.6k
  auto LoadInst = Builder.createLoad(LoadTy, Ptr, true);
257
17.6k
  LoadInst.setAlignment(1 << Alignment);
258
17.6k
  stackPush(switchEndian(LoadInst));
259
17.6k
}
260
261
void FunctionCompiler::compileLoadOp(unsigned MemoryIndex, uint64_t Offset,
262
                                     unsigned Alignment, LLVM::Type LoadTy,
263
                                     LLVM::Type ExtendTy,
264
7.71k
                                     bool Signed) noexcept {
265
7.71k
  compileLoadOp(MemoryIndex, Offset, Alignment, LoadTy);
266
7.71k
  if (Signed) {
267
3.21k
    Stack.back() = Builder.createSExt(Stack.back(), ExtendTy);
268
4.50k
  } else {
269
4.50k
    Stack.back() = Builder.createZExt(Stack.back(), ExtendTy);
270
4.50k
  }
271
7.71k
}
272
273
void FunctionCompiler::compileStoreOp(uint32_t MemoryIndex, uint64_t Offset,
274
                                      uint32_t Alignment, LLVM::Type LoadTy,
275
3.09k
                                      bool Trunc, bool BitCast) noexcept {
276
3.09k
  if constexpr (LLVM::kForceUnalignment) {
277
3.09k
    Alignment = 0;
278
3.09k
  }
279
3.09k
  auto V = stackPop();
280
3.09k
  auto Off = Builder.createZExt(stackPop(), Context.Int64Ty);
281
3.09k
  boundsCheckMemory64(MemoryIndex, Off, Offset,
282
3.09k
                      LoadTy.getPrimitiveSizeInBits() / 8);
283
3.09k
  if (Offset != 0) {
284
2.33k
    Off = Builder.createAdd(Off, LLContext.getInt64(Offset));
285
2.33k
  }
286
287
3.09k
  if (Trunc) {
288
663
    V = Builder.createTrunc(V, LoadTy);
289
663
  }
290
3.09k
  if (BitCast) {
291
215
    V = Builder.createBitCast(V, LoadTy);
292
215
  }
293
3.09k
  V = switchEndian(V);
294
3.09k
  auto VPtr = Builder.createInBoundsGEP1(
295
3.09k
      Context.Int8Ty, Context.getMemory(Builder, ModCtx, MemoryIndex), Off);
296
3.09k
  auto Ptr = Builder.createBitCast(VPtr, LoadTy.getPointerTo());
297
3.09k
  auto StoreInst = Builder.createStore(V, Ptr, true);
298
3.09k
  StoreInst.setAlignment(1 << Alignment);
299
3.09k
}
300
301
} // namespace WasmEdge