Skip to content

Commit f7f1e3d

Browse files
authored
[Metal] Add experimental Metal support (#6805)
This adds a new `-metal` flag to DXC which can be used to generate Metal's IR directly from DXC after compilation. There are some limitations in this flag which are worth noting: 1) It does not support library shaders (yet) 2) It does not support disassembly (yet) 3) It is _wildly_ under tested because wtihout (2) we can't do anything to really verify correct output (yay?)
1 parent 0a11435 commit f7f1e3d

File tree

13 files changed

+177
-1
lines changed

13 files changed

+177
-1
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ Development kits containing only the dxc.exe driver app, the dxcompiler.dll, and
3434

3535
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.
3636

37+
### Metal CodeGen
38+
39+
When built from source DXC can utilize the [Metal Shader
40+
Converter](https://developer.apple.com/metal/shader-converter/) if it is
41+
available during build and configuration time. This allows DXC to generate Metal
42+
shader libraries directly using the `-metal` flag.
43+
44+
Note: DXC cannot currently disassemble Metal shaders so the `-Fc` flag cannot be
45+
used in conjunction with the `-Fo` flag.
46+
3747
## Building Sources
3848

3949
See the full documentation for [Building and testing DXC](docs/BuildingAndTestingDXC.rst) for detailed instructions.

cmake/config-ix.cmake

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,12 @@ else()
568568
endif()
569569

570570
string(REPLACE " " ";" LLVM_BINDINGS_LIST "${LLVM_BINDINGS}")
571+
572+
# HLSL Change Begin - Metal IR Converter
573+
find_package(MetalIRConverter)
574+
if (METAL_IRCONVERTER_FOUND)
575+
set(ENABLE_METAL_CODEGEN On)
576+
message(STATUS "Enabling Metal Support")
577+
add_definitions(-DENABLE_METAL_CODEGEN)
578+
endif()
579+
# HLSL Change End - Metal IR Converter
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
find_path(METAL_IRCONVERTER_INCLUDE_DIR metal_irconverter.h
2+
HINTS /usr/local/include/metal_irconverter
3+
DOC "Path to metal IR converter headers"
4+
)
5+
6+
find_library(METAL_IRCONVERTER_LIB NAMES metalirconverter
7+
PATH_SUFFIXES lib
8+
)
9+
10+
include(FindPackageHandleStandardArgs)
11+
FIND_PACKAGE_HANDLE_STANDARD_ARGS(METAL_IRCONVERTER
12+
REQUIRED_VARS METAL_IRCONVERTER_LIB METAL_IRCONVERTER_INCLUDE_DIR)
13+
14+
message(STATUS "Metal IR Converter Include Dir: ${METAL_IRCONVERTER_INCLUDE_DIR}")
15+
message(STATUS "Metal IR Converter Library: ${METAL_IRCONVERTER_LIB}")
16+
mark_as_advanced(METAL_IRCONVERTER_LIB METAL_IRCONVERTER_INCLUDE_DIR)

include/dxc/Support/HLSLOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ class DxcOpts {
274274
SpirvOptions; // All SPIR-V CodeGen-related options
275275
#endif
276276
// SPIRV Change Ends
277+
278+
bool GenMetal = false; // OPT_metal
277279
};
278280

279281
/// Use this class to capture, convert and handle the lifetime for the

include/dxc/Support/HLSLOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,11 @@ def disable_exception_handling : Flag<["-", "/"], "disable-exception-handling">,
346346
def skip_serialization : Flag<["-", "/"], "skip-serialization">, Group<hlslcore_Group>, Flags<[CoreOption, HelpHidden]>,
347347
HelpText<"Return a module interface instead of serialized output">;
348348

349+
def metal : Flag<["-"], "metal">,
350+
Group<spirv_Group>,
351+
Flags<[CoreOption, DriverOption]>,
352+
HelpText<"Generate Metal code">;
353+
349354
// SPIRV Change Starts
350355
def spirv : Flag<["-"], "spirv">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
351356
HelpText<"Generate SPIR-V code">;

lib/DxcSupport/HLSLOptions.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
10891089

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

1092+
opts.GenMetal = Args.hasFlag(OPT_metal, OPT_INVALID, false);
1093+
10921094
// SPIRV Change Starts
10931095
#ifdef ENABLE_SPIRV_CODEGEN
10941096
opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
@@ -1313,6 +1315,21 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
13131315
#endif // ENABLE_SPIRV_CODEGEN
13141316
// SPIRV Change Ends
13151317

1318+
#ifndef ENABLE_METAL_CODEGEN
1319+
if (opts.GenMetal) {
1320+
errors << "Metal CodeGen not available. "
1321+
"Please rebuild with Metal IR Converter installed.";
1322+
return 1;
1323+
}
1324+
#endif
1325+
1326+
if (opts.GenMetal) {
1327+
if (!opts.AssemblyCode.empty() || opts.OutputObject.empty()) {
1328+
errors << "Disassembly of Metal IR not supported (yet).";
1329+
return 1;
1330+
}
1331+
}
1332+
13161333
// Validation for DebugInfo here because spirv uses same DebugInfo opt,
13171334
// and legacy wrappers will add EmbedDebug in this case, leading to this
13181335
// failing if placed before spirv path sets DebugInfo to true.

tools/clang/test/DXC/metal.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// REQUIRES: metal
2+
3+
// Metal libraries are LLVM bitcode. This check inspects the magic number from
4+
// the metal library output.
5+
// RUN: %dxc %S/Inputs/smoke.hlsl /T ps_6_0 -metal -Fo Tmp.metal
6+
// RUN: head -c 4 Tmp.metal | FileCheck -check-prefix=MTL %s
7+
// MTL: {{^MTLB}}

tools/clang/test/DXC/no_metal.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// UNSUPPORTED: metal
2+
3+
// RUN:not %dxc %S/Inputs/smoke.hlsl /T ps_6_0 -metal 2>&1 | FileCheck %s
4+
// CHECK:Metal CodeGen not available
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// REQUIRES: metal
2+
3+
// These cases both fail because the shader converter library cannot emit
4+
// textual IR.
5+
// RUN: not %dxc %S/Inputs/smoke.hlsl /T ps_6_0 -metal -Fo Tmp.metal -Fc Tmp.air 2>&1 | FileCheck %s
6+
// RUN: not %dxc %S/Inputs/smoke.hlsl /T ps_6_0 -metal 2>&1 | FileCheck %s
7+
// CHECK: Disassembly of Metal IR not supported (yet).

tools/clang/test/lit.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,9 @@ if config.enable_backtrace == "1":
504504
if config.spirv:
505505
config.available_features.add("spirv")
506506

507+
if config.metal:
508+
config.available_features.add("metal")
509+
507510
# Check supported dxil version
508511
def get_dxil_version():
509512
result = subprocess.run([lit.util.which('dxc', llvm_tools_dir), "--version"], stdout=subprocess.PIPE)

tools/clang/test/lit.site.cfg.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ config.enable_backtrace = "@ENABLE_BACKTRACES@"
2222
config.host_arch = "@HOST_ARCH@"
2323
config.spirv = "@ENABLE_SPIRV_CODEGEN@" =="ON"
2424
config.hlsl_headers_dir = "@HLSL_HEADERS_DIR@" # HLSL change
25+
config.metal = "@ENABLE_METAL_CODEGEN@".upper() == "ON" # HLSL change
2526

2627
# Support substitution of the tools and libs dirs with user parameters. This is
2728
# used when we can't determine the tool dir at configuration time.

tools/clang/tools/dxcompiler/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ target_link_libraries(dxcompiler PRIVATE ${LIBRARIES})
136136
if (ENABLE_SPIRV_CODEGEN)
137137
target_link_libraries(dxcompiler PRIVATE clangSPIRV)
138138
endif (ENABLE_SPIRV_CODEGEN)
139+
if (ENABLE_METAL_CODEGEN)
140+
target_link_libraries(dxcompiler PRIVATE ${METAL_IRCONVERTER_LIB})
141+
target_include_directories(dxcompiler PRIVATE ${METAL_IRCONVERTER_INCLUDE_DIR})
142+
143+
get_filename_component(METAL_IRCONVERTER_LIB_DIR ${METAL_IRCONVERTER_LIB} DIRECTORY CACHE)
144+
set_property(TARGET dxcompiler APPEND_STRING
145+
PROPERTY LINK_FLAGS " -Wl,-rpath,${METAL_IRCONVERTER_LIB_DIR}")
146+
endif (ENABLE_METAL_CODEGEN)
139147
include_directories(AFTER ${LLVM_INCLUDE_DIR}/dxc/Tracing ${DIASDK_INCLUDE_DIRS} ${HLSL_VERSION_LOCATION})
140148
include_directories(${LLVM_SOURCE_DIR}/tools/clang/tools/dxcvalidator)
141149

tools/clang/tools/dxcompiler/dxcompilerobj.cpp

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171
#include "clang/Basic/Version.h"
7272
#endif // SUPPORT_QUERY_GIT_COMMIT_INFO
7373

74+
#ifdef ENABLE_METAL_CODEGEN
75+
#include "metal_irconverter.h"
76+
#endif
77+
7478
#define CP_UTF16 1200
7579

7680
using namespace llvm;
@@ -817,6 +821,10 @@ class DxcCompiler : public IDxcCompiler3,
817821
}
818822
compiler.getLangOpts().IsHLSLLibrary = opts.IsLibraryProfile();
819823

824+
if (compiler.getLangOpts().IsHLSLLibrary && opts.GenMetal)
825+
return ErrorWithString("Shader libraries unsupported in Metal (yet)",
826+
riid, ppResult);
827+
820828
// Clear entry function if library target
821829
if (compiler.getLangOpts().IsHLSLLibrary)
822830
compiler.getLangOpts().HLSLEntryFunction =
@@ -1107,7 +1115,86 @@ class DxcCompiler : public IDxcCompiler3,
11071115
&pHashBlob));
11081116
IFT(pResult->SetOutputObject(DXC_OUT_SHADER_HASH, pHashBlob));
11091117
} // SUCCEEDED(valHR)
1110-
} // compileOK && !opts.CodeGenHighLevel
1118+
#ifdef ENABLE_METAL_CODEGEN
1119+
// This is a bit hacky because we don't currently have a good way to
1120+
// disassemble AIR.
1121+
if (opts.GenMetal && produceFullContainer &&
1122+
!opts.OutputObject.empty()) {
1123+
IRCompiler *MetalCompiler = IRCompilerCreate();
1124+
IRCompilerSetEntryPointName(
1125+
MetalCompiler,
1126+
compiler.getCodeGenOpts().HLSLEntryFunction.c_str());
1127+
1128+
IRObject *DXILObj = IRObjectCreateFromDXIL(
1129+
static_cast<const uint8_t *>(pOutputBlob->GetBufferPointer()),
1130+
pOutputBlob->GetBufferSize(), IRBytecodeOwnershipNone);
1131+
1132+
// Compile DXIL to Metal IR:
1133+
IRError *Error = nullptr;
1134+
IRObject *AIR = IRCompilerAllocCompileAndLink(MetalCompiler, NULL,
1135+
DXILObj, &Error);
1136+
1137+
if (!AIR) {
1138+
IRObjectDestroy(DXILObj);
1139+
IRCompilerDestroy(MetalCompiler);
1140+
IRErrorDestroy(Error);
1141+
return ErrorWithString(
1142+
"Error occurred in Metal Shader Conversion", riid, ppResult);
1143+
}
1144+
1145+
IRMetalLibBinary *MetalLib = IRMetalLibBinaryCreate();
1146+
IRShaderStage Stage = IRShaderStageInvalid;
1147+
const ShaderModel *SM = hlsl::ShaderModel::GetByName(
1148+
compiler.getLangOpts().HLSLProfile);
1149+
switch (SM->GetKind()) {
1150+
case DXIL::ShaderKind::Vertex:
1151+
Stage = IRShaderStageVertex;
1152+
break;
1153+
case DXIL::ShaderKind::Pixel:
1154+
Stage = IRShaderStageFragment;
1155+
break;
1156+
case DXIL::ShaderKind::Hull:
1157+
Stage = IRShaderStageHull;
1158+
break;
1159+
case DXIL::ShaderKind::Domain:
1160+
Stage = IRShaderStageDomain;
1161+
break;
1162+
case DXIL::ShaderKind::Mesh:
1163+
Stage = IRShaderStageMesh;
1164+
break;
1165+
case DXIL::ShaderKind::Amplification:
1166+
Stage = IRShaderStageAmplification;
1167+
break;
1168+
case DXIL::ShaderKind::Geometry:
1169+
Stage = IRShaderStageGeometry;
1170+
break;
1171+
case DXIL::ShaderKind::Compute:
1172+
Stage = IRShaderStageCompute;
1173+
break;
1174+
}
1175+
assert(Stage != IRShaderStageInvalid &&
1176+
"Library targets not supported for Metal (yet).");
1177+
IRObjectGetMetalLibBinary(AIR, Stage, MetalLib);
1178+
size_t MetalLibSize = IRMetalLibGetBytecodeSize(MetalLib);
1179+
std::unique_ptr<uint8_t[]> MetalLibBytes =
1180+
std::unique_ptr<uint8_t[]>(new uint8_t[MetalLibSize]);
1181+
IRMetalLibGetBytecode(MetalLib, MetalLibBytes.get());
1182+
1183+
// Store the metallib to custom format or disk, or use to create a
1184+
// MTLLibrary.
1185+
1186+
CComPtr<IDxcBlob> MetalBlob;
1187+
IFT(hlsl::DxcCreateBlobOnHeapCopy(
1188+
MetalLibBytes.get(), (uint32_t)MetalLibSize, &MetalBlob));
1189+
std::swap(pOutputBlob, MetalBlob);
1190+
1191+
IRMetalLibBinaryDestroy(MetalLib);
1192+
IRObjectDestroy(DXILObj);
1193+
IRObjectDestroy(AIR);
1194+
IRCompilerDestroy(MetalCompiler);
1195+
}
1196+
#endif
1197+
} // compileOK && !opts.CodeGenHighLevel
11111198
}
11121199

11131200
std::string remarks;

0 commit comments

Comments
 (0)