Skip to content

Commit f789159

Browse files
committed
[ms] [llvm-ml] Implement support for PROC NEAR/FAR
Matches ML.EXE by translating "ret" instructions inside a `PROC FAR` to "retf", and automatically prepending a `push cs` to all near calls to a `PROC FAR`.
1 parent c3f7502 commit f789159

File tree

5 files changed

+178
-19
lines changed

5 files changed

+178
-19
lines changed

llvm/include/llvm/MC/MCContext.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,13 @@ class MCContext {
9797
IsDXContainer
9898
};
9999

100+
enum DefaultRetType { IsNear, IsFar };
101+
100102
private:
101103
Environment Env;
102104

105+
DefaultRetType DefaultRet = IsNear;
106+
103107
/// The name of the Segment where Swift5 Reflection Section data will be
104108
/// outputted
105109
StringRef Swift5ReflectionSegmentName;
@@ -394,6 +398,9 @@ class MCContext {
394398

395399
Environment getObjectFileType() const { return Env; }
396400

401+
DefaultRetType getDefaultRetType() const { return DefaultRet; }
402+
void setDefaultRetType(DefaultRetType DR) { DefaultRet = DR; }
403+
397404
const StringRef &getSwift5ReflectionSegmentName() const {
398405
return Swift5ReflectionSegmentName;
399406
}

llvm/include/llvm/MC/MCSymbolCOFF.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class MCSymbolCOFF : public MCSymbol {
2525
SF_ClassShift = 0,
2626

2727
SF_SafeSEH = 0x0100,
28+
SF_FarProc = 0x0200,
2829
SF_WeakExternalCharacteristicsMask = 0x0E00,
2930
SF_WeakExternalCharacteristicsShift = 9,
3031
};
@@ -66,6 +67,13 @@ class MCSymbolCOFF : public MCSymbol {
6667
modifyFlags(SF_SafeSEH, SF_SafeSEH);
6768
}
6869

70+
bool isFarProc() const {
71+
return getFlags() & SF_FarProc;
72+
}
73+
void setIsFarProc() const {
74+
modifyFlags(SF_FarProc, SF_FarProc);
75+
}
76+
6977
static bool classof(const MCSymbol *S) { return S->isCOFF(); }
7078
};
7179

llvm/lib/MC/MCParser/COFFMasmParser.cpp

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "third_party/llvm/llvm-project/llvm/include/llvm/MC/MCContext.h"
910
#include "llvm/ADT/StringRef.h"
1011
#include "llvm/ADT/Twine.h"
1112
#include "llvm/BinaryFormat/COFF.h"
@@ -41,6 +42,7 @@ class COFFMasmParser : public MCAsmParserExtension {
4142
StringRef COMDATSymName, COFF::COMDATType Type,
4243
Align Alignment);
4344

45+
bool parseDirectiveModel(StringRef, SMLoc);
4446
bool parseDirectiveProc(StringRef, SMLoc);
4547
bool parseDirectiveEndProc(StringRef, SMLoc);
4648
bool parseDirectiveSegment(StringRef, SMLoc);
@@ -167,7 +169,7 @@ class COFFMasmParser : public MCAsmParserExtension {
167169
// .exit
168170
// .fardata
169171
// .fardata?
170-
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
172+
addDirectiveHandler<&COFFMasmParser::parseDirectiveModel>(".model");
171173
// .stack
172174
// .startup
173175

@@ -201,8 +203,13 @@ class COFFMasmParser : public MCAsmParserExtension {
201203
}
202204

203205
/// Stack of active procedure definitions.
204-
SmallVector<StringRef, 1> CurrentProcedures;
205-
SmallVector<bool, 1> CurrentProceduresFramed;
206+
enum ProcDistance { PROC_DISTANCE_NEAR = 0, PROC_DISTANCE_FAR = 1 };
207+
struct ProcInfo {
208+
StringRef Name;
209+
ProcDistance Distance = PROC_DISTANCE_NEAR;
210+
bool IsFramed = false;
211+
};
212+
SmallVector<ProcInfo, 1> CurrentProcedures;
206213

207214
public:
208215
COFFMasmParser() = default;
@@ -435,48 +442,75 @@ bool COFFMasmParser::parseDirectiveOption(StringRef Directive, SMLoc Loc) {
435442
return false;
436443
}
437444

445+
/// parseDirectiveModel
446+
/// ::= ".model" "flat"
447+
bool COFFMasmParser::parseDirectiveModel(StringRef Directive, SMLoc Loc) {
448+
if (!getLexer().is(AsmToken::Identifier))
449+
return TokError("expected identifier in directive");
450+
451+
StringRef ModelType = getTok().getIdentifier();
452+
if (!ModelType.equals_insensitive("flat")) {
453+
return TokError(
454+
"expected 'flat' for memory model; no other models supported");
455+
}
456+
457+
// Ignore; no action necessary.
458+
Lex();
459+
return false;
460+
}
461+
438462
/// parseDirectiveProc
439463
/// TODO(epastor): Implement parameters and other attributes.
440-
/// ::= label "proc" [[distance]]
464+
/// ::= label "proc" [[distance]] [[frame]]
441465
/// statements
442466
/// label "endproc"
443467
bool COFFMasmParser::parseDirectiveProc(StringRef Directive, SMLoc Loc) {
444468
if (!getStreamer().getCurrentFragment())
445469
return Error(getTok().getLoc(), "expected section directive");
446470

447-
StringRef Label;
448-
if (getParser().parseIdentifier(Label))
471+
ProcInfo Proc;
472+
if (getParser().parseIdentifier(Proc.Name))
449473
return Error(Loc, "expected identifier for procedure");
450474
if (getLexer().is(AsmToken::Identifier)) {
451475
StringRef nextVal = getTok().getString();
452476
SMLoc nextLoc = getTok().getLoc();
453477
if (nextVal.equals_insensitive("far")) {
454-
// TODO(epastor): Handle far procedure definitions.
455478
Lex();
456-
return Error(nextLoc, "far procedure definitions not yet supported");
479+
Proc.Distance = PROC_DISTANCE_FAR;
480+
nextVal = getTok().getString();
481+
nextLoc = getTok().getLoc();
457482
} else if (nextVal.equals_insensitive("near")) {
458483
Lex();
484+
Proc.Distance = PROC_DISTANCE_NEAR;
459485
nextVal = getTok().getString();
460486
nextLoc = getTok().getLoc();
461487
}
462488
}
463-
MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
489+
MCSymbolCOFF *Sym =
490+
cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Proc.Name));
464491

465492
// Define symbol as simple external function
466493
Sym->setExternal(true);
467494
Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
495+
if (Proc.Distance == PROC_DISTANCE_FAR)
496+
Sym->setIsFarProc();
497+
498+
getContext().setDefaultRetType(Proc.Distance == PROC_DISTANCE_NEAR
499+
? MCContext::IsNear
500+
: MCContext::IsFar);
468501

469-
bool Framed = false;
470502
if (getLexer().is(AsmToken::Identifier) &&
471503
getTok().getString().equals_insensitive("frame")) {
472504
Lex();
473-
Framed = true;
505+
Proc.IsFramed = true;
506+
getStreamer().emitWinCFIStartProc(Sym, Loc);
507+
}
508+
if (Proc.IsFramed) {
474509
getStreamer().emitWinCFIStartProc(Sym, Loc);
475510
}
476511
getStreamer().emitLabel(Sym, Loc);
477512

478-
CurrentProcedures.push_back(Label);
479-
CurrentProceduresFramed.push_back(Framed);
513+
CurrentProcedures.push_back(std::move(Proc));
480514
return false;
481515
}
482516
bool COFFMasmParser::parseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
@@ -487,15 +521,19 @@ bool COFFMasmParser::parseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
487521

488522
if (CurrentProcedures.empty())
489523
return Error(Loc, "endp outside of procedure block");
490-
else if (!CurrentProcedures.back().equals_insensitive(Label))
524+
else if (!CurrentProcedures.back().Name.equals_insensitive(Label))
491525
return Error(LabelLoc, "endp does not match current procedure '" +
492-
CurrentProcedures.back() + "'");
526+
CurrentProcedures.back().Name + "'");
493527

494-
if (CurrentProceduresFramed.back()) {
528+
if (CurrentProcedures.back().IsFramed) {
495529
getStreamer().emitWinCFIEndProc(Loc);
496530
}
497531
CurrentProcedures.pop_back();
498-
CurrentProceduresFramed.pop_back();
532+
getContext().setDefaultRetType(
533+
(CurrentProcedures.empty() ||
534+
CurrentProcedures.back().Distance == PROC_DISTANCE_NEAR)
535+
? MCContext::IsNear
536+
: MCContext::IsFar);
499537
return false;
500538
}
501539

llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "MCTargetDesc/X86TargetStreamer.h"
1515
#include "TargetInfo/X86TargetInfo.h"
1616
#include "X86Operand.h"
17+
#include "third_party/llvm/llvm-project/llvm/include/llvm/MC/MCInst.h"
1718
#include "llvm/ADT/STLExtras.h"
1819
#include "llvm/ADT/SmallString.h"
1920
#include "llvm/ADT/SmallVector.h"
@@ -32,6 +33,7 @@
3233
#include "llvm/MC/MCStreamer.h"
3334
#include "llvm/MC/MCSubtargetInfo.h"
3435
#include "llvm/MC/MCSymbol.h"
36+
#include "llvm/MC/MCSymbolCOFF.h"
3537
#include "llvm/MC/TargetRegistry.h"
3638
#include "llvm/Support/CommandLine.h"
3739
#include "llvm/Support/Compiler.h"
@@ -1202,6 +1204,10 @@ class X86AsmParser : public MCTargetAsmParser {
12021204
void MatchFPUWaitAlias(SMLoc IDLoc, X86Operand &Op, OperandVector &Operands,
12031205
MCStreamer &Out, bool MatchingInlineAsm);
12041206

1207+
void MatchMASMFarCallToNear(SMLoc IDLoc, X86Operand &Op,
1208+
OperandVector &Operands, MCStreamer &Out,
1209+
bool MatchingInlineAsm);
1210+
12051211
bool ErrorMissingFeature(SMLoc IDLoc, const FeatureBitset &MissingFeatures,
12061212
bool MatchingInlineAsm);
12071213

@@ -2740,11 +2746,11 @@ bool X86AsmParser::parseIntelOperand(OperandVector &Operands, StringRef Name) {
27402746
if ((BaseReg || IndexReg || RegNo || DefaultBaseReg))
27412747
Operands.push_back(X86Operand::CreateMem(
27422748
getPointerWidth(), RegNo, Disp, BaseReg, IndexReg, Scale, Start, End,
2743-
Size, DefaultBaseReg, /*SymName=*/StringRef(), /*OpDecl=*/nullptr,
2749+
Size, DefaultBaseReg, /*SymName=*/SM.getSymName(), /*OpDecl=*/nullptr,
27442750
/*FrontendSize=*/0, /*UseUpRegs=*/false, MaybeDirectBranchDest));
27452751
else
27462752
Operands.push_back(X86Operand::CreateMem(
2747-
getPointerWidth(), Disp, Start, End, Size, /*SymName=*/StringRef(),
2753+
getPointerWidth(), Disp, Start, End, Size, /*SymName=*/SM.getSymName(),
27482754
/*OpDecl=*/nullptr, /*FrontendSize=*/0, /*UseUpRegs=*/false,
27492755
MaybeDirectBranchDest));
27502756
return false;
@@ -3442,6 +3448,14 @@ bool X86AsmParser::parseInstruction(ParseInstructionInfo &Info, StringRef Name,
34423448
}
34433449
}
34443450

3451+
if (Parser.isParsingMasm() && !is64BitMode()) {
3452+
// MASM implicitly converts "ret" to "retf" in far procedures; this is
3453+
// reflected in the default return type in the MCContext.
3454+
if (PatchedName == "ret" &&
3455+
getContext().getDefaultRetType() == MCContext::IsFar)
3456+
PatchedName = "retf";
3457+
}
3458+
34453459
// Determine whether this is an instruction prefix.
34463460
// FIXME:
34473461
// Enhance prefixes integrity robustness. for example, following forms
@@ -4130,6 +4144,11 @@ bool X86AsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
41304144
// First, handle aliases that expand to multiple instructions.
41314145
MatchFPUWaitAlias(IDLoc, static_cast<X86Operand &>(*Operands[0]), Operands,
41324146
Out, MatchingInlineAsm);
4147+
if (getParser().isParsingMasm() && !is64BitMode()) {
4148+
MatchMASMFarCallToNear(IDLoc, static_cast<X86Operand &>(*Operands[0]),
4149+
Operands, Out, MatchingInlineAsm);
4150+
}
4151+
41334152
unsigned Prefixes = getPrefixes(Operands);
41344153

41354154
MCInst Inst;
@@ -4191,6 +4210,37 @@ void X86AsmParser::MatchFPUWaitAlias(SMLoc IDLoc, X86Operand &Op,
41914210
}
41924211
}
41934212

4213+
void X86AsmParser::MatchMASMFarCallToNear(SMLoc IDLoc, X86Operand &Op,
4214+
OperandVector &Operands,
4215+
MCStreamer &Out,
4216+
bool MatchingInlineAsm) {
4217+
// FIXME: This should be replaced with a real .td file alias mechanism.
4218+
// Also, MatchInstructionImpl should actually *do* the EmitInstruction
4219+
// call.
4220+
if (Op.getToken() != "call")
4221+
return;
4222+
// This is a call instruction...
4223+
4224+
X86Operand &Operand = static_cast<X86Operand &>(*Operands[1]);
4225+
MCSymbol *Sym = getContext().lookupSymbol(Operand.getSymName());
4226+
if (Sym == nullptr || !Sym->isInSection() || !Sym->isCOFF() ||
4227+
!dyn_cast<MCSymbolCOFF>(Sym)->isFarProc())
4228+
return;
4229+
// Sym is a reference to a far proc in a code section....
4230+
4231+
if (Out.getCurrentSectionOnly() == &Sym->getSection()) {
4232+
// This is a call to a symbol declared as a far proc, and will be emitted as
4233+
// a near call... so we need to explicitly push the code section register
4234+
// before the call.
4235+
MCInst Inst;
4236+
Inst.setOpcode(X86::PUSH32r);
4237+
Inst.addOperand(MCOperand::createReg(MCRegister(X86::CS)));
4238+
Inst.setLoc(IDLoc);
4239+
if (!MatchingInlineAsm)
4240+
emitInstruction(Inst, Operands, Out);
4241+
}
4242+
}
4243+
41944244
bool X86AsmParser::ErrorMissingFeature(SMLoc IDLoc,
41954245
const FeatureBitset &MissingFeatures,
41964246
bool MatchingInlineAsm) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
; RUN: llvm-ml -m32 -filetype=s %s /Fo - | FileCheck %s
2+
3+
.code
4+
5+
DefaultProc PROC
6+
ret
7+
DefaultProc ENDP
8+
; CHECK: DefaultProc:
9+
; CHECK: {{^ *}}ret{{ *$}}
10+
11+
t1:
12+
call DefaultProc
13+
; CHECK: t1:
14+
; CHECK-NEXT: call DefaultProc
15+
16+
NearProc PROC NEAR
17+
ret
18+
NearProc ENDP
19+
; CHECK: NearProc:
20+
; CHECK: {{^ *}}ret{{ *$}}
21+
22+
t2:
23+
call NearProc
24+
; CHECK: t2:
25+
; CHECK-NEXT: call NearProc
26+
27+
FarProcInCode PROC FAR
28+
ret
29+
FarProcInCode ENDP
30+
; CHECK: FarProcInCode:
31+
; CHECK: {{^ *}}retf{{ *$}}
32+
33+
t3:
34+
call FarProcInCode
35+
; CHECK: t3:
36+
; CHECK-NEXT: push cs
37+
; CHECK-NEXT: call FarProcInCode
38+
39+
FarCode SEGMENT SHARED NOPAGE NOCACHE INFO READ WRITE EXECUTE DISCARD
40+
41+
FarProcInFarCode PROC FAR
42+
ret
43+
FarProcInFarCode ENDP
44+
; CHECK: FarProcInFarCode:
45+
; CHECK: {{^ *}}retf{{ *$}}
46+
47+
FarCode ENDS
48+
49+
.code
50+
51+
t4:
52+
call FarProcInFarCode
53+
; CHECK: t4:
54+
; CHECK-NEXT: call FarProcInFarCode
55+
56+
END

0 commit comments

Comments
 (0)