diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3509,6 +3509,35 @@ source_filename = "/path/to/source.c" +.. _structured_data: + +Structured Data +--------------- + +Dictionaries of key-value pairs are used in some cases to represent data in an +easily extendable, human-readable manner. + +The labels used in key-value pairs are identifiers followed immediately by a +colon (':'), like the label of a named basic block. + +:Syntax: + +:: + + sdata ::= '{' (sdata_field ',')* sdata_field? '}' + sdata_field ::= label sdata_value + sdata_value ::= 'type' type + ::= 'iN' integer + ::= 'i1' 'true' | 'i1' 'false' + +:Examples: + +:: + + {} + { layout: type float, } + { foo: i1 true, bar: i32 10 } + .. _typesystem: Type System diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -20,6 +20,7 @@ #include "llvm/IR/FMF.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/ModuleSummaryIndex.h" +#include "llvm/IR/StructuredData.h" #include #include @@ -558,6 +559,10 @@ bool parseGlobalObjectMetadataAttachment(GlobalObject &GO); bool parseOptionalFunctionMetadata(Function &F); + bool parseStructuredData( + function_ref + ParseField); + template bool parseMDField(LocTy Loc, StringRef Name, FieldTy &Result); template bool parseMDField(StringRef Name, FieldTy &Result); diff --git a/llvm/include/llvm/IR/StructuredData.h b/llvm/include/llvm/IR/StructuredData.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/StructuredData.h @@ -0,0 +1,83 @@ +//===- llvm/IR/StructuredData.h ---------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides structured data objects that are used as an intermediate +// abstraction for (de)serializing extensible IR objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_STRUCTUREDDATA_H +#define LLVM_IR_STRUCTUREDDATA_H + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +class Type; + +namespace sdata { + +/// A value of structured data. +class Value { +private: + using Storage = std::variant; + + Storage S; + +public: + Value() = default; + explicit Value(Type *T) : S(T) {} + explicit Value(bool B) : S(APInt(1, B ? 1 : 0)) {} + explicit Value(APInt I) : S(I) {} + + Value &operator=(Type *T) { + assert(T); + S = T; + return *this; + } + Value &operator=(bool B) { + S = APInt(1, B ? 1 : 0); + return *this; + } + Value &operator=(APInt I) { + S = I; + return *this; + } + + bool isAPInt() const { return std::holds_alternative(S); } + bool isBool() const { + return isAPInt() && std::get(S).getBitWidth() == 1; + } + bool isType() const { return std::holds_alternative(S); } + + const APInt &getAPInt() const { + assert(isAPInt()); + return std::get(S); + } + bool getBool() const { + assert(isBool()); + return std::get(S).getZExtValue(); + } + Type *getType() const { + assert(isType()); + return std::get(S); + } +}; + +// Convenience function to create an Error object when an error is encountered +// while deserializing structured data. +Error makeDeserializeError(const Twine &Msg); + +} // end namespace sdata + +} // end namespace llvm + +#endif diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -4190,6 +4190,82 @@ return false; } +/// parseStructuredData +/// ::= '{' (key value (',' key value))? ','? '}' +/// +/// value ::= 'type' type +/// ::= 'i1' 'true' | 'i1' 'false' +/// ::= 'iN' integer +bool LLParser::parseStructuredData( + function_ref ParseField) { + if (parseToken(lltok::lbrace, "expected '{' here")) + return true; + + while (Lex.getKind() != lltok::rbrace) { + if (Lex.getKind() != lltok::LabelStr) + return tokError("expected '}' or field label here"); + + LocTy KeyLoc = Lex.getLoc(); + std::string Key = Lex.getStrVal(); + Lex.Lex(); + + LocTy ValueLoc = Lex.getLoc(); + sdata::Value V; + switch (Lex.getKind()) { + case lltok::kw_type: { + Lex.Lex(); // eat 'type' + + Type *T; + if (parseType(T, /*AllowVoid=*/true)) + return true; + + V = sdata::Value(T); + break; + } + case lltok::Type: { + Type *Ty = Lex.getTyVal(); + if (auto *IntTy = dyn_cast(Ty)) { + Lex.Lex(); + + switch (Lex.getKind()) { + case lltok::APSInt: + V = sdata::Value(Lex.getAPSIntVal().extOrTrunc(IntTy->getBitWidth())); + Lex.Lex(); + break; + case lltok::kw_true: + case lltok::kw_false: + if (IntTy->getBitWidth() != 1) + return tokError("true/false can only be used with i1"); + V = sdata::Value(Lex.getKind() == lltok::kw_true); + Lex.Lex(); + break; + default: + return tokError("expected an integer value"); + } + + break; + } + + return tokError("only integer types are supported in structured data"); + } + + default: + return tokError("expected structured data value"); + } + + if (ParseField(KeyLoc, Key, ValueLoc, V)) + return true; + + if (Lex.getKind() == lltok::rbrace) + break; + if (parseToken(lltok::comma, "expected ',' or '}' here")) + return true; + } + + Lex.Lex(); // eat the '}' + return false; +} + bool LLParser::parseMDTuple(MDNode *&MD, bool IsDistinct) { SmallVector Elts; if (parseMDNodeVector(Elts)) diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -58,6 +58,7 @@ #include "llvm/IR/ModuleSlotTracker.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Operator.h" +#include "llvm/IR/StructuredData.h" #include "llvm/IR/Type.h" #include "llvm/IR/TypeFinder.h" #include "llvm/IR/TypedPointerType.h" @@ -1323,6 +1324,31 @@ // AsmWriter Implementation //===----------------------------------------------------------------------===// +static void printStructuredData( + raw_ostream &Out, ArrayRef> Fields, + AsmWriterContext &Ctx) { + Out << "{\n"; + for (const auto &Field : Fields) { + Out << " " << Field.first << ": "; + if (Field.second.isBool()) { + if (Field.second.getBool()) + Out << "i1 true"; + else + Out << "i1 false"; + } else if (Field.second.isAPInt()) { + const APInt &I = Field.second.getAPInt(); + Out << 'i' << I.getBitWidth() << ' ' << I; + } else if (Field.second.isType()) { + Out << "type "; + Ctx.TypePrinter->print(Field.second.getType(), Out); + } else { + llvm_unreachable("unhandled sdata::Value type"); + } + Out << ",\n"; + } + Out << '}'; +} + static void WriteAsOperandInternal(raw_ostream &Out, const Value *V, AsmWriterContext &WriterCtx); diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -57,6 +57,7 @@ ReplaceConstant.cpp Statepoint.cpp StructuralHash.cpp + StructuredData.cpp Type.cpp TypedPointerType.cpp TypeFinder.cpp diff --git a/llvm/lib/IR/StructuredData.cpp b/llvm/lib/IR/StructuredData.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/IR/StructuredData.cpp @@ -0,0 +1,43 @@ +//===- StructuredData.cpp -------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/StructuredData.h" + +using namespace llvm; +using namespace sdata; + +namespace { + +enum class DeserializeErrorCode : int { + Generic = 1, +}; + +class DeserializeErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { + return "Structure Data Deserialize Error"; + } + + std::string message(int condition) const override { + return "Error while deserializing structured data"; + } + + static DeserializeErrorCategory &get() { + static DeserializeErrorCategory TheCategory; + return TheCategory; + } +}; + +} // anonymous namespace + +Error llvm::sdata::makeDeserializeError(const Twine &Msg) { + return createStringError( + std::error_code(static_cast(DeserializeErrorCode::Generic), + DeserializeErrorCategory::get()), + Msg); +}