Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion enzyme/Enzyme/TypeAnalysis/TypeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2115,8 +2115,17 @@ void TypeAnalyzer::visitGEPOperator(GEPOperator &gep) {
}
}

// An index is an offset rather than a pointer if the GEP promises not to
// wrap. `nusw` suffices; `inbounds` also guarantees the result stays within
// the object, which this rule does not need. flang emits `nusw nuw`, never
// `inbounds`, for array element addresses.
bool indexCannotWrap = gep.isInBounds();
#if LLVM_VERSION_MAJOR >= 19
indexCannotWrap |= gep.hasNoUnsignedSignedWrap();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wsmoses not sure if this is 100% legal, but flang loves to emit nusw

#endif

if (has_non_const_idx &&
(gep.isInBounds() ||
(indexCannotWrap ||
(!EnzymeStrictAliasing &&
pointerAnalysis.Inner0() == BaseType::Pointer &&
getAnalysis(&gep).Inner0() == BaseType::Pointer))) {
Expand Down
1 change: 1 addition & 0 deletions enzyme/test/Fortran/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ message("Building Fortran tests")

add_subdirectory(ForwardMode)
add_subdirectory(ReverseMode)
add_subdirectory(TypeAnalysis)

# Run regression and unit tests
add_lit_testsuite(check-enzyme-fortran "Running enzyme fortran integration tests"
Expand Down
8 changes: 8 additions & 0 deletions enzyme/test/Fortran/TypeAnalysis/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Run regression and unit tests
add_lit_testsuite(check-enzyme-fortran-typeanalysis "Running enzyme fortran type analysis tests"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${ENZYME_TEST_DEPS} LLVMEnzyme-${LLVM_VERSION_MAJOR}
ARGS -v
)

set_target_properties(check-enzyme-fortran-typeanalysis PROPERTIES FOLDER "Tests")
23 changes: 23 additions & 0 deletions enzyme/test/Fortran/TypeAnalysis/gep_nusw.f90

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling that the Fortitude linter I propose to introduce in #3130 for Fortran source would complain here that we have a subroutine that isn't contained in a program or module. Would this type of test still work if the subroutine were put inside a program or module?

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
! REQUIRES: flangenzyme
! RUN: %fc -S -emit-llvm -O1 %s -o %t.ll
! RUN: FileCheck %s --check-prefix=IR < %t.ll
! RUN: %opt < %t.ll %newLoadEnzyme -passes="print-type-analysis" -type-analysis-func=stride_index_ -S -o /dev/null | FileCheck %s --check-prefix=TA

! Companion to test/TypeAnalysis/gepnusw.ll: shows that the `nusw nuw` GEP the
! rule is about is what flang actually emits for an array element address.

subroutine stride_index(a, n, i, res)
implicit none
integer, intent(in) :: n, i
double precision, intent(in) :: a(n, *)
double precision, intent(out) :: res
res = a(i, 2)
end subroutine stride_index

! IR: getelementptr nusw nuw

! The leading dimension `n`, and the stride computation indexing with it:
! TA: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Integer, [-1,1]:Integer, [-1,2]:Integer, [-1,3]:Integer}
! TA: %{{[0-9]+}} = load i32, ptr %{{[0-9]+}}, align 4{{.*}}: {[-1]:Integer}
! TA-NEXT: %{{[0-9]+}} = tail call i32 @llvm.smax.i32({{.*}}): {[-1]:Integer}
! TA-NEXT: %{{[0-9]+}} = zext nneg i32 %{{[0-9]+}} to i64: {[-1]:Integer}
51 changes: 51 additions & 0 deletions enzyme/test/TypeAnalysis/gepnusw.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
; RUN: if [ %llvmver -ge 19 ]; then %opt < %s %newLoadEnzyme -passes="print-type-analysis" -type-analysis-func=f_nusw -S -o /dev/null | FileCheck %s --check-prefix=NUSW; fi
; RUN: if [ %llvmver -ge 19 ]; then %opt < %s %newLoadEnzyme -passes="print-type-analysis" -type-analysis-func=f_inbounds -S -o /dev/null | FileCheck %s --check-prefix=INBOUNDS; fi

; A `nusw` GEP must type its indices exactly as an `inbounds` one does. The two
; functions below are identical apart from the no-wrap flag.

declare i32 @llvm.smax.i32(i32, i32)

define double @f_nusw(ptr %yh, ptr %ldyh, i64 %i) {
entry:
%n = load i32, ptr %ldyh, align 4
%m = call i32 @llvm.smax.i32(i32 %n, i32 0)
%z = zext i32 %m to i64
%idx = mul i64 %i, %z
%p = getelementptr nusw nuw double, ptr %yh, i64 %idx
%v = load double, ptr %p, align 8
ret double %v
}

define double @f_inbounds(ptr %yh, ptr %ldyh, i64 %i) {
entry:
%n = load i32, ptr %ldyh, align 4
%m = call i32 @llvm.smax.i32(i32 %n, i32 0)
%z = zext i32 %m to i64
%idx = mul i64 %i, %z
%p = getelementptr inbounds double, ptr %yh, i64 %idx
%v = load double, ptr %p, align 8
ret double %v
}

; NUSW: ptr %yh: {[-1]:Pointer}
; NUSW-NEXT: ptr %ldyh: {[-1]:Pointer, [-1,0]:Integer, [-1,1]:Integer, [-1,2]:Integer, [-1,3]:Integer}
; NUSW-NEXT: i64 %i: {[-1]:Integer}
; NUSW-NEXT: entry
; NUSW-NEXT: %n = load i32, ptr %ldyh, align 4: {[-1]:Integer}
; NUSW-NEXT: %m = call i32 @llvm.smax.i32(i32 %n, i32 0): {[-1]:Integer}
; NUSW-NEXT: %z = zext i32 %m to i64: {[-1]:Integer}
; NUSW-NEXT: %idx = mul i64 %i, %z: {[-1]:Integer}
; NUSW-NEXT: %p = getelementptr nusw nuw double, ptr %yh, i64 %idx: {[-1]:Pointer, [-1,0]:Float@double}
; NUSW-NEXT: %v = load double, ptr %p, align 8: {[-1]:Float@double}

; INBOUNDS: ptr %yh: {[-1]:Pointer}
; INBOUNDS-NEXT: ptr %ldyh: {[-1]:Pointer, [-1,0]:Integer, [-1,1]:Integer, [-1,2]:Integer, [-1,3]:Integer}
; INBOUNDS-NEXT: i64 %i: {[-1]:Integer}
; INBOUNDS-NEXT: entry
; INBOUNDS-NEXT: %n = load i32, ptr %ldyh, align 4: {[-1]:Integer}
; INBOUNDS-NEXT: %m = call i32 @llvm.smax.i32(i32 %n, i32 0): {[-1]:Integer}
; INBOUNDS-NEXT: %z = zext i32 %m to i64: {[-1]:Integer}
; INBOUNDS-NEXT: %idx = mul i64 %i, %z: {[-1]:Integer}
; INBOUNDS-NEXT: %p = getelementptr inbounds double, ptr %yh, i64 %idx: {[-1]:Pointer, [-1,0]:Float@double}
; INBOUNDS-NEXT: %v = load double, ptr %p, align 8: {[-1]:Float@double}
Loading