Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
*
4
* Copyright 2016 Mozilla Foundation
5
*
6
* Licensed under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
9
*
11
*
12
* Unless required by applicable law or agreed to in writing, software
13
* distributed under the License is distributed on an "AS IS" BASIS,
14
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
* See the License for the specific language governing permissions and
16
* limitations under the License.
17
*/
18
19
#ifndef wasm_op_iter_h
20
#define wasm_op_iter_h
21
22
#include "mozilla/CompactPair.h"
23
#include "mozilla/Poison.h"
24
25
#include <type_traits>
26
27
#include "jit/AtomicOp.h"
28
#include "js/Printf.h"
29
#include "wasm/WasmUtility.h"
30
#include "wasm/WasmValidate.h"
31
32
namespace js {
33
namespace wasm {
34
35
template <typename PointerType>
36
class TaggedValue {
37
public:
38
enum Kind {
39
ImmediateKind1 = 0,
40
ImmediateKind2 = 1,
41
PointerKind1 = 2,
42
PointerKind2 = 3
43
};
44
45
private:
46
uintptr_t bits_;
47
48
static constexpr uintptr_t PayloadShift = 2;
49
static constexpr uintptr_t KindMask = 0x3;
50
static constexpr uintptr_t PointerKindBit = 0x2;
51
52
constexpr static bool IsPointerKind(Kind kind) {
53
return uintptr_t(kind) & PointerKindBit;
54
}
55
constexpr static bool IsImmediateKind(Kind kind) {
56
return !IsPointerKind(kind);
57
}
58
59
static_assert(IsImmediateKind(ImmediateKind1), "immediate kind 1");
60
static_assert(IsImmediateKind(ImmediateKind2), "immediate kind 2");
61
static_assert(IsPointerKind(PointerKind1), "pointer kind 1");
62
static_assert(IsPointerKind(PointerKind2), "pointer kind 2");
63
64
static uintptr_t PackImmediate(Kind kind, uint32_t imm) {
65
MOZ_ASSERT(IsImmediateKind(kind));
66
MOZ_ASSERT((uintptr_t(kind) & KindMask) == kind);
67
MOZ_ASSERT((imm & (uint32_t(KindMask) << (32 - PayloadShift))) == 0);
68
return uintptr_t(kind) | (uintptr_t(imm) << PayloadShift);
69
}
70
71
static uintptr_t PackPointer(Kind kind, PointerType* ptr) {
72
uintptr_t ptrBits = reinterpret_cast<uintptr_t>(ptr);
73
MOZ_ASSERT(IsPointerKind(kind));
74
MOZ_ASSERT((uintptr_t(kind) & KindMask) == kind);
75
MOZ_ASSERT((ptrBits & KindMask) == 0);
76
return uintptr_t(kind) | ptrBits;
77
}
78
79
public:
80
TaggedValue(Kind kind, uint32_t imm) : bits_(PackImmediate(kind, imm)) {}
81
TaggedValue(Kind kind, PointerType* ptr) : bits_(PackPointer(kind, ptr)) {}
82
83
uintptr_t bits() const { return bits_; }
84
Kind kind() const { return Kind(bits() & KindMask); }
85
uint32_t immediate() const {
86
MOZ_ASSERT(IsImmediateKind(kind()));
87
return mozilla::AssertedCast<uint32_t>(bits() >> PayloadShift);
88
}
89
PointerType* pointer() const {
90
MOZ_ASSERT(IsPointerKind(kind()));
91
return reinterpret_cast<PointerType*>(bits() & ~KindMask);
92
}
93
};
94
95
// ResultType represents the WebAssembly spec's `resulttype`. Semantically, a
96
// result type is just a vec(valtype). For effiency, though, the ResultType
97
// value is packed into a word, with separate encodings for these 3 cases:
98
// []
99
// [valtype]
100
// pointer to ValTypeVector
101
//
102
// Additionally there is an encoding indicating uninitialized ResultType
103
// values.
104
//
105
// Generally in the latter case the ValTypeVector is the args() or results() of
106
// a FuncType in the compilation unit, so as long as the lifetime of the
107
// ResultType value is less than the OpIter, we can just borrow the pointer
108
// without ownership or copying.
109
class ResultType {
110
using Tagged = TaggedValue<const ValTypeVector>;
111
Tagged tagged_;
112
113
enum Kind {
114
EmptyKind = Tagged::ImmediateKind1,
115
SingleKind = Tagged::ImmediateKind2,
116
#ifdef ENABLE_WASM_MULTI_VALUE
117
VectorKind = Tagged::PointerKind1,
118
#endif
119
InvalidKind = Tagged::PointerKind2,
120
};
121
122
ResultType(Kind kind, uint32_t imm) : tagged_(Tagged::Kind(kind), imm) {}
123
#ifdef ENABLE_WASM_MULTI_VALUE
124
explicit ResultType(const ValTypeVector* ptr)
125
: tagged_(Tagged::Kind(VectorKind), ptr) {}
126
#endif
127
128
Kind kind() const { return Kind(tagged_.kind()); }
129
130
ValType singleValType() const {
131
MOZ_ASSERT(kind() == SingleKind);
132
return ValType(PackedTypeCodeFromBits(tagged_.immediate()));
133
}
134
135
#ifdef ENABLE_WASM_MULTI_VALUE
136
const ValTypeVector& values() const {
137
MOZ_ASSERT(kind() == VectorKind);
138
return *tagged_.pointer();
139
}
140
#endif
141
142
public:
143
ResultType() : tagged_(Tagged::Kind(InvalidKind), nullptr) {}
144
145
static ResultType Empty() { return ResultType(EmptyKind, uint32_t(0)); }
146
static ResultType Single(ValType vt) {
147
return ResultType(SingleKind, vt.bitsUnsafe());
148
}
149
static ResultType Vector(const ValTypeVector& vals) {
150
switch (vals.length()) {
151
case 0:
152
return Empty();
153
case 1:
154
return Single(vals[0]);
155
default:
156
#ifdef ENABLE_WASM_MULTI_VALUE
157
return ResultType(&vals);
158
#else
159
MOZ_CRASH("multi-value returns not supported");
160
#endif
161
}
162
}
163
164
bool empty() const { return kind() == EmptyKind; }
165
166
size_t length() const {
167
switch (kind()) {
168
case EmptyKind:
169
return 0;
170
case SingleKind:
171
return 1;
172
#ifdef ENABLE_WASM_MULTI_VALUE
173
case VectorKind:
174
return values().length();
175
#endif
176
default:
177
MOZ_CRASH("bad resulttype");
178
}
179
}
180
181
ValType operator[](size_t i) const {
182
switch (kind()) {
183
case SingleKind:
184
MOZ_ASSERT(i == 0);
185
return singleValType();
186
#ifdef ENABLE_WASM_MULTI_VALUE
187
case VectorKind:
188
return values()[i];
189
#endif
190
default:
191
MOZ_CRASH("bad resulttype");
192
}
193
}
194
195
bool operator==(ResultType rhs) const {
196
switch (kind()) {
197
case EmptyKind:
198
case SingleKind:
199
case InvalidKind:
200
return tagged_.bits() == rhs.tagged_.bits();
201
#ifdef ENABLE_WASM_MULTI_VALUE
202
case VectorKind: {
203
if (rhs.kind() != VectorKind) {
204
return false;
205
}
206
return EqualContainers(values(), rhs.values());
207
}
208
#endif
209
default:
210
MOZ_CRASH("bad resulttype");
211
}
212
}
213
bool operator!=(ResultType rhs) const { return !(*this == rhs); }
214
};
215
216
// BlockType represents the WebAssembly spec's `blocktype`. Semantically, a
217
// block type is just a (vec(valtype) -> vec(valtype)) with four special
218
// encodings which are represented explicitly in BlockType:
219
// [] -> []
220
// [] -> [valtype]
221
// [params] -> [results] via pointer to FuncType
222
// [] -> [results] via pointer to FuncType (ignoring [params])
223
224
class BlockType {
225
using Tagged = TaggedValue<const FuncType>;
226
Tagged tagged_;
227
228
enum Kind {
229
VoidToVoidKind = Tagged::ImmediateKind1,
230
VoidToSingleKind = Tagged::ImmediateKind2,
231
#ifdef ENABLE_WASM_MULTI_VALUE
232
FuncKind = Tagged::PointerKind1,
233
FuncResultsKind = Tagged::PointerKind2
234
#endif
235
};
236
237
BlockType(Kind kind, uint32_t imm) : tagged_(Tagged::Kind(kind), imm) {}
238
#ifdef ENABLE_WASM_MULTI_VALUE
239
BlockType(Kind kind, const FuncType& type)
240
: tagged_(Tagged::Kind(kind), &type) {}
241
#endif
242
243
Kind kind() const { return Kind(tagged_.kind()); }
244
ValType singleValType() const {
245
MOZ_ASSERT(kind() == VoidToSingleKind);
246
return ValType(PackedTypeCodeFromBits(tagged_.immediate()));
247
}
248
249
#ifdef ENABLE_WASM_MULTI_VALUE
250
const FuncType& funcType() const { return *tagged_.pointer(); }
251
#endif
252
253
public:
254
BlockType()
255
: tagged_(Tagged::Kind(VoidToVoidKind),
256
uint32_t(InvalidPackedTypeCode())) {}
257
258
static BlockType VoidToVoid() {
259
return BlockType(VoidToVoidKind, uint32_t(0));
260
}
261
static BlockType VoidToSingle(ValType vt) {
262
return BlockType(VoidToSingleKind, vt.bitsUnsafe());
263
}
264
static BlockType Func(const FuncType& type) {
265
#ifdef ENABLE_WASM_MULTI_VALUE
266
if (type.args().length() == 0) {
267
return FuncResults(type);
268
}
269
return BlockType(FuncKind, type);
270
#else
271
MOZ_ASSERT(type.args().length() == 0);
272
return FuncResults(type);
273
#endif
274
}
275
static BlockType FuncResults(const FuncType& type) {
276
switch (type.results().length()) {
277
case 0:
278
return VoidToVoid();
279
case 1:
280
return VoidToSingle(type.results()[0]);
281
default:
282
#ifdef ENABLE_WASM_MULTI_VALUE
283
return BlockType(FuncResultsKind, type);
284
#else
285
MOZ_CRASH("multi-value returns not supported");
286
#endif
287
}
288
}
289
290
ResultType params() const {
291
switch (kind()) {
292
case VoidToVoidKind:
293
case VoidToSingleKind:
294
#ifdef ENABLE_WASM_MULTI_VALUE
295
case FuncResultsKind:
296
#endif
297
return ResultType::Empty();
298
#ifdef ENABLE_WASM_MULTI_VALUE
299
case FuncKind:
300
return ResultType::Vector(funcType().args());
301
#endif
302
default:
303
MOZ_CRASH("unexpected kind");
304
}
305
}
306
307
ResultType results() const {
308
switch (kind()) {
309
case VoidToVoidKind:
310
return ResultType::Empty();
311
case VoidToSingleKind:
312
return ResultType::Single(singleValType());
313
#ifdef ENABLE_WASM_MULTI_VALUE
314
case FuncKind:
315
case FuncResultsKind:
316
return ResultType::Vector(funcType().results());
317
#endif
318
default:
319
MOZ_CRASH("unexpected kind");
320
}
321
}
322
323
bool operator==(BlockType rhs) const {
324
if (kind() != rhs.kind()) {
325
return false;
326
}
327
switch (kind()) {
328
case VoidToVoidKind:
329
case VoidToSingleKind:
330
return tagged_.bits() == rhs.tagged_.bits();
331
#ifdef ENABLE_WASM_MULTI_VALUE
332
case FuncKind:
333
return funcType() == rhs.funcType();
334
case FuncResultsKind:
335
return EqualContainers(funcType().results(), rhs.funcType().results());
336
#endif
337
default:
338
MOZ_CRASH("unexpected kind");
339
}
340
}
341
342
bool operator!=(BlockType rhs) const { return !(*this == rhs); }
343
};
344
345
// The kind of a control-flow stack item.
346
enum class LabelKind : uint8_t { Body, Block, Loop, Then, Else };
347
348
// The type of values on the operand stack during validation. This is either a
349
// ValType or the special type "Bottom".
350
351
class StackType {
352
PackedTypeCode tc_;
353
354
explicit StackType(PackedTypeCode tc) : tc_(tc) {}
355
356
public:
357
StackType() : tc_(InvalidPackedTypeCode()) {}
358
359
explicit StackType(const ValType& t) : tc_(t.packed()) {
360
MOZ_ASSERT(IsValid(tc_));
361
MOZ_ASSERT(!isBottom());
362
}
363
364
static StackType bottom() { return StackType(PackTypeCode(TypeCode::Limit)); }
365
366
bool isBottom() const {
367
MOZ_ASSERT(IsValid(tc_));
368
return UnpackTypeCodeType(tc_) == TypeCode::Limit;
369
}
370
371
ValType valType() const {
372
MOZ_ASSERT(IsValid(tc_));
373
MOZ_ASSERT(!isBottom());
374
return ValType(tc_);
375
}
376
377
bool isValidForOldSelect() const {
378
MOZ_ASSERT(IsValid(tc_));
379
if (isBottom()) {
380
return true;
381
}
382
switch (valType().kind()) {
383
case ValType::I32:
384
case ValType::F32:
385
case ValType::I64:
386
case ValType::F64:
387
return true;
388
default:
389
return false;
390
}
391
}
392
393
bool operator==(const StackType& that) const {
394
MOZ_ASSERT(IsValid(tc_) && IsValid(that.tc_));
395
return tc_ == that.tc_;
396
}
397
398
bool operator!=(const StackType& that) const {
399
MOZ_ASSERT(IsValid(tc_) && IsValid(that.tc_));
400
return tc_ != that.tc_;
401
}
402
};
403
404
#ifdef DEBUG
405
// Families of opcodes that share a signature and validation logic.
406
enum class OpKind {
407
Block,
408
Loop,
409
Unreachable,
410
Drop,
411
I32,
412
I64,
413
F32,
414
F64,
415
Br,
416
BrIf,
417
BrTable,
418
Nop,
419
Unary,
420
Binary,
421
Comparison,
422
Conversion,
423
Load,
424
Store,
425
TeeStore,
426
MemorySize,
427
MemoryGrow,
428
Select,
429
GetLocal,
430
SetLocal,
431
TeeLocal,
432
GetGlobal,
433
SetGlobal,
434
TeeGlobal,
435
Call,
436
CallIndirect,
437
OldCallDirect,
438
OldCallIndirect,
439
Return,
440
If,
441
Else,
442
End,
443
Wait,
444
Wake,
445
Fence,
446
AtomicLoad,
447
AtomicStore,
448
AtomicBinOp,
449
AtomicCompareExchange,
450
OldAtomicLoad,
451
OldAtomicStore,
452
OldAtomicBinOp,
453
OldAtomicCompareExchange,
454
OldAtomicExchange,
455
ExtractLane,
456
ReplaceLane,
457
Swizzle,
458
Shuffle,
459
Splat,
460
MemOrTableCopy,
461
DataOrElemDrop,
462
MemFill,
463
MemOrTableInit,
464
TableFill,
465
TableGet,
466
TableGrow,
467
TableSet,
468
TableSize,
469
RefNull,
470
RefFunc,
471
StructNew,
472
StructGet,
473
StructSet,
474
StructNarrow,
475
};
476
477
// Return the OpKind for a given Op. This is used for sanity-checking that
478
// API users use the correct read function for a given Op.
479
OpKind Classify(OpBytes op);
480
#endif
481
482
// Common fields for linear memory access.
483
template <typename Value>
484
struct LinearMemoryAddress {
485
Value base;
486
uint32_t offset;
487
uint32_t align;
488
489
LinearMemoryAddress() : offset(0), align(0) {}
490
LinearMemoryAddress(Value base, uint32_t offset, uint32_t align)
491
: base(base), offset(offset), align(align) {}
492
};
493
494
template <typename ControlItem>
495
class ControlStackEntry {
496
// Use a pair to optimize away empty ControlItem.
497
mozilla::CompactPair<BlockType, ControlItem> typeAndItem_;
498
499
// The "base" of a control stack entry is valueStack_.length() minus
500
// type().params().length(), i.e., the size of the value stack "below"
501
// this block.
502
uint32_t valueStackBase_;
503
bool polymorphicBase_;
504
505
LabelKind kind_;
506
507
public:
508
ControlStackEntry(LabelKind kind, BlockType type, uint32_t valueStackBase)
509
: typeAndItem_(type, ControlItem()),
510
valueStackBase_(valueStackBase),
511
polymorphicBase_(false),
512
kind_(kind) {
513
MOZ_ASSERT(type != BlockType());
514
}
515
516
LabelKind kind() const { return kind_; }
517
BlockType type() const { return typeAndItem_.first(); }
518
ResultType resultType() const { return type().results(); }
519
ResultType branchTargetType() const {
520
return kind_ == LabelKind::Loop ? type().params() : type().results();
521
}
522
uint32_t valueStackBase() const { return valueStackBase_; }
523
ControlItem& controlItem() { return typeAndItem_.second(); }
524
void setPolymorphicBase() { polymorphicBase_ = true; }
525
bool polymorphicBase() const { return polymorphicBase_; }
526
527
void switchToElse() {
528
MOZ_ASSERT(kind() == LabelKind::Then);
529
kind_ = LabelKind::Else;
530
polymorphicBase_ = false;
531
}
532
};
533
534
template <typename Value>
535
class TypeAndValueT {
536
// Use a Pair to optimize away empty Value.
537
mozilla::CompactPair<StackType, Value> tv_;
538
539
public:
540
TypeAndValueT() : tv_(StackType::bottom(), Value()) {}
541
explicit TypeAndValueT(StackType type) : tv_(type, Value()) {}
542
explicit TypeAndValueT(ValType type) : tv_(StackType(type), Value()) {}
543
TypeAndValueT(StackType type, Value value) : tv_(type, value) {}
544
TypeAndValueT(ValType type, Value value) : tv_(StackType(type), value) {}
545
StackType type() const { return tv_.first(); }
546
StackType& typeRef() { return tv_.first(); }
547
Value value() const { return tv_.second(); }
548
void setValue(Value value) { tv_.second() = value; }
549
};
550
551
// An iterator over the bytes of a function body. It performs validation
552
// and unpacks the data into a usable form.
553
//
554
// The MOZ_STACK_CLASS attribute here is because of the use of DebugOnly.
555
// There's otherwise nothing inherent in this class which would require
556
// it to be used on the stack.
557
template <typename Policy>
558
class MOZ_STACK_CLASS OpIter : private Policy {
559
public:
560
using Value = typename Policy::Value;
561
using ValueVector = typename Policy::ValueVector;
562
using TypeAndValue = TypeAndValueT<Value>;
563
typedef Vector<TypeAndValue, 8, SystemAllocPolicy> TypeAndValueStack;
564
using ControlItem = typename Policy::ControlItem;
565
using Control = ControlStackEntry<ControlItem>;
566
typedef Vector<Control, 8, SystemAllocPolicy> ControlStack;
567
568
private:
569
Decoder& d_;
570
const ModuleEnvironment& env_;
571
572
TypeAndValueStack valueStack_;
573
TypeAndValueStack elseParamStack_;
574
ControlStack controlStack_;
575
576
#ifdef DEBUG
577
OpBytes op_;
578
#endif
579
size_t offsetOfLastReadOp_;
580
581
MOZ_MUST_USE bool readFixedU8(uint8_t* out) { return d_.readFixedU8(out); }
582
MOZ_MUST_USE bool readFixedU32(uint32_t* out) { return d_.readFixedU32(out); }
583
MOZ_MUST_USE bool readVarS32(int32_t* out) { return d_.readVarS32(out); }
584
MOZ_MUST_USE bool readVarU32(uint32_t* out) { return d_.readVarU32(out); }
585
MOZ_MUST_USE bool readVarS64(int64_t* out) { return d_.readVarS64(out); }
586
MOZ_MUST_USE bool readVarU64(uint64_t* out) { return d_.readVarU64(out); }
587
MOZ_MUST_USE bool readFixedF32(float* out) { return d_.readFixedF32(out); }
588
MOZ_MUST_USE bool readFixedF64(double* out) { return d_.readFixedF64(out); }
589
590
MOZ_MUST_USE bool readMemOrTableIndex(bool isMem, uint32_t* index);
591
MOZ_MUST_USE bool readLinearMemoryAddress(uint32_t byteSize,
592
LinearMemoryAddress<Value>* addr);
593
MOZ_MUST_USE bool readLinearMemoryAddressAligned(
594
uint32_t byteSize, LinearMemoryAddress<Value>* addr);
595
MOZ_MUST_USE bool readBlockType(BlockType* type);
596
MOZ_MUST_USE bool readStructTypeIndex(uint32_t* typeIndex);
597
MOZ_MUST_USE bool readFieldIndex(uint32_t* fieldIndex,
598
const StructType& structType);
599
600
MOZ_MUST_USE bool popCallArgs(const ValTypeVector& expectedTypes,
601
ValueVector* values);
602
603
MOZ_MUST_USE bool failEmptyStack();
604
MOZ_MUST_USE bool popStackType(StackType* type, Value* value);
605
MOZ_MUST_USE bool popWithType(ValType expected, Value* value);
606
MOZ_MUST_USE bool popWithType(ResultType expected, ValueVector* values);
607
MOZ_MUST_USE bool popThenPushType(ResultType expected, ValueVector* values);
608
MOZ_MUST_USE bool ensureTopHasType(ResultType expected, ValueVector* values);
609
610
MOZ_MUST_USE bool pushControl(LabelKind kind, BlockType type);
611
MOZ_MUST_USE bool checkStackAtEndOfBlock(ResultType* type,
612
ValueVector* values);
613
MOZ_MUST_USE bool getControl(uint32_t relativeDepth, Control** controlEntry);
614
MOZ_MUST_USE bool checkBranchValue(uint32_t relativeDepth, ResultType* type,
615
ValueVector* values);
616
MOZ_MUST_USE bool checkBrTableEntry(uint32_t* relativeDepth,
617
ResultType prevBranchType,
618
ResultType* branchType,
619
ValueVector* branchValues);
620
621
MOZ_MUST_USE bool push(ValType t) { return valueStack_.emplaceBack(t); }
622
MOZ_MUST_USE bool push(TypeAndValue tv) { return valueStack_.append(tv); }
623
MOZ_MUST_USE bool push(ResultType t) {
624
for (size_t i = 0; i < t.length(); i++) {
625
if (!push(t[i])) {
626
return false;
627
}
628
}
629
return true;
630
}
631
void infalliblePush(StackType t) { valueStack_.infallibleEmplaceBack(t); }
632
void infalliblePush(ValType t) {
633
valueStack_.infallibleEmplaceBack(StackType(t));
634
}
635
void infalliblePush(TypeAndValue tv) { valueStack_.infallibleAppend(tv); }
636
637
void afterUnconditionalBranch() {
638
valueStack_.shrinkTo(controlStack_.back().valueStackBase());
639
controlStack_.back().setPolymorphicBase();
640
}
641
642
inline bool checkIsSubtypeOf(ValType lhs, ValType rhs);
643
644
public:
645
#ifdef DEBUG
646
explicit OpIter(const ModuleEnvironment& env, Decoder& decoder)
647
: d_(decoder),
648
env_(env),
649
op_(OpBytes(Op::Limit)),
650
offsetOfLastReadOp_(0) {}
651
#else
652
explicit OpIter(const ModuleEnvironment& env, Decoder& decoder)
653
: d_(decoder), env_(env), offsetOfLastReadOp_(0) {}
654
#endif
655
656
// Return the decoding byte offset.
657
uint32_t currentOffset() const { return d_.currentOffset(); }
658
659
// Return the offset within the entire module of the last-read op.
660
size_t lastOpcodeOffset() const {
661
return offsetOfLastReadOp_ ? offsetOfLastReadOp_ : d_.currentOffset();
662
}
663
664
// Return a BytecodeOffset describing where the current op should be reported
665
// to trap/call.
666
BytecodeOffset bytecodeOffset() const {
667
return BytecodeOffset(lastOpcodeOffset());
668
}
669
670
// Test whether the iterator has reached the end of the buffer.
671
bool done() const { return d_.done(); }
672
673
// Return a pointer to the end of the buffer being decoded by this iterator.
674
const uint8_t* end() const { return d_.end(); }
675
676
// Report a general failure.
677
MOZ_MUST_USE bool fail(const char* msg) MOZ_COLD;
678
679
// Report a general failure with a context
680
MOZ_MUST_USE bool fail_ctx(const char* fmt, const char* context) MOZ_COLD;
681
682
// Report an unrecognized opcode.
683
MOZ_MUST_USE bool unrecognizedOpcode(const OpBytes* expr) MOZ_COLD;
684
685
// Return whether the innermost block has a polymorphic base of its stack.
686
// Ideally this accessor would be removed; consider using something else.
687
bool currentBlockHasPolymorphicBase() const {
688
return !controlStack_.empty() && controlStack_.back().polymorphicBase();
689
}
690
691
// ------------------------------------------------------------------------
692
// Decoding and validation interface.
693
694
MOZ_MUST_USE bool readOp(OpBytes* op);
695
MOZ_MUST_USE bool readFunctionStart(uint32_t funcIndex);
696
MOZ_MUST_USE bool readFunctionEnd(const uint8_t* bodyEnd);
697
MOZ_MUST_USE bool readReturn(ValueVector* values);
698
MOZ_MUST_USE bool readBlock(ResultType* paramType);
699
MOZ_MUST_USE bool readLoop(ResultType* paramType);
700
MOZ_MUST_USE bool readIf(ResultType* paramType, Value* condition);
701
MOZ_MUST_USE bool readElse(ResultType* paramType, ResultType* resultType,
702
ValueVector* thenResults);
703
MOZ_MUST_USE bool readEnd(LabelKind* kind, ResultType* type,
704
ValueVector* results,
705
ValueVector* resultsForEmptyElse);
706
void popEnd();
707
MOZ_MUST_USE bool readBr(uint32_t* relativeDepth, ResultType* type,
708
ValueVector* values);
709
MOZ_MUST_USE bool readBrIf(uint32_t* relativeDepth, ResultType* type,
710
ValueVector* values, Value* condition);
711
MOZ_MUST_USE bool readBrTable(Uint32Vector* depths, uint32_t* defaultDepth,
712
ResultType* defaultBranchValueType,
713
ValueVector* branchValues, Value* index);
714
MOZ_MUST_USE bool readUnreachable();
715
MOZ_MUST_USE bool readDrop();
716
MOZ_MUST_USE bool readUnary(ValType operandType, Value* input);
717
MOZ_MUST_USE bool readConversion(ValType operandType, ValType resultType,
718
Value* input);
719
MOZ_MUST_USE bool readBinary(ValType operandType, Value* lhs, Value* rhs);
720
MOZ_MUST_USE bool readComparison(ValType operandType, Value* lhs, Value* rhs);
721
MOZ_MUST_USE bool readLoad(ValType resultType, uint32_t byteSize,
722
LinearMemoryAddress<Value>* addr);
723
MOZ_MUST_USE bool readStore(ValType resultType, uint32_t byteSize,
724
LinearMemoryAddress<Value>* addr, Value* value);
725
MOZ_MUST_USE bool readTeeStore(ValType resultType, uint32_t byteSize,
726
LinearMemoryAddress<Value>* addr,
727
Value* value);
728
MOZ_MUST_USE bool readNop();
729
MOZ_MUST_USE bool readMemorySize();
730
MOZ_MUST_USE bool readMemoryGrow(Value* input);
731
MOZ_MUST_USE bool readSelect(bool typed, StackType* type, Value* trueValue,
732
Value* falseValue, Value* condition);
733
MOZ_MUST_USE bool readGetLocal(const ValTypeVector& locals, uint32_t* id);
734
MOZ_MUST_USE bool readSetLocal(const ValTypeVector& locals, uint32_t* id,
735
Value* value);
736
MOZ_MUST_USE bool readTeeLocal(const ValTypeVector& locals, uint32_t* id,
737
Value* value);
738
MOZ_MUST_USE bool readGetGlobal(uint32_t* id);
739
MOZ_MUST_USE bool readSetGlobal(uint32_t* id, Value* value);
740
MOZ_MUST_USE bool readTeeGlobal(uint32_t* id, Value* value);
741
MOZ_MUST_USE bool readI32Const(int32_t* i32);
742
MOZ_MUST_USE bool readI64Const(int64_t* i64);
743
MOZ_MUST_USE bool readF32Const(float* f32);
744
MOZ_MUST_USE bool readF64Const(double* f64);
745
MOZ_MUST_USE bool readRefFunc(uint32_t* funcTypeIndex);
746
MOZ_MUST_USE bool readRefNull();
747
MOZ_MUST_USE bool readCall(uint32_t* calleeIndex, ValueVector* argValues);
748
MOZ_MUST_USE bool readCallIndirect(uint32_t* funcTypeIndex,
749
uint32_t* tableIndex, Value* callee,
750
ValueVector* argValues);
751
MOZ_MUST_USE bool readOldCallDirect(uint32_t numFuncImports,
752
uint32_t* funcIndex,
753
ValueVector* argValues);
754
MOZ_MUST_USE bool readOldCallIndirect(uint32_t* funcTypeIndex, Value* callee,
755
ValueVector* argValues);
756
MOZ_MUST_USE bool readWake(LinearMemoryAddress<Value>* addr, Value* count);
757
MOZ_MUST_USE bool readWait(LinearMemoryAddress<Value>* addr,
758
ValType resultType, uint32_t byteSize,
759
Value* value, Value* timeout);
760
MOZ_MUST_USE bool readFence();
761
MOZ_MUST_USE bool readAtomicLoad(LinearMemoryAddress<Value>* addr,
762
ValType resultType, uint32_t byteSize);
763
MOZ_MUST_USE bool readAtomicStore(LinearMemoryAddress<Value>* addr,
764
ValType resultType, uint32_t byteSize,
765
Value* value);
766
MOZ_MUST_USE bool readAtomicRMW(LinearMemoryAddress<Value>* addr,
767
ValType resultType, uint32_t byteSize,
768
Value* value);
769
MOZ_MUST_USE bool readAtomicCmpXchg(LinearMemoryAddress<Value>* addr,
770
ValType resultType, uint32_t byteSize,
771
Value* oldValue, Value* newValue);
772
MOZ_MUST_USE bool readMemOrTableCopy(bool isMem, uint32_t* dstMemOrTableIndex,
773
Value* dst, uint32_t* srcMemOrTableIndex,
774
Value* src, Value* len);
775
MOZ_MUST_USE bool readDataOrElemDrop(bool isData, uint32_t* segIndex);
776
MOZ_MUST_USE bool readMemFill(Value* start, Value* val, Value* len);
777
MOZ_MUST_USE bool readMemOrTableInit(bool isMem, uint32_t* segIndex,
778
uint32_t* dstTableIndex, Value* dst,
779
Value* src, Value* len);
780
MOZ_MUST_USE bool readTableFill(uint32_t* tableIndex, Value* start,
781
Value* val, Value* len);
782
MOZ_MUST_USE bool readTableGet(uint32_t* tableIndex, Value* index);
783
MOZ_MUST_USE bool readTableGrow(uint32_t* tableIndex, Value* initValue,
784
Value* delta);
785
MOZ_MUST_USE bool readTableSet(uint32_t* tableIndex, Value* index,
786
Value* value);
787
MOZ_MUST_USE bool readTableSize(uint32_t* tableIndex);
788
MOZ_MUST_USE bool readStructNew(uint32_t* typeIndex, ValueVector* argValues);
789
MOZ_MUST_USE bool readStructGet(uint32_t* typeIndex, uint32_t* fieldIndex,
790
Value* ptr);
791
MOZ_MUST_USE bool readStructSet(uint32_t* typeIndex, uint32_t* fieldIndex,
792
Value* ptr, Value* val);
793
MOZ_MUST_USE bool readStructNarrow(ValType* inputType, ValType* outputType,
794
Value* ptr);
795
MOZ_MUST_USE bool readValType(ValType* type);
796
MOZ_MUST_USE bool readReferenceType(ValType* type, const char* const context);
797
798
// At a location where readOp is allowed, peek at the next opcode
799
// without consuming it or updating any internal state.
800
// Never fails: returns uint16_t(Op::Limit) in op->b0 if it can't read.
801
void peekOp(OpBytes* op);
802
803
// ------------------------------------------------------------------------
804
// Stack management.
805
806
// Set the top N result values.
807
void setResults(size_t count, const ValueVector& values) {
808
MOZ_ASSERT(valueStack_.length() >= count);
809
size_t base = valueStack_.length() - count;
810
for (size_t i = 0; i < count; i++) {
811
valueStack_[base + i].setValue(values[i]);
812
}
813
}
814
815
bool getResults(size_t count, ValueVector* values) {
816
MOZ_ASSERT(valueStack_.length() >= count);
817
if (!values->resize(count)) {
818
return false;
819
}
820
size_t base = valueStack_.length() - count;
821
for (size_t i = 0; i < count; i++) {
822
(*values)[i] = valueStack_[base + i].value();
823
}
824
return true;
825
}
826
827
// Set the result value of the current top-of-value-stack expression.
828
void setResult(Value value) { valueStack_.back().setValue(value); }
829
830
// Return the result value of the current top-of-value-stack expression.
831
Value getResult() { return valueStack_.back().value(); }
832
833
// Return a reference to the top of the control stack.
834
ControlItem& controlItem() { return controlStack_.back().controlItem(); }
835
836
// Return a reference to an element in the control stack.
837
ControlItem& controlItem(uint32_t relativeDepth) {
838
return controlStack_[controlStack_.length() - 1 - relativeDepth]
839
.controlItem();
840
}
841
842
// Return a reference to the outermost element on the control stack.
843
ControlItem& controlOutermost() { return controlStack_[0].controlItem(); }
844
845
// Test whether the control-stack is empty, meaning we've consumed the final
846
// end of the function body.
847
bool controlStackEmpty() const { return controlStack_.empty(); }
848
};
849
850
template <typename Policy>
851
inline bool OpIter<Policy>::checkIsSubtypeOf(ValType actual, ValType expected) {
852
if (actual == expected) {
853
return true;
854
}
855
856
if (actual.isReference() && expected.isReference() &&
857
env_.isRefSubtypeOf(actual, expected)) {
858
return true;
859
}
860
861
UniqueChars error(
862
JS_smprintf("type mismatch: expression has type %s but expected %s",
863
ToCString(actual), ToCString(expected)));
864
if (!error) {
865
return false;
866
}
867
868
return fail(error.get());
869
}
870
871
template <typename Policy>
872
inline bool OpIter<Policy>::unrecognizedOpcode(const OpBytes* expr) {
873
UniqueChars error(JS_smprintf("unrecognized opcode: %x %x", expr->b0,
874
IsPrefixByte(expr->b0) ? expr->b1 : 0));
875
if (!error) {
876
return false;
877
}
878
879
return fail(error.get());
880
}
881
882
template <typename Policy>
883
inline bool OpIter<Policy>::fail(const char* msg) {
884
return d_.fail(lastOpcodeOffset(), msg);
885
}
886
887
template <typename Policy>
888
inline bool OpIter<Policy>::fail_ctx(const char* fmt, const char* context) {
889
UniqueChars error(JS_smprintf(fmt, context));
890
if (!error) {
891
return false;
892
}
893
return fail(error.get());
894
}
895
896
template <typename Policy>
897
inline bool OpIter<Policy>::failEmptyStack() {
898
return valueStack_.empty() ? fail("popping value from empty stack")
899
: fail("popping value from outside block");
900
}
901
902
// This function pops exactly one value from the stack, yielding Bottom types in
903
// various cases and therefore making it the caller's responsibility to do the
904
// right thing for StackType::Bottom. Prefer (pop|top)WithType. This is an
905
// optimization for the super-common case where the caller is statically
906
// expecting the resulttype `[valtype]`.
907
template <typename Policy>
908
inline bool OpIter<Policy>::popStackType(StackType* type, Value* value) {
909
Control& block = controlStack_.back();
910
911
MOZ_ASSERT(valueStack_.length() >= block.valueStackBase());
912
if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackBase())) {
913
// If the base of this block's stack is polymorphic, then we can pop a
914
// dummy value of any type; it won't be used since we're in unreachable
915
// code.
916
if (block.polymorphicBase()) {
917
*type = StackType::bottom();
918
*value = Value();
919
920
// Maintain the invariant that, after a pop, there is always memory
921
// reserved to push a value infallibly.
922
return valueStack_.reserve(valueStack_.length() + 1);
923
}
924
925
return failEmptyStack();
926
}
927
928
TypeAndValue& tv = valueStack_.back();
929
*type = tv.type();
930
*value = tv.value();
931
valueStack_.popBack();
932
return true;
933
}
934
935
// This function pops exactly one value from the stack, checking that it has the
936
// expected type which can either be a specific value type or a type variable.
937
template <typename Policy>
938
inline bool OpIter<Policy>::popWithType(ValType expectedType, Value* value) {
939
StackType stackType;
940
if (!popStackType(&stackType, value)) {
941
return false;
942
}
943
944
return stackType.isBottom() ||
945
checkIsSubtypeOf(stackType.valType(), expectedType);
946
}
947
948
// Pops each of the given expected types (in reverse, because it's a stack).
949
template <typename Policy>
950
inline bool OpIter<Policy>::popWithType(ResultType expected,
951
ValueVector* values) {
952
size_t expectedLength = expected.length();
953
if (!values->resize(expectedLength)) {
954
return false;
955
}
956
for (size_t i = 0; i < expectedLength; i++) {
957
size_t reverseIndex = expectedLength - i - 1;
958
ValType expectedType = expected[reverseIndex];
959
Value* value = &(*values)[reverseIndex];
960
if (!popWithType(expectedType, value)) {
961
return false;
962
}
963
}
964
return true;
965
}
966
967
// This function is an optimization of the sequence:
968
// popWithType(ResultType, tmp)
969
// push(ResultType, tmp)
970
template <typename Policy>
971
inline bool OpIter<Policy>::popThenPushType(ResultType expected,
972
ValueVector* values) {
973
if (expected.empty()) {
974
return true;
975
}
976
977
Control& block = controlStack_.back();
978
979
size_t expectedLength = expected.length();
980
if (!values->resize(expectedLength)) {
981
return false;
982
}
983
984
for (size_t i = 0; i != expectedLength; i++) {
985
// We're iterating as-if we were popping each expected/actual type one by
986
// one, which means iterating the array of expected results backwards.
987
// The "current" value stack length refers to what the value stack length
988
// would have been if we were popping it.
989
size_t reverseIndex = expectedLength - i - 1;
990
ValType expectedType = expected[reverseIndex];
991
Value* value = &(*values)[reverseIndex];
992
size_t currentValueStackLength = valueStack_.length() - i;
993
994
MOZ_ASSERT(currentValueStackLength >= block.valueStackBase());
995
if (currentValueStackLength == block.valueStackBase()) {
996
if (!block.polymorphicBase()) {
997
return failEmptyStack();
998
}
999
1000
// If the base of this block's stack is polymorphic, then we can just
1001
// pull out as many fake values as we need to validate; they won't be used
1002
// since we're in unreachable code. We must however push these types on
1003
// the operand stack since they are now fixed by this constraint.
1004
if (!valueStack_.insert(valueStack_.begin() + currentValueStackLength,
1005
TypeAndValue(expectedType))) {
1006
return false;
1007
}
1008
1009
*value = Value();
1010
} else {
1011
TypeAndValue& observed = valueStack_[currentValueStackLength - 1];
1012
1013
if (observed.type().isBottom()) {
1014
observed.typeRef() = StackType(expectedType);
1015
*value = Value();
1016
} else {
1017
if (!checkIsSubtypeOf(observed.type().valType(), expectedType)) {
1018
return false;
1019
}
1020
1021
*value = observed.value();
1022
}
1023
}
1024
}
1025
return true;
1026
}
1027
1028
// This function checks that the top of the stack is a subtype of expected.
1029
// Like topWithType, it may insert synthetic StackType::Bottom entries if the
1030
// block's stack is polymorphic, which happens during unreachable code. However
1031
// unlike popThenPushType, it doesn't otherwise modify the value stack to update
1032
// stack types. Finally, ensureTopHasType allows passing |nullptr| as |values|
1033
// to avoid collecting values.
1034
1035
template <typename Policy>
1036
inline bool OpIter<Policy>::ensureTopHasType(ResultType expected,
1037
ValueVector* values) {
1038
if (expected.empty()) {
1039
return true;
1040
}
1041
1042
Control& block = controlStack_.back();
1043
1044
size_t expectedLength = expected.length();
1045
if (values && !values->resize(expectedLength)) {
1046
return false;
1047
}
1048
1049
for (size_t i = 0; i != expectedLength; i++) {
1050
// We're iterating as-if we were popping each expected/actual type one by
1051
// one, which means iterating the array of expected results backwards.
1052
// The "current" value stack length refers to what the value stack length
1053
// would have been if we were popping it.
1054
size_t reverseIndex = expectedLength - i - 1;
1055
ValType expectedType = expected[reverseIndex];
1056
auto collectValue = [&](const Value& v) {
1057
if (values) {
1058
(*values)[reverseIndex] = v;
1059
}
1060
};
1061
size_t currentValueStackLength = valueStack_.length() - i;
1062
1063
MOZ_ASSERT(currentValueStackLength >= block.valueStackBase());
1064
if (currentValueStackLength == block.valueStackBase()) {
1065
if (!block.polymorphicBase()) {
1066
return failEmptyStack();
1067
}
1068
1069
// Fill missing values with StackType::Bottom.
1070
if (!valueStack_.insert(valueStack_.begin() + currentValueStackLength,
1071
TypeAndValue(StackType::bottom()))) {
1072
return false;
1073
}
1074
1075
collectValue(Value());
1076
} else {
1077
TypeAndValue& observed = valueStack_[currentValueStackLength - 1];
1078
1079
if (observed.type().isBottom()) {
1080
collectValue(Value());
1081
} else {
1082
if (!checkIsSubtypeOf(observed.type().valType(), expectedType)) {
1083
return false;
1084
}
1085
1086
collectValue(observed.value());
1087
}
1088
}
1089
}
1090
1091
return true;
1092
}
1093
1094
template <typename Policy>
1095
inline bool OpIter<Policy>::pushControl(LabelKind kind, BlockType type) {
1096
ResultType paramType = type.params();
1097
1098
ValueVector values;
1099
if (!popThenPushType(paramType, &values)) {
1100
return false;
1101
}
1102
MOZ_ASSERT(valueStack_.length() >= paramType.length());
1103
uint32_t valueStackBase = valueStack_.length() - paramType.length();
1104
return controlStack_.emplaceBack(kind, type, valueStackBase);
1105
}
1106
1107
template <typename Policy>
1108
inline bool OpIter<Policy>::checkStackAtEndOfBlock(ResultType* expectedType,
1109
ValueVector* values) {
1110
Control& block = controlStack_.back();
1111
*expectedType = block.type().results();
1112
1113
MOZ_ASSERT(valueStack_.length() >= block.valueStackBase());
1114
if (expectedType->length() < valueStack_.length() - block.valueStackBase()) {
1115
return fail("unused values not explicitly dropped by end of block");
1116
}
1117
1118
return popThenPushType(*expectedType, values);
1119
}
1120
1121
template <typename Policy>
1122
inline bool OpIter<Policy>::getControl(uint32_t relativeDepth,
1123
Control** controlEntry) {
1124
if (relativeDepth >= controlStack_.length()) {
1125
return fail("branch depth exceeds current nesting level");
1126
}
1127
1128
*controlEntry = &controlStack_[controlStack_.length() - 1 - relativeDepth];
1129
return true;
1130
}
1131
1132
template <typename Policy>
1133
inline bool OpIter<Policy>::readBlockType(BlockType* type) {
1134
uint8_t nextByte;
1135
if (!d_.peekByte(&nextByte)) {
1136
return fail("unable to read block type");
1137
}
1138
1139
if (nextByte == uint8_t(TypeCode::BlockVoid)) {
1140
d_.uncheckedReadFixedU8();
1141
*type = BlockType::VoidToVoid();
1142
return true;
1143
}
1144
1145
if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
1146
ValType v;
1147
if (!readValType(&v)) {
1148
return false;
1149
}
1150
*type = BlockType::VoidToSingle(v);
1151
return true;
1152
}
1153
1154
#ifdef ENABLE_WASM_MULTI_VALUE
1155
if (!env_.multiValuesEnabled()) {
1156
return fail("invalid block type reference");
1157
}
1158
1159
int32_t x;
1160
if (!d_.readVarS32(&x) || x < 0 || uint32_t(x) >= env_.types.length()) {
1161
return fail("invalid block type type index");
1162
}
1163
1164
if (!env_.types[x].isFuncType()) {
1165
return fail("block type type index must be func type");
1166
}
1167
1168
*type = BlockType::Func(env_.types[x].funcType());
1169
1170
return true;
1171
#else
1172
return fail("invalid block type reference");
1173
#endif
1174
}
1175
1176
template <typename Policy>
1177
inline bool OpIter<Policy>::readOp(OpBytes* op) {
1178
MOZ_ASSERT(!controlStack_.empty());
1179
1180
offsetOfLastReadOp_ = d_.currentOffset();
1181
1182
if (MOZ_UNLIKELY(!d_.readOp(op))) {
1183
return fail("unable to read opcode");
1184
}
1185
1186
#ifdef DEBUG
1187
op_ = *op;
1188
#endif
1189
1190
return true;
1191
}
1192
1193
template <typename Policy>
1194
inline void OpIter<Policy>::peekOp(OpBytes* op) {
1195
const uint8_t* pos = d_.currentPosition();
1196
1197
if (MOZ_UNLIKELY(!d_.readOp(op))) {
1198
op->b0 = uint16_t(Op::Limit);
1199
}
1200
1201
d_.rollbackPosition(pos);
1202
}
1203
1204
template <typename Policy>
1205
inline bool OpIter<Policy>::readFunctionStart(uint32_t funcIndex) {
1206
MOZ_ASSERT(elseParamStack_.empty());
1207
MOZ_ASSERT(valueStack_.empty());
1208
MOZ_ASSERT(controlStack_.empty());
1209
MOZ_ASSERT(op_.b0 == uint16_t(Op::Limit));
1210
BlockType type = BlockType::FuncResults(*env_.funcTypes[funcIndex]);
1211
return pushControl(LabelKind::Body, type);
1212
}
1213
1214
template <typename Policy>
1215
inline bool OpIter<Policy>::readFunctionEnd(const uint8_t* bodyEnd) {
1216
if (d_.currentPosition() != bodyEnd) {
1217
return fail("function body length mismatch");
1218
}
1219
1220
if (!controlStack_.empty()) {
1221
return fail("unbalanced function body control flow");
1222
}
1223
MOZ_ASSERT(elseParamStack_.empty());
1224
1225
#ifdef DEBUG
1226
op_ = OpBytes(Op::Limit);
1227
#endif
1228
valueStack_.clear();
1229
return true;
1230
}
1231
1232
template <typename Policy>
1233
inline bool OpIter<Policy>::readReturn(ValueVector* values) {
1234
MOZ_ASSERT(Classify(op_) == OpKind::Return);
1235
1236
Control& body = controlStack_[0];
1237
MOZ_ASSERT(body.kind() == LabelKind::Body);
1238
1239
if (!popWithType(body.resultType(), values)) {
1240
return false;
1241
}
1242
1243
afterUnconditionalBranch();
1244
return true;
1245
}
1246
1247
template <typename Policy>
1248
inline bool OpIter<Policy>::readBlock(ResultType* paramType) {
1249
MOZ_ASSERT(Classify(op_) == OpKind::Block);
1250
1251
BlockType type;
1252
if (!readBlockType(&type)) {
1253
return false;
1254
}
1255
1256
*paramType = type.params();
1257
return pushControl(LabelKind::Block, type);
1258
}
1259
1260
template <typename Policy>
1261
inline bool OpIter<Policy>::readLoop(ResultType* paramType) {
1262
MOZ_ASSERT(Classify(op_) == OpKind::Loop);
1263
1264
BlockType type;
1265
if (!readBlockType(&type)) {
1266
return false;
1267
}
1268
1269
*paramType = type.params();
1270
return pushControl(LabelKind::Loop, type);
1271
}
1272
1273
template <typename Policy>
1274
inline bool OpIter<Policy>::readIf(ResultType* paramType, Value* condition) {
1275
MOZ_ASSERT(Classify(op_) == OpKind::If);
1276
1277
BlockType type;
1278
if (!readBlockType(&type)) {
1279
return false;
1280
}
1281
1282
if (!popWithType(ValType::I32, condition)) {
1283
return false;
1284
}
1285
1286
if (!pushControl(LabelKind::Then, type)) {
1287
return false;
1288
}
1289
1290
*paramType = type.params();
1291
size_t paramsLength = type.params().length();
1292
return elseParamStack_.append(valueStack_.end() - paramsLength, paramsLength);
1293
}
1294
1295
template <typename Policy>
1296
inline bool OpIter<Policy>::readElse(ResultType* paramType,
1297
ResultType* resultType,
1298
ValueVector* thenResults) {
1299
MOZ_ASSERT(Classify(op_) == OpKind::Else);
1300
1301
Control& block = controlStack_.back();
1302
if (block.kind() != LabelKind::Then) {
1303
return fail("else can only be used within an if");
1304
}
1305
1306
*paramType = block.type().params();
1307
if (!checkStackAtEndOfBlock(resultType, thenResults)) {
1308
return false;
1309
}
1310
1311
valueStack_.shrinkTo(block.valueStackBase());
1312
1313
size_t nparams = block.type().params().length();
1314
MOZ_ASSERT(elseParamStack_.length() >= nparams);
1315
valueStack_.infallibleAppend(elseParamStack_.end() - nparams, nparams);
1316
elseParamStack_.shrinkBy(nparams);
1317
1318
block.switchToElse();
1319
return true;
1320
}
1321
1322
template <typename Policy>
1323
inline bool OpIter<Policy>::readEnd(LabelKind* kind, ResultType* type,
1324
ValueVector* results,
1325
ValueVector* resultsForEmptyElse) {
1326
MOZ_ASSERT(Classify(op_) == OpKind::End);
1327
1328
if (!checkStackAtEndOfBlock(type, results)) {
1329
return false;
1330
}
1331
1332
Control& block = controlStack_.back();
1333
1334
if (block.kind() == LabelKind::Then) {
1335
ResultType params = block.type().params();
1336
// If an `if` block ends with `end` instead of `else`, then the `else` block
1337
// implicitly passes the `if` parameters as the `else` results. In that
1338
// case, assert that the `if`'s param type matches the result type.
1339
if (params != block.type().results()) {
1340
return fail("if without else with a result value");
1341
}
1342
1343
size_t nparams = params.length();
1344
MOZ_ASSERT(elseParamStack_.length() >= nparams);
1345
if (!resultsForEmptyElse->resize(nparams)) {
1346
return false;
1347
}
1348
const TypeAndValue* elseParams = elseParamStack_.end() - nparams;
1349
for (size_t i = 0; i < nparams; i++) {
1350
(*resultsForEmptyElse)[i] = elseParams[i].value();
1351
}
1352
elseParamStack_.shrinkBy(nparams);
1353
}
1354
1355
*kind = block.kind();
1356
return true;
1357
}
1358
1359
template <typename Policy>
1360
inline void OpIter<Policy>::popEnd() {
1361
MOZ_ASSERT(Classify(op_) == OpKind::End);
1362
1363
controlStack_.popBack();
1364
}
1365
1366
template <typename Policy>
1367
inline bool OpIter<Policy>::checkBranchValue(uint32_t relativeDepth,
1368
ResultType* type,
1369
ValueVector* values) {
1370
Control* block = nullptr;
1371
if (!getControl(relativeDepth, &block)) {
1372
return false;
1373
}
1374
1375
*type = block->branchTargetType();
1376
return popThenPushType(*type, values);
1377
}
1378
1379
template <typename Policy>
1380
inline bool OpIter<Policy>::readBr(uint32_t* relativeDepth, ResultType* type,
1381
ValueVector* values) {
1382
MOZ_ASSERT(Classify(op_) == OpKind::Br);
1383
1384
if (!readVarU32(relativeDepth)) {
1385
return fail("unable to read br depth");
1386
}
1387
1388
if (!checkBranchValue(*relativeDepth, type, values)) {
1389
return false;
1390
}
1391
1392
afterUnconditionalBranch();
1393
return true;
1394
}
1395
1396
template <typename Policy>
1397
inline bool OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ResultType* type,
1398
ValueVector* values, Value* condition) {
1399
MOZ_ASSERT(Classify(op_) == OpKind::BrIf);
1400
1401
if (!readVarU32(relativeDepth)) {
1402
return fail("unable to read br_if depth");
1403
}
1404
1405
if (!popWithType(ValType::I32, condition)) {
1406
return false;
1407
}
1408
1409
return checkBranchValue(*relativeDepth, type, values);
1410
}
1411
1412
#define UNKNOWN_ARITY UINT32_MAX
1413
1414
template <typename Policy>
1415
inline bool OpIter<Policy>::checkBrTableEntry(uint32_t* relativeDepth,
1416
ResultType prevType,
1417
ResultType* type,
1418
ValueVector* branchValues) {
1419
if (!readVarU32(relativeDepth)) {
1420
return fail("unable to read br_table depth");
1421
}
1422
1423
Control* block = nullptr;
1424
if (!getControl(*relativeDepth, &block)) {
1425
return false;
1426
}
1427
1428
*type = block->branchTargetType();
1429
1430
if (prevType != ResultType()) {
1431
if (prevType.length() != type->length()) {
1432
return fail("br_table targets must all have the same arity");
1433
}
1434
1435
// Avoid re-collecting the same values for subsequent branch targets.
1436
branchValues = nullptr;
1437
}
1438
1439
return ensureTopHasType(*type, branchValues);
1440
}
1441
1442
template <typename Policy>
1443
inline bool OpIter<Policy>::readBrTable(Uint32Vector* depths,
1444
uint32_t* defaultDepth,
1445
ResultType* defaultBranchType,
1446
ValueVector* branchValues,
1447
Value* index) {
1448
MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
1449
1450
uint32_t tableLength;
1451
if (!readVarU32(&tableLength)) {
1452
return fail("unable to read br_table table length");
1453
}
1454
1455
if (tableLength > MaxBrTableElems) {
1456
return fail("br_table too big");
1457
}
1458
1459
if (!popWithType(ValType::I32, index)) {
1460
return false;
1461
}
1462
1463
if (!depths->resize(tableLength)) {
1464
return false;
1465
}
1466
1467
ResultType prevBranchType;
1468
for (uint32_t i = 0; i < tableLength; i++) {
1469
ResultType branchType;
1470
if (!checkBrTableEntry(&(*depths)[i], prevBranchType, &branchType,
1471
branchValues)) {
1472
return false;
1473
}
1474
prevBranchType = branchType;
1475
}
1476
1477
if (!checkBrTableEntry(defaultDepth, prevBranchType, defaultBranchType,
1478
branchValues)) {
1479
return false;
1480
}
1481
1482
MOZ_ASSERT(*defaultBranchType != ResultType());
1483
1484
afterUnconditionalBranch();
1485
return true;
1486
}
1487
1488
#undef UNKNOWN_ARITY
1489
1490
template <typename Policy>
1491
inline bool OpIter<Policy>::readUnreachable() {
1492
MOZ_ASSERT(Classify(op_) == OpKind::Unreachable);
1493
1494
afterUnconditionalBranch();
1495
return true;
1496
}
1497
1498
template <typename Policy>
1499
inline bool OpIter<Policy>::readDrop() {
1500
MOZ_ASSERT(Classify(op_) == OpKind::Drop);
1501
StackType type;
1502
Value value;
1503
return popStackType(&type, &value);
1504
}
1505
1506
template <typename Policy>
1507
inline bool OpIter<Policy>::readUnary(ValType operandType, Value* input) {
1508
MOZ_ASSERT(Classify(op_) == OpKind::Unary);
1509
1510
if (!popWithType(operandType, input)) {
1511
return false;
1512
}
1513
1514
infalliblePush(operandType);
1515
1516
return true;
1517
}
1518
1519
template <typename Policy>
1520
inline bool OpIter<Policy>::readConversion(ValType operandType,
1521
ValType resultType, Value* input) {
1522
MOZ_ASSERT(Classify(op_) == OpKind::Conversion);
1523
1524
if (!popWithType(operandType, input)) {
1525
return false;
1526
}
1527
1528
infalliblePush(resultType);
1529
1530
return true;
1531
}
1532
1533
template <typename Policy>
1534
inline bool OpIter<Policy>::readBinary(ValType operandType, Value* lhs,
1535
Value* rhs) {
1536
MOZ_ASSERT(Classify(op_) == OpKind::Binary);
1537
1538
if (!popWithType(operandType, rhs)) {
1539
return false;
1540
}
1541
1542
if (!popWithType(operandType, lhs)) {
1543
return false;
1544
}
1545
1546
infalliblePush(operandType);
1547
1548
return true;
1549
}
1550
1551
template <typename Policy>
1552
inline bool OpIter<Policy>::readComparison(ValType operandType, Value* lhs,
1553
Value* rhs) {
1554
MOZ_ASSERT(Classify(op_) == OpKind::Comparison);
1555
1556
if (!popWithType(operandType, rhs)) {
1557
return false;
1558
}
1559
1560
if (!popWithType(operandType, lhs)) {
1561
return false;
1562
}
1563
1564
infalliblePush(ValType::I32);
1565
1566
return true;
1567
}
1568
1569
// For memories, the index is currently always a placeholder zero byte.
1570
//
1571
// For tables, the index is a placeholder zero byte until we get multi-table
1572
// with the reftypes proposal.
1573
//
1574
// The zero-ness of the value must be checked by the caller.
1575
template <typename Policy>
1576
inline bool OpIter<Policy>::readMemOrTableIndex(bool isMem, uint32_t* index) {
1577
#ifdef ENABLE_WASM_REFTYPES
1578
bool readByte = isMem;
1579
#else
1580
bool readByte = true;
1581
#endif
1582
if (readByte) {
1583
uint8_t indexTmp;
1584
if (!readFixedU8(&indexTmp)) {
1585
return fail("unable to read memory or table index");
1586
}
1587
*index = indexTmp;
1588
} else {
1589
if (!readVarU32(index)) {
1590
return fail("unable to read memory or table index");
1591
}
1592
}
1593
return true;
1594
}
1595
1596
template <typename Policy>
1597
inline bool OpIter<Policy>::readLinearMemoryAddress(
1598
uint32_t byteSize, LinearMemoryAddress<Value>* addr) {
1599
if (!env_.usesMemory()) {
1600
return fail("can't touch memory without memory");
1601
}
1602
1603
uint8_t alignLog2;
1604
if (!readFixedU8(&alignLog2)) {
1605
return fail("unable to read load alignment");
1606
}
1607
1608
if (!readVarU32(&addr->offset)) {
1609
return fail("unable to read load offset");
1610
}
1611
1612
if (alignLog2 >= 32 || (uint32_t(1) << alignLog2) > byteSize) {
1613
return fail("greater than natural alignment");
1614
}
1615
1616
if (!popWithType(ValType::I32, &addr->base)) {
1617
return false;
1618
}
1619
1620
addr->align = uint32_t(1) << alignLog2;
1621
return true;
1622
}
1623
1624
template <typename Policy>
1625
inline bool OpIter<Policy>::readLinearMemoryAddressAligned(
1626
uint32_t byteSize, LinearMemoryAddress<Value>* addr) {
1627
if (!readLinearMemoryAddress(byteSize, addr)) {
1628
return false;
1629
}
1630
1631
if (addr->align != byteSize) {
1632
return fail("not natural alignment");
1633
}
1634
1635
return true;
1636
}
1637
1638
template <typename Policy>
1639
inline bool OpIter<Policy>::readLoad(ValType resultType, uint32_t byteSize,
1640
LinearMemoryAddress<Value>* addr) {
1641
MOZ_ASSERT(Classify(op_) == OpKind::Load);
1642
1643
if (!readLinearMemoryAddress(byteSize, addr)) {
1644
return false;
1645
}
1646
1647
infalliblePush(resultType);
1648
1649
return true;
1650
}
1651
1652
template <typename Policy>
1653
inline bool OpIter<Policy>::readStore(ValType resultType, uint32_t byteSize,
1654
LinearMemoryAddress<Value>* addr,
1655
Value* value) {
1656
MOZ_ASSERT(Classify(op_) == OpKind::Store);
1657
1658
if (!popWithType(resultType, value)) {
1659
return false;
1660
}
1661
1662
if (!readLinearMemoryAddress(byteSize, addr)) {
1663
return false;
1664
}
1665
1666
return true;
1667
}
1668
1669
template <typename Policy>
1670
inline bool OpIter<Policy>::readTeeStore(ValType resultType, uint32_t byteSize,
1671
LinearMemoryAddress<Value>* addr,
1672
Value* value) {
1673
MOZ_ASSERT(Classify(op_) == OpKind::TeeStore);
1674
1675
if (!popWithType(resultType, value)) {
1676
return false;
1677
}
1678
1679
if (!readLinearMemoryAddress(byteSize, addr)) {
1680
return false;
1681
}
1682
1683
infalliblePush(TypeAndValue(resultType, *value));
1684
return true;
1685
}
1686
1687
template <typename Policy>
1688
inline bool OpIter<Policy>::readNop() {
1689
MOZ_ASSERT(Classify(op_) == OpKind::Nop);
1690
1691
return true;
1692
}
1693
1694
template <typename Policy>
1695
inline bool OpIter<Policy>::readMemorySize() {
1696
MOZ_ASSERT(Classify(op_) == OpKind::MemorySize);
1697
1698
if (!env_.usesMemory()) {
1699
return fail("can't touch memory without memory");
1700
}
1701
1702
uint8_t flags;
1703
if (!readFixedU8(&flags)) {
1704
return fail("failed to read memory flags");
1705
}
1706
1707
if (flags != uint8_t(MemoryTableFlags::Default)) {
1708
return fail("unexpected flags");
1709
}
1710
1711
return push(ValType::I32);
1712
}
1713
1714
template <typename Policy>
1715
inline bool OpIter<Policy>::readMemoryGrow(Value* input) {
1716
MOZ_ASSERT(Classify(op_) == OpKind::MemoryGrow);
1717
1718
if (!env_.usesMemory()) {
1719
return fail("can't touch memory without memory");
1720
}
1721
1722
uint8_t flags;
1723
if (!readFixedU8(&flags)) {
1724
return fail("failed to read memory flags");
1725
}
1726
1727
if (flags != uint8_t(MemoryTableFlags::Default)) {
1728
return fail("unexpected flags");
1729
}
1730
1731
if (!popWithType(ValType::I32, input)) {
1732
return false;
1733
}
1734
1735
infalliblePush(ValType::I32);
1736
1737
return true;
1738
}
1739
1740
template <typename Policy>
1741
inline bool OpIter<Policy>::readSelect(bool typed, StackType* type,
1742
Value* trueValue, Value* falseValue,
1743
Value* condition) {
1744
MOZ_ASSERT(Classify(op_) == OpKind::Select);
1745
1746
if (typed) {
1747
uint32_t length;
1748
if (!readVarU32(&length)) {
1749
return fail("unable to read select result length");
1750
}
1751
if (length != 1) {
1752
return fail("bad number of results");
1753
}
1754
ValType result;
1755
if (!readValType(&result)) {
1756
return fail("invalid result type for select");
1757
}
1758
1759
if (!popWithType(ValType::I32, condition)) {
1760
return false;
1761
}
1762
if (!popWithType(result, falseValue)) {
1763
return false;
1764
}