| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 |
| ; RUN: opt < %s -passes=nvptx-promote-param-align -S | FileCheck %s |
| |
| target datalayout = "e-i64:64-i128:128-v16:16-v32:32-n16:32:64" |
| target triple = "nvptx64-nvidia-cuda" |
| |
| %struct.S = type { i32, i32 } |
| |
| @gfp = internal global ptr null |
| |
| ; Kernels aren't promoted, but byval params still get an explicit ABI align (4). |
| define ptx_kernel void @kernel_byval(ptr byval(%struct.S) align 1 %s) { |
| ; CHECK-LABEL: define ptx_kernel void @kernel_byval( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; External functions aren't promoted, but the byval param still gets ABI align. |
| define void @extern_byval(ptr byval(%struct.S) %s) { |
| ; CHECK-LABEL: define void @extern_byval( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; Internal byval: explicit ABI align (4) plus alignstack(16) promotion. |
| define internal void @internal_byval(ptr byval(%struct.S) %s) { |
| ; CHECK-LABEL: define internal void @internal_byval( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 alignstack(16) [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; Already aligned above 16: unchanged. |
| define internal void @internal_byval_high_align(ptr byval(%struct.S) align 32 %s) { |
| ; CHECK-LABEL: define internal void @internal_byval_high_align( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 32 [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; Internal aggregate (non-byval) parameter: promoted to alignstack(16). |
| define internal void @internal_aggr(%struct.S %s) { |
| ; CHECK-LABEL: define internal void @internal_aggr( |
| ; CHECK-SAME: [[STRUCT_S:%.*]] alignstack(16) [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; Scalars gain nothing from vectorization: untouched. |
| define internal void @internal_scalar(i32 %x, ptr %p) { |
| ; CHECK-LABEL: define internal void @internal_scalar( |
| ; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; Internal aggregate return value: promoted to alignstack(16). |
| define internal %struct.S @internal_ret() { |
| ; CHECK-LABEL: define internal alignstack(16) %struct.S @internal_ret() { |
| ; CHECK-NEXT: ret [[STRUCT_S:%.*]] zeroinitializer |
| ; |
| ret %struct.S zeroinitializer |
| } |
| |
| ; An explicit stackalign takes precedence. |
| define internal void @internal_explicit_stackalign(%struct.S alignstack(8) %s) { |
| ; CHECK-LABEL: define internal void @internal_explicit_stackalign( |
| ; CHECK-SAME: [[STRUCT_S:%.*]] alignstack(8) [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| ; The promoted alignment is propagated to constant-offset byval loads. |
| define internal void @internal_load_propagation(ptr byval(%struct.S) %s) { |
| ; CHECK-LABEL: define internal void @internal_load_propagation( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 alignstack(16) [[S:%.*]]) { |
| ; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[S]], align 16 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i64 0, i32 1 |
| ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[GEP]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| %v1 = load i32, ptr %s, align 1 |
| %gep = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1 |
| %v2 = load i32, ptr %gep, align 1 |
| ret void |
| } |
| |
| ; External byval loads get the ABI align (4) propagated, avoiding byte-wise loads. |
| define void @extern_load_propagation(ptr byval(%struct.S) %s) { |
| ; CHECK-LABEL: define void @extern_load_propagation( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) { |
| ; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[S]], align 4 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i64 0, i32 1 |
| ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[GEP]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| %v1 = load i32, ptr %s, align 1 |
| %gep = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1 |
| %v2 = load i32, ptr %gep, align 1 |
| ret void |
| } |
| |
| ; Kernel byval loads get the ABI align (4) propagated. |
| define ptx_kernel void @kernel_load_propagation(ptr byval(%struct.S) align 1 %s) { |
| ; CHECK-LABEL: define ptx_kernel void @kernel_load_propagation( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) { |
| ; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[S]], align 4 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S]], i64 0, i32 1 |
| ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[GEP]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| %v1 = load i32, ptr %s, align 1 |
| %gep = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1 |
| %v2 = load i32, ptr %gep, align 1 |
| ret void |
| } |
| |
| ; The promoted alignment is mirrored onto direct call sites. |
| define internal %struct.S @callee(%struct.S %a) { |
| ; CHECK-LABEL: define internal alignstack(16) %struct.S @callee( |
| ; CHECK-SAME: [[STRUCT_S:%.*]] alignstack(16) [[A:%.*]]) { |
| ; CHECK-NEXT: ret [[STRUCT_S]] [[A]] |
| ; |
| ret %struct.S %a |
| } |
| |
| define void @caller() { |
| ; CHECK-LABEL: define void @caller() { |
| ; CHECK-NEXT: [[R:%.*]] = call alignstack(16) [[STRUCT_S:%.*]] @[[CALLEE:[a-zA-Z0-9_$\"\\.-]*[a-zA-Z_$\"\\.-][a-zA-Z0-9_$\"\\.-]*]]([[STRUCT_S]] alignstack(16) zeroinitializer) |
| ; CHECK-NEXT: ret void |
| ; |
| %r = call %struct.S @callee(%struct.S zeroinitializer) |
| ret void |
| } |
| |
| ; A byval call matching the callee's ABI is promoted and mirrored onto the call. |
| define internal void @byval_match(ptr byval(%struct.S) %s) { |
| ; CHECK-LABEL: define internal void @byval_match( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 alignstack(16) [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| define void @byval_match_caller(ptr %p) { |
| ; CHECK-LABEL: define void @byval_match_caller( |
| ; CHECK-SAME: ptr [[P:%.*]]) { |
| ; CHECK-NEXT: call void @byval_match(ptr byval([[STRUCT_S:%.*]]) alignstack(16) [[P]]) |
| ; CHECK-NEXT: ret void |
| ; |
| call void @byval_match(ptr byval(%struct.S) %p) |
| ret void |
| } |
| |
| ; A byval call whose align differs from the callee is not promoted. |
| define internal void @byval_abi_mismatch(ptr byval(%struct.S) align 8 %s) { |
| ; CHECK-LABEL: define internal void @byval_abi_mismatch( |
| ; CHECK-SAME: ptr byval([[STRUCT_S:%.*]]) align 8 [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| define void @byval_abi_mismatch_caller(ptr %p) { |
| ; CHECK-LABEL: define void @byval_abi_mismatch_caller( |
| ; CHECK-SAME: ptr [[P:%.*]]) { |
| ; CHECK-NEXT: call void @byval_abi_mismatch(ptr byval([[STRUCT_S:%.*]]) align 4 [[P]]) |
| ; CHECK-NEXT: ret void |
| ; |
| call void @byval_abi_mismatch(ptr byval(%struct.S) align 4 %p) |
| ret void |
| } |
| |
| ; Address-taken functions aren't promoted (an indirect call wouldn't see them). |
| define internal void @addr_taken(%struct.S %s) { |
| ; CHECK-LABEL: define internal void @addr_taken( |
| ; CHECK-SAME: [[STRUCT_S:%.*]] [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| define void @take_address() { |
| ; CHECK-LABEL: define void @take_address() { |
| ; CHECK-NEXT: store ptr @addr_taken, ptr @gfp, align 8 |
| ; CHECK-NEXT: ret void |
| ; |
| store ptr @addr_taken, ptr @gfp |
| ret void |
| } |
| |
| ; A type-mismatched call is lowered as indirect, so the callee isn't promoted. |
| define internal void @type_mismatch(%struct.S %s) { |
| ; CHECK-LABEL: define internal void @type_mismatch( |
| ; CHECK-SAME: [[STRUCT_S:%.*]] [[S:%.*]]) { |
| ; CHECK-NEXT: ret void |
| ; |
| ret void |
| } |
| |
| define void @mismatch_caller() { |
| ; CHECK-LABEL: define void @mismatch_caller() { |
| ; CHECK-NEXT: call void @type_mismatch(i64 0) |
| ; CHECK-NEXT: ret void |
| ; |
| call void @type_mismatch(i64 0) |
| ret void |
| } |