Index: llvm/include/llvm/Support/YAMLTraits.h =================================================================== --- llvm/include/llvm/Support/YAMLTraits.h +++ llvm/include/llvm/Support/YAMLTraits.h @@ -915,7 +915,7 @@ yamlize(*this, Val.getValue(), Required, Ctx); this->postflightKey(SaveInfo); } else { - if (UseDefault) + if (!outputting() && UseDefault) Val = DefaultValue; } } @@ -932,7 +932,7 @@ this->postflightKey(SaveInfo); } else { - if ( UseDefault ) + if (!outputting() && UseDefault) Val = DefaultValue; } } @@ -1111,6 +1111,8 @@ bool ScalarMatchFound = false; }; +class PreMappingOutput; + /// /// The Output class is used to generate a yaml document from in-memory structs /// and vectors. @@ -1118,6 +1120,7 @@ class Output : public IO { public: Output(raw_ostream &, void *Ctxt = nullptr, int WrapColumn = 70); + Output(Output& Parent); ~Output() override; /// Set whether or not to output optional values which are equal @@ -1127,6 +1130,14 @@ /// anyway. void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; } + void setOptimizeIndentations(bool Optimize); + + IO *getPreMappingOutput() const; + + void endPremapping(); + + void setMaxPadding(int Val) { MaxPadding = Val; } + bool outputting() const override; bool mapTag(StringRef, bool) override; void beginMapping() override; @@ -1192,6 +1203,7 @@ raw_ostream &Out; int WrapColumn; SmallVector StateStack; + SmallVector MaxPaddingsStack; int Column = 0; int ColumnAtFlowStart = 0; int ColumnAtMapFlowStart = 0; @@ -1199,8 +1211,30 @@ bool NeedFlowSequenceComma = false; bool EnumerationMatchFound = false; bool WriteDefaultValues = false; + int MaxPadding = -1; StringRef Padding; StringRef PaddingBeforeContainer; + std::unique_ptr PreMappingOut; +}; + +class PreMappingOutput final : public Output { + Output &Parent; + size_t MaxKeyLen; + +public: + PreMappingOutput(Output &O) : Output(O), Parent(O), MaxKeyLen(0) {} + + bool preflightKey(const char *Key, bool Required, bool SameAsDefault, bool &, + void *&) override { + if (Required || !SameAsDefault) + MaxKeyLen = std::max(MaxKeyLen, strlen(Key)); + return false; + } + + void endPremapping() { + Parent.setMaxPadding(MaxKeyLen + 1); + MaxKeyLen = 0; + } }; namespace detail { @@ -1210,7 +1244,18 @@ MappingContextTraits::mapping(io, Val, Ctx); } +template void doPreMapping(IO &InOut, T &Val, EmptyContext &Ctx) { + if (!InOut.outputting()) + return; + + Output &Out = (Output &)InOut; + if (IO *O = Out.getPreMappingOutput()) + MappingTraits::mapping(*O, Val); + Out.endPremapping(); +} + template void doMapping(IO &io, T &Val, EmptyContext &Ctx) { + doPreMapping(io, Val, Ctx); MappingTraits::mapping(io, Val); } Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -1575,12 +1575,18 @@ void MappingTraits::mapping(IO &IO, ELFYAML::Object &Object) { assert(!IO.getContext() && "The IO context is initialized already"); IO.setContext(&Object); + if (IO.outputting()) + ((Output &)IO).setOptimizeIndentations(true); + IO.mapTag("!ELF", true); IO.mapRequired("FileHeader", Object.Header); IO.mapOptional("ProgramHeaders", Object.ProgramHeaders); IO.mapOptional("Sections", Object.Chunks); IO.mapOptional("Symbols", Object.Symbols); IO.mapOptional("DynamicSymbols", Object.DynamicSymbols); + + if (IO.outputting()) + ((Output &)IO).setOptimizeIndentations(false); IO.setContext(nullptr); } Index: llvm/lib/Support/YAMLTraits.cpp =================================================================== --- llvm/lib/Support/YAMLTraits.cpp +++ llvm/lib/Support/YAMLTraits.cpp @@ -437,6 +437,8 @@ Output::Output(raw_ostream &yout, void *context, int WrapColumn) : IO(context), Out(yout), WrapColumn(WrapColumn) {} +Output::Output(Output &Parent) : IO(Parent.getContext()), Out(Parent.Out) {} + Output::~Output() = default; bool Output::outputting() const { @@ -447,8 +449,26 @@ StateStack.push_back(inMapFirstKey); PaddingBeforeContainer = Padding; Padding = "\n"; + + if (PreMappingOut) + MaxPaddingsStack.push_back(MaxPadding); +} + +void Output::setOptimizeIndentations(bool Optimize) { + assert((Optimize && !PreMappingOut) || (!Optimize && PreMappingOut)); + if (Optimize) + PreMappingOut = std::make_unique(*this); + else + PreMappingOut.reset(); } +void Output::endPremapping() { + if (PreMappingOut) + PreMappingOut->endPremapping(); +} + +IO *Output::getPreMappingOutput() const { return PreMappingOut.get(); } + bool Output::mapTag(StringRef Tag, bool Use) { if (Use) { // If this tag is being written inside a sequence we should write the start @@ -489,6 +509,9 @@ Padding = "\n"; } StateStack.pop_back(); + + if (PreMappingOut) + MaxPadding = MaxPaddingsStack.pop_back_val(); } std::vector Output::keys() { @@ -806,11 +829,14 @@ void Output::paddedKey(StringRef key) { output(key); output(":"); - const char *spaces = " "; - if (key.size() < strlen(spaces)) - Padding = &spaces[key.size()]; + + static std::string Spaces(16, ' '); + int PaddingLen; + if (MaxPadding == -1) + PaddingLen = std::max(Spaces.size() - key.size(), 1); else - Padding = " "; + PaddingLen = std::min(std::max(MaxPadding - key.size(), 1), Spaces.size()); + Padding = StringRef(Spaces).take_front(PaddingLen); } void Output::flowKey(StringRef Key) { Index: llvm/test/tools/obj2yaml/elf-output-indentation.yaml =================================================================== --- /dev/null +++ llvm/test/tools/obj2yaml/elf-output-indentation.yaml @@ -0,0 +1,79 @@ +## obj2yaml tries to optimize indentations between keys and +## values to make an output nicer. +## In this test we demonstrate the output produced. + +# RUN: yaml2obj %s -o %t +# RUN: obj2yaml %t | FileCheck %s --strict-whitespace --match-full-lines + +# CHECK:--- !ELF +# CHECK-NEXT:FileHeader: +# CHECK-NEXT: Class: ELFCLASS64 +# CHECK-NEXT: Data: ELFDATA2LSB +# CHECK-NEXT: Type: ET_REL +# CHECK-NEXT: Machine: EM_X86_64 +# CHECK-NEXT:Sections: +# CHECK-NEXT: - Name: .text +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: - Name: .data +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: AddressAlign: 0x0000000000000001 +# CHECK-NEXT: - Name: .rela.text +# CHECK-NEXT: Type: SHT_RELA +# CHECK-NEXT: Link: .symtab +# CHECK-NEXT: EntSize: 0x0000000000000018 +# CHECK-NEXT: Info: .text +# CHECK-NEXT: Relocations: +# CHECK-NEXT: - Offset: 0x0000000000000000 +# CHECK-NEXT: Type: R_X86_64_PC32 +# CHECK-NEXT: - Name: .rela.text2 +# CHECK-NEXT: Type: SHT_RELA +# CHECK-NEXT: Link: .symtab +# CHECK-NEXT: EntSize: 0x0000000000000018 +# CHECK-NEXT:Symbols: [] +# CHECK-NEXT:DynamicSymbols: +# CHECK-NEXT: - Name: dynamic +# CHECK-NEXT: Binding: STB_GLOBAL +# CHECK-NEXT: Value: 0x0000000012345678 +# CHECK-NEXT: - Name: both +# CHECK-NEXT: Value: 0x0000000087654321 +# CHECK-NEXT:... + +## Use an arbitrary YAML with different fields. + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .data + Type: SHT_PROGBITS + AddressAlign: 0x1 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + EntSize: 0x0000000000000018 + Info: .text + Relocations: + - Offset: 0x0000000000000000 + Type: R_X86_64_PC32 + - Name: .rela.text2 + Type: SHT_RELA + Link: .symtab + EntSize: 0x0000000000000018 + Relocations: [] +ProgramHeaders: + - Type: PT_LOAD + VAddr: 0x100 + Sections: + - Section: .text +Symbols: [] +DynamicSymbols: + - Name: dynamic + Binding: STB_GLOBAL + Value: 0x0000000012345678 + - Name: both + Value: 0x0000000087654321 Index: llvm/tools/obj2yaml/elf2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/elf2yaml.cpp +++ llvm/tools/obj2yaml/elf2yaml.cpp @@ -1152,6 +1152,7 @@ std::unique_ptr YAML(YAMLOrErr.get()); yaml::Output Yout(Out); + Yout.setMaxPadding(1); Yout << *YAML; return Error::success();