示例#1
0
/// 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;
}