Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: src/wasm/function-body-decoder.cc

Issue 2707203003: Merged: Squashed multiple commits. (Closed)
Patch Set: TypeCheckFallThru() on end. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/asmjs/asm-wasm-builder.cc ('k') | src/wasm/wasm-opcodes.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/signature.h" 5 #include "src/signature.h"
6 6
7 #include "src/bit-vector.h" 7 #include "src/bit-vector.h"
8 #include "src/flags.h" 8 #include "src/flags.h"
9 #include "src/handles.h" 9 #include "src/handles.h"
10 #include "src/zone/zone-containers.h" 10 #include "src/zone/zone-containers.h"
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 explicit TryInfo(SsaEnv* c) : catch_env(c), exception(nullptr) {} 78 explicit TryInfo(SsaEnv* c) : catch_env(c), exception(nullptr) {}
79 }; 79 };
80 80
81 struct MergeValues { 81 struct MergeValues {
82 uint32_t arity; 82 uint32_t arity;
83 union { 83 union {
84 Value* array; 84 Value* array;
85 Value first; 85 Value first;
86 } vals; // Either multiple values or a single value. 86 } vals; // Either multiple values or a single value.
87 87
88 Value& first() { 88 Value& operator[](size_t i) {
89 DCHECK_GT(arity, 0); 89 DCHECK_GT(arity, i);
90 return arity == 1 ? vals.first : vals.array[0]; 90 return arity == 1 ? vals.first : vals.array[i];
91 } 91 }
92 }; 92 };
93 93
94 static Value* NO_VALUE = nullptr; 94 static Value* NO_VALUE = nullptr;
95 95
96 enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry }; 96 enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry };
97 97
98 // An entry on the control stack (i.e. if, block, loop). 98 // An entry on the control stack (i.e. if, block, loop).
99 struct Control { 99 struct Control {
100 const byte* pc; 100 const byte* pc;
101 ControlKind kind; 101 ControlKind kind;
102 int stack_depth; // stack height at the beginning of the construct. 102 size_t stack_depth; // stack height at the beginning of the construct.
103 SsaEnv* end_env; // end environment for the construct. 103 SsaEnv* end_env; // end environment for the construct.
104 SsaEnv* false_env; // false environment (only for if). 104 SsaEnv* false_env; // false environment (only for if).
105 TryInfo* try_info; // Information used for compiling try statements. 105 TryInfo* try_info; // Information used for compiling try statements.
106 int32_t previous_catch; // The previous Control (on the stack) with a catch. 106 int32_t previous_catch; // The previous Control (on the stack) with a catch.
107 bool unreachable; // The current block has been ended.
107 108
108 // Values merged into the end of this control construct. 109 // Values merged into the end of this control construct.
109 MergeValues merge; 110 MergeValues merge;
110 111
111 inline bool is_if() const { return kind == kControlIf; } 112 inline bool is_if() const { return kind == kControlIf; }
112 inline bool is_block() const { return kind == kControlBlock; } 113 inline bool is_block() const { return kind == kControlBlock; }
113 inline bool is_loop() const { return kind == kControlLoop; } 114 inline bool is_loop() const { return kind == kControlLoop; }
114 inline bool is_try() const { return kind == kControlTry; } 115 inline bool is_try() const { return kind == kControlTry; }
115 116
116 // Named constructors. 117 // Named constructors.
117 static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env, 118 static Control Block(const byte* pc, size_t stack_depth, SsaEnv* end_env,
118 int32_t previous_catch) { 119 int32_t previous_catch) {
119 return {pc, kControlBlock, stack_depth, end_env, 120 return {pc, kControlBlock, stack_depth, end_env, nullptr,
120 nullptr, nullptr, previous_catch, {0, {NO_VALUE}}}; 121 nullptr, previous_catch, false, {0, {NO_VALUE}}};
121 } 122 }
122 123
123 static Control If(const byte* pc, int stack_depth, SsaEnv* end_env, 124 static Control If(const byte* pc, size_t stack_depth, SsaEnv* end_env,
124 SsaEnv* false_env, int32_t previous_catch) { 125 SsaEnv* false_env, int32_t previous_catch) {
125 return {pc, kControlIf, stack_depth, end_env, 126 return {pc, kControlIf, stack_depth, end_env, false_env,
126 false_env, nullptr, previous_catch, {0, {NO_VALUE}}}; 127 nullptr, previous_catch, false, {0, {NO_VALUE}}};
127 } 128 }
128 129
129 static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env, 130 static Control Loop(const byte* pc, size_t stack_depth, SsaEnv* end_env,
130 int32_t previous_catch) { 131 int32_t previous_catch) {
131 return {pc, kControlLoop, stack_depth, end_env, 132 return {pc, kControlLoop, stack_depth, end_env, nullptr,
132 nullptr, nullptr, previous_catch, {0, {NO_VALUE}}}; 133 nullptr, previous_catch, false, {0, {NO_VALUE}}};
133 } 134 }
134 135
135 static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env, 136 static Control Try(const byte* pc, size_t stack_depth, SsaEnv* end_env,
136 Zone* zone, SsaEnv* catch_env, int32_t previous_catch) { 137 Zone* zone, SsaEnv* catch_env, int32_t previous_catch) {
137 DCHECK_NOT_NULL(catch_env); 138 DCHECK_NOT_NULL(catch_env);
138 TryInfo* try_info = new (zone) TryInfo(catch_env); 139 TryInfo* try_info = new (zone) TryInfo(catch_env);
139 return {pc, kControlTry, stack_depth, end_env, 140 return {pc, kControlTry, stack_depth, end_env, nullptr,
140 nullptr, try_info, previous_catch, {0, {NO_VALUE}}}; 141 try_info, previous_catch, false, {0, {NO_VALUE}}};
141 } 142 }
142 }; 143 };
143 144
144 // Macros that build nodes only if there is a graph and the current SSA 145 // Macros that build nodes only if there is a graph and the current SSA
145 // environment is reachable from start. This avoids problems with malformed 146 // environment is reachable from start. This avoids problems with malformed
146 // TF graphs when decoding inputs that have unreachable code. 147 // TF graphs when decoding inputs that have unreachable code.
147 #define BUILD(func, ...) \ 148 #define BUILD(func, ...) \
148 (build() ? CheckForException(builder_->func(__VA_ARGS__)) : nullptr) 149 (build() ? CheckForException(builder_->func(__VA_ARGS__)) : nullptr)
149 #define BUILD0(func) (build() ? CheckForException(builder_->func()) : nullptr) 150 #define BUILD0(func) (build() ? CheckForException(builder_->func()) : nullptr)
150 151
(...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after
647 if (c->merge.arity == 1) { 648 if (c->merge.arity == 1) {
648 c->merge.vals.first = {pc_, nullptr, sig_->GetReturn(0)}; 649 c->merge.vals.first = {pc_, nullptr, sig_->GetReturn(0)};
649 } else if (c->merge.arity > 1) { 650 } else if (c->merge.arity > 1) {
650 c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity); 651 c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity);
651 for (unsigned i = 0; i < c->merge.arity; i++) { 652 for (unsigned i = 0; i < c->merge.arity; i++) {
652 c->merge.vals.array[i] = {pc_, nullptr, sig_->GetReturn(i)}; 653 c->merge.vals.array[i] = {pc_, nullptr, sig_->GetReturn(i)};
653 } 654 }
654 } 655 }
655 } 656 }
656 657
657 if (pc_ >= end_) return; // Nothing to do. 658 while (pc_ < end_) { // decoding loop.
658
659 while (true) { // decoding loop.
660 unsigned len = 1; 659 unsigned len = 1;
661 WasmOpcode opcode = static_cast<WasmOpcode>(*pc_); 660 WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
662 if (!WasmOpcodes::IsPrefixOpcode(opcode)) { 661 if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
663 TRACE(" @%-8d #%02x:%-20s|", startrel(pc_), opcode, 662 TRACE(" @%-8d #%02x:%-20s|", startrel(pc_), opcode,
664 WasmOpcodes::ShortOpcodeName(opcode)); 663 WasmOpcodes::ShortOpcodeName(opcode));
665 } 664 }
666 665
667 FunctionSig* sig = WasmOpcodes::Signature(opcode); 666 FunctionSig* sig = WasmOpcodes::Signature(opcode);
668 if (sig) { 667 if (sig) {
669 BuildSimpleOperator(opcode, sig); 668 BuildSimpleOperator(opcode, sig);
670 } else { 669 } else {
671 // Complex bytecode. 670 // Complex bytecode.
672 switch (opcode) { 671 switch (opcode) {
673 case kExprNop: 672 case kExprNop:
674 break; 673 break;
675 case kExprBlock: { 674 case kExprBlock: {
676 // The break environment is the outer environment. 675 // The break environment is the outer environment.
677 BlockTypeOperand operand(this, pc_); 676 BlockTypeOperand operand(this, pc_);
678 SsaEnv* break_env = ssa_env_; 677 SsaEnv* break_env = ssa_env_;
679 PushBlock(break_env); 678 PushBlock(break_env);
680 SetEnv("block:start", Steal(break_env)); 679 SetEnv("block:start", Steal(break_env));
681 SetBlockType(&control_.back(), operand); 680 SetBlockType(&control_.back(), operand);
682 len = 1 + operand.length; 681 len = 1 + operand.length;
683 break; 682 break;
684 } 683 }
685 case kExprThrow: { 684 case kExprThrow: {
686 CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); 685 CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
687 Value value = Pop(0, kWasmI32); 686 Value value = Pop(0, kWasmI32);
688 BUILD(Throw, value.node); 687 BUILD(Throw, value.node);
688 // TODO(titzer): Throw should end control, but currently we build a
689 // (reachable) runtime call instead of connecting it directly to
690 // end.
691 // EndControl();
689 break; 692 break;
690 } 693 }
691 case kExprTry: { 694 case kExprTry: {
692 CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); 695 CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
693 BlockTypeOperand operand(this, pc_); 696 BlockTypeOperand operand(this, pc_);
694 SsaEnv* outer_env = ssa_env_; 697 SsaEnv* outer_env = ssa_env_;
695 SsaEnv* try_env = Steal(outer_env); 698 SsaEnv* try_env = Steal(outer_env);
696 SsaEnv* catch_env = UnreachableEnv(); 699 SsaEnv* catch_env = UnreachableEnv();
697 PushTry(outer_env, catch_env); 700 PushTry(outer_env, catch_env);
698 SetEnv("try_catch:start", try_env); 701 SetEnv("try_catch:start", try_env);
(...skipping 15 matching lines...) Expand all
714 if (!c->is_try()) { 717 if (!c->is_try()) {
715 error("catch does not match any try"); 718 error("catch does not match any try");
716 break; 719 break;
717 } 720 }
718 721
719 if (c->try_info->catch_env == nullptr) { 722 if (c->try_info->catch_env == nullptr) {
720 error(pc_, "catch already present for try with catch"); 723 error(pc_, "catch already present for try with catch");
721 break; 724 break;
722 } 725 }
723 726
724 if (ssa_env_->go()) { 727 FallThruTo(c);
725 MergeValuesInto(c);
726 }
727 stack_.resize(c->stack_depth); 728 stack_.resize(c->stack_depth);
728 729
729 DCHECK_NOT_NULL(c->try_info); 730 DCHECK_NOT_NULL(c->try_info);
730 SsaEnv* catch_env = c->try_info->catch_env; 731 SsaEnv* catch_env = c->try_info->catch_env;
731 c->try_info->catch_env = nullptr; 732 c->try_info->catch_env = nullptr;
732 SetEnv("catch:begin", catch_env); 733 SetEnv("catch:begin", catch_env);
733 current_catch_ = c->previous_catch; 734 current_catch_ = c->previous_catch;
734 735
735 if (Validate(pc_, operand)) { 736 if (Validate(pc_, operand)) {
736 if (ssa_env_->locals) { 737 if (ssa_env_->locals) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
780 Control* c = &control_.back(); 781 Control* c = &control_.back();
781 if (!c->is_if()) { 782 if (!c->is_if()) {
782 error(pc_, c->pc, "else does not match an if"); 783 error(pc_, c->pc, "else does not match an if");
783 break; 784 break;
784 } 785 }
785 if (c->false_env == nullptr) { 786 if (c->false_env == nullptr) {
786 error(pc_, c->pc, "else already present for if"); 787 error(pc_, c->pc, "else already present for if");
787 break; 788 break;
788 } 789 }
789 FallThruTo(c); 790 FallThruTo(c);
791 stack_.resize(c->stack_depth);
790 // Switch to environment for false branch. 792 // Switch to environment for false branch.
791 stack_.resize(c->stack_depth);
792 SetEnv("if_else:false", c->false_env); 793 SetEnv("if_else:false", c->false_env);
793 c->false_env = nullptr; // record that an else is already seen 794 c->false_env = nullptr; // record that an else is already seen
794 break; 795 break;
795 } 796 }
796 case kExprEnd: { 797 case kExprEnd: {
797 if (control_.empty()) { 798 if (control_.empty()) {
798 error("end does not match any if, try, or block"); 799 error("end does not match any if, try, or block");
799 return; 800 return;
800 } 801 }
801 const char* name = "block:end"; 802 const char* name = "block:end";
802 Control* c = &control_.back(); 803 Control* c = &control_.back();
803 if (c->is_loop()) { 804 if (c->is_loop()) {
804 // A loop just leaves the values on the stack. 805 // A loop just leaves the values on the stack.
805 TypeCheckLoopFallThru(c); 806 TypeCheckFallThru(c);
807 if (c->unreachable) PushEndValues(c);
806 PopControl(); 808 PopControl();
807 SetEnv("loop:end", ssa_env_); 809 SetEnv("loop:end", ssa_env_);
808 break; 810 break;
809 } 811 }
810 if (c->is_if()) { 812 if (c->is_if()) {
811 if (c->false_env != nullptr) { 813 if (c->false_env != nullptr) {
812 // End the true branch of a one-armed if. 814 // End the true branch of a one-armed if.
813 Goto(c->false_env, c->end_env); 815 Goto(c->false_env, c->end_env);
814 if (ssa_env_->go() && 816 if (!c->unreachable && stack_.size() != c->stack_depth) {
815 static_cast<int>(stack_.size()) != c->stack_depth) {
816 error("end of if expected empty stack"); 817 error("end of if expected empty stack");
817 stack_.resize(c->stack_depth); 818 stack_.resize(c->stack_depth);
818 } 819 }
819 if (c->merge.arity > 0) { 820 if (c->merge.arity > 0) {
820 error("non-void one-armed if"); 821 error("non-void one-armed if");
821 } 822 }
822 name = "if:merge"; 823 name = "if:merge";
823 } else { 824 } else {
824 // End the false branch of a two-armed if. 825 // End the false branch of a two-armed if.
825 name = "if_else:merge"; 826 name = "if_else:merge";
826 } 827 }
827 } else if (c->is_try()) { 828 } else if (c->is_try()) {
828 name = "try:end"; 829 name = "try:end";
829 830
830 // validate that catch was seen. 831 // validate that catch was seen.
831 if (c->try_info->catch_env != nullptr) { 832 if (c->try_info->catch_env != nullptr) {
832 error(pc_, "missing catch in try"); 833 error(pc_, "missing catch in try");
833 break; 834 break;
834 } 835 }
835 } 836 }
836 FallThruTo(c); 837 FallThruTo(c);
837 SetEnv(name, c->end_env); 838 SetEnv(name, c->end_env);
839 PushEndValues(c);
838 840
839 // Push the end values onto the stack. 841 if (control_.size() == 1) {
840 stack_.resize(c->stack_depth); 842 // If at the last (implicit) control, check we are at end.
841 if (c->merge.arity == 1) {
842 stack_.push_back(c->merge.vals.first);
843 } else {
844 for (unsigned i = 0; i < c->merge.arity; i++) {
845 stack_.push_back(c->merge.vals.array[i]);
846 }
847 }
848
849 PopControl();
850
851 if (control_.empty()) {
852 // If the last (implicit) control was popped, check we are at end.
853 if (pc_ + 1 != end_) { 843 if (pc_ + 1 != end_) {
854 error(pc_, pc_ + 1, "trailing code after function end"); 844 error(pc_, pc_ + 1, "trailing code after function end");
845 break;
855 } 846 }
856 last_end_found_ = true; 847 last_end_found_ = true;
857 if (ssa_env_->go()) { 848 if (ssa_env_->go()) {
858 // The result of the block is the return value. 849 // The result of the block is the return value.
859 TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); 850 TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
860 DoReturn(); 851 DoReturn();
861 TRACE("\n"); 852 TRACE("\n");
853 } else {
854 TypeCheckFallThru(c);
862 } 855 }
863 return;
864 } 856 }
857 PopControl();
865 break; 858 break;
866 } 859 }
867 case kExprSelect: { 860 case kExprSelect: {
868 Value cond = Pop(2, kWasmI32); 861 Value cond = Pop(2, kWasmI32);
869 Value fval = Pop(); 862 Value fval = Pop();
870 Value tval = Pop(); 863 Value tval = Pop(0, fval.type);
871 if (tval.type == kWasmStmt || tval.type != fval.type) {
872 if (tval.type != kWasmEnd && fval.type != kWasmEnd) {
873 error("type mismatch in select");
874 break;
875 }
876 }
877 if (build()) { 864 if (build()) {
878 DCHECK(tval.type != kWasmEnd);
879 DCHECK(fval.type != kWasmEnd);
880 DCHECK(cond.type != kWasmEnd);
881 TFNode* controls[2]; 865 TFNode* controls[2];
882 builder_->BranchNoHint(cond.node, &controls[0], &controls[1]); 866 builder_->BranchNoHint(cond.node, &controls[0], &controls[1]);
883 TFNode* merge = builder_->Merge(2, controls); 867 TFNode* merge = builder_->Merge(2, controls);
884 TFNode* vals[2] = {tval.node, fval.node}; 868 TFNode* vals[2] = {tval.node, fval.node};
885 TFNode* phi = builder_->Phi(tval.type, 2, vals, merge); 869 TFNode* phi = builder_->Phi(tval.type, 2, vals, merge);
886 Push(tval.type, phi); 870 Push(tval.type, phi);
887 ssa_env_->control = merge; 871 ssa_env_->control = merge;
888 } else { 872 } else {
889 Push(tval.type, nullptr); 873 Push(tval.type == kWasmVar ? fval.type : tval.type, nullptr);
890 } 874 }
891 break; 875 break;
892 } 876 }
893 case kExprBr: { 877 case kExprBr: {
894 BreakDepthOperand operand(this, pc_); 878 BreakDepthOperand operand(this, pc_);
895 if (Validate(pc_, operand, control_)) { 879 if (Validate(pc_, operand, control_)) {
896 BreakTo(operand.depth); 880 BreakTo(operand.depth);
897 } 881 }
898 len = 1 + operand.length; 882 len = 1 + operand.length;
899 EndControl(); 883 EndControl();
(...skipping 21 matching lines...) Expand all
921 Value key = Pop(0, kWasmI32); 905 Value key = Pop(0, kWasmI32);
922 if (failed()) break; 906 if (failed()) break;
923 907
924 SsaEnv* break_env = ssa_env_; 908 SsaEnv* break_env = ssa_env_;
925 if (operand.table_count > 0) { 909 if (operand.table_count > 0) {
926 // Build branches to the various blocks based on the table. 910 // Build branches to the various blocks based on the table.
927 TFNode* sw = BUILD(Switch, operand.table_count + 1, key.node); 911 TFNode* sw = BUILD(Switch, operand.table_count + 1, key.node);
928 912
929 SsaEnv* copy = Steal(break_env); 913 SsaEnv* copy = Steal(break_env);
930 ssa_env_ = copy; 914 ssa_env_ = copy;
915 MergeValues* merge = nullptr;
931 while (ok() && iterator.has_next()) { 916 while (ok() && iterator.has_next()) {
932 uint32_t i = iterator.cur_index(); 917 uint32_t i = iterator.cur_index();
933 const byte* pos = iterator.pc(); 918 const byte* pos = iterator.pc();
934 uint32_t target = iterator.next(); 919 uint32_t target = iterator.next();
935 if (target >= control_.size()) { 920 if (target >= control_.size()) {
936 error(pos, "improper branch in br_table"); 921 error(pos, "improper branch in br_table");
937 break; 922 break;
938 } 923 }
939 ssa_env_ = Split(copy); 924 ssa_env_ = Split(copy);
940 ssa_env_->control = (i == operand.table_count) 925 ssa_env_->control = (i == operand.table_count)
941 ? BUILD(IfDefault, sw) 926 ? BUILD(IfDefault, sw)
942 : BUILD(IfValue, i, sw); 927 : BUILD(IfValue, i, sw);
943 BreakTo(target); 928 BreakTo(target);
929
930 // Check that label types match up.
931 Control* c = &control_[control_.size() - target - 1];
932 if (i == 0) {
933 merge = &c->merge;
934 } else if (merge->arity != c->merge.arity) {
935 error(pos, pos,
936 "inconsistent arity in br_table target %d"
937 " (previous was %u, this one %u)",
938 i, merge->arity, c->merge.arity);
939 } else if (control_.back().unreachable) {
940 for (uint32_t j = 0; ok() && j < merge->arity; ++j) {
941 if ((*merge)[j].type != c->merge[j].type) {
942 error(pos, pos,
943 "type error in br_table target %d operand %d"
944 " (previous expected %s, this one %s)",
945 i, j, WasmOpcodes::TypeName((*merge)[j].type),
946 WasmOpcodes::TypeName(c->merge[j].type));
947 }
948 }
949 }
944 } 950 }
945 if (failed()) break; 951 if (failed()) break;
946 } else { 952 } else {
947 // Only a default target. Do the equivalent of br. 953 // Only a default target. Do the equivalent of br.
948 const byte* pos = iterator.pc(); 954 const byte* pos = iterator.pc();
949 uint32_t target = iterator.next(); 955 uint32_t target = iterator.next();
950 if (target >= control_.size()) { 956 if (target >= control_.size()) {
951 error(pos, "improper branch in br_table"); 957 error(pos, "improper branch in br_table");
952 break; 958 break;
953 } 959 }
954 BreakTo(target); 960 BreakTo(target);
955 } 961 }
956 // br_table ends the control flow like br. 962 // br_table ends the control flow like br.
957 ssa_env_ = break_env; 963 ssa_env_ = break_env;
958 } 964 }
959 len = 1 + iterator.length(); 965 len = 1 + iterator.length();
966 EndControl();
960 break; 967 break;
961 } 968 }
962 case kExprReturn: { 969 case kExprReturn: {
963 DoReturn(); 970 DoReturn();
964 break; 971 break;
965 } 972 }
966 case kExprUnreachable: { 973 case kExprUnreachable: {
967 BUILD(Unreachable, position()); 974 BUILD(Unreachable, position());
968 EndControl(); 975 EndControl();
969 break; 976 break;
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
1200 } else { 1207 } else {
1201 error("Invalid opcode"); 1208 error("Invalid opcode");
1202 return; 1209 return;
1203 } 1210 }
1204 } 1211 }
1205 } 1212 }
1206 } 1213 }
1207 1214
1208 #if DEBUG 1215 #if DEBUG
1209 if (FLAG_trace_wasm_decoder) { 1216 if (FLAG_trace_wasm_decoder) {
1217 PrintF(" ");
1218 for (size_t i = 0; i < control_.size(); ++i) {
1219 Control* c = &control_[i];
1220 enum ControlKind {
1221 kControlIf,
1222 kControlBlock,
1223 kControlLoop,
1224 kControlTry
1225 };
1226 switch (c->kind) {
1227 case kControlIf:
1228 PrintF("I");
1229 break;
1230 case kControlBlock:
1231 PrintF("B");
1232 break;
1233 case kControlLoop:
1234 PrintF("L");
1235 break;
1236 case kControlTry:
1237 PrintF("T");
1238 break;
1239 default:
1240 break;
1241 }
1242 PrintF("%u", c->merge.arity);
1243 if (c->unreachable) PrintF("*");
1244 }
1245 PrintF(" | ");
1210 for (size_t i = 0; i < stack_.size(); ++i) { 1246 for (size_t i = 0; i < stack_.size(); ++i) {
1211 Value& val = stack_[i]; 1247 Value& val = stack_[i];
1212 WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc); 1248 WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc);
1213 if (WasmOpcodes::IsPrefixOpcode(opcode)) { 1249 if (WasmOpcodes::IsPrefixOpcode(opcode)) {
1214 opcode = static_cast<WasmOpcode>(opcode << 8 | *(val.pc + 1)); 1250 opcode = static_cast<WasmOpcode>(opcode << 8 | *(val.pc + 1));
1215 } 1251 }
1216 PrintF(" %c@%d:%s", WasmOpcodes::ShortNameOf(val.type), 1252 PrintF(" %c@%d:%s", WasmOpcodes::ShortNameOf(val.type),
1217 static_cast<int>(val.pc - start_), 1253 static_cast<int>(val.pc - start_),
1218 WasmOpcodes::ShortOpcodeName(opcode)); 1254 WasmOpcodes::ShortOpcodeName(opcode));
1219 switch (opcode) { 1255 switch (opcode) {
1220 case kExprI32Const: { 1256 case kExprI32Const: {
1221 ImmI32Operand operand(this, val.pc); 1257 ImmI32Operand operand(this, val.pc);
1222 PrintF("[%d]", operand.value); 1258 PrintF("[%d]", operand.value);
1223 break; 1259 break;
1224 } 1260 }
1225 case kExprGetLocal: { 1261 case kExprGetLocal: {
1226 LocalIndexOperand operand(this, val.pc); 1262 LocalIndexOperand operand(this, val.pc);
1227 PrintF("[%u]", operand.index); 1263 PrintF("[%u]", operand.index);
1228 break; 1264 break;
1229 } 1265 }
1230 case kExprSetLocal: // fallthru 1266 case kExprSetLocal: // fallthru
1231 case kExprTeeLocal: { 1267 case kExprTeeLocal: {
1232 LocalIndexOperand operand(this, val.pc); 1268 LocalIndexOperand operand(this, val.pc);
1233 PrintF("[%u]", operand.index); 1269 PrintF("[%u]", operand.index);
1234 break; 1270 break;
1235 } 1271 }
1236 default: 1272 default:
1237 break; 1273 break;
1238 } 1274 }
1275 if (val.node == nullptr) PrintF("?");
1239 } 1276 }
1240 PrintF("\n"); 1277 PrintF("\n");
1241 } 1278 }
1242 #endif 1279 #endif
1243 pc_ += len; 1280 pc_ += len;
1244 if (pc_ >= end_) {
1245 // End of code reached or exceeded.
1246 if (pc_ > end_ && ok()) error("Beyond end of code");
1247 return;
1248 }
1249 } // end decode loop 1281 } // end decode loop
1282 if (pc_ > end_ && ok()) error("Beyond end of code");
1250 } 1283 }
1251 1284
1252 void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); } 1285 void EndControl() {
1286 ssa_env_->Kill(SsaEnv::kControlEnd);
1287 if (!control_.empty()) {
1288 stack_.resize(control_.back().stack_depth);
1289 control_.back().unreachable = true;
1290 }
1291 }
1253 1292
1254 void SetBlockType(Control* c, BlockTypeOperand& operand) { 1293 void SetBlockType(Control* c, BlockTypeOperand& operand) {
1255 c->merge.arity = operand.arity; 1294 c->merge.arity = operand.arity;
1256 if (c->merge.arity == 1) { 1295 if (c->merge.arity == 1) {
1257 c->merge.vals.first = {pc_, nullptr, operand.read_entry(0)}; 1296 c->merge.vals.first = {pc_, nullptr, operand.read_entry(0)};
1258 } else if (c->merge.arity > 1) { 1297 } else if (c->merge.arity > 1) {
1259 c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity); 1298 c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity);
1260 for (unsigned i = 0; i < c->merge.arity; i++) { 1299 for (unsigned i = 0; i < c->merge.arity; i++) {
1261 c->merge.vals.array[i] = {pc_, nullptr, operand.read_entry(i)}; 1300 c->merge.vals.array[i] = {pc_, nullptr, operand.read_entry(i)};
1262 } 1301 }
(...skipping 16 matching lines...) Expand all
1279 } 1318 }
1280 return nullptr; 1319 return nullptr;
1281 } 1320 }
1282 } 1321 }
1283 1322
1284 ValueType GetReturnType(FunctionSig* sig) { 1323 ValueType GetReturnType(FunctionSig* sig) {
1285 return sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(); 1324 return sig->return_count() == 0 ? kWasmStmt : sig->GetReturn();
1286 } 1325 }
1287 1326
1288 void PushBlock(SsaEnv* end_env) { 1327 void PushBlock(SsaEnv* end_env) {
1289 const int stack_depth = static_cast<int>(stack_.size());
1290 control_.emplace_back( 1328 control_.emplace_back(
1291 Control::Block(pc_, stack_depth, end_env, current_catch_)); 1329 Control::Block(pc_, stack_.size(), end_env, current_catch_));
1292 } 1330 }
1293 1331
1294 void PushLoop(SsaEnv* end_env) { 1332 void PushLoop(SsaEnv* end_env) {
1295 const int stack_depth = static_cast<int>(stack_.size());
1296 control_.emplace_back( 1333 control_.emplace_back(
1297 Control::Loop(pc_, stack_depth, end_env, current_catch_)); 1334 Control::Loop(pc_, stack_.size(), end_env, current_catch_));
1298 } 1335 }
1299 1336
1300 void PushIf(SsaEnv* end_env, SsaEnv* false_env) { 1337 void PushIf(SsaEnv* end_env, SsaEnv* false_env) {
1301 const int stack_depth = static_cast<int>(stack_.size());
1302 control_.emplace_back( 1338 control_.emplace_back(
1303 Control::If(pc_, stack_depth, end_env, false_env, current_catch_)); 1339 Control::If(pc_, stack_.size(), end_env, false_env, current_catch_));
1304 } 1340 }
1305 1341
1306 void PushTry(SsaEnv* end_env, SsaEnv* catch_env) { 1342 void PushTry(SsaEnv* end_env, SsaEnv* catch_env) {
1307 const int stack_depth = static_cast<int>(stack_.size()); 1343 control_.emplace_back(Control::Try(pc_, stack_.size(), end_env, zone_,
1308 control_.emplace_back(Control::Try(pc_, stack_depth, end_env, zone_,
1309 catch_env, current_catch_)); 1344 catch_env, current_catch_));
1310 current_catch_ = static_cast<int32_t>(control_.size() - 1); 1345 current_catch_ = static_cast<int32_t>(control_.size() - 1);
1311 } 1346 }
1312 1347
1313 void PopControl() { control_.pop_back(); } 1348 void PopControl() { control_.pop_back(); }
1314 1349
1315 int DecodeLoadMem(ValueType type, MachineType mem_type) { 1350 int DecodeLoadMem(ValueType type, MachineType mem_type) {
1316 if (!CheckHasMemory()) return 0; 1351 if (!CheckHasMemory()) return 0;
1317 MemoryAccessOperand operand(this, pc_, 1352 MemoryAccessOperand operand(this, pc_,
1318 ElementSizeLog2Of(mem_type.representation())); 1353 ElementSizeLog2Of(mem_type.representation()));
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
1406 for (int i = count - 1; i >= 0; i--) { 1441 for (int i = count - 1; i >= 0; i--) {
1407 Value val = Pop(i, sig_->GetReturn(i)); 1442 Value val = Pop(i, sig_->GetReturn(i));
1408 if (buffer) buffer[i] = val.node; 1443 if (buffer) buffer[i] = val.node;
1409 } 1444 }
1410 1445
1411 BUILD(Return, count, buffer); 1446 BUILD(Return, count, buffer);
1412 EndControl(); 1447 EndControl();
1413 } 1448 }
1414 1449
1415 void Push(ValueType type, TFNode* node) { 1450 void Push(ValueType type, TFNode* node) {
1416 if (type != kWasmStmt && type != kWasmEnd) { 1451 if (type != kWasmStmt) {
1417 stack_.push_back({pc_, node, type}); 1452 stack_.push_back({pc_, node, type});
1418 } 1453 }
1419 } 1454 }
1420 1455
1456 void PushEndValues(Control* c) {
1457 DCHECK_EQ(c, &control_.back());
1458 stack_.resize(c->stack_depth);
1459 if (c->merge.arity == 1) {
1460 stack_.push_back(c->merge.vals.first);
1461 } else {
1462 for (unsigned i = 0; i < c->merge.arity; i++) {
1463 stack_.push_back(c->merge.vals.array[i]);
1464 }
1465 }
1466 DCHECK_EQ(c->stack_depth + c->merge.arity, stack_.size());
1467 }
1468
1421 void PushReturns(FunctionSig* sig, TFNode** rets) { 1469 void PushReturns(FunctionSig* sig, TFNode** rets) {
1422 for (size_t i = 0; i < sig->return_count(); i++) { 1470 for (size_t i = 0; i < sig->return_count(); i++) {
1423 // When verifying only, then {rets} will be null, so push null. 1471 // When verifying only, then {rets} will be null, so push null.
1424 Push(sig->GetReturn(i), rets ? rets[i] : nullptr); 1472 Push(sig->GetReturn(i), rets ? rets[i] : nullptr);
1425 } 1473 }
1426 } 1474 }
1427 1475
1428 const char* SafeOpcodeNameAt(const byte* pc) { 1476 const char* SafeOpcodeNameAt(const byte* pc) {
1429 if (pc >= end_) return "<end>"; 1477 if (pc >= end_) return "<end>";
1430 return WasmOpcodes::ShortOpcodeName(static_cast<WasmOpcode>(*pc)); 1478 return WasmOpcodes::ShortOpcodeName(static_cast<WasmOpcode>(*pc));
1431 } 1479 }
1432 1480
1433 Value Pop(int index, ValueType expected) { 1481 Value Pop(int index, ValueType expected) {
1434 if (!ssa_env_->go()) {
1435 // Unreachable code is essentially not typechecked.
1436 return {pc_, nullptr, expected};
1437 }
1438 Value val = Pop(); 1482 Value val = Pop();
1439 if (val.type != expected) { 1483 if (val.type != expected && val.type != kWasmVar && expected != kWasmVar) {
1440 if (val.type != kWasmEnd) { 1484 error(pc_, val.pc, "%s[%d] expected type %s, found %s of type %s",
1441 error(pc_, val.pc, "%s[%d] expected type %s, found %s of type %s", 1485 SafeOpcodeNameAt(pc_), index, WasmOpcodes::TypeName(expected),
1442 SafeOpcodeNameAt(pc_), index, WasmOpcodes::TypeName(expected), 1486 SafeOpcodeNameAt(val.pc), WasmOpcodes::TypeName(val.type));
1443 SafeOpcodeNameAt(val.pc), WasmOpcodes::TypeName(val.type));
1444 }
1445 } 1487 }
1446 return val; 1488 return val;
1447 } 1489 }
1448 1490
1449 Value Pop() { 1491 Value Pop() {
1450 if (!ssa_env_->go()) {
1451 // Unreachable code is essentially not typechecked.
1452 return {pc_, nullptr, kWasmEnd};
1453 }
1454 size_t limit = control_.empty() ? 0 : control_.back().stack_depth; 1492 size_t limit = control_.empty() ? 0 : control_.back().stack_depth;
1455 if (stack_.size() <= limit) { 1493 if (stack_.size() <= limit) {
1456 Value val = {pc_, nullptr, kWasmStmt}; 1494 // Popping past the current control start in reachable code.
1457 error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_)); 1495 Value val = {pc_, nullptr, kWasmVar};
1496 if (!control_.back().unreachable) {
1497 error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_));
1498 }
1458 return val; 1499 return val;
1459 } 1500 }
1460 Value val = stack_.back(); 1501 Value val = stack_.back();
1461 stack_.pop_back(); 1502 stack_.pop_back();
1462 return val; 1503 return val;
1463 } 1504 }
1464 1505
1465 Value PopUpTo(int stack_depth) {
1466 if (!ssa_env_->go()) {
1467 // Unreachable code is essentially not typechecked.
1468 return {pc_, nullptr, kWasmEnd};
1469 }
1470 if (stack_depth == static_cast<int>(stack_.size())) {
1471 Value val = {pc_, nullptr, kWasmStmt};
1472 return val;
1473 } else {
1474 DCHECK_LE(stack_depth, stack_.size());
1475 Value val = Pop();
1476 stack_.resize(stack_depth);
1477 return val;
1478 }
1479 }
1480
1481 int baserel(const byte* ptr) { 1506 int baserel(const byte* ptr) {
1482 return base_ ? static_cast<int>(ptr - base_) : 0; 1507 return base_ ? static_cast<int>(ptr - base_) : 0;
1483 } 1508 }
1484 1509
1485 int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); } 1510 int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
1486 1511
1487 void BreakTo(unsigned depth) { 1512 void BreakTo(unsigned depth) {
1488 if (!ssa_env_->go()) return;
1489 Control* c = &control_[control_.size() - depth - 1]; 1513 Control* c = &control_[control_.size() - depth - 1];
1490 if (c->is_loop()) { 1514 if (c->is_loop()) {
1491 // This is the inner loop block, which does not have a value. 1515 // This is the inner loop block, which does not have a value.
1492 Goto(ssa_env_, c->end_env); 1516 Goto(ssa_env_, c->end_env);
1493 } else { 1517 } else {
1494 // Merge the value(s) into the end of the block. 1518 // Merge the value(s) into the end of the block.
1495 if (c->stack_depth + c->merge.arity > stack_.size()) { 1519 size_t expected = control_.back().stack_depth + c->merge.arity;
1520 if (stack_.size() < expected && !control_.back().unreachable) {
1496 error( 1521 error(
1497 pc_, pc_, 1522 pc_, pc_,
1498 "expected at least %d values on the stack for br to @%d, found %d", 1523 "expected at least %u values on the stack for br to @%d, found %d",
1499 c->merge.arity, startrel(c->pc), 1524 c->merge.arity, startrel(c->pc),
1500 static_cast<int>(stack_.size() - c->stack_depth)); 1525 static_cast<int>(stack_.size() - c->stack_depth));
1501 return; 1526 return;
1502 } 1527 }
1503 MergeValuesInto(c); 1528 MergeValuesInto(c);
1504 } 1529 }
1505 } 1530 }
1506 1531
1507 void FallThruTo(Control* c) { 1532 void FallThruTo(Control* c) {
1508 if (!ssa_env_->go()) return; 1533 DCHECK_EQ(c, &control_.back());
1509 // Merge the value(s) into the end of the block. 1534 // Merge the value(s) into the end of the block.
1510 int arity = static_cast<int>(c->merge.arity); 1535 size_t expected = c->stack_depth + c->merge.arity;
1511 if (c->stack_depth + arity != static_cast<int>(stack_.size())) { 1536 if (stack_.size() == expected ||
1512 error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d", 1537 (stack_.size() < expected && c->unreachable)) {
1513 arity, startrel(c->pc)); 1538 MergeValuesInto(c);
1539 c->unreachable = false;
1514 return; 1540 return;
1515 } 1541 }
1516 MergeValuesInto(c); 1542 error(pc_, pc_, "expected %u elements on the stack for fallthru to @%d",
1543 c->merge.arity, startrel(c->pc));
1517 } 1544 }
1518 1545
1519 inline Value& GetMergeValueFromStack(Control* c, int i) { 1546 inline Value& GetMergeValueFromStack(Control* c, size_t i) {
1520 return stack_[stack_.size() - c->merge.arity + i]; 1547 return stack_[stack_.size() - c->merge.arity + i];
1521 } 1548 }
1522 1549
1523 void TypeCheckLoopFallThru(Control* c) { 1550 void TypeCheckFallThru(Control* c) {
1524 if (!ssa_env_->go()) return; 1551 DCHECK_EQ(c, &control_.back());
1525 // Fallthru must match arity exactly. 1552 // Fallthru must match arity exactly.
1526 int arity = static_cast<int>(c->merge.arity); 1553 int arity = static_cast<int>(c->merge.arity);
1527 if (c->stack_depth + arity != static_cast<int>(stack_.size())) { 1554 if (c->stack_depth + arity < stack_.size() ||
1555 (c->stack_depth + arity != stack_.size() && !c->unreachable)) {
1528 error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d", 1556 error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
1529 arity, startrel(c->pc)); 1557 arity, startrel(c->pc));
1530 return; 1558 return;
1531 } 1559 }
1532 // Typecheck the values left on the stack. 1560 // Typecheck the values left on the stack.
1533 for (unsigned i = 0; i < c->merge.arity; i++) { 1561 size_t avail = stack_.size() - c->stack_depth;
1562 for (size_t i = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
1563 i < c->merge.arity; i++) {
1534 Value& val = GetMergeValueFromStack(c, i); 1564 Value& val = GetMergeValueFromStack(c, i);
1535 Value& old = 1565 Value& old = c->merge[i];
1536 c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
1537 if (val.type != old.type) { 1566 if (val.type != old.type) {
1538 error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i, 1567 error(pc_, pc_, "type error in merge[%zu] (expected %s, got %s)", i,
1539 WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); 1568 WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
1540 return; 1569 return;
1541 } 1570 }
1542 } 1571 }
1543 } 1572 }
1544 1573
1545 void MergeValuesInto(Control* c) { 1574 void MergeValuesInto(Control* c) {
1546 SsaEnv* target = c->end_env; 1575 SsaEnv* target = c->end_env;
1547 bool first = target->state == SsaEnv::kUnreachable; 1576 bool first = target->state == SsaEnv::kUnreachable;
1577 bool reachable = ssa_env_->go();
1548 Goto(ssa_env_, target); 1578 Goto(ssa_env_, target);
1549 1579
1550 for (unsigned i = 0; i < c->merge.arity; i++) { 1580 size_t avail = stack_.size() - control_.back().stack_depth;
1581 for (size_t i = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
1582 i < c->merge.arity; i++) {
1551 Value& val = GetMergeValueFromStack(c, i); 1583 Value& val = GetMergeValueFromStack(c, i);
1552 Value& old = 1584 Value& old = c->merge[i];
1553 c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i]; 1585 if (val.type != old.type && val.type != kWasmVar) {
1554 if (val.type != old.type) { 1586 error(pc_, pc_, "type error in merge[%zu] (expected %s, got %s)", i,
1555 error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
1556 WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); 1587 WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
1557 return; 1588 return;
1558 } 1589 }
1559 if (builder_) { 1590 if (builder_ && reachable) {
1591 DCHECK_NOT_NULL(val.node);
1560 old.node = 1592 old.node =
1561 first ? val.node : CreateOrMergeIntoPhi(old.type, target->control, 1593 first ? val.node : CreateOrMergeIntoPhi(old.type, target->control,
1562 old.node, val.node); 1594 old.node, val.node);
1563 } else {
1564 old.node = nullptr;
1565 } 1595 }
1566 } 1596 }
1567 } 1597 }
1568 1598
1569 void SetEnv(const char* reason, SsaEnv* env) { 1599 void SetEnv(const char* reason, SsaEnv* env) {
1570 #if DEBUG 1600 #if DEBUG
1571 if (FLAG_trace_wasm_decoder) { 1601 if (FLAG_trace_wasm_decoder) {
1572 char state = 'X'; 1602 char state = 'X';
1573 if (env) { 1603 if (env) {
1574 switch (env->state) { 1604 switch (env->state) {
1575 case SsaEnv::kReached: 1605 case SsaEnv::kReached:
1576 state = 'R'; 1606 state = 'R';
1577 break; 1607 break;
1578 case SsaEnv::kUnreachable: 1608 case SsaEnv::kUnreachable:
1579 state = 'U'; 1609 state = 'U';
1580 break; 1610 break;
1581 case SsaEnv::kMerged: 1611 case SsaEnv::kMerged:
1582 state = 'M'; 1612 state = 'M';
1583 break; 1613 break;
1584 case SsaEnv::kControlEnd: 1614 case SsaEnv::kControlEnd:
1585 state = 'E'; 1615 state = 'E';
1586 break; 1616 break;
1587 } 1617 }
1588 } 1618 }
1589 PrintF(" env = %p, state = %c, reason = %s", static_cast<void*>(env), 1619 PrintF("{set_env = %p, state = %c, reason = %s", static_cast<void*>(env),
1590 state, reason); 1620 state, reason);
1591 if (env && env->control) { 1621 if (env && env->control) {
1592 PrintF(", control = "); 1622 PrintF(", control = ");
1593 compiler::WasmGraphBuilder::PrintDebugName(env->control); 1623 compiler::WasmGraphBuilder::PrintDebugName(env->control);
1594 } 1624 }
1595 PrintF("\n"); 1625 PrintF("}");
1596 } 1626 }
1597 #endif 1627 #endif
1598 ssa_env_ = env; 1628 ssa_env_ = env;
1599 if (builder_) { 1629 if (builder_) {
1600 builder_->set_control_ptr(&env->control); 1630 builder_->set_control_ptr(&env->control);
1601 builder_->set_effect_ptr(&env->effect); 1631 builder_->set_effect_ptr(&env->effect);
1602 } 1632 }
1603 } 1633 }
1604 1634
1605 TFNode* CheckForException(TFNode* node) { 1635 TFNode* CheckForException(TFNode* node) {
(...skipping 441 matching lines...) Expand 10 before | Expand all | Expand 10 after
2047 BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals, 2077 BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
2048 const byte* start, const byte* end) { 2078 const byte* start, const byte* end) {
2049 Decoder decoder(start, end); 2079 Decoder decoder(start, end);
2050 return WasmDecoder::AnalyzeLoopAssignment(&decoder, start, 2080 return WasmDecoder::AnalyzeLoopAssignment(&decoder, start,
2051 static_cast<int>(num_locals), zone); 2081 static_cast<int>(num_locals), zone);
2052 } 2082 }
2053 2083
2054 } // namespace wasm 2084 } // namespace wasm
2055 } // namespace internal 2085 } // namespace internal
2056 } // namespace v8 2086 } // namespace v8
OLDNEW
« no previous file with comments | « src/asmjs/asm-wasm-builder.cc ('k') | src/wasm/wasm-opcodes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698