@@ -45,13 +45,8 @@ struct ControlFrame
4545 size_t immediates_offset{0 };
4646
4747 // / The frame stack height of the parent frame.
48- // / TODO: Storing this is not strictly required, as the parent frame is available
49- // / in the control_stack.
5048 const int parent_stack_height{0 };
5149
52- // / The frame stack height.
53- int stack_height{0 };
54-
5550 // / Whether the remainder of the block is unreachable (used to handle stack-polymorphic typing
5651 // / after branches).
5752 bool unreachable{false };
@@ -65,8 +60,7 @@ struct ControlFrame
6560 type{_type},
6661 code_offset{_code_offset},
6762 immediates_offset{_immediates_offset},
68- parent_stack_height{_parent_stack_height},
69- stack_height{_parent_stack_height}
63+ parent_stack_height{_parent_stack_height}
7064 {}
7165};
7266
@@ -93,23 +87,12 @@ void update_operand_stack(ControlFrame& frame, Stack<ValType>& operand_stack,
9387 span<const ValType> inputs, span<const ValType> outputs)
9488{
9589 const auto inputs_size = static_cast <int >(inputs.size ());
96- const auto outputs_size = static_cast <int >(outputs.size ());
9790
98- // Update frame.stack_height.
99- if (frame.stack_height < frame.parent_stack_height + inputs_size)
100- {
101- // Stack is polymorphic after unreachable instruction: underflow is ignored,
102- // but we need to count stack growth to detect extra values at the end of the block.
103- if (frame.unreachable )
104- {
105- // Add instruction's/call's output values only.
106- frame.stack_height = frame.parent_stack_height + outputs_size;
107- }
108- else
109- throw validation_error{" stack underflow" };
110- }
111- else
112- frame.stack_height += outputs_size - inputs_size;
91+ // Stack is polymorphic after unreachable instruction: underflow is ignored,
92+ // but we need to count stack growth to detect extra values at the end of the block.
93+ if (!frame.unreachable &&
94+ static_cast <int >(operand_stack.size ()) < frame.parent_stack_height + inputs_size)
95+ throw validation_error{" stack underflow" };
11396
11497 // Update operand_stack.
11598 for (auto it = inputs.begin () + inputs.size (); it != inputs.begin (); --it)
@@ -135,20 +118,22 @@ inline void update_caller_frame(
135118 update_operand_stack (frame, operand_stack, func_type.inputs , func_type.outputs );
136119}
137120
138- void validate_result_count (const ControlFrame& frame)
121+ void validate_result_count (const ControlFrame& frame, const Stack<ValType>& operand_stack )
139122{
123+ const auto frame_stack_height = static_cast <int >(operand_stack.size ());
124+
140125 // This is checked by "stack underflow".
141- assert (frame. stack_height >= frame.parent_stack_height );
126+ assert (frame_stack_height >= frame.parent_stack_height );
142127
143128 const auto arity = frame.type .has_value () ? 1 : 0 ;
144129
145- if (frame. stack_height > frame.parent_stack_height + arity)
130+ if (frame_stack_height > frame.parent_stack_height + arity)
146131 throw validation_error{" too many results" };
147132
148133 if (frame.unreachable )
149134 return ;
150135
151- if (frame. stack_height < frame.parent_stack_height + arity)
136+ if (frame_stack_height < frame.parent_stack_height + arity)
152137 throw validation_error{" missing result" };
153138}
154139
@@ -161,14 +146,16 @@ inline uint8_t get_branch_arity(const ControlFrame& frame) noexcept
161146 return frame.instruction == Instr::loop ? 0 : arity;
162147}
163148
164- inline void validate_branch_stack_height (
165- const ControlFrame& current_frame , const ControlFrame& branch_frame )
149+ inline void validate_branch_stack_height (const ControlFrame& current_frame,
150+ const ControlFrame& branch_frame , const Stack<ValType>& operand_stack )
166151{
167- assert (current_frame.stack_height >= current_frame.parent_stack_height );
152+ const auto current_frame_stack_height = static_cast <int >(operand_stack.size ());
153+
154+ assert (current_frame_stack_height >= current_frame.parent_stack_height );
168155
169156 const auto arity = get_branch_arity (branch_frame);
170157 if (!current_frame.unreachable &&
171- (current_frame. stack_height < current_frame.parent_stack_height + arity))
158+ (current_frame_stack_height < current_frame.parent_stack_height + arity))
172159 throw validation_error{" branch stack underflow" };
173160}
174161
@@ -190,22 +177,18 @@ void push_branch_immediates(
190177 push (immediates, arity);
191178}
192179
193- inline void mark_frame_unreachable (ControlFrame& frame) noexcept
180+ inline void mark_frame_unreachable (ControlFrame& frame, Stack<ValType>& operand_stack ) noexcept
194181{
195182 frame.unreachable = true ;
196- frame. stack_height = frame.parent_stack_height ;
183+ operand_stack. shrink ( static_cast < size_t >( frame.parent_stack_height )) ;
197184}
198185
199186inline void drop_operand (
200187 ControlFrame& frame, Stack<ValType>& operand_stack, std::optional<ValType> expected_type)
201188{
202- if (frame.stack_height < frame.parent_stack_height + 1 )
203- {
204- if (!frame.unreachable )
205- throw validation_error{" stack underflow" };
206- }
207- else
208- --frame.stack_height ;
189+ if (static_cast <int >(operand_stack.size ()) < frame.parent_stack_height + 1 &&
190+ !frame.unreachable )
191+ throw validation_error{" stack underflow" };
209192
210193 const auto actual_type =
211194 (frame.unreachable &&
@@ -216,9 +199,8 @@ inline void drop_operand(
216199 throw validation_error{" type mismatch" };
217200}
218201
219- inline void push_operand (ControlFrame& frame, Stack<ValType>& operand_stack, ValType type)
202+ inline void push_operand (Stack<ValType>& operand_stack, ValType type)
220203{
221- ++frame.stack_height ;
222204 operand_stack.push (type);
223205}
224206
@@ -282,7 +264,8 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
282264 // already popped/reset), but it does not matter, as these instructions do not modify
283265 // stack height anyway.
284266 if (!frame.unreachable )
285- code.max_stack_height = std::max (code.max_stack_height , frame.stack_height );
267+ code.max_stack_height =
268+ std::max (code.max_stack_height , static_cast <int >(operand_stack.size ()));
286269
287270 update_operand_stack (frame, operand_stack, types.inputs , types.outputs );
288271
@@ -375,7 +358,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
375358 break ;
376359
377360 case Instr::unreachable:
378- mark_frame_unreachable (frame);
361+ mark_frame_unreachable (frame, operand_stack );
379362 break ;
380363
381364 case Instr::drop:
@@ -454,8 +437,8 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
454437 std::tie (type, pos) = parse_blocktype (pos, end);
455438
456439 // Push label with immediates offset after arity.
457- control_stack.emplace (Instr::block, type, frame. stack_height , code. instructions . size (),
458- code.immediates .size ());
440+ control_stack.emplace (Instr::block, type, static_cast < int >(operand_stack. size () ),
441+ code.instructions . size (), code. immediates .size ());
459442 break ;
460443 }
461444
@@ -464,8 +447,8 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
464447 std::optional<ValType> type;
465448 std::tie (type, pos) = parse_blocktype (pos, end);
466449
467- control_stack.emplace (Instr::loop, type, frame. stack_height , code. instructions . size (),
468- code.immediates .size ());
450+ control_stack.emplace (Instr::loop, type, static_cast < int >(operand_stack. size () ),
451+ code.instructions . size (), code. immediates .size ());
469452
470453 break ;
471454 }
@@ -475,8 +458,8 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
475458 std::optional<ValType> type;
476459 std::tie (type, pos) = parse_blocktype (pos, end);
477460
478- control_stack.emplace (Instr::if_, type, frame. stack_height , code. instructions . size (),
479- code.immediates .size ());
461+ control_stack.emplace (Instr::if_, type, static_cast < int >(operand_stack. size () ),
462+ code.instructions . size (), code. immediates .size ());
480463
481464 // Placeholders for immediate values, filled at the matching end or else instructions.
482465 push (code.immediates , uint32_t {0 }); // Diff to the else instruction
@@ -490,10 +473,9 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
490473 if (frame.instruction != Instr::if_)
491474 throw parser_error{" unexpected else instruction (if instruction missing)" };
492475
493- validate_result_count (frame); // else is the end of if.
476+ validate_result_count (frame, operand_stack ); // else is the end of if.
494477
495478 // Reset frame after if. The if result type validation not implemented yet.
496- frame.stack_height = frame.parent_stack_height ;
497479 frame.unreachable = false ;
498480 const auto if_imm_offset = frame.immediates_offset ;
499481 frame.immediates_offset = code.immediates .size ();
@@ -518,7 +500,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
518500
519501 case Instr::end:
520502 {
521- validate_result_count (frame);
503+ validate_result_count (frame, operand_stack );
522504
523505 if (frame.instruction != Instr::loop) // If end of block/if/else instruction.
524506 {
@@ -553,11 +535,13 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
553535 }
554536 const auto type = frame.type ;
555537 control_stack.pop (); // Pop the current frame.
538+ operand_stack.shrink (static_cast <size_t >(std::max (0 , frame.parent_stack_height )));
539+ // operand_stack.shrink(static_cast<size_t>(frame.parent_stack_height));
556540
557541 if (control_stack.empty ())
558542 continue_parsing = false ;
559543 else if (type.has_value ())
560- control_stack. top (). stack_height += 1 ; // The results of the popped frame.
544+ operand_stack. push (*type);
561545 break ;
562546 }
563547
@@ -572,15 +556,16 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
572556
573557 auto & branch_frame = control_stack[label_idx];
574558
575- validate_branch_stack_height (frame, branch_frame);
559+ validate_branch_stack_height (frame, branch_frame, operand_stack );
576560
577561 // Remember this br immediates offset to fill it at end instruction.
578562 branch_frame.br_immediate_offsets .push_back (code.immediates .size ());
579563
580- push_branch_immediates (branch_frame, frame.stack_height , code.immediates );
564+ push_branch_immediates (
565+ branch_frame, static_cast <int >(operand_stack.size ()), code.immediates );
581566
582567 if (instr == Instr::br)
583- mark_frame_unreachable (frame);
568+ mark_frame_unreachable (frame, operand_stack );
584569
585570 break ;
586571 }
@@ -604,7 +589,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
604589 auto & default_branch_frame = control_stack[default_label_idx];
605590 const auto default_branch_arity = get_branch_arity (default_branch_frame);
606591
607- validate_branch_stack_height (frame, default_branch_frame);
592+ validate_branch_stack_height (frame, default_branch_frame, operand_stack );
608593
609594 // Remember immediates offset for all br items to fill them at end instruction.
610595 push (code.immediates , static_cast <uint32_t >(label_indices.size ()));
@@ -616,13 +601,15 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
616601 throw validation_error{" br_table labels have inconsistent types" };
617602
618603 branch_frame.br_immediate_offsets .push_back (code.immediates .size ());
619- push_branch_immediates (branch_frame, frame.stack_height , code.immediates );
604+ push_branch_immediates (
605+ branch_frame, static_cast <int >(operand_stack.size ()), code.immediates );
620606 }
621607
622608 default_branch_frame.br_immediate_offsets .push_back (code.immediates .size ());
623- push_branch_immediates (default_branch_frame, frame.stack_height , code.immediates );
609+ push_branch_immediates (
610+ default_branch_frame, static_cast <int >(operand_stack.size ()), code.immediates );
624611
625- mark_frame_unreachable (frame);
612+ mark_frame_unreachable (frame, operand_stack );
626613
627614 break ;
628615 }
@@ -635,13 +622,14 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
635622
636623 auto & branch_frame = control_stack[label_idx];
637624
638- validate_branch_stack_height (frame, branch_frame);
625+ validate_branch_stack_height (frame, branch_frame, operand_stack );
639626
640627 branch_frame.br_immediate_offsets .push_back (code.immediates .size ());
641628
642- push_branch_immediates (control_stack[label_idx], frame.stack_height , code.immediates );
629+ push_branch_immediates (
630+ control_stack[label_idx], static_cast <int >(operand_stack.size ()), code.immediates );
643631
644- mark_frame_unreachable (frame);
632+ mark_frame_unreachable (frame, operand_stack );
645633 break ;
646634 }
647635
@@ -688,7 +676,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
688676 uint32_t local_idx;
689677 std::tie (local_idx, pos) = leb128u_decode<uint32_t >(pos, end);
690678
691- push_operand (frame, operand_stack, find_local_type (func_inputs, locals, local_idx));
679+ push_operand (operand_stack, find_local_type (func_inputs, locals, local_idx));
692680
693681 push (code.immediates , local_idx);
694682 break ;
@@ -710,7 +698,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
710698
711699 const auto type = find_local_type (func_inputs, locals, local_idx);
712700 drop_operand (frame, operand_stack, type);
713- push_operand (frame, operand_stack, type);
701+ push_operand (operand_stack, type);
714702
715703 push (code.immediates , local_idx);
716704 break ;
@@ -724,7 +712,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
724712 if (global_idx >= module .get_global_count ())
725713 throw validation_error{" accessing global with invalid index" };
726714
727- push_operand (frame, operand_stack, module .get_global_type (global_idx).value_type );
715+ push_operand (operand_stack, module .get_global_type (global_idx).value_type );
728716
729717 push (code.immediates , global_idx);
730718 break ;
0 commit comments