From 9f8ac5639287709878407264d0ec8a6e07cb10ac Mon Sep 17 00:00:00 2001 From: Yi LIU Date: Fri, 13 Feb 2026 07:55:01 +0800 Subject: [PATCH] Fix Memory64Lowering table.copy size wrapping for mixed tables visitTableCopy wrapped curr->size using only the dest table's type, but the size operand is i64 only when both source and dest tables are 64-bit. When copying between tables of different address types, wrapping an i32 size as if it were i64 causes an assertion failure. Check both tables before wrapping the size operand, matching the type logic in child-typer.h. --- src/passes/Memory64Lowering.cpp | 7 ++- .../passes/memory64-lowering-table-copy.wast | 63 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/lit/passes/memory64-lowering-table-copy.wast diff --git a/src/passes/Memory64Lowering.cpp b/src/passes/Memory64Lowering.cpp index 9d7a2a4252c..e588de2cce5 100644 --- a/src/passes/Memory64Lowering.cpp +++ b/src/passes/Memory64Lowering.cpp @@ -237,7 +237,12 @@ struct Memory64Lowering : public WalkerPass> { void visitTableCopy(TableCopy* curr) { wrapTableAddress64(curr->dest, curr->destTable); wrapTableAddress64(curr->source, curr->sourceTable); - wrapTableAddress64(curr->size, curr->destTable); + // The size type is i64 only when both tables are 64-bit. + auto& module = *getModule(); + if (module.getTable(curr->destTable)->is64() && + module.getTable(curr->sourceTable)->is64()) { + wrapAddress64(curr->size, curr->destTable, true); + } } void visitTableInit(TableInit* curr) { diff --git a/test/lit/passes/memory64-lowering-table-copy.wast b/test/lit/passes/memory64-lowering-table-copy.wast new file mode 100644 index 00000000000..afdaeb9f11f --- /dev/null +++ b/test/lit/passes/memory64-lowering-table-copy.wast @@ -0,0 +1,63 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt --memory64-lowering --enable-memory64 --enable-reference-types --enable-bulk-memory -S -o - | filecheck %s + +;; Test that table.copy with mixed 32/64-bit tables correctly handles the +;; size operand type. +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (table $t64 10 funcref) + (table $t64 i64 10 funcref) + ;; CHECK: (table $t32 10 funcref) + (table $t32 10 funcref) + + ;; CHECK: (func $table-copy-mixed-64-to-32 + ;; CHECK-NEXT: (table.copy $t32 $t64 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $table-copy-mixed-64-to-32 + ;; Copy from 64-bit table to 32-bit table. The size is i32 because not + ;; both tables are 64-bit, so no wrapping of size should occur. + (table.copy $t32 $t64 (i32.const 0) (i64.const 0) (i32.const 5)) + ) + + ;; CHECK: (func $table-copy-mixed-32-to-64 + ;; CHECK-NEXT: (table.copy $t64 $t32 + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $table-copy-mixed-32-to-64 + ;; Copy from 32-bit table to 64-bit table. The size is i32 because not + ;; both tables are 64-bit, so no wrapping of size should occur. + (table.copy $t64 $t32 (i64.const 0) (i32.const 0) (i32.const 5)) + ) + + ;; CHECK: (func $table-copy-both-64 + ;; CHECK-NEXT: (table.copy $t64 $t64 + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $table-copy-both-64 + ;; Copy between same 64-bit table. All operands including size are i64 + ;; and should be wrapped. + (table.copy $t64 $t64 (i64.const 0) (i64.const 5) (i64.const 3)) + ) +)