From f3c7f6462d8bc8087a8a842440e28a1858b640c5 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 21 Mar 2025 11:51:35 -0700 Subject: [PATCH] Fix RefFunc type updating in I64ToI32Lowering We recently started updating RefFunc types in I64ToI32Lowering to ensure that their types matched the updated types of their functions. But the way we updated the types of RefFunc expressions and Functions were not the same. When updating Functions that return i64, we replace the result type with i32 and use a global to propagate the remaining bits to the caller. Previously when updating RefFunc result types, we would instead split i64s into pairs of i32s, depending on multivalue to lower the type. Update the logic for updating RefFunc results to match the existing logic for updating Functions. --- src/passes/I64ToI32Lowering.cpp | 42 ++++++++++--------- .../passes/flatten_i64-to-i32-lowering.wast | 23 ++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp index 67dfd36d849..0cc6b2bd35b 100644 --- a/src/passes/I64ToI32Lowering.cpp +++ b/src/passes/I64ToI32Lowering.cpp @@ -300,33 +300,35 @@ struct I64ToI32Lowering : public WalkerPass> { void visitRefFunc(RefFunc* curr) { auto sig = curr->type.getHeapType().getSignature(); - auto lowerTypes = [](Type types) { - bool hasI64 = false; - for (auto t : types) { - if (t == Type::i64) { - hasI64 = true; - break; - } - } - if (!hasI64) { - return types; + bool hasI64Param = false; + for (auto t : sig.params) { + if (t == Type::i64) { + hasI64Param = true; + break; } - std::vector newTypes; - for (auto t : types) { + } + auto params = sig.params; + if (hasI64Param) { + std::vector newParams; + for (auto t : sig.params) { if (t == Type::i64) { - newTypes.push_back(Type::i32); - newTypes.push_back(Type::i32); + newParams.push_back(Type::i32); + newParams.push_back(Type::i32); } else { - newTypes.push_back(t); + newParams.push_back(t); } } - return Type(newTypes); + params = Type(newParams); }; + auto results = sig.results; + // Update the results the same way we do when visiting functions. We use a + // global rather than multivalue to lower i64 results. + if (results == Type::i64) { + results = Type::i32; + } - auto newParams = lowerTypes(sig.params); - auto newResults = lowerTypes(sig.results); - if (newParams != sig.params || newResults != sig.results) { - curr->type = curr->type.with(HeapType(Signature(newParams, newResults))); + if (params != sig.params || results != sig.results) { + curr->type = curr->type.with(HeapType(Signature(params, results))); } } diff --git a/test/lit/passes/flatten_i64-to-i32-lowering.wast b/test/lit/passes/flatten_i64-to-i32-lowering.wast index e5e556c79ab..beb15b2e46d 100644 --- a/test/lit/passes/flatten_i64-to-i32-lowering.wast +++ b/test/lit/passes/flatten_i64-to-i32-lowering.wast @@ -665,3 +665,26 @@ ) ) ) + +;; Make sure we update the ref.func in the table with the correct return type. +(module + (table 1 1 funcref) + + (elem (i32.const 0) $f) + + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (global $i64toi32_i32$HIGH_BITS (mut i32) (i32.const 0)) + + ;; CHECK: (table $0 1 1 funcref) + + ;; CHECK: (elem $0 (i32.const 0) $f) + + ;; CHECK: (func $f (type $0) (result i32) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $f (result i64) + (unreachable) + ) +)