================
@@ -170,8 +324,504 @@ RValue WebAssemblyABIInfo::EmitVAArg(CodeGenFunction
&CGF, Address VAListAddr,
/*AllowHigherAlign=*/true, Slot);
}
+// Generate wrapper name for runtime function pointer binding
+std::string WebAssemblyTargetCodeGenInfo::getRuntimeWrapperName(
+ const FunctionProtoType *SrcProto, const FunctionProtoType *DstProto,
+ const ASTContext &Ctx) const {
+ std::string Name = "__wasm_runtime_wrapper_";
+
+ // Encode source signature
+ QualType SrcRetTy = SrcProto->getReturnType();
+ if (SrcRetTy->isVoidType()) {
+ Name += 'v';
+ } else {
+ Name += getTypeSig(SrcRetTy, Ctx);
+ }
+ for (QualType ParamType : SrcProto->param_types()) {
+ Name += getTypeSig(ParamType, Ctx);
+ }
+
+ Name += "_to_";
+
+ // Encode destination signature
+ QualType DstRetTy = DstProto->getReturnType();
+ if (DstRetTy->isVoidType()) {
+ Name += 'v';
+ } else {
+ Name += getTypeSig(DstRetTy, Ctx);
+ }
+ for (QualType ParamType : DstProto->param_types()) {
+ Name += getTypeSig(ParamType, Ctx);
+ }
+
+ return Name;
+}
+
+// Emit runtime binding for function pointer cast
+// This handles cases like g_list_free_full where a runtime parameter
+// needs to be cast from fewer params to more params
+llvm::Value
*WebAssemblyTargetCodeGenInfo::emitWasmRuntimeFunctionPointerBinding(
+ CodeGenFunction &CGF, llvm::Value *FnPtr, QualType SrcType,
+ QualType DstType, bool IsImmediate) const {
+
+ const FunctionProtoType *SrcProto =
+ SrcType->getPointeeType()->getAs<FunctionProtoType>();
+ const FunctionProtoType *DstProto =
+ DstType->getPointeeType()->getAs<FunctionProtoType>();
+
+ if (!SrcProto || !DstProto)
+ return nullptr;
+
+ // Check parameter counts: source must have same or fewer params than
destination
+ // We can add parameters (caller provides them, we ignore extras when
calling source)
+ // We cannot remove parameters (caller doesn't provide them, we can't invent
values)
+ unsigned SrcParams = SrcProto->getNumParams();
+ unsigned DstParams = DstProto->getNumParams();
+
+ if (SrcParams > DstParams)
+ return nullptr; // Can't remove parameters
+
+ // Check return types: we can discard a return value but cannot invent one.
+ // Compare LLVM types (not C types) since wasm only cares about
i32/i64/f32/f64.
+ QualType SrcRetTy = SrcProto->getReturnType();
+ QualType DstRetTy = DstProto->getReturnType();
+ llvm::Type *SrcRetLLVMTy = CGF.CGM.getTypes().ConvertType(SrcRetTy);
+ llvm::Type *DstRetLLVMTy = CGF.CGM.getTypes().ConvertType(DstRetTy);
+ bool sameReturnType = SrcRetLLVMTy == DstRetLLVMTy;
+
+ if (!DstRetTy->isVoidType() && !sameReturnType)
+ return nullptr; // Can't invent return values
+
+ // Reject if signatures are identical (no adaptation needed)
+ if (SrcParams == DstParams && sameReturnType)
+ return nullptr;
+
+ // A null function pointer needs no wrapper — fall through to bitcast
+ if (isa<llvm::ConstantPointerNull>(FnPtr))
+ return nullptr;
+
+ LLVM_DEBUG(llvm::dbgs() << "emitWasmRuntimeFunctionPointerBinding: "
+ << "src params=" << SrcParams
+ << " dst params=" << DstParams << "\n");
+
+ llvm::Module &M = CGF.CGM.getModule();
+ llvm::LLVMContext &Context = M.getContext();
+ llvm::PointerType *PtrTy = llvm::PointerType::getUnqual(Context);
+ llvm::Type *I32Ty = llvm::IntegerType::getInt32Ty(Context);
+
+ // Pre-allocated pool: N wrapper functions + N TLS slots per signature pair.
+ // Each runtime invocation atomically claims a slot. This supports both
+ // "call immediately" and "store for later" patterns without overwrites.
+ static const unsigned POOL_SIZE = 64;
+
+ std::string WrapperName = getRuntimeWrapperName(SrcProto, DstProto,
CGF.CGM.getContext());
+
+ std::string SourceId = M.getSourceFileName();
+ if (SourceId.empty())
+ SourceId = M.getName();
+ for (char &C : SourceId)
+ if (!isalnum(C) && C != '_')
+ C = '_';
+ WrapperName += "_" + SourceId;
+
+ std::string PoolName = "__wasm_runtime_pool_" + WrapperName;
+
+ // Get or create pool globals (once per module per signature pair)
+ llvm::GlobalVariable *Counter = M.getNamedGlobal(PoolName + "_counter");
+ llvm::GlobalVariable *ImmediateSlot = M.getNamedGlobal(PoolName +
"_immediate_slot");
+ llvm::Function *ImmediateWrapper = M.getFunction(WrapperName + "_immediate");
+ llvm::ArrayType *SlotArrayTy = llvm::ArrayType::get(PtrTy, POOL_SIZE);
+ llvm::GlobalVariable *Slots = nullptr;
+ llvm::GlobalVariable *WrapperTable = nullptr;
+ llvm::FunctionType *SrcFnType = nullptr;
+ llvm::FunctionType *DstFnType = nullptr;
+
+ if (!Counter) {
+ SrcFnType = llvm::cast<llvm::FunctionType>(
+ CGF.CGM.getTypes().ConvertType(QualType(SrcProto, 0)));
+ DstFnType = llvm::cast<llvm::FunctionType>(
+ CGF.CGM.getTypes().ConvertType(QualType(DstProto, 0)));
+
+ Counter = new llvm::GlobalVariable(
+ M, I32Ty, false, llvm::GlobalValue::InternalLinkage,
+ llvm::ConstantInt::get(I32Ty, 0), PoolName + "_counter");
+ Counter->setThreadLocalMode(llvm::GlobalValue::GeneralDynamicTLSModel);
+
+ // Immediate-call TLS slot: per-thread, no races, reused every call
+ ImmediateSlot = new llvm::GlobalVariable(
+ M, PtrTy, false, llvm::GlobalValue::InternalLinkage,
+ llvm::ConstantPointerNull::get(PtrTy), PoolName + "_immediate_slot");
+
ImmediateSlot->setThreadLocalMode(llvm::GlobalValue::GeneralDynamicTLSModel);
+
+ // Immediate wrapper: loads from TLS slot, calls with adapted signature
+ ImmediateWrapper = llvm::Function::Create(
+ DstFnType, llvm::GlobalValue::InternalLinkage,
+ WrapperName + "_immediate", M);
+ ImmediateWrapper->addFnAttr(llvm::Attribute::NoInline);
+ ImmediateWrapper->addFnAttr(llvm::Attribute::NoUnwind);
+ {
+ llvm::BasicBlock *BB = llvm::BasicBlock::Create(Context, "entry",
ImmediateWrapper);
+ llvm::IRBuilder<> B(BB);
+ llvm::Value *FP = B.CreateLoad(PtrTy, ImmediateSlot);
+ llvm::BasicBlock *CallBB = llvm::BasicBlock::Create(Context, "call",
ImmediateWrapper);
+ llvm::BasicBlock *NullBB = llvm::BasicBlock::Create(Context, "nullslot",
ImmediateWrapper);
+ B.CreateCondBr(B.CreateIsNotNull(FP), CallBB, NullBB);
+ B.SetInsertPoint(CallBB);
+ llvm::SmallVector<llvm::Value *, 8> ImmArgs;
+ auto AI = ImmediateWrapper->arg_begin();
+ for (unsigned J = 0; J < SrcParams && AI != ImmediateWrapper->arg_end();
++J, ++AI) {
+ llvm::Value *A = &*AI;
+ if (A->getType() != SrcFnType->getParamType(J))
+ A = B.CreateBitOrPointerCast(A, SrcFnType->getParamType(J));
+ ImmArgs.push_back(A);
+ }
+ llvm::CallInst *ImmCall = B.CreateCall(SrcFnType, FP, ImmArgs);
+ if (DstFnType->getReturnType()->isVoidTy()) {
+ B.CreateRetVoid(); B.SetInsertPoint(NullBB); B.CreateRetVoid();
+ } else {
+ llvm::Value *R = ImmCall;
+ if (R->getType() != DstFnType->getReturnType())
+ R = B.CreateBitOrPointerCast(R, DstFnType->getReturnType());
+ B.CreateRet(R);
+ B.SetInsertPoint(NullBB);
+ B.CreateRet(llvm::Constant::getNullValue(DstFnType->getReturnType()));
+ }
+ }
+
+ Slots = new llvm::GlobalVariable(
+ M, SlotArrayTy, false, llvm::GlobalValue::InternalLinkage,
+ llvm::ConstantAggregateZero::get(SlotArrayTy), PoolName + "_slots");
+
+ // 8-entry direct-mapped cache: avoids pool allocation for repeated fn_ptrs
+ static const unsigned CACHE_SIZE = 8;
+ llvm::ArrayType *CacheTy = llvm::ArrayType::get(PtrTy, CACHE_SIZE);
+ new llvm::GlobalVariable(
+ M, CacheTy, false, llvm::GlobalValue::InternalLinkage,
+ llvm::ConstantAggregateZero::get(CacheTy), PoolName + "_cache_keys");
+ new llvm::GlobalVariable(
+ M, CacheTy, false, llvm::GlobalValue::InternalLinkage,
+ llvm::ConstantAggregateZero::get(CacheTy), PoolName +
"_cache_wrappers");
+
+ // Pre-generate POOL_SIZE wrapper functions + build lookup table
+ llvm::SmallVector<llvm::Constant *, 64> WrappersConst;
+ for (unsigned I = 0; I < POOL_SIZE; ++I) {
+ std::string InstName = WrapperName + "_" + std::to_string(I);
+ llvm::Function *W = llvm::Function::Create(
+ DstFnType, llvm::GlobalValue::InternalLinkage, InstName, M);
+ W->addFnAttr(llvm::Attribute::NoInline);
+ W->addFnAttr(llvm::Attribute::NoUnwind);
+
+ llvm::BasicBlock *BB = llvm::BasicBlock::Create(Context, "entry", W);
+ llvm::IRBuilder<> B(BB);
+
+ llvm::Value *SlotPtr = B.CreateConstInBoundsGEP1_32(PtrTy, Slots, I);
+ llvm::Value *FP = B.CreateLoad(PtrTy, SlotPtr);
+
+ // Defensive null check: if slot was never written, skip call
+ llvm::BasicBlock *CallBB = llvm::BasicBlock::Create(Context, "call", W);
+ llvm::BasicBlock *NullBB = llvm::BasicBlock::Create(Context, "nullslot",
W);
+ llvm::Value *IsNotNull = B.CreateIsNotNull(FP);
+ B.CreateCondBr(IsNotNull, CallBB, NullBB);
+
+ B.SetInsertPoint(CallBB);
+ llvm::SmallVector<llvm::Value *, 8> CallArgs;
+ auto ArgIt = W->arg_begin();
+ for (unsigned J = 0; J < SrcParams && ArgIt != W->arg_end(); ++J,
++ArgIt) {
+ llvm::Value *A = &*ArgIt;
+ if (A->getType() != SrcFnType->getParamType(J))
+ A = B.CreateBitOrPointerCast(A, SrcFnType->getParamType(J));
+ CallArgs.push_back(A);
+ }
+ llvm::CallInst *Call = B.CreateCall(SrcFnType, FP, CallArgs);
+
+ if (DstFnType->getReturnType()->isVoidTy()) {
+ B.CreateRetVoid();
+ B.SetInsertPoint(NullBB);
+ B.CreateRetVoid();
+ } else {
+ llvm::Value *Ret = Call;
+ if (Ret->getType() != DstFnType->getReturnType())
+ Ret = B.CreateBitOrPointerCast(Ret, DstFnType->getReturnType());
+ B.CreateRet(Ret);
+ B.SetInsertPoint(NullBB);
+ B.CreateRet(llvm::Constant::getNullValue(DstFnType->getReturnType()));
+ }
+
+ WrappersConst.push_back(llvm::ConstantExpr::getBitCast(W, PtrTy));
+ }
+
+ // Create constant lookup table (not TLS — read-only function pointers)
+ llvm::ArrayType *WrapTblTy = llvm::ArrayType::get(PtrTy, POOL_SIZE);
+ WrapperTable = new llvm::GlobalVariable(
+ M, WrapTblTy, true, llvm::GlobalValue::InternalLinkage,
+ llvm::ConstantArray::get(WrapTblTy, WrappersConst),
+ PoolName + "_wrappers");
+ WrapperTable->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+ } else {
+ Slots = M.getNamedGlobal(PoolName + "_slots");
+ WrapperTable = M.getNamedGlobal(PoolName + "_wrappers");
+ SrcFnType = llvm::cast<llvm::FunctionType>(
+ CGF.CGM.getTypes().ConvertType(QualType(SrcProto, 0)));
+ }
+
+ // Runtime null check
+ llvm::Value *IsNull = CGF.Builder.CreateIsNull(FnPtr);
+ llvm::BasicBlock *NullContBB = llvm::BasicBlock::Create(Context, "nullcont",
CGF.CurFn);
+ llvm::BasicBlock *NotNullBB = llvm::BasicBlock::Create(Context, "notnull",
CGF.CurFn);
+ CharUnits PtrAlign = CGF.CGM.getPointerAlign();
+
+ if (IsImmediate) {
+ // === Immediate call: 1 TLS slot + 1 wrapper, no branches after null
check ===
+ CGF.Builder.CreateCondBr(IsNull, NullContBB, NotNullBB);
+
+ CGF.Builder.SetInsertPoint(NotNullBB);
+ CGF.Builder.CreateStore(FnPtr, Address(ImmediateSlot, PtrTy, PtrAlign));
+ CGF.Builder.CreateBr(NullContBB);
+
+ CGF.Builder.SetInsertPoint(NullContBB);
+ llvm::PHINode *PHI = CGF.Builder.CreatePHI(PtrTy, 2);
+ PHI->addIncoming(llvm::ConstantExpr::getBitCast(ImmediateWrapper, PtrTy),
NotNullBB);
+ PHI->addIncoming(llvm::ConstantPointerNull::get(PtrTy), NullContBB);
+ return PHI;
+ }
+
+ // === Store-for-later: pool with 64 slots + 8-entry cache + atomic counter
===
+ CGF.Builder.CreateCondBr(IsNull, NullContBB, NotNullBB);
+
+ CGF.Builder.SetInsertPoint(NotNullBB);
+ llvm::ArrayType *WrapTblTy = llvm::ArrayType::get(PtrTy, POOL_SIZE);
+ static const unsigned CACHE_SIZE = 8;
+ llvm::ArrayType *CacheTy = llvm::ArrayType::get(PtrTy, CACHE_SIZE);
+ llvm::GlobalVariable *CacheKeys = M.getNamedGlobal(PoolName + "_cache_keys");
+ llvm::GlobalVariable *CacheWrappers = M.getNamedGlobal(PoolName +
"_cache_wrappers");
+ llvm::BasicBlock *CacheHitBB = llvm::BasicBlock::Create(Context, "cachehit",
CGF.CurFn);
+ llvm::BasicBlock *CacheMissBB = llvm::BasicBlock::Create(Context,
"cachemiss", CGF.CurFn);
+ llvm::BasicBlock *ScanBB = llvm::BasicBlock::Create(Context, "scan",
CGF.CurFn);
+ llvm::BasicBlock *ScanFoundBB = llvm::BasicBlock::Create(Context, "scanfnd",
CGF.CurFn);
+ llvm::BasicBlock *ScanNextBB = llvm::BasicBlock::Create(Context, "scannxt",
CGF.CurFn);
+ llvm::BasicBlock *AllocCheckBB = llvm::BasicBlock::Create(Context,
"allocchk", CGF.CurFn);
+ llvm::BasicBlock *AllocStoreBB = llvm::BasicBlock::Create(Context,
"allocstr", CGF.CurFn);
+ llvm::BasicBlock *OverflowBB = llvm::BasicBlock::Create(Context, "overflow",
CGF.CurFn);
+ llvm::BasicBlock *ContBB = llvm::BasicBlock::Create(Context, "cont",
CGF.CurFn);
+
+ // Cache lookup: idx = (fn_ptr >> 2) & 7
+ llvm::Value *CacheIdx = CGF.Builder.CreateAnd(
+ CGF.Builder.CreateLShr(
+ CGF.Builder.CreatePtrToInt(FnPtr, I32Ty),
+ llvm::ConstantInt::get(I32Ty, 2)),
+ llvm::ConstantInt::get(I32Ty, CACHE_SIZE - 1));
+ llvm::Value *CacheKeyGEP = CGF.Builder.CreateInBoundsGEP(
+ CacheTy, CacheKeys, {llvm::ConstantInt::get(I32Ty, 0), CacheIdx});
+ Address CacheKeyAddr(CacheKeyGEP, PtrTy, PtrAlign);
+ llvm::Value *CachedFn = CGF.Builder.CreateLoad(CacheKeyAddr);
+ llvm::Value *CacheHit = CGF.Builder.CreateICmpEQ(CachedFn, FnPtr);
+ llvm::Value *CacheWrapGEP = CGF.Builder.CreateInBoundsGEP(
+ CacheTy, CacheWrappers, {llvm::ConstantInt::get(I32Ty, 0), CacheIdx});
+ Address CacheWrapAddr(CacheWrapGEP, PtrTy, PtrAlign);
+
+ CGF.Builder.CreateCondBr(CacheHit, CacheHitBB, CacheMissBB);
+
+ // Cache hit: load cached wrapper, branch to cont
+ CGF.Builder.SetInsertPoint(CacheHitBB);
+ llvm::Value *CacheHitW = CGF.Builder.CreateLoad(CacheWrapAddr);
+ CGF.Builder.CreateBr(ContBB);
+
+ // Cache miss: scan pool for existing mapping
+ CGF.Builder.SetInsertPoint(CacheMissBB);
+ CGF.Builder.CreateBr(ScanBB);
+
+ // Scan loop: find fn_ptr in slots[0..POOL_SIZE-1]
+ CGF.Builder.SetInsertPoint(ScanBB);
+ llvm::PHINode *ScanIdx = CGF.Builder.CreatePHI(I32Ty, 2);
+ ScanIdx->addIncoming(llvm::ConstantInt::get(I32Ty, 0), CacheMissBB);
+ llvm::Value *ScanSlotGEP = CGF.Builder.CreateInBoundsGEP(
+ SlotArrayTy, Slots, {llvm::ConstantInt::get(I32Ty, 0), ScanIdx});
+ llvm::Value *ScanFn = CGF.Builder.CreateLoad(Address(ScanSlotGEP, PtrTy,
PtrAlign));
+ llvm::Value *ScanMatch = CGF.Builder.CreateICmpEQ(ScanFn, FnPtr);
+ CGF.Builder.CreateCondBr(ScanMatch, ScanFoundBB, ScanNextBB);
+
+ // Found existing slot: update cache, return existing wrapper
+ CGF.Builder.SetInsertPoint(ScanFoundBB);
+ CGF.Builder.CreateStore(FnPtr, CacheKeyAddr);
+ llvm::Value *FoundWrapGEP = CGF.Builder.CreateInBoundsGEP(
+ WrapTblTy, WrapperTable, {llvm::ConstantInt::get(I32Ty, 0), ScanIdx});
+ llvm::Value *FoundW = CGF.Builder.CreateLoad(Address(FoundWrapGEP, PtrTy,
PtrAlign));
+ CGF.Builder.CreateStore(FoundW, CacheWrapAddr);
+ CGF.Builder.CreateBr(ContBB);
+
+ // Advance scan
+ CGF.Builder.SetInsertPoint(ScanNextBB);
+ llvm::Value *NextIdx = CGF.Builder.CreateAdd(
+ ScanIdx, llvm::ConstantInt::get(I32Ty, 1));
+ llvm::Value *ScanEnd = CGF.Builder.CreateICmpUGE(
+ NextIdx, llvm::ConstantInt::get(I32Ty, POOL_SIZE));
+ ScanIdx->addIncoming(NextIdx, ScanNextBB);
+ CGF.Builder.CreateCondBr(ScanEnd, AllocCheckBB, ScanBB);
+
+ // Allocate new slot: atomic counter increment
+ CGF.Builder.SetInsertPoint(AllocCheckBB);
+ Address CounterAddr(Counter, I32Ty, PtrAlign);
+ llvm::Value *Slot = CGF.Builder.CreateAtomicRMW(
+ llvm::AtomicRMWInst::Add, CounterAddr,
+ llvm::ConstantInt::get(I32Ty, 1), llvm::AtomicOrdering::Monotonic);
+ llvm::Value *InBounds = CGF.Builder.CreateICmpULT(
+ Slot, llvm::ConstantInt::get(I32Ty, POOL_SIZE));
+ CGF.Builder.CreateCondBr(InBounds, AllocStoreBB, OverflowBB);
+
+ // Overflow
+ CGF.Builder.SetInsertPoint(OverflowBB);
+ CGF.Builder.CreateCall(llvm::Intrinsic::getOrInsertDeclaration(
+ &M, llvm::Intrinsic::trap));
+ CGF.Builder.CreateUnreachable();
+
+ // Store in pool + update cache
+ CGF.Builder.SetInsertPoint(AllocStoreBB);
+ llvm::Value *SlotIdx[] = {llvm::ConstantInt::get(I32Ty, 0), Slot};
+ llvm::Value *SlotGEP = CGF.Builder.CreateInBoundsGEP(
+ SlotArrayTy, Slots, SlotIdx);
+ CGF.Builder.CreateStore(FnPtr, Address(SlotGEP, PtrTy, PtrAlign));
+ llvm::Value *WrapGEP = CGF.Builder.CreateInBoundsGEP(
+ WrapTblTy, WrapperTable, SlotIdx);
+ llvm::Value *W = CGF.Builder.CreateLoad(Address(WrapGEP, PtrTy, PtrAlign));
+ CGF.Builder.CreateStore(FnPtr, CacheKeyAddr);
+ CGF.Builder.CreateStore(W, CacheWrapAddr);
+ CGF.Builder.CreateBr(ContBB);
+
+ // Null path
+ CGF.Builder.SetInsertPoint(NullContBB);
+ CGF.Builder.CreateBr(ContBB);
+
+ // ContBB: PHI for result
+ CGF.Builder.SetInsertPoint(ContBB);
+ llvm::PHINode *PHI = CGF.Builder.CreatePHI(PtrTy, 4);
+ PHI->addIncoming(CacheHitW, CacheHitBB);
+ PHI->addIncoming(FoundW, ScanFoundBB);
+ PHI->addIncoming(W, AllocStoreBB);
+ PHI->addIncoming(llvm::ConstantPointerNull::get(PtrTy), NullContBB);
+ return PHI;
+}
+
std::unique_ptr<TargetCodeGenInfo>
CodeGen::createWebAssemblyTargetCodeGenInfo(CodeGenModule &CGM,
WebAssemblyABIKind K) {
return std::make_unique<WebAssemblyTargetCodeGenInfo>(CGM.getTypes(), K);
}
+
+// 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()) {
----------------
turran wrote:
Great news!
(Indeed, the gslist (and glist) needed my small patch)
https://github.com/llvm/llvm-project/pull/153168
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits