Skip to content
This repository has been archived by the owner on Feb 5, 2019. It is now read-only.

Commit

Permalink
[InstCombine] Fix negative GEP offset evaluation for 32-bit pointers
Browse files Browse the repository at this point in the history
This fixes https://bugs.llvm.org/show_bug.cgi?id=39908.

The evaluateGEPOffsetExpression() function simplifies GEP offsets for
use in comparisons against zero, basically by converting X*Scale+Offset==0
to X+Offset/Scale==0 if Scale divides Offset. However, before this is done,
Offset is masked down to the pointer size. This results in incorrect
results for negative Offsets, because we basically end up dividing the
32-bit offset *zero* extended to 64-bit bits (rather than sign extended).

Fix this by explicitly sign extending the truncated value.

Differential Revision: https://reviews.llvm.org/D55449

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@348987 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
nikic committed Dec 13, 2018
1 parent 47d569d commit 85ec369
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
8 changes: 3 additions & 5 deletions lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,11 +522,9 @@ static Value *evaluateGEPOffsetExpression(User *GEP, InstCombiner &IC,
}

// Otherwise, there is an index. The computation we will do will be modulo
// the pointer size, so get it.
uint64_t PtrSizeMask = ~0ULL >> (64-IntPtrWidth);

Offset &= PtrSizeMask;
VariableScale &= PtrSizeMask;
// the pointer size.
Offset = SignExtend64(Offset, IntPtrWidth);
VariableScale = SignExtend64(VariableScale, IntPtrWidth);

// To do this transformation, any constant index must be a multiple of the
// variable scale factor. For example, we can evaluate "12 + 4*i" as "3 + i",
Expand Down
49 changes: 49 additions & 0 deletions test/Transforms/InstCombine/pr39908.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -instcombine -S | FileCheck %s

target datalayout = "p:32:32"

%S = type { [2 x i32] }

define i1 @test([0 x %S]* %p, i32 %n) {
; CHECK-LABEL: @test(
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[N:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP]]
;
%start.cast = bitcast [0 x %S]* %p to %S*
%end = getelementptr inbounds [0 x %S], [0 x %S]* %p, i32 0, i32 %n, i32 0, i32 0
%end.cast = bitcast i32* %end to %S*
%last = getelementptr inbounds %S, %S* %end.cast, i32 -1
%cmp = icmp eq %S* %last, %start.cast
ret i1 %cmp
}

; Same test using 64-bit indices.
define i1 @test64([0 x %S]* %p, i64 %n) {
; CHECK-LABEL: @test64(
; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[N:%.*]] to i32
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: ret i1 [[CMP]]
;
%start.cast = bitcast [0 x %S]* %p to %S*
%end = getelementptr inbounds [0 x %S], [0 x %S]* %p, i64 0, i64 %n, i32 0, i64 0
%end.cast = bitcast i32* %end to %S*
%last = getelementptr inbounds %S, %S* %end.cast, i64 -1
%cmp = icmp eq %S* %last, %start.cast
ret i1 %cmp
}

; Here the offset overflows and is treated modulo 2^32. This is UB.
define i1 @test64_overflow([0 x %S]* %p, i64 %n) {
; CHECK-LABEL: @test64_overflow(
; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[N:%.*]] to i32
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: ret i1 [[CMP]]
;
%start.cast = bitcast [0 x %S]* %p to %S*
%end = getelementptr inbounds [0 x %S], [0 x %S]* %p, i64 0, i64 %n, i32 0, i64 8589934592
%end.cast = bitcast i32* %end to %S*
%last = getelementptr inbounds %S, %S* %end.cast, i64 -1
%cmp = icmp eq %S* %last, %start.cast
ret i1 %cmp
}

0 comments on commit 85ec369

Please sign in to comment.