; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s @g = private unnamed_addr constant [5 x i8] c"test\00" @g_overaligned = private unnamed_addr constant [5 x i8] c"test\00", align 8 @g_external = external global [5 x i8] declare void @free(ptr allocptr noundef captures(none)) mustprogress nounwind willreturn allockind("free") memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" declare ptr @malloc(i64) mustprogress nofree nounwind willreturn allockind("alloc,uninitialized") allocsize(0) memory(inaccessiblemem: readwrite) "alloc-family"="malloc" declare void @may_not_return(i1) define i8 @load_global(i64 %idx) { ; CHECK-LABEL: define i8 @load_global( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_global_const_offset(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_const_offset( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP1:%.*]] = getelementptr nuw i8, ptr @g, i64 1 ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[GEP1]], i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep1 = getelementptr nuw i8, ptr @g, i64 1 %gep = getelementptr nuw i8, ptr %gep1, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 4 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_global_atomic(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_atomic( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load atomic i8, ptr [[GEP]] unordered, align 1 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g, i64 %idx %load = load atomic i8, ptr %gep unordered, align 1 %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i1 @store_global(i64 %idx) { ; CHECK-LABEL: define i1 @store_global( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: store i8 0, ptr [[GEP]], align 1 ; CHECK-NEXT: ret i1 true ; %gep = getelementptr nuw i8, ptr @g, i64 %idx store i8 0, ptr %gep %cmp = icmp ult i64 %idx, 5 ret i1 %cmp } define i1 @store_global_atomic(i64 %idx) { ; CHECK-LABEL: define i1 @store_global_atomic( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: store atomic i8 0, ptr [[GEP]] release, align 1 ; CHECK-NEXT: ret i1 true ; %gep = getelementptr nuw i8, ptr @g, i64 %idx store atomic i8 0, ptr %gep release, align 1 %cmp = icmp ult i64 %idx, 5 ret i1 %cmp } define i8 @load_byval(ptr byval([5 x i8]) %p, i64 %idx) { ; CHECK-LABEL: define i8 @load_byval( ; CHECK-SAME: ptr byval([5 x i8]) [[P:%.*]], i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr %p, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_alloca(i64 %idx) { ; CHECK-LABEL: define i8 @load_alloca( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[ALLOC:%.*]] = alloca [5 x i8], align 1 ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[ALLOC]], ptr @g, i64 5, i1 false) ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[ALLOC]], i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %alloc = alloca [5 x i8], align 1 call void @llvm.memcpy.p0.p0.i64(ptr %alloc, ptr @g, i64 5, i1 false) %gep = getelementptr nuw i8, ptr %alloc, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_malloc(i64 %idx) { ; CHECK-LABEL: define i8 @load_malloc( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[ALLOC:%.*]] = call ptr @malloc(i64 5) ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[ALLOC]], ptr @g, i64 5, i1 false) ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[ALLOC]], i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: call void @free(ptr [[ALLOC]]) ; CHECK-NEXT: ret i8 [[ADD]] ; %alloc = call ptr @malloc(i64 5) call void @llvm.memcpy.p0.p0.i64(ptr %alloc, ptr @g, i64 5, i1 false) %gep = getelementptr nuw i8, ptr %alloc, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext call void @free(ptr %alloc) ret i8 %add } define i32 @load_byval_i32(ptr byval([10 x i8]) %p, i64 %idx) { ; CHECK-LABEL: define i32 @load_byval_i32( ; CHECK-SAME: ptr byval([10 x i8]) [[P:%.*]], i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[GEP]], align 4 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i32 ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i32 [[ADD]] ; %gep = getelementptr nuw i8, ptr %p, i64 %idx %load = load i32, ptr %gep %cmp = icmp ult i64 %idx, 7 %zext = zext i1 %cmp to i32 %add = add i32 %load, %zext ret i32 %add } define i8 @load_global_may_noreturn_dom_bb(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_may_noreturn_dom_bb( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: call void @may_not_return(i1 [[CMP1]]) ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: br label %[[NEXT:.*]] ; CHECK: [[NEXT]]: ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 true to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g, i64 %idx %cmp1 = icmp ult i64 %idx, 5 call void @may_not_return(i1 %cmp1) ; %cmp1 should not be simplified. %load = load i8, ptr %gep br label %next next: %cmp2 = icmp ult i64 %idx, 5 %zext = zext i1 %cmp2 to i8 %add = add i8 %load, %zext ret i8 %add } ; Negative tests. define i8 @load_global_overaligned(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_overaligned( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g_overaligned, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g_overaligned, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_global_external(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_external( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g_external, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g_external, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_from_non_gep(ptr %p, i64 %idx) { ; CHECK-LABEL: define i8 @load_from_non_gep( ; CHECK-SAME: ptr [[P:%.*]], i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[P]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %load = load i8, ptr %p %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_global_multi_indices(i64 %idx1, i64 %idx2) { ; CHECK-LABEL: define i8 @load_global_multi_indices( ; CHECK-SAME: i64 [[IDX1:%.*]], i64 [[IDX2:%.*]]) { ; CHECK-NEXT: [[GEP1:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX1]] ; CHECK-NEXT: [[GEP2:%.*]] = getelementptr nuw i8, ptr [[GEP1]], i64 [[IDX2]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP2]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX1]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep1 = getelementptr nuw i8, ptr @g, i64 %idx1 %gep2 = getelementptr nuw i8, ptr %gep1, i64 %idx2 %load = load i8, ptr %gep2 %cmp = icmp ult i64 %idx1, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_global_without_nuw(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_without_nuw( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr i8, ptr @g, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i32 @load_byval_i32_smaller_range(ptr byval([10 x i8]) %p, i64 %idx) { ; CHECK-LABEL: define i32 @load_byval_i32_smaller_range( ; CHECK-SAME: ptr byval([10 x i8]) [[P:%.*]], i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[GEP]], align 4 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 6 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i32 ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i32 [[ADD]] ; %gep = getelementptr nuw i8, ptr %p, i64 %idx %load = load i32, ptr %gep %cmp = icmp ult i64 %idx, 6 %zext = zext i1 %cmp to i32 %add = add i32 %load, %zext ret i32 %add } define i8 @load_global_volatile(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_volatile( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load volatile i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g, i64 %idx %load = load volatile i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i1 @store_global_volatile(i64 %idx) { ; CHECK-LABEL: define i1 @store_global_volatile( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: store volatile i8 0, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: ret i1 [[CMP]] ; %gep = getelementptr nuw i8, ptr @g, i64 %idx store volatile i8 0, ptr %gep %cmp = icmp ult i64 %idx, 5 ret i1 %cmp } define i8 @load_global_vscale(i64 %idx) { ; CHECK-LABEL: define i8 @load_global_vscale( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load , ptr [[GEP]], align 1 ; CHECK-NEXT: [[EXT:%.*]] = extractelement [[LOAD]], i64 0 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[EXT]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g, i64 %idx %load = load , ptr %gep %ext = extractelement %load, i64 0 %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %ext, %zext ret i8 %add } define i8 @load_from_null(i64 %idx) { ; CHECK-LABEL: define i8 @load_from_null( ; CHECK-SAME: i64 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr null, i64 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr null, i64 %idx %load = load i8, ptr %gep %cmp = icmp ult i64 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add } define i8 @load_global_non_canonical_gep(i32 %idx) { ; CHECK-LABEL: define i8 @load_global_non_canonical_gep( ; CHECK-SAME: i32 [[IDX:%.*]]) { ; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr @g, i32 [[IDX]] ; CHECK-NEXT: [[LOAD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX]], 5 ; CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8 ; CHECK-NEXT: [[ADD:%.*]] = add i8 [[LOAD]], [[ZEXT]] ; CHECK-NEXT: ret i8 [[ADD]] ; %gep = getelementptr nuw i8, ptr @g, i32 %idx %load = load i8, ptr %gep %cmp = icmp ult i32 %idx, 5 %zext = zext i1 %cmp to i8 %add = add i8 %load, %zext ret i8 %add }