1 //===-- llvm-ml.cpp - masm-compatible assembler -----------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // A simple driver around MasmParser; based on llvm-mc.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/ADT/StringSwitch.h"
14 #include "llvm/MC/MCAsmBackend.h"
15 #include "llvm/MC/MCAsmInfo.h"
16 #include "llvm/MC/MCCodeEmitter.h"
17 #include "llvm/MC/MCContext.h"
18 #include "llvm/MC/MCInstPrinter.h"
19 #include "llvm/MC/MCInstrInfo.h"
20 #include "llvm/MC/MCObjectFileInfo.h"
21 #include "llvm/MC/MCObjectWriter.h"
22 #include "llvm/MC/MCParser/AsmLexer.h"
23 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
24 #include "llvm/MC/MCRegisterInfo.h"
25 #include "llvm/MC/MCStreamer.h"
26 #include "llvm/MC/MCSubtargetInfo.h"
27 #include "llvm/MC/MCSymbol.h"
28 #include "llvm/MC/MCTargetOptionsCommandFlags.h"
29 #include "llvm/MC/TargetRegistry.h"
30 #include "llvm/Option/Arg.h"
31 #include "llvm/Option/ArgList.h"
32 #include "llvm/Option/Option.h"
33 #include "llvm/Support/Compression.h"
34 #include "llvm/Support/FileUtilities.h"
35 #include "llvm/Support/FormatVariadic.h"
36 #include "llvm/Support/FormattedStream.h"
37 #include "llvm/Support/InitLLVM.h"
38 #include "llvm/Support/LLVMDriver.h"
39 #include "llvm/Support/MemoryBuffer.h"
40 #include "llvm/Support/Path.h"
41 #include "llvm/Support/Process.h"
42 #include "llvm/Support/SourceMgr.h"
43 #include "llvm/Support/TargetSelect.h"
44 #include "llvm/Support/ToolOutputFile.h"
45 #include "llvm/Support/WithColor.h"
46 #include "llvm/TargetParser/Host.h"
51 using namespace llvm::opt
;
56 OPT_INVALID
= 0, // This is not an option ID.
57 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
62 #define PREFIX(NAME, VALUE) \
63 static constexpr StringLiteral NAME##_init[] = VALUE; \
64 static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
65 std::size(NAME##_init) - 1);
69 static constexpr opt::OptTable::Info InfoTable
[] = {
70 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
75 class MLOptTable
: public opt::GenericOptTable
{
77 MLOptTable() : opt::GenericOptTable(InfoTable
, /*IgnoreCase=*/false) {}
81 static Triple
GetTriple(StringRef ProgName
, opt::InputArgList
&Args
) {
82 // Figure out the target triple.
83 StringRef DefaultBitness
= "32";
84 SmallString
<255> Program
= ProgName
;
85 sys::path::replace_extension(Program
, "");
86 if (Program
.endswith("ml64"))
87 DefaultBitness
= "64";
89 StringRef TripleName
=
90 StringSwitch
<StringRef
>(Args
.getLastArgValue(OPT_bitness
, DefaultBitness
))
91 .Case("32", "i386-pc-windows")
92 .Case("64", "x86_64-pc-windows")
94 return Triple(Triple::normalize(TripleName
));
97 static std::unique_ptr
<ToolOutputFile
> GetOutputStream(StringRef Path
) {
99 auto Out
= std::make_unique
<ToolOutputFile
>(Path
, EC
, sys::fs::OF_None
);
101 WithColor::error() << EC
.message() << '\n';
108 static int AsLexInput(SourceMgr
&SrcMgr
, MCAsmInfo
&MAI
, raw_ostream
&OS
) {
110 Lexer
.setBuffer(SrcMgr
.getMemoryBuffer(SrcMgr
.getMainFileID())->getBuffer());
111 Lexer
.setLexMasmIntegers(true);
112 Lexer
.useMasmDefaultRadix(true);
113 Lexer
.setLexMasmHexFloats(true);
114 Lexer
.setLexMasmStrings(true);
117 while (Lexer
.Lex().isNot(AsmToken::Eof
)) {
118 Lexer
.getTok().dump(OS
);
120 if (Lexer
.getTok().getKind() == AsmToken::Error
)
127 static int AssembleInput(StringRef ProgName
, const Target
*TheTarget
,
128 SourceMgr
&SrcMgr
, MCContext
&Ctx
, MCStreamer
&Str
,
129 MCAsmInfo
&MAI
, MCSubtargetInfo
&STI
,
130 MCInstrInfo
&MCII
, MCTargetOptions
&MCOptions
,
131 const opt::ArgList
&InputArgs
) {
134 if (InputArgs
.hasArg(OPT_timestamp
)) {
135 StringRef TimestampStr
= InputArgs
.getLastArgValue(OPT_timestamp
);
136 int64_t IntTimestamp
;
137 if (TimestampStr
.getAsInteger(10, IntTimestamp
)) {
138 WithColor::error(errs(), ProgName
)
139 << "invalid timestamp '" << TimestampStr
140 << "'; must be expressed in seconds since the UNIX epoch.\n";
143 Timestamp
= IntTimestamp
;
145 Timestamp
= time(nullptr);
147 if (InputArgs
.hasArg(OPT_utc
)) {
149 TM
= *gmtime(&Timestamp
);
152 TM
= *localtime(&Timestamp
);
155 std::unique_ptr
<MCAsmParser
> Parser(
156 createMCMasmParser(SrcMgr
, Ctx
, Str
, MAI
, TM
, 0));
157 std::unique_ptr
<MCTargetAsmParser
> TAP(
158 TheTarget
->createMCAsmParser(STI
, *Parser
, MCII
, MCOptions
));
161 WithColor::error(errs(), ProgName
)
162 << "this target does not support assembly parsing.\n";
166 Parser
->setShowParsedOperands(InputArgs
.hasArg(OPT_show_inst_operands
));
167 Parser
->setTargetParser(*TAP
);
168 Parser
->getLexer().setLexMasmIntegers(true);
169 Parser
->getLexer().useMasmDefaultRadix(true);
170 Parser
->getLexer().setLexMasmHexFloats(true);
171 Parser
->getLexer().setLexMasmStrings(true);
173 auto Defines
= InputArgs
.getAllArgValues(OPT_define
);
174 for (StringRef Define
: Defines
) {
175 const auto NameValue
= Define
.split('=');
176 StringRef Name
= NameValue
.first
, Value
= NameValue
.second
;
177 if (Parser
->defineMacro(Name
, Value
)) {
178 WithColor::error(errs(), ProgName
)
179 << "can't define macro '" << Name
<< "' = '" << Value
<< "'\n";
184 int Res
= Parser
->Run(/*NoInitialTextSection=*/true);
189 int llvm_ml_main(int Argc
, char **Argv
, const llvm::ToolContext
&) {
190 InitLLVM
X(Argc
, Argv
);
191 StringRef ProgName
= sys::path::filename(Argv
[0]);
193 // Initialize targets and assembly printers/parsers.
194 llvm::InitializeAllTargetInfos();
195 llvm::InitializeAllTargetMCs();
196 llvm::InitializeAllAsmParsers();
197 llvm::InitializeAllDisassemblers();
200 unsigned MissingArgIndex
, MissingArgCount
;
201 ArrayRef
<const char *> ArgsArr
= ArrayRef(Argv
+ 1, Argc
- 1);
202 opt::InputArgList InputArgs
=
203 T
.ParseArgs(ArgsArr
, MissingArgIndex
, MissingArgCount
);
205 std::string InputFilename
;
206 for (auto *Arg
: InputArgs
.filtered(OPT_INPUT
)) {
207 std::string ArgString
= Arg
->getAsString(InputArgs
);
209 std::error_code IsFileEC
=
210 llvm::sys::fs::is_regular_file(ArgString
, IsFile
);
211 if (ArgString
== "-" || IsFile
) {
212 if (!InputFilename
.empty()) {
213 WithColor::warning(errs(), ProgName
)
214 << "does not support multiple assembly files in one command; "
215 << "ignoring '" << InputFilename
<< "'\n";
217 InputFilename
= ArgString
;
220 raw_string_ostream
OS(Diag
);
221 OS
<< ArgString
<< ": " << IsFileEC
.message();
224 if (T
.findNearest(ArgString
, Nearest
) < 2)
225 OS
<< ", did you mean '" << Nearest
<< "'?";
227 WithColor::error(errs(), ProgName
) << OS
.str() << '\n';
231 for (auto *Arg
: InputArgs
.filtered(OPT_assembly_file
)) {
232 if (!InputFilename
.empty()) {
233 WithColor::warning(errs(), ProgName
)
234 << "does not support multiple assembly files in one command; "
235 << "ignoring '" << InputFilename
<< "'\n";
237 InputFilename
= Arg
->getValue();
240 for (auto *Arg
: InputArgs
.filtered(OPT_unsupported_Group
)) {
241 WithColor::warning(errs(), ProgName
)
242 << "ignoring unsupported '" << Arg
->getOption().getName()
246 if (InputArgs
.hasArg(OPT_debug
)) {
249 for (auto *Arg
: InputArgs
.filtered(OPT_debug_only
)) {
250 setCurrentDebugTypes(Arg
->getValues().data(), Arg
->getNumValues());
253 if (InputArgs
.hasArg(OPT_help
)) {
254 std::string Usage
= llvm::formatv("{0} [ /options ] file", ProgName
).str();
255 T
.printHelp(outs(), Usage
.c_str(), "LLVM MASM Assembler",
256 /*ShowHidden=*/false);
258 } else if (InputFilename
.empty()) {
259 outs() << "USAGE: " << ProgName
<< " [ /options ] file\n"
260 << "Run \"" << ProgName
<< " /?\" or \"" << ProgName
261 << " /help\" for more info.\n";
265 MCTargetOptions MCOptions
;
266 MCOptions
.AssemblyLanguage
= "masm";
267 MCOptions
.MCFatalWarnings
= InputArgs
.hasArg(OPT_fatal_warnings
);
269 Triple TheTriple
= GetTriple(ProgName
, InputArgs
);
271 const Target
*TheTarget
= TargetRegistry::lookupTarget("", TheTriple
, Error
);
273 WithColor::error(errs(), ProgName
) << Error
;
276 const std::string
&TripleName
= TheTriple
.getTriple();
278 bool SafeSEH
= InputArgs
.hasArg(OPT_safeseh
);
279 if (SafeSEH
&& !(TheTriple
.isArch32Bit() && TheTriple
.isX86())) {
281 << "/safeseh applies only to 32-bit X86 platforms; ignoring.\n";
285 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferPtr
=
286 MemoryBuffer::getFileOrSTDIN(InputFilename
);
287 if (std::error_code EC
= BufferPtr
.getError()) {
288 WithColor::error(errs(), ProgName
)
289 << InputFilename
<< ": " << EC
.message() << '\n';
295 // Tell SrcMgr about this buffer, which is what the parser will pick up.
296 SrcMgr
.AddNewSourceBuffer(std::move(*BufferPtr
), SMLoc());
298 // Record the location of the include directories so that the lexer can find
299 // included files later.
300 std::vector
<std::string
> IncludeDirs
=
301 InputArgs
.getAllArgValues(OPT_include_path
);
302 if (!InputArgs
.hasArg(OPT_ignore_include_envvar
)) {
303 if (std::optional
<std::string
> IncludeEnvVar
=
304 llvm::sys::Process::GetEnv("INCLUDE")) {
305 SmallVector
<StringRef
, 8> Dirs
;
306 StringRef(*IncludeEnvVar
)
307 .split(Dirs
, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
308 IncludeDirs
.reserve(IncludeDirs
.size() + Dirs
.size());
309 for (StringRef Dir
: Dirs
)
310 IncludeDirs
.push_back(Dir
.str());
313 SrcMgr
.setIncludeDirs(IncludeDirs
);
315 std::unique_ptr
<MCRegisterInfo
> MRI(TheTarget
->createMCRegInfo(TripleName
));
316 assert(MRI
&& "Unable to create target register info!");
318 std::unique_ptr
<MCAsmInfo
> MAI(
319 TheTarget
->createMCAsmInfo(*MRI
, TripleName
, MCOptions
));
320 assert(MAI
&& "Unable to create target asm info!");
322 MAI
->setPreserveAsmComments(InputArgs
.hasArg(OPT_preserve_comments
));
324 std::unique_ptr
<MCSubtargetInfo
> STI(TheTarget
->createMCSubtargetInfo(
325 TripleName
, /*CPU=*/"", /*Features=*/""));
326 assert(STI
&& "Unable to create subtarget info!");
328 // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
329 // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
330 MCContext
Ctx(TheTriple
, MAI
.get(), MRI
.get(), STI
.get(), &SrcMgr
);
331 std::unique_ptr
<MCObjectFileInfo
> MOFI(TheTarget
->createMCObjectFileInfo(
332 Ctx
, /*PIC=*/false, /*LargeCodeModel=*/true));
333 Ctx
.setObjectFileInfo(MOFI
.get());
335 if (InputArgs
.hasArg(OPT_save_temp_labels
))
336 Ctx
.setAllowTemporaryLabels(false);
338 // Set compilation information.
339 SmallString
<128> CWD
;
340 if (!sys::fs::current_path(CWD
))
341 Ctx
.setCompilationDir(CWD
);
342 Ctx
.setMainFileName(InputFilename
);
344 StringRef FileType
= InputArgs
.getLastArgValue(OPT_filetype
, "obj");
345 SmallString
<255> DefaultOutputFilename
;
346 if (InputArgs
.hasArg(OPT_as_lex
)) {
347 DefaultOutputFilename
= "-";
349 DefaultOutputFilename
= InputFilename
;
350 sys::path::replace_extension(DefaultOutputFilename
, FileType
);
352 const StringRef OutputFilename
=
353 InputArgs
.getLastArgValue(OPT_output_file
, DefaultOutputFilename
);
354 std::unique_ptr
<ToolOutputFile
> Out
= GetOutputStream(OutputFilename
);
358 std::unique_ptr
<buffer_ostream
> BOS
;
359 raw_pwrite_stream
*OS
= &Out
->os();
360 std::unique_ptr
<MCStreamer
> Str
;
362 std::unique_ptr
<MCInstrInfo
> MCII(TheTarget
->createMCInstrInfo());
363 assert(MCII
&& "Unable to create instruction info!");
365 MCInstPrinter
*IP
= nullptr;
366 if (FileType
== "s") {
367 const bool OutputATTAsm
= InputArgs
.hasArg(OPT_output_att_asm
);
368 const unsigned OutputAsmVariant
= OutputATTAsm
? 0U // ATT dialect
369 : 1U; // Intel dialect
370 IP
= TheTarget
->createMCInstPrinter(TheTriple
, OutputAsmVariant
, *MAI
,
375 << "unable to create instruction printer for target triple '"
376 << TheTriple
.normalize() << "' with "
377 << (OutputATTAsm
? "ATT" : "Intel") << " assembly variant.\n";
381 // Set the display preference for hex vs. decimal immediates.
382 IP
->setPrintImmHex(InputArgs
.hasArg(OPT_print_imm_hex
));
384 // Set up the AsmStreamer.
385 std::unique_ptr
<MCCodeEmitter
> CE
;
386 if (InputArgs
.hasArg(OPT_show_encoding
))
387 CE
.reset(TheTarget
->createMCCodeEmitter(*MCII
, Ctx
));
389 std::unique_ptr
<MCAsmBackend
> MAB(
390 TheTarget
->createMCAsmBackend(*STI
, *MRI
, MCOptions
));
391 auto FOut
= std::make_unique
<formatted_raw_ostream
>(*OS
);
392 Str
.reset(TheTarget
->createAsmStreamer(
393 Ctx
, std::move(FOut
), /*asmverbose*/ true,
394 /*useDwarfDirectory*/ true, IP
, std::move(CE
), std::move(MAB
),
395 InputArgs
.hasArg(OPT_show_inst
)));
397 } else if (FileType
== "null") {
398 Str
.reset(TheTarget
->createNullStreamer(Ctx
));
399 } else if (FileType
== "obj") {
400 if (!Out
->os().supportsSeeking()) {
401 BOS
= std::make_unique
<buffer_ostream
>(Out
->os());
405 MCCodeEmitter
*CE
= TheTarget
->createMCCodeEmitter(*MCII
, Ctx
);
406 MCAsmBackend
*MAB
= TheTarget
->createMCAsmBackend(*STI
, *MRI
, MCOptions
);
407 Str
.reset(TheTarget
->createMCObjectStreamer(
408 TheTriple
, Ctx
, std::unique_ptr
<MCAsmBackend
>(MAB
),
409 MAB
->createObjectWriter(*OS
), std::unique_ptr
<MCCodeEmitter
>(CE
), *STI
,
410 MCOptions
.MCRelaxAll
, MCOptions
.MCIncrementalLinkerCompatible
,
411 /*DWARFMustBeAtTheEnd*/ false));
413 llvm_unreachable("Invalid file type!");
416 if (TheTriple
.isOSBinFormatCOFF()) {
417 // Emit an absolute @feat.00 symbol. This is a features bitfield read by
419 int64_t Feat00Flags
= 0x2;
421 // According to the PE-COFF spec, the LSB of this value marks the object
422 // for "registered SEH". This means that all SEH handler entry points
423 // must be registered in .sxdata. Use of any unregistered handlers will
424 // cause the process to terminate immediately.
427 MCSymbol
*Feat00Sym
= Ctx
.getOrCreateSymbol("@feat.00");
428 Feat00Sym
->setRedefinable(true);
429 Str
->emitSymbolAttribute(Feat00Sym
, MCSA_Global
);
430 Str
->emitAssignment(Feat00Sym
, MCConstantExpr::create(Feat00Flags
, Ctx
));
433 // Use Assembler information for parsing.
434 Str
->setUseAssemblerInfoForParsing(true);
437 if (InputArgs
.hasArg(OPT_as_lex
)) {
438 // -as-lex; Lex only, and output a stream of tokens
439 Res
= AsLexInput(SrcMgr
, *MAI
, Out
->os());
441 Res
= AssembleInput(ProgName
, TheTarget
, SrcMgr
, Ctx
, *Str
, *MAI
, *STI
,
442 *MCII
, MCOptions
, InputArgs
);
445 // Keep output if no errors.