diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index 1cea53f695292..ea57321b995c1 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -581,7 +581,8 @@ class MemorySanitizer { friend struct VarArgHelperBase; friend struct VarArgAMD64Helper; friend struct VarArgAArch64Helper; - friend struct VarArgPowerPCHelper; + friend struct VarArgPowerPC64Helper; + friend struct VarArgPowerPC32Helper; friend struct VarArgSystemZHelper; friend struct VarArgI386Helper; friend struct VarArgGenericHelper; @@ -6392,13 +6393,165 @@ struct VarArgAArch64Helper : public VarArgHelperBase { } }; -/// PowerPC-specific implementation of VarArgHelper. -struct VarArgPowerPCHelper : public VarArgHelperBase { +/// PowerPC64-specific implementation of VarArgHelper. +struct VarArgPowerPC64Helper : public VarArgHelperBase { AllocaInst *VAArgTLSCopy = nullptr; Value *VAArgSize = nullptr; - VarArgPowerPCHelper(Function &F, MemorySanitizer &MS, - MemorySanitizerVisitor &MSV, unsigned VAListTagSize) + VarArgPowerPC64Helper(Function &F, MemorySanitizer &MS, + MemorySanitizerVisitor &MSV, unsigned VAListTagSize) + : VarArgHelperBase(F, MS, MSV, VAListTagSize) {} + + void visitCallBase(CallBase &CB, IRBuilder<> &IRB) override { + // For PowerPC, we need to deal with alignment of stack arguments - + // they are mostly aligned to 8 bytes, but vectors and i128 arrays + // are aligned to 16 bytes, byvals can be aligned to 8 or 16 bytes, + // For that reason, we compute current offset from stack pointer (which is + // always properly aligned), and offset for the first vararg, then subtract + // them. + unsigned VAArgBase; + Triple TargetTriple(F.getParent()->getTargetTriple()); + // Parameter save area starts at 48 bytes from frame pointer for ABIv1, + // and 32 bytes for ABIv2. This is usually determined by target + // endianness, but in theory could be overridden by function attribute. + if (TargetTriple.isPPC64()) { + if (TargetTriple.isPPC64ELFv2ABI()) + VAArgBase = 32; + else + VAArgBase = 48; + } else { + // Parameter save area is 8 bytes from frame pointer in PPC32 + VAArgBase = 8; + } + unsigned VAArgOffset = VAArgBase; + const DataLayout &DL = F.getDataLayout(); + for (const auto &[ArgNo, A] : llvm::enumerate(CB.args())) { + bool IsFixed = ArgNo < CB.getFunctionType()->getNumParams(); + bool IsByVal = CB.paramHasAttr(ArgNo, Attribute::ByVal); + if (IsByVal) { + assert(A->getType()->isPointerTy()); + Type *RealTy = CB.getParamByValType(ArgNo); + uint64_t ArgSize = DL.getTypeAllocSize(RealTy); + Align ArgAlign = CB.getParamAlign(ArgNo).value_or(Align(8)); + if (ArgAlign < 8) + ArgAlign = Align(8); + VAArgOffset = alignTo(VAArgOffset, ArgAlign); + if (!IsFixed) { + Value *Base = + getShadowPtrForVAArgument(IRB, VAArgOffset - VAArgBase, ArgSize); + if (Base) { + Value *AShadowPtr, *AOriginPtr; + std::tie(AShadowPtr, AOriginPtr) = + MSV.getShadowOriginPtr(A, IRB, IRB.getInt8Ty(), + kShadowTLSAlignment, /*isStore*/ false); + + IRB.CreateMemCpy(Base, kShadowTLSAlignment, AShadowPtr, + kShadowTLSAlignment, ArgSize); + } + } + VAArgOffset += alignTo(ArgSize, Align(8)); + } else { + Value *Base; + uint64_t ArgSize = DL.getTypeAllocSize(A->getType()); + Align ArgAlign = Align(8); + if (A->getType()->isArrayTy()) { + // Arrays are aligned to element size, except for long double + // arrays, which are aligned to 8 bytes. + Type *ElementTy = A->getType()->getArrayElementType(); + if (!ElementTy->isPPC_FP128Ty()) + ArgAlign = Align(DL.getTypeAllocSize(ElementTy)); + } else if (A->getType()->isVectorTy()) { + // Vectors are naturally aligned. + ArgAlign = Align(ArgSize); + } + if (ArgAlign < 8) + ArgAlign = Align(8); + VAArgOffset = alignTo(VAArgOffset, ArgAlign); + if (DL.isBigEndian()) { + // Adjusting the shadow for argument with size < 8 to match the + // placement of bits in big endian system + if (ArgSize < 8) + VAArgOffset += (8 - ArgSize); + } + if (!IsFixed) { + Base = + getShadowPtrForVAArgument(IRB, VAArgOffset - VAArgBase, ArgSize); + if (Base) + IRB.CreateAlignedStore(MSV.getShadow(A), Base, kShadowTLSAlignment); + } + VAArgOffset += ArgSize; + VAArgOffset = alignTo(VAArgOffset, Align(8)); + } + if (IsFixed) + VAArgBase = VAArgOffset; + } + + Constant *TotalVAArgSize = + ConstantInt::get(MS.IntptrTy, VAArgOffset - VAArgBase); + // Here using VAArgOverflowSizeTLS as VAArgSizeTLS to avoid creation of + // a new class member i.e. it is the total size of all VarArgs. + IRB.CreateStore(TotalVAArgSize, MS.VAArgOverflowSizeTLS); + } + + void finalizeInstrumentation() override { + assert(!VAArgSize && !VAArgTLSCopy && + "finalizeInstrumentation called twice"); + IRBuilder<> IRB(MSV.FnPrologueEnd); + VAArgSize = IRB.CreateLoad(IRB.getInt64Ty(), MS.VAArgOverflowSizeTLS); + Value *CopySize = VAArgSize; + + if (!VAStartInstrumentationList.empty()) { + // If there is a va_start in this function, make a backup copy of + // va_arg_tls somewhere in the function entry block. + + VAArgTLSCopy = IRB.CreateAlloca(Type::getInt8Ty(*MS.C), CopySize); + VAArgTLSCopy->setAlignment(kShadowTLSAlignment); + IRB.CreateMemSet(VAArgTLSCopy, Constant::getNullValue(IRB.getInt8Ty()), + CopySize, kShadowTLSAlignment, false); + + Value *SrcSize = IRB.CreateBinaryIntrinsic( + Intrinsic::umin, CopySize, + ConstantInt::get(IRB.getInt64Ty(), kParamTLSSize)); + IRB.CreateMemCpy(VAArgTLSCopy, kShadowTLSAlignment, MS.VAArgTLS, + kShadowTLSAlignment, SrcSize); + } + + // Instrument va_start. + // Copy va_list shadow from the backup copy of the TLS contents. + Triple TargetTriple(F.getParent()->getTargetTriple()); + for (CallInst *OrigInst : VAStartInstrumentationList) { + NextNodeIRBuilder IRB(OrigInst); + Value *VAListTag = OrigInst->getArgOperand(0); + Value *RegSaveAreaPtrPtr = IRB.CreatePtrToInt(VAListTag, MS.IntptrTy); + + // In PPC32 va_list_tag is a struct, whereas in PPC64 it's a pointer + if (!TargetTriple.isPPC64()) { + RegSaveAreaPtrPtr = + IRB.CreateAdd(RegSaveAreaPtrPtr, ConstantInt::get(MS.IntptrTy, 8)); + } + RegSaveAreaPtrPtr = IRB.CreateIntToPtr(RegSaveAreaPtrPtr, MS.PtrTy); + + Value *RegSaveAreaPtr = IRB.CreateLoad(MS.PtrTy, RegSaveAreaPtrPtr); + Value *RegSaveAreaShadowPtr, *RegSaveAreaOriginPtr; + const DataLayout &DL = F.getDataLayout(); + unsigned IntptrSize = DL.getTypeStoreSize(MS.IntptrTy); + const Align Alignment = Align(IntptrSize); + std::tie(RegSaveAreaShadowPtr, RegSaveAreaOriginPtr) = + MSV.getShadowOriginPtr(RegSaveAreaPtr, IRB, IRB.getInt8Ty(), + Alignment, /*isStore*/ true); + IRB.CreateMemCpy(RegSaveAreaShadowPtr, Alignment, VAArgTLSCopy, Alignment, + CopySize); + } + } +}; + +/// PowerPC32-specific implementation of VarArgHelper. +struct VarArgPowerPC32Helper : public VarArgHelperBase { + AllocaInst *VAArgTLSCopy = nullptr; + Value *VAArgSize = nullptr; + + VarArgPowerPC32Helper(Function &F, MemorySanitizer &MS, + MemorySanitizerVisitor &MSV, unsigned VAListTagSize) : VarArgHelperBase(F, MS, MSV, VAListTagSize) {} void visitCallBase(CallBase &CB, IRBuilder<> &IRB) override { @@ -7067,10 +7220,10 @@ static VarArgHelper *CreateVarArgHelper(Function &Func, MemorySanitizer &Msan, // On PowerPC32 VAListTag is a struct // {char, char, i16 padding, char *, char *} if (TargetTriple.isPPC32()) - return new VarArgPowerPCHelper(Func, Msan, Visitor, /*VAListTagSize=*/12); + return new VarArgPowerPC32Helper(Func, Msan, Visitor, /*VAListTagSize=*/12); if (TargetTriple.isPPC64()) - return new VarArgPowerPCHelper(Func, Msan, Visitor, /*VAListTagSize=*/8); + return new VarArgPowerPC64Helper(Func, Msan, Visitor, /*VAListTagSize=*/8); if (TargetTriple.isRISCV32()) return new VarArgRISCVHelper(Func, Msan, Visitor, /*VAListTagSize=*/4);