Skip to content

Commit 47132d3

Browse files
committed
Encode the basic refinement of escaped local blocks
1 parent 10c54db commit 47132d3

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
@@ -1855,11 +1855,12 @@ expr Memory::int2ptr(const expr &val) const {
18551855
return expr::mkIf(val == 0, null, fn);
18561856
}
18571857

1858-
expr Memory::blockValRefined(const Memory &other, unsigned bid, bool local,
1858+
expr Memory::blockValRefined(const Memory &other, unsigned bid,
1859+
optional<unsigned> bid_other,
18591860
const expr &offset, set<expr> &undef) const {
1860-
assert(!local);
1861-
auto &mem1 = non_local_block_val[bid];
1862-
auto &mem2 = other.non_local_block_val[bid].val;
1861+
auto &mem1 = (bid_other ? local_block_val : non_local_block_val)[bid];
1862+
auto &mem2 = bid_other ? other.local_block_val[*bid_other].val :
1863+
other.non_local_block_val[bid].val;
18631864

18641865
if (mem1.val.eq(mem2))
18651866
return true;
@@ -1890,7 +1891,23 @@ expr Memory::blockValRefined(const Memory &other, unsigned bid, bool local,
18901891
return val.refined(val2);
18911892
}
18921893

1894+
expr Memory::blockPropertiesRefined(const Pointer &src, const Pointer &tgt)
1895+
const {
1896+
expr aligned(true);
1897+
expr src_align = src.blockAlignment();
1898+
expr tgt_align = tgt.blockAlignment();
1899+
// if they are both non-const, then the condition holds per the precondition
1900+
if (src_align.isConst() || tgt_align.isConst())
1901+
aligned = src_align.ule(tgt_align);
1902+
1903+
return src.isBlockAlive() == tgt.isBlockAlive() &&
1904+
src.blockSize() == tgt.blockSize() &&
1905+
src.getAllocType() == tgt.getAllocType() &&
1906+
aligned;
1907+
}
1908+
18931909
expr Memory::blockRefined(const Pointer &src, const Pointer &tgt, unsigned bid,
1910+
optional<unsigned> bid_other,
18941911
set<expr> &undef) const {
18951912
unsigned bytes_per_byte = bits_byte / 8;
18961913

@@ -1905,29 +1922,18 @@ expr Memory::blockRefined(const Pointer &src, const Pointer &tgt, unsigned bid,
19051922
expr off_expr = expr::mkUInt(off, Pointer::bitsShortOffset());
19061923
val_refines
19071924
&= (ptr_offset == off_expr).implies(
1908-
blockValRefined(tgt.getMemory(), bid, false, off_expr, undef));
1925+
blockValRefined(tgt.getMemory(), bid, bid_other, off_expr, undef));
19091926
}
19101927
} else {
19111928
val_refines
19121929
= src.getOffsetSizet().ult(src.blockSizeOffsetT()).implies(
1913-
blockValRefined(tgt.getMemory(), bid, false, ptr_offset, undef));
1930+
blockValRefined(tgt.getMemory(), bid, bid_other, ptr_offset, undef));
19141931
}
19151932

19161933
assert(src.isWritable().eq(tgt.isWritable()));
19171934

1918-
expr aligned(true);
1919-
expr src_align = src.blockAlignment();
1920-
expr tgt_align = tgt.blockAlignment();
1921-
// if they are both non-const, then the condition holds per the precondition
1922-
if (src_align.isConst() || tgt_align.isConst())
1923-
aligned = src_align.ule(tgt_align);
1924-
19251935
expr alive = src.isBlockAlive();
1926-
return alive == tgt.isBlockAlive() &&
1927-
blk_size == tgt.blockSize() &&
1928-
src.getAllocType() == tgt.getAllocType() &&
1929-
aligned &&
1930-
alive.implies(val_refines);
1936+
return blockPropertiesRefined(src, tgt) && alive.implies(val_refines);
19311937
}
19321938

19331939
tuple<expr, Pointer, set<expr>>
@@ -1958,7 +1964,8 @@ Memory::refined(const Memory &other, bool skip_constants,
19581964
Pointer q(other, p());
19591965
if (p.isByval().isTrue() && q.isByval().isTrue())
19601966
continue;
1961-
ret &= (ptr_bid == bid_expr).implies(blockRefined(p, q, bid, undef_vars));
1967+
ret &= (ptr_bid == bid_expr)
1968+
.implies(blockRefined(p, q, bid, nullopt, undef_vars));
19621969
}
19631970

19641971
// 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
@@ -486,35 +486,63 @@ expr Pointer::refined(const Pointer &other) const {
486486
isBlockAlive().implies(other.isBlockAlive()));
487487
}
488488

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

496-
// TODO: check block value for byval_bytes
497537
if (byval_bytes)
498-
return true;
538+
return encodeByValArgRefinement(other, undef, byval_bytes);
499539

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

516-
// TODO: this induces an infinite loop
517-
// block_refined(other);
545+
local = (other.isLocal() || other.isByval()) && local;
518546

519547
return expr::mkIf(isNull(), other.isNull(),
520548
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)