Skip to content

Commit ac12e6a

Browse files
committed
[DSE] Use precise loc for memset_chk during overwrite checks
memset_chk may not write the number of bytes specified by the third argument, if it is larger than the destination size (specified as 4th argument). Reviewed By: asbirlea Differential Revision: https://reviews.llvm.org/D115167
1 parent 7a3f916 commit ac12e6a

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,26 @@ struct DSEState {
834834
CodeMetrics::collectEphemeralValues(&F, &AC, EphValues);
835835
}
836836

837+
LocationSize strengthenLocationSize(const Instruction *I,
838+
LocationSize Size) const {
839+
if (auto *CB = dyn_cast<CallBase>(I)) {
840+
LibFunc F;
841+
if (TLI.getLibFunc(*CB, F) && TLI.has(F) && F == LibFunc_memset_chk) {
842+
// Use the precise location size specified by the 3rd argument
843+
// for determining KillingI overwrites DeadLoc if it is a memset_chk
844+
// instruction. memset_chk will write either the amount specified as 3rd
845+
// argument or the function will immediately abort and exit the program.
846+
// NOTE: AA may determine NoAlias if it can prove that the access size
847+
// is larger than the allocation size due to that being UB. To avoid
848+
// returning potentially invalid NoAlias results by AA, limit the use of
849+
// the precise location size to isOverwrite.
850+
if (const auto *Len = dyn_cast<ConstantInt>(CB->getArgOperand(2)))
851+
return LocationSize::precise(Len->getZExtValue());
852+
}
853+
}
854+
return Size;
855+
}
856+
837857
/// Return 'OW_Complete' if a store to the 'KillingLoc' location (by \p
838858
/// KillingI instruction) completely overwrites a store to the 'DeadLoc'
839859
/// location (by \p DeadI instruction).
@@ -853,23 +873,25 @@ struct DSEState {
853873
if (!isGuaranteedLoopIndependent(DeadI, KillingI, DeadLoc))
854874
return OW_Unknown;
855875

876+
LocationSize KillingLocSize =
877+
strengthenLocationSize(KillingI, KillingLoc.Size);
856878
const Value *DeadPtr = DeadLoc.Ptr->stripPointerCasts();
857879
const Value *KillingPtr = KillingLoc.Ptr->stripPointerCasts();
858880
const Value *DeadUndObj = getUnderlyingObject(DeadPtr);
859881
const Value *KillingUndObj = getUnderlyingObject(KillingPtr);
860882

861883
// Check whether the killing store overwrites the whole object, in which
862884
// case the size/offset of the dead store does not matter.
863-
if (DeadUndObj == KillingUndObj && KillingLoc.Size.isPrecise()) {
885+
if (DeadUndObj == KillingUndObj && KillingLocSize.isPrecise()) {
864886
uint64_t KillingUndObjSize = getPointerSize(KillingUndObj, DL, TLI, &F);
865887
if (KillingUndObjSize != MemoryLocation::UnknownSize &&
866-
KillingUndObjSize == KillingLoc.Size.getValue())
888+
KillingUndObjSize == KillingLocSize.getValue())
867889
return OW_Complete;
868890
}
869891

870892
// FIXME: Vet that this works for size upper-bounds. Seems unlikely that we'll
871893
// get imprecise values here, though (except for unknown sizes).
872-
if (!KillingLoc.Size.isPrecise() || !DeadLoc.Size.isPrecise()) {
894+
if (!KillingLocSize.isPrecise() || !DeadLoc.Size.isPrecise()) {
873895
// In case no constant size is known, try to an IR values for the number
874896
// of bytes written and check if they match.
875897
const auto *KillingMemI = dyn_cast<MemIntrinsic>(KillingI);
@@ -886,7 +908,7 @@ struct DSEState {
886908
return isMaskedStoreOverwrite(KillingI, DeadI, BatchAA);
887909
}
888910

889-
const uint64_t KillingSize = KillingLoc.Size.getValue();
911+
const uint64_t KillingSize = KillingLocSize.getValue();
890912
const uint64_t DeadSize = DeadLoc.Size.getValue();
891913

892914
// Query the alias information

llvm/test/Transforms/DeadStoreElimination/libcalls-chk.ll

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ declare void @use(ptr)
1212
; strncpy -> __memset_chk, full overwrite
1313
define void @dse_strncpy_memset_chk_test1(ptr noalias %out, ptr noalias %in, i64 %n) {
1414
; CHECK-LABEL: @dse_strncpy_memset_chk_test1(
15-
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @strncpy(ptr [[OUT:%.*]], ptr [[IN:%.*]], i64 100)
16-
; CHECK-NEXT: [[CALL_2:%.*]] = tail call ptr @__memset_chk(ptr [[OUT]], i32 42, i64 100, i64 [[N:%.*]])
15+
; CHECK-NEXT: [[CALL_2:%.*]] = tail call ptr @__memset_chk(ptr [[OUT:%.*]], i32 42, i64 100, i64 [[N:%.*]])
1716
; CHECK-NEXT: ret void
1817
;
1918
%call = tail call ptr @strncpy(ptr %out, ptr %in, i64 100)
@@ -23,8 +22,7 @@ define void @dse_strncpy_memset_chk_test1(ptr noalias %out, ptr noalias %in, i64
2322

2423
define void @dse_memset_chk_eliminate_store1(ptr %out, i64 %n) {
2524
; CHECK-LABEL: @dse_memset_chk_eliminate_store1(
26-
; CHECK-NEXT: store i8 10, ptr [[OUT:%.*]], align 1
27-
; CHECK-NEXT: [[CALL_2:%.*]] = tail call ptr @__memset_chk(ptr [[OUT]], i32 42, i64 100, i64 [[N:%.*]])
25+
; CHECK-NEXT: [[CALL_2:%.*]] = tail call ptr @__memset_chk(ptr [[OUT:%.*]], i32 42, i64 100, i64 [[N:%.*]])
2826
; CHECK-NEXT: ret void
2927
;
3028
store i8 10, ptr %out
@@ -48,7 +46,6 @@ define void @dse_memset_chk_eliminate_store2(ptr %out, i64 %n) {
4846
define void @dse_memset_chk_eliminates_store_local_object_escapes_after(i64 %n) {
4947
; CHECK-LABEL: @dse_memset_chk_eliminates_store_local_object_escapes_after(
5048
; CHECK-NEXT: [[A:%.*]] = alloca [200 x i8], align 1
51-
; CHECK-NEXT: store i8 10, ptr [[A]], align 1
5249
; CHECK-NEXT: [[OUT_100:%.*]] = getelementptr i8, ptr [[A]], i64 100
5350
; CHECK-NEXT: store i8 10, ptr [[OUT_100]], align 1
5451
; CHECK-NEXT: [[CALL_2:%.*]] = tail call ptr @__memset_chk(ptr [[A]], i32 42, i64 100, i64 [[N:%.*]])
@@ -68,7 +65,6 @@ define void @dse_memset_chk_eliminates_store_local_object_escapes_before(i64 %n)
6865
; CHECK-LABEL: @dse_memset_chk_eliminates_store_local_object_escapes_before(
6966
; CHECK-NEXT: [[A:%.*]] = alloca [200 x i8], align 1
7067
; CHECK-NEXT: call void @use(ptr [[A]])
71-
; CHECK-NEXT: store i8 10, ptr [[A]], align 1
7268
; CHECK-NEXT: [[OUT_100:%.*]] = getelementptr i8, ptr [[A]], i64 100
7369
; CHECK-NEXT: store i8 0, ptr [[OUT_100]], align 1
7470
; CHECK-NEXT: [[CALL_2:%.*]] = tail call ptr @__memset_chk(ptr [[A]], i32 42, i64 100, i64 [[N:%.*]])

0 commit comments

Comments
 (0)