| // Copyright 2023 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "compiler/plugins/target/MetalSPIRV/MSLToMetalLib.h" |
| |
| #include <stdlib.h> |
| |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/FileUtilities.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "mlir/Support/LogicalResult.h" |
| |
| #define DEBUG_TYPE "iree-msl-to-metal-lib" |
| |
| namespace mlir::iree_compiler::IREE::HAL { |
| |
| /// Returns the command to compile the given MSL source file into Metal library. |
| static std::string getMetalCompileCommand(MetalTargetPlatform platform, |
| llvm::StringRef mslFile, |
| llvm::StringRef libFile) { |
| const char *sdk = ""; |
| switch (platform) { |
| case MetalTargetPlatform::macOS: |
| sdk = "macosx"; |
| break; |
| case MetalTargetPlatform::iOS: |
| sdk = "iphoneos"; |
| break; |
| case MetalTargetPlatform::iOSSimulator: |
| sdk = "iphonesimulator"; |
| break; |
| } |
| |
| // Metal shader offline compilation involves two steps: |
| // 1. Compile the MSL source code into an Apple IR (AIR) file, |
| // 2. Compile the AIR file into a Metal library. |
| return llvm::Twine("xcrun -sdk ") |
| .concat(sdk) |
| .concat(" metal -c ") |
| .concat(mslFile) |
| .concat(" -o - | xcrun -sdk ") |
| .concat(sdk) |
| .concat(" metallib - -o ") |
| .concat(libFile) |
| .str(); |
| } |
| |
| /// Returns the given command via system shell. |
| static LogicalResult runSystemCommand(llvm::StringRef command) { |
| LLVM_DEBUG(llvm::dbgs() << "Running system command: '" << command << "'\n"); |
| int exitCode = system(command.data()); |
| if (exitCode == 0) |
| return success(); |
| llvm::errs() << "Failed to run system command '" << command |
| << "' with error code: " << exitCode << "\n"; |
| return failure(); |
| } |
| |
| std::unique_ptr<llvm::MemoryBuffer> |
| compileMSLToMetalLib(MetalTargetPlatform targetPlatform, |
| llvm::StringRef mslCode, llvm::StringRef entryPoint) { |
| llvm::SmallString<32> mslFile, airFile, libFile; |
| int mslFd = 0; |
| llvm::sys::fs::createTemporaryFile(entryPoint, "metal", mslFd, mslFile); |
| llvm::sys::fs::createTemporaryFile(entryPoint, "metallib", libFile); |
| llvm::FileRemover mslRemover(mslFile.c_str()); |
| llvm::FileRemover libRemover(libFile.c_str()); |
| |
| { // Write input MSL code to the temporary file. |
| llvm::raw_fd_ostream inputStream(mslFd, /*shouldClose=*/true); |
| inputStream << mslCode << "\n"; |
| } |
| |
| std::string command = |
| getMetalCompileCommand(targetPlatform, mslFile, libFile); |
| if (failed(runSystemCommand(command))) |
| return nullptr; |
| |
| auto fileOrErr = |
| llvm::MemoryBuffer::getFileOrSTDIN(libFile, /*isText=*/false); |
| if (std::error_code error = fileOrErr.getError()) { |
| llvm::errs() << "Failed to open generated metallib file '" << libFile |
| << "' with error: " << error.message(); |
| return nullptr; |
| } |
| |
| return std::move(*fileOrErr); |
| } |
| |
| } // namespace mlir::iree_compiler::IREE::HAL |