145 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- TensorSpec.cpp - tensor type abstraction ---------------------------===//
 | |
| //
 | |
| // 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
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // Implementation file for the abstraction of a tensor type, and JSON loading
 | |
| // utils.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| #include "llvm/Config/config.h"
 | |
| 
 | |
| #include "llvm/ADT/Twine.h"
 | |
| #include "llvm/Analysis/TensorSpec.h"
 | |
| #include "llvm/Support/CommandLine.h"
 | |
| #include "llvm/Support/Debug.h"
 | |
| #include "llvm/Support/JSON.h"
 | |
| #include "llvm/Support/ManagedStatic.h"
 | |
| #include "llvm/Support/MemoryBuffer.h"
 | |
| #include "llvm/Support/Path.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| #include <cassert>
 | |
| #include <numeric>
 | |
| 
 | |
| using namespace llvm;
 | |
| 
 | |
| namespace llvm {
 | |
| 
 | |
| #define TFUTILS_GETDATATYPE_IMPL(T, E)                                         \
 | |
|   template <> TensorType TensorSpec::getDataType<T>() { return TensorType::E; }
 | |
| 
 | |
| SUPPORTED_TENSOR_TYPES(TFUTILS_GETDATATYPE_IMPL)
 | |
| 
 | |
| #undef TFUTILS_GETDATATYPE_IMPL
 | |
| 
 | |
| TensorSpec::TensorSpec(const std::string &Name, int Port, TensorType Type,
 | |
|                        size_t ElementSize, const std::vector<int64_t> &Shape)
 | |
|     : Name(Name), Port(Port), Type(Type), Shape(Shape),
 | |
|       ElementCount(std::accumulate(Shape.begin(), Shape.end(), 1,
 | |
|                                    std::multiplies<int64_t>())),
 | |
|       ElementSize(ElementSize) {}
 | |
| 
 | |
| Optional<TensorSpec> getTensorSpecFromJSON(LLVMContext &Ctx,
 | |
|                                            const json::Value &Value) {
 | |
|   auto EmitError = [&](const llvm::Twine &Message) -> Optional<TensorSpec> {
 | |
|     std::string S;
 | |
|     llvm::raw_string_ostream OS(S);
 | |
|     OS << Value;
 | |
|     Ctx.emitError("Unable to parse JSON Value as spec (" + Message + "): " + S);
 | |
|     return None;
 | |
|   };
 | |
|   // FIXME: accept a Path as a parameter, and use it for error reporting.
 | |
|   json::Path::Root Root("tensor_spec");
 | |
|   json::ObjectMapper Mapper(Value, Root);
 | |
|   if (!Mapper)
 | |
|     return EmitError("Value is not a dict");
 | |
| 
 | |
|   std::string TensorName;
 | |
|   int TensorPort = -1;
 | |
|   std::string TensorType;
 | |
|   std::vector<int64_t> TensorShape;
 | |
| 
 | |
|   if (!Mapper.map<std::string>("name", TensorName))
 | |
|     return EmitError("'name' property not present or not a string");
 | |
|   if (!Mapper.map<std::string>("type", TensorType))
 | |
|     return EmitError("'type' property not present or not a string");
 | |
|   if (!Mapper.map<int>("port", TensorPort))
 | |
|     return EmitError("'port' property not present or not an int");
 | |
|   if (!Mapper.map<std::vector<int64_t>>("shape", TensorShape))
 | |
|     return EmitError("'shape' property not present or not an int array");
 | |
| 
 | |
| #define PARSE_TYPE(T, E)                                                       \
 | |
|   if (TensorType == #T)                                                        \
 | |
|     return TensorSpec::createSpec<T>(TensorName, TensorShape, TensorPort);
 | |
|   SUPPORTED_TENSOR_TYPES(PARSE_TYPE)
 | |
| #undef PARSE_TYPE
 | |
|   return None;
 | |
| }
 | |
| 
 | |
| Optional<std::vector<LoggedFeatureSpec>>
 | |
| loadOutputSpecs(LLVMContext &Ctx, StringRef ExpectedDecisionName,
 | |
|                 StringRef ModelPath, StringRef SpecFileOverride) {
 | |
|   SmallVector<char, 128> OutputSpecsPath;
 | |
|   StringRef FileName = SpecFileOverride;
 | |
|   if (FileName.empty()) {
 | |
|     llvm::sys::path::append(OutputSpecsPath, ModelPath, "output_spec.json");
 | |
|     FileName = {OutputSpecsPath.data(), OutputSpecsPath.size()};
 | |
|   }
 | |
| 
 | |
|   auto BufferOrError = MemoryBuffer::getFileOrSTDIN(FileName);
 | |
|   if (!BufferOrError) {
 | |
|     Ctx.emitError("Error opening output specs file: " + FileName + " : " +
 | |
|                   BufferOrError.getError().message());
 | |
|     return None;
 | |
|   }
 | |
|   auto ParsedJSONValues = json::parse(BufferOrError.get()->getBuffer());
 | |
|   if (!ParsedJSONValues) {
 | |
|     Ctx.emitError("Could not parse specs file: " + FileName);
 | |
|     return None;
 | |
|   }
 | |
|   auto ValuesArray = ParsedJSONValues->getAsArray();
 | |
|   if (!ValuesArray) {
 | |
|     Ctx.emitError("Expected an array of {tensor_spec:<TensorSpec>, "
 | |
|                   "logging_name:<name>} dictionaries");
 | |
|     return None;
 | |
|   }
 | |
|   std::vector<LoggedFeatureSpec> Ret;
 | |
|   for (const auto &Value : *ValuesArray)
 | |
|     if (const auto *Obj = Value.getAsObject())
 | |
|       if (const auto *SpecPart = Obj->get("tensor_spec"))
 | |
|         if (auto TensorSpec = getTensorSpecFromJSON(Ctx, *SpecPart))
 | |
|           if (auto LoggingName = Obj->getString("logging_name")) {
 | |
|             if (!TensorSpec->isElementType<int64_t>() &&
 | |
|                 !TensorSpec->isElementType<int32_t>() &&
 | |
|                 !TensorSpec->isElementType<float>()) {
 | |
|               Ctx.emitError(
 | |
|                   "Only int64, int32, and float tensors are supported. "
 | |
|                   "Found unsupported type for tensor named " +
 | |
|                   TensorSpec->name());
 | |
|               return None;
 | |
|             }
 | |
|             Ret.push_back({*TensorSpec, LoggingName->str()});
 | |
|           }
 | |
| 
 | |
|   if (ValuesArray->size() != Ret.size()) {
 | |
|     Ctx.emitError(
 | |
|         "Unable to parse output spec. It should be a json file containing an "
 | |
|         "array of dictionaries. Each dictionary must have a 'tensor_spec' key, "
 | |
|         "with a json object describing a TensorSpec; and a 'logging_name' key, "
 | |
|         "which is a string to use as name when logging this tensor in the "
 | |
|         "training log.");
 | |
|     return None;
 | |
|   }
 | |
|   if (Ret.empty() || *Ret[0].LoggingName != ExpectedDecisionName) {
 | |
|     Ctx.emitError("The first output spec must describe the decision tensor, "
 | |
|                   "and must have the logging_name " +
 | |
|                   StringRef(ExpectedDecisionName));
 | |
|     return None;
 | |
|   }
 | |
|   return Ret;
 | |
| }
 | |
| } // namespace llvm
 |