| 1 | //===- SystemZHLASMAsmStreamer.cpp - HLASM Assembly Text Output -----------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "SystemZHLASMAsmStreamer.h" |
| 10 | #include "llvm/ADT/StringExtras.h" |
| 11 | #include "llvm/BinaryFormat/GOFF.h" |
| 12 | #include "llvm/MC/MCExpr.h" |
| 13 | #include "llvm/MC/MCGOFFAttributes.h" |
| 14 | #include "llvm/MC/MCGOFFStreamer.h" |
| 15 | #include "llvm/MC/MCSymbolGOFF.h" |
| 16 | #include "llvm/Support/Casting.h" |
| 17 | #include "llvm/Support/Signals.h" |
| 18 | #include <sstream> |
| 19 | |
| 20 | using namespace llvm; |
| 21 | |
| 22 | void SystemZHLASMAsmStreamer::visitUsedSymbol(const MCSymbol &Sym) { |
| 23 | Assembler->registerSymbol(Symbol: Sym); |
| 24 | } |
| 25 | |
| 26 | void SystemZHLASMAsmStreamer::EmitEOL() { |
| 27 | // Comments are emitted on a new line before the instruction. |
| 28 | if (IsVerboseAsm) |
| 29 | EmitComment(); |
| 30 | |
| 31 | std::istringstream Stream(Str); |
| 32 | SmallVector<std::string> Lines; |
| 33 | std::string Line; |
| 34 | while (std::getline(in&: Stream, str&: Line, delim: '\n')) |
| 35 | Lines.push_back(Elt: Line); |
| 36 | |
| 37 | for (auto S : Lines) { |
| 38 | if (LLVM_LIKELY(S.length() < ContIndicatorColumn)) { |
| 39 | FOS << S; |
| 40 | // Each line in HLASM must fill the full 80 characters. |
| 41 | FOS.PadToColumn(NewCol: InstLimit); |
| 42 | FOS << "\n" ; |
| 43 | } else { |
| 44 | // If last character before end of the line is not a space |
| 45 | // we must insert an additional non-space character that |
| 46 | // is not part of the statement coding. We just reuse |
| 47 | // the existing character by making the new substring start |
| 48 | // 1 character sooner, thus "duplicating" that character |
| 49 | // If The last character is a space. We insert an X instead. |
| 50 | std::string TmpSubStr = S.substr(pos: 0, n: ContIndicatorColumn); |
| 51 | if (!TmpSubStr.compare(pos: ContIndicatorColumn - 1, n1: 1, s: " " )) |
| 52 | TmpSubStr.replace(pos: ContIndicatorColumn - 1, n1: 1, s: "X" ); |
| 53 | |
| 54 | FOS << TmpSubStr; |
| 55 | FOS.PadToColumn(NewCol: InstLimit); |
| 56 | FOS << "\n" ; |
| 57 | |
| 58 | size_t Emitted = ContIndicatorColumn - 1; |
| 59 | |
| 60 | while (Emitted < S.length()) { |
| 61 | if ((S.length() - Emitted) < ContLen) |
| 62 | TmpSubStr = S.substr(pos: Emitted, n: S.length()); |
| 63 | else { |
| 64 | TmpSubStr = S.substr(pos: Emitted, n: ContLen); |
| 65 | if (!TmpSubStr.compare(pos: ContLen - 1, n1: 1, s: " " )) |
| 66 | TmpSubStr.replace(pos: ContLen - 1, n1: 1, s: "X" ); |
| 67 | } |
| 68 | FOS.PadToColumn(NewCol: ContStartColumn); |
| 69 | FOS << TmpSubStr; |
| 70 | FOS.PadToColumn(NewCol: InstLimit); |
| 71 | FOS << "\n" ; |
| 72 | Emitted += ContLen - 1; |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | Str.clear(); |
| 77 | } |
| 78 | |
| 79 | void SystemZHLASMAsmStreamer::changeSection(MCSection *Section, |
| 80 | uint32_t Subsection) { |
| 81 | MAI->printSwitchToSection(*Section, Subsection, |
| 82 | getContext().getTargetTriple(), OS); |
| 83 | MCStreamer::changeSection(Section, Subsection); |
| 84 | EmitEOL(); |
| 85 | } |
| 86 | |
| 87 | void SystemZHLASMAsmStreamer::emitAlignmentDS(uint64_t ByteAlignment, |
| 88 | std::optional<int64_t> Value, |
| 89 | unsigned ValueSize, |
| 90 | unsigned MaxBytesToEmit) { |
| 91 | if (!isPowerOf2_64(Value: ByteAlignment)) |
| 92 | report_fatal_error(reason: "Only power-of-two alignments are supported " ); |
| 93 | |
| 94 | OS << " DS 0" ; |
| 95 | switch (ValueSize) { |
| 96 | default: |
| 97 | llvm_unreachable("Invalid size for machine code value!" ); |
| 98 | case 1: |
| 99 | OS << "B" ; |
| 100 | break; |
| 101 | case 2: |
| 102 | OS << "H" ; |
| 103 | break; |
| 104 | case 4: |
| 105 | OS << "F" ; |
| 106 | break; |
| 107 | case 8: |
| 108 | OS << "D" ; |
| 109 | break; |
| 110 | case 16: |
| 111 | OS << "Q" ; |
| 112 | break; |
| 113 | } |
| 114 | |
| 115 | EmitEOL(); |
| 116 | } |
| 117 | |
| 118 | void SystemZHLASMAsmStreamer::(const Twine &T, bool EOL) { |
| 119 | if (!IsVerboseAsm) |
| 120 | return; |
| 121 | |
| 122 | T.toVector(Out&: CommentToEmit); |
| 123 | |
| 124 | if (EOL) |
| 125 | CommentToEmit.push_back(Elt: '\n'); // Place comment in a new line. |
| 126 | } |
| 127 | |
| 128 | void SystemZHLASMAsmStreamer::() { |
| 129 | if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0) |
| 130 | return; |
| 131 | |
| 132 | StringRef = CommentToEmit; |
| 133 | |
| 134 | assert(Comments.back() == '\n' && "Comment array not newline terminated" ); |
| 135 | do { |
| 136 | // Emit a line of comments, but not exceeding 80 characters. |
| 137 | size_t Position = std::min(a: InstLimit - 2, b: Comments.find(C: '\n')); |
| 138 | FOS << MAI->getCommentString() << ' ' << Comments.substr(Start: 0, N: Position) |
| 139 | << '\n'; |
| 140 | |
| 141 | if (Comments[Position] == '\n') |
| 142 | Position++; |
| 143 | Comments = Comments.substr(Start: Position); |
| 144 | } while (!Comments.empty()); |
| 145 | |
| 146 | CommentToEmit.clear(); |
| 147 | } |
| 148 | |
| 149 | void SystemZHLASMAsmStreamer::emitValueToAlignment(Align Alignment, |
| 150 | int64_t Fill, |
| 151 | uint8_t FillLen, |
| 152 | unsigned MaxBytesToEmit) { |
| 153 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: Fill, ValueSize: FillLen, MaxBytesToEmit); |
| 154 | } |
| 155 | |
| 156 | void SystemZHLASMAsmStreamer::emitCodeAlignment(Align Alignment, |
| 157 | const MCSubtargetInfo *STI, |
| 158 | unsigned MaxBytesToEmit) { |
| 159 | // Emit with a text fill value. |
| 160 | if (MAI->getTextAlignFillValue()) |
| 161 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: MAI->getTextAlignFillValue(), ValueSize: 1, |
| 162 | MaxBytesToEmit); |
| 163 | else |
| 164 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: std::nullopt, ValueSize: 1, MaxBytesToEmit); |
| 165 | } |
| 166 | |
| 167 | void SystemZHLASMAsmStreamer::emitBytes(StringRef Data) { |
| 168 | assert(getCurrentSectionOnly() && |
| 169 | "Cannot emit contents before setting section!" ); |
| 170 | if (Data.empty()) |
| 171 | return; |
| 172 | |
| 173 | OS << " DC " ; |
| 174 | size_t Len = Data.size(); |
| 175 | SmallVector<uint8_t> Chars; |
| 176 | Chars.resize(N: Len); |
| 177 | OS << "XL" << Len; |
| 178 | uint32_t Index = 0; |
| 179 | for (uint8_t C : Data) { |
| 180 | Chars[Index] = C; |
| 181 | Index++; |
| 182 | } |
| 183 | |
| 184 | OS << '\'' << toHex(Input: Chars) << '\''; |
| 185 | |
| 186 | EmitEOL(); |
| 187 | } |
| 188 | |
| 189 | void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst, |
| 190 | const MCSubtargetInfo &STI) { |
| 191 | |
| 192 | InstPrinter->printInst(MI: &Inst, Address: 0, Annot: "" , STI, OS); |
| 193 | EmitEOL(); |
| 194 | } |
| 195 | |
| 196 | static void emitXATTR(raw_ostream &OS, StringRef Name, |
| 197 | GOFF::ESDLinkageType Linkage, |
| 198 | GOFF::ESDExecutable Executable, |
| 199 | GOFF::ESDBindingScope BindingScope) { |
| 200 | llvm::ListSeparator Sep("," ); |
| 201 | OS << Name << " XATTR " ; |
| 202 | OS << Sep << "LINKAGE(" << (Linkage == GOFF::ESD_LT_OS ? "OS" : "XPLINK" ) |
| 203 | << ")" ; |
| 204 | if (Executable != GOFF::ESD_EXE_Unspecified) |
| 205 | OS << Sep << "REFERENCE(" |
| 206 | << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA" ) << ")" ; |
| 207 | if (BindingScope != GOFF::ESD_BSC_Unspecified) { |
| 208 | OS << Sep << "SCOPE(" ; |
| 209 | switch (BindingScope) { |
| 210 | case GOFF::ESD_BSC_Section: |
| 211 | OS << "SECTION" ; |
| 212 | break; |
| 213 | case GOFF::ESD_BSC_Module: |
| 214 | OS << "MODULE" ; |
| 215 | break; |
| 216 | case GOFF::ESD_BSC_Library: |
| 217 | OS << "LIBRARY" ; |
| 218 | break; |
| 219 | case GOFF::ESD_BSC_ImportExport: |
| 220 | OS << "EXPORT" ; |
| 221 | break; |
| 222 | default: |
| 223 | break; |
| 224 | } |
| 225 | OS << ')'; |
| 226 | } |
| 227 | OS << '\n'; |
| 228 | } |
| 229 | |
| 230 | void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { |
| 231 | MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol); |
| 232 | |
| 233 | MCStreamer::emitLabel(Symbol: Sym, Loc); |
| 234 | |
| 235 | // Emit label and ENTRY statement only if not implied by CSECT. Do not emit a |
| 236 | // label if the symbol is on a PR section. |
| 237 | bool EmitLabelAndEntry = |
| 238 | !static_cast<MCSectionGOFF *>(getCurrentSectionOnly())->isPR(); |
| 239 | if (!Sym->isTemporary() && Sym->isInEDSection()) { |
| 240 | EmitLabelAndEntry = |
| 241 | Sym->getName() != |
| 242 | static_cast<MCSectionGOFF &>(Sym->getSection()).getParent()->getName(); |
| 243 | if (EmitLabelAndEntry) { |
| 244 | OS << " ENTRY " << Sym->getName(); |
| 245 | EmitEOL(); |
| 246 | } |
| 247 | |
| 248 | emitXATTR(OS, Name: Sym->getName(), Linkage: Sym->getLinkage(), Executable: Sym->getCodeData(), |
| 249 | BindingScope: Sym->getBindingScope()); |
| 250 | EmitEOL(); |
| 251 | } |
| 252 | |
| 253 | if (EmitLabelAndEntry) { |
| 254 | OS << Sym->getName() << " DS 0H" ; |
| 255 | EmitEOL(); |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym, |
| 260 | MCSymbolAttr Attribute) { |
| 261 | return static_cast<MCSymbolGOFF *>(Sym)->setSymbolAttribute(Attribute); |
| 262 | } |
| 263 | |
| 264 | void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) { |
| 265 | String.consume_back(Suffix: "\n" ); |
| 266 | OS << String; |
| 267 | EmitEOL(); |
| 268 | } |
| 269 | |
| 270 | // Slight duplicate of MCExpr::print due to HLASM only recognizing limited |
| 271 | // arithmetic operators (+-*/). |
| 272 | void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value, |
| 273 | unsigned Size, bool Parens) { |
| 274 | switch (Value->getKind()) { |
| 275 | case MCExpr::Constant: { |
| 276 | OS << "XL" << Size << '\''; |
| 277 | MAI->printExpr(OS, *Value); |
| 278 | OS << '\''; |
| 279 | return; |
| 280 | } |
| 281 | case MCExpr::Binary: { |
| 282 | const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value); |
| 283 | int64_t Const; |
| 284 | // Or is handled differently. |
| 285 | if (BE.getOpcode() == MCBinaryExpr::Or) { |
| 286 | emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true); |
| 287 | OS << ','; |
| 288 | emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true); |
| 289 | return; |
| 290 | } |
| 291 | |
| 292 | if (Parens) |
| 293 | OS << "AD(" ; |
| 294 | emitHLASMValueImpl(Value: BE.getLHS(), Size); |
| 295 | |
| 296 | switch (BE.getOpcode()) { |
| 297 | case MCBinaryExpr::LShr: { |
| 298 | Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue(); |
| 299 | OS << '/' << (1 << Const); |
| 300 | if (Parens) |
| 301 | OS << ')'; |
| 302 | return; |
| 303 | } |
| 304 | case MCBinaryExpr::Add: |
| 305 | OS << '+'; |
| 306 | break; |
| 307 | case MCBinaryExpr::Div: |
| 308 | OS << '/'; |
| 309 | break; |
| 310 | case MCBinaryExpr::Mul: |
| 311 | OS << '*'; |
| 312 | break; |
| 313 | case MCBinaryExpr::Sub: |
| 314 | OS << '-'; |
| 315 | break; |
| 316 | default: |
| 317 | getContext().reportError(L: SMLoc(), |
| 318 | Msg: "Unrecognized HLASM arithmetic expression!" ); |
| 319 | } |
| 320 | emitHLASMValueImpl(Value: BE.getRHS(), Size); |
| 321 | if (Parens) |
| 322 | OS << ')'; |
| 323 | return; |
| 324 | } |
| 325 | case MCExpr::Target: |
| 326 | MAI->printExpr(OS, *Value); |
| 327 | return; |
| 328 | default: |
| 329 | Parens &= isa<MCSymbolRefExpr>(Val: Value); |
| 330 | if (Parens) |
| 331 | OS << "AD(" ; |
| 332 | MAI->printExpr(OS, *Value); |
| 333 | if (Parens) |
| 334 | OS << ')'; |
| 335 | return; |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, |
| 340 | SMLoc Loc) { |
| 341 | assert(Size <= 8 && "Invalid size" ); |
| 342 | assert(getCurrentSectionOnly() && |
| 343 | "Cannot emit contents before setting section!" ); |
| 344 | |
| 345 | MCStreamer::emitValueImpl(Value, Size, Loc); |
| 346 | OS << " DC " ; |
| 347 | emitHLASMValueImpl(Value, Size, Parens: true); |
| 348 | EmitEOL(); |
| 349 | } |
| 350 | |
| 351 | void SystemZHLASMAsmStreamer::finishImpl() { |
| 352 | for (auto &Symbol : getAssembler().symbols()) { |
| 353 | if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined()) |
| 354 | continue; |
| 355 | auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol)); |
| 356 | OS << " " << (Sym.isWeak() ? "WXTRN" : "EXTRN" ) << " " << Sym.getName(); |
| 357 | EmitEOL(); |
| 358 | emitXATTR(OS, Name: Sym.getName(), Linkage: Sym.getLinkage(), Executable: Sym.getCodeData(), |
| 359 | BindingScope: Sym.getBindingScope()); |
| 360 | EmitEOL(); |
| 361 | } |
| 362 | |
| 363 | // Finish the assembly output. |
| 364 | OS << " END" ; |
| 365 | EmitEOL(); |
| 366 | } |
| 367 | |