| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -S -mtriple=amdgpu7.00-amd-amdhsa -passes=sroa,amdgpu-promote-alloca < %s | FileCheck %s | |
| ; An object written one element at a time but read back in wider pieces still | |
| ; promotes: the wide access is split across elements the same way a vector | |
| ; access is. | |
| define amdgpu_kernel void @store_i32_load_i64(ptr addrspace(1) %out, i32 %i, i32 %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @store_i32_load_i64( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], i32 [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = freeze <8 x i32> poison | |
| ; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i32> [[A]], i32 [[V]], i32 [[I]] | |
| ; CHECK-NEXT: [[J:%.*]] = shl i32 [[I]], 1 | |
| ; CHECK-NEXT: [[TMP2:%.*]] = bitcast <8 x i32> [[TMP1]] to <4 x i64> | |
| ; CHECK-NEXT: [[TMP3:%.*]] = lshr i32 [[J]], 1 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = extractelement <4 x i64> [[TMP2]], i32 [[TMP3]] | |
| ; CHECK-NEXT: store i64 [[TMP4]], ptr addrspace(1) [[OUT]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x i32], align 16, addrspace(5) | |
| %p = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %i | |
| store i32 %v, ptr addrspace(5) %p, align 4 | |
| %j = shl i32 %i, 1 | |
| %q = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %j | |
| %l = load i64, ptr addrspace(5) %q, align 8 | |
| store i64 %l, ptr addrspace(1) %out, align 8 | |
| ret void | |
| } | |
| ; The same in the other direction: a wide store and narrow loads. | |
| define amdgpu_kernel void @store_i64_load_i32(ptr addrspace(1) %out, i32 %i, i64 %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @store_i64_load_i32( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], i64 [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = freeze <8 x i32> poison | |
| ; CHECK-NEXT: [[J:%.*]] = shl i32 [[I]], 1 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = bitcast i64 [[V]] to <2 x i32> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = extractelement <2 x i32> [[TMP1]], i64 0 | |
| ; CHECK-NEXT: [[TMP3:%.*]] = insertelement <8 x i32> [[A]], i32 [[TMP2]], i32 [[J]] | |
| ; CHECK-NEXT: [[TMP4:%.*]] = add i32 [[J]], 1 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1 | |
| ; CHECK-NEXT: [[TMP6:%.*]] = insertelement <8 x i32> [[TMP3]], i32 [[TMP5]], i32 [[TMP4]] | |
| ; CHECK-NEXT: [[TMP7:%.*]] = extractelement <8 x i32> [[TMP6]], i32 [[I]] | |
| ; CHECK-NEXT: store i32 [[TMP7]], ptr addrspace(1) [[OUT]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x i32], align 16, addrspace(5) | |
| %j = shl i32 %i, 1 | |
| %p = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %j | |
| store i64 %v, ptr addrspace(5) %p, align 8 | |
| %q = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %i | |
| %l = load i32, ptr addrspace(5) %q, align 4 | |
| store i32 %l, ptr addrspace(1) %out, align 4 | |
| ret void | |
| } | |
| ; A span that is not a power of two: i96 covers three elements | |
| define amdgpu_kernel void @store_i96_load_i32(ptr addrspace(1) %out, i32 %i, i96 %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @store_i96_load_i32( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], i96 [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = freeze <8 x i32> poison | |
| ; CHECK-NEXT: [[J:%.*]] = mul i32 [[I]], 3 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = bitcast i96 [[V]] to <3 x i32> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = extractelement <3 x i32> [[TMP1]], i64 0 | |
| ; CHECK-NEXT: [[TMP3:%.*]] = insertelement <8 x i32> [[A]], i32 [[TMP2]], i32 [[J]] | |
| ; CHECK-NEXT: [[TMP4:%.*]] = add i32 [[J]], 1 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = extractelement <3 x i32> [[TMP1]], i64 1 | |
| ; CHECK-NEXT: [[TMP6:%.*]] = insertelement <8 x i32> [[TMP3]], i32 [[TMP5]], i32 [[TMP4]] | |
| ; CHECK-NEXT: [[TMP7:%.*]] = add i32 [[J]], 2 | |
| ; CHECK-NEXT: [[TMP8:%.*]] = extractelement <3 x i32> [[TMP1]], i64 2 | |
| ; CHECK-NEXT: [[TMP9:%.*]] = insertelement <8 x i32> [[TMP6]], i32 [[TMP8]], i32 [[TMP7]] | |
| ; CHECK-NEXT: [[TMP10:%.*]] = extractelement <8 x i32> [[TMP9]], i32 [[I]] | |
| ; CHECK-NEXT: store i32 [[TMP10]], ptr addrspace(1) [[OUT]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x i32], align 16, addrspace(5) | |
| %j = mul i32 %i, 3 | |
| %p = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %j | |
| store i96 %v, ptr addrspace(5) %p, align 4 | |
| %q = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %i | |
| %l = load i32, ptr addrspace(5) %q, align 4 | |
| store i32 %l, ptr addrspace(1) %out, align 4 | |
| ret void | |
| } | |
| ; <3 x i32> has the same 12-byte footprint as i96 and already went down the | |
| ; subvector path before this change: the two must expand identically. | |
| define amdgpu_kernel void @store_v3i32_load_i32(ptr addrspace(1) %out, i32 %i, <3 x i32> %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @store_v3i32_load_i32( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], <3 x i32> [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = freeze <8 x i32> poison | |
| ; CHECK-NEXT: [[J:%.*]] = mul i32 [[I]], 3 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = extractelement <3 x i32> [[V]], i64 0 | |
| ; CHECK-NEXT: [[TMP2:%.*]] = insertelement <8 x i32> [[A]], i32 [[TMP1]], i32 [[J]] | |
| ; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[J]], 1 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = extractelement <3 x i32> [[V]], i64 1 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = insertelement <8 x i32> [[TMP2]], i32 [[TMP4]], i32 [[TMP3]] | |
| ; CHECK-NEXT: [[TMP6:%.*]] = add i32 [[J]], 2 | |
| ; CHECK-NEXT: [[TMP7:%.*]] = extractelement <3 x i32> [[V]], i64 2 | |
| ; CHECK-NEXT: [[TMP8:%.*]] = insertelement <8 x i32> [[TMP5]], i32 [[TMP7]], i32 [[TMP6]] | |
| ; CHECK-NEXT: [[TMP9:%.*]] = extractelement <8 x i32> [[TMP8]], i32 [[I]] | |
| ; CHECK-NEXT: store i32 [[TMP9]], ptr addrspace(1) [[OUT]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x i32], align 16, addrspace(5) | |
| %j = mul i32 %i, 3 | |
| %p = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %j | |
| store <3 x i32> %v, ptr addrspace(5) %p, align 4 | |
| %q = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %i | |
| %l = load i32, ptr addrspace(5) %q, align 4 | |
| store i32 %l, ptr addrspace(1) %out, align 4 | |
| ret void | |
| } | |
| ; Negative: i48 spans six bytes, which is not a whole number of i32 elements. | |
| define amdgpu_kernel void @unaligned_span_not_promoted(ptr addrspace(1) %out, i32 %i, i32 %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @unaligned_span_not_promoted( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], i32 [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = alloca [8 x i32], align 16, addrspace(5) | |
| ; CHECK-NEXT: [[P:%.*]] = getelementptr [8 x i32], ptr addrspace(5) [[A]], i32 0, i32 [[I]] | |
| ; CHECK-NEXT: store i32 [[V]], ptr addrspace(5) [[P]], align 4 | |
| ; CHECK-NEXT: [[J:%.*]] = shl i32 [[I]], 1 | |
| ; CHECK-NEXT: [[Q:%.*]] = getelementptr [8 x i32], ptr addrspace(5) [[A]], i32 0, i32 [[J]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i48, ptr addrspace(5) [[Q]], align 8 | |
| ; CHECK-NEXT: store i48 [[L]], ptr addrspace(1) [[OUT]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x i32], align 16, addrspace(5) | |
| %p = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %i | |
| store i32 %v, ptr addrspace(5) %p, align 4 | |
| %j = shl i32 %i, 1 | |
| %q = getelementptr [8 x i32], ptr addrspace(5) %a, i32 0, i32 %j | |
| %l = load i48, ptr addrspace(5) %q, align 8 | |
| store i48 %l, ptr addrspace(1) %out, align 8 | |
| ret void | |
| } | |
| ; Negative: i33 is stored in five bytes but only 33 of those bits are value | |
| ; bits, so there is no bitcast between it and a run of five elements. | |
| define amdgpu_kernel void @padded_access_not_promoted(ptr addrspace(1) %out, i32 %i, i8 %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @padded_access_not_promoted( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], i8 [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = alloca [16 x i8], align 16, addrspace(5) | |
| ; CHECK-NEXT: [[P:%.*]] = getelementptr [16 x i8], ptr addrspace(5) [[A]], i32 0, i32 [[I]] | |
| ; CHECK-NEXT: store i8 [[V]], ptr addrspace(5) [[P]], align 1 | |
| ; CHECK-NEXT: [[J:%.*]] = mul i32 [[I]], 5 | |
| ; CHECK-NEXT: [[Q:%.*]] = getelementptr [16 x i8], ptr addrspace(5) [[A]], i32 0, i32 [[J]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i33, ptr addrspace(5) [[Q]], align 1 | |
| ; CHECK-NEXT: store i33 [[L]], ptr addrspace(1) [[OUT]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [16 x i8], align 16, addrspace(5) | |
| %p = getelementptr [16 x i8], ptr addrspace(5) %a, i32 0, i32 %i | |
| store i8 %v, ptr addrspace(5) %p, align 1 | |
| %j = mul i32 %i, 5 | |
| %q = getelementptr [16 x i8], ptr addrspace(5) %a, i32 0, i32 %j | |
| %l = load i33, ptr addrspace(5) %q, align 1 | |
| store i33 %l, ptr addrspace(1) %out, align 8 | |
| ret void | |
| } | |
| ; The elements do not have to be integers: an i64 spans two 32-bit pointers, | |
| ; and the pieces are recovered with a ptrtoint rather than a plain bitcast. | |
| define amdgpu_kernel void @store_ptr_load_i64(ptr addrspace(1) %out, i32 %i, ptr addrspace(5) %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @store_ptr_load_i64( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], ptr addrspace(5) [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = freeze <8 x ptr addrspace(5)> poison | |
| ; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x ptr addrspace(5)> [[A]], ptr addrspace(5) [[V]], i32 [[I]] | |
| ; CHECK-NEXT: [[J:%.*]] = shl i32 [[I]], 1 | |
| ; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint <8 x ptr addrspace(5)> [[TMP1]] to <8 x i32> | |
| ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i32> [[TMP2]] to <4 x i64> | |
| ; CHECK-NEXT: [[TMP4:%.*]] = lshr i32 [[J]], 1 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = extractelement <4 x i64> [[TMP3]], i32 [[TMP4]] | |
| ; CHECK-NEXT: store i64 [[TMP5]], ptr addrspace(1) [[OUT]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x ptr addrspace(5)], align 16, addrspace(5) | |
| %p = getelementptr [8 x ptr addrspace(5)], ptr addrspace(5) %a, i32 0, i32 %i | |
| store ptr addrspace(5) %v, ptr addrspace(5) %p, align 4 | |
| %j = shl i32 %i, 1 | |
| %q = getelementptr [8 x ptr addrspace(5)], ptr addrspace(5) %a, i32 0, i32 %j | |
| %l = load i64, ptr addrspace(5) %q, align 8 | |
| store i64 %l, ptr addrspace(1) %out, align 8 | |
| ret void | |
| } | |
| ; Negative: a double is the same size as a 64-bit pointer, so it covers exactly | |
| ; one element and is not split. There is no cast between the two types, so the | |
| ; access is still rejected. | |
| define amdgpu_kernel void @same_size_element_not_castable(ptr addrspace(1) %out, i32 %i, ptr %v) { | |
| ; CHECK-LABEL: define amdgpu_kernel void @same_size_element_not_castable( | |
| ; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]], i32 [[I:%.*]], ptr [[V:%.*]]) { | |
| ; CHECK-NEXT: [[A:%.*]] = alloca [8 x ptr], align 16, addrspace(5) | |
| ; CHECK-NEXT: [[P:%.*]] = getelementptr [8 x ptr], ptr addrspace(5) [[A]], i32 0, i32 [[I]] | |
| ; CHECK-NEXT: store ptr [[V]], ptr addrspace(5) [[P]], align 8 | |
| ; CHECK-NEXT: [[J:%.*]] = add i32 [[I]], 1 | |
| ; CHECK-NEXT: [[Q:%.*]] = getelementptr [8 x ptr], ptr addrspace(5) [[A]], i32 0, i32 [[J]] | |
| ; CHECK-NEXT: [[L:%.*]] = load double, ptr addrspace(5) [[Q]], align 8 | |
| ; CHECK-NEXT: store double [[L]], ptr addrspace(1) [[OUT]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %a = alloca [8 x ptr], align 16, addrspace(5) | |
| %p = getelementptr [8 x ptr], ptr addrspace(5) %a, i32 0, i32 %i | |
| store ptr %v, ptr addrspace(5) %p, align 8 | |
| %j = add i32 %i, 1 | |
| %q = getelementptr [8 x ptr], ptr addrspace(5) %a, i32 0, i32 %j | |
| %l = load double, ptr addrspace(5) %q, align 8 | |
| store double %l, ptr addrspace(1) %out, align 8 | |
| ret void | |
| } |