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
20using namespace llvm;
21
22void SystemZHLASMAsmStreamer::visitUsedSymbol(const MCSymbol &Sym) {
23 Assembler->registerSymbol(Symbol: Sym);
24}
25
26void 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
79void 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
87void 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
118void SystemZHLASMAsmStreamer::AddComment(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
128void SystemZHLASMAsmStreamer::EmitComment() {
129 if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0)
130 return;
131
132 StringRef Comments = 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
149void 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
156void 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
167void 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
189void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst,
190 const MCSubtargetInfo &STI) {
191
192 InstPrinter->printInst(MI: &Inst, Address: 0, Annot: "", STI, OS);
193 EmitEOL();
194}
195
196static void emitXATTR(raw_ostream &OS, StringRef Name, MCSectionGOFF *ADA,
197 bool IsIndirectReference, 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
205 const bool NotUnspecified = (Executable != GOFF::ESD_EXE_Unspecified);
206 if (NotUnspecified || IsIndirectReference) {
207 OS << Sep << "REFERENCE(";
208 llvm::ListSeparator SepRef(",");
209
210 if (NotUnspecified)
211 OS << SepRef << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA");
212
213 if (IsIndirectReference)
214 OS << SepRef << "INDIRECT";
215
216 OS << ")";
217 }
218 if (ADA)
219 OS << Sep << "PSECT(" << ADA->getName() << ")";
220 if (BindingScope != GOFF::ESD_BSC_Unspecified) {
221 OS << Sep << "SCOPE(";
222 switch (BindingScope) {
223 case GOFF::ESD_BSC_Section:
224 OS << "SECTION";
225 break;
226 case GOFF::ESD_BSC_Module:
227 OS << "MODULE";
228 break;
229 case GOFF::ESD_BSC_Library:
230 OS << "LIBRARY";
231 break;
232 case GOFF::ESD_BSC_ImportExport:
233 OS << "EXPORT";
234 break;
235 default:
236 break;
237 }
238 OS << ')';
239 }
240}
241
242void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) {
243 MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol);
244
245 MCStreamer::emitLabel(Symbol: Sym, Loc);
246
247 // Emit label and ENTRY statement only if not implied by CSECT. Do not emit a
248 // label if the symbol is on a PR section.
249 bool EmitLabelAndEntry =
250 !static_cast<MCSectionGOFF *>(getCurrentSectionOnly())->isPR();
251 if (!Sym->isTemporary() && Sym->isInEDSection()) {
252 EmitLabelAndEntry =
253 Sym->getName() !=
254 static_cast<MCSectionGOFF &>(Sym->getSection()).getParent()->getName();
255 if (EmitLabelAndEntry) {
256 OS << " ENTRY " << Sym->getName();
257 EmitEOL();
258 }
259
260 emitXATTR(OS, Name: Sym->getName(), ADA: Sym->getADA(), IsIndirectReference: Sym->isIndirect(),
261 Linkage: Sym->getLinkage(), Executable: Sym->getCodeData(), BindingScope: Sym->getBindingScope());
262 EmitEOL();
263 if (Sym->hasExternalName())
264 OS << Sym->getName() << " ALIAS C'" << Sym->getExternalName() << "'\n";
265 }
266
267 if (EmitLabelAndEntry) {
268 OS << Sym->getName() << " DS 0H";
269 EmitEOL();
270 }
271}
272
273bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
274 MCSymbolAttr Attribute) {
275 return static_cast<MCSymbolGOFF *>(Sym)->setSymbolAttribute(Attribute);
276}
277
278void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) {
279 String.consume_back(Suffix: "\n");
280 OS << String;
281 EmitEOL();
282}
283
284// Slight duplicate of MCExpr::print due to HLASM only recognizing limited
285// arithmetic operators (+-*/).
286void SystemZHLASMAsmStreamer::emitHLASMValueImpl(const MCExpr *Value,
287 unsigned Size, bool Parens) {
288 switch (Value->getKind()) {
289 case MCExpr::Constant: {
290 OS << "XL" << Size << '\'';
291 MAI->printExpr(OS, *Value);
292 OS << '\'';
293 return;
294 }
295 case MCExpr::Binary: {
296 const MCBinaryExpr &BE = cast<MCBinaryExpr>(Val: *Value);
297 int64_t Const;
298 // Or is handled differently.
299 if (BE.getOpcode() == MCBinaryExpr::Or) {
300 emitHLASMValueImpl(Value: BE.getLHS(), Size, Parens: true);
301 OS << ',';
302 emitHLASMValueImpl(Value: BE.getRHS(), Size, Parens: true);
303 return;
304 }
305
306 if (Parens)
307 OS << "AD(";
308 emitHLASMValueImpl(Value: BE.getLHS(), Size);
309
310 switch (BE.getOpcode()) {
311 case MCBinaryExpr::LShr: {
312 Const = cast<MCConstantExpr>(Val: BE.getRHS())->getValue();
313 OS << '/' << (1 << Const);
314 if (Parens)
315 OS << ')';
316 return;
317 }
318 case MCBinaryExpr::Add:
319 OS << '+';
320 break;
321 case MCBinaryExpr::Div:
322 OS << '/';
323 break;
324 case MCBinaryExpr::Mul:
325 OS << '*';
326 break;
327 case MCBinaryExpr::Sub:
328 OS << '-';
329 break;
330 default:
331 getContext().reportError(L: SMLoc(),
332 Msg: "Unrecognized HLASM arithmetic expression!");
333 }
334 emitHLASMValueImpl(Value: BE.getRHS(), Size);
335 if (Parens)
336 OS << ')';
337 return;
338 }
339 case MCExpr::Target:
340 MAI->printExpr(OS, *Value);
341 return;
342 default:
343 Parens &= isa<MCSymbolRefExpr>(Val: Value);
344 if (Parens)
345 OS << "AD(";
346 MAI->printExpr(OS, *Value);
347 if (Parens)
348 OS << ')';
349 return;
350 }
351}
352
353void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size,
354 SMLoc Loc) {
355 assert(Size <= 8 && "Invalid size");
356 assert(getCurrentSectionOnly() &&
357 "Cannot emit contents before setting section!");
358
359 MCStreamer::emitValueImpl(Value, Size, Loc);
360 OS << " DC ";
361 emitHLASMValueImpl(Value, Size, Parens: true);
362 EmitEOL();
363}
364
365void SystemZHLASMAsmStreamer::finishImpl() {
366 for (auto &Symbol : getAssembler().symbols()) {
367 if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined())
368 continue;
369 auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol));
370 OS << " " << (Sym.isWeak() ? "WXTRN" : "EXTRN") << " " << Sym.getName();
371 EmitEOL();
372 emitXATTR(OS, Name: Sym.getName(), ADA: Sym.getADA(), IsIndirectReference: Sym.isIndirect(),
373 Linkage: Sym.getLinkage(), Executable: Sym.getCodeData(), BindingScope: Sym.getBindingScope());
374 EmitEOL();
375 if (Sym.hasExternalName())
376 OS << Sym.getName() << " ALIAS C'" << Sym.getExternalName() << "'\n";
377 }
378
379 // Finish the assembly output.
380 OS << " END";
381 EmitEOL();
382}
383