[MC] Change AsmParser to leverage Assembler during evaluation

Teach AsmParser to check with Assembler for when evaluating constant
expressions.  This improves the handing of preprocessor expressions
that must be resolved at parse time. This idiom can be found as
assembling-time assertion checks in source-level assemblers. Note that
this relies on the MCStreamer to keep sufficient tabs on Section /
Fragment information which the MCAsmStreamer does not. As a result the
textual output may fail where the equivalent object generation would
pass. This can most easily be resolved by folding the MCAsmStreamer
and MCObjectStreamer together which is planned for in a separate
patch.

Currently, this feature is only enabled for assembly input, keeping IR
compilation consistent between assembly and object generation.

Reviewers: echristo, rnk, probinson, espindola, peter.smith

Reviewed By: peter.smith

Subscribers: eraman, peter.smith, arichardson, jyknight, hiraditya, llvm-commits

Differential Revision: https://reviews.llvm.org/D45164

llvm-svn: 331218
This commit is contained in:
Nirav Dave 2018-04-30 19:22:40 +00:00
parent 8fe04ad3f7
commit 6c0665e221
17 changed files with 153 additions and 23 deletions

View File

@ -0,0 +1,12 @@
// REQUIRES: x86-registered-target
// RUN: %clang --target=x86_64-unknown-linux-gnu -c %s -o /dev/null
// Check that cc1as can use assembler info in object generation.
.data
foo:
.if . - foo == 0
.byte 0xaa
.else
.byte 0x00
.endif

View File

@ -435,6 +435,9 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
Str.get()->InitSections(Opts.NoExecStack);
}
// Assembly to object compilation should leverage assembly info.
Str->setUseAssemblerInfoForParsing(true);
bool Failed = false;
std::unique_ptr<MCAsmParser> Parser(

View File

@ -96,6 +96,7 @@ public:
const SectionAddrMap &Addrs) const;
bool evaluateAsAbsolute(int64_t &Res) const;
bool evaluateAsAbsolute(int64_t &Res, const MCAssembler &Asm) const;
bool evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm) const;
bool evaluateAsAbsolute(int64_t &Res, const MCAsmLayout &Layout) const;
bool evaluateKnownAbsolute(int64_t &Res, const MCAsmLayout &Layout) const;

View File

@ -89,7 +89,7 @@ public:
void visitUsedSymbol(const MCSymbol &Sym) override;
MCAssembler &getAssembler() { return *Assembler; }
MCAssembler *getAssemblerPtr() override;
/// \name MCStreamer Interface
/// @{

View File

@ -211,6 +211,8 @@ class MCStreamer {
/// requires.
unsigned NextWinCFIID = 0;
bool UseAssemblerInfoForParsing;
protected:
MCStreamer(MCContext &Ctx);
@ -247,6 +249,11 @@ public:
MCContext &getContext() const { return Context; }
virtual MCAssembler *getAssemblerPtr() { return nullptr; }
void setUseAssemblerInfoForParsing(bool v) { UseAssemblerInfoForParsing = v; }
bool getUseAssemblerInfoForParsing() { return UseAssemblerInfoForParsing; }
MCTargetStreamer *getTargetStreamer() {
return TargetStreamer.get();
}

View File

@ -316,6 +316,8 @@ public:
Index = Value;
}
bool isUnset() const { return SymbolContents == SymContentsUnset; }
uint64_t getOffset() const {
assert((SymbolContents == SymContentsUnset ||
SymbolContents == SymContentsOffset) &&

View File

@ -132,6 +132,9 @@ void AsmPrinter::EmitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
std::unique_ptr<MCAsmParser> Parser(
createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum));
// Do not use assembler-level information for parsing inline assembly.
OutStreamer->setUseAssemblerInfoForParsing(false);
// We create a new MCInstrInfo here since we might be at the module level
// and not have a MachineFunction to initialize the TargetInstrInfo from and
// we only need MCInstrInfo for asm parsing. We create one unconditionally

View File

@ -78,6 +78,9 @@ public:
InstPrinter->setCommentStream(CommentStream);
}
MCAssembler &getAssembler() { return *Assembler; }
MCAssembler *getAssemblerPtr() override { return nullptr; }
inline void EmitEOL() {
// Dump Explicit Comments here.
emitExplicitComments();
@ -1656,10 +1659,10 @@ void MCAsmStreamer::AddEncodingComment(const MCInst &Inst,
raw_svector_ostream VecOS(Code);
// If we have no code emitter, don't emit code.
if (!Assembler->getEmitterPtr())
if (!getAssembler().getEmitterPtr())
return;
Assembler->getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI);
getAssembler().getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI);
// If we are showing fixups, create symbolic markers in the encoded
// representation. We do this by making a per-bit map to the fixup item index,
@ -1672,7 +1675,7 @@ void MCAsmStreamer::AddEncodingComment(const MCInst &Inst,
for (unsigned i = 0, e = Fixups.size(); i != e; ++i) {
MCFixup &F = Fixups[i];
const MCFixupKindInfo &Info =
Assembler->getBackend().getFixupKindInfo(F.getKind());
getAssembler().getBackend().getFixupKindInfo(F.getKind());
for (unsigned j = 0; j != Info.TargetSize; ++j) {
unsigned Index = F.getOffset() * 8 + Info.TargetOffset + j;
assert(Index < Code.size() * 8 && "Invalid offset in fixup!");
@ -1737,7 +1740,7 @@ void MCAsmStreamer::AddEncodingComment(const MCInst &Inst,
for (unsigned i = 0, e = Fixups.size(); i != e; ++i) {
MCFixup &F = Fixups[i];
const MCFixupKindInfo &Info =
Assembler->getBackend().getFixupKindInfo(F.getKind());
getAssembler().getBackend().getFixupKindInfo(F.getKind());
OS << " fixup " << char('A' + i) << " - " << "offset: " << F.getOffset()
<< ", value: " << *F.getValue() << ", kind: " << Info.Name << "\n";
}

View File

@ -441,6 +441,10 @@ bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler &Asm) const {
return evaluateAsAbsolute(Res, &Asm, nullptr, nullptr);
}
bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm) const {
return evaluateAsAbsolute(Res, Asm, nullptr, nullptr);
}
bool MCExpr::evaluateKnownAbsolute(int64_t &Res,
const MCAsmLayout &Layout) const {
return evaluateAsAbsolute(Res, &Layout.getAssembler(), &Layout, nullptr,
@ -494,7 +498,7 @@ static void AttemptToFoldSymbolOffsetDifference(
return;
if (SA.getFragment() == SB.getFragment() && !SA.isVariable() &&
!SB.isVariable()) {
!SA.isUnset() && !SB.isVariable() && !SB.isUnset()) {
Addend += (SA.getOffset() - SB.getOffset());
// Pointers to Thumb symbols need to have their low-bit set to allow

View File

@ -35,6 +35,15 @@ MCObjectStreamer::MCObjectStreamer(MCContext &Context,
MCObjectStreamer::~MCObjectStreamer() {}
// AssemblerPtr is used for evaluation of expressions and causes
// difference between asm and object outputs. Return nullptr to in
// inline asm mode to limit divergence to assembly inputs.
MCAssembler *MCObjectStreamer::getAssemblerPtr() {
if (getUseAssemblerInfoForParsing())
return Assembler.get();
return nullptr;
}
void MCObjectStreamer::flushPendingLabels(MCFragment *F, uint64_t FOffset) {
if (PendingLabels.empty())
return;
@ -155,7 +164,7 @@ void MCObjectStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size,
// Avoid fixups when possible.
int64_t AbsValue;
if (Value->evaluateAsAbsolute(AbsValue, getAssembler())) {
if (Value->evaluateAsAbsolute(AbsValue, getAssemblerPtr())) {
if (!isUIntN(8 * Size, AbsValue) && !isIntN(8 * Size, AbsValue)) {
getContext().reportError(
Loc, "value evaluated as " + Twine(AbsValue) + " is out of range.");
@ -217,7 +226,7 @@ void MCObjectStreamer::EmitLabel(MCSymbol *Symbol, SMLoc Loc, MCFragment *F) {
void MCObjectStreamer::EmitULEB128Value(const MCExpr *Value) {
int64_t IntValue;
if (Value->evaluateAsAbsolute(IntValue, getAssembler())) {
if (Value->evaluateAsAbsolute(IntValue, getAssemblerPtr())) {
EmitULEB128IntValue(IntValue);
return;
}
@ -226,7 +235,7 @@ void MCObjectStreamer::EmitULEB128Value(const MCExpr *Value) {
void MCObjectStreamer::EmitSLEB128Value(const MCExpr *Value) {
int64_t IntValue;
if (Value->evaluateAsAbsolute(IntValue, getAssembler())) {
if (Value->evaluateAsAbsolute(IntValue, getAssemblerPtr())) {
EmitSLEB128IntValue(IntValue);
return;
}
@ -254,7 +263,7 @@ bool MCObjectStreamer::changeSectionImpl(MCSection *Section,
int64_t IntSubsection = 0;
if (Subsection &&
!Subsection->evaluateAsAbsolute(IntSubsection, getAssembler()))
!Subsection->evaluateAsAbsolute(IntSubsection, getAssemblerPtr()))
report_fatal_error("Cannot evaluate subsection number");
if (IntSubsection < 0 || IntSubsection > 8192)
report_fatal_error("Subsection number out of range");
@ -400,7 +409,7 @@ void MCObjectStreamer::EmitDwarfAdvanceLineAddr(int64_t LineDelta,
}
const MCExpr *AddrDelta = buildSymbolDiff(*this, Label, LastLabel);
int64_t Res;
if (AddrDelta->evaluateAsAbsolute(Res, getAssembler())) {
if (AddrDelta->evaluateAsAbsolute(Res, getAssemblerPtr())) {
MCDwarfLineAddr::Emit(this, Assembler->getDWARFLinetableParams(), LineDelta,
Res);
return;
@ -412,7 +421,7 @@ void MCObjectStreamer::EmitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel,
const MCSymbol *Label) {
const MCExpr *AddrDelta = buildSymbolDiff(*this, Label, LastLabel);
int64_t Res;
if (AddrDelta->evaluateAsAbsolute(Res, getAssembler())) {
if (AddrDelta->evaluateAsAbsolute(Res, getAssemblerPtr())) {
MCDwarfFrameEmitter::EmitAdvanceLoc(*this, Res);
return;
}
@ -608,7 +617,7 @@ void MCObjectStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue,
void MCObjectStreamer::emitFill(const MCExpr &NumValues, int64_t Size,
int64_t Expr, SMLoc Loc) {
int64_t IntNumValues;
if (!NumValues.evaluateAsAbsolute(IntNumValues, getAssembler())) {
if (!NumValues.evaluateAsAbsolute(IntNumValues, getAssemblerPtr())) {
getContext().reportError(Loc, "expected absolute expression");
return;
}

View File

@ -775,7 +775,7 @@ bool AsmParser::processIncbinFile(const std::string &Filename, int64_t Skip,
Bytes = Bytes.drop_front(Skip);
if (Count) {
int64_t Res;
if (!Count->evaluateAsAbsolute(Res))
if (!Count->evaluateAsAbsolute(Res, getStreamer().getAssemblerPtr()))
return Error(Loc, "expected absolute expression");
if (Res < 0)
return Warning(Loc, "negative count has no effect");
@ -1378,7 +1378,8 @@ bool AsmParser::parseExpression(const MCExpr *&Res, SMLoc &EndLoc) {
Lex();
}
// Try to constant fold it up front, if possible.
// Try to constant fold it up front, if possible. Do not exploit
// assembler here.
int64_t Value;
if (Res->evaluateAsAbsolute(Value))
Res = MCConstantExpr::create(Value, getContext());
@ -1419,7 +1420,7 @@ bool AsmParser::parseAbsoluteExpression(int64_t &Res) {
if (parseExpression(Expr))
return true;
if (!Expr->evaluateAsAbsolute(Res))
if (!Expr->evaluateAsAbsolute(Res, getStreamer().getAssemblerPtr()))
return Error(StartLoc, "expected absolute expression");
return false;
@ -2616,7 +2617,8 @@ bool AsmParser::parseMacroArguments(const MCAsmMacro *M,
Lex();
if (parseExpression(AbsoluteExp, EndLoc))
return false;
if (!AbsoluteExp->evaluateAsAbsolute(Value))
if (!AbsoluteExp->evaluateAsAbsolute(Value,
getStreamer().getAssemblerPtr()))
return Error(StrLoc, "expected absolute expression");
const char *StrChar = StrLoc.getPointer();
const char *EndChar = EndLoc.getPointer();
@ -2916,8 +2918,9 @@ bool AsmParser::parseDirectiveReloc(SMLoc DirectiveLoc) {
if (parseExpression(Offset))
return true;
if (check(!Offset->evaluateAsAbsolute(OffsetValue), OffsetLoc,
"expression is not a constant value") ||
if (check(!Offset->evaluateAsAbsolute(OffsetValue,
getStreamer().getAssemblerPtr()),
OffsetLoc, "expression is not a constant value") ||
check(OffsetValue < 0, OffsetLoc, "expression is negative") ||
parseToken(AsmToken::Comma, "expected comma") ||
check(getTok().isNot(AsmToken::Identifier), "expected relocation name"))
@ -5344,7 +5347,7 @@ bool AsmParser::parseDirectiveRept(SMLoc DirectiveLoc, StringRef Dir) {
return true;
int64_t Count;
if (!CountExpr->evaluateAsAbsolute(Count)) {
if (!CountExpr->evaluateAsAbsolute(Count, getStreamer().getAssemblerPtr())) {
return Error(CountLoc, "unexpected token in '" + Dir + "' directive");
}

View File

@ -75,7 +75,8 @@ void MCTargetStreamer::emitValue(const MCExpr *Value) {
void MCTargetStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) {}
MCStreamer::MCStreamer(MCContext &Ctx)
: Context(Ctx), CurrentWinFrameInfo(nullptr) {
: Context(Ctx), CurrentWinFrameInfo(nullptr),
UseAssemblerInfoForParsing(false) {
SectionStack.push_back(std::pair<MCSectionSubPair, MCSectionSubPair>());
}

View File

@ -0,0 +1,18 @@
# RUN: not llvm-mc -triple x86_64-unknown-unknown %s 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s 2>&1 | FileCheck %s --check-prefix=ERR
.text
test2:
jmp baz
# ERR: [[@LINE+1]]:5: error: expected absolute expression
.if . - text2 == 1
nop
.else
ret
.endif
push fs
# No additional errors.
#
# ERR-NOT: {{[0-9]+}}:{{[0-9]+}}: error:

View File

@ -0,0 +1,14 @@
; RUN: not llc -mtriple x86_64-unknown-linux-gnu -o %t.s -filetype=asm %s 2>&1 | FileCheck %s
; RUN: not llc -mtriple x86_64-unknown-linux-gnu -o %t.o -filetype=obj %s 2>&1 | FileCheck %s
; Assembler-aware expression evaluation should be disabled in inline
; assembly to prevent differences in behavior between object and
; assembly output.
; CHECK: <inline asm>:1:17: error: expected absolute expression
define i32 @main() local_unnamed_addr {
tail call void asm sideeffect "foo: nop; .if . - foo==1; nop;.endif", "~{dirflag},~{fpsr},~{flags}"()
ret i32 0
}

View File

@ -0,0 +1,47 @@
# RUN: not llvm-mc -triple x86_64-unknown-unknown %s 2>&1 | FileCheck %s --check-prefix=ASM-ERR
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s | llvm-objdump -j .data -s - | FileCheck %s --check-prefix=OBJDATA
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s | llvm-objdump -j .text -s - | FileCheck %s --check-prefix=OBJTEXT
.data
# OBJDATA: Contents of section .data
# OBJDATA-NEXT: 0000 aa0506ff
foo2:
# ASM-ERR: [[@LINE+1]]:5: error: expected absolute expression
.if . - foo2 == 0
.byte 0xaa
.else
.byte 0x00
.endif
foo3:
.byte 5
# ASM-ERR: [[@LINE+1]]:5: error: expected absolute expression
.if . - foo3 == 1
.byte 6
.else
.byte 7
.endif
.byte 0xff
# nop is a fixed size instruction so this should pass.
# OBJTEXT: Contents of section .text
# OBJTEXT-NEXT: 0000 9090ff34 25
.text
text1:
nop
# ASM-ERR: [[@LINE+1]]:5: error: expected absolute expression
.if . - text1 == 1
nop
.else
ret
.endif
push gs
# No additional errors.
#
# ASM-ERR-NOT: {{[0-9]+}}:{{[0-9]+}}: error:

View File

@ -1,7 +1,7 @@
# RUN: llvm-mc -triple i386-unknown-unknown %s 2> %t.err | FileCheck %s
# RUN: FileCheck --check-prefix=CHECK-WARNINGS %s < %t.err
# RUN: llvm-mc -triple i386-unknown-unknown -filetype=obj -o %t.o %s 2> %t.err
# RUN: FileCheck --check-prefix=OBJ-WARNINGS %s < %t.err
# RUN: llvm-mc -triple i386-unknown-unknown -filetype=obj -o %t.o %s 2> %t.err2
# RUN: FileCheck --check-prefix=OBJ-WARNINGS %s < %t.err2
# CHECK: TEST0:
# CHECK: .fill 1, 1, 0xa

View File

@ -475,6 +475,9 @@ int main(int argc, char **argv) {
Str->InitSections(true);
}
// Use Assembler information for parsing.
Str->setUseAssemblerInfoForParsing(true);
int Res = 1;
bool disassemble = false;
switch (Action) {