|  | // 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 |