Skip to content

Commit 948efa6

Browse files
committed
Encode the basic refinement of escaped local blocks
1 parent e6e17b9 commit 948efa6

File tree

8 files changed

+121
-41
lines changed

8 files changed

+121
-41
lines changed

ir/memory.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,11 +1903,12 @@ expr Memory::int2ptr(const expr &val) const {
19031903
return expr::mkIf(val == 0, null, fn);
19041904
}
19051905

1906-
expr Memory::blockValRefined(const Memory &other, unsigned bid, bool local,
1906+
expr Memory::blockValRefined(const Memory &other, unsigned bid,
1907+
optional<unsigned> bid_other,
19071908
const expr &offset, set<expr> &undef) const {
1908-
assert(!local);
1909-
auto &mem1 = non_local_block_val[bid];
1910-
auto &mem2 = other.non_local_block_val[bid].val;
1909+
auto &mem1 = (bid_other ? local_block_val : non_local_block_val)[bid];
1910+
auto &mem2 = bid_other ? other.local_block_val[*bid_other].val :
1911+
other.non_local_block_val[bid].val;
19111912

19121913
if (mem1.val.eq(mem2))
19131914
return true;
@@ -1938,7 +1939,23 @@ expr Memory::blockValRefined(const Memory &other, unsigned bid, bool local,
19381939
return val.refined(val2);
19391940
}
19401941

1942+
expr Memory::blockPropertiesRefined(const Pointer &src, const Pointer &tgt)
1943+
const {
1944+
expr aligned(true);
1945+
expr src_align = src.blockAlignment();
1946+
expr tgt_align = tgt.blockAlignment();
1947+
// if they are both non-const, then the condition holds per the precondition
1948+
if (src_align.isConst() || tgt_align.isConst())
1949+
aligned = src_align.ule(tgt_align);
1950+
1951+
return src.isBlockAlive() == tgt.isBlockAlive() &&
1952+
src.blockSize() == tgt.blockSize() &&
1953+
src.getAllocType() == tgt.getAllocType() &&
1954+
aligned;
1955+
}
1956+
19411957
expr Memory::blockRefined(const Pointer &src, const Pointer &tgt, unsigned bid,
1958+
optional<unsigned> bid_other,
19421959
set<expr> &undef) const {
19431960
unsigned bytes_per_byte = bits_byte / 8;
19441961

@@ -1953,29 +1970,18 @@ expr Memory::blockRefined(const Pointer &src, const Pointer &tgt, unsigned bid,
19531970
expr off_expr = expr::mkUInt(off, Pointer::bitsShortOffset());
19541971
val_refines
19551972
&= (ptr_offset == off_expr).implies(
1956-
blockValRefined(tgt.getMemory(), bid, false, off_expr, undef));
1973+
blockValRefined(tgt.getMemory(), bid, bid_other, off_expr, undef));
19571974
}
19581975
} else {
19591976
val_refines
19601977
= src.getOffsetSizet().ult(src.blockSizeOffsetT()).implies(
1961-
blockValRefined(tgt.getMemory(), bid, false, ptr_offset, undef));
1978+
blockValRefined(tgt.getMemory(), bid, bid_other, ptr_offset, undef));
19621979
}
19631980

19641981
assert(src.isWritable().eq(tgt.isWritable()));
19651982

1966-
expr aligned(true);
1967-
expr src_align = src.blockAlignment();
1968-
expr tgt_align = tgt.blockAlignment();
1969-
// if they are both non-const, then the condition holds per the precondition
1970-
if (src_align.isConst() || tgt_align.isConst())
1971-
aligned = src_align.ule(tgt_align);
1972-
19731983
expr alive = src.isBlockAlive();
1974-
return alive == tgt.isBlockAlive() &&
1975-
blk_size == tgt.blockSize() &&
1976-
src.getAllocType() == tgt.getAllocType() &&
1977-
aligned &&
1978-
alive.implies(val_refines);
1984+
return blockPropertiesRefined(src, tgt) && alive.implies(val_refines);
19791985
}
19801986

19811987
tuple<expr, Pointer, set<expr>>
@@ -2027,7 +2033,8 @@ Memory::refined(const Memory &other, bool fncall,
20272033
Pointer q(other, p());
20282034
if (p.isByval().isTrue() && q.isByval().isTrue())
20292035
continue;
2030-
ret &= (ptr_bid == bid_expr).implies(blockRefined(p, q, bid, undef_vars));
2036+
ret &= (ptr_bid == bid_expr)
2037+
.implies(blockRefined(p, q, bid, nullopt, undef_vars));
20312038
}
20322039

20332040
// restrict refinement check to set of request blocks

ir/memory.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,17 @@ class Memory {
195195
const smt::expr &bytes, const smt::expr &val,
196196
const std::set<smt::expr> &undef, uint64_t align);
197197

198-
smt::expr blockValRefined(const Memory &other, unsigned bid, bool local,
198+
// If bid_src == nullopt, it encodes the non-local block refinement
199+
// Otherwise, it encodes the local block refinement between bid and *bid_other
200+
smt::expr blockValRefined(const Memory &other, unsigned bid,
201+
std::optional<unsigned> bid_other,
199202
const smt::expr &offset,
200203
std::set<smt::expr> &undef) const;
201204
smt::expr blockRefined(const Pointer &src, const Pointer &tgt, unsigned bid,
205+
std::optional<unsigned> bid_other,
202206
std::set<smt::expr> &undef) const;
207+
smt::expr blockPropertiesRefined(const Pointer &src, const Pointer &tgt)
208+
const;
203209

204210
void mkLocalDisjAddrAxioms(const smt::expr &allocated,
205211
const smt::expr &short_bid,

ir/pointer.cpp

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -485,35 +485,63 @@ expr Pointer::refined(const Pointer &other) const {
485485
isBlockAlive().implies(other.isBlockAlive()));
486486
}
487487

488+
expr Pointer::encodeLoadedByteRefined(
489+
const Pointer &other, set<expr> &undef_vars) const {
490+
// TODO: can we remove these const casts?
491+
auto b1 = const_cast<Memory *>(&m)->raw_load(*this, undef_vars);
492+
auto b2 = const_cast<Memory *>(&other.m)->raw_load(other, undef_vars);
493+
return b1.refined(b2);
494+
}
495+
496+
expr Pointer::encodeLocalPtrRefinement(
497+
const Pointer &other, set<expr> &undefs) const {
498+
expr tgt_bid = other.getShortBid();
499+
500+
expr ofs = expr::mkFreshVar("localblk_ofs", expr::mkUInt(0, bits_for_offset));
501+
Pointer this_ofs = *this + ofs;
502+
Pointer other_ofs = other + ofs;
503+
504+
uint64_t bid_const, bid_tgt_const;
505+
bool is_const_bid = this_ofs.getShortBid().isUInt(bid_const) &&
506+
tgt_bid.isUInt(bid_tgt_const);
507+
expr blkrefined;
508+
if (is_const_bid)
509+
// Look into the bytes
510+
blkrefined = m.blockRefined(*this, other, (unsigned)bid_const,
511+
(unsigned)bid_tgt_const, undefs);
512+
else
513+
blkrefined = m.blockPropertiesRefined(*this, other);
514+
515+
return other.isLocal() && getOffset() == other.getOffset() &&
516+
move(blkrefined);
517+
}
518+
519+
expr Pointer::encodeByValArgRefinement(
520+
const Pointer &other, set<expr> &undefs, unsigned size) const {
521+
expr ofs = expr::mkFreshVar("localblk_ofs", expr::mkUInt(0, bits_for_offset));
522+
Pointer this_ofs = *this + ofs;
523+
Pointer other_ofs = other + ofs;
524+
525+
return ofs.ugt(expr::mkUInt(size, ofs))
526+
.implies(this_ofs.encodeLoadedByteRefined(other_ofs, undefs));
527+
}
528+
488529
expr Pointer::fninputRefined(const Pointer &other, set<expr> &undef,
489530
unsigned byval_bytes) const {
490531
expr size = blockSizeOffsetT();
491532
expr off = getOffsetSizet();
492533
expr size2 = other.blockSizeOffsetT();
493534
expr off2 = other.getOffsetSizet();
494535

495-
// TODO: check block value for byval_bytes
496536
if (byval_bytes)
497-
return true;
537+
return encodeByValArgRefinement(other, undef, byval_bytes);
498538

499-
expr local
500-
= expr::mkIf(isHeapAllocated(),
501-
getAllocType() == other.getAllocType() &&
502-
off == off2 && size == size2,
503-
504-
expr::mkIf(off.sge(0),
505-
off2.sge(0) &&
506-
expr::mkIf(off.ule(size),
507-
off2.ule(size2) && off2.uge(off) &&
508-
(size2 - off2).uge(size - off),
509-
off2.ugt(size2) && off == off2 &&
510-
size2.uge(size)),
511-
// maintains same dereferenceability before/after
512-
off == off2 && size2.uge(size)));
513-
local = (other.isLocal() || other.isByval()) && local;
539+
expr islocal = isLocal();
540+
expr local = false;
541+
if (!islocal.isFalse() && !other.isLocal().isFalse())
542+
local = encodeLocalPtrRefinement(other, undef);
514543

515-
// TODO: this induces an infinite loop
516-
// block_refined(other);
544+
local = (other.isLocal() || other.isByval()) && local;
517545

518546
return expr::mkIf(isNull(), other.isNull(),
519547
expr::mkIf(isLocal(), local, *this == other) &&

ir/pointer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ class Pointer {
3535
const smt::FunctionExpr &nonlocal_fn,
3636
const smt::expr &ret_type, bool src_name = false) const;
3737

38+
smt::expr encodeLoadedByteRefined(const Pointer &other,
39+
std::set<smt::expr> &undefs) const;
40+
smt::expr encodeLocalPtrRefinement(const Pointer &other,
41+
std::set<smt::expr> &undefs) const;
42+
smt::expr encodeByValArgRefinement(const Pointer &otherByval,
43+
std::set<smt::expr> &undefs,
44+
unsigned byval_size) const;
45+
3846
public:
3947
Pointer(const Memory &m, const smt::expr &bid, const smt::expr &offset,
4048
const smt::expr &attr);

tests/alive-tv/bugs/pr10067.srctgt.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,5 @@ define i32 @tgt() noinline {
4444
}
4545

4646
declare void @bar(%struct1* sret(%struct1))
47+
48+
; ERROR: Couldn't prove the correctness of the transformation

tests/alive-tv/bugs/pr41949-2.srctgt.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
; https://bugs.llvm.org/show_bug.cgi?id=41949
2-
; To detect a bug from this, bytes of escaped local blocks should be checked
32

43
;source_filename = "41949.ll"
54
target datalayout = "E"
@@ -23,3 +22,5 @@ define void @tgt(i32* %p) {
2322
}
2423

2524
declare void @test1(i32*)
25+
26+
; ERROR: Source is more defined than target

tests/alive-tv/calls/call-movestr2.src.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
; TODO: needs refinement of local blocks working
2-
; XPASS: Transformation seems to be correct
32

43
define i8 @f() {
54
%a = alloca i8
@@ -9,3 +8,5 @@ define i8 @f() {
98
}
109

1110
declare i8 @g(i8*)
11+
12+
; ERROR: Source is more defined than target
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
2+
target triple = "x86_64-unknown-linux-gnu"
3+
4+
define void @src(i64 %i) {
5+
entry:
6+
%storage = alloca [16 x i8], align 16
7+
%0 = getelementptr inbounds [16 x i8], [16 x i8]* %storage, i64 0, i64 0
8+
call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 16 dereferenceable(16) %0, i8 0, i64 16, i1 false)
9+
%arrayidx = getelementptr inbounds [16 x i8], [16 x i8]* %storage, i64 0, i64 %i
10+
call void @use(i8* nonnull align 1 dereferenceable(1) %arrayidx)
11+
ret void
12+
}
13+
14+
declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1)
15+
16+
declare void @use(i8* nonnull align 1 dereferenceable(1))
17+
18+
define void @tgt(i64 %i) {
19+
entry:
20+
%storage = alloca [16 x i8], align 16
21+
%0 = getelementptr inbounds [16 x i8], [16 x i8]* %storage, i64 0, i64 0
22+
%arrayidx = getelementptr inbounds [16 x i8], [16 x i8]* %storage, i64 0, i64 %i
23+
call void @use(i8* nonnull align 1 dereferenceable(1) %arrayidx)
24+
ret void
25+
}
26+
27+
; ERROR: Source is more defined than target

0 commit comments

Comments
 (0)