; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -mtriple=arm -type-promotion -verify -disable-type-promotion=false -S %s -o - | FileCheck %s

define void @phi_pointers(i16* %a, i16* %b, i8 zeroext %M, i8 zeroext %N) {
; CHECK-LABEL: @phi_pointers(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = zext i8 [[M:%.*]] to i32
; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[N:%.*]] to i32
; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[TMP0]], 1
; CHECK-NEXT:    [[AND:%.*]] = and i32 [[ADD]], 1
; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[ADD]], [[TMP1]]
; CHECK-NEXT:    [[BASE:%.*]] = select i1 [[CMP]], i16* [[A:%.*]], i16* [[B:%.*]]
; CHECK-NEXT:    [[OTHER:%.*]] = select i1 [[CMP]], i16* [[B]], i16* [[B]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[PTR:%.*]] = phi i16* [ [[BASE]], [[ENTRY:%.*]] ], [ [[GEP:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[IDX:%.*]] = phi i32 [ [[AND]], [[ENTRY]] ], [ [[INC:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOAD:%.*]] = load i16, i16* [[PTR]], align 2
; CHECK-NEXT:    [[INC]] = add nuw nsw i32 [[IDX]], 1
; CHECK-NEXT:    [[GEP]] = getelementptr inbounds i16, i16* [[PTR]], i32 [[INC]]
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i16* [[GEP]], [[OTHER]]
; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %add = add nuw i8 %M, 1
  %and = and i8 %add, 1
  %cmp = icmp ugt i8 %add, %N
  %base = select i1 %cmp, i16* %a, i16* %b
  %other = select i1 %cmp, i16* %b, i16* %b
  br label %loop

loop:
  %ptr = phi i16* [ %base, %entry ], [ %gep, %loop ]
  %idx = phi i8 [ %and, %entry ], [ %inc, %loop ]
  %load = load i16, i16* %ptr, align 2
  %inc = add nuw nsw i8 %idx, 1
  %gep = getelementptr inbounds i16, i16* %ptr, i8 %inc
  %cond = icmp eq i16* %gep, %other
  br i1 %cond, label %exit, label %loop

exit:
  ret void
}

define void @phi_pointers_null(i16* %a, i16* %b, i8 zeroext %M, i8 zeroext %N) {
; CHECK-LABEL: @phi_pointers_null(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = zext i8 [[M:%.*]] to i32
; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[N:%.*]] to i32
; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[TMP0]], 1
; CHECK-NEXT:    [[AND:%.*]] = and i32 [[ADD]], 1
; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[ADD]], [[TMP1]]
; CHECK-NEXT:    [[BASE:%.*]] = select i1 [[CMP]], i16* [[A:%.*]], i16* [[B:%.*]]
; CHECK-NEXT:    [[OTHER:%.*]] = select i1 [[CMP]], i16* [[B]], i16* [[B]]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp eq i16* [[BASE]], [[OTHER]]
; CHECK-NEXT:    br i1 [[CMP_1]], label [[FAIL:%.*]], label [[LOOP:%.*]]
; CHECK:       fail:
; CHECK-NEXT:    br label [[LOOP]]
; CHECK:       loop:
; CHECK-NEXT:    [[PTR:%.*]] = phi i16* [ [[BASE]], [[ENTRY:%.*]] ], [ null, [[FAIL]] ], [ [[GEP:%.*]], [[IF_THEN:%.*]] ]
; CHECK-NEXT:    [[IDX:%.*]] = phi i32 [ [[AND]], [[ENTRY]] ], [ 0, [[FAIL]] ], [ [[INC:%.*]], [[IF_THEN]] ]
; CHECK-NEXT:    [[UNDEF:%.*]] = icmp eq i16* [[PTR]], undef
; CHECK-NEXT:    br i1 [[UNDEF]], label [[EXIT:%.*]], label [[IF_THEN]]
; CHECK:       if.then:
; CHECK-NEXT:    [[LOAD:%.*]] = load i16, i16* [[PTR]], align 2
; CHECK-NEXT:    [[INC]] = add nuw nsw i32 [[IDX]], 1
; CHECK-NEXT:    [[GEP]] = getelementptr inbounds i16, i16* [[PTR]], i32 [[INC]]
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i16* [[GEP]], [[OTHER]]
; CHECK-NEXT:    br i1 [[COND]], label [[EXIT]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %add = add nuw i8 %M, 1
  %and = and i8 %add, 1
  %cmp = icmp ugt i8 %add, %N
  %base = select i1 %cmp, i16* %a, i16* %b
  %other = select i1 %cmp, i16* %b, i16* %b
  %cmp.1 = icmp eq i16* %base, %other
  br i1 %cmp.1, label %fail, label %loop

fail:
  br label %loop

loop:
  %ptr = phi i16* [ %base, %entry ], [ null, %fail ], [ %gep, %if.then ]
  %idx = phi i8 [ %and, %entry ], [ 0, %fail ], [ %inc, %if.then ]
  %undef = icmp eq i16* %ptr, undef
  br i1 %undef, label %exit, label %if.then

if.then:
  %load = load i16, i16* %ptr, align 2
  %inc = add nuw nsw i8 %idx, 1
  %gep = getelementptr inbounds i16, i16* %ptr, i8 %inc
  %cond = icmp eq i16* %gep, %other
  br i1 %cond, label %exit, label %loop

exit:
  ret void
}

declare i8 @do_something_with_ptr(i8, i16*)

define i8 @call_pointer(i8 zeroext %x, i8 zeroext %y, i16* %a, i16* %b) {
; CHECK-LABEL: @call_pointer(
; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; CHECK-NEXT:    [[TMP2:%.*]] = zext i8 [[Y:%.*]] to i32
; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[OR]], 1
; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[SHR]], 2
; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[ADD]], 0
; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[CMP]], i16* [[A:%.*]], i16* [[B:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = trunc i32 [[SHR]] to i8
; CHECK-NEXT:    [[CALL:%.*]] = tail call zeroext i8 @do_something_with_ptr(i8 [[TMP3]], i16* [[PTR]])
; CHECK-NEXT:    [[TMP4:%.*]] = zext i8 [[CALL]] to i32
; CHECK-NEXT:    [[TMP5:%.*]] = trunc i32 [[TMP4]] to i8
; CHECK-NEXT:    ret i8 [[TMP5]]
;
  %or = or i8 %x, %y
  %shr = lshr i8 %or, 1
  %add = add nuw i8 %shr, 2
  %cmp = icmp ne i8 %add, 0
  %ptr = select i1 %cmp, i16* %a, i16* %b
  %call = tail call zeroext i8 @do_something_with_ptr(i8 %shr, i16* %ptr)
  ret i8 %call
}

define i16 @pointer_to_pointer(i16** %arg, i16 zeroext %limit) {
; CHECK-LABEL: @pointer_to_pointer(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[ADDR:%.*]] = load i16*, i16** [[ARG:%.*]]
; CHECK-NEXT:    [[VAL:%.*]] = load i16, i16* [[ADDR]]
; CHECK-NEXT:    [[TMP0:%.*]] = zext i16 [[VAL]] to i32
; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[TMP0]], 7
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], 256
; CHECK-NEXT:    [[RES:%.*]] = select i1 [[CMP]], i16 128, i16 255
; CHECK-NEXT:    ret i16 [[RES]]
;
entry:
  %addr = load i16*, i16** %arg
  %val = load i16, i16* %addr
  %add = add nuw i16 %val, 7
  %cmp = icmp ult i16 %add, 256
  %res = select i1 %cmp, i16 128, i16 255
  ret i16 %res
}

define i8 @gep_2d_array(i8** %a, i8 zeroext %arg) {
; CHECK-LABEL: @gep_2d_array(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = zext i8 [[ARG:%.*]] to i32
; CHECK-NEXT:    [[ARRAYIDX_US:%.*]] = getelementptr inbounds i8*, i8** [[A:%.*]], i32 0
; CHECK-NEXT:    [[TMP1:%.*]] = load i8*, i8** [[ARRAYIDX_US]], align 4
; CHECK-NEXT:    [[TMP2:%.*]] = load i8, i8* [[TMP1]], align 1
; CHECK-NEXT:    [[TMP3:%.*]] = zext i8 [[TMP2]] to i32
; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i32 [[TMP3]], 1
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[SUB]], [[TMP0]]
; CHECK-NEXT:    [[RES:%.*]] = select i1 [[CMP]], i8 27, i8 54
; CHECK-NEXT:    ret i8 [[RES]]
;
entry:
  %arrayidx.us = getelementptr inbounds i8*, i8** %a, i32 0
  %0 = load i8*, i8** %arrayidx.us, align 4
  %1 = load i8, i8* %0, align 1
  %sub = sub nuw i8 %1, 1
  %cmp = icmp ult i8 %sub, %arg
  %res = select i1 %cmp, i8 27, i8 54
  ret i8 %res
}

define void @gep_2d_array_loop(i16** nocapture readonly %a, i16** nocapture readonly %b, i32 %N) {
; CHECK-LABEL: @gep_2d_array_loop(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[CMP30:%.*]] = icmp eq i32 [[N:%.*]], 0
; CHECK-NEXT:    br i1 [[CMP30]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_COND1_PREHEADER_US:%.*]]
; CHECK:       for.cond1.preheader.us:
; CHECK-NEXT:    [[Y_031_US:%.*]] = phi i32 [ [[INC13_US:%.*]], [[FOR_COND1_FOR_COND_CLEANUP3_CRIT_EDGE_US:%.*]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    br label [[FOR_BODY4_US:%.*]]
; CHECK:       for.body4.us:
; CHECK-NEXT:    [[X_029_US:%.*]] = phi i32 [ 0, [[FOR_COND1_PREHEADER_US]] ], [ [[INC_US:%.*]], [[FOR_BODY4_US]] ]
; CHECK-NEXT:    [[ARRAYIDX_US:%.*]] = getelementptr inbounds i16*, i16** [[A:%.*]], i32 [[X_029_US]]
; CHECK-NEXT:    [[TMP0:%.*]] = load i16*, i16** [[ARRAYIDX_US]], align 4
; CHECK-NEXT:    [[ARRAYIDX5_US:%.*]] = getelementptr inbounds i16, i16* [[TMP0]], i32 [[Y_031_US]]
; CHECK-NEXT:    [[TMP1:%.*]] = load i16, i16* [[ARRAYIDX5_US]], align 2
; CHECK-NEXT:    [[TMP2:%.*]] = zext i16 [[TMP1]] to i32
; CHECK-NEXT:    [[DEC_US:%.*]] = add nuw i32 [[TMP2]], 65535
; CHECK-NEXT:    [[CMP6_US:%.*]] = icmp ult i32 [[DEC_US]], 16383
; CHECK-NEXT:    [[SHL_US:%.*]] = shl nuw i32 [[DEC_US]], 2
; CHECK-NEXT:    [[SPEC_SELECT_US:%.*]] = select i1 [[CMP6_US]], i32 [[SHL_US]], i32 [[DEC_US]]
; CHECK-NEXT:    [[ARRAYIDX10_US:%.*]] = getelementptr inbounds i16*, i16** [[B:%.*]], i32 [[X_029_US]]
; CHECK-NEXT:    [[TMP3:%.*]] = load i16*, i16** [[ARRAYIDX10_US]], align 4
; CHECK-NEXT:    [[ARRAYIDX11_US:%.*]] = getelementptr inbounds i16, i16* [[TMP3]], i32 [[Y_031_US]]
; CHECK-NEXT:    [[TMP4:%.*]] = trunc i32 [[SPEC_SELECT_US]] to i16
; CHECK-NEXT:    store i16 [[TMP4]], i16* [[ARRAYIDX11_US]], align 2
; CHECK-NEXT:    [[INC_US]] = add nuw i32 [[X_029_US]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i32 [[INC_US]], [[N]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_COND1_FOR_COND_CLEANUP3_CRIT_EDGE_US]], label [[FOR_BODY4_US]]
; CHECK:       for.cond1.for.cond.cleanup3_crit_edge.us:
; CHECK-NEXT:    [[INC13_US]] = add nuw i32 [[Y_031_US]], 1
; CHECK-NEXT:    [[EXITCOND32:%.*]] = icmp eq i32 [[INC13_US]], [[N]]
; CHECK-NEXT:    br i1 [[EXITCOND32]], label [[FOR_COND_CLEANUP]], label [[FOR_COND1_PREHEADER_US]]
; CHECK:       for.cond.cleanup:
; CHECK-NEXT:    ret void
;
entry:
  %cmp30 = icmp eq i32 %N, 0
  br i1 %cmp30, label %for.cond.cleanup, label %for.cond1.preheader.us

for.cond1.preheader.us:
  %y.031.us = phi i32 [ %inc13.us, %for.cond1.for.cond.cleanup3_crit_edge.us ], [ 0, %entry ]
  br label %for.body4.us

for.body4.us:
  %x.029.us = phi i32 [ 0, %for.cond1.preheader.us ], [ %inc.us, %for.body4.us ]
  %arrayidx.us = getelementptr inbounds i16*, i16** %a, i32 %x.029.us
  %0 = load i16*, i16** %arrayidx.us, align 4
  %arrayidx5.us = getelementptr inbounds i16, i16* %0, i32 %y.031.us
  %1 = load i16, i16* %arrayidx5.us, align 2
  %dec.us = add nuw i16 %1, -1
  %cmp6.us = icmp ult i16 %dec.us, 16383
  %shl.us = shl nuw i16 %dec.us, 2
  %spec.select.us = select i1 %cmp6.us, i16 %shl.us, i16 %dec.us
  %arrayidx10.us = getelementptr inbounds i16*, i16** %b, i32 %x.029.us
  %2 = load i16*, i16** %arrayidx10.us, align 4
  %arrayidx11.us = getelementptr inbounds i16, i16* %2, i32 %y.031.us
  store i16 %spec.select.us, i16* %arrayidx11.us, align 2
  %inc.us = add nuw i32 %x.029.us, 1
  %exitcond = icmp eq i32 %inc.us, %N
  br i1 %exitcond, label %for.cond1.for.cond.cleanup3_crit_edge.us, label %for.body4.us

for.cond1.for.cond.cleanup3_crit_edge.us:
  %inc13.us = add nuw i32 %y.031.us, 1
  %exitcond32 = icmp eq i32 %inc13.us, %N
  br i1 %exitcond32, label %for.cond.cleanup, label %for.cond1.preheader.us

for.cond.cleanup:
  ret void
}
