Emit the `table.fill` operation.
(&mut self, table_index: TableIndex)
| 771 | |
| 772 | /// Emit the `table.fill` operation. |
| 773 | pub fn emit_table_fill(&mut self, table_index: TableIndex) -> Result<()> { |
| 774 | // Put all of this opcode's arguments into registers. |
| 775 | let len = self.context.pop_to_reg(self.masm, None)?; |
| 776 | let init = self.context.pop_to_reg(self.masm, None)?; |
| 777 | let offset = self.context.pop_to_reg(self.masm, None)?; |
| 778 | |
| 779 | // Perform a bounds check to see if `offset+len` is inbounds. |
| 780 | let table_data = self.env.resolve_table_data(table_index); |
| 781 | self.emit_compute_table_size(&table_data)?; |
| 782 | let table_size = self.context.pop_to_reg(self.masm, None)?; |
| 783 | let tmp = self.context.any_gpr(self.masm)?; |
| 784 | let idx_size = table_data.index_type().try_into()?; |
| 785 | self.masm.mov(writable!(tmp), offset.reg.into(), idx_size)?; |
| 786 | self.masm.checked_uadd( |
| 787 | writable!(tmp), |
| 788 | tmp, |
| 789 | len.reg.into(), |
| 790 | idx_size, |
| 791 | TRAP_TABLE_OUT_OF_BOUNDS, |
| 792 | )?; |
| 793 | self.masm.cmp(tmp, table_size.reg.into(), idx_size)?; |
| 794 | self.masm |
| 795 | .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?; |
| 796 | self.context.free_reg(tmp); |
| 797 | self.context.free_reg(table_size); |
| 798 | |
| 799 | let header = self.masm.get_label()?; |
| 800 | let exit = self.masm.get_label()?; |
| 801 | |
| 802 | self.masm.bind(header)?; |
| 803 | |
| 804 | // Exit the loop once there are no more elements to copy. |
| 805 | self.masm.branch( |
| 806 | IntCmpKind::Eq, |
| 807 | len.reg, |
| 808 | RegImm::i64(0), |
| 809 | exit, |
| 810 | OperandSize::S64, |
| 811 | )?; |
| 812 | |
| 813 | // Duplicate `offset`, where we're writing, and `init` what we're |
| 814 | // writing, into temporary registers. These are used by `emit_table_set` |
| 815 | // below. |
| 816 | let tmp_index = self.context.any_gpr(self.masm)?; |
| 817 | let tmp_init = self.context.any_gpr(self.masm)?; |
| 818 | self.masm |
| 819 | .mov(writable!(tmp_index), offset.reg.into(), OperandSize::S64)?; |
| 820 | self.masm |
| 821 | .mov(writable!(tmp_init), init.reg.into(), OperandSize::S64)?; |
| 822 | |
| 823 | // Spill all this loop's variables onto the stack. |
| 824 | self.context.stack.push(TypedReg::i64(len.reg).into()); |
| 825 | self.context.stack.push(TypedReg::i64(init.reg).into()); |
| 826 | self.context.stack.push(TypedReg::i64(offset.reg).into()); |
| 827 | |
| 828 | // Emit `table.set`, consuming our temporary registers. |
| 829 | self.context.stack.push(TypedReg::i64(tmp_index).into()); |
| 830 | self.context.stack.push(TypedReg::i64(tmp_init).into()); |
no test coverage detected