/// Generate a thunk that puts the LSDA of ParentFunc in EAX and then calls /// PersonalityFn, forwarding the parameters passed to PEXCEPTION_ROUTINE: /// typedef _EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE)( /// _EXCEPTION_RECORD *, void *, _CONTEXT *, void *); /// We essentially want this code: /// movl $lsda, %eax /// jmpl ___CxxFrameHandler3 Function *WinEHStatePass::generateLSDAInEAXThunk(Function *ParentFunc) { LLVMContext &Context = ParentFunc->getContext(); Type *Int32Ty = Type::getInt32Ty(Context); Type *Int8PtrType = Type::getInt8PtrTy(Context); Type *ArgTys[5] = {Int8PtrType, Int8PtrType, Int8PtrType, Int8PtrType, Int8PtrType}; FunctionType *TrampolineTy = FunctionType::get(Int32Ty, makeArrayRef(&ArgTys[0], 4), /*isVarArg=*/false); FunctionType *TargetFuncTy = FunctionType::get(Int32Ty, makeArrayRef(&ArgTys[0], 5), /*isVarArg=*/false); Function *Trampoline = Function::Create( TrampolineTy, GlobalValue::InternalLinkage, Twine("__ehhandler$") + ParentFunc->getName(), TheModule); BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", Trampoline); IRBuilder<> Builder(EntryBB); Value *LSDA = emitEHLSDA(Builder, ParentFunc); Value *CastPersonality = Builder.CreateBitCast(PersonalityFn, TargetFuncTy->getPointerTo()); auto AI = Trampoline->arg_begin(); Value *Args[5] = {LSDA, AI++, AI++, AI++, AI++}; CallInst *Call = Builder.CreateCall(CastPersonality, Args); // Can't use musttail due to prototype mismatch, but we can use tail. Call->setTailCall(true); // Set inreg so we pass it in EAX. Call->addAttribute(1, Attribute::InReg); Builder.CreateRet(Call); return Trampoline; }
static bool ExpandVarArgCall(Module *M, InstType *Call, DataLayout *DL) { FunctionType *FuncType = cast<FunctionType>( Call->getCalledValue()->getType()->getPointerElementType()); if (!FuncType->isFunctionVarArg()) return false; if (auto *F = dyn_cast<Function>(Call->getCalledValue())) if (isEmscriptenJSArgsFunc(M, F->getName())) return false; Function *F = Call->getParent()->getParent(); LLVMContext &Ctx = M->getContext(); SmallVector<AttributeSet, 8> Attrs; Attrs.push_back(Call->getAttributes().getFnAttributes()); Attrs.push_back(Call->getAttributes().getRetAttributes()); // Split argument list into fixed and variable arguments. SmallVector<Value *, 8> FixedArgs; SmallVector<Value *, 8> VarArgs; SmallVector<Type *, 8> VarArgsTypes; for (unsigned I = 0, E = FuncType->getNumParams(); I < E; ++I) { FixedArgs.push_back(Call->getArgOperand(I)); // AttributeSets use 1-based indexing. Attrs.push_back(Call->getAttributes().getParamAttributes(I + 1)); } for (unsigned I = FuncType->getNumParams(), E = Call->getNumArgOperands(); I < E; ++I) { Value *ArgVal = Call->getArgOperand(I); VarArgs.push_back(ArgVal); bool isByVal = Call->getAttributes().hasAttribute(I + 1, Attribute::ByVal); // For "byval" arguments we must dereference the pointer. VarArgsTypes.push_back(isByVal ? ArgVal->getType()->getPointerElementType() : ArgVal->getType()); } if (VarArgsTypes.size() == 0) { // Some buggy code (e.g. 176.gcc in Spec2k) uses va_arg on an // empty argument list, which gives undefined behaviour in C. To // work around such programs, we create a dummy varargs buffer on // the stack even though there are no arguments to put in it. // This allows va_arg to read an undefined value from the stack // rather than crashing by reading from an uninitialized pointer. // An alternative would be to pass a null pointer to catch the // invalid use of va_arg. VarArgsTypes.push_back(Type::getInt32Ty(Ctx)); } // Create struct type for packing variable arguments into. StructType *VarArgsTy = StructType::get(Ctx, VarArgsTypes); // Allocate space for the variable argument buffer. Do this at the // start of the function so that we don't leak space if the function // is called in a loop. IRBuilder<> IRB(F->getEntryBlock().getFirstInsertionPt()); auto *Buf = IRB.CreateAlloca(VarArgsTy, nullptr, "vararg_buffer"); // Call llvm.lifetime.start/end intrinsics to indicate that Buf is // only used for the duration of the function call, so that the // stack space can be reused elsewhere. auto LifetimeStart = Intrinsic::getDeclaration(M, Intrinsic::lifetime_start); auto LifetimeEnd = Intrinsic::getDeclaration(M, Intrinsic::lifetime_end); auto *I8Ptr = Type::getInt8Ty(Ctx)->getPointerTo(); auto *BufPtr = IRB.CreateBitCast(Buf, I8Ptr, "vararg_lifetime_bitcast"); auto *BufSize = ConstantInt::get(Ctx, APInt(64, DL->getTypeAllocSize(VarArgsTy))); IRB.CreateCall2(LifetimeStart, BufSize, BufPtr); // Copy variable arguments into buffer. int Index = 0; IRB.SetInsertPoint(Call); for (Value *Arg : VarArgs) { Value *Indexes[] = {ConstantInt::get(Ctx, APInt(32, 0)), ConstantInt::get(Ctx, APInt(32, Index))}; Value *Ptr = IRB.CreateInBoundsGEP(Buf, Indexes, "vararg_ptr"); bool isByVal = Call->getAttributes().hasAttribute( FuncType->getNumParams() + Index + 1, Attribute::ByVal); if (isByVal) IRB.CreateMemCpy(Ptr, Arg, DL->getTypeAllocSize( Arg->getType()->getPointerElementType()), /*Align=*/1); else IRB.CreateStore(Arg, Ptr); ++Index; } // Cast function to new type to add our extra pointer argument. SmallVector<Type *, 8> ArgTypes(FuncType->param_begin(), FuncType->param_end()); ArgTypes.push_back(VarArgsTy->getPointerTo()); FunctionType *NFTy = FunctionType::get(FuncType->getReturnType(), ArgTypes, /*isVarArg=*/false); Value *CastFunc = IRB.CreateBitCast(Call->getCalledValue(), NFTy->getPointerTo(), "vararg_func"); // Create the converted function call. FixedArgs.push_back(Buf); Instruction *NewCall; if (auto *C = dyn_cast<CallInst>(Call)) { auto *N = IRB.CreateCall(CastFunc, FixedArgs); N->setAttributes(AttributeSet::get(Ctx, Attrs)); NewCall = N; IRB.CreateCall2(LifetimeEnd, BufSize, BufPtr); } else if (auto *C = dyn_cast<InvokeInst>(Call)) { auto *N = IRB.CreateInvoke(CastFunc, C->getNormalDest(), C->getUnwindDest(), FixedArgs, C->getName()); N->setAttributes(AttributeSet::get(Ctx, Attrs)); (IRBuilder<>(C->getNormalDest()->getFirstInsertionPt())) .CreateCall2(LifetimeEnd, BufSize, BufPtr); (IRBuilder<>(C->getUnwindDest()->getFirstInsertionPt())) .CreateCall2(LifetimeEnd, BufSize, BufPtr); NewCall = N; } else { llvm_unreachable("not a call/invoke"); } NewCall->takeName(Call); Call->replaceAllUsesWith(NewCall); Call->eraseFromParent(); return true; }
// Convert the given call to use normalized argument/return types. template <class T> static bool ConvertCall(T *Call, Pass *P) { // Don't try to change calls to intrinsics. if (isa<IntrinsicInst>(Call)) return false; FunctionType *FTy = cast<FunctionType>( Call->getCalledValue()->getType()->getPointerElementType()); FunctionType *NFTy = NormalizeFunctionType(FTy); if (NFTy == FTy) return false; // No change needed. // Convert arguments. SmallVector<Value *, 8> Args; for (unsigned I = 0; I < Call->getNumArgOperands(); ++I) { Value *Arg = Call->getArgOperand(I); if (NFTy->getParamType(I) != FTy->getParamType(I)) { Instruction::CastOps CastType = Call->getAttributes().hasAttribute(I + 1, Attribute::SExt) ? Instruction::SExt : Instruction::ZExt; Arg = CopyDebug(CastInst::Create(CastType, Arg, NFTy->getParamType(I), "arg_ext", Call), Call); } Args.push_back(Arg); } Value *CastFunc = CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), Call->getName() + ".arg_cast", Call), Call); Value *Result = NULL; if (CallInst *OldCall = dyn_cast<CallInst>(Call)) { CallInst *NewCall = CopyDebug(CallInst::Create(CastFunc, Args, "", OldCall), OldCall); NewCall->takeName(OldCall); NewCall->setAttributes(OldCall->getAttributes()); NewCall->setCallingConv(OldCall->getCallingConv()); NewCall->setTailCall(OldCall->isTailCall()); Result = NewCall; if (FTy->getReturnType() != NFTy->getReturnType()) { Result = CopyDebug(new TruncInst(NewCall, FTy->getReturnType(), NewCall->getName() + ".ret_trunc", Call), Call); } } else if (InvokeInst *OldInvoke = dyn_cast<InvokeInst>(Call)) { BasicBlock *Parent = OldInvoke->getParent(); BasicBlock *NormalDest = OldInvoke->getNormalDest(); BasicBlock *UnwindDest = OldInvoke->getUnwindDest(); if (FTy->getReturnType() != NFTy->getReturnType()) { if (BasicBlock *SplitDest = SplitCriticalEdge(Parent, NormalDest)) { NormalDest = SplitDest; } } InvokeInst *New = CopyDebug(InvokeInst::Create(CastFunc, NormalDest, UnwindDest, Args, "", OldInvoke), OldInvoke); New->takeName(OldInvoke); if (FTy->getReturnType() != NFTy->getReturnType()) { Result = CopyDebug(new TruncInst(New, FTy->getReturnType(), New->getName() + ".ret_trunc", NormalDest->getTerminator()), OldInvoke); } else { Result = New; } New->setAttributes(OldInvoke->getAttributes()); New->setCallingConv(OldInvoke->getCallingConv()); } Call->replaceAllUsesWith(Result); Call->eraseFromParent(); return true; }