| ; RUN: opt -S -dxil-op-lower %s | FileCheck %s |
| |
| target triple = "dxil-pc-shadermodel6.6-compute" |
| |
| declare void @scalar_user(float) |
| declare void @vector_user(<4 x float>) |
| declare void @check_user(i1) |
| |
| define void @loadv4f32() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; The temporary casts should all have been cleaned up |
| ; CHECK-NOT: %dx.resource.casthandle |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR:]] |
| %load0 = call {<4 x float>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 0) |
| %data0 = extractvalue {<4 x float>, i1} %load0, 0 |
| |
| ; The extract order depends on the users, so don't enforce that here. |
| ; CHECK-DAG: [[VAL0_0:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA0]], 0 |
| %data0_0 = extractelement <4 x float> %data0, i32 0 |
| ; CHECK-DAG: [[VAL0_2:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA0]], 2 |
| %data0_2 = extractelement <4 x float> %data0, i32 2 |
| |
| ; If all of the uses are extracts, we skip creating a vector |
| ; CHECK-NOT: insertelement |
| ; CHECK-DAG: call void @scalar_user(float [[VAL0_0]]) |
| ; CHECK-DAG: call void @scalar_user(float [[VAL0_2]]) |
| call void @scalar_user(float %data0_0) |
| call void @scalar_user(float %data0_2) |
| |
| ; CHECK: [[DATA4:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 4, i32 undef) #[[#ATTR]] |
| %load4 = call {<4 x float>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 4) |
| %data4 = extractvalue {<4 x float>, i1} %load4, 0 |
| |
| ; CHECK: extractvalue %dx.types.ResRet.f32 [[DATA4]], 0 |
| ; CHECK: extractvalue %dx.types.ResRet.f32 [[DATA4]], 1 |
| ; CHECK: extractvalue %dx.types.ResRet.f32 [[DATA4]], 2 |
| ; CHECK: extractvalue %dx.types.ResRet.f32 [[DATA4]], 3 |
| ; CHECK: insertelement <4 x float> undef |
| ; CHECK: insertelement <4 x float> |
| ; CHECK: insertelement <4 x float> |
| ; CHECK: insertelement <4 x float> |
| call void @vector_user(<4 x float> %data4) |
| |
| ; CHECK: [[DATA12:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 12, i32 undef) #[[#ATTR]] |
| %load12 = call {<4 x float>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 12) |
| %data12 = extractvalue {<4 x float>, i1} %load12, 0 |
| |
| ; CHECK: [[DATA12_3:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA12]], 3 |
| %data12_3 = extractelement <4 x float> %data12, i32 3 |
| |
| ; If there are a mix of users we need the vector, but extracts are direct |
| ; CHECK: call void @scalar_user(float [[DATA12_3]]) |
| call void @scalar_user(float %data12_3) |
| call void @vector_user(<4 x float> %data12) |
| |
| ret void |
| } |
| |
| define void @index_dynamic(i32 %bufindex, i32 %elemindex) { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[LOAD:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 %bufindex, i32 undef) #[[#ATTR]] |
| %load = call {<4 x float>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 %bufindex) |
| %data = extractvalue {<4 x float>, i1} %load, 0 |
| |
| ; CHECK: [[ALLOCA:%.*]] = alloca [4 x float] |
| ; CHECK: [[V0:%.*]] = extractvalue %dx.types.ResRet.f32 [[LOAD]], 0 |
| ; CHECK: [[A0:%.*]] = getelementptr inbounds [4 x float], ptr [[ALLOCA]], i32 0, i32 0 |
| ; CHECK: store float [[V0]], ptr [[A0]] |
| ; CHECK: [[V1:%.*]] = extractvalue %dx.types.ResRet.f32 [[LOAD]], 1 |
| ; CHECK: [[A1:%.*]] = getelementptr inbounds [4 x float], ptr [[ALLOCA]], i32 0, i32 1 |
| ; CHECK: store float [[V1]], ptr [[A1]] |
| ; CHECK: [[V2:%.*]] = extractvalue %dx.types.ResRet.f32 [[LOAD]], 2 |
| ; CHECK: [[A2:%.*]] = getelementptr inbounds [4 x float], ptr [[ALLOCA]], i32 0, i32 2 |
| ; CHECK: store float [[V2]], ptr [[A2]] |
| ; CHECK: [[V3:%.*]] = extractvalue %dx.types.ResRet.f32 [[LOAD]], 3 |
| ; CHECK: [[A3:%.*]] = getelementptr inbounds [4 x float], ptr [[ALLOCA]], i32 0, i32 3 |
| ; CHECK: store float [[V3]], ptr [[A3]] |
| ; |
| ; CHECK: [[PTR:%.*]] = getelementptr inbounds [4 x float], ptr [[ALLOCA]], i32 0, i32 %elemindex |
| ; CHECK: [[X:%.*]] = load float, ptr [[PTR]] |
| %x = extractelement <4 x float> %data, i32 %elemindex |
| |
| ; CHECK: call void @scalar_user(float [[X]]) |
| call void @scalar_user(float %x) |
| |
| ret void |
| } |
| |
| define void @loadf32() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", float, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]] |
| %load0 = call {float, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", float, 0, 0, 0) %buffer, i32 0) |
| %data0 = extractvalue {float, i1} %load0, 0 |
| |
| ; CHECK: [[VAL0:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA0]], 0 |
| ; CHECK: call void @scalar_user(float [[VAL0]]) |
| call void @scalar_user(float %data0) |
| |
| ret void |
| } |
| |
| define void @loadv2f32() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <2 x float>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v2f32_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]] |
| %data0 = call {<2 x float>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <2 x float>, 0, 0, 0) %buffer, i32 0) |
| |
| ret void |
| } |
| |
| define void @loadv4f32_checkbit() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]] |
| %data0 = call {<4 x float>, i1} @llvm.dx.resource.load.typedbuffer.f32( |
| target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 0) |
| |
| ; CHECK: [[STATUS:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA0]], 4 |
| ; CHECK: [[MAPPED:%.*]] = call i1 @dx.op.checkAccessFullyMapped.i32(i32 71, i32 [[STATUS]]) #[[#ATTR]] |
| %check = extractvalue {<4 x float>, i1} %data0, 1 |
| |
| ; CHECK: call void @check_user(i1 [[MAPPED]]) |
| call void @check_user(i1 %check) |
| |
| ret void |
| } |
| |
| define void @loadv4i32() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4i32_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.i32 @dx.op.bufferLoad.i32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]] |
| %data0 = call {<4 x i32>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) %buffer, i32 0) |
| |
| ret void |
| } |
| |
| define void @loadv4f16() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <4 x half>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f16_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f16 @dx.op.bufferLoad.f16(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]] |
| %data0 = call {<4 x half>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x half>, 0, 0, 0) %buffer, i32 0) |
| |
| ret void |
| } |
| |
| define void @loadv4i16() { |
| ; CHECK: [[BIND:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, |
| ; CHECK: [[HANDLE:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] |
| %buffer = call target("dx.TypedBuffer", <4 x i16>, 0, 0, 0) |
| @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4i16_0_0_0( |
| i32 0, i32 0, i32 1, i32 0, i1 false) |
| |
| ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.i16 @dx.op.bufferLoad.i16(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]] |
| %data0 = call {<4 x i16>, i1} @llvm.dx.resource.load.typedbuffer( |
| target("dx.TypedBuffer", <4 x i16>, 0, 0, 0) %buffer, i32 0) |
| |
| ret void |
| } |
| |
| ; CHECK: attributes #[[#ATTR]] = {{{.*}} memory(read) {{.*}}} |