diff --git a/src/passes/Memory64Lowering.cpp b/src/passes/Memory64Lowering.cpp index 9d7a2a4252c..dd0fa09d7fa 100644 --- a/src/passes/Memory64Lowering.cpp +++ b/src/passes/Memory64Lowering.cpp @@ -224,8 +224,31 @@ struct Memory64Lowering : public WalkerPass> { if (table->is64()) { wrapTableAddress64(curr->delta, curr->table); auto* size = static_cast(curr); - extendTableAddress64(size, curr->table); - replaceCurrent(size); + // TableGrow returns -1 in case of failure. We cannot just use + // extend_32_u in this case so we handle it the same way as MemoryGrow: + // + // (if (result i64) + // (i32.eq (i32.const -1) (local.tee $tmp (table.grow X))) + // (then + // (i64.const -1) + // ) + // (else + // (i64.extend_i32_u (local.get $tmp)) + // ) + // ) + Builder builder(module); + auto tmp = builder.addVar(getFunction(), Type::i32); + Expression* isMinusOne = + builder.makeBinary(EqInt32, + builder.makeConst(int32_t(-1)), + builder.makeLocalTee(tmp, size, Type::i32)); + auto* newSize = builder.makeLocalGet(tmp, Type::i32); + Expression* ifExp = + builder.makeIf(isMinusOne, + builder.makeConst(int64_t(-1)), + builder.makeUnary(UnaryOp::ExtendUInt32, newSize)); + curr->type = Type::i32; + replaceCurrent(ifExp); } } diff --git a/test/lit/passes/memory64-lowering.wast b/test/lit/passes/memory64-lowering.wast index a7d904d9f95..2745c04b729 100644 --- a/test/lit/passes/memory64-lowering.wast +++ b/test/lit/passes/memory64-lowering.wast @@ -399,16 +399,33 @@ ) ;; CHECK: (func $test_table_grow (result i64) - ;; CHECK-NEXT: (i64.extend_i32_u - ;; CHECK-NEXT: (table.grow $t64 - ;; CHECK-NEXT: (ref.null nofunc) - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const 10) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (if (result i64) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (table.grow $t64 + ;; CHECK-NEXT: (ref.null nofunc) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i64.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (i64.extend_i32_u + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test_table_grow (result i64) + ;; table.grow returns -1 on failure. The lowering must check for -1 and + ;; return i64(-1) instead of i64.extend_i32_u(i32(-1)) which would be + ;; 4294967295. (table.grow $t64 (ref.null func) (i64.const 10)) )