; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt -passes=gvn -passes=instnamer -S %s | FileCheck %s ; This test ensures that given dx.resource.getpointer reads inaccessible memory, ; the GVN pass is prevented from sinking these intrinsics out of branches which ; would create phi nodes on the returned ptr. ; ; NOTE: The following ir represents case F and G from: ; https://godbolt.org/z/cK4xh1P49. %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", i32, 1, 0, 1) } %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } %__cblayout_c = type <{ i32 }> @In = internal global %"class.hlsl::RWBuffer" poison, align 4 @.str = private unnamed_addr constant [3 x i8] c"In\00", align 1 @Out0 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 @.str.2 = private unnamed_addr constant [5 x i8] c"Out0\00", align 1 @Out1 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 @.str.4 = private unnamed_addr constant [5 x i8] c"Out1\00", align 1 @c.cb = local_unnamed_addr global target("dx.CBuffer", %__cblayout_c) poison @cond = external hidden local_unnamed_addr addrspace(2) global i32, align 4 @c.str = private unnamed_addr constant [2 x i8] c"c\00", align 1 define void @main() local_unnamed_addr { ; CHECK-LABEL: define void @main() local_unnamed_addr { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[TMP0:%.*]] = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str) ; CHECK-NEXT: store target("dx.TypedBuffer", i32, 1, 0, 1) [[TMP0]], ptr @In, align 4 ; CHECK-NEXT: [[TMP1:%.*]] = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 1, i32 1, i32 0, ptr nonnull @.str.2) ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP1]], ptr @Out0, align 4 ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP1]], ptr getelementptr inbounds nuw (i8, ptr @Out0, i32 4), align 4 ; CHECK-NEXT: [[TMP2:%.*]] = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 2, i32 1, i32 0, ptr nonnull @.str.4) ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP2]], ptr @Out1, align 4 ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP2]], ptr getelementptr inbounds nuw (i8, ptr @Out1, i32 4), align 4 ; CHECK-NEXT: [[C_CB_H_I_I:%.*]] = tail call target("dx.CBuffer", [[__CBLAYOUT_C:%.*]]) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_cst(i32 4, i32 0, i32 1, i32 0, ptr nonnull @c.str) ; CHECK-NEXT: store target("dx.CBuffer", [[__CBLAYOUT_C]]) [[C_CB_H_I_I]], ptr @c.cb, align 4 ; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.dx.flattened.thread.id.in.group() ; CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr addrspace(2) @cond, align 4 ; CHECK-NEXT: [[LOADEDV_I:%.*]] = trunc nuw i32 [[TMP4]] to i1 ; CHECK-NEXT: br i1 [[LOADEDV_I]], label %[[IF_THEN_I:.*]], label %[[IF_ELSE_I:.*]] ; CHECK: [[IF_THEN_I]]: ; CHECK-NEXT: [[TMP5:%.*]] = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) [[TMP0]], i32 [[TMP3]]) ; CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP5]], align 4 ; CHECK-NEXT: [[HLSL_WAVE_ACTIVE_SUM_I:%.*]] = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 [[TMP6]]) ; CHECK-NEXT: [[TMP7:%.*]] = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) [[TMP1]], i32 [[TMP3]]) ; CHECK-NEXT: store i32 [[HLSL_WAVE_ACTIVE_SUM_I]], ptr [[TMP7]], align 4 ; CHECK-NEXT: br label %[[MAIN_EXIT:.*]] ; CHECK: [[IF_ELSE_I]]: ; CHECK-NEXT: [[TMP8:%.*]] = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) [[TMP0]], i32 [[TMP3]]) ; CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[TMP8]], align 4 ; CHECK-NEXT: [[TMP10:%.*]] = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) [[TMP1]], i32 0) ; CHECK-NEXT: store i32 [[TMP9]], ptr [[TMP10]], align 4 ; CHECK-NEXT: br label %[[MAIN_EXIT]] ; CHECK: [[MAIN_EXIT]]: ; CHECK-NEXT: [[TMP11:%.*]] = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) [[TMP0]], i32 [[TMP3]]) ; CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP11]], align 4 ; CHECK-NEXT: [[HLSL_WAVE_ACTIVE_SUM5_I:%.*]] = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 [[TMP12]]) ; CHECK-NEXT: [[TMP13:%.*]] = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) [[TMP1]], i32 [[TMP3]]) ; CHECK-NEXT: store i32 [[HLSL_WAVE_ACTIVE_SUM5_I]], ptr [[TMP13]], align 4 ; CHECK-NEXT: ret void ; entry: %0 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str) store target("dx.TypedBuffer", i32, 1, 0, 1) %0, ptr @In, align 4 %1 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 1, i32 1, i32 0, ptr nonnull @.str.2) store target("dx.RawBuffer", i32, 1, 0) %1, ptr @Out0, align 4 store target("dx.RawBuffer", i32, 1, 0) %1, ptr getelementptr inbounds nuw (i8, ptr @Out0, i32 4), align 4 %2 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 2, i32 1, i32 0, ptr nonnull @.str.4) store target("dx.RawBuffer", i32, 1, 0) %2, ptr @Out1, align 4 store target("dx.RawBuffer", i32, 1, 0) %2, ptr getelementptr inbounds nuw (i8, ptr @Out1, i32 4), align 4 %c.cb_h.i.i = tail call target("dx.CBuffer", %__cblayout_c) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_cst(i32 4, i32 0, i32 1, i32 0, ptr nonnull @c.str) store target("dx.CBuffer", %__cblayout_c) %c.cb_h.i.i, ptr @c.cb, align 4 %3 = tail call i32 @llvm.dx.flattened.thread.id.in.group() %4 = load i32, ptr addrspace(2) @cond, align 4 %loadedv.i = trunc nuw i32 %4 to i1 br i1 %loadedv.i, label %if.then.i, label %if.else.i if.then.i: ; preds = %entry %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %0, i32 %3) %6 = load i32, ptr %5, align 4 %hlsl.wave.active.sum.i = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 %6) %7 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %1, i32 %3) store i32 %hlsl.wave.active.sum.i, ptr %7, align 4 br label %main.exit if.else.i: ; preds = %entry %8 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %0, i32 %3) %9 = load i32, ptr %8, align 4 %10 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %1, i32 0) store i32 %9, ptr %10, align 4 br label %main.exit main.exit: ; preds = %if.then.i, %if.else.i %11 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %0, i32 %3) %12 = load i32, ptr %11, align 4 %hlsl.wave.active.sum5.i = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 %12) %13 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %1, i32 %3) store i32 %hlsl.wave.active.sum5.i, ptr %13, align 4 ret void }