| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py | |
| ; RUN: opt -mtriple=aarch64-unknown-linux-gnu -passes=pre-isel-intrinsic-lowering -S < %s | FileCheck --check-prefixes=CHECK,LP64 %s | |
| ; RUN: opt -mtriple=arm64_32-apple-watchos -passes=pre-isel-intrinsic-lowering -S < %s | FileCheck --check-prefixes=CHECK,ILP32 %s | |
| ; Test that @llvm.can.load.speculatively lowers to an alignment check for | |
| ; power-of-2 sizes up to the 16-byte MTE tag granule, and to false otherwise. | |
| define i1 @size_1(ptr %ptr) { | |
| ; LP64-LABEL: @size_1( | |
| ; LP64-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 0 | |
| ; LP64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0 | |
| ; LP64-NEXT: ret i1 [[TMP3]] | |
| ; | |
| ; ILP32-LABEL: @size_1( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 0 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 | |
| ; ILP32-NEXT: ret i1 [[TMP3]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 1) | |
| ret i1 %can_load | |
| } | |
| define i1 @size_2(ptr %ptr) { | |
| ; LP64-LABEL: @size_2( | |
| ; LP64-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 1 | |
| ; LP64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0 | |
| ; LP64-NEXT: ret i1 [[TMP3]] | |
| ; | |
| ; ILP32-LABEL: @size_2( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 | |
| ; ILP32-NEXT: ret i1 [[TMP3]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 2) | |
| ret i1 %can_load | |
| } | |
| define i1 @size_4(ptr %ptr) { | |
| ; LP64-LABEL: @size_4( | |
| ; LP64-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 3 | |
| ; LP64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0 | |
| ; LP64-NEXT: ret i1 [[TMP3]] | |
| ; | |
| ; ILP32-LABEL: @size_4( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 3 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 | |
| ; ILP32-NEXT: ret i1 [[TMP3]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 4) | |
| ret i1 %can_load | |
| } | |
| define i1 @size_8(ptr %ptr) { | |
| ; LP64-LABEL: @size_8( | |
| ; LP64-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 7 | |
| ; LP64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0 | |
| ; LP64-NEXT: ret i1 [[TMP3]] | |
| ; | |
| ; ILP32-LABEL: @size_8( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 7 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 | |
| ; ILP32-NEXT: ret i1 [[TMP3]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 8) | |
| ret i1 %can_load | |
| } | |
| define i1 @size_16(ptr %ptr) { | |
| ; LP64-LABEL: @size_16( | |
| ; LP64-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 15 | |
| ; LP64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0 | |
| ; LP64-NEXT: ret i1 [[TMP3]] | |
| ; | |
| ; ILP32-LABEL: @size_16( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 15 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 | |
| ; ILP32-NEXT: ret i1 [[TMP3]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 16) | |
| ret i1 %can_load | |
| } | |
| ; The alignment of the pointer operand does not change the emitted check. | |
| define i1 @size_16_ptr_align_16(ptr align 16 %ptr) { | |
| ; LP64-LABEL: @size_16_ptr_align_16( | |
| ; LP64-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 15 | |
| ; LP64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0 | |
| ; LP64-NEXT: ret i1 [[TMP3]] | |
| ; | |
| ; ILP32-LABEL: @size_16_ptr_align_16( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 15 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 | |
| ; ILP32-NEXT: ret i1 [[TMP3]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr align 16 %ptr, i64 16) | |
| ret i1 %can_load | |
| } | |
| ; Size > 16 - returns false (may cross MTE tag granule boundary) | |
| define i1 @size_32(ptr %ptr) { | |
| ; CHECK-LABEL: @size_32( | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 32) | |
| ret i1 %can_load | |
| } | |
| ; Non-power-of-2 size: the alignment check is only valid for powers of 2. | |
| define i1 @size_non_pow2(ptr %ptr) { | |
| ; CHECK-LABEL: @size_non_pow2( | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 12) | |
| ret i1 %can_load | |
| } | |
| ; Non-power-of-2 size larger than the tag granule. | |
| define i1 @size_non_pow2_large(ptr %ptr) { | |
| ; CHECK-LABEL: @size_non_pow2_large( | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 100) | |
| ret i1 %can_load | |
| } | |
| ; Zero size is not a positive power of 2 either. | |
| define i1 @size_zero(ptr %ptr) { | |
| ; CHECK-LABEL: @size_zero( | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 0) | |
| ret i1 %can_load | |
| } | |
| ; Test with address space | |
| define i1 @addrspace1(ptr addrspace(1) %ptr) { | |
| ; CHECK-LABEL: @addrspace1( | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p1(ptr addrspace(1) %ptr, i64 16) | |
| ret i1 %can_load | |
| } | |
| ; The granule comparison must happen before the size is narrowed to the address | |
| ; type, so a size exceeding the address width is not truncated into range. | |
| define i1 @runtime_size(ptr %ptr, i64 %size) { | |
| ; LP64-LABEL: @runtime_size( | |
| ; LP64-NEXT: [[TMP1:%.*]] = icmp ule i64 [[SIZE:%.*]], 16 | |
| ; LP64-NEXT: [[TMP2:%.*]] = sub i64 [[SIZE]], 1 | |
| ; LP64-NEXT: [[TMP3:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i64 | |
| ; LP64-NEXT: [[TMP4:%.*]] = and i64 [[TMP3]], [[TMP2]] | |
| ; LP64-NEXT: [[TMP5:%.*]] = icmp eq i64 [[TMP4]], 0 | |
| ; LP64-NEXT: [[TMP6:%.*]] = and i1 [[TMP1]], [[TMP5]] | |
| ; LP64-NEXT: ret i1 [[TMP6]] | |
| ; | |
| ; ILP32-LABEL: @runtime_size( | |
| ; ILP32-NEXT: [[TMP1:%.*]] = icmp ule i64 [[SIZE:%.*]], 16 | |
| ; ILP32-NEXT: [[TMP2:%.*]] = trunc i64 [[SIZE]] to i32 | |
| ; ILP32-NEXT: [[TMP3:%.*]] = sub i32 [[TMP2]], 1 | |
| ; ILP32-NEXT: [[TMP4:%.*]] = ptrtoaddr ptr [[PTR:%.*]] to i32 | |
| ; ILP32-NEXT: [[TMP5:%.*]] = and i32 [[TMP4]], [[TMP3]] | |
| ; ILP32-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP5]], 0 | |
| ; ILP32-NEXT: [[TMP7:%.*]] = and i1 [[TMP1]], [[TMP6]] | |
| ; ILP32-NEXT: ret i1 [[TMP7]] | |
| ; | |
| %can_load = call i1 @llvm.can.load.speculatively.p0(ptr %ptr, i64 %size) | |
| ret i1 %can_load | |
| } | |
| declare i1 @llvm.can.load.speculatively.p0(ptr, i64) | |
| declare i1 @llvm.can.load.speculatively.p1(ptr addrspace(1), i64) |