| ; 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} |