Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Metal] Add experimental Metal support #6805

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ Development kits containing only the dxc.exe driver app, the dxcompiler.dll, and

As an example of community contribution, this project can also target the [SPIR-V](https://www.khronos.org/registry/spir-v/) intermediate representation. Please see the [doc](docs/SPIR-V.rst) for how HLSL features are mapped to SPIR-V, and the [wiki](https://github.com/microsoft/DirectXShaderCompiler/wiki/SPIR%E2%80%90V-CodeGen) page for how to build, use, and contribute to the SPIR-V CodeGen.

### Metal CodeGen

When built from source DXC can utilize the [Metal Shader
Converter](https://developer.apple.com/metal/shader-converter/) if it is
available during build and configuration time. This allows DXC to generate Metal
shader libraries directly using the `-metal` flag.

Note: DXC cannot currently disassemble Metal shaders so the `-Fc` flag cannot be
used in conjunction with the `-Fo` flag.

## Building Sources

See the full documentation for [Building and testing DXC](docs/BuildingAndTestingDXC.rst) for detailed instructions.
Expand Down
9 changes: 9 additions & 0 deletions cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -568,3 +568,12 @@ else()
endif()

string(REPLACE " " ";" LLVM_BINDINGS_LIST "${LLVM_BINDINGS}")

# HLSL Change Begin - Metal IR Converter
find_package(MetalIRConverter)
if (METAL_IRCONVERTER_FOUND)
set(ENABLE_METAL_CODEGEN On)
message(STATUS "Enabling Metal Support")
add_definitions(-DENABLE_METAL_CODEGEN)
endif()
# HLSL Change End - Metal IR Converter
16 changes: 16 additions & 0 deletions cmake/modules/FindMetalIRConverter.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
find_path(METAL_IRCONVERTER_INCLUDE_DIR metal_irconverter.h
HINTS /usr/local/include/metal_irconverter
DOC "Path to metal IR converter headers"
)

find_library(METAL_IRCONVERTER_LIB NAMES metalirconverter
PATH_SUFFIXES lib
)

include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(METAL_IRCONVERTER
REQUIRED_VARS METAL_IRCONVERTER_LIB METAL_IRCONVERTER_INCLUDE_DIR)

message(STATUS "Metal IR Converter Include Dir: ${METAL_IRCONVERTER_INCLUDE_DIR}")
message(STATUS "Metal IR Converter Library: ${METAL_IRCONVERTER_LIB}")
mark_as_advanced(METAL_IRCONVERTER_LIB METAL_IRCONVERTER_INCLUDE_DIR)
2 changes: 2 additions & 0 deletions include/dxc/Support/HLSLOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ class DxcOpts {
SpirvOptions; // All SPIR-V CodeGen-related options
#endif
// SPIRV Change Ends

bool GenMetal = false; // OPT_metal
};

/// Use this class to capture, convert and handle the lifetime for the
Expand Down
3 changes: 3 additions & 0 deletions include/dxc/Support/HLSLOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ def disable_exception_handling : Flag<["-", "/"], "disable-exception-handling">,
def skip_serialization : Flag<["-", "/"], "skip-serialization">, Group<hlslcore_Group>, Flags<[CoreOption, HelpHidden]>,
HelpText<"Return a module interface instead of serialized output">;

def metal : Flag<["-"], "metal">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Generate Metal code">;

// SPIRV Change Starts
def spirv : Flag<["-"], "spirv">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Generate SPIR-V code">;
Expand Down
17 changes: 17 additions & 0 deletions lib/DxcSupport/HLSLOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,

addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, opts.Warnings);

opts.GenMetal = Args.hasFlag(OPT_metal, OPT_INVALID, false);

// SPIRV Change Starts
#ifdef ENABLE_SPIRV_CODEGEN
opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
Expand Down Expand Up @@ -1253,6 +1255,21 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV Change Ends

#ifndef ENABLE_METAL_CODEGEN
if (opts.GenMetal) {
errors << "Metal CodeGen not available. "
"Please rebuild with Metal IR Converter installed.";
return 1;
}
#endif

if (opts.GenMetal) {
if (!opts.OutputObject.empty() && opts.AssemblyCode.empty()) {
errors << "Disassembly of Metal IR not supported (yet).";
return 1;
}
}

// Validation for DebugInfo here because spirv uses same DebugInfo opt,
// and legacy wrappers will add EmbedDebug in this case, leading to this
// failing if placed before spirv path sets DebugInfo to true.
Expand Down
5 changes: 5 additions & 0 deletions tools/clang/test/DXC/metal.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// REQUIRES: metal

// RUN: %dxc /T ps_6_0 %S/Inputs/smoke.hlsl -metal | FileCheck %s

// CHECK: define void @main()
4 changes: 4 additions & 0 deletions tools/clang/test/DXC/no_metal.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// UNSUPPORTED: metal

// RUN:not %dxc %S/Inputs/smoke.hlsl /T ps_6_0 -metal 2>&1 | FileCheck %s
// CHECK:Metal CodeGen not available
4 changes: 4 additions & 0 deletions tools/clang/test/DXC/no_metal_disassembly.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// REQUIRES: metal

// RUN:not %dxc %S/Inputs/smoke.hlsl /T ps_6_0 -metal -Fo Tmp.metal -Fc Tmp.air 2>&1 | FileCheck %s
// CHECK: Disassembly of Metal IR not supported (yet).
3 changes: 3 additions & 0 deletions tools/clang/test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ if config.enable_backtrace == "1":
if config.spirv:
config.available_features.add("spirv")

if config.metal:
config.available_features.add("metal")

# Check supported dxil version
def get_dxil_version():
result = subprocess.run([lit.util.which('dxc', llvm_tools_dir), "--version"], stdout=subprocess.PIPE)
Expand Down
1 change: 1 addition & 0 deletions tools/clang/test/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ config.enable_shared = @ENABLE_SHARED@
config.enable_backtrace = "@ENABLE_BACKTRACES@"
config.host_arch = "@HOST_ARCH@"
config.spirv = "@ENABLE_SPIRV_CODEGEN@" =="ON"
config.metal = "@ENABLE_METAL_CODEGEN@".upper() == "ON"

# Support substitution of the tools and libs dirs with user parameters. This is
# used when we can't determine the tool dir at configuration time.
Expand Down
8 changes: 8 additions & 0 deletions tools/clang/tools/dxcompiler/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ target_link_libraries(dxcompiler PRIVATE ${LIBRARIES})
if (ENABLE_SPIRV_CODEGEN)
target_link_libraries(dxcompiler PRIVATE clangSPIRV)
endif (ENABLE_SPIRV_CODEGEN)
if (ENABLE_METAL_CODEGEN)
target_link_libraries(dxcompiler PRIVATE ${METAL_IRCONVERTER_LIB})
target_include_directories(dxcompiler PRIVATE ${METAL_IRCONVERTER_INCLUDE_DIR})

get_filename_component(METAL_IRCONVERTER_LIB_DIR ${METAL_IRCONVERTER_LIB} DIRECTORY CACHE)
set_property(TARGET dxcompiler APPEND_STRING
PROPERTY LINK_FLAGS " -Wl,-rpath,${METAL_IRCONVERTER_LIB_DIR}")
endif (ENABLE_METAL_CODEGEN)
include_directories(AFTER ${LLVM_INCLUDE_DIR}/dxc/Tracing ${DIASDK_INCLUDE_DIRS} ${HLSL_VERSION_LOCATION})

set_target_properties(dxcompiler
Expand Down
89 changes: 88 additions & 1 deletion tools/clang/tools/dxcompiler/dxcompilerobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
#include "clang/Basic/Version.h"
#endif // SUPPORT_QUERY_GIT_COMMIT_INFO

#ifdef ENABLE_METAL_CODEGEN
#include "metal_irconverter.h"
#endif

#define CP_UTF16 1200

using namespace llvm;
Expand Down Expand Up @@ -828,6 +832,10 @@ class DxcCompiler : public IDxcCompiler3,
}
compiler.getLangOpts().IsHLSLLibrary = opts.IsLibraryProfile();

if (compiler.getLangOpts().IsHLSLLibrary && opts.GenMetal)
return ErrorWithString("Shader libraries unsupported in Metal (yet)",
riid, ppResult);

// Clear entry function if library target
if (compiler.getLangOpts().IsHLSLLibrary)
compiler.getLangOpts().HLSLEntryFunction =
Expand Down Expand Up @@ -1117,7 +1125,86 @@ class DxcCompiler : public IDxcCompiler3,
&pHashBlob));
IFT(pResult->SetOutputObject(DXC_OUT_SHADER_HASH, pHashBlob));
} // SUCCEEDED(valHR)
} // compileOK && !opts.CodeGenHighLevel
#ifdef ENABLE_METAL_CODEGEN
// This is a bit hacky because we don't currently have a good way to
// disassemble AIR.
if (opts.GenMetal && produceFullContainer &&
!opts.OutputObject.empty()) {
IRCompiler *MetalCompiler = IRCompilerCreate();
IRCompilerSetEntryPointName(
MetalCompiler,
compiler.getCodeGenOpts().HLSLEntryFunction.c_str());

IRObject *DXILObj = IRObjectCreateFromDXIL(
static_cast<const uint8_t *>(pOutputBlob->GetBufferPointer()),
pOutputBlob->GetBufferSize(), IRBytecodeOwnershipNone);

// Compile DXIL to Metal IR:
IRError *Error = nullptr;
IRObject *AIR = IRCompilerAllocCompileAndLink(MetalCompiler, NULL,
DXILObj, &Error);

if (!AIR) {
IRObjectDestroy(DXILObj);
IRCompilerDestroy(MetalCompiler);
IRErrorDestroy(Error);
return ErrorWithString(
"Error occurred in Metal Shader Conversion", riid, ppResult);
}

IRMetalLibBinary *MetalLib = IRMetalLibBinaryCreate();
IRShaderStage Stage = IRShaderStageInvalid;
const ShaderModel *SM = hlsl::ShaderModel::GetByName(
compiler.getLangOpts().HLSLProfile);
switch (SM->GetKind()) {
case DXIL::ShaderKind::Vertex:
Stage = IRShaderStageVertex;
break;
case DXIL::ShaderKind::Pixel:
Stage = IRShaderStageFragment;
break;
case DXIL::ShaderKind::Hull:
Stage = IRShaderStageHull;
break;
case DXIL::ShaderKind::Domain:
Stage = IRShaderStageDomain;
break;
case DXIL::ShaderKind::Mesh:
Stage = IRShaderStageMesh;
break;
case DXIL::ShaderKind::Amplification:
Stage = IRShaderStageAmplification;
break;
case DXIL::ShaderKind::Geometry:
Stage = IRShaderStageGeometry;
break;
case DXIL::ShaderKind::Compute:
Stage = IRShaderStageCompute;
break;
}
assert(Stage != IRShaderStageInvalid &&
"Library targets not supported for Metal (yet).");
IRObjectGetMetalLibBinary(AIR, Stage, MetalLib);
size_t MetalLibSize = IRMetalLibGetBytecodeSize(MetalLib);
uint8_t *MetalLibBytes = new uint8_t[MetalLibSize];
IRMetalLibGetBytecode(MetalLib, MetalLibBytes);

// Store the metallib to custom format or disk, or use to create a
// MTLLibrary.

CComPtr<IDxcBlob> MetalBlob;
IFT(hlsl::DxcCreateBlobOnHeapCopy(
MetalLibBytes, (uint32_t)MetalLibSize, &MetalBlob));
std::swap(pOutputBlob, MetalBlob);

delete[] MetalLibBytes;
IRMetalLibBinaryDestroy(MetalLib);
IRObjectDestroy(DXILObj);
IRObjectDestroy(AIR);
IRCompilerDestroy(MetalCompiler);
}
#endif
} // compileOK && !opts.CodeGenHighLevel
}

std::string remarks;
Expand Down
Loading