Skip to content

[ms] [llvm-ml] Implement support for PROC NEAR/FAR #131707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions llvm/include/llvm/MC/MCParser/MCMasmParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===- llvm/MC/MasmParser.h - MASM Parser Interface -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_MC_MCPARSER_MCMASMPARSER_H
#define LLVM_MC_MCPARSER_MCMASMPARSER_H

#include "llvm/MC/MCParser/MCAsmParser.h"

namespace llvm {

/// MASM-type assembler parser interface.
class MCMasmParser : public MCAsmParser {
public:
virtual bool getDefaultRetIsFar() const = 0;
virtual void setDefaultRetIsFar(bool IsFar) = 0;

bool isParsingMasm() const override { return true; }

static bool classof(const MCAsmParser *AP) { return AP->isParsingMasm(); }
};

} // end namespace llvm

#endif // LLVM_MC_MCPARSER_MCMASMPARSER_H
4 changes: 4 additions & 0 deletions llvm/include/llvm/MC/MCSymbolCOFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class MCSymbolCOFF : public MCSymbol {
SF_ClassShift = 0,

SF_SafeSEH = 0x0100,
SF_FarProc = 0x0200,
SF_WeakExternalCharacteristicsMask = 0x0E00,
SF_WeakExternalCharacteristicsShift = 9,
};
Expand Down Expand Up @@ -66,6 +67,9 @@ class MCSymbolCOFF : public MCSymbol {
modifyFlags(SF_SafeSEH, SF_SafeSEH);
}

bool isFarProc() const { return getFlags() & SF_FarProc; }
void setIsFarProc() const { modifyFlags(SF_FarProc, SF_FarProc); }

static bool classof(const MCSymbol *S) { return S->isCOFF(); }
};

Expand Down
80 changes: 59 additions & 21 deletions llvm/lib/MC/MCParser/COFFMasmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
#include "llvm/MC/MCAsmMacro.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
#include "llvm/MC/MCParser/MCMasmParser.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCSectionCOFF.h"
#include "llvm/MC/MCStreamer.h"
Expand Down Expand Up @@ -41,6 +43,7 @@ class COFFMasmParser : public MCAsmParserExtension {
StringRef COMDATSymName, COFF::COMDATType Type,
Align Alignment);

bool parseDirectiveModel(StringRef, SMLoc);
bool parseDirectiveProc(StringRef, SMLoc);
bool parseDirectiveEndProc(StringRef, SMLoc);
bool parseDirectiveSegment(StringRef, SMLoc);
Expand Down Expand Up @@ -167,7 +170,7 @@ class COFFMasmParser : public MCAsmParserExtension {
// .exit
// .fardata
// .fardata?
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
addDirectiveHandler<&COFFMasmParser::parseDirectiveModel>(".model");
// .stack
// .startup

Expand Down Expand Up @@ -201,8 +204,13 @@ class COFFMasmParser : public MCAsmParserExtension {
}

/// Stack of active procedure definitions.
SmallVector<StringRef, 1> CurrentProcedures;
SmallVector<bool, 1> CurrentProceduresFramed;
enum ProcDistance { PROC_DISTANCE_NEAR = 0, PROC_DISTANCE_FAR = 1 };
struct ProcInfo {
StringRef Name;
ProcDistance Distance = PROC_DISTANCE_NEAR;
bool IsFramed = false;
};
SmallVector<ProcInfo, 1> CurrentProcedures;

public:
COFFMasmParser() = default;
Expand Down Expand Up @@ -435,48 +443,75 @@ bool COFFMasmParser::parseDirectiveOption(StringRef Directive, SMLoc Loc) {
return false;
}

/// parseDirectiveModel
/// ::= ".model" "flat"
bool COFFMasmParser::parseDirectiveModel(StringRef Directive, SMLoc Loc) {
if (!getLexer().is(AsmToken::Identifier))
return TokError("expected identifier in directive");

StringRef ModelType = getTok().getIdentifier();
if (!ModelType.equals_insensitive("flat")) {
return TokError(
"expected 'flat' for memory model; no other models supported");
}

// Ignore; no action necessary.
Lex();
return false;
}

/// parseDirectiveProc
/// TODO(epastor): Implement parameters and other attributes.
/// ::= label "proc" [[distance]]
/// ::= label "proc" [[distance]] [[frame]]
/// statements
/// label "endproc"
bool COFFMasmParser::parseDirectiveProc(StringRef Directive, SMLoc Loc) {
if (!getStreamer().getCurrentFragment())
return Error(getTok().getLoc(), "expected section directive");

StringRef Label;
if (getParser().parseIdentifier(Label))
ProcInfo Proc;
if (getParser().parseIdentifier(Proc.Name))
return Error(Loc, "expected identifier for procedure");
if (getLexer().is(AsmToken::Identifier)) {
while (getLexer().is(AsmToken::Identifier)) {
StringRef nextVal = getTok().getString();
SMLoc nextLoc = getTok().getLoc();
if (nextVal.equals_insensitive("far")) {
// TODO(epastor): Handle far procedure definitions.
Lex();
return Error(nextLoc, "far procedure definitions not yet supported");
Proc.Distance = PROC_DISTANCE_FAR;
nextVal = getTok().getString();
nextLoc = getTok().getLoc();
} else if (nextVal.equals_insensitive("near")) {
Lex();
Proc.Distance = PROC_DISTANCE_NEAR;
nextVal = getTok().getString();
nextLoc = getTok().getLoc();
} else if (nextVal.equals_insensitive("frame")) {
Lex();
Proc.IsFramed = true;
nextVal = getTok().getString();
nextLoc = getTok().getLoc();
} else {
break;
}
}
MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
MCSymbolCOFF *Sym =
cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Proc.Name));

// Define symbol as simple external function
Sym->setExternal(true);
Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
if (Proc.Distance == PROC_DISTANCE_FAR)
Sym->setIsFarProc();

cast<MCMasmParser>(getParser())
.setDefaultRetIsFar(Proc.Distance == PROC_DISTANCE_FAR);

bool Framed = false;
if (getLexer().is(AsmToken::Identifier) &&
getTok().getString().equals_insensitive("frame")) {
Lex();
Framed = true;
if (Proc.IsFramed) {
getStreamer().emitWinCFIStartProc(Sym, Loc);
}
getStreamer().emitLabel(Sym, Loc);

CurrentProcedures.push_back(Label);
CurrentProceduresFramed.push_back(Framed);
CurrentProcedures.push_back(std::move(Proc));
return false;
}
bool COFFMasmParser::parseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
Expand All @@ -487,15 +522,18 @@ bool COFFMasmParser::parseDirectiveEndProc(StringRef Directive, SMLoc Loc) {

if (CurrentProcedures.empty())
return Error(Loc, "endp outside of procedure block");
else if (!CurrentProcedures.back().equals_insensitive(Label))
else if (!CurrentProcedures.back().Name.equals_insensitive(Label))
return Error(LabelLoc, "endp does not match current procedure '" +
CurrentProcedures.back() + "'");
CurrentProcedures.back().Name + "'");

if (CurrentProceduresFramed.back()) {
if (CurrentProcedures.back().IsFramed) {
getStreamer().emitWinCFIEndProc(Loc);
}
CurrentProcedures.pop_back();
CurrentProceduresFramed.pop_back();
cast<MCMasmParser>(getParser())
.setDefaultRetIsFar(!CurrentProcedures.empty() &&
CurrentProcedures.back().Distance ==
PROC_DISTANCE_FAR);
return false;
}

Expand Down
17 changes: 14 additions & 3 deletions llvm/lib/MC/MCParser/MasmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
#include "llvm/MC/MCParser/MCMasmParser.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
Expand Down Expand Up @@ -65,6 +66,7 @@
#include <memory>
#include <optional>
#include <sstream>
#include <stdbool.h>
#include <string>
#include <tuple>
#include <utility>
Expand Down Expand Up @@ -373,7 +375,7 @@ FieldInitializer &FieldInitializer::operator=(FieldInitializer &&Initializer) {
/// The concrete assembly parser instance.
// Note that this is a full MCAsmParser, not an MCAsmParserExtension!
// It's a peer of AsmParser, not of COFFAsmParser, WasmAsmParser, etc.
class MasmParser : public MCAsmParser {
class MasmParser : public MCMasmParser {
private:
AsmLexer Lexer;
MCContext &Ctx;
Expand Down Expand Up @@ -456,6 +458,9 @@ class MasmParser : public MCAsmParser {
/// Are we parsing ms-style inline assembly?
bool ParsingMSInlineAsm = false;

/// Is the current default `ret` instruction far?
bool DefaultRetIsFar = false;

// Current <...> expression depth.
unsigned AngleBracketDepth = 0U;

Expand All @@ -481,6 +486,14 @@ class MasmParser : public MCAsmParser {
DirectiveKindMap[Directive] = DirectiveKindMap[Alias];
}

/// @name MCMasmParser Interface
/// {

bool getDefaultRetIsFar() const override { return DefaultRetIsFar; }
void setDefaultRetIsFar(bool IsFar) override { DefaultRetIsFar = IsFar; }

/// }

/// @name MCAsmParser Interface
/// {

Expand Down Expand Up @@ -517,8 +530,6 @@ class MasmParser : public MCAsmParser {
}
bool isParsingMSInlineAsm() override { return ParsingMSInlineAsm; }

bool isParsingMasm() const override { return true; }

bool defineMacro(StringRef Name, StringRef Value) override;

bool lookUpField(StringRef Name, AsmFieldInfo &Info) const override;
Expand Down
54 changes: 52 additions & 2 deletions llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCParser/MCMasmParser.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolCOFF.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
Expand Down Expand Up @@ -1202,6 +1204,10 @@ class X86AsmParser : public MCTargetAsmParser {
void MatchFPUWaitAlias(SMLoc IDLoc, X86Operand &Op, OperandVector &Operands,
MCStreamer &Out, bool MatchingInlineAsm);

void MatchMASMFarCallToNear(SMLoc IDLoc, X86Operand &Op,
OperandVector &Operands, MCStreamer &Out,
bool MatchingInlineAsm);

bool ErrorMissingFeature(SMLoc IDLoc, const FeatureBitset &MissingFeatures,
bool MatchingInlineAsm);

Expand Down Expand Up @@ -2740,11 +2746,11 @@ bool X86AsmParser::parseIntelOperand(OperandVector &Operands, StringRef Name) {
if ((BaseReg || IndexReg || RegNo || DefaultBaseReg))
Operands.push_back(X86Operand::CreateMem(
getPointerWidth(), RegNo, Disp, BaseReg, IndexReg, Scale, Start, End,
Size, DefaultBaseReg, /*SymName=*/StringRef(), /*OpDecl=*/nullptr,
Size, DefaultBaseReg, /*SymName=*/SM.getSymName(), /*OpDecl=*/nullptr,
/*FrontendSize=*/0, /*UseUpRegs=*/false, MaybeDirectBranchDest));
else
Operands.push_back(X86Operand::CreateMem(
getPointerWidth(), Disp, Start, End, Size, /*SymName=*/StringRef(),
getPointerWidth(), Disp, Start, End, Size, /*SymName=*/SM.getSymName(),
/*OpDecl=*/nullptr, /*FrontendSize=*/0, /*UseUpRegs=*/false,
MaybeDirectBranchDest));
return false;
Expand Down Expand Up @@ -3442,6 +3448,14 @@ bool X86AsmParser::parseInstruction(ParseInstructionInfo &Info, StringRef Name,
}
}

if (Parser.isParsingMasm() && !is64BitMode()) {
// MASM implicitly converts "ret" to "retf" in far procedures; this is
// reflected in the default return type in the MCContext.
if (PatchedName == "ret" &&
cast<MCMasmParser>(getParser()).getDefaultRetIsFar())
PatchedName = "retf";
}

// Determine whether this is an instruction prefix.
// FIXME:
// Enhance prefixes integrity robustness. for example, following forms
Expand Down Expand Up @@ -4130,6 +4144,11 @@ bool X86AsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
// First, handle aliases that expand to multiple instructions.
MatchFPUWaitAlias(IDLoc, static_cast<X86Operand &>(*Operands[0]), Operands,
Out, MatchingInlineAsm);
if (getParser().isParsingMasm() && !is64BitMode()) {
MatchMASMFarCallToNear(IDLoc, static_cast<X86Operand &>(*Operands[0]),
Operands, Out, MatchingInlineAsm);
}

unsigned Prefixes = getPrefixes(Operands);

MCInst Inst;
Expand Down Expand Up @@ -4191,6 +4210,37 @@ void X86AsmParser::MatchFPUWaitAlias(SMLoc IDLoc, X86Operand &Op,
}
}

void X86AsmParser::MatchMASMFarCallToNear(SMLoc IDLoc, X86Operand &Op,
OperandVector &Operands,
MCStreamer &Out,
bool MatchingInlineAsm) {
// FIXME: This should be replaced with a real .td file alias mechanism.
// Also, MatchInstructionImpl should actually *do* the EmitInstruction
// call.
if (Op.getToken() != "call")
return;
// This is a call instruction...

X86Operand &Operand = static_cast<X86Operand &>(*Operands[1]);
MCSymbol *Sym = getContext().lookupSymbol(Operand.getSymName());
if (Sym == nullptr || !Sym->isInSection() || !Sym->isCOFF() ||
!dyn_cast<MCSymbolCOFF>(Sym)->isFarProc())
return;
// Sym is a reference to a far proc in a code section....

if (Out.getCurrentSectionOnly() == &Sym->getSection()) {
// This is a call to a symbol declared as a far proc, and will be emitted as
// a near call... so we need to explicitly push the code section register
// before the call.
MCInst Inst;
Inst.setOpcode(X86::PUSH32r);
Inst.addOperand(MCOperand::createReg(MCRegister(X86::CS)));
Inst.setLoc(IDLoc);
if (!MatchingInlineAsm)
emitInstruction(Inst, Operands, Out);
}
}

bool X86AsmParser::ErrorMissingFeature(SMLoc IDLoc,
const FeatureBitset &MissingFeatures,
bool MatchingInlineAsm) {
Expand Down
Loading