Skip to content

Commit

Permalink
[SPIRV] Add support of the GL_EXT_spirv_intrinsics
Browse files Browse the repository at this point in the history
Related to the issue: microsoft#3919
Add these attributes

vk::ext_capability
vk::ext_extension
vk::ext_instruction
  • Loading branch information
jiaolu committed Sep 18, 2021
1 parent 5fba0c3 commit 92920f1
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 1 deletion.
24 changes: 24 additions & 0 deletions tools/clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,14 @@ def VKBinding : InheritableAttr {
let Documentation = [Undocumented];
}

def VKCapabilityExt : InheritableAttr {
let Spellings = [CXX11<"vk", "ext_capability">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Args = [IntArgument<"capability">];
let LangOpts = [SPIRV];
let Documentation = [Undocumented];
}

def VKCounterBinding : InheritableAttr {
let Spellings = [CXX11<"vk", "counter_binding">];
let Subjects = SubjectList<[CounterStructuredBuffer], ErrorDiag, "ExpectedCounterStructuredBuffer">;
Expand All @@ -1018,6 +1026,14 @@ def VKCounterBinding : InheritableAttr {
let Documentation = [Undocumented];
}

def VKExtensionExt : InheritableAttr {
let Spellings = [CXX11<"vk", "ext_extension">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Args = [StringArgument<"name">];
let LangOpts = [SPIRV];
let Documentation = [Undocumented];
}

// Global variables that are of struct type
def StructGlobalVar : SubsetSubject<Var, [{S->hasGlobalStorage() && S->getType()->isStructureType()}]>;

Expand Down Expand Up @@ -1083,6 +1099,14 @@ def VKInputAttachmentIndex : InheritableAttr {
let Documentation = [Undocumented];
}

def VKInstructionExt : InheritableAttr {
let Spellings = [CXX11<"vk", "ext_instruction">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Args = [IntArgument<"opcode">, StringArgument<"instruction_set", 1>];
let LangOpts = [SPIRV];
let Documentation = [Undocumented];
}

// Global variables that are of scalar type
def ScalarGlobalVar : SubsetSubject<Var, [{S->hasGlobalStorage() && S->getType()->isScalarType()}]>;

Expand Down
7 changes: 7 additions & 0 deletions tools/clang/include/clang/SPIRV/SpirvBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,13 @@ class SpirvBuilder {
/// OpIgnoreIntersectionKHR/OpTerminateIntersectionKHR
void createRaytracingTerminateKHR(spv::Op opcode, SourceLocation loc);

/// \brief Create spirv intrinsic instructions
SpirvInstruction *
createSpirvIntrInstExt(uint32_t opcode, QualType retType,
llvm::ArrayRef<SpirvInstruction *> operands,
llvm::StringRef ext, llvm::StringRef instSet,
llvm::ArrayRef<uint32_t> capts, SourceLocation loc);

/// \brief Returns a clone SPIR-V variable for CTBuffer with FXC memory layout
/// and creates copy instructions from the CTBuffer to the clone variable in
/// module.init if it contains HLSL matrix 1xN. Otherwise, returns nullptr.
Expand Down
31 changes: 31 additions & 0 deletions tools/clang/include/clang/SPIRV/SpirvInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class SpirvInstruction {
IK_Store, // OpStore
IK_UnaryOp, // Unary operations
IK_VectorShuffle, // OpVectorShuffle
IK_SpirvIntrinsicInstruction, // Spirv Instructions for no particular op

// For DebugInfo instructions defined in OpenCL.DebugInfo.100
IK_DebugInfoNone,
Expand Down Expand Up @@ -1999,6 +2000,36 @@ class SpirvDemoteToHelperInvocationEXT : public SpirvInstruction {
bool invokeVisitor(Visitor *v) override;
};

class SpirvIntrinsicInstruction : public SpirvInstruction {
public:
SpirvIntrinsicInstruction(QualType resultType, uint32_t opcode,
llvm::ArrayRef<SpirvInstruction *> operands,
llvm::StringRef ext, SpirvExtInstImport *set,
llvm::ArrayRef<uint32_t> capts, SourceLocation loc);

DEFINE_RELEASE_MEMORY_FOR_CLASS(SpirvIntrinsicInstruction)

// For LLVM-style RTTI
static bool classof(const SpirvInstruction *inst) {
return inst->getKind() == IK_SpirvIntrinsicInstruction;
}

bool invokeVisitor(Visitor *v) override;

llvm::ArrayRef<SpirvInstruction *> getOperands() const { return operands; }
llvm::ArrayRef<uint32_t> getCapabilities() const { return capabilities; }
llvm::StringRef getExtension() const { return extension; }
SpirvExtInstImport *getInstructionSet() const { return instructionSet; }
uint32_t getInstruction() const { return instruction; }

private:
uint32_t instruction;
llvm::SmallVector<SpirvInstruction *, 4> operands;
llvm::SmallVector<uint32_t, 4> capabilities;
std::string extension;
SpirvExtInstImport *instructionSet;
};

/// \breif Base class for all OpenCL.DebugInfo.100 extension instructions.
/// Note that all of these instructions should be added to the SPIR-V module as
/// an OpExtInst instructions. So, all of these instructions must:
Expand Down
1 change: 1 addition & 0 deletions tools/clang/include/clang/SPIRV/SpirvVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class Visitor {
DEFINE_VISIT_METHOD(SpirvRayQueryOpKHR)
DEFINE_VISIT_METHOD(SpirvReadClock)
DEFINE_VISIT_METHOD(SpirvRayTracingTerminateOpKHR)
DEFINE_VISIT_METHOD(SpirvIntrinsicInstruction)
#undef DEFINE_VISIT_METHOD

protected:
Expand Down
10 changes: 10 additions & 0 deletions tools/clang/lib/SPIRV/CapabilityVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,16 @@ bool CapabilityVisitor::visitInstruction(SpirvInstruction *instr) {
addCapability(getNonUniformCapability(resultType));
}

if (instr->getKind() == SpirvInstruction::IK_SpirvIntrinsicInstruction) {
SpirvIntrinsicInstruction *pSpvInst =
dyn_cast<SpirvIntrinsicInstruction>(instr);
for (auto &cap : pSpvInst->getCapabilities()) {
addCapability(static_cast<spv::Capability>(cap));
}
if (pSpvInst->getExtension().size() > 0)
spvBuilder.requireExtension(pSpvInst->getExtension(), loc);
}

// Add opcode-specific capabilities
switch (opcode) {
case spv::Op::OpDPdxCoarse:
Expand Down
17 changes: 17 additions & 0 deletions tools/clang/lib/SPIRV/EmitVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,23 @@ bool EmitVisitor::visit(SpirvRayTracingTerminateOpKHR *inst) {
return true;
}

bool EmitVisitor::visit(SpirvIntrinsicInstruction *inst) {
initInstruction(inst);
curInst.push_back(inst->getResultTypeId());
curInst.push_back(getOrAssignResultId<SpirvInstruction>(inst));
if (inst->getInstructionSet()) {
curInst.push_back(
getOrAssignResultId<SpirvInstruction>(inst->getInstructionSet()));
curInst.push_back(inst->getInstruction());
}

for (const auto operand : inst->getOperands())
curInst.push_back(getOrAssignResultId<SpirvInstruction>(operand));

finalizeInstruction(&mainBinary);
return true;
}

// EmitTypeHandler ------

void EmitTypeHandler::initTypeInstruction(spv::Op op) {
Expand Down
1 change: 1 addition & 0 deletions tools/clang/lib/SPIRV/EmitVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ class EmitVisitor : public Visitor {
bool visit(SpirvDebugTypeMember *) override;
bool visit(SpirvDebugTypeTemplate *) override;
bool visit(SpirvDebugTypeTemplateParameter *) override;
bool visit(SpirvIntrinsicInstruction *) override;

using Visitor::visit;

Expand Down
17 changes: 17 additions & 0 deletions tools/clang/lib/SPIRV/SpirvBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,23 @@ SpirvInstruction *SpirvBuilder::createReadClock(SpirvInstruction *scope,
return inst;
}

SpirvInstruction *SpirvBuilder::createSpirvIntrInstExt(
uint32_t opcode, QualType retType,
llvm::ArrayRef<SpirvInstruction *> operands, llvm::StringRef ext,
llvm::StringRef instSet, llvm::ArrayRef<uint32_t> capts,
SourceLocation loc) {
assert(insertPoint && "null insert point");

SpirvExtInstImport *set =
(instSet.size() == 0) ? nullptr : getExtInstSet(instSet);

auto *inst = new (context) SpirvIntrinsicInstruction(
retType, opcode, operands, ext, set, capts, loc);

insertPoint->addInstruction(inst);
return inst;
}

void SpirvBuilder::createRaytracingTerminateKHR(spv::Op opcode,
SourceLocation loc) {
assert(insertPoint && "null insert point");
Expand Down
43 changes: 42 additions & 1 deletion tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2288,11 +2288,16 @@ SpirvInstruction *SpirvEmitter::doCallExpr(const CallExpr *callExpr) {
if (const auto *memberCall = dyn_cast<CXXMemberCallExpr>(callExpr))
return doCXXMemberCallExpr(memberCall);

auto funcDecl = callExpr->getDirectCallee();
// Intrinsic functions such as 'dot' or 'mul'
if (hlsl::IsIntrinsicOp(callExpr->getDirectCallee())) {
if (hlsl::IsIntrinsicOp(funcDecl)) {
return processIntrinsicCallExpr(callExpr);
}

if (funcDecl && funcDecl->hasAttr<VKInstructionExtAttr>()) {
return processSpvIntrinsicCallExpr(callExpr);
}

// Normal standalone functions
return processCall(callExpr);
}
Expand Down Expand Up @@ -12392,6 +12397,42 @@ SpirvEmitter::processRayQueryIntrinsics(const CXXMemberCallExpr *expr,
return retVal;
}

SpirvInstruction *
SpirvEmitter::processSpvIntrinsicCallExpr(const CallExpr *expr) {
auto funcDecl = expr->getDirectCallee();
auto &attrs = funcDecl->getAttrs();
QualType retType = funcDecl->getReturnType();
llvm::SmallVector<uint32_t, 8> capbilities;
llvm::StringRef extExtension = "";
llvm::StringRef instSet = "";
uint32_t op = 0;
for (auto &attr : attrs) {
if (auto capAttr = dyn_cast<VKCapabilityExtAttr>(attr)) {
capbilities.push_back(capAttr->getCapability());
} else if (auto extAttr = dyn_cast<VKExtensionExtAttr>(attr)) {
extExtension = extAttr->getName();
} else if (auto instAttr = dyn_cast<VKInstructionExtAttr>(attr)) {
op = instAttr->getOpcode();
instSet = instAttr->getInstruction_set();
}
}

llvm::SmallVector<SpirvInstruction *, 8> spvArgs;

const auto args = expr->getArgs();
for (uint32_t i = 0; i < expr->getNumArgs(); ++i) {
spvArgs.push_back(doExpr(args[i]));
}

const auto loc = expr->getExprLoc();

SpirvInstruction *retVal = spvBuilder.createSpirvIntrInstExt(
op, retType, spvArgs, extExtension, instSet, capbilities, loc);

retVal->setRValue();
return retVal;
}

bool SpirvEmitter::spirvToolsValidate(std::vector<uint32_t> *mod,
std::string *messages) {
spvtools::SpirvTools tools(featureManager.getTargetEnv());
Expand Down
2 changes: 2 additions & 0 deletions tools/clang/lib/SPIRV/SpirvEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,8 @@ class SpirvEmitter : public ASTConsumer {
/// Process ray query intrinsics
SpirvInstruction *processRayQueryIntrinsics(const CXXMemberCallExpr *expr,
hlsl::IntrinsicOp opcode);
/// Process spirv intrinsic instruction
SpirvInstruction *processSpvIntrinsicCallExpr(const CallExpr *expr);

private:
/// Returns the <result-id> for constant value 0 of the given type.
Expand Down
13 changes: 13 additions & 0 deletions tools/clang/lib/SPIRV/SpirvInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvDebugTypeTemplateParameter)
DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvRayQueryOpKHR)
DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvReadClock)
DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvRayTracingTerminateOpKHR)
DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvIntrinsicInstruction)

#undef DEFINE_INVOKE_VISITOR_FOR_CLASS

Expand Down Expand Up @@ -1004,5 +1005,17 @@ SpirvRayTracingTerminateOpKHR::SpirvRayTracingTerminateOpKHR(spv::Op opcode,
opcode == spv::Op::OpIgnoreIntersectionKHR);
}

SpirvIntrinsicInstruction::SpirvIntrinsicInstruction(
QualType resultType, uint32_t opcode,
llvm::ArrayRef<SpirvInstruction *> vecOperands, llvm::StringRef ext,
SpirvExtInstImport *set, llvm::ArrayRef<uint32_t> capts, SourceLocation loc)
: SpirvInstruction(IK_SpirvIntrinsicInstruction,
set != nullptr ? spv::Op::OpExtInst
: static_cast<spv::Op>(opcode),
resultType, loc),
instruction(opcode), operands(vecOperands.begin(), vecOperands.end()),
capabilities(capts.begin(), capts.end()), extension(ext.data()),
instructionSet(set) {}

} // namespace spirv
} // namespace clang
16 changes: 16 additions & 0 deletions tools/clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11945,6 +11945,22 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
case AttributeList::AT_VKShaderRecordEXT:
declAttr = ::new (S.Context) VKShaderRecordEXTAttr(A.getRange(), S.Context, A.getAttributeSpellingListIndex());
break;
case AttributeList::AT_VKCapabilityExt:
declAttr = ::new (S.Context) VKCapabilityExtAttr(
A.getRange(), S.Context, ValidateAttributeIntArg(S, A),
A.getAttributeSpellingListIndex());
break;
case AttributeList::AT_VKExtensionExt:
declAttr = ::new (S.Context) VKExtensionExtAttr(
A.getRange(), S.Context, ValidateAttributeStringArg(S, A, nullptr),
A.getAttributeSpellingListIndex());
break;
case AttributeList::AT_VKInstructionExt:
declAttr = ::new (S.Context) VKInstructionExtAttr(
A.getRange(), S.Context, ValidateAttributeIntArg(S, A),
ValidateAttributeStringArg(S, A, nullptr, 1),
A.getAttributeSpellingListIndex());
break;
default:
Handled = false;
return;
Expand Down
34 changes: 34 additions & 0 deletions tools/clang/test/CodeGenSPIRV/spv.intrinsicInstruction.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Run: %dxc -T vs_6_0 -E main

struct SInstanceData {
float4x3 VisualToWorld;
float4 Output;
};

struct VS_INPUT {
float3 Position : POSITION;
SInstanceData InstanceData : TEXCOORD4;
};

[[vk::ext_capability(5055)]]
[[vk::ext_extension("SPV_KHR_shader_clock")]]
[[vk::ext_instruction(/* OpReadClockKHR */ 5056)]]
uint64_t ReadClock(uint scope);

[[vk::ext_instruction(/* Sin*/ 13, "GLSL.std.450")]]
float4 spv_sin(float4 v);

// CHECK: OpCapability ShaderClockKHR
// CHECK-NEXT: OpExtension "SPV_KHR_shader_clock"
// CHECK-NEXT: {{%\d+}} = OpExtInstImport "GLSL.std.450"

float4 main(const VS_INPUT v) : SV_Position {
SInstanceData I = v.InstanceData;
uint64_t clock;
// CHECK: {{%\d+}} = OpExtInst %v4float {{%\d+}} Sin {{%\d+}}
I.Output = spv_sin(v.InstanceData.Output);
// CHECK: {{%\d+}} = OpReadClockKHR %ulong %uint_1
clock = ReadClock(vk::DeviceScope);

return I.Output;
}
4 changes: 4 additions & 0 deletions tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,10 @@ TEST_F(FileTest, IntrinsicsVkReadClock) {
runFileTest("intrinsics.vkreadclock.hlsl");
}

TEST_F(FileTest, IntrinsicsSpirv) {
runFileTest("spv.intrinsicInstruction.hlsl");
}

// Intrinsics added in SM 6.6
TEST_F(FileTest, IntrinsicsSM66PackU8S8) {
runFileTest("intrinsics.sm6_6.pack_s8u8.hlsl");
Expand Down

0 comments on commit 92920f1

Please sign in to comment.