Skip to content

Commit 06da989

Browse files
authored
[SPIRV] Add vulkan memory option to DXC (microsoft#6848)
Some recent features require the vulkan memory model. We need a way to support it in DXC. This commit adds an option to enable the Vulkan memory model. The generated code will not change, but the compiler will call the upgrade-memory-model pass in spirv-opt to generate the correct code. The expectation is that new code that requieres the vulkan memory model can be written using the vulkan memory model, and then have the pass update other references. This change will undo the fix for microsoft#6066. The device scope in GLSL450 was not a real device scope. It corresponds to QueueFamily scope in the Vulkan memory model. We will start to use that scope for the atomic operations in order to keep the behavoiur of the atomics the same between the two memory models. Haveing a true device scope atomic will not be possible with DXC. We should be able to do something better when atomic operations are implemented in clang. Fixes microsoft#5784
1 parent 84f68ed commit 06da989

15 files changed

+201
-68
lines changed

include/dxc/Support/HLSLOptions.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ def fspv_entrypoint_name_EQ : Joined<["-"], "fspv-entrypoint-name=">, Group<spir
396396
HelpText<"Specify the SPIR-V entry point name. Defaults to the HLSL entry point name.">;
397397
def fspv_enable_maximal_reconvergence: Flag<["-"], "fspv-enable-maximal-reconvergence">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
398398
HelpText<"Enables the MaximallyReconvergesKHR execution mode for this module.">;
399+
def fspv_use_vulkan_memory_model: Flag<["-"], "fspv-use-vulkan-memory-model">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
400+
HelpText<"Generates SPIR-V modules that use the Vulkan memory model instead of GLSL450.">;
399401
def fvk_auto_shift_bindings: Flag<["-"], "fvk-auto-shift-bindings">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
400402
HelpText<"Apply fvk-*-shift to resources without an explicit register assignment.">;
401403
def Wno_vk_ignored_features : Joined<["-"], "Wno-vk-ignored-features">, Group<spirv_Group>, Flags<[CoreOption, DriverOption, HelpHidden]>,

include/dxc/Support/SPIRVOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct SpirvCodeGenOptions {
6868
bool fixFuncCallArguments;
6969
bool allowRWStructuredBufferArrays;
7070
bool enableMaximalReconvergence;
71+
bool useVulkanMemoryModel;
7172
bool IEEEStrict;
7273
/// Maximum length in words for the OpString literal containing the shader
7374
/// source for DebugSource and DebugSourceContinued. If the source code length

lib/DxcSupport/HLSLOptions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
10851085
Args.hasFlag(OPT_fvk_allow_rwstructuredbuffer_arrays, OPT_INVALID, false);
10861086
opts.SpirvOptions.enableMaximalReconvergence =
10871087
Args.hasFlag(OPT_fspv_enable_maximal_reconvergence, OPT_INVALID, false);
1088+
opts.SpirvOptions.useVulkanMemoryModel =
1089+
Args.hasFlag(OPT_fspv_use_vulkan_memory_model, OPT_INVALID, false);
10881090

10891091
if (!handleVkShiftArgs(Args, OPT_fvk_b_shift, "b", &opts.SpirvOptions.bShift,
10901092
errors) ||

tools/clang/include/clang/SPIRV/SpirvBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,9 @@ class SpirvBuilder {
770770
/// the feature used at the given source location.
771771
inline void requireCapability(spv::Capability, SourceLocation loc = {});
772772

773+
/// \brief Returns true if the module requires the given capability.
774+
inline bool hasCapability(spv::Capability cap);
775+
773776
/// \brief Adds an extension to the module under construction for translating
774777
/// the given target at the given source location.
775778
inline void requireExtension(llvm::StringRef extension, SourceLocation);
@@ -896,6 +899,11 @@ void SpirvBuilder::requireCapability(spv::Capability cap, SourceLocation loc) {
896899
}
897900
}
898901

902+
bool SpirvBuilder::hasCapability(spv::Capability cap) {
903+
SpirvCapability capability({}, cap);
904+
return mod->hasCapability(capability);
905+
}
906+
899907
void SpirvBuilder::requireExtension(llvm::StringRef ext, SourceLocation loc) {
900908
auto *extension = new (context) SpirvExtension(loc, ext);
901909
if (!mod->addExtension(extension))

tools/clang/include/clang/SPIRV/SpirvModule.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class SpirvModule {
102102
// Returns false otherwise (e.g. if the capability already existed).
103103
bool addCapability(SpirvCapability *cap);
104104

105+
// Returns true if the capability is in the module.
106+
bool hasCapability(SpirvCapability &cap);
107+
105108
// Set the memory model of the module.
106109
void setMemoryModel(SpirvMemoryModel *model);
107110

tools/clang/lib/SPIRV/CapabilityVisitor.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ void CapabilityVisitor::addCapability(spv::Capability cap, SourceLocation loc) {
4646
void CapabilityVisitor::addCapabilityForType(const SpirvType *type,
4747
SourceLocation loc,
4848
spv::StorageClass sc) {
49-
// Defent against instructions that do not have a return type.
49+
// Defend against instructions that do not have a return type.
5050
if (!type)
5151
return;
5252

@@ -827,8 +827,6 @@ void CapabilityVisitor::AddVulkanMemoryModelForVolatile(SpirvDecoration *decor,
827827
"Volatile builtin variable in raytracing", loc);
828828
}
829829
addCapability(spv::Capability::VulkanMemoryModel, loc);
830-
spvBuilder.setMemoryModel(spv::AddressingModel::Logical,
831-
spv::MemoryModel::VulkanKHR);
832830
}
833831
}
834832

@@ -894,10 +892,6 @@ bool CapabilityVisitor::visit(SpirvModule *, Visitor::Phase phase) {
894892
{spv::Capability::RayTracingKHR});
895893
}
896894

897-
addExtensionAndCapabilitiesIfEnabled(
898-
Extension::KHR_vulkan_memory_model,
899-
{spv::Capability::VulkanMemoryModelDeviceScope});
900-
901895
addExtensionAndCapabilitiesIfEnabled(
902896
Extension::NV_shader_subgroup_partitioned,
903897
{spv::Capability::GroupNonUniformPartitionedNV});

tools/clang/lib/SPIRV/FeatureManager.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ FeatureManager::FeatureManager(DiagnosticsEngine &de,
8787
} else {
8888
for (auto ext : opts.allowedExtensions)
8989
allowExtension(ext);
90+
91+
// The option to use the vulkan memory model implies the extension is
92+
// available.
93+
if (opts.useVulkanMemoryModel) {
94+
allowExtension("SPV_KHR_vulkan_memory_model");
95+
}
9096
}
9197
}
9298

tools/clang/lib/SPIRV/SpirvEmitter.cpp

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,10 @@ void SpirvEmitter::HandleTranslationUnit(ASTContext &context) {
874874
if (context.getDiagnostics().hasErrorOccurred())
875875
return;
876876

877+
if (!UpgradeToVulkanMemoryModelIfNeeded(&m)) {
878+
return;
879+
}
880+
877881
// Check the existance of Texture and Sampler with
878882
// [[vk::combinedImageSampler]] for the same descriptor set and binding.
879883
auto resourceInfoForSampledImages =
@@ -14673,8 +14677,9 @@ SpirvEmitter::createFunctionScopeTempFromParameter(const ParmVarDecl *param) {
1467314677
return tempVar;
1467414678
}
1467514679

14676-
bool SpirvEmitter::spirvToolsFixupOpExtInst(std::vector<uint32_t> *mod,
14677-
std::string *messages) {
14680+
bool SpirvEmitter::spirvToolsRunPass(std::vector<uint32_t> *mod,
14681+
spvtools::Optimizer::PassToken token,
14682+
std::string *messages) {
1467814683
spvtools::Optimizer optimizer(featureManager.getTargetEnv());
1467914684
optimizer.SetMessageConsumer(
1468014685
[messages](spv_message_level_t /*level*/, const char * /*source*/,
@@ -14691,33 +14696,28 @@ bool SpirvEmitter::spirvToolsFixupOpExtInst(std::vector<uint32_t> *mod,
1469114696
options.set_preserve_bindings(spirvOptions.preserveBindings);
1469214697
options.set_max_id_bound(spirvOptions.maxId);
1469314698

14694-
optimizer.RegisterPass(
14695-
spvtools::CreateOpExtInstWithForwardReferenceFixupPass());
14696-
14699+
optimizer.RegisterPass(std::move(token));
1469714700
return optimizer.Run(mod->data(), mod->size(), mod, options);
1469814701
}
1469914702

14703+
bool SpirvEmitter::spirvToolsFixupOpExtInst(std::vector<uint32_t> *mod,
14704+
std::string *messages) {
14705+
spvtools::Optimizer::PassToken token =
14706+
spvtools::CreateOpExtInstWithForwardReferenceFixupPass();
14707+
return spirvToolsRunPass(mod, std::move(token), messages);
14708+
}
14709+
1470014710
bool SpirvEmitter::spirvToolsTrimCapabilities(std::vector<uint32_t> *mod,
1470114711
std::string *messages) {
14702-
spvtools::Optimizer optimizer(featureManager.getTargetEnv());
14703-
optimizer.SetMessageConsumer(
14704-
[messages](spv_message_level_t /*level*/, const char * /*source*/,
14705-
const spv_position_t & /*position*/,
14706-
const char *message) { *messages += message; });
14707-
14708-
string::RawOstreamBuf printAllBuf(llvm::errs());
14709-
std::ostream printAllOS(&printAllBuf);
14710-
if (spirvOptions.printAll)
14711-
optimizer.SetPrintAll(&printAllOS);
14712-
14713-
spvtools::OptimizerOptions options;
14714-
options.set_run_validator(false);
14715-
options.set_preserve_bindings(spirvOptions.preserveBindings);
14716-
options.set_max_id_bound(spirvOptions.maxId);
14717-
14718-
optimizer.RegisterPass(spvtools::CreateTrimCapabilitiesPass());
14712+
spvtools::Optimizer::PassToken token = spvtools::CreateTrimCapabilitiesPass();
14713+
return spirvToolsRunPass(mod, std::move(token), messages);
14714+
}
1471914715

14720-
return optimizer.Run(mod->data(), mod->size(), mod, options);
14716+
bool SpirvEmitter::spirvToolsUpgradeToVulkanMemoryModel(
14717+
std::vector<uint32_t> *mod, std::string *messages) {
14718+
spvtools::Optimizer::PassToken token =
14719+
spvtools::CreateUpgradeMemoryModelPass();
14720+
return spirvToolsRunPass(mod, std::move(token), messages);
1472114721
}
1472214722

1472314723
bool SpirvEmitter::spirvToolsOptimize(std::vector<uint32_t> *mod,
@@ -15123,5 +15123,26 @@ SpirvEmitter::splatScalarToGenerate(QualType type, SpirvInstruction *scalar,
1512315123
return {};
1512415124
}
1512515125

15126+
bool SpirvEmitter::UpgradeToVulkanMemoryModelIfNeeded(
15127+
std::vector<uint32_t> *module) {
15128+
// DXC generates code assuming the vulkan memory model is not used. However,
15129+
// if a feature is used that requires the Vulkan memory model, then some code
15130+
// may need to be rewritten.
15131+
if (!spirvOptions.useVulkanMemoryModel &&
15132+
!spvBuilder.hasCapability(spv::Capability::VulkanMemoryModel))
15133+
return true;
15134+
15135+
std::string messages;
15136+
if (!spirvToolsUpgradeToVulkanMemoryModel(module, &messages)) {
15137+
emitFatalError("failed to use the vulkan memory model: %0", {}) << messages;
15138+
emitNote("please file a bug report on "
15139+
"https://github.com/Microsoft/DirectXShaderCompiler/issues "
15140+
"with source code if possible",
15141+
{});
15142+
return false;
15143+
}
15144+
return true;
15145+
}
15146+
1512615147
} // end namespace spirv
1512715148
} // end namespace clang

tools/clang/lib/SPIRV/SpirvEmitter.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include "ConstEvaluator.h"
3838
#include "DeclResultIdMapper.h"
39+
#include "spirv-tools/optimizer.hpp"
3940

4041
namespace spvtools {
4142
namespace opt {
@@ -1200,6 +1201,13 @@ class SpirvEmitter : public ASTConsumer {
12001201
/// Returns true on success and false otherwise.
12011202
bool spirvToolsOptimize(std::vector<uint32_t> *mod, std::string *messages);
12021203

1204+
// \brief Runs the pass represented by the given pass token on the module.
1205+
// Returns true if the pass was successfully run. Any messages from the
1206+
// optimizer are returned in `messages`.
1207+
bool spirvToolsRunPass(std::vector<uint32_t> *mod,
1208+
spvtools::Optimizer::PassToken token,
1209+
std::string *messages);
1210+
12031211
// \brief Calls SPIRV-Tools optimizer fix-opextinst-opcodes pass. This pass
12041212
// fixes OpExtInst/OpExtInstWithForwardRefsKHR opcodes to use the correct one
12051213
// depending of the presence of forward references.
@@ -1214,6 +1222,13 @@ class SpirvEmitter : public ASTConsumer {
12141222
bool spirvToolsTrimCapabilities(std::vector<uint32_t> *mod,
12151223
std::string *messages);
12161224

1225+
// \brief Runs the upgrade memory model pass using SPIRV-Tools's optimizer.
1226+
// This pass will modify the module, |mod|, so that it conforms to the Vulkan
1227+
// memory model instead of the GLSL450 memory model. Returns
1228+
// info/warning/error messages via |messages|.
1229+
bool spirvToolsUpgradeToVulkanMemoryModel(std::vector<uint32_t> *mod,
1230+
std::string *messages);
1231+
12171232
/// \brief Helper function to run SPIRV-Tools optimizer's legalization passes.
12181233
/// Runs the SPIRV-Tools legalization on the given SPIR-V module |mod|, and
12191234
/// gets the info/warning/error messages via |messages|. If
@@ -1268,6 +1283,11 @@ class SpirvEmitter : public ASTConsumer {
12681283
SpirvInstruction *scalar,
12691284
SpirvLayoutRule rule);
12701285

1286+
/// Modifies the instruction in the code that use the GLSL450 memory module to
1287+
/// use the Vulkan memory model. This is done only if it has been requested or
1288+
/// the Vulkan memory model capability has been added to the module.
1289+
bool UpgradeToVulkanMemoryModelIfNeeded(std::vector<uint32_t> *module);
1290+
12711291
public:
12721292
/// \brief Wrapper method to create a fatal error message and report it
12731293
/// in the diagnostic engine associated with this consumer.

0 commit comments

Comments
 (0)