; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt -passes=simplifycfg -passes=instnamer -S %s | FileCheck %s ; This test ensures that given dx.resource.getpointer reads inaccessible memory, ; the SimplifyCFG pass will be prevented from sinking these intrinsics out of ; branches which would create phi nodes on the returned ptr. ; ; NOTE: The following test ir is generated from: ; https://godbolt.org/z/1EdGTbscE. %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } %__cblayout_d = type <{ i32, i32, i32, i32 }> @a = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 @.str = private unnamed_addr constant [2 x i8] c"a\00", align 1 @b = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 @.str.2 = private unnamed_addr constant [2 x i8] c"b\00", align 1 @c = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 @.str.4 = private unnamed_addr constant [2 x i8] c"c\00", align 1 @d.cb = local_unnamed_addr global target("dx.CBuffer", %__cblayout_d) poison @e = external hidden local_unnamed_addr addrspace(2) global i32, align 4 @f = external hidden local_unnamed_addr addrspace(2) global i32, align 4 @g = external hidden local_unnamed_addr addrspace(2) global i32, align 4 @h = external hidden local_unnamed_addr addrspace(2) global i32, align 4 @d.str = private unnamed_addr constant [2 x i8] c"d\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.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str) ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP0]], ptr @a, align 4 ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP0]], ptr getelementptr inbounds nuw (i8, ptr @a, i32 4), align 4 ; CHECK-NEXT: [[TMP1:%.*]] = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str.2) ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP1]], ptr @b, align 4 ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP1]], ptr getelementptr inbounds nuw (i8, ptr @b, i32 4), align 4 ; CHECK-NEXT: [[TMP2:%.*]] = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 4, i32 0, i32 1, i32 0, ptr nonnull @.str.4) ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP2]], ptr @c, align 4 ; CHECK-NEXT: store target("dx.RawBuffer", i32, 1, 0) [[TMP2]], ptr getelementptr inbounds nuw (i8, ptr @c, i32 4), align 4 ; CHECK-NEXT: [[D_CB_H_I_I:%.*]] = tail call target("dx.CBuffer", [[__CBLAYOUT_D:%.*]]) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_dst(i32 6, i32 0, i32 1, i32 0, ptr nonnull @d.str) ; CHECK-NEXT: store target("dx.CBuffer", [[__CBLAYOUT_D]]) [[D_CB_H_I_I]], ptr @d.cb, align 4 ; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr addrspace(2) @h, align 4 ; CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[TMP3]], 0 ; CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_ELSE_I:.*]], label %[[IF_THEN_I:.*]] ; CHECK: [[IF_THEN_I]]: ; CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr addrspace(2) @f, align 4 ; CHECK-NEXT: [[TMP5:%.*]] = 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 [[TMP4]]) ; CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP5]], align 4 ; CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr addrspace(2) @g, align 4 ; CHECK-NEXT: [[TMP8:%.*]] = 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) [[TMP2]], i32 [[TMP7]]) ; CHECK-NEXT: store i32 [[TMP6]], ptr [[TMP8]], align 4 ; CHECK-NEXT: br label %[[MAIN_EXIT:.*]] ; CHECK: [[IF_ELSE_I]]: ; CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr addrspace(2) @g, align 4 ; CHECK-NEXT: [[CMP_I:%.*]] = icmp eq i32 [[TMP9]], 0 ; CHECK-NEXT: br i1 [[CMP_I]], label %[[IF_THEN2_I:.*]], label %[[IF_ELSE6_I:.*]] ; CHECK: [[IF_THEN2_I]]: ; 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: [[TMP11:%.*]] = load i32, ptr [[TMP10]], align 4 ; CHECK-NEXT: [[TMP12:%.*]] = 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) [[TMP2]], i32 0) ; CHECK-NEXT: store i32 [[TMP11]], ptr [[TMP12]], align 4 ; CHECK-NEXT: br label %[[MAIN_EXIT]] ; CHECK: [[IF_ELSE6_I]]: ; CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr addrspace(2) @e, align 4 ; CHECK-NEXT: [[TMP14:%.*]] = 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) [[TMP0]], i32 [[TMP13]]) ; CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[TMP14]], align 4 ; CHECK-NEXT: [[TMP16:%.*]] = 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) [[TMP2]], i32 [[TMP9]]) ; CHECK-NEXT: store i32 [[TMP15]], ptr [[TMP16]], align 4 ; CHECK-NEXT: br label %[[MAIN_EXIT]] ; CHECK: [[MAIN_EXIT]]: ; CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr addrspace(2) @f, align 4 ; CHECK-NEXT: [[TMP18:%.*]] = load i32, ptr addrspace(2) @g, align 4 ; CHECK-NEXT: [[TMP19:%.*]] = 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) [[TMP2]], i32 [[TMP18]]) ; CHECK-NEXT: [[TMP20:%.*]] = load i32, ptr [[TMP19]], align 4 ; CHECK-NEXT: [[ADD_I:%.*]] = add i32 [[TMP20]], [[TMP17]] ; CHECK-NEXT: store i32 [[ADD_I]], ptr [[TMP19]], align 4 ; CHECK-NEXT: ret void ; entry: %0 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str) store target("dx.RawBuffer", i32, 1, 0) %0, ptr @a, align 4 store target("dx.RawBuffer", i32, 1, 0) %0, ptr getelementptr inbounds nuw (i8, ptr @a, i32 4), align 4 %1 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str.2) store target("dx.RawBuffer", i32, 1, 0) %1, ptr @b, align 4 store target("dx.RawBuffer", i32, 1, 0) %1, ptr getelementptr inbounds nuw (i8, ptr @b, i32 4), align 4 %2 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 4, i32 0, i32 1, i32 0, ptr nonnull @.str.4) store target("dx.RawBuffer", i32, 1, 0) %2, ptr @c, align 4 store target("dx.RawBuffer", i32, 1, 0) %2, ptr getelementptr inbounds nuw (i8, ptr @c, i32 4), align 4 %d.cb_h.i.i = tail call target("dx.CBuffer", %__cblayout_d) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_dst(i32 6, i32 0, i32 1, i32 0, ptr nonnull @d.str) store target("dx.CBuffer", %__cblayout_d) %d.cb_h.i.i, ptr @d.cb, align 4 %3 = load i32, ptr addrspace(2) @h, align 4 %tobool.not.i = icmp eq i32 %3, 0 br i1 %tobool.not.i, label %if.else.i, label %if.then.i if.then.i: ; preds = %entry %4 = load i32, ptr addrspace(2) @f, align 4 %5 = 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 %4) %6 = load i32, ptr %5, align 4 %7 = load i32, ptr addrspace(2) @g, align 4 %8 = 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) %2, i32 %7) store i32 %6, ptr %8, align 4 br label %main.exit if.else.i: ; preds = %entry %9 = load i32, ptr addrspace(2) @g, align 4 %cmp.i = icmp eq i32 %9, 0 br i1 %cmp.i, label %if.then2.i, label %if.else6.i if.then2.i: ; preds = %if.else.i %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) %11 = load i32, ptr %10, align 4 %12 = 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) %2, i32 0) store i32 %11, ptr %12, align 4 br label %main.exit if.else6.i: ; preds = %if.else.i %13 = load i32, ptr addrspace(2) @e, align 4 %14 = 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) %0, i32 %13) %15 = load i32, ptr %14, align 4 %16 = 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) %2, i32 %9) store i32 %15, ptr %16, align 4 br label %main.exit main.exit: ; preds = %if.then.i, %if.then2.i, %if.else6.i %17 = load i32, ptr addrspace(2) @f, align 4 %18 = load i32, ptr addrspace(2) @g, align 4 %19 = 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) %2, i32 %18) %20 = load i32, ptr %19, align 4 %add.i = add i32 %20, %17 store i32 %add.i, ptr %19, align 4 ret void }