191 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
//===- DXContainerEmitter.cpp - Convert YAML to a DXContainer -------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, 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
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
///
 | 
						|
/// \file
 | 
						|
/// Binary emitter for yaml to DXContainer binary
 | 
						|
///
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "llvm/BinaryFormat/DXContainer.h"
 | 
						|
#include "llvm/ObjectYAML/ObjectYAML.h"
 | 
						|
#include "llvm/ObjectYAML/yaml2obj.h"
 | 
						|
#include "llvm/Support/Errc.h"
 | 
						|
#include "llvm/Support/Error.h"
 | 
						|
#include "llvm/Support/raw_ostream.h"
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
namespace {
 | 
						|
class DXContainerWriter {
 | 
						|
public:
 | 
						|
  DXContainerWriter(DXContainerYAML::Object &ObjectFile)
 | 
						|
      : ObjectFile(ObjectFile) {}
 | 
						|
 | 
						|
  Error write(raw_ostream &OS);
 | 
						|
 | 
						|
private:
 | 
						|
  DXContainerYAML::Object &ObjectFile;
 | 
						|
 | 
						|
  Error computePartOffsets();
 | 
						|
  Error validatePartOffsets();
 | 
						|
  Error validateSize(uint32_t Computed);
 | 
						|
 | 
						|
  void writeHeader(raw_ostream &OS);
 | 
						|
  void writeParts(raw_ostream &OS);
 | 
						|
};
 | 
						|
} // namespace
 | 
						|
 | 
						|
Error DXContainerWriter::validateSize(uint32_t Computed) {
 | 
						|
  if (!ObjectFile.Header.FileSize)
 | 
						|
    ObjectFile.Header.FileSize = Computed;
 | 
						|
  else if (*ObjectFile.Header.FileSize < Computed)
 | 
						|
    return createStringError(errc::result_out_of_range,
 | 
						|
                             "File size specified is too small.");
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
Error DXContainerWriter::validatePartOffsets() {
 | 
						|
  if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size())
 | 
						|
    return createStringError(
 | 
						|
        errc::invalid_argument,
 | 
						|
        "Mismatch between number of parts and part offsets.");
 | 
						|
  uint32_t RollingOffset =
 | 
						|
      sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
 | 
						|
  for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
 | 
						|
    if (RollingOffset > std::get<1>(I))
 | 
						|
      return createStringError(errc::invalid_argument,
 | 
						|
                               "Offset mismatch, not enough space for data.");
 | 
						|
    RollingOffset =
 | 
						|
        std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size;
 | 
						|
  }
 | 
						|
  if (Error Err = validateSize(RollingOffset))
 | 
						|
    return Err;
 | 
						|
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
Error DXContainerWriter::computePartOffsets() {
 | 
						|
  if (ObjectFile.Header.PartOffsets)
 | 
						|
    return validatePartOffsets();
 | 
						|
  uint32_t RollingOffset =
 | 
						|
      sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
 | 
						|
  ObjectFile.Header.PartOffsets = std::vector<uint32_t>();
 | 
						|
  for (const auto &Part : ObjectFile.Parts) {
 | 
						|
    ObjectFile.Header.PartOffsets->push_back(RollingOffset);
 | 
						|
    RollingOffset += sizeof(dxbc::PartHeader) + Part.Size;
 | 
						|
  }
 | 
						|
  if (Error Err = validateSize(RollingOffset))
 | 
						|
    return Err;
 | 
						|
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
void DXContainerWriter::writeHeader(raw_ostream &OS) {
 | 
						|
  dxbc::Header Header;
 | 
						|
  memcpy(Header.Magic, "DXBC", 4);
 | 
						|
  memcpy(Header.FileHash.Digest, ObjectFile.Header.Hash.data(), 16);
 | 
						|
  Header.Version.Major = ObjectFile.Header.Version.Major;
 | 
						|
  Header.Version.Minor = ObjectFile.Header.Version.Minor;
 | 
						|
  Header.FileSize = *ObjectFile.Header.FileSize;
 | 
						|
  Header.PartCount = ObjectFile.Parts.size();
 | 
						|
  if (sys::IsBigEndianHost)
 | 
						|
    Header.swapBytes();
 | 
						|
  OS.write(reinterpret_cast<char *>(&Header), sizeof(Header));
 | 
						|
  SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(),
 | 
						|
                                ObjectFile.Header.PartOffsets->end());
 | 
						|
  if (sys::IsBigEndianHost)
 | 
						|
    for (auto &O : Offsets)
 | 
						|
      sys::swapByteOrder(O);
 | 
						|
  OS.write(reinterpret_cast<char *>(Offsets.data()),
 | 
						|
           Offsets.size() * sizeof(uint32_t));
 | 
						|
}
 | 
						|
 | 
						|
void DXContainerWriter::writeParts(raw_ostream &OS) {
 | 
						|
  uint32_t RollingOffset =
 | 
						|
      sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
 | 
						|
  for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
 | 
						|
    if (RollingOffset < std::get<1>(I)) {
 | 
						|
      uint32_t PadBytes = std::get<1>(I) - RollingOffset;
 | 
						|
      OS.write_zeros(PadBytes);
 | 
						|
    }
 | 
						|
    DXContainerYAML::Part P = std::get<0>(I);
 | 
						|
    OS.write(P.Name.c_str(), 4);
 | 
						|
    if (sys::IsBigEndianHost)
 | 
						|
      sys::swapByteOrder(P.Size);
 | 
						|
    OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t));
 | 
						|
    RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader);
 | 
						|
 | 
						|
    if (P.Name == "DXIL" && P.Program) {
 | 
						|
      dxbc::ProgramHeader Header;
 | 
						|
      Header.MajorVersion = P.Program->MajorVersion;
 | 
						|
      Header.MinorVersion = P.Program->MinorVersion;
 | 
						|
      Header.Unused = 0;
 | 
						|
      Header.ShaderKind = P.Program->ShaderKind;
 | 
						|
      memcpy(Header.Bitcode.Magic, "DXIL", 4);
 | 
						|
      Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion;
 | 
						|
      Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion;
 | 
						|
      Header.Bitcode.Unused = 0;
 | 
						|
 | 
						|
      // Compute the optional fields if needed...
 | 
						|
      if (P.Program->DXILOffset)
 | 
						|
        Header.Bitcode.Offset = P.Program->DXILOffset.getValue();
 | 
						|
      else
 | 
						|
        Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader);
 | 
						|
 | 
						|
      if (P.Program->DXILSize)
 | 
						|
        Header.Bitcode.Size = P.Program->DXILSize.getValue();
 | 
						|
      else
 | 
						|
        Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0;
 | 
						|
 | 
						|
      if (P.Program->Size)
 | 
						|
        Header.Size = P.Program->Size.getValue();
 | 
						|
      else
 | 
						|
        Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size;
 | 
						|
 | 
						|
      uint32_t BitcodeOffset = Header.Bitcode.Offset;
 | 
						|
      if (sys::IsBigEndianHost)
 | 
						|
        Header.swapBytes();
 | 
						|
      OS.write(reinterpret_cast<const char *>(&Header),
 | 
						|
               sizeof(dxbc::ProgramHeader));
 | 
						|
      if (P.Program->DXIL) {
 | 
						|
        if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) {
 | 
						|
          uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader);
 | 
						|
          OS.write_zeros(PadBytes);
 | 
						|
        }
 | 
						|
        OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()),
 | 
						|
                 P.Program->DXIL->size());
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Error DXContainerWriter::write(raw_ostream &OS) {
 | 
						|
  if (Error Err = computePartOffsets())
 | 
						|
    return Err;
 | 
						|
  writeHeader(OS);
 | 
						|
  writeParts(OS);
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
namespace llvm {
 | 
						|
namespace yaml {
 | 
						|
 | 
						|
bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,
 | 
						|
                      ErrorHandler EH) {
 | 
						|
  DXContainerWriter Writer(Doc);
 | 
						|
  if (Error Err = Writer.write(Out)) {
 | 
						|
    handleAllErrors(std::move(Err),
 | 
						|
                    [&](const ErrorInfoBase &Err) { EH(Err.message()); });
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace yaml
 | 
						|
} // namespace llvm
 |