2 * Copyright 2015 WebAssembly Community Group participants
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 // Simple WebAssembly interpreter. This operates directly on the AST,
19 // for simplicity and clarity. A goal is for it to be possible for
20 // people to read this code and understand WebAssembly semantics.
23 #ifndef wasm_wasm_interpreter_h
24 #define wasm_wasm_interpreter_h
30 #include "support/bits.h"
31 #include "support/safe_integer.h"
33 #include "wasm-traversal.h"
36 #ifdef WASM_INTERPRETER_DEBUG
37 #include "wasm-printing.h"
43 using namespace cashew
;
47 extern Name WASM
, RETURN_FLOW
;
53 // Stuff that flows around during executing expressions: a literal, or a change in control flow.
57 Flow(Literal value
) : value(value
) {}
58 Flow(Name breakTo
) : breakTo(breakTo
) {}
61 Name breakTo
; // if non-null, a break is going on
63 bool breaking() { return breakTo
.is(); }
65 void clearIf(Name target
) {
66 if (breakTo
== target
) {
71 friend std::ostream
& operator<<(std::ostream
& o
, Flow
& flow
) {
72 o
<< "(flow " << (flow
.breakTo
.is() ? flow
.breakTo
.str
: "-") << " : " << flow
.value
<< ')';
77 // A list of literals, for function calls
78 typedef std::vector
<Literal
> LiteralList
;
81 #ifdef WASM_INTERPRETER_DEBUG
83 static int indentLevel
;
85 const char* entryName
;
88 Indenter(const char* entry
);
94 #define NOTE_ENTER(x) Indenter _int_blah(x); { \
96 std::cout << "visit " << x << " : " << curr << "\n"; }
97 #define NOTE_ENTER_(x) Indenter _int_blah(x); { \
99 std::cout << "visit " << x << "\n"; }
100 #define NOTE_NAME(p0) { \
102 std::cout << "name " << '(' << Name(p0) << ")\n"; }
103 #define NOTE_EVAL1(p0) { \
105 std::cout << "eval " #p0 " (" << p0 << ")\n"; }
106 #define NOTE_EVAL2(p0, p1) { \
108 std::cout << "eval " #p0 " (" << p0 << "), " #p1 " (" << p1 << ")\n"; }
109 #else // WASM_INTERPRETER_DEBUG
110 #define NOTE_ENTER(x)
111 #define NOTE_ENTER_(x)
112 #define NOTE_NAME(p0)
113 #define NOTE_EVAL1(p0)
114 #define NOTE_EVAL2(p0, p1)
115 #endif // WASM_INTERPRETER_DEBUG
117 // Execute an expression
118 template<typename SubType
>
119 class ExpressionRunner
: public Visitor
<SubType
, Flow
> {
121 Flow
visit(Expression
*curr
) {
122 auto ret
= Visitor
<SubType
, Flow
>::visit(curr
);
123 if (!ret
.breaking() && (isConcreteWasmType(curr
->type
) || isConcreteWasmType(ret
.value
.type
))) {
124 #if 1 // def WASM_INTERPRETER_DEBUG
125 if (ret
.value
.type
!= curr
->type
) {
126 std::cerr
<< "expected " << printWasmType(curr
->type
) << ", seeing " << printWasmType(ret
.value
.type
) << " from\n" << curr
<< '\n';
129 assert(ret
.value
.type
== curr
->type
);
134 Flow
visitBlock(Block
*curr
) {
136 // special-case Block, because Block nesting (in their first element) can be incredibly deep
137 std::vector
<Block
*> stack
;
138 stack
.push_back(curr
);
139 while (curr
->list
.size() > 0 && curr
->list
[0]->is
<Block
>()) {
140 curr
= curr
->list
[0]->cast
<Block
>();
141 stack
.push_back(curr
);
144 auto* top
= stack
.back();
145 while (stack
.size() > 0) {
148 if (flow
.breaking()) {
149 flow
.clearIf(curr
->name
);
152 auto& list
= curr
->list
;
153 for (size_t i
= 0; i
< list
.size(); i
++) {
154 if (curr
!= top
&& i
== 0) {
155 // one of the block recursions we already handled
158 flow
= visit(list
[i
]);
159 if (flow
.breaking()) {
160 flow
.clearIf(curr
->name
);
167 Flow
visitIf(If
*curr
) {
169 Flow flow
= visit(curr
->condition
);
170 if (flow
.breaking()) return flow
;
171 NOTE_EVAL1(flow
.value
);
172 if (flow
.value
.geti32()) {
173 Flow flow
= visit(curr
->ifTrue
);
174 if (!flow
.breaking() && !curr
->ifFalse
) flow
.value
= Literal(); // if_else returns a value, but if does not
177 if (curr
->ifFalse
) return visit(curr
->ifFalse
);
180 Flow
visitLoop(Loop
*curr
) {
183 Flow flow
= visit(curr
->body
);
184 if (flow
.breaking()) {
185 if (flow
.breakTo
== curr
->name
) continue; // lol
187 return flow
; // loop does not loop automatically, only continue achieves that
190 Flow
visitBreak(Break
*curr
) {
192 bool condition
= true;
195 flow
= visit(curr
->value
);
196 if (flow
.breaking()) return flow
;
198 if (curr
->condition
) {
199 Flow conditionFlow
= visit(curr
->condition
);
200 if (conditionFlow
.breaking()) return conditionFlow
;
201 condition
= conditionFlow
.value
.getInteger() != 0;
202 if (!condition
) return flow
;
204 flow
.breakTo
= curr
->name
;
207 Flow
visitSwitch(Switch
*curr
) {
208 NOTE_ENTER("Switch");
212 flow
= visit(curr
->value
);
213 if (flow
.breaking()) return flow
;
217 flow
= visit(curr
->condition
);
218 if (flow
.breaking()) return flow
;
219 int64_t index
= flow
.value
.getInteger();
220 Name target
= curr
->default_
;
221 if (index
>= 0 && (size_t)index
< curr
->targets
.size()) {
222 target
= curr
->targets
[(size_t)index
];
224 flow
.breakTo
= target
;
229 Flow
visitConst(Const
*curr
) {
231 NOTE_EVAL1(curr
->value
);
232 return Flow(curr
->value
); // heh
234 Flow
visitUnary(Unary
*curr
) {
236 Flow flow
= visit(curr
->value
);
237 if (flow
.breaking()) return flow
;
238 Literal value
= flow
.value
;
240 if (value
.type
== i32
) {
242 case ClzInt32
: return value
.countLeadingZeroes();
243 case CtzInt32
: return value
.countTrailingZeroes();
244 case PopcntInt32
: return value
.popCount();
245 case EqZInt32
: return Literal(int32_t(value
== Literal(int32_t(0))));
246 case ReinterpretInt32
: return value
.castToF32();
247 case ExtendSInt32
: return value
.extendToSI64();
248 case ExtendUInt32
: return value
.extendToUI64();
249 case ConvertUInt32ToFloat32
: return value
.convertUToF32();
250 case ConvertUInt32ToFloat64
: return value
.convertUToF64();
251 case ConvertSInt32ToFloat32
: return value
.convertSToF32();
252 case ConvertSInt32ToFloat64
: return value
.convertSToF64();
253 default: WASM_UNREACHABLE();
256 if (value
.type
== i64
) {
258 case ClzInt64
: return value
.countLeadingZeroes();
259 case CtzInt64
: return value
.countTrailingZeroes();
260 case PopcntInt64
: return value
.popCount();
261 case EqZInt64
: return Literal(int32_t(value
== Literal(int64_t(0))));
262 case WrapInt64
: return value
.truncateToI32();
263 case ReinterpretInt64
: return value
.castToF64();
264 case ConvertUInt64ToFloat32
: return value
.convertUToF32();
265 case ConvertUInt64ToFloat64
: return value
.convertUToF64();
266 case ConvertSInt64ToFloat32
: return value
.convertSToF32();
267 case ConvertSInt64ToFloat64
: return value
.convertSToF64();
268 default: WASM_UNREACHABLE();
271 if (value
.type
== f32
) {
273 case NegFloat32
: return value
.neg();
274 case AbsFloat32
: return value
.abs();
275 case CeilFloat32
: return value
.ceil();
276 case FloorFloat32
: return value
.floor();
277 case TruncFloat32
: return value
.trunc();
278 case NearestFloat32
: return value
.nearbyint();
279 case SqrtFloat32
: return value
.sqrt();
280 case TruncSFloat32ToInt32
:
281 case TruncSFloat32ToInt64
: return truncSFloat(curr
, value
);
282 case TruncUFloat32ToInt32
:
283 case TruncUFloat32ToInt64
: return truncUFloat(curr
, value
);
284 case ReinterpretFloat32
: return value
.castToI32();
285 case PromoteFloat32
: return value
.extendToF64();
286 default: WASM_UNREACHABLE();
289 if (value
.type
== f64
) {
291 case NegFloat64
: return value
.neg();
292 case AbsFloat64
: return value
.abs();
293 case CeilFloat64
: return value
.ceil();
294 case FloorFloat64
: return value
.floor();
295 case TruncFloat64
: return value
.trunc();
296 case NearestFloat64
: return value
.nearbyint();
297 case SqrtFloat64
: return value
.sqrt();
298 case TruncSFloat64ToInt32
:
299 case TruncSFloat64ToInt64
: return truncSFloat(curr
, value
);
300 case TruncUFloat64ToInt32
:
301 case TruncUFloat64ToInt64
: return truncUFloat(curr
, value
);
302 case ReinterpretFloat64
: return value
.castToI64();
303 case DemoteFloat64
: {
304 double val
= value
.getFloat();
305 if (std::isnan(val
)) return Literal(float(val
));
306 if (std::isinf(val
)) return Literal(float(val
));
307 // when close to the limit, but still truncatable to a valid value, do that
308 // see https://github.com/WebAssembly/sexpr-wasm-prototype/blob/2d375e8d502327e814d62a08f22da9d9b6b675dc/src/wasm-interpreter.c#L247
309 uint64_t bits
= value
.reinterpreti64();
310 if (bits
> 0x47efffffe0000000ULL
&& bits
< 0x47effffff0000000ULL
) return Literal(std::numeric_limits
<float>::max());
311 if (bits
> 0xc7efffffe0000000ULL
&& bits
< 0xc7effffff0000000ULL
) return Literal(-std::numeric_limits
<float>::max());
312 // when we must convert to infinity, do that
313 if (val
< -std::numeric_limits
<float>::max()) return Literal(-std::numeric_limits
<float>::infinity());
314 if (val
> std::numeric_limits
<float>::max()) return Literal(std::numeric_limits
<float>::infinity());
315 return value
.truncateToF32();
317 default: WASM_UNREACHABLE();
322 Flow
visitBinary(Binary
*curr
) {
323 NOTE_ENTER("Binary");
324 Flow flow
= visit(curr
->left
);
325 if (flow
.breaking()) return flow
;
326 Literal left
= flow
.value
;
327 flow
= visit(curr
->right
);
328 if (flow
.breaking()) return flow
;
329 Literal right
= flow
.value
;
330 NOTE_EVAL2(left
, right
);
331 assert(isConcreteWasmType(curr
->left
->type
) ? left
.type
== curr
->left
->type
: true);
332 assert(isConcreteWasmType(curr
->right
->type
) ? right
.type
== curr
->right
->type
: true);
333 if (left
.type
== i32
) {
335 case AddInt32
: return left
.add(right
);
336 case SubInt32
: return left
.sub(right
);
337 case MulInt32
: return left
.mul(right
);
339 if (right
.getInteger() == 0) trap("i32.div_s by 0");
340 if (left
.getInteger() == std::numeric_limits
<int32_t>::min() && right
.getInteger() == -1) trap("i32.div_s overflow"); // signed division overflow
341 return left
.divS(right
);
344 if (right
.getInteger() == 0) trap("i32.div_u by 0");
345 return left
.divU(right
);
348 if (right
.getInteger() == 0) trap("i32.rem_s by 0");
349 if (left
.getInteger() == std::numeric_limits
<int32_t>::min() && right
.getInteger() == -1) return Literal(int32_t(0));
350 return left
.remS(right
);
353 if (right
.getInteger() == 0) trap("i32.rem_u by 0");
354 return left
.remU(right
);
356 case AndInt32
: return left
.and_(right
);
357 case OrInt32
: return left
.or_(right
);
358 case XorInt32
: return left
.xor_(right
);
359 case ShlInt32
: return left
.shl(right
.and_(Literal(int32_t(31))));
360 case ShrUInt32
: return left
.shrU(right
.and_(Literal(int32_t(31))));
361 case ShrSInt32
: return left
.shrS(right
.and_(Literal(int32_t(31))));
362 case RotLInt32
: return left
.rotL(right
);
363 case RotRInt32
: return left
.rotR(right
);
364 case EqInt32
: return left
.eq(right
);
365 case NeInt32
: return left
.ne(right
);
366 case LtSInt32
: return left
.ltS(right
);
367 case LtUInt32
: return left
.ltU(right
);
368 case LeSInt32
: return left
.leS(right
);
369 case LeUInt32
: return left
.leU(right
);
370 case GtSInt32
: return left
.gtS(right
);
371 case GtUInt32
: return left
.gtU(right
);
372 case GeSInt32
: return left
.geS(right
);
373 case GeUInt32
: return left
.geU(right
);
374 default: WASM_UNREACHABLE();
376 } else if (left
.type
== i64
) {
378 case AddInt64
: return left
.add(right
);
379 case SubInt64
: return left
.sub(right
);
380 case MulInt64
: return left
.mul(right
);
382 if (right
.getInteger() == 0) trap("i64.div_s by 0");
383 if (left
.getInteger() == LLONG_MIN
&& right
.getInteger() == -1LL) trap("i64.div_s overflow"); // signed division overflow
384 return left
.divS(right
);
387 if (right
.getInteger() == 0) trap("i64.div_u by 0");
388 return left
.divU(right
);
391 if (right
.getInteger() == 0) trap("i64.rem_s by 0");
392 if (left
.getInteger() == LLONG_MIN
&& right
.getInteger() == -1LL) return Literal(int64_t(0));
393 return left
.remS(right
);
396 if (right
.getInteger() == 0) trap("i64.rem_u by 0");
397 return left
.remU(right
);
399 case AndInt64
: return left
.and_(right
);
400 case OrInt64
: return left
.or_(right
);
401 case XorInt64
: return left
.xor_(right
);
402 case ShlInt64
: return left
.shl(right
.and_(Literal(int64_t(63))));
403 case ShrUInt64
: return left
.shrU(right
.and_(Literal(int64_t(63))));
404 case ShrSInt64
: return left
.shrS(right
.and_(Literal(int64_t(63))));
405 case RotLInt64
: return left
.rotL(right
);
406 case RotRInt64
: return left
.rotR(right
);
407 case EqInt64
: return left
.eq(right
);
408 case NeInt64
: return left
.ne(right
);
409 case LtSInt64
: return left
.ltS(right
);
410 case LtUInt64
: return left
.ltU(right
);
411 case LeSInt64
: return left
.leS(right
);
412 case LeUInt64
: return left
.leU(right
);
413 case GtSInt64
: return left
.gtS(right
);
414 case GtUInt64
: return left
.gtU(right
);
415 case GeSInt64
: return left
.geS(right
);
416 case GeUInt64
: return left
.geU(right
);
417 default: WASM_UNREACHABLE();
419 } else if (left
.type
== f32
|| left
.type
== f64
) {
421 case AddFloat32
: case AddFloat64
: return left
.add(right
);
422 case SubFloat32
: case SubFloat64
: return left
.sub(right
);
423 case MulFloat32
: case MulFloat64
: return left
.mul(right
);
424 case DivFloat32
: case DivFloat64
: return left
.div(right
);
425 case CopySignFloat32
: case CopySignFloat64
: return left
.copysign(right
);
426 case MinFloat32
: case MinFloat64
: return left
.min(right
);
427 case MaxFloat32
: case MaxFloat64
: return left
.max(right
);
428 case EqFloat32
: case EqFloat64
: return left
.eq(right
);
429 case NeFloat32
: case NeFloat64
: return left
.ne(right
);
430 case LtFloat32
: case LtFloat64
: return left
.lt(right
);
431 case LeFloat32
: case LeFloat64
: return left
.le(right
);
432 case GtFloat32
: case GtFloat64
: return left
.gt(right
);
433 case GeFloat32
: case GeFloat64
: return left
.ge(right
);
434 default: WASM_UNREACHABLE();
439 Flow
visitSelect(Select
*curr
) {
440 NOTE_ENTER("Select");
441 Flow ifTrue
= visit(curr
->ifTrue
);
442 if (ifTrue
.breaking()) return ifTrue
;
443 Flow ifFalse
= visit(curr
->ifFalse
);
444 if (ifFalse
.breaking()) return ifFalse
;
445 Flow condition
= visit(curr
->condition
);
446 if (condition
.breaking()) return condition
;
447 NOTE_EVAL1(condition
.value
);
448 return condition
.value
.geti32() ? ifTrue
: ifFalse
; // ;-)
450 Flow
visitDrop(Drop
*curr
) {
452 Flow value
= visit(curr
->value
);
453 if (value
.breaking()) return value
;
456 Flow
visitReturn(Return
*curr
) {
457 NOTE_ENTER("Return");
460 flow
= visit(curr
->value
);
461 if (flow
.breaking()) return flow
;
462 NOTE_EVAL1(flow
.value
);
464 flow
.breakTo
= RETURN_FLOW
;
467 Flow
visitNop(Nop
*curr
) {
471 Flow
visitUnreachable(Unreachable
*curr
) {
472 NOTE_ENTER("Unreachable");
477 Literal
truncSFloat(Unary
* curr
, Literal value
) {
478 double val
= value
.getFloat();
479 if (std::isnan(val
)) trap("truncSFloat of nan");
480 if (curr
->type
== i32
) {
481 if (value
.type
== f32
) {
482 if (!isInRangeI32TruncS(value
.reinterpreti32())) trap("i32.truncSFloat overflow");
484 if (!isInRangeI32TruncS(value
.reinterpreti64())) trap("i32.truncSFloat overflow");
486 return Literal(int32_t(val
));
488 if (value
.type
== f32
) {
489 if (!isInRangeI64TruncS(value
.reinterpreti32())) trap("i64.truncSFloat overflow");
491 if (!isInRangeI64TruncS(value
.reinterpreti64())) trap("i64.truncSFloat overflow");
493 return Literal(int64_t(val
));
497 Literal
truncUFloat(Unary
* curr
, Literal value
) {
498 double val
= value
.getFloat();
499 if (std::isnan(val
)) trap("truncUFloat of nan");
500 if (curr
->type
== i32
) {
501 if (value
.type
== f32
) {
502 if (!isInRangeI32TruncU(value
.reinterpreti32())) trap("i32.truncUFloat overflow");
504 if (!isInRangeI32TruncU(value
.reinterpreti64())) trap("i32.truncUFloat overflow");
506 return Literal(uint32_t(val
));
508 if (value
.type
== f32
) {
509 if (!isInRangeI64TruncU(value
.reinterpreti32())) trap("i64.truncUFloat overflow");
511 if (!isInRangeI64TruncU(value
.reinterpreti64())) trap("i64.truncUFloat overflow");
513 return Literal(uint64_t(val
));
517 virtual void trap(const char* why
) {
522 // Execute an constant expression in a global init or memory offset
523 template<typename GlobalManager
>
524 class ConstantExpressionRunner
: public ExpressionRunner
<ConstantExpressionRunner
<GlobalManager
>> {
525 GlobalManager
& globals
;
527 ConstantExpressionRunner(GlobalManager
& globals
) : globals(globals
) {}
529 Flow
visitLoop(Loop
* curr
) { WASM_UNREACHABLE(); }
530 Flow
visitCall(Call
* curr
) { WASM_UNREACHABLE(); }
531 Flow
visitCallImport(CallImport
* curr
) { WASM_UNREACHABLE(); }
532 Flow
visitCallIndirect(CallIndirect
* curr
) { WASM_UNREACHABLE(); }
533 Flow
visitGetLocal(GetLocal
*curr
) { WASM_UNREACHABLE(); }
534 Flow
visitSetLocal(SetLocal
*curr
) { WASM_UNREACHABLE(); }
535 Flow
visitGetGlobal(GetGlobal
*curr
) {
536 return Flow(globals
[curr
->name
]);
538 Flow
visitSetGlobal(SetGlobal
*curr
) { WASM_UNREACHABLE(); }
539 Flow
visitLoad(Load
*curr
) { WASM_UNREACHABLE(); }
540 Flow
visitStore(Store
*curr
) { WASM_UNREACHABLE(); }
541 Flow
visitHost(Host
*curr
) { WASM_UNREACHABLE(); }
545 // An instance of a WebAssembly module, which can execute it via AST interpretation.
547 // To embed this interpreter, you need to provide an ExternalInterface instance
548 // (see below) which provides the embedding-specific details, that is, how to
549 // connect to the embedding implementation.
551 // To call into the interpreter, use callExport.
554 template<typename GlobalManager
, typename SubType
>
555 class ModuleInstanceBase
{
558 // You need to implement one of these to create a concrete interpreter. The
559 // ExternalInterface provides embedding-specific functionality like calling
560 // an imported function or accessing memory.
562 struct ExternalInterface
{
563 virtual void init(Module
& wasm
, SubType
& instance
) {}
564 virtual void importGlobals(GlobalManager
& globals
, Module
& wasm
) = 0;
565 virtual Literal
callImport(Import
* import
, LiteralList
& arguments
) = 0;
566 virtual Literal
callTable(Index index
, LiteralList
& arguments
, WasmType result
, SubType
& instance
) = 0;
567 virtual void growMemory(Address oldSize
, Address newSize
) = 0;
568 virtual void trap(const char* why
) = 0;
570 // the default impls for load and store switch on the sizes. you can either
571 // customize load/store, or the sub-functions which they call
572 virtual Literal
load(Load
* load
, Address addr
) {
573 switch (load
->type
) {
575 switch (load
->bytes
) {
576 case 1: return load
->signed_
? Literal((int32_t)load8s(addr
)) : Literal((int32_t)load8u(addr
));
577 case 2: return load
->signed_
? Literal((int32_t)load16s(addr
)) : Literal((int32_t)load16u(addr
));
578 case 4: return Literal((int32_t)load32s(addr
));
579 default: WASM_UNREACHABLE();
584 switch (load
->bytes
) {
585 case 1: return load
->signed_
? Literal((int64_t)load8s(addr
)) : Literal((int64_t)load8u(addr
));
586 case 2: return load
->signed_
? Literal((int64_t)load16s(addr
)) : Literal((int64_t)load16u(addr
));
587 case 4: return load
->signed_
? Literal((int64_t)load32s(addr
)) : Literal((int64_t)load32u(addr
));
588 case 8: return Literal((int64_t)load64s(addr
));
589 default: WASM_UNREACHABLE();
593 case f32
: return Literal(load32u(addr
)).castToF32();
594 case f64
: return Literal(load64u(addr
)).castToF64();
595 default: WASM_UNREACHABLE();
598 virtual void store(Store
* store
, Address addr
, Literal value
) {
599 switch (store
->valueType
) {
601 switch (store
->bytes
) {
602 case 1: store8(addr
, value
.geti32()); break;
603 case 2: store16(addr
, value
.geti32()); break;
604 case 4: store32(addr
, value
.geti32()); break;
605 default: WASM_UNREACHABLE();
610 switch (store
->bytes
) {
611 case 1: store8(addr
, value
.geti64()); break;
612 case 2: store16(addr
, value
.geti64()); break;
613 case 4: store32(addr
, value
.geti64()); break;
614 case 8: store64(addr
, value
.geti64()); break;
615 default: WASM_UNREACHABLE();
619 // write floats carefully, ensuring all bits reach memory
620 case f32
: store32(addr
, value
.reinterpreti32()); break;
621 case f64
: store64(addr
, value
.reinterpreti64()); break;
622 default: WASM_UNREACHABLE();
626 virtual int8_t load8s(Address addr
) { WASM_UNREACHABLE(); }
627 virtual uint8_t load8u(Address addr
) { WASM_UNREACHABLE(); }
628 virtual int16_t load16s(Address addr
) { WASM_UNREACHABLE(); }
629 virtual uint16_t load16u(Address addr
) { WASM_UNREACHABLE(); }
630 virtual int32_t load32s(Address addr
) { WASM_UNREACHABLE(); }
631 virtual uint32_t load32u(Address addr
) { WASM_UNREACHABLE(); }
632 virtual int64_t load64s(Address addr
) { WASM_UNREACHABLE(); }
633 virtual uint64_t load64u(Address addr
) { WASM_UNREACHABLE(); }
635 virtual void store8(Address addr
, int8_t value
) { WASM_UNREACHABLE(); }
636 virtual void store16(Address addr
, int16_t value
) { WASM_UNREACHABLE(); }
637 virtual void store32(Address addr
, int32_t value
) { WASM_UNREACHABLE(); }
638 virtual void store64(Address addr
, int64_t value
) { WASM_UNREACHABLE(); }
642 return static_cast<SubType
*>(this);
648 GlobalManager globals
;
650 ModuleInstanceBase(Module
& wasm
, ExternalInterface
* externalInterface
) : wasm(wasm
), externalInterface(externalInterface
) {
651 // import globals from the outside
652 externalInterface
->importGlobals(globals
, wasm
);
654 memorySize
= wasm
.memory
.initial
;
655 // generate internal (non-imported) globals
656 for (auto& global
: wasm
.globals
) {
657 globals
[global
->name
] = ConstantExpressionRunner
<GlobalManager
>(globals
).visit(global
->init
).value
;
659 // initialize the rest of the external interface
660 externalInterface
->init(wasm
, *self());
661 // run start, if present
662 if (wasm
.start
.is()) {
663 LiteralList arguments
;
664 callFunction(wasm
.start
, arguments
);
668 // call an exported function
669 Literal
callExport(Name name
, LiteralList
& arguments
) {
670 Export
*export_
= wasm
.getExportOrNull(name
);
671 if (!export_
) externalInterface
->trap("callExport not found");
672 return callFunction(export_
->value
, arguments
);
675 Literal
callExport(Name name
) {
676 LiteralList arguments
;
677 return callExport(name
, arguments
);
680 // get an exported global
681 Literal
getExport(Name name
) {
682 Export
*export_
= wasm
.getExportOrNull(name
);
683 if (!export_
) externalInterface
->trap("getExport external not found");
684 Name internalName
= export_
->value
;
685 auto iter
= globals
.find(internalName
);
686 if (iter
== globals
.end()) externalInterface
->trap("getExport internal not found");
690 std::string
printFunctionStack() {
691 std::string ret
= "/== (binaryen interpreter stack trace)\n";
692 for (int i
= int(functionStack
.size()) - 1; i
>= 0; i
--) {
693 ret
+= std::string("|: ") + functionStack
[i
].str
+ "\n";
695 ret
+= std::string("\\==\n");
700 // Keep a record of call depth, to guard against excessive recursion.
703 // Function name stack. We maintain this explicitly to allow printing of
705 std::vector
<Name
> functionStack
;
708 // Call a function, starting an invocation.
709 Literal
callFunction(Name name
, LiteralList
& arguments
) {
710 // if the last call ended in a jump up the stack, it might have left stuff for us to clean up here
712 functionStack
.clear();
713 return callFunctionInternal(name
, arguments
);
716 // Internal function call. Must be public so that callTable implementations can use it (refactor?)
717 Literal
callFunctionInternal(Name name
, LiteralList
& arguments
) {
719 class FunctionScope
{
721 std::vector
<Literal
> locals
;
724 FunctionScope(Function
* function
, LiteralList
& arguments
)
725 : function(function
) {
726 if (function
->params
.size() != arguments
.size()) {
727 std::cerr
<< "Function `" << function
->name
<< "` expects "
728 << function
->params
.size() << " parameters, got "
729 << arguments
.size() << " arguments." << std::endl
;
732 locals
.resize(function
->getNumLocals());
733 for (size_t i
= 0; i
< function
->getNumLocals(); i
++) {
734 if (i
< arguments
.size()) {
735 assert(function
->isParam(i
));
736 if (function
->params
[i
] != arguments
[i
].type
) {
737 std::cerr
<< "Function `" << function
->name
<< "` expects type "
738 << printWasmType(function
->params
[i
])
739 << " for parameter " << i
<< ", got "
740 << printWasmType(arguments
[i
].type
) << "." << std::endl
;
743 locals
[i
] = arguments
[i
];
745 assert(function
->isVar(i
));
746 locals
[i
].type
= function
->getLocalType(i
);
752 // Executes expresions with concrete runtime info, the function and module at runtime
753 class RuntimeExpressionRunner
: public ExpressionRunner
<RuntimeExpressionRunner
> {
754 ModuleInstanceBase
& instance
;
755 FunctionScope
& scope
;
758 RuntimeExpressionRunner(ModuleInstanceBase
& instance
, FunctionScope
& scope
) : instance(instance
), scope(scope
) {}
760 Flow
generateArguments(const ExpressionList
& operands
, LiteralList
& arguments
) {
761 NOTE_ENTER_("generateArguments");
762 arguments
.reserve(operands
.size());
763 for (auto expression
: operands
) {
764 Flow flow
= this->visit(expression
);
765 if (flow
.breaking()) return flow
;
766 NOTE_EVAL1(flow
.value
);
767 arguments
.push_back(flow
.value
);
772 Flow
visitCall(Call
*curr
) {
774 NOTE_NAME(curr
->target
);
775 LiteralList arguments
;
776 Flow flow
= generateArguments(curr
->operands
, arguments
);
777 if (flow
.breaking()) return flow
;
778 Flow ret
= instance
.callFunctionInternal(curr
->target
, arguments
);
779 #ifdef WASM_INTERPRETER_DEBUG
780 std::cout
<< "(returned to " << scope
.function
->name
<< ")\n";
784 Flow
visitCallImport(CallImport
*curr
) {
785 NOTE_ENTER("CallImport");
786 LiteralList arguments
;
787 Flow flow
= generateArguments(curr
->operands
, arguments
);
788 if (flow
.breaking()) return flow
;
789 return instance
.externalInterface
->callImport(instance
.wasm
.getImport(curr
->target
), arguments
);
791 Flow
visitCallIndirect(CallIndirect
*curr
) {
792 NOTE_ENTER("CallIndirect");
793 LiteralList arguments
;
794 Flow flow
= generateArguments(curr
->operands
, arguments
);
795 if (flow
.breaking()) return flow
;
796 Flow target
= this->visit(curr
->target
);
797 if (target
.breaking()) return target
;
798 Index index
= target
.value
.geti32();
799 return instance
.externalInterface
->callTable(index
, arguments
, curr
->type
, *instance
.self());
802 Flow
visitGetLocal(GetLocal
*curr
) {
803 NOTE_ENTER("GetLocal");
804 auto index
= curr
->index
;
806 NOTE_EVAL1(scope
.locals
[index
]);
807 return scope
.locals
[index
];
809 Flow
visitSetLocal(SetLocal
*curr
) {
810 NOTE_ENTER("SetLocal");
811 auto index
= curr
->index
;
812 Flow flow
= this->visit(curr
->value
);
813 if (flow
.breaking()) return flow
;
815 NOTE_EVAL1(flow
.value
);
816 assert(curr
->isTee() ? flow
.value
.type
== curr
->type
: true);
817 scope
.locals
[index
] = flow
.value
;
818 return curr
->isTee() ? flow
: Flow();
821 Flow
visitGetGlobal(GetGlobal
*curr
) {
822 NOTE_ENTER("GetGlobal");
823 auto name
= curr
->name
;
825 assert(instance
.globals
.find(name
) != instance
.globals
.end());
826 NOTE_EVAL1(instance
.globals
[name
]);
827 return instance
.globals
[name
];
829 Flow
visitSetGlobal(SetGlobal
*curr
) {
830 NOTE_ENTER("SetGlobal");
831 auto name
= curr
->name
;
832 Flow flow
= this->visit(curr
->value
);
833 if (flow
.breaking()) return flow
;
835 NOTE_EVAL1(flow
.value
);
836 instance
.globals
[name
] = flow
.value
;
840 Flow
visitLoad(Load
*curr
) {
842 Flow flow
= this->visit(curr
->ptr
);
843 if (flow
.breaking()) return flow
;
845 auto addr
= instance
.getFinalAddress(curr
, flow
.value
);
846 auto ret
= instance
.externalInterface
->load(curr
, addr
);
851 Flow
visitStore(Store
*curr
) {
853 Flow ptr
= this->visit(curr
->ptr
);
854 if (ptr
.breaking()) return ptr
;
855 Flow value
= this->visit(curr
->value
);
856 if (value
.breaking()) return value
;
857 auto addr
= instance
.getFinalAddress(curr
, ptr
.value
);
860 instance
.externalInterface
->store(curr
, addr
, value
.value
);
864 Flow
visitAtomicRMW(AtomicRMW
*curr
) {
865 NOTE_ENTER("AtomicRMW");
866 Flow ptr
= this->visit(curr
->ptr
);
867 if (ptr
.breaking()) return ptr
;
868 auto value
= this->visit(curr
->value
);
869 if (value
.breaking()) return value
;
871 auto addr
= instance
.getFinalAddress(curr
, ptr
.value
);
874 auto loaded
= instance
.doAtomicLoad(addr
, curr
->bytes
, curr
->type
);
876 auto computed
= value
.value
;
878 case Add
: computed
= computed
.add(value
.value
); break;
879 case Sub
: computed
= computed
.sub(value
.value
); break;
880 case And
: computed
= computed
.and_(value
.value
); break;
881 case Or
: computed
= computed
.or_(value
.value
); break;
882 case Xor
: computed
= computed
.xor_(value
.value
); break;
883 case Xchg
: computed
= value
.value
; break;
884 default: WASM_UNREACHABLE();
886 instance
.doAtomicStore(addr
, curr
->bytes
, computed
);
889 Flow
visitAtomicCmpxchg(AtomicCmpxchg
*curr
) {
890 NOTE_ENTER("AtomicCmpxchg");
891 Flow ptr
= this->visit(curr
->ptr
);
892 if (ptr
.breaking()) return ptr
;
894 auto expected
= this->visit(curr
->expected
);
895 if (expected
.breaking()) return expected
;
896 auto replacement
= this->visit(curr
->replacement
);
897 if (replacement
.breaking()) return replacement
;
898 auto addr
= instance
.getFinalAddress(curr
, ptr
.value
);
900 NOTE_EVAL1(expected
);
901 NOTE_EVAL1(replacement
);
902 auto loaded
= instance
.doAtomicLoad(addr
, curr
->bytes
, curr
->type
);
904 if (loaded
== expected
.value
) {
905 instance
.doAtomicStore(addr
, curr
->bytes
, replacement
.value
);
909 Flow
visitAtomicWait(AtomicWait
*curr
) {
910 NOTE_ENTER("AtomicWait");
911 Flow ptr
= this->visit(curr
->ptr
);
912 if (ptr
.breaking()) return ptr
;
914 auto expected
= this->visit(curr
->expected
);
915 NOTE_EVAL1(expected
);
916 if (expected
.breaking()) return expected
;
917 auto timeout
= this->visit(curr
->timeout
);
919 if (timeout
.breaking()) return timeout
;
920 auto bytes
= getWasmTypeSize(curr
->expectedType
);
921 auto addr
= instance
.getFinalAddress(ptr
.value
, bytes
);
922 auto loaded
= instance
.doAtomicLoad(addr
, bytes
, curr
->expectedType
);
924 if (loaded
!= expected
.value
) {
925 return Literal(int32_t(1)); // not equal
927 // TODO: add threads support!
928 // for now, just assume we are woken up
929 return Literal(int32_t(0)); // woken up
931 Flow
visitAtomicWake(AtomicWake
*curr
) {
932 NOTE_ENTER("AtomicWake");
933 Flow ptr
= this->visit(curr
->ptr
);
934 if (ptr
.breaking()) return ptr
;
936 auto count
= this->visit(curr
->wakeCount
);
938 if (count
.breaking()) return count
;
939 // TODO: add threads support!
940 return Literal(int32_t(0)); // none woken up
943 Flow
visitHost(Host
*curr
) {
946 case PageSize
: return Literal((int32_t)Memory::kPageSize
);
947 case CurrentMemory
: return Literal(int32_t(instance
.memorySize
));
949 auto fail
= Literal(int32_t(-1));
950 Flow flow
= this->visit(curr
->operands
[0]);
951 if (flow
.breaking()) return flow
;
952 int32_t ret
= instance
.memorySize
;
953 uint32_t delta
= flow
.value
.geti32();
954 if (delta
> uint32_t(-1) /Memory::kPageSize
) return fail
;
955 if (instance
.memorySize
>= uint32_t(-1) - delta
) return fail
;
956 uint32_t newSize
= instance
.memorySize
+ delta
;
957 if (newSize
> instance
.wasm
.memory
.max
) return fail
;
958 instance
.externalInterface
->growMemory(instance
.memorySize
* Memory::kPageSize
, newSize
* Memory::kPageSize
);
959 instance
.memorySize
= newSize
;
960 return Literal(int32_t(ret
));
963 Name id
= curr
->nameOperand
;
964 if (id
== WASM
) return Literal(1);
965 return Literal((int32_t)0);
967 default: WASM_UNREACHABLE();
971 void trap(const char* why
) override
{
972 instance
.externalInterface
->trap(why
);
976 if (callDepth
> maxCallDepth
) externalInterface
->trap("stack limit");
977 auto previousCallDepth
= callDepth
;
979 auto previousFunctionStackSize
= functionStack
.size();
980 functionStack
.push_back(name
);
982 Function
*function
= wasm
.getFunction(name
);
984 FunctionScope
scope(function
, arguments
);
986 #ifdef WASM_INTERPRETER_DEBUG
987 std::cout
<< "entering " << function
->name
988 << "\n with arguments:\n";
989 for (unsigned i
= 0; i
< arguments
.size(); ++i
) {
990 std::cout
<< " $" << i
<< ": " << arguments
[i
] << '\n';
994 Flow flow
= RuntimeExpressionRunner(*this, scope
).visit(function
->body
);
995 assert(!flow
.breaking() || flow
.breakTo
== RETURN_FLOW
); // cannot still be breaking, it means we missed our stop
996 Literal ret
= flow
.value
;
997 if (function
->result
!= ret
.type
) {
998 std::cerr
<< "calling " << function
->name
<< " resulted in " << ret
<< " but the function type is " << function
->result
<< '\n';
1001 callDepth
= previousCallDepth
; // may decrease more than one, if we jumped up the stack
1002 // if we jumped up the stack, we also need to pop higher frames
1003 while (functionStack
.size() > previousFunctionStackSize
) {
1004 functionStack
.pop_back();
1006 #ifdef WASM_INTERPRETER_DEBUG
1007 std::cout
<< "exiting " << function
->name
<< " with " << ret
<< '\n';
1014 Address memorySize
; // in pages
1016 void trapIfGt(uint64_t lhs
, uint64_t rhs
, const char* msg
) {
1018 std::stringstream ss
;
1019 ss
<< msg
<< ": " << lhs
<< " > " << rhs
;
1020 externalInterface
->trap(ss
.str().c_str());
1025 Address
getFinalAddress(LS
* curr
, Literal ptr
) {
1026 Address memorySizeBytes
= memorySize
* Memory::kPageSize
;
1027 uint64_t addr
= ptr
.type
== i32
? ptr
.geti32() : ptr
.geti64();
1028 trapIfGt(curr
->offset
, memorySizeBytes
, "offset > memory");
1029 trapIfGt(addr
, memorySizeBytes
- curr
->offset
, "final > memory");
1030 addr
+= curr
->offset
;
1031 trapIfGt(curr
->bytes
, memorySizeBytes
, "bytes > memory");
1032 checkLoadAddress(addr
, curr
->bytes
);
1036 Address
getFinalAddress(Literal ptr
, Index bytes
) {
1037 Address memorySizeBytes
= memorySize
* Memory::kPageSize
;
1038 uint64_t addr
= ptr
.type
== i32
? ptr
.geti32() : ptr
.geti64();
1039 trapIfGt(addr
, memorySizeBytes
- bytes
, "highest > memory");
1043 void checkLoadAddress(Address addr
, Index bytes
) {
1044 Address memorySizeBytes
= memorySize
* Memory::kPageSize
;
1045 trapIfGt(addr
, memorySizeBytes
- bytes
, "highest > memory");
1048 Literal
doAtomicLoad(Address addr
, Index bytes
, WasmType type
) {
1049 checkLoadAddress(addr
, bytes
);
1051 ptr
.value
= Literal(int32_t(addr
));
1055 load
.signed_
= true;
1057 load
.isAtomic
= true; // understatement
1060 return externalInterface
->load(&load
, addr
);
1063 void doAtomicStore(Address addr
, Index bytes
, Literal toStore
) {
1065 ptr
.value
= Literal(int32_t(addr
));
1068 value
.value
= toStore
;
1069 value
.type
= toStore
.type
;
1071 store
.bytes
= bytes
;
1072 store
.align
= bytes
;
1073 store
.isAtomic
= true; // understatement
1075 store
.value
= &value
;
1076 store
.valueType
= value
.type
;
1077 return externalInterface
->store(&store
, addr
, toStore
);
1080 ExternalInterface
* externalInterface
;
1083 // The default ModuleInstance uses a trivial global manager
1084 typedef std::map
<Name
, Literal
> TrivialGlobalManager
;
1085 class ModuleInstance
: public ModuleInstanceBase
<TrivialGlobalManager
, ModuleInstance
> {
1087 ModuleInstance(Module
& wasm
, ExternalInterface
* externalInterface
) : ModuleInstanceBase(wasm
, externalInterface
) {}
1092 #endif // wasm_wasm_interpreter_h