blob: b65608c10fe3471a93de2af82f291e75b578184a [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -passes='loop(loop-idiom)' < %s -S | FileCheck %s
; Only introduce a `wcslen` call if the argument is known to be aligned to
; `alignof(wchar_t)`.
; Alignment unknown, no `wcslen`.
define i64 @align_unknown(ptr %p) {
; CHECK-LABEL: define i64 @align_unknown(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[INC]] = add i64 [[I]], 1
; CHECK-NEXT: [[P_I:%.*]] = getelementptr i32, ptr [[P]], i64 [[I]]
; CHECK-NEXT: [[CH:%.*]] = load i32, ptr [[P_I]], align 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CH]], 0
; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[LOOP]] ]
; CHECK-NEXT: ret i64 [[I_LCSSA]]
;
entry:
br label %loop
loop:
%i = phi i64 [ 0, %entry ], [ %inc, %loop ]
%inc = add i64 %i, 1
%p_i = getelementptr i32, ptr %p, i64 %i
%ch = load i32, ptr %p_i, align 1
%cmp = icmp eq i32 %ch, 0
br i1 %cmp, label %exit, label %loop
exit:
ret i64 %i
}
; Alignment is enough, generate `wcslen`.
define i64 @aligned_base(ptr align 4 %p) {
; CHECK-LABEL: define i64 @aligned_base(
; CHECK-SAME: ptr align 4 [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[P]])
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[INC]] = add i64 [[I]], 1
; CHECK-NEXT: [[P_I:%.*]] = getelementptr i32, ptr [[P]], i64 [[I]]
; CHECK-NEXT: [[CH:%.*]] = load i32, ptr [[P_I]], align 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CH]], 0
; CHECK-NEXT: br i1 true, label %[[EXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret i64 [[WCSLEN]]
;
entry:
br label %loop
loop:
%i = phi i64 [ 0, %entry ], [ %inc, %loop ]
%inc = add i64 %i, 1
%p_i = getelementptr i32, ptr %p, i64 %i
%ch = load i32, ptr %p_i, align 1
%cmp = icmp eq i32 %ch, 0
br i1 %cmp, label %exit, label %loop
exit:
ret i64 %i
}
; Same as `aligned_base`, but the pointer is a phi instead of a GEP on the
; induction variable. Generate `wcslen`.
define i64 @aligned_base_ptr_phi(ptr align 4 %p) {
; CHECK-LABEL: define i64 @aligned_base_ptr_phi(
; CHECK-SAME: ptr align 4 [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[I_LCSSA:%.*]] = call i64 @wcslen(ptr [[P]])
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[P_I:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[P_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[INC]] = add i64 [[I]], 1
; CHECK-NEXT: [[P_NEXT]] = getelementptr i32, ptr [[P_I]], i64 1
; CHECK-NEXT: [[CH:%.*]] = load i32, ptr [[P_I]], align 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CH]], 0
; CHECK-NEXT: br i1 true, label %[[EXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret i64 [[I_LCSSA]]
;
entry:
br label %loop
loop:
%i = phi i64 [ 0, %entry ], [ %inc, %loop ]
%p_i = phi ptr [ %p, %entry ], [ %p_next, %loop ]
%inc = add i64 %i, 1
%p_next = getelementptr i32, ptr %p_i, i64 1
%ch = load i32, ptr %p_i, align 1
%cmp = icmp eq i32 %ch, 0
br i1 %cmp, label %exit, label %loop
exit:
ret i64 %i
}
; The base pointer is not aligned, but the load from it has `align`.
; It's UB in the original code and with `wcslen` the same way, so generate a
; `wcslen`.
define i64 @aligned_load(ptr %p) {
; CHECK-LABEL: define i64 @aligned_load(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[P]])
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[INC]] = add i64 [[I]], 1
; CHECK-NEXT: [[P_I:%.*]] = getelementptr i32, ptr [[P]], i64 [[I]]
; CHECK-NEXT: [[CH:%.*]] = load i32, ptr [[P_I]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CH]], 0
; CHECK-NEXT: br i1 true, label %[[EXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret i64 [[WCSLEN]]
;
entry:
br label %loop
loop:
%i = phi i64 [ 0, %entry ], [ %inc, %loop ]
%inc = add i64 %i, 1
%p_i = getelementptr i32, ptr %p, i64 %i
%ch = load i32, ptr %p_i, align 4
%cmp = icmp eq i32 %ch, 0
br i1 %cmp, label %exit, label %loop
exit:
ret i64 %i
}
; The base is aligned, but the load offsets are not. No `wcslen`.
define i64 @misaligned_offset(ptr align 4 %p) {
; CHECK-LABEL: define i64 @misaligned_offset(
; CHECK-SAME: ptr align 4 [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[BASE:%.*]] = getelementptr i8, ptr [[P]], i64 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[INC]] = add i64 [[I]], 1
; CHECK-NEXT: [[P_I:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[I]]
; CHECK-NEXT: [[CH:%.*]] = load i32, ptr [[P_I]], align 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CH]], 0
; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[LOOP]] ]
; CHECK-NEXT: ret i64 [[I_LCSSA]]
;
entry:
%base = getelementptr i8, ptr %p, i64 1
br label %loop
loop:
%i = phi i64 [ 0, %entry ], [ %inc, %loop ]
%inc = add i64 %i, 1
%p_i = getelementptr i32, ptr %base, i64 %i
%ch = load i32, ptr %p_i, align 1
%cmp = icmp eq i32 %ch, 0
br i1 %cmp, label %exit, label %loop
exit:
ret i64 %i
}
; Missed optimization: the loop only runs on aligned base, but we currently
; don't make use of that information in the pass to introduce a `wcslen` call.
define i64 @aligned_by_guard(ptr %p) {
; CHECK-LABEL: define i64 @aligned_by_guard(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[ADDR:%.*]] = ptrtoint ptr [[P]] to i64
; CHECK-NEXT: [[LOW:%.*]] = and i64 [[ADDR]], 3
; CHECK-NEXT: [[IS_ALIGNED:%.*]] = icmp eq i64 [[LOW]], 0
; CHECK-NEXT: br i1 [[IS_ALIGNED]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[INC:%.*]], %[[LOOP]] ], [ 0, %[[LOOP_PREHEADER]] ]
; CHECK-NEXT: [[INC]] = add i64 [[I]], 1
; CHECK-NEXT: [[P_I:%.*]] = getelementptr i32, ptr [[P]], i64 [[I]]
; CHECK-NEXT: [[CH:%.*]] = load i32, ptr [[P_I]], align 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CH]], 0
; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[LOOP]] ]
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ -1, %[[ENTRY]] ], [ [[I_LCSSA]], %[[EXIT_LOOPEXIT]] ]
; CHECK-NEXT: ret i64 [[RES]]
;
entry:
%addr = ptrtoint ptr %p to i64
%low = and i64 %addr, 3
%is_aligned = icmp eq i64 %low, 0
br i1 %is_aligned, label %loop, label %exit
loop:
%i = phi i64 [ 0, %entry ], [ %inc, %loop ]
%inc = add i64 %i, 1
%p_i = getelementptr i32, ptr %p, i64 %i
%ch = load i32, ptr %p_i, align 1
%cmp = icmp eq i32 %ch, 0
br i1 %cmp, label %exit, label %loop
exit:
%res = phi i64 [ -1, %entry ], [ %i, %loop ]
ret i64 %res
}
; Test null pointer handling. SCEV-based alignment check returns 64 low zero
; bits for the load base here, which used to cause a compiler crash when we
; converted it to an `Align` value. (`Align` allows at most 63)
define i64 @wcslen_at_zero() null_pointer_is_valid {
; CHECK-LABEL: define i64 @wcslen_at_zero(
; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr null)
; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[WCSLEN]], 2
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr null, i64 [[TMP0]]
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[P:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[P_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P]], align 1
; CHECK-NEXT: [[P_NEXT]] = getelementptr inbounds i8, ptr [[P]], i64 4
; CHECK-NEXT: [[Z:%.*]] = icmp eq i32 [[V]], 0
; CHECK-NEXT: br i1 true, label %[[DONE:.*]], label %[[LOOP]]
; CHECK: [[DONE]]:
; CHECK-NEXT: [[D:%.*]] = ptrtoint ptr [[SCEVGEP]] to i64
; CHECK-NEXT: ret i64 [[D]]
;
entry:
br label %loop
loop:
%p = phi ptr [ null, %entry ], [ %p.next, %loop ]
%v = load i32, ptr %p, align 1
%p.next = getelementptr inbounds i8, ptr %p, i64 4
%z = icmp eq i32 %v, 0
br i1 %z, label %done, label %loop
done:
%d = ptrtoint ptr %p to i64
ret i64 %d
}
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"wchar_size", i32 4}