diff --git a/llvm/test/Transforms/InstCombine/to-ptrmask.ll b/llvm/test/Transforms/InstCombine/to-ptrmask.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/to-ptrmask.ll @@ -0,0 +1,289 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare ptr @llvm.ptrmask(ptr, i64) +declare void @use.i64(i64) + +define ptr @GEP_default_pattern_to_ptrmask(ptr %p, i64 %m) { +; CHECK-LABEL: define ptr @GEP_default_pattern_to_ptrmask +; CHECK-SAME: (ptr [[P:%.*]], i64 [[M:%.*]]) { +; CHECK-NEXT: [[PI:%.*]] = ptrtoint ptr [[P]] to i64 +; CHECK-NEXT: [[PI_M:%.*]] = and i64 [[PI]], [[M]] +; CHECK-NEXT: [[PI_OFF:%.*]] = sub i64 [[PI_M]], [[PI]] +; CHECK-NEXT: [[PM:%.*]] = getelementptr i8, ptr [[P]], i64 [[PI_OFF]] +; CHECK-NEXT: ret ptr [[PM]] +; + %pi = ptrtoint ptr %p to i64 + %pi_m = and i64 %pi, %m + %pi_off = sub i64 %pi_m, %pi + %pm = getelementptr i8, ptr %p, i64 %pi_off + ret ptr %pm +} + +define <2 x ptr> @GEP_default_pattern_fail_vec(<2 x ptr> %p, <2 x i64> %m) { +; CHECK-LABEL: define <2 x ptr> @GEP_default_pattern_fail_vec +; CHECK-SAME: (<2 x ptr> [[P:%.*]], <2 x i64> [[M:%.*]]) { +; CHECK-NEXT: [[PI:%.*]] = ptrtoint <2 x ptr> [[P]] to <2 x i64> +; CHECK-NEXT: [[PI_M:%.*]] = and <2 x i64> [[PI]], [[M]] +; CHECK-NEXT: [[PI_OFF:%.*]] = sub <2 x i64> [[PI_M]], [[PI]] +; CHECK-NEXT: [[PM:%.*]] = getelementptr i8, <2 x ptr> [[P]], <2 x i64> [[PI_OFF]] +; CHECK-NEXT: ret <2 x ptr> [[PM]] +; + %pi = ptrtoint <2 x ptr> %p to <2 x i64> + %pi_m = and <2 x i64> %pi, %m + %pi_off = sub <2 x i64> %pi_m, %pi + %pm = getelementptr i8, <2 x ptr> %p, <2 x i64> %pi_off + ret <2 x ptr> %pm +} + +define ptr @GEP_default_pattern_with_multiuse_fail(ptr %p, i64 %m) { +; CHECK-LABEL: define ptr @GEP_default_pattern_with_multiuse_fail +; CHECK-SAME: (ptr [[P:%.*]], i64 [[M:%.*]]) { +; CHECK-NEXT: [[PI:%.*]] = ptrtoint ptr [[P]] to i64 +; CHECK-NEXT: [[PI_M:%.*]] = and i64 [[PI]], [[M]] +; CHECK-NEXT: [[PI_OFF:%.*]] = sub i64 [[PI_M]], [[PI]] +; CHECK-NEXT: call void @use.i64(i64 [[PI_OFF]]) +; CHECK-NEXT: [[PM:%.*]] = getelementptr i8, ptr [[P]], i64 [[PI_OFF]] +; CHECK-NEXT: ret ptr [[PM]] +; + %pi = ptrtoint ptr %p to i64 + %pi_m = and i64 %pi, %m + %pi_off = sub i64 %pi_m, %pi + call void @use.i64(i64 %pi_off) + %pm = getelementptr i8, ptr %p, i64 %pi_off + ret ptr %pm +} + +define ptr @GEP_default_pattern_fail_i32(ptr %p, i32 %m) { +; CHECK-LABEL: define ptr @GEP_default_pattern_fail_i32 +; CHECK-SAME: (ptr [[P:%.*]], i32 [[M:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64 +; CHECK-NEXT: [[PI:%.*]] = trunc i64 [[TMP1]] to i32 +; CHECK-NEXT: [[PI_M:%.*]] = and i32 [[PI]], [[M]] +; CHECK-NEXT: [[PI_OFF:%.*]] = sub i32 [[PI_M]], [[PI]] +; CHECK-NEXT: [[TMP2:%.*]] = sext i32 [[PI_OFF]] to i64 +; CHECK-NEXT: [[PM:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP2]] +; CHECK-NEXT: ret ptr [[PM]] +; + %pi = ptrtoint ptr %p to i32 + %pi_m = and i32 %pi, %m + %pi_off = sub i32 %pi_m, %pi + %pm = getelementptr i8, ptr %p, i32 %pi_off + ret ptr %pm +} + +define ptr @GEP_default_pattern_fail_not_i8(ptr %p, i64 %m) { +; CHECK-LABEL: define ptr @GEP_default_pattern_fail_not_i8 +; CHECK-SAME: (ptr [[P:%.*]], i64 [[M:%.*]]) { +; CHECK-NEXT: [[PI:%.*]] = ptrtoint ptr [[P]] to i64 +; CHECK-NEXT: [[PI_M:%.*]] = and i64 [[PI]], [[M]] +; CHECK-NEXT: [[PI_OFF:%.*]] = sub i64 [[PI_M]], [[PI]] +; CHECK-NEXT: [[PM:%.*]] = getelementptr i16, ptr [[P]], i64 [[PI_OFF]] +; CHECK-NEXT: ret ptr [[PM]] +; + %pi = ptrtoint ptr %p to i64 + %pi_m = and i64 %pi, %m + %pi_off = sub i64 %pi_m, %pi + %pm = getelementptr i16, ptr %p, i64 %pi_off + ret ptr %pm +} + +define ptr @builtin_align_up_to_ptrmask(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_up_to_ptrmask +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = add i64 [[INTPTR]], 7 +; CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[OVER_BOUNDARY]], -8 +; CHECK-NEXT: [[DIFF:%.*]] = sub i64 [[ALIGNED_INTPTR]], [[INTPTR]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %over_boundary = add i64 %intptr, 7 + %aligned_intptr = and i64 %over_boundary, -8 + %diff = sub i64 %aligned_intptr, %intptr + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_up_to_ptrmask_fail_invalid_constants1(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_up_to_ptrmask_fail_invalid_constants1 +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = add i64 [[INTPTR]], 7 +; CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[OVER_BOUNDARY]], -16 +; CHECK-NEXT: [[DIFF:%.*]] = sub i64 [[ALIGNED_INTPTR]], [[INTPTR]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %over_boundary = add i64 %intptr, 7 + %aligned_intptr = and i64 %over_boundary, -16 + %diff = sub i64 %aligned_intptr, %intptr + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_up_to_ptrmask_fail_invalid_constants2(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_up_to_ptrmask_fail_invalid_constants2 +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = add i64 [[INTPTR]], 8 +; CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[OVER_BOUNDARY]], -9 +; CHECK-NEXT: [[DIFF:%.*]] = sub i64 [[ALIGNED_INTPTR]], [[INTPTR]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %over_boundary = add i64 %intptr, 8 + %aligned_intptr = and i64 %over_boundary, -9 + %diff = sub i64 %aligned_intptr, %intptr + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_up_to_ptrmask_fail_multiuse(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_up_to_ptrmask_fail_multiuse +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = add i64 [[INTPTR]], 7 +; CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[OVER_BOUNDARY]], -8 +; CHECK-NEXT: call void @use.i64(i64 [[ALIGNED_INTPTR]]) +; CHECK-NEXT: [[DIFF:%.*]] = sub i64 [[ALIGNED_INTPTR]], [[INTPTR]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %over_boundary = add i64 %intptr, 7 + %aligned_intptr = and i64 %over_boundary, -8 + call void @use.i64(i64 %aligned_intptr) + %diff = sub i64 %aligned_intptr, %intptr + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_down_to_ptrmask(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_down_to_ptrmask +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[AND:%.*]] = and i64 [[INTPTR]], 7 +; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i64 0, [[AND]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %and = and i64 %intptr, 7 + %diff = sub nsw i64 0, %and + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_down_to_ptrmask_fail_non_mask(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_down_to_ptrmask_fail_non_mask +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[AND:%.*]] = and i64 [[INTPTR]], 8 +; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i64 0, [[AND]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %and = and i64 %intptr, 8 + %diff = sub nsw i64 0, %and + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_down_to_ptrmask_fail_not_i8(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_down_to_ptrmask_fail_not_i8 +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[AND:%.*]] = and i64 [[INTPTR]], 7 +; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i64 0, [[AND]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds <2 x i8>, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %and = and i64 %intptr, 7 + %diff = sub nsw i64 0, %and + %aligned_result = getelementptr inbounds <2 x i8>, ptr %x, i64 %diff + ret ptr %aligned_result +} + +define ptr @builtin_align_down_to_ptrmask_fail_i32(ptr %x) { +; CHECK-LABEL: define ptr @builtin_align_down_to_ptrmask_fail_i32 +; CHECK-SAME: (ptr [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[INTPTR:%.*]] = trunc i64 [[TMP0]] to i32 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[INTPTR]], 7 +; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i32 0, [[AND]] +; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[DIFF]] to i64 +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[TMP1]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i32 + %and = and i32 %intptr, 7 + %diff = sub nsw i32 0, %and + %aligned_result = getelementptr inbounds i8, ptr %x, i32 %diff + ret ptr %aligned_result +} +; We could match this pattern without ever including extra instructions +define ptr @builtin_align_down_up_ptrmask_non_const_TODO(ptr %x, i64 %alignment) { +; CHECK-LABEL: define ptr @builtin_align_down_up_ptrmask_non_const_TODO +; CHECK-SAME: (ptr [[X:%.*]], i64 [[ALIGNMENT:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[MASK:%.*]] = add i64 [[ALIGNMENT]], -1 +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = add i64 [[MASK]], [[INTPTR]] +; CHECK-NEXT: [[INVERTED_MASK:%.*]] = sub i64 0, [[ALIGNMENT]] +; CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[OVER_BOUNDARY]], [[INVERTED_MASK]] +; CHECK-NEXT: [[DIFF:%.*]] = sub i64 [[ALIGNED_INTPTR]], [[INTPTR]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %mask = add i64 %alignment, -1 + %intptr = ptrtoint ptr %x to i64 + %over_boundary = add i64 %mask, %intptr + %inverted_mask = sub i64 0, %alignment + %aligned_intptr = and i64 %over_boundary, %inverted_mask + %diff = sub i64 %aligned_intptr, %intptr + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +} +; We would need to create an extra NOT instruction here +define ptr @builtin_align_down_to_ptrmask_non_const_fail(ptr %x, i64 %alignment) { +; CHECK-LABEL: define ptr @builtin_align_down_to_ptrmask_non_const_fail +; CHECK-SAME: (ptr [[X:%.*]], i64 [[ALIGNMENT:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[X]] to i64 +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[ALIGNMENT]], -1 +; CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[INTPTR]] +; CHECK-NEXT: [[DIFF:%.*]] = sub i64 0, [[AND]] +; CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[DIFF]] +; CHECK-NEXT: ret ptr [[ALIGNED_RESULT]] +; +entry: + %intptr = ptrtoint ptr %x to i64 + %add = add i64 %alignment, -1 + %and = and i64 %add, %intptr + %diff = sub i64 0, %and + %aligned_result = getelementptr inbounds i8, ptr %x, i64 %diff + ret ptr %aligned_result +}