Skip to content

Commit b15ee71

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 b15ee71

File tree

5 files changed

+173
-19
lines changed

5 files changed

+173
-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: 4 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,9 @@ class MCSymbolCOFF : public MCSymbol {
6667
modifyFlags(SF_SafeSEH, SF_SafeSEH);
6768
}
6869

70+
bool isFarProc() const { return getFlags() & SF_FarProc; }
71+
void setIsFarProc() const { modifyFlags(SF_FarProc, SF_FarProc); }
72+
6973
static bool classof(const MCSymbol *S) { return S->isCOFF(); }
7074
};
7175

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: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "llvm/MC/MCStreamer.h"
3333
#include "llvm/MC/MCSubtargetInfo.h"
3434
#include "llvm/MC/MCSymbol.h"
35+
#include "llvm/MC/MCSymbolCOFF.h"
3536
#include "llvm/MC/TargetRegistry.h"
3637
#include "llvm/Support/CommandLine.h"
3738
#include "llvm/Support/Compiler.h"
@@ -1202,6 +1203,10 @@ class X86AsmParser : public MCTargetAsmParser {
12021203
void MatchFPUWaitAlias(SMLoc IDLoc, X86Operand &Op, OperandVector &Operands,
12031204
MCStreamer &Out, bool MatchingInlineAsm);
12041205

1206+
void MatchMASMFarCallToNear(SMLoc IDLoc, X86Operand &Op,
1207+
OperandVector &Operands, MCStreamer &Out,
1208+
bool MatchingInlineAsm);
1209+
12051210
bool ErrorMissingFeature(SMLoc IDLoc, const FeatureBitset &MissingFeatures,
12061211
bool MatchingInlineAsm);
12071212

@@ -2740,11 +2745,11 @@ bool X86AsmParser::parseIntelOperand(OperandVector &Operands, StringRef Name) {
27402745
if ((BaseReg || IndexReg || RegNo || DefaultBaseReg))
27412746
Operands.push_back(X86Operand::CreateMem(
27422747
getPointerWidth(), RegNo, Disp, BaseReg, IndexReg, Scale, Start, End,
2743-
Size, DefaultBaseReg, /*SymName=*/StringRef(), /*OpDecl=*/nullptr,
2748+
Size, DefaultBaseReg, /*SymName=*/SM.getSymName(), /*OpDecl=*/nullptr,
27442749
/*FrontendSize=*/0, /*UseUpRegs=*/false, MaybeDirectBranchDest));
27452750
else
27462751
Operands.push_back(X86Operand::CreateMem(
2747-
getPointerWidth(), Disp, Start, End, Size, /*SymName=*/StringRef(),
2752+
getPointerWidth(), Disp, Start, End, Size, /*SymName=*/SM.getSymName(),
27482753
/*OpDecl=*/nullptr, /*FrontendSize=*/0, /*UseUpRegs=*/false,
27492754
MaybeDirectBranchDest));
27502755
return false;
@@ -3442,6 +3447,14 @@ bool X86AsmParser::parseInstruction(ParseInstructionInfo &Info, StringRef Name,
34423447
}
34433448
}
34443449

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

41354153
MCInst Inst;
@@ -4191,6 +4209,37 @@ void X86AsmParser::MatchFPUWaitAlias(SMLoc IDLoc, X86Operand &Op,
41914209
}
41924210
}
41934211

4212+
void X86AsmParser::MatchMASMFarCallToNear(SMLoc IDLoc, X86Operand &Op,
4213+
OperandVector &Operands,
4214+
MCStreamer &Out,
4215+
bool MatchingInlineAsm) {
4216+
// FIXME: This should be replaced with a real .td file alias mechanism.
4217+
// Also, MatchInstructionImpl should actually *do* the EmitInstruction
4218+
// call.
4219+
if (Op.getToken() != "call")
4220+
return;
4221+
// This is a call instruction...
4222+
4223+
X86Operand &Operand = static_cast<X86Operand &>(*Operands[1]);
4224+
MCSymbol *Sym = getContext().lookupSymbol(Operand.getSymName());
4225+
if (Sym == nullptr || !Sym->isInSection() || !Sym->isCOFF() ||
4226+
!dyn_cast<MCSymbolCOFF>(Sym)->isFarProc())
4227+
return;
4228+
// Sym is a reference to a far proc in a code section....
4229+
4230+
if (Out.getCurrentSectionOnly() == &Sym->getSection()) {
4231+
// This is a call to a symbol declared as a far proc, and will be emitted as
4232+
// a near call... so we need to explicitly push the code section register
4233+
// before the call.
4234+
MCInst Inst;
4235+
Inst.setOpcode(X86::PUSH32r);
4236+
Inst.addOperand(MCOperand::createReg(MCRegister(X86::CS)));
4237+
Inst.setLoc(IDLoc);
4238+
if (!MatchingInlineAsm)
4239+
emitInstruction(Inst, Operands, Out);
4240+
}
4241+
}
4242+
41944243
bool X86AsmParser::ErrorMissingFeature(SMLoc IDLoc,
41954244
const FeatureBitset &MissingFeatures,
41964245
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)