[memcpyopt] Conservatively handle non-integral pointers

If we allow the non-integral pointers to become memset and memcpy, we loose the ability to reason about pointer propagation.  This patch is modeled on changes we've carried downstream for a long time, figured it was worth being equally conservative for other users.  There is room to refine the semantics and handling here if anyone is motivated.
This commit is contained in:
Philip Reames 2020-10-01 16:44:12 -07:00
parent aab6f7db47
commit bb0344644a
2 changed files with 52 additions and 2 deletions

View File

@ -352,8 +352,15 @@ Instruction *MemCpyOptPass::tryMergingIntoMemset(Instruction *StartInst,
// If this is a store, see if we can merge it in.
if (!NextStore->isSimple()) break;
Value *StoredVal = NextStore->getValueOperand();
// Don't convert stores of non-integral pointer types to memsets (which
// stores integers).
if (DL.isNonIntegralPointerType(StoredVal->getType()->getScalarType()))
break;
// Check to see if this stored value is of the same byte-splattable value.
Value *StoredByte = isBytewiseValue(NextStore->getOperand(0), DL);
Value *StoredByte = isBytewiseValue(StoredVal, DL);
if (isa<UndefValue>(ByteVal) && StoredByte)
ByteVal = StoredByte;
if (ByteVal != StoredByte)
@ -556,8 +563,15 @@ bool MemCpyOptPass::processStore(StoreInst *SI, BasicBlock::iterator &BBI) {
const DataLayout &DL = SI->getModule()->getDataLayout();
Value *StoredVal = SI->getValueOperand();
// Not all the transforms below are correct for non-integral pointers, bail
// until we've audited the individual pieces.
if (DL.isNonIntegralPointerType(StoredVal->getType()->getScalarType()))
return false;
// Load to store forwarding can be interpreted as memcpy.
if (LoadInst *LI = dyn_cast<LoadInst>(SI->getOperand(0))) {
if (LoadInst *LI = dyn_cast<LoadInst>(StoredVal)) {
if (LI->isSimple() && LI->hasOneUse() &&
LI->getParent() == SI->getParent()) {

View File

@ -0,0 +1,36 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -memcpyopt -S < %s | FileCheck %s
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128-ni:1"
define void @illegal_memset(i64 addrspace(1)** %p) {
; CHECK-LABEL: @illegal_memset(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[P1:%.*]] = bitcast i64 addrspace(1)** [[P:%.*]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[P1]], i8 0, i64 8, i1 false)
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64 addrspace(1)*, i64 addrspace(1)** [[P]], i64 1
; CHECK-NEXT: store i64 addrspace(1)* null, i64 addrspace(1)** [[GEP]], align 8
; CHECK-NEXT: ret void
;
entry:
%p1 = bitcast i64 addrspace(1)** %p to i8*
call void @llvm.memset.p0i8.i64(i8* %p1, i8 0, i64 8, i32 0, i1 false)
%gep = getelementptr i64 addrspace(1)*, i64 addrspace(1)** %p, i64 1
store i64 addrspace(1)* null, i64 addrspace(1)** %gep
ret void
}
define void @illegal_memcpy(<2 x i8 addrspace(1)*>* noalias align 16 %a,
; CHECK-LABEL: @illegal_memcpy(
; CHECK-NEXT: [[VAL:%.*]] = load <2 x i8 addrspace(1)*>, <2 x i8 addrspace(1)*>* [[A:%.*]], align 16
; CHECK-NEXT: store <2 x i8 addrspace(1)*> [[VAL]], <2 x i8 addrspace(1)*>* [[B:%.*]], align 16
; CHECK-NEXT: ret void
;
<2 x i8 addrspace(1)*>* noalias align 16 %b) {
%val = load <2 x i8 addrspace(1)*>, <2 x i8 addrspace(1)*>* %a, align 16
store <2 x i8 addrspace(1)*> %val, <2 x i8 addrspace(1)*>* %b, align 16
ret void
}
declare void @llvm.memset.p1i8.i64(i8 addrspace(1)*, i8, i64, i32, i1)
declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i32, i1)