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
This commit is contained in:
David Majnemer 2014-02-01 07:19:38 +00:00
parent 69a0d80f55
commit e18fc7bc2d
3 changed files with 99 additions and 15 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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