From e18fc7bc2d947abc4ab6c07a0efbb6285ed404d2 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Sat, 1 Feb 2014 07:19:38 +0000 Subject: MC: Improve the .fill directive's compatibility with GAS Per the GAS documentation, .fill should permit pattern widths that aren't a power of two. While I was in the neighborhood, I added some sanity checking. This change was motivated by a use of this construct in the Linux Kernel. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@200606 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/MC/MCAsmStreamer.cpp | 39 +++++++++++++++++++++++++--------- lib/MC/MCParser/AsmParser.cpp | 32 ++++++++++++++++++++++++---- test/MC/AsmParser/directive_fill.s | 43 +++++++++++++++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index a8885164a8..1791f73832 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -673,6 +673,7 @@ void MCAsmStreamer::EmitIntValue(uint64_t Value, unsigned Size) { } void MCAsmStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size) { + assert(Size <= 8 && "Invalid size"); assert(getCurrentSection().first && "Cannot emit contents before setting section!"); const char *Directive = 0; @@ -681,19 +682,37 @@ void MCAsmStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size) { case 1: Directive = MAI->getData8bitsDirective(); break; case 2: Directive = MAI->getData16bitsDirective(); break; case 4: Directive = MAI->getData32bitsDirective(); break; - case 8: - Directive = MAI->getData64bitsDirective(); - // If the target doesn't support 64-bit data, emit as two 32-bit halves. - if (Directive) break; + case 8: Directive = MAI->getData64bitsDirective(); break; + } + + if (!Directive) { int64_t IntValue; if (!Value->EvaluateAsAbsolute(IntValue)) report_fatal_error("Don't know how to emit this value."); - if (MAI->isLittleEndian()) { - EmitIntValue((uint32_t)(IntValue >> 0 ), 4); - EmitIntValue((uint32_t)(IntValue >> 32), 4); - } else { - EmitIntValue((uint32_t)(IntValue >> 32), 4); - EmitIntValue((uint32_t)(IntValue >> 0 ), 4); + + // We couldn't handle the requested integer size so we fallback by breaking + // the request down into several, smaller, integers. Since sizes greater + // than eight are invalid and size equivalent to eight should have been + // handled earlier, we use four bytes as our largest piece of granularity. + bool IsLittleEndian = MAI->isLittleEndian(); + for (unsigned Emitted = 0; Emitted != Size;) { + unsigned Remaining = Size - Emitted; + // The size of our partial emission must be a power of two less than + // eight. + unsigned EmissionSize = PowerOf2Floor(Remaining); + if (EmissionSize > 4) + EmissionSize = 4; + // Calculate the byte offset of our partial emission taking into account + // the endianness of the target. + unsigned ByteOffset = + IsLittleEndian ? Emitted : (Remaining - EmissionSize); + uint64_t ValueToEmit = IntValue >> (ByteOffset * 8); + // We truncate our partial emission to fit within the bounds of the + // emission domain. This produces nicer output and silences potential + // truncation warnings when round tripping through another assembler. + ValueToEmit &= ~0ULL >> (64 - EmissionSize * 8); + EmitIntValue(ValueToEmit, EmissionSize); + Emitted += EmissionSize; } return; } diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index 3f813a73cc..9157ac4f67 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -2397,18 +2397,27 @@ bool AsmParser::parseDirectiveZero() { bool AsmParser::parseDirectiveFill() { checkForValidSection(); + SMLoc RepeatLoc = getLexer().getLoc(); int64_t NumValues; if (parseAbsoluteExpression(NumValues)) return true; + if (NumValues < 0) { + Warning(RepeatLoc, + "'.fill' directive with negative repeat count has no effect"); + NumValues = 0; + } + int64_t FillSize = 1; int64_t FillExpr = 0; + SMLoc SizeLoc, ExprLoc; if (getLexer().isNot(AsmToken::EndOfStatement)) { if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in '.fill' directive"); Lex(); + SizeLoc = getLexer().getLoc(); if (parseAbsoluteExpression(FillSize)) return true; @@ -2417,6 +2426,7 @@ bool AsmParser::parseDirectiveFill() { return TokError("unexpected token in '.fill' directive"); Lex(); + ExprLoc = getLexer().getLoc(); if (parseAbsoluteExpression(FillExpr)) return true; @@ -2427,11 +2437,25 @@ bool AsmParser::parseDirectiveFill() { } } - if (FillSize != 1 && FillSize != 2 && FillSize != 4 && FillSize != 8) - return TokError("invalid '.fill' size, expected 1, 2, 4, or 8"); + if (FillSize < 0) { + Warning(SizeLoc, "'.fill' directive with negative size has no effect"); + NumValues = 0; + } + if (FillSize > 8) { + Warning(SizeLoc, "'.fill' directive with size greater than 8 has been truncated to 8"); + FillSize = 8; + } - for (uint64_t i = 0, e = NumValues; i != e; ++i) - getStreamer().EmitIntValue(FillExpr, FillSize); + if (!isUInt<32>(FillExpr) && FillSize > 4) + Warning(ExprLoc, "'.fill' directive pattern has been truncated to 32-bits"); + + int64_t NonZeroFillSize = FillSize > 4 ? 4 : FillSize; + FillExpr &= ~0ULL >> (64 - NonZeroFillSize * 8); + + for (uint64_t i = 0, e = NumValues; i != e; ++i) { + getStreamer().EmitIntValue(FillExpr, NonZeroFillSize); + getStreamer().EmitIntValue(0, FillSize - NonZeroFillSize); + } return false; } diff --git a/test/MC/AsmParser/directive_fill.s b/test/MC/AsmParser/directive_fill.s index bb3ced091c..11da32929f 100644 --- a/test/MC/AsmParser/directive_fill.s +++ b/test/MC/AsmParser/directive_fill.s @@ -1,4 +1,5 @@ -# RUN: llvm-mc -triple i386-unknown-unknown %s | FileCheck %s +# RUN: llvm-mc -triple i386-unknown-unknown %s 2> %t.err | FileCheck %s +# RUN: FileCheck --check-prefix=CHECK-WARNINGS %s < %t.err # CHECK: TEST0: # CHECK: .byte 10 @@ -31,3 +32,43 @@ TEST3: # CHECK: .short 0 TEST4: .fill 4, 2 + +# CHECK: TEST5 +# CHECK: .short 2 +# CHECK: .byte 0 +# CHECK: .short 2 +# CHECK: .byte 0 +# CHECK: .short 2 +# CHECK: .byte 0 +# CHECK: .short 2 +# CHECK: .byte 0 +TEST5: + .fill 4, 3, 2 + +# CHECK: TEST6 +# CHECK: .long 2 +# CHECK: .long 0 +# CHECK-WARNINGS: '.fill' directive with size greater than 8 has been truncated to 8 +TEST6: + .fill 1, 9, 2 + +# CHECK: TEST7 +# CHECK: .long 0 +# CHECK: .long 0 +# CHECK-WARNINGS: '.fill' directive pattern has been truncated to 32-bits +TEST7: + .fill 1, 8, 1<<32 + +# CHECK-WARNINGS: '.fill' directive with negative repeat count has no effect +TEST8: + .fill -1, 8, 1 + +# CHECK-WARNINGS: '.fill' directive with negative size has no effect +TEST9: + .fill 1, -1, 1 + +# CHECK: TEST10 +# CHECK: .short 22136 +# CHECK: .byte 52 +TEST10: + .fill 1, 3, 0x12345678 -- cgit v1.2.3