https://github.com/turran updated 
https://github.com/llvm/llvm-project/pull/153168

>From 69abea14899b11e7caf5da4ec54b69ed593616e5 Mon Sep 17 00:00:00 2001
From: Jorge Zapata <[email protected]>
Date: Mon, 23 Jun 2025 12:53:57 +0200
Subject: [PATCH 1/5] [wasm] Support different signature function pointers

---
 clang/lib/CodeGen/CGCall.cpp              |  20 ++
 clang/lib/CodeGen/CGExprConstant.cpp      |  13 ++
 clang/lib/CodeGen/TargetInfo.h            |  11 ++
 clang/lib/CodeGen/Targets/WebAssembly.cpp | 222 ++++++++++++++++++++++
 4 files changed, 266 insertions(+)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index b959982809911..8eb3afee08ff5 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4932,6 +4932,26 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, 
const Expr *E,
     return;
   }
 
+  // For WebAssembly target we need to create thunk functions
+  // to properly handle function pointers args with a different signature.
+  // Due to opaque pointers, this can not be handled in LLVM
+  // (WebAssemblyFixFunctionBitcast) anymore
+  if (CGM.getTriple().isWasm() && type->isFunctionPointerType()) {
+    if (const DeclRefExpr *DRE =
+            CGM.getTargetCodeGenInfo().getWasmFunctionDeclRefExpr(
+                E, CGM.getContext())) {
+      llvm::Value *V = EmitLValue(DRE).getPointer(*this);
+      llvm::Function *Thunk =
+          CGM.getTargetCodeGenInfo().getOrCreateWasmFunctionPointerThunk(
+              CGM, V, DRE->getDecl()->getType(), type);
+      if (Thunk) {
+        RValue R = RValue::get(Thunk);
+        args.add(R, type);
+        return;
+      }
+    }
+  }
+
   args.add(EmitAnyExprToTemp(E), type);
 }
 
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index a96c1518d2a1d..956f780ad71e3 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2243,6 +2243,19 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
 
     if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
       llvm::Constant *C = CGM.getRawFunctionPointer(FD);
+      // ForWebAssembly target we need to create thunk functions
+      // to properly handle function pointers args with a different signature
+      // Due to opaque pointers, this can not be handled in LLVM
+      // (WebAssemblyFixFunctionBitcast) anymore
+      if (CGM.getTriple().isWasm() && DestType->isFunctionPointerType()) {
+        llvm::Function *Thunk =
+            CGM.getTargetCodeGenInfo().getOrCreateWasmFunctionPointerThunk(
+                CGM, C, D->getType(), DestType);
+        if (Thunk) {
+          C = Thunk;
+        }
+      }
+
       if (FD->getType()->isCFIUncheckedCalleeFunctionType())
         C = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(C));
       return PtrAuthSign(C);
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index d0edae1295094..962178b2f549c 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -421,6 +421,17 @@ class TargetCodeGenInfo {
   /// Return the WebAssembly funcref reference type.
   virtual llvm::Type *getWasmFuncrefReferenceType() const { return nullptr; }
 
+  virtual const DeclRefExpr *getWasmFunctionDeclRefExpr(const Expr *E,
+                                                        ASTContext &Ctx) const 
{
+    return nullptr;
+  }
+
+  virtual llvm::Function *getOrCreateWasmFunctionPointerThunk(
+      CodeGenModule &CGM, llvm::Value *OriginalFnPtr, QualType SrcType,
+      QualType DstType) const {
+    return nullptr;
+  }
+
   /// Emit the device-side copy of the builtin surface type.
   virtual bool emitCUDADeviceBuiltinSurfaceDeviceCopy(CodeGenFunction &CGF,
                                                       LValue Dst,
diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp 
b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index ac8dcd2a0540a..a63448aea1b0e 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -9,9 +9,14 @@
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
 
+#include "clang/AST/ParentMapContext.h"
+#include <sstream>
+
 using namespace clang;
 using namespace clang::CodeGen;
 
+#define DEBUG_TYPE "clang-target-wasm"
+
 
//===----------------------------------------------------------------------===//
 // WebAssembly ABI Implementation
 //
@@ -93,6 +98,112 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
   virtual llvm::Type *getWasmFuncrefReferenceType() const override {
     return llvm::Type::getWasm_FuncrefTy(getABIInfo().getVMContext());
   }
+
+  virtual const DeclRefExpr *
+  getWasmFunctionDeclRefExpr(const Expr *E, ASTContext &Ctx) const override {
+    // Go down in the tree until finding the DeclRefExpr
+    const DeclRefExpr *DRE = findDeclRefExpr(E);
+    if (!DRE)
+      return nullptr;
+
+    // Final case. The argument is a declared function
+    if (isa<FunctionDecl>(DRE->getDecl())) {
+      return DRE;
+    }
+
+    // Complex case. The argument is a variable, we need to check
+    // every assignment of the variable and see if we are bitcasting
+    // or not.
+    if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+      DRE = findDeclRefExprForVarUp(E, VD, Ctx);
+      if (DRE)
+        return DRE;
+
+      // If no assignment exists on every parent scope, check for the
+      // initialization
+      if (!DRE && VD->hasInit()) {
+        return getWasmFunctionDeclRefExpr(VD->getInit(), Ctx);
+      }
+    }
+
+    return nullptr;
+  }
+
+  virtual llvm::Function *getOrCreateWasmFunctionPointerThunk(
+      CodeGenModule &CGM, llvm::Value *OriginalFnPtr, QualType SrcType,
+      QualType DstType) const override {
+
+    // Get the signatures
+    const FunctionProtoType *SrcProtoType = 
SrcType->getAs<FunctionProtoType>();
+    const FunctionProtoType *DstProtoType = DstType->getAs<PointerType>()
+                                                ->getPointeeType()
+                                                ->getAs<FunctionProtoType>();
+
+    // This should only work for different number of arguments
+    if (DstProtoType->getNumParams() <= SrcProtoType->getNumParams())
+      return nullptr;
+
+    // Get the llvm function types
+    llvm::FunctionType *DstFunctionType = llvm::cast<llvm::FunctionType>(
+        CGM.getTypes().ConvertType(QualType(DstProtoType, 0)));
+    llvm::FunctionType *SrcFunctionType = llvm::cast<llvm::FunctionType>(
+        CGM.getTypes().ConvertType(QualType(SrcProtoType, 0)));
+
+    // Construct the Thunk function with the Target (destination) signature
+    std::string ThunkName = getThunkName(OriginalFnPtr->getName().str(),
+                                         DstProtoType, CGM.getContext());
+    llvm::Module &M = CGM.getModule();
+    llvm::Function *Thunk = llvm::Function::Create(
+        DstFunctionType, llvm::Function::InternalLinkage, ThunkName, M);
+
+    // Build the thunk body
+    llvm::IRBuilder<> Builder(
+        llvm::BasicBlock::Create(M.getContext(), "entry", Thunk));
+
+    // Gather the arguments for calling the original function
+    std::vector<llvm::Value *> CallArgs;
+    unsigned CallN = SrcProtoType->getNumParams();
+
+    auto ArgIt = Thunk->arg_begin();
+    for (unsigned i = 0; i < CallN && ArgIt != Thunk->arg_end(); ++i, ++ArgIt) 
{
+      llvm::Value *A = &*ArgIt;
+      CallArgs.push_back(A);
+    }
+
+    // Create the call to the original function pointer
+    llvm::CallInst *Call =
+        Builder.CreateCall(SrcFunctionType, OriginalFnPtr, CallArgs);
+
+    // Handle return type
+    llvm::Type *ThunkRetTy = DstFunctionType->getReturnType();
+
+    if (ThunkRetTy->isVoidTy()) {
+      Builder.CreateRetVoid();
+    } else {
+      llvm::Value *Ret = Call;
+      if (Ret->getType() != ThunkRetTy)
+        Ret = Builder.CreateBitCast(Ret, ThunkRetTy);
+      Builder.CreateRet(Ret);
+    }
+    LLVM_DEBUG(llvm::dbgs() << "getOrCreateWasmFunctionPointerThunk:"
+                            << " from " << OriginalFnPtr->getName().str()
+                            << " to " << ThunkName << "\n");
+    return Thunk;
+  }
+
+private:
+  // Build the thunk name: "%s_{type1}_{type2}_..."
+  std::string getThunkName(std::string OrigName,
+                           const FunctionProtoType *DstProto,
+                           const ASTContext &Ctx) const;
+  std::string sanitizeTypeString(const std::string &typeStr) const;
+  std::string getTypeName(const QualType &qt, const ASTContext &Ctx) const;
+  const DeclRefExpr *findDeclRefExpr(const Expr *E) const;
+  const DeclRefExpr *findDeclRefExprForVarDown(const Stmt *Parent,
+                                               const VarDecl *V,
+                                               ASTContext &Ctx) const;
+  const DeclRefExpr *findDeclRefExprForVarUp(const Expr *E, const VarDecl *V,
+                                             ASTContext &Ctx) const;
 };
 
 /// Classify argument of given type \p Ty.
@@ -173,3 +284,114 @@ CodeGen::createWebAssemblyTargetCodeGenInfo(CodeGenModule 
&CGM,
                                             WebAssemblyABIKind K) {
   return std::make_unique<WebAssemblyTargetCodeGenInfo>(CGM.getTypes(), K);
 }
+
+// Helper to sanitize type name string for use in function name
+std::string WebAssemblyTargetCodeGenInfo::sanitizeTypeString(
+    const std::string &typeStr) const {
+  std::string s;
+  for (char c : typeStr) {
+    if (isalnum(c))
+      s += c;
+    else if (c == ' ')
+      s += '_';
+    else
+      s += '_';
+  }
+  return s;
+}
+
+// Helper to generate the type string from QualType
+std::string
+WebAssemblyTargetCodeGenInfo::getTypeName(const QualType &qt,
+                                          const ASTContext &Ctx) const {
+  PrintingPolicy Policy(Ctx.getLangOpts());
+  Policy.SuppressTagKeyword = true;
+  Policy.SuppressScope = true;
+  Policy.AnonymousTagLocations = false;
+  std::string typeStr = qt.getAsString(Policy);
+  return sanitizeTypeString(typeStr);
+}
+
+std::string
+WebAssemblyTargetCodeGenInfo::getThunkName(std::string OrigName,
+                                           const FunctionProtoType *DstProto,
+                                           const ASTContext &Ctx) const {
+  std::ostringstream oss;
+  oss << "__" << OrigName;
+  for (unsigned i = 0; i < DstProto->getNumParams(); ++i) {
+    oss << "_" << getTypeName(DstProto->getParamType(i), Ctx);
+  }
+  return oss.str();
+}
+
+/// Recursively find the first DeclRefExpr in an Expr subtree.
+/// Returns nullptr if not found.
+const DeclRefExpr *
+WebAssemblyTargetCodeGenInfo::findDeclRefExpr(const Expr *E) const {
+  if (!E)
+    return nullptr;
+
+  // In case it is a function call, abort
+  if (isa<CallExpr>(E))
+    return nullptr;
+
+  // If this node is a DeclRefExpr, return it.
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+    return DRE;
+
+  // Otherwise, recurse into children.
+  for (const Stmt *Child : E->children()) {
+    if (const auto *ChildExpr = dyn_cast_or_null<Expr>(Child)) {
+      if (const DeclRefExpr *Found = findDeclRefExpr(ChildExpr))
+        return Found;
+    }
+  }
+  return nullptr;
+}
+
+const DeclRefExpr *WebAssemblyTargetCodeGenInfo::findDeclRefExprForVarDown(
+    const Stmt *Parent, const VarDecl *V, ASTContext &Ctx) const {
+  if (!Parent)
+    return nullptr;
+
+  // Find down every assignment of V
+  // FIXME we need to stop before the expression where V is used
+  const BinaryOperator *A = nullptr;
+  for (const Stmt *Child : Parent->children()) {
+    if (const auto *BO = dyn_cast_or_null<BinaryOperator>(Child)) {
+      if (!BO->isAssignmentOp())
+        continue;
+      auto *LHS = llvm::dyn_cast<DeclRefExpr>(BO->getLHS());
+      if (LHS && LHS->getDecl() == V) {
+        A = BO;
+      }
+    }
+  }
+
+  // We have an assignment of the Var, recurse in it
+  if (A) {
+    return getWasmFunctionDeclRefExpr(A->getRHS(), Ctx);
+  }
+
+  return nullptr;
+}
+
+const DeclRefExpr *WebAssemblyTargetCodeGenInfo::findDeclRefExprForVarUp(
+    const Expr *E, const VarDecl *V, ASTContext &Ctx) const {
+  const clang::Stmt *cur = E;
+  while (cur) {
+    auto parents = Ctx.getParentMapContext().getParents(*cur);
+    if (parents.empty())
+      break;
+    const clang::Stmt *parentStmt = parents[0].get<clang::Stmt>();
+    if (!parentStmt)
+      break;
+    if (const auto *CS = dyn_cast<clang::CompoundStmt>(parentStmt)) {
+      const DeclRefExpr *DRE = findDeclRefExprForVarDown(CS, V, Ctx);
+      if (DRE)
+        return DRE;
+    }
+    cur = parentStmt;
+  }
+  return nullptr;
+}
\ No newline at end of file

>From 5d3865fd34f19df6d2fca87e9df31b9e168abdb5 Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <[email protected]>
Date: Mon, 11 Aug 2025 14:47:55 +0200
Subject: [PATCH 2/5] Prefer use of Wasm signatures when building the thunk
 name

---
 clang/lib/CodeGen/Targets/WebAssembly.cpp | 66 ++++++++++++-----------
 1 file changed, 36 insertions(+), 30 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp 
b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index a63448aea1b0e..10e3fd4c5a8df 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -10,7 +10,6 @@
 #include "TargetInfo.h"
 
 #include "clang/AST/ParentMapContext.h"
-#include <sstream>
 
 using namespace clang;
 using namespace clang::CodeGen;
@@ -192,10 +191,11 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
   }
 
 private:
-  // Build the thunk name: "%s_{type1}_{type2}_..."
+  // Build the thunk name: "%s_{OrigName}_{WasmSig}"
   std::string getThunkName(std::string OrigName,
                            const FunctionProtoType *DstProto,
                            const ASTContext &Ctx) const;
+  char getTypeSig(const QualType &Ty, const ASTContext &Ctx) const;
   std::string sanitizeTypeString(const std::string &typeStr) const;
   std::string getTypeName(const QualType &qt, const ASTContext &Ctx) const;
   const DeclRefExpr *findDeclRefExpr(const Expr *E) const;
@@ -285,43 +285,49 @@ CodeGen::createWebAssemblyTargetCodeGenInfo(CodeGenModule 
&CGM,
   return std::make_unique<WebAssemblyTargetCodeGenInfo>(CGM.getTypes(), K);
 }
 
-// Helper to sanitize type name string for use in function name
-std::string WebAssemblyTargetCodeGenInfo::sanitizeTypeString(
-    const std::string &typeStr) const {
-  std::string s;
-  for (char c : typeStr) {
-    if (isalnum(c))
-      s += c;
-    else if (c == ' ')
-      s += '_';
-    else
-      s += '_';
+// Helper to get the type signature character for a given QualType
+// Returns a character that represents the given QualType in a wasm signature.
+// See getInvokeSig() in WebAssemblyAsmPrinter for related logic.
+char WebAssemblyTargetCodeGenInfo::getTypeSig(const QualType &Ty,
+                                              const ASTContext &Ctx) const {
+  if (Ty->isAnyPointerType()) {
+    return Ctx.getTypeSize(Ctx.VoidPtrTy) == 32 ? 'i' : 'j';
+  }
+  if (Ty->isIntegerType()) {
+    return Ctx.getTypeSize(Ty) <= 32 ? 'i' : 'j';
+  }
+  if (Ty->isFloatingType()) {
+    return Ctx.getTypeSize(Ty) <= 32 ? 'f' : 'd';
+  }
+  if (Ty->isVectorType()) {
+    return 'V';
+  }
+  if (Ty->isWebAssemblyTableType()) {
+    return 'F';
+  }
+  if (Ty->isWebAssemblyExternrefType()) {
+    return 'X';
   }
-  return s;
-}
 
-// Helper to generate the type string from QualType
-std::string
-WebAssemblyTargetCodeGenInfo::getTypeName(const QualType &qt,
-                                          const ASTContext &Ctx) const {
-  PrintingPolicy Policy(Ctx.getLangOpts());
-  Policy.SuppressTagKeyword = true;
-  Policy.SuppressScope = true;
-  Policy.AnonymousTagLocations = false;
-  std::string typeStr = qt.getAsString(Policy);
-  return sanitizeTypeString(typeStr);
+  llvm_unreachable("Unhandled QualType");
 }
 
 std::string
 WebAssemblyTargetCodeGenInfo::getThunkName(std::string OrigName,
                                            const FunctionProtoType *DstProto,
                                            const ASTContext &Ctx) const {
-  std::ostringstream oss;
-  oss << "__" << OrigName;
+
+  std::string ThunkName = "__" + OrigName + "_";
+  QualType RetTy = DstProto->getReturnType();
+  if (RetTy->isVoidType()) {
+    ThunkName += 'v';
+  } else {
+    ThunkName += getTypeSig(RetTy, Ctx);
+  }
   for (unsigned i = 0; i < DstProto->getNumParams(); ++i) {
-    oss << "_" << getTypeName(DstProto->getParamType(i), Ctx);
+    ThunkName += getTypeSig(DstProto->getParamType(i), Ctx);
   }
-  return oss.str();
+  return ThunkName;
 }
 
 /// Recursively find the first DeclRefExpr in an Expr subtree.
@@ -394,4 +400,4 @@ const DeclRefExpr 
*WebAssemblyTargetCodeGenInfo::findDeclRefExprForVarUp(
     cur = parentStmt;
   }
   return nullptr;
-}
\ No newline at end of file
+}

>From e811555ab4f7f8a33bdfec7de879b77c4553c559 Mon Sep 17 00:00:00 2001
From: Jorge Zapata <[email protected]>
Date: Mon, 11 Aug 2025 16:31:04 +0200
Subject: [PATCH 3/5] [wasm] Add a thunk cache

---
 clang/lib/CodeGen/Targets/WebAssembly.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp 
b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index 10e3fd4c5a8df..4aa62948c4685 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -8,6 +8,7 @@
 
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
+#include "llvm/ADT/StringMap.h"
 
 #include "clang/AST/ParentMapContext.h"
 
@@ -56,6 +57,7 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
       : TargetCodeGenInfo(std::make_unique<WebAssemblyABIInfo>(CGT, K)) {
     SwiftInfo =
         std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
+    ThunkCache = llvm::StringMap<llvm::Function *>();
   }
 
   void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
@@ -151,6 +153,16 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
     // Construct the Thunk function with the Target (destination) signature
     std::string ThunkName = getThunkName(OriginalFnPtr->getName().str(),
                                          DstProtoType, CGM.getContext());
+    // Check if we already have a thunk for this function
+    if (auto It = ThunkCache.find(ThunkName); It != ThunkCache.end()) {
+      LLVM_DEBUG(llvm::dbgs() << "getOrCreateWasmFunctionPointerThunk: "
+                              << "found existing thunk for "
+                              << OriginalFnPtr->getName().str() << " as "
+                              << ThunkName << "\n");
+      return It->second;
+    }
+
+    // Create the thunk function
     llvm::Module &M = CGM.getModule();
     llvm::Function *Thunk = llvm::Function::Create(
         DstFunctionType, llvm::Function::InternalLinkage, ThunkName, M);
@@ -187,10 +199,14 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
     LLVM_DEBUG(llvm::dbgs() << "getOrCreateWasmFunctionPointerThunk:"
                             << " from " << OriginalFnPtr->getName().str()
                             << " to " << ThunkName << "\n");
+    // Cache the thunk
+    ThunkCache[ThunkName] = Thunk;
     return Thunk;
   }
 
 private:
+  // The thunk cache
+  mutable llvm::StringMap<llvm::Function *> ThunkCache;
   // Build the thunk name: "%s_{OrigName}_{WasmSig}"
   std::string getThunkName(std::string OrigName,
                            const FunctionProtoType *DstProto,

>From c17adfad2880eb9c70573f9ccc1b4062d9c89f58 Mon Sep 17 00:00:00 2001
From: Jorge Zapata <[email protected]>
Date: Tue, 12 Aug 2025 12:02:29 +0200
Subject: [PATCH 4/5] [wasm] Add function pointer thunk tests

---
 .../CodeGenWebAssembly/function-pointer-arg.c | 25 ++++++++++++++++
 .../function-pointer-field.c                  | 30 +++++++++++++++++++
 2 files changed, 55 insertions(+)
 create mode 100644 clang/test/CodeGenWebAssembly/function-pointer-arg.c
 create mode 100644 clang/test/CodeGenWebAssembly/function-pointer-field.c

diff --git a/clang/test/CodeGenWebAssembly/function-pointer-arg.c 
b/clang/test/CodeGenWebAssembly/function-pointer-arg.c
new file mode 100644
index 0000000000000..ff7b4186bbf7b
--- /dev/null
+++ b/clang/test/CodeGenWebAssembly/function-pointer-arg.c
@@ -0,0 +1,25 @@
+// REQUIRES: webassembly-registered-target
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -O0 -o - %s | 
FileCheck %s
+
+// Test of function pointer bitcast in a function argument with different 
argument number in wasm32
+
+#define FUNCTION_POINTER(f) ((FunctionPointer)(f))
+typedef int (*FunctionPointer)(int a, int b);
+
+int fp_as_arg(FunctionPointer fp, int a, int b) {
+  return fp(a, b);
+}
+
+int fp_less(int a) {
+  return a;
+}
+
+// CHECK-LABEL: @test
+// CHECK: call i32 @fp_as_arg(ptr noundef @__fp_less_iii, i32 noundef 10, i32 
noundef 20)
+void test() {
+  fp_as_arg(FUNCTION_POINTER(fp_less), 10, 20);
+}
+
+// CHECK: define internal i32 @__fp_less_iii(i32 %0, i32 %1)
+// CHECK: %2 = call i32 @fp_less(i32 %0)
+// CHECK: ret i32 %2
\ No newline at end of file
diff --git a/clang/test/CodeGenWebAssembly/function-pointer-field.c 
b/clang/test/CodeGenWebAssembly/function-pointer-field.c
new file mode 100644
index 0000000000000..103a265ebf5fb
--- /dev/null
+++ b/clang/test/CodeGenWebAssembly/function-pointer-field.c
@@ -0,0 +1,30 @@
+// REQUIRES: webassembly-registered-target
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -O0 -o - %s | 
FileCheck %s
+
+// Test of function pointer bitcast in a struct field with different argument 
number in wasm32
+
+#define FUNCTION_POINTER(f) ((FunctionPointer)(f))
+typedef int (*FunctionPointer)(int a, int b); 
+
+// CHECK: @__const.test.sfp = private unnamed_addr constant 
%struct._StructWithFunctionPointer { ptr @__fp_less_iii }, align 4
+
+typedef struct _StructWithFunctionPointer {
+  FunctionPointer fp;
+} StructWithFunctionPointer;
+
+int fp_less(int a) {
+  return a;
+}
+                                                  
+// CHECK-LABEL: @test
+void test() {
+  StructWithFunctionPointer sfp = {
+    FUNCTION_POINTER(fp_less)
+  };
+
+  int a1 = sfp.fp(10, 20);
+}
+
+// CHECK: define internal i32 @__fp_less_iii(i32 %0, i32 %1)
+// CHECK: %2 = call i32 @fp_less(i32 %0)
+// CHECK: ret i32 %2

>From ae6740a9e61829b6617a241a9c9b3279a94989ba Mon Sep 17 00:00:00 2001
From: Jorge Zapata <[email protected]>
Date: Wed, 3 Sep 2025 17:14:02 +0200
Subject: [PATCH 5/5] [wasm] Add -fwasm-fix-function-bitcasts to enable the
 thunk generation

---
 clang/include/clang/Basic/LangOptions.def              | 2 ++
 clang/include/clang/Driver/Options.td                  | 6 ++++++
 clang/lib/CodeGen/CGCall.cpp                           | 3 ++-
 clang/lib/CodeGen/CGExprConstant.cpp                   | 4 +++-
 clang/lib/Driver/ToolChains/WebAssembly.cpp            | 4 ++++
 clang/test/CodeGenWebAssembly/function-pointer-arg.c   | 2 +-
 clang/test/CodeGenWebAssembly/function-pointer-field.c | 2 +-
 7 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 08d98a77e0252..bc43097c42923 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -495,6 +495,8 @@ LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, 
"Experimental lifetime safety
 
 LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector 
type")
 
+LANGOPT(WasmFixFunctionBitcasts, 1, 0, Compatible, "Enable auto-generation of 
thunks for mismatched function pointer casts in WebAssembly")
+
 #undef LANGOPT
 #undef ENUM_LANGOPT
 #undef VALUE_LANGOPT
diff --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index 6aab43c9ed57f..b1f384e4c629d 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9457,6 +9457,7 @@ def fvk_use_scalar_layout
     : DXCFlag<"fvk-use-scalar-layout">,
       HelpText<"Use scalar memory layout for Vulkan resources.">;
 
+// WebAssembly-only Options
 def no_wasm_opt : Flag<["--"], "no-wasm-opt">,
   Group<m_Group>,
   HelpText<"Disable the wasm-opt optimizer">,
@@ -9465,3 +9466,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
   Group<m_Group>,
   HelpText<"Enable the wasm-opt optimizer (default)">,
   MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+def fwasm_fix_function_bitcasts : Flag<["-"], "fwasm-fix-function-bitcasts">,
+  Group<f_Group>,
+  HelpText<"Enable auto-generation of thunks for mismatched function pointer 
casts in WebAssembly">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<LangOpts<"WasmFixFunctionBitcasts">>;
\ No newline at end of file
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 8eb3afee08ff5..f16d0ac745ddd 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4936,7 +4936,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, 
const Expr *E,
   // to properly handle function pointers args with a different signature.
   // Due to opaque pointers, this can not be handled in LLVM
   // (WebAssemblyFixFunctionBitcast) anymore
-  if (CGM.getTriple().isWasm() && type->isFunctionPointerType()) {
+  if (CGM.getTriple().isWasm() && CGM.getLangOpts().WasmFixFunctionBitcasts &&
+      type->isFunctionPointerType()) {
     if (const DeclRefExpr *DRE =
             CGM.getTargetCodeGenInfo().getWasmFunctionDeclRefExpr(
                 E, CGM.getContext())) {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 956f780ad71e3..22e0eedd36770 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2247,7 +2247,9 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
       // to properly handle function pointers args with a different signature
       // Due to opaque pointers, this can not be handled in LLVM
       // (WebAssemblyFixFunctionBitcast) anymore
-      if (CGM.getTriple().isWasm() && DestType->isFunctionPointerType()) {
+      if (CGM.getTriple().isWasm() &&
+        CGM.getLangOpts().WasmFixFunctionBitcasts &&
+        DestType->isFunctionPointerType()) {
         llvm::Function *Thunk =
             CGM.getTargetCodeGenInfo().getOrCreateWasmFunctionPointerThunk(
                 CGM, C, D->getType(), DestType);
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp 
b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index 5054868b5ff4d..8e01dba539301 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -412,6 +412,10 @@ void WebAssembly::addClangTargetOptions(const ArgList 
&DriverArgs,
     CC1Args.push_back("-wasm-enable-eh");
   }
 
+  if (DriverArgs.getLastArg(options::OPT_fwasm_fix_function_bitcasts)) {
+    CC1Args.push_back("-fwasm-fix-function-bitcasts");
+  }
+
   for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
     StringRef Opt = A->getValue(0);
     if (Opt.starts_with("-emscripten-cxx-exceptions-allowed")) {
diff --git a/clang/test/CodeGenWebAssembly/function-pointer-arg.c 
b/clang/test/CodeGenWebAssembly/function-pointer-arg.c
index ff7b4186bbf7b..4ca85f11fe15f 100644
--- a/clang/test/CodeGenWebAssembly/function-pointer-arg.c
+++ b/clang/test/CodeGenWebAssembly/function-pointer-arg.c
@@ -1,5 +1,5 @@
 // REQUIRES: webassembly-registered-target
-// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -O0 -o - %s | 
FileCheck %s
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -O0 
-fwasm-fix-function-bitcasts -o - %s | FileCheck %s
 
 // Test of function pointer bitcast in a function argument with different 
argument number in wasm32
 
diff --git a/clang/test/CodeGenWebAssembly/function-pointer-field.c 
b/clang/test/CodeGenWebAssembly/function-pointer-field.c
index 103a265ebf5fb..8c8424dc922e8 100644
--- a/clang/test/CodeGenWebAssembly/function-pointer-field.c
+++ b/clang/test/CodeGenWebAssembly/function-pointer-field.c
@@ -1,5 +1,5 @@
 // REQUIRES: webassembly-registered-target
-// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -O0 -o - %s | 
FileCheck %s
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -O0 
-fwasm-fix-function-bitcasts -o - %s | FileCheck %s
 
 // Test of function pointer bitcast in a struct field with different argument 
number in wasm32
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to