| 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/MCSectionGOFF.h" |
| 14 | #include "llvm/MC/MCSymbolGOFF.h" |
| 15 | #include "llvm/Support/Casting.h" |
| 16 | #include "llvm/Support/Format.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 | static void emitXATTR(raw_ostream &OS, StringRef Name, MCSectionGOFF *ADA, |
| 80 | bool IsIndirectReference, GOFF::ESDLinkageType Linkage, |
| 81 | GOFF::ESDExecutable Executable, |
| 82 | GOFF::ESDBindingScope BindingScope) { |
| 83 | llvm::ListSeparator Sep("," ); |
| 84 | OS << Name << " XATTR " ; |
| 85 | OS << Sep << "LINKAGE(" << (Linkage == GOFF::ESD_LT_OS ? "OS" : "XPLINK" ) |
| 86 | << ")" ; |
| 87 | |
| 88 | const bool NotUnspecified = (Executable != GOFF::ESD_EXE_Unspecified); |
| 89 | if (NotUnspecified || IsIndirectReference) { |
| 90 | OS << Sep << "REFERENCE(" ; |
| 91 | llvm::ListSeparator SepRef("," ); |
| 92 | |
| 93 | if (NotUnspecified) |
| 94 | OS << SepRef << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA" ); |
| 95 | |
| 96 | if (IsIndirectReference) |
| 97 | OS << SepRef << "INDIRECT" ; |
| 98 | |
| 99 | OS << ")" ; |
| 100 | } |
| 101 | // Emit PSECT only for code symbols. |
| 102 | if (ADA && Executable != GOFF::ESD_EXE_DATA) |
| 103 | OS << Sep << "PSECT(" << ADA->getName() << ")" ; |
| 104 | if (BindingScope != GOFF::ESD_BSC_Unspecified) { |
| 105 | OS << Sep << "SCOPE(" ; |
| 106 | switch (BindingScope) { |
| 107 | case GOFF::ESD_BSC_Section: |
| 108 | OS << "SECTION" ; |
| 109 | break; |
| 110 | case GOFF::ESD_BSC_Module: |
| 111 | OS << "MODULE" ; |
| 112 | break; |
| 113 | case GOFF::ESD_BSC_Library: |
| 114 | OS << "LIBRARY" ; |
| 115 | break; |
| 116 | case GOFF::ESD_BSC_ImportExport: |
| 117 | OS << "EXPORT" ; |
| 118 | break; |
| 119 | default: |
| 120 | break; |
| 121 | } |
| 122 | OS << ')'; |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | static void emitCATTR(raw_ostream &OS, StringRef Name, GOFF::ESDRmode Rmode, |
| 127 | GOFF::ESDAlignment Alignment, |
| 128 | GOFF::ESDLoadingBehavior LoadBehavior, |
| 129 | GOFF::ESDExecutable Executable, bool IsReadOnly, |
| 130 | uint32_t SortKey, uint8_t FillByteValue, |
| 131 | StringRef PartName) { |
| 132 | OS << Name << " CATTR " ; |
| 133 | OS << "ALIGN(" << static_cast<unsigned>(Alignment) << ")," |
| 134 | << "FILL(" << static_cast<unsigned>(FillByteValue) << ")" ; |
| 135 | switch (LoadBehavior) { |
| 136 | case GOFF::ESD_LB_Deferred: |
| 137 | OS << ",DEFLOAD" ; |
| 138 | break; |
| 139 | case GOFF::ESD_LB_NoLoad: |
| 140 | OS << ",NOLOAD" ; |
| 141 | break; |
| 142 | default: |
| 143 | break; |
| 144 | } |
| 145 | switch (Executable) { |
| 146 | case GOFF::ESD_EXE_CODE: |
| 147 | OS << ",EXECUTABLE" ; |
| 148 | break; |
| 149 | case GOFF::ESD_EXE_DATA: |
| 150 | OS << ",NOTEXECUTABLE" ; |
| 151 | break; |
| 152 | default: |
| 153 | break; |
| 154 | } |
| 155 | if (IsReadOnly) |
| 156 | OS << ",READONLY" ; |
| 157 | if (Rmode != GOFF::ESD_RMODE_None) { |
| 158 | OS << ','; |
| 159 | OS << "RMODE(" ; |
| 160 | switch (Rmode) { |
| 161 | case GOFF::ESD_RMODE_None: |
| 162 | llvm_unreachable("" ); |
| 163 | case GOFF::ESD_RMODE_24: |
| 164 | OS << "24" ; |
| 165 | break; |
| 166 | case GOFF::ESD_RMODE_31: |
| 167 | OS << "31" ; |
| 168 | break; |
| 169 | case GOFF::ESD_RMODE_64: |
| 170 | OS << "64" ; |
| 171 | break; |
| 172 | } |
| 173 | OS << ')'; |
| 174 | } |
| 175 | if (SortKey) |
| 176 | OS << ",PRIORITY(" << SortKey << ")" ; |
| 177 | if (!PartName.empty()) |
| 178 | OS << ",PART(" << PartName << ")" ; |
| 179 | OS << '\n'; |
| 180 | } |
| 181 | |
| 182 | static void emitALIAS(raw_ostream &OS, StringRef Name, StringRef ExternalName) { |
| 183 | OS << Name << " ALIAS C'" ; |
| 184 | for (char C : ExternalName) { |
| 185 | if (C == '\'' || C == '&') |
| 186 | OS << C; |
| 187 | OS << C; |
| 188 | } |
| 189 | OS << "'\n" ; |
| 190 | } |
| 191 | |
| 192 | void SystemZHLASMAsmStreamer::changeSection(MCSection *Section, |
| 193 | uint32_t Subsection) { |
| 194 | auto &Sec = *static_cast<MCSectionGOFF *>(Section); |
| 195 | auto EmitExternalName = [&Sec, this]() { |
| 196 | if (Sec.hasExternalName()) |
| 197 | emitALIAS(OS, Name: Sec.getName(), ExternalName: Sec.getExternalName()); |
| 198 | }; |
| 199 | switch (Sec.getSymbolType()) { |
| 200 | case GOFF::ESD_ST_SectionDefinition: { |
| 201 | OS << Sec.getName() << " CSECT\n" ; |
| 202 | Sec.setEmitted(); |
| 203 | EmitExternalName(); |
| 204 | break; |
| 205 | } |
| 206 | case GOFF::ESD_ST_ElementDefinition: { |
| 207 | changeSection(Section: Sec.getParent(), Subsection); |
| 208 | if (!Sec.isEmitted()) { |
| 209 | GOFF::EDAttr ED = Sec.getEDAttributes(); |
| 210 | emitCATTR(OS, Name: Sec.getName(), Rmode: ED.Rmode, Alignment: Sec.getEDAlignment(), |
| 211 | LoadBehavior: ED.LoadBehavior, Executable: GOFF::ESD_EXE_Unspecified, IsReadOnly: ED.IsReadOnly, SortKey: 0, |
| 212 | FillByteValue: ED.FillByteValue, PartName: StringRef()); |
| 213 | if (auto *BeginSym = static_cast<MCSymbolGOFF *>(Sec.getBeginSymbol())) { |
| 214 | if (BeginSym->getADA()) { |
| 215 | emitXATTR(OS, Name: BeginSym->getName(), ADA: BeginSym->getADA(), |
| 216 | /*IsIndirectReference=*/false, Linkage: GOFF::ESD_LT_XPLink, |
| 217 | Executable: GOFF::ESD_EXE_Unspecified, BindingScope: GOFF::ESD_BSC_Section); |
| 218 | OS << '\n'; |
| 219 | } |
| 220 | } |
| 221 | Sec.setEmitted(); |
| 222 | EmitExternalName(); |
| 223 | } else |
| 224 | OS << Sec.getName() << " CATTR\n" ; |
| 225 | break; |
| 226 | } |
| 227 | case GOFF::ESD_ST_PartReference: { |
| 228 | MCSectionGOFF *ED = Sec.getParent(); |
| 229 | changeSection(Section: ED->getParent(), Subsection); |
| 230 | if (!Sec.isEmitted()) { |
| 231 | GOFF::EDAttr EDAttr = ED->getEDAttributes(); |
| 232 | GOFF::PRAttr PRAttr = Sec.getPRAttributes(); |
| 233 | emitCATTR(OS, Name: ED->getName(), Rmode: EDAttr.Rmode, Alignment: ED->getEDAlignment(), |
| 234 | LoadBehavior: EDAttr.LoadBehavior, Executable: PRAttr.Executable, IsReadOnly: EDAttr.IsReadOnly, |
| 235 | SortKey: PRAttr.SortKey, FillByteValue: EDAttr.FillByteValue, PartName: Sec.getName()); |
| 236 | MCSectionGOFF *ADA = |
| 237 | Sec.getBeginSymbol() != nullptr |
| 238 | ? static_cast<MCSymbolGOFF *>(Sec.getBeginSymbol())->getADA() |
| 239 | : nullptr; |
| 240 | emitXATTR(OS, Name: Sec.getName(), ADA, /*IsIndirectReference=*/false, |
| 241 | Linkage: PRAttr.Linkage, Executable: PRAttr.Executable, BindingScope: PRAttr.BindingScope); |
| 242 | OS << '\n'; |
| 243 | ED->setEmitted(); |
| 244 | Sec.setEmitted(); |
| 245 | EmitExternalName(); |
| 246 | } else |
| 247 | OS << ED->getName() << " CATTR PART(" << Sec.getName() << ")\n" ; |
| 248 | break; |
| 249 | } |
| 250 | default: |
| 251 | llvm_unreachable("Wrong section type" ); |
| 252 | } |
| 253 | MCStreamer::changeSection(Section, Subsection); |
| 254 | EmitEOL(); |
| 255 | } |
| 256 | |
| 257 | void SystemZHLASMAsmStreamer::emitAlignmentDS(uint64_t ByteAlignment, |
| 258 | std::optional<int64_t> Value, |
| 259 | unsigned ValueSize, |
| 260 | unsigned MaxBytesToEmit) { |
| 261 | if (!isPowerOf2_64(Value: ByteAlignment)) |
| 262 | report_fatal_error(reason: "Only power-of-two alignments are supported " ); |
| 263 | |
| 264 | OS << " DS 0" ; |
| 265 | switch (ValueSize) { |
| 266 | default: |
| 267 | llvm_unreachable("Invalid size for machine code value!" ); |
| 268 | case 1: |
| 269 | OS << "B" ; |
| 270 | break; |
| 271 | case 2: |
| 272 | OS << "H" ; |
| 273 | break; |
| 274 | case 4: |
| 275 | OS << "F" ; |
| 276 | break; |
| 277 | case 8: |
| 278 | OS << "D" ; |
| 279 | break; |
| 280 | case 16: |
| 281 | OS << "Q" ; |
| 282 | break; |
| 283 | } |
| 284 | |
| 285 | EmitEOL(); |
| 286 | } |
| 287 | |
| 288 | void SystemZHLASMAsmStreamer::(const Twine &T, bool TabPrefix) { |
| 289 | OS << MAI->getCommentString() << T; |
| 290 | EmitEOL(); |
| 291 | } |
| 292 | |
| 293 | void SystemZHLASMAsmStreamer::(const Twine &T, bool EOL) { |
| 294 | if (!IsVerboseAsm) |
| 295 | return; |
| 296 | |
| 297 | T.toVector(Out&: CommentToEmit); |
| 298 | |
| 299 | if (EOL) |
| 300 | CommentToEmit.push_back(Elt: '\n'); // Place comment in a new line. |
| 301 | } |
| 302 | |
| 303 | void SystemZHLASMAsmStreamer::() { |
| 304 | if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0) |
| 305 | return; |
| 306 | |
| 307 | StringRef = CommentToEmit; |
| 308 | |
| 309 | assert(Comments.back() == '\n' && "Comment array not newline terminated" ); |
| 310 | do { |
| 311 | // Emit a line of comments, but not exceeding 80 characters. |
| 312 | size_t Position = std::min(a: InstLimit - 2, b: Comments.find(C: '\n')); |
| 313 | FOS << MAI->getCommentString() << ' ' << Comments.substr(Start: 0, N: Position) |
| 314 | << '\n'; |
| 315 | |
| 316 | if (Comments[Position] == '\n') |
| 317 | Position++; |
| 318 | Comments = Comments.substr(Start: Position); |
| 319 | } while (!Comments.empty()); |
| 320 | |
| 321 | CommentToEmit.clear(); |
| 322 | } |
| 323 | |
| 324 | void SystemZHLASMAsmStreamer::emitValueToAlignment(Align Alignment, |
| 325 | int64_t Fill, |
| 326 | uint8_t FillLen, |
| 327 | unsigned MaxBytesToEmit) { |
| 328 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: Fill, ValueSize: FillLen, MaxBytesToEmit); |
| 329 | } |
| 330 | |
| 331 | void SystemZHLASMAsmStreamer::emitCodeAlignment(Align Alignment, |
| 332 | const MCSubtargetInfo &STI, |
| 333 | unsigned MaxBytesToEmit) { |
| 334 | // Emit with a text fill value. |
| 335 | if (MAI->getTextAlignFillValue()) |
| 336 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: MAI->getTextAlignFillValue(), ValueSize: 1, |
| 337 | MaxBytesToEmit); |
| 338 | else |
| 339 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: std::nullopt, ValueSize: 1, MaxBytesToEmit); |
| 340 | } |
| 341 | |
| 342 | void SystemZHLASMAsmStreamer::emitBytes(StringRef Data) { |
| 343 | assert(getCurrentSectionOnly() && |
| 344 | "Cannot emit contents before setting section!" ); |
| 345 | if (Data.empty()) |
| 346 | return; |
| 347 | |
| 348 | OS << " DC " ; |
| 349 | size_t Len = Data.size(); |
| 350 | SmallVector<uint8_t> Chars; |
| 351 | Chars.resize(N: Len); |
| 352 | OS << "XL" << Len; |
| 353 | uint32_t Index = 0; |
| 354 | for (uint8_t C : Data) { |
| 355 | Chars[Index] = C; |
| 356 | Index++; |
| 357 | } |
| 358 | |
| 359 | OS << '\'' << toHex(Input: Chars) << '\''; |
| 360 | |
| 361 | EmitEOL(); |
| 362 | } |
| 363 | |
| 364 | void SystemZHLASMAsmStreamer::emitFill(const MCExpr &NumBytes, |
| 365 | uint64_t FillValue, SMLoc Loc) { |
| 366 | int64_t Count; |
| 367 | if (!NumBytes.evaluateAsAbsolute(Res&: Count)) { |
| 368 | getContext().reportError(L: Loc, Msg: "fill length must be absolute in HLASM" ); |
| 369 | return; |
| 370 | } |
| 371 | if (Count < 0) { |
| 372 | getContext().reportError(L: Loc, Msg: "invalid number of bytes" ); |
| 373 | return; |
| 374 | } |
| 375 | if (Count > 0x7FFFFFFF) { |
| 376 | getContext().reportError(L: Loc, Msg: "fill length too large for HLASM" ); |
| 377 | return; |
| 378 | } |
| 379 | if (Count == 0) |
| 380 | return; |
| 381 | |
| 382 | OS << " DC " << Count << "XL1'" |
| 383 | << format_hex_no_prefix(N: FillValue & 0xff, Width: 2, Upper: true) << '\''; |
| 384 | EmitEOL(); |
| 385 | } |
| 386 | |
| 387 | void SystemZHLASMAsmStreamer::(const MCInst &Inst, |
| 388 | const MCSubtargetInfo &STI) { |
| 389 | raw_ostream &OS = getCommentOS(); |
| 390 | SmallString<256> Code; |
| 391 | SmallVector<MCFixup, 4> Fixups; |
| 392 | |
| 393 | // If we have no code emitter, don't emit code. |
| 394 | if (!getAssembler().getEmitterPtr()) |
| 395 | return; |
| 396 | |
| 397 | getAssembler().getEmitter().encodeInstruction(Inst, CB&: Code, Fixups, STI); |
| 398 | |
| 399 | // If we are showing fixups, create symbolic markers in the encoded |
| 400 | // representation. We do this by making a per-bit map to the fixup item index, |
| 401 | // then trying to display it as nicely as possible. |
| 402 | SmallVector<uint8_t, 64> FixupMap; |
| 403 | FixupMap.resize(N: Code.size() * 8); |
| 404 | for (unsigned I = 0, E = Code.size() * 8; I != E; ++I) |
| 405 | FixupMap[I] = 0; |
| 406 | |
| 407 | for (unsigned I = 0, E = Fixups.size(); I != E; ++I) { |
| 408 | MCFixup &F = Fixups[I]; |
| 409 | MCFixupKindInfo Info = |
| 410 | getAssembler().getBackend().getFixupKindInfo(Kind: F.getKind()); |
| 411 | for (unsigned J = 0; J != Info.TargetSize; ++J) { |
| 412 | unsigned Index = F.getOffset() * 8 + Info.TargetOffset + J; |
| 413 | assert(Index < Code.size() * 8 && "Invalid offset in fixup!" ); |
| 414 | FixupMap[Index] = 1 + I; |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | OS << "encoding: [" ; |
| 419 | for (unsigned I = 0, E = Code.size(); I != E; ++I) { |
| 420 | if (I) |
| 421 | OS << ','; |
| 422 | |
| 423 | // See if all bits are the same map entry. |
| 424 | uint8_t MapEntry = FixupMap[I * 8 + 0]; |
| 425 | for (unsigned J = 1; J != 8; ++J) { |
| 426 | if (FixupMap[I * 8 + J] == MapEntry) |
| 427 | continue; |
| 428 | |
| 429 | MapEntry = uint8_t(~0U); |
| 430 | break; |
| 431 | } |
| 432 | |
| 433 | if (MapEntry != uint8_t(~0U)) { |
| 434 | if (MapEntry == 0) { |
| 435 | OS << format(Fmt: "0x%02x" , Vals: uint8_t(Code[I])); |
| 436 | } else { |
| 437 | if (Code[I]) { |
| 438 | // FIXME: Some of the 8 bits require fix up. |
| 439 | OS << format(Fmt: "0x%02x" , Vals: uint8_t(Code[I])) << '\'' |
| 440 | << char('A' + MapEntry - 1) << '\''; |
| 441 | } else |
| 442 | OS << char('A' + MapEntry - 1); |
| 443 | } |
| 444 | } else { |
| 445 | // Otherwise, write out in binary. |
| 446 | OS << "0b" ; |
| 447 | for (unsigned J = 8; J--;) { |
| 448 | unsigned Bit = (Code[I] >> J) & 1; |
| 449 | unsigned FixupBit = I * 8 + (7 - J); |
| 450 | if (uint8_t MapEntry = FixupMap[FixupBit]) { |
| 451 | assert(Bit == 0 && "Encoder wrote into fixed up bit!" ); |
| 452 | OS << char('A' + MapEntry - 1); |
| 453 | } else |
| 454 | OS << Bit; |
| 455 | } |
| 456 | } |
| 457 | } |
| 458 | OS << "]\n" ; |
| 459 | |
| 460 | for (unsigned I = 0, E = Fixups.size(); I != E; ++I) { |
| 461 | MCFixup &F = Fixups[I]; |
| 462 | OS << " fixup " << char('A' + I) << " - " |
| 463 | << "offset: " << F.getOffset() << ", value: " ; |
| 464 | MAI->printExpr(OS, *F.getValue()); |
| 465 | auto Kind = F.getKind(); |
| 466 | if (mc::isRelocation(FixupKind: Kind)) |
| 467 | OS << ", relocation type: " << Kind; |
| 468 | else { |
| 469 | OS << ", kind: " ; |
| 470 | auto Info = getAssembler().getBackend().getFixupKindInfo(Kind); |
| 471 | if (F.isPCRel() && StringRef(Info.Name).starts_with(Prefix: "FK_Data_" )) |
| 472 | OS << "FK_PCRel_" << (Info.TargetSize / 8); |
| 473 | else |
| 474 | OS << Info.Name; |
| 475 | } |
| 476 | OS << "\n" ; |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst, |
| 481 | const MCSubtargetInfo &STI) { |
| 482 | // Show the encoding in a comment if we have a code emitter. |
| 483 | addEncodingComment(Inst, STI); |
| 484 | EmitEOL(); |
| 485 | |
| 486 | InstPrinter->printInst(MI: &Inst, Address: 0, Annot: "" , STI, OS); |
| 487 | EmitEOL(); |
| 488 | } |
| 489 | |
| 490 | void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { |
| 491 | MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol); |
| 492 | |
| 493 | MCStreamer::emitLabel(Symbol: Sym, Loc); |
| 494 | |
| 495 | // Emit label and ENTRY statement only if not implied by CSECT. Do not emit a |
| 496 | // label if the symbol is on a PR section. |
| 497 | bool EmitLabelAndEntry = |
| 498 | !static_cast<MCSectionGOFF *>(getCurrentSectionOnly())->isPR(); |
| 499 | if (!Sym->isTemporary() && Sym->isInEDSection()) { |
| 500 | EmitLabelAndEntry = |
| 501 | Sym->getName() != |
| 502 | static_cast<MCSectionGOFF &>(Sym->getSection()).getParent()->getName(); |
| 503 | if (EmitLabelAndEntry) { |
| 504 | OS << " ENTRY " << Sym->getName(); |
| 505 | EmitEOL(); |
| 506 | } |
| 507 | |
| 508 | emitXATTR(OS, Name: Sym->getName(), ADA: Sym->getADA(), IsIndirectReference: Sym->isIndirect(), |
| 509 | Linkage: Sym->getLinkage(), Executable: Sym->getCodeData(), BindingScope: Sym->getBindingScope()); |
| 510 | EmitEOL(); |
| 511 | if (Sym->hasExternalName()) |
| 512 | emitALIAS(OS, Name: Sym->getName(), ExternalName: Sym->getExternalName()); |
| 513 | } |
| 514 | |
| 515 | if (EmitLabelAndEntry) { |
| 516 | OS << Sym->getName() << " DS 0H" ; |
| 517 | EmitEOL(); |
| 518 | } |
| 519 | } |
| 520 | |
| 521 | bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym, |
| 522 | MCSymbolAttr Attribute) { |
| 523 | return static_cast<MCSymbolGOFF *>(Sym)->setSymbolAttribute(Attribute); |
| 524 | } |
| 525 | |
| 526 | void SystemZHLASMAsmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size, |
| 527 | Align ByteAlignment) { |
| 528 | auto *Symbol = static_cast<MCSymbolGOFF *>(S); |
| 529 | MCSectionGOFF *Section = |
| 530 | Symbol->getSectionForCommonSymbol(Ctx&: getContext(), ByteAlignment); |
| 531 | pushSection(); |
| 532 | switchSection(Section); |
| 533 | emitLabel(Symbol, Loc: SMLoc()); |
| 534 | emitZeros(NumBytes: Size); |
| 535 | popSection(); |
| 536 | } |
| 537 | |
| 538 | void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) { |
| 539 | String.consume_back(Suffix: "\n" ); |
| 540 | OS << String; |
| 541 | EmitEOL(); |
| 542 | } |
| 543 | |
| 544 | // Slight duplicate of MCExpr::print due to HLASM only recognizing limited |
| 545 | // arithmetic operators (+-*/). |
| 546 | void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value, |
| 547 | unsigned Size, bool Parens) { |
| 548 | switch (Value->getKind()) { |
| 549 | case MCExpr::Constant: { |
| 550 | int64_t Val = cast<MCConstantExpr>(Val: Value)->getValue(); |
| 551 | OS << "XL" << Size << '\'' |
| 552 | << format_hex_no_prefix(N: Val, Width: Size * 2, /*Upper=*/true) << '\''; |
| 553 | return; |
| 554 | } |
| 555 | case MCExpr::Binary: { |
| 556 | const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value); |
| 557 | int64_t Const; |
| 558 | // Or is handled differently. |
| 559 | if (BE.getOpcode() == MCBinaryExpr::Or) { |
| 560 | emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true); |
| 561 | OS << ','; |
| 562 | emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true); |
| 563 | return; |
| 564 | } |
| 565 | |
| 566 | if (Parens) |
| 567 | OS << "AD(" ; |
| 568 | emitHLASMValueImpl(Value: BE.getLHS(), Size); |
| 569 | |
| 570 | switch (BE.getOpcode()) { |
| 571 | case MCBinaryExpr::LShr: { |
| 572 | Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue(); |
| 573 | OS << '/' << (1 << Const); |
| 574 | if (Parens) |
| 575 | OS << ')'; |
| 576 | return; |
| 577 | } |
| 578 | case MCBinaryExpr::Add: |
| 579 | OS << '+'; |
| 580 | break; |
| 581 | case MCBinaryExpr::Div: |
| 582 | OS << '/'; |
| 583 | break; |
| 584 | case MCBinaryExpr::Mul: |
| 585 | OS << '*'; |
| 586 | break; |
| 587 | case MCBinaryExpr::Sub: |
| 588 | OS << '-'; |
| 589 | break; |
| 590 | default: |
| 591 | getContext().reportError(L: SMLoc(), |
| 592 | Msg: "Unrecognized HLASM arithmetic expression!" ); |
| 593 | } |
| 594 | emitHLASMValueImpl(Value: BE.getRHS(), Size); |
| 595 | if (Parens) |
| 596 | OS << ')'; |
| 597 | return; |
| 598 | } |
| 599 | case MCExpr::Target: |
| 600 | MAI->printExpr(OS, *Value); |
| 601 | return; |
| 602 | default: |
| 603 | Parens &= isa<MCSymbolRefExpr>(Val: Value); |
| 604 | if (Parens) |
| 605 | OS << "AD(" ; |
| 606 | MAI->printExpr(OS, *Value); |
| 607 | if (Parens) |
| 608 | OS << ')'; |
| 609 | return; |
| 610 | } |
| 611 | } |
| 612 | |
| 613 | void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, |
| 614 | SMLoc Loc) { |
| 615 | assert(Size <= 8 && "Invalid size" ); |
| 616 | assert(getCurrentSectionOnly() && |
| 617 | "Cannot emit contents before setting section!" ); |
| 618 | |
| 619 | MCStreamer::emitValueImpl(Value, Size, Loc); |
| 620 | OS << " DC " ; |
| 621 | emitHLASMValueImpl(Value, Size, Parens: true); |
| 622 | EmitEOL(); |
| 623 | } |
| 624 | |
| 625 | void SystemZHLASMAsmStreamer::finishImpl() { |
| 626 | for (auto &Symbol : getAssembler().symbols()) { |
| 627 | if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined()) |
| 628 | continue; |
| 629 | auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol)); |
| 630 | if (Sym.getCodeData() == GOFF::ESD_EXE_DATA) { |
| 631 | OS << Sym.getADA()->getParent()->getExternalName() << " CATTR PART(" |
| 632 | << Sym.getName() << ")" ; |
| 633 | EmitEOL(); |
| 634 | } else { |
| 635 | OS << " " << (Sym.isWeak() ? "WXTRN" : "EXTRN" ) << " " << Sym.getName(); |
| 636 | EmitEOL(); |
| 637 | } |
| 638 | emitXATTR(OS, Name: Sym.getName(), ADA: Sym.getADA(), IsIndirectReference: Sym.isIndirect(), |
| 639 | Linkage: Sym.getLinkage(), Executable: Sym.getCodeData(), BindingScope: Sym.getBindingScope()); |
| 640 | EmitEOL(); |
| 641 | if (Sym.hasExternalName()) |
| 642 | emitALIAS(OS, Name: Sym.getName(), ExternalName: Sym.getExternalName()); |
| 643 | } |
| 644 | |
| 645 | // Finish the assembly output. |
| 646 | OS << " END" ; |
| 647 | EmitEOL(); |
| 648 | } |
| 649 | |