| 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/Support/Casting.h" |
| 12 | #include "llvm/Support/Signals.h" |
| 13 | #include <sstream> |
| 14 | |
| 15 | using namespace llvm; |
| 16 | |
| 17 | void SystemZHLASMAsmStreamer::EmitEOL() { |
| 18 | // Comments are emitted on a new line before the instruction. |
| 19 | if (IsVerboseAsm) |
| 20 | EmitComment(); |
| 21 | |
| 22 | std::istringstream Stream(Str); |
| 23 | SmallVector<std::string> Lines; |
| 24 | std::string Line; |
| 25 | while (std::getline(in&: Stream, str&: Line, delim: '\n')) |
| 26 | Lines.push_back(Elt: Line); |
| 27 | |
| 28 | for (auto S : Lines) { |
| 29 | if (LLVM_LIKELY(S.length() < ContIndicatorColumn)) { |
| 30 | FOS << S; |
| 31 | // Each line in HLASM must fill the full 80 characters. |
| 32 | FOS.PadToColumn(NewCol: InstLimit); |
| 33 | FOS << "\n" ; |
| 34 | } else { |
| 35 | // If last character before end of the line is not a space |
| 36 | // we must insert an additional non-space character that |
| 37 | // is not part of the statement coding. We just reuse |
| 38 | // the existing character by making the new substring start |
| 39 | // 1 character sooner, thus "duplicating" that character |
| 40 | // If The last character is a space. We insert an X instead. |
| 41 | std::string TmpSubStr = S.substr(pos: 0, n: ContIndicatorColumn); |
| 42 | if (!TmpSubStr.compare(pos: ContIndicatorColumn - 1, n1: 1, s: " " )) |
| 43 | TmpSubStr.replace(pos: ContIndicatorColumn - 1, n1: 1, s: "X" ); |
| 44 | |
| 45 | FOS << TmpSubStr; |
| 46 | FOS.PadToColumn(NewCol: InstLimit); |
| 47 | FOS << "\n" ; |
| 48 | |
| 49 | size_t Emitted = ContIndicatorColumn - 1; |
| 50 | |
| 51 | while (Emitted < S.length()) { |
| 52 | if ((S.length() - Emitted) < ContLen) |
| 53 | TmpSubStr = S.substr(pos: Emitted, n: S.length()); |
| 54 | else { |
| 55 | TmpSubStr = S.substr(pos: Emitted, n: ContLen); |
| 56 | if (!TmpSubStr.compare(pos: ContLen - 1, n1: 1, s: " " )) |
| 57 | TmpSubStr.replace(pos: ContLen - 1, n1: 1, s: "X" ); |
| 58 | } |
| 59 | FOS.PadToColumn(NewCol: ContStartColumn); |
| 60 | FOS << TmpSubStr; |
| 61 | FOS.PadToColumn(NewCol: InstLimit); |
| 62 | FOS << "\n" ; |
| 63 | Emitted += ContLen - 1; |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | Str.clear(); |
| 68 | } |
| 69 | |
| 70 | void SystemZHLASMAsmStreamer::changeSection(MCSection *Section, |
| 71 | uint32_t Subsection) { |
| 72 | Section->printSwitchToSection(MAI: *MAI, T: getContext().getTargetTriple(), OS, |
| 73 | Subsection); |
| 74 | MCStreamer::changeSection(Section, Subsection); |
| 75 | } |
| 76 | |
| 77 | void SystemZHLASMAsmStreamer::emitAlignmentDS(uint64_t ByteAlignment, |
| 78 | std::optional<int64_t> Value, |
| 79 | unsigned ValueSize, |
| 80 | unsigned MaxBytesToEmit) { |
| 81 | if (!isPowerOf2_64(Value: ByteAlignment)) |
| 82 | report_fatal_error(reason: "Only power-of-two alignments are supported " ); |
| 83 | |
| 84 | OS << " DS 0" ; |
| 85 | switch (ValueSize) { |
| 86 | default: |
| 87 | llvm_unreachable("Invalid size for machine code value!" ); |
| 88 | case 1: |
| 89 | OS << "B" ; |
| 90 | break; |
| 91 | case 2: |
| 92 | OS << "H" ; |
| 93 | break; |
| 94 | case 4: |
| 95 | OS << "F" ; |
| 96 | break; |
| 97 | case 8: |
| 98 | OS << "D" ; |
| 99 | break; |
| 100 | case 16: |
| 101 | OS << "Q" ; |
| 102 | break; |
| 103 | } |
| 104 | |
| 105 | EmitEOL(); |
| 106 | } |
| 107 | |
| 108 | void SystemZHLASMAsmStreamer::(const Twine &T, bool EOL) { |
| 109 | if (!IsVerboseAsm) |
| 110 | return; |
| 111 | |
| 112 | T.toVector(Out&: CommentToEmit); |
| 113 | |
| 114 | if (EOL) |
| 115 | CommentToEmit.push_back(Elt: '\n'); // Place comment in a new line. |
| 116 | } |
| 117 | |
| 118 | void SystemZHLASMAsmStreamer::() { |
| 119 | if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0) |
| 120 | return; |
| 121 | |
| 122 | StringRef = CommentToEmit; |
| 123 | |
| 124 | assert(Comments.back() == '\n' && "Comment array not newline terminated" ); |
| 125 | do { |
| 126 | // Emit a line of comments, but not exceeding 80 characters. |
| 127 | size_t Position = std::min(a: InstLimit - 2, b: Comments.find(C: '\n')); |
| 128 | FOS << MAI->getCommentString() << ' ' << Comments.substr(Start: 0, N: Position) |
| 129 | << '\n'; |
| 130 | |
| 131 | if (Comments[Position] == '\n') |
| 132 | Position++; |
| 133 | Comments = Comments.substr(Start: Position); |
| 134 | } while (!Comments.empty()); |
| 135 | |
| 136 | CommentToEmit.clear(); |
| 137 | } |
| 138 | |
| 139 | void SystemZHLASMAsmStreamer::emitValueToAlignment(Align Alignment, |
| 140 | int64_t Value, |
| 141 | unsigned ValueSize, |
| 142 | unsigned MaxBytesToEmit) { |
| 143 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value, ValueSize, MaxBytesToEmit); |
| 144 | } |
| 145 | |
| 146 | void SystemZHLASMAsmStreamer::emitCodeAlignment(Align Alignment, |
| 147 | const MCSubtargetInfo *STI, |
| 148 | unsigned MaxBytesToEmit) { |
| 149 | // Emit with a text fill value. |
| 150 | if (MAI->getTextAlignFillValue()) |
| 151 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: MAI->getTextAlignFillValue(), ValueSize: 1, |
| 152 | MaxBytesToEmit); |
| 153 | else |
| 154 | emitAlignmentDS(ByteAlignment: Alignment.value(), Value: std::nullopt, ValueSize: 1, MaxBytesToEmit); |
| 155 | } |
| 156 | |
| 157 | void SystemZHLASMAsmStreamer::emitBytes(StringRef Data) { |
| 158 | assert(getCurrentSectionOnly() && |
| 159 | "Cannot emit contents before setting section!" ); |
| 160 | if (Data.empty()) |
| 161 | return; |
| 162 | |
| 163 | OS << " DC " ; |
| 164 | size_t Len = Data.size(); |
| 165 | SmallVector<uint8_t> Chars; |
| 166 | Chars.resize(N: Len); |
| 167 | OS << "XL" << Len; |
| 168 | uint32_t Index = 0; |
| 169 | for (uint8_t C : Data) { |
| 170 | Chars[Index] = C; |
| 171 | Index++; |
| 172 | } |
| 173 | |
| 174 | OS << '\'' << toHex(Input: Chars) << '\''; |
| 175 | |
| 176 | EmitEOL(); |
| 177 | } |
| 178 | |
| 179 | void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst, |
| 180 | const MCSubtargetInfo &STI) { |
| 181 | |
| 182 | InstPrinter->printInst(MI: &Inst, Address: 0, Annot: "" , STI, OS); |
| 183 | EmitEOL(); |
| 184 | } |
| 185 | |
| 186 | void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { |
| 187 | |
| 188 | MCStreamer::emitLabel(Symbol, Loc); |
| 189 | |
| 190 | Symbol->print(OS, MAI); |
| 191 | // TODO Need to adjust this based on Label type |
| 192 | OS << " DS 0H" ; |
| 193 | // TODO Update LabelSuffix in SystemZMCAsmInfoGOFF once tests have been |
| 194 | // moved to HLASM syntax. |
| 195 | // OS << MAI->getLabelSuffix(); |
| 196 | EmitEOL(); |
| 197 | } |
| 198 | |
| 199 | void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) { |
| 200 | String.consume_back(Suffix: "\n" ); |
| 201 | OS << String; |
| 202 | EmitEOL(); |
| 203 | } |
| 204 | |
| 205 | // Slight duplicate of MCExpr::print due to HLASM only recognizing limited |
| 206 | // arithmetic operators (+-*/). |
| 207 | void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value, |
| 208 | unsigned Size, bool Parens) { |
| 209 | switch (Value->getKind()) { |
| 210 | case MCExpr::Constant: { |
| 211 | OS << "XL" << Size << '\''; |
| 212 | MAI->printExpr(OS, *Value); |
| 213 | OS << '\''; |
| 214 | return; |
| 215 | } |
| 216 | case MCExpr::Binary: { |
| 217 | const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value); |
| 218 | int64_t Const; |
| 219 | // Or is handled differently. |
| 220 | if (BE.getOpcode() == MCBinaryExpr::Or) { |
| 221 | emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true); |
| 222 | OS << ','; |
| 223 | emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true); |
| 224 | return; |
| 225 | } |
| 226 | |
| 227 | if (Parens) |
| 228 | OS << "A(" ; |
| 229 | emitHLASMValueImpl(Value: BE.getLHS(), Size); |
| 230 | |
| 231 | switch (BE.getOpcode()) { |
| 232 | case MCBinaryExpr::LShr: { |
| 233 | Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue(); |
| 234 | OS << '/' << (1 << Const); |
| 235 | if (Parens) |
| 236 | OS << ')'; |
| 237 | return; |
| 238 | } |
| 239 | case MCBinaryExpr::Add: |
| 240 | OS << '+'; |
| 241 | break; |
| 242 | case MCBinaryExpr::Div: |
| 243 | OS << '/'; |
| 244 | break; |
| 245 | case MCBinaryExpr::Mul: |
| 246 | OS << '*'; |
| 247 | break; |
| 248 | case MCBinaryExpr::Sub: |
| 249 | OS << '-'; |
| 250 | break; |
| 251 | default: |
| 252 | getContext().reportError(L: SMLoc(), |
| 253 | Msg: "Unrecognized HLASM arithmetic expression!" ); |
| 254 | } |
| 255 | emitHLASMValueImpl(Value: BE.getRHS(), Size); |
| 256 | if (Parens) |
| 257 | OS << ')'; |
| 258 | return; |
| 259 | } |
| 260 | case MCExpr::Target: |
| 261 | MAI->printExpr(OS, *Value); |
| 262 | return; |
| 263 | default: |
| 264 | if (Parens) |
| 265 | OS << "A(" ; |
| 266 | MAI->printExpr(OS, *Value); |
| 267 | if (Parens) |
| 268 | OS << ')'; |
| 269 | return; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, |
| 274 | SMLoc Loc) { |
| 275 | assert(Size <= 8 && "Invalid size" ); |
| 276 | assert(getCurrentSectionOnly() && |
| 277 | "Cannot emit contents before setting section!" ); |
| 278 | |
| 279 | OS << " DC " ; |
| 280 | emitHLASMValueImpl(Value, Size, Parens: true); |
| 281 | EmitEOL(); |
| 282 | } |
| 283 | |
| 284 | void SystemZHLASMAsmStreamer::emitEnd() { |
| 285 | OS << " END" ; |
| 286 | EmitEOL(); |
| 287 | } |
| 288 | |