diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 7530aebdcb581..a9b660a3d7da5 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5084,6 +5084,12 @@ def HLSLWaveGetLaneCount : LangBuiltin<"HLSL_LANG"> { let Prototype = "unsigned int()"; } +def HLSLCheckAccessFullyMapped : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_check_access_fully_mapped"]; + let Attributes = [NoThrow, Const]; + let Prototype = "void(...)"; +} + def HLSLClamp : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_elementwise_clamp"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 5ae3fed099cd4..9de98d5aa8494 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -886,6 +886,29 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, "Intrinsic WaveGetLaneIndex not supported by target architecture"); } } + case Builtin::BI__builtin_hlsl_check_access_fully_mapped: { + // We don't define a DXIL intrinsic, instead it is a cast from uint to bool. + switch (CGM.getTarget().getTriple().getArch()) { + case llvm::Triple::dxil: { + Value *Status = EmitScalarExpr(E->getArg(0)); + return Builder.CreateICmpNE( + Status, ConstantInt::get(Status->getType(), 0), "BoolCast"); + } + + case llvm::Triple::spirv: { + // OpImageSparseTexelsResident consumes a uint residency code + Value *Status = EmitScalarExpr(E->getArg(0)); + + return Builder.CreateIntrinsic( + llvm::Intrinsic::spv_check_access_fully_mapped, + /* Overload types */ {}, + /* Arguments */ {Status}); + } + default: + llvm_unreachable("Intrinsic CheckAccessFullyMapped not supported by " + "target architecture"); + } + } case Builtin::BI__builtin_hlsl_wave_is_first_lane: { Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic(); return EmitRuntimeCall( diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index a538be5ebd099..c473306037755 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -666,9 +666,16 @@ smoothstep(__detail::HLSL_FIXED_VECTOR Min, return __detail::smoothstep_vec_impl(Min, Max, X); } -inline bool CheckAccessFullyMapped(uint Status) { - return static_cast(Status); -} +//===----------------------------------------------------------------------===// +// CheckAccessFullyMapped builtins +//===----------------------------------------------------------------------===// + +/// \fn bool CheckAccessFullyMapped(uint32 x) +/// \brief Returns true if the x parameter represents a value that indicates +/// to the driver that the memory is resident, false otherwise. + +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_check_access_fully_mapped) +bool CheckAccessFullyMapped(uint32_t x); //===----------------------------------------------------------------------===// // fwidth builtin diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 0a164a7b5bbbd..2bfee3be4baa5 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -3304,6 +3304,26 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { TheCall->setType(ArgTyA); break; } + case Builtin::BI__builtin_hlsl_check_access_fully_mapped: { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + Expr *Arg = TheCall->getArg(0); + QualType ArgTyA = Arg->getType(); + + if (CheckArgTypeMatches(&SemaRef, Arg, + SemaRef.getASTContext().UnsignedIntTy)) + return true; + + if (!ArgTyA->isScalarType()) { + SemaRef.Diag(Arg->getBeginLoc(), diag::err_typecheck_convert_incompatible) + << ArgTyA << SemaRef.getASTContext().UnsignedIntTy << 1 << 0 << 0; + return true; + } + + QualType BoolType = getASTContext().BoolTy; + TheCall->setType(BoolType); + break; + } case Builtin::BI__builtin_hlsl_wave_active_max: case Builtin::BI__builtin_hlsl_wave_active_min: case Builtin::BI__builtin_hlsl_wave_active_sum: { diff --git a/clang/test/CodeGenHLSL/builtins/checkaccessfullymapped.hlsl b/clang/test/CodeGenHLSL/builtins/checkaccessfullymapped.hlsl new file mode 100644 index 0000000000000..178d98c1e36b4 --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/checkaccessfullymapped.hlsl @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm \ +// RUN: -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,DX + +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: spirv-unknown-vulkan-compute %s -emit-llvm \ +// RUN: -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,SPV + +// CHECK: test_check_access_fully_mapped +// DX: %[[X_ADDR:.*]] = alloca i32, align 4 +// DX: store i32 %x, ptr %[[X_ADDR]], align 4 +// DX: %[[X_LOAD:.*]] = load i32, ptr %[[X_ADDR]], align 4 +// DX: %[[BoolCast:.*]] = icmp ne i32 %[[X_LOAD]], 0 +// DX: ret i1 %[[BoolCast]] + +// SPV: %[[X_ADDR:.*]] = alloca i32, align 4 +// SPV: store i32 %x, ptr %[[X_ADDR]], align 4 +// SPV: %[[X_LOAD:.*]] = loasd i32, ptr %[[X_ADDR]], align 4 +// SPV: %[[CAFM:.*]] = call i1 @llvm.spv.check.access.fully.mapped(i32 %[[X_LOAD]]) +// SPV: ret i1 %[[CAFM]] + +bool test_check_access_fully_mapped(uint x) +{ + return CheckAccessFullyMapped(x); +} diff --git a/clang/test/SemaHLSL/BuiltIns/checkaccessfullymapped-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/checkaccessfullymapped-errors.hlsl new file mode 100644 index 0000000000000..b429ce846f5b9 --- /dev/null +++ b/clang/test/SemaHLSL/BuiltIns/checkaccessfullymapped-errors.hlsl @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -disable-llvm-passes -verify -verify-ignore-unexpected + +void test_too_few_arg() +{ + return __builtin_hlsl_check_access_fully_mapped(); + // expected-error@-1 {{too few arguments to function call, expected 1, have 0}} +} + +void test_too_many_arg(float2 p0) +{ + return __builtin_hlsl_check_access_fully_mapped(p0, p0, p0); + // expected-error@-1 {{too many arguments to function call, expected 1, have 3}} +} + +bool builtin_bool_to_float_type_promotion(bool p1) +{ + return __builtin_hlsl_check_access_fully_mapped(p1); + // expected-error@-1 {{passing 'bool' to parameter of incompatible type 'unsigned int'}} +} + + +bool2 builtin_check_access_fully_mapped_int2_to_float2_promotion(int2 p1) +{ + return __builtin_hlsl_check_access_fully_mapped(p1); + // expected-error@-1 {{passing 'int2' (aka 'vector') to parameter of incompatible type 'unsigned int'}} +} diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 366f8cf36d75c..182c02560e816 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -41,7 +41,8 @@ let TargetPrefix = "spv" in { def int_spv_alloca : Intrinsic<[llvm_any_ty], [llvm_i8_ty], [ImmArg>]>; def int_spv_alloca_array : Intrinsic<[llvm_any_ty], [llvm_anyint_ty, llvm_i8_ty], [ImmArg>]>; def int_spv_undef : Intrinsic<[llvm_i32_ty], []>; - def int_spv_inline_asm : Intrinsic<[], [llvm_metadata_ty, llvm_metadata_ty, llvm_vararg_ty]>; + def int_spv_inline_asm : Intrinsic<[], [llvm_metadata_ty, llvm_metadata_ty, llvm_vararg_ty]>; + def int_spv_check_access_fully_mapped : Intrinsic<[llvm_i1_ty], [llvm_i32_ty], [IntrNoMem]>; // Expect, Assume Intrinsics def int_spv_assume : Intrinsic<[], [llvm_i1_ty]>; diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index d3fc08eb56cb3..103233fbd0df1 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -3588,6 +3588,17 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpDPdyCoarse); case Intrinsic::spv_fwidth: return selectDerivativeInst(ResVReg, ResType, I, SPIRV::OpFwidth); + case Intrinsic::spv_check_access_fully_mapped: { + MachineBasicBlock &BB = *I.getParent(); + Register StatusReg = I.getOperand(1).getReg(); + + return BuildMI(BB, I, I.getDebugLoc(), + TII.get(SPIRV::OpImageSparseTexelsResident)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(StatusReg) + .constrainAllUses(TII, TRI, RBI); + } default: { std::string DiagMsg; raw_string_ostream OS(DiagMsg); diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/check_access_fully_mapped.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/check_access_fully_mapped.ll new file mode 100644 index 0000000000000..47f9235c8dc69 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/check_access_fully_mapped.ll @@ -0,0 +1,22 @@ +; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; Make sure SPIRV operation function calls for check access fully mapped are lowered correctly. + +; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450" +; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16 +; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4 +; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4 + +define noundef i1 @check_access_fully_mapped(i32 %i) { +entry: + ; CHECK: %[[#]] = OpFunction %[[#vec4_float_16]] None %[[#]] + ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]] + ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_16]] + ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_16]] %[[#op_ext_glsl]] Step %[[#arg0]] %[[#arg1]] + %hlsl.check_access_fully_mapped = call i1 @llvm.spv.check_access_fully_mapped(i32 %i) + ret i1 %hlsl.check_access_fully_mapped +} + +declare i1 @llvm.spv.check_access_fully_mapped(i32)