Emit the `table.grow` operation.
(&mut self, table_index: TableIndex)
| 695 | |
| 696 | /// Emit the `table.grow` operation. |
| 697 | pub fn emit_table_grow(&mut self, table_index: TableIndex) -> Result<()> { |
| 698 | let ptr_type = self.env.ptr_type(); |
| 699 | let idx_type = self.env.table(table_index).idx_type; |
| 700 | |
| 701 | // Duplicate the `delta` argument on the stack since we'll need it at |
| 702 | // the end if growth succeeds. |
| 703 | let delta = self.context.pop_to_reg(self.masm, None)?; |
| 704 | let tmp = self.context.any_gpr(self.masm)?; |
| 705 | self.masm |
| 706 | .mov(writable!(tmp), delta.reg.into(), delta.ty.try_into()?)?; |
| 707 | self.context.stack.push(TypedReg::new(delta.ty, tmp).into()); |
| 708 | self.context.stack.push(delta.into()); |
| 709 | |
| 710 | // Invoke the `table.grow` builtin on the host which will return whether |
| 711 | // the growth succeeded, and if so where it's located. |
| 712 | let at = self.context.stack.ensure_index_at(1)?; |
| 713 | let builtin = self.env.builtins.table_grow::<M::ABI>()?; |
| 714 | let builtin = self.prepare_builtin_defined_table_arg(table_index, at, builtin)?; |
| 715 | FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?; |
| 716 | |
| 717 | // Pop everything that's on the stack now. The builtin took `delta` and |
| 718 | // pushed a result, and then peel off our duplicate of `delta` plus the |
| 719 | // initialization element of `table.grow` itself. |
| 720 | let result = self.context.pop_to_reg(self.masm, None)?; |
| 721 | let len = self.context.pop_to_reg(self.masm, None)?; |
| 722 | let init = self.context.pop_to_reg(self.masm, None)?; |
| 723 | |
| 724 | // Save a copy of `result` on the stack since we'll need it after |
| 725 | // `table.fill` is done. |
| 726 | let tmp_result = self.context.any_gpr(self.masm)?; |
| 727 | self.masm.mov( |
| 728 | writable!(tmp_result), |
| 729 | result.reg.into(), |
| 730 | result.ty.try_into()?, |
| 731 | )?; |
| 732 | self.context |
| 733 | .stack |
| 734 | .push(TypedReg::new(result.ty, tmp_result).into()); |
| 735 | |
| 736 | // Test if the result of growth is -1. If it is, then we're done. |
| 737 | // Otherwise fall through to `table.fill`. |
| 738 | let done = self.masm.get_label()?; |
| 739 | self.masm.branch( |
| 740 | IntCmpKind::Eq, |
| 741 | result.reg, |
| 742 | RegImm::i64(-1), |
| 743 | done, |
| 744 | OperandSize::S64, |
| 745 | )?; |
| 746 | |
| 747 | // Prepare the arguments for `table.fill` in the order the wasm |
| 748 | // instruction expects. |
| 749 | self.context.stack.push(result.into()); |
| 750 | self.context.stack.push(init.into()); |
| 751 | self.context.stack.push(len.into()); |
| 752 | self.emit_table_fill(table_index)?; |
| 753 | |
| 754 | self.masm.bind(done)?; |
no test coverage detected