if (isa<ConstantPointerNull>(Op))
return eraseInstFromFunction(FI);
+ // If we free a pointer we've been explicitly told won't be freed, this
+ // would be full UB and thus we can conclude this is unreachable. Cases:
+ // 1) freeing a pointer which is explicitly nofree
+ // 2) calling free from a call site marked nofree
+ // 3) calling free in a function scope marked nofree
+ if (auto *A = dyn_cast<Argument>(Op->stripPointerCasts()))
+ if (A->hasAttribute(Attribute::NoFree) ||
+ FI.hasFnAttr(Attribute::NoFree) ||
+ FI.getFunction()->hasFnAttribute(Attribute::NoFree)) {
+ // Leave a marker since we can't modify the CFG here.
+ CreateNonTerminatorUnreachable(&FI);
+ return eraseInstFromFunction(FI);
+ }
+
// If we optimize for code size, try to move the call to free before the null
// test so that simplify cfg can remove the empty block and dead code
// elimination the branch. I.e., helps to turn something like:
define void @test11() {
; CHECK-LABEL: @test11(
-; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable(8) i8* @_Znwm(i64 8) #7
+; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable(8) i8* @_Znwm(i64 8) #8
; CHECK-NEXT: call void @_ZdlPv(i8* nonnull [[CALL]])
; CHECK-NEXT: ret void
;
ret void
}
+; Freeing a no-free pointer -> full UB
+define void @test13(i8* nofree %foo) {
+; CHECK-LABEL: @test13(
+; CHECK-NEXT: store i1 true, i1* undef, align 1
+; CHECK-NEXT: ret void
+;
+ call void @free(i8* %foo)
+ ret void
+}
+
+; Freeing a no-free pointer -> full UB
+define void @test14(i8* %foo) nofree {
+; CHECK-LABEL: @test14(
+; CHECK-NEXT: store i1 true, i1* undef, align 1
+; CHECK-NEXT: ret void
+;
+ call void @free(i8* %foo)
+ ret void
+}
+
+; free call marked no-free -> full UB
+define void @test15(i8* %foo) {
+; CHECK-LABEL: @test15(
+; CHECK-NEXT: store i1 true, i1* undef, align 1
+; CHECK-NEXT: ret void
+;
+ call void @free(i8* %foo) nofree
+ ret void
+}