MC/Mach-O: Implement initial support for relaxation.

- The implementation is currently very brain dead and inefficient, but I have a
   clear plan on how to fix it.

 - The good news is, it works and correctly assembles 403.gcc (when built with
   Clang, at '-Os', '-Os -g', and '-O3'). Even better, at '-Os' and '-Os -g',
   the resulting binary is exactly equivalent to that when built with the system
   assembler. So it probably works! :)

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@98396 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Daniel Dunbar 2010-03-12 22:07:14 +00:00
parent 5027064c69
commit f08fde41f3
4 changed files with 193 additions and 9 deletions

View File

@ -621,11 +621,19 @@ private:
unsigned SubsectionsViaSymbols : 1;
private:
/// Check whether a fixup can be satisfied, or whether it needs to be relaxed
/// (increased in size, in order to hold its value correctly).
bool FixupNeedsRelaxation(MCAsmFixup &Fixup, MCDataFragment *DF);
/// LayoutSection - Assign offsets and sizes to the fragments in the section
/// \arg SD, and update the section size. The section file offset should
/// already have been computed.
void LayoutSection(MCSectionData &SD);
/// LayoutOnce - Perform one layout iteration and return true if any offsets
/// were adjusted.
bool LayoutOnce();
// FIXME: Make protected once we factor out object writer classes.
public:
/// Evaluate a fixup to a relocatable expression and the value which should be

View File

@ -1275,6 +1275,40 @@ void MCAssembler::Finish() {
llvm::errs() << "assembler backend - pre-layout\n--\n";
dump(); });
// Layout until everything fits.
while (LayoutOnce())
continue;
DEBUG_WITH_TYPE("mc-dump", {
llvm::errs() << "assembler backend - post-layout\n--\n";
dump(); });
// Write the object file.
MachObjectWriter MOW(OS);
MOW.WriteObject(*this);
OS.flush();
}
bool MCAssembler::FixupNeedsRelaxation(MCAsmFixup &Fixup, MCDataFragment *DF) {
// FIXME: Share layout object.
MCAsmLayout Layout(*this);
// Currently we only need to relax X86::reloc_pcrel_1byte.
if (unsigned(Fixup.Kind) != X86::reloc_pcrel_1byte)
return false;
// If we cannot resolve the fixup value, it requires relaxation.
MCValue Target;
uint64_t Value;
if (!EvaluateFixup(Layout, Fixup, DF, Target, Value))
return true;
// Otherwise, relax if the value is too big for a (signed) i8.
return int64_t(Value) != int64_t(int8_t(Value));
}
bool MCAssembler::LayoutOnce() {
// Layout the concrete sections and fragments.
uint64_t Address = 0;
MCSectionData *Prev = 0;
@ -1316,21 +1350,95 @@ void MCAssembler::Finish() {
SD.setAddress(Address);
LayoutSection(SD);
Address += SD.getSize();
}
DEBUG_WITH_TYPE("mc-dump", {
llvm::errs() << "assembler backend - post-layout\n--\n";
dump(); });
// Scan the fixups in order and relax any that don't fit.
for (iterator it = begin(), ie = end(); it != ie; ++it) {
MCSectionData &SD = *it;
// Write the object file.
MachObjectWriter MOW(OS);
MOW.WriteObject(*this);
for (MCSectionData::iterator it2 = SD.begin(),
ie2 = SD.end(); it2 != ie2; ++it2) {
MCDataFragment *DF = dyn_cast<MCDataFragment>(it2);
if (!DF)
continue;
OS.flush();
for (MCDataFragment::fixup_iterator it3 = DF->fixup_begin(),
ie3 = DF->fixup_end(); it3 != ie3; ++it3) {
MCAsmFixup &Fixup = *it3;
// Check whether we need to relax this fixup.
if (!FixupNeedsRelaxation(Fixup, DF))
continue;
// Relax the instruction.
//
// FIXME: This is a huge temporary hack which just looks for x86
// branches; the only thing we need to relax on x86 is
// 'X86::reloc_pcrel_1byte'. Once we have MCInst fragments, this will be
// replaced by a TargetAsmBackend hook (most likely tblgen'd) to relax
// an individual MCInst.
SmallVectorImpl<char> &C = DF->getContents();
uint64_t PrevOffset = Fixup.Offset;
unsigned Amt = 0;
// jcc instructions
if (unsigned(C[Fixup.Offset-1]) >= 0x70 &&
unsigned(C[Fixup.Offset-1]) <= 0x7f) {
C[Fixup.Offset] = C[Fixup.Offset-1] + 0x10;
C[Fixup.Offset-1] = char(0x0f);
++Fixup.Offset;
Amt = 4;
// jmp rel8
} else if (C[Fixup.Offset-1] == char(0xeb)) {
C[Fixup.Offset-1] = char(0xe9);
Amt = 3;
} else
llvm_unreachable("unknown 1 byte pcrel instruction!");
Fixup.Value = MCBinaryExpr::Create(
MCBinaryExpr::Sub, Fixup.Value,
MCConstantExpr::Create(3, getContext()),
getContext());
C.insert(C.begin() + Fixup.Offset, Amt, char(0));
Fixup.Kind = MCFixupKind(X86::reloc_pcrel_4byte);
// Update the remaining fixups, which have slid.
//
// FIXME: This is bad for performance, but will be eliminated by the
// move to MCInst specific fragments.
++it3;
for (; it3 != ie3; ++it3)
it3->Offset += Amt;
// Update all the symbols for this fragment, which may have slid.
//
// FIXME: This is really really bad for performance, but will be
// eliminated by the move to MCInst specific fragments.
for (MCAssembler::symbol_iterator it = symbol_begin(),
ie = symbol_end(); it != ie; ++it) {
MCSymbolData &SD = *it;
if (it->getFragment() != DF)
continue;
if (SD.getOffset() > PrevOffset)
SD.setOffset(SD.getOffset() + Amt);
}
// Restart layout.
//
// FIXME: This is O(N^2), but will be eliminated once we have a smart
// MCAsmLayout object.
return true;
}
}
}
return false;
}
// Debugging methods
namespace llvm {

View File

@ -0,0 +1,31 @@
// RUN: llvm-mc -triple i386-apple-darwin9 %s -filetype=obj -o - | macho-dump --dump-section-data | FileCheck %s
// FIXME: This is a horrible way of checking the output, we need an llvm-mc
// based 'otool'. Use:
// (f=relax-jumps;
// llvm-mc -filetype=obj -o $f.mc.o $f.s &&
// as -arch i386 -o $f.as.o $f.s &&
// otool -tvr $f.mc.o | tail +2 > $f.mc.dump &&
// otool -tvr $f.as.o | tail +2 > $f.as.dump &&
// diff $f.{as,mc}.dump)
// to examine the results in a more sensible fashion.
// CHECK: ('_section_data', '\x90
// CHECK: \x0f\x842\xff\xff\xff\x0f\x82\xe6\x00\x00\x00\x0f\x87&\xff\xff\xff\x0f\x8f\xda\x00\x00\x00\x0f\x88\x1a\xff\xff\xff\x0f\x83\xce\x00\x00\x00\x0f\x89\x0e\xff\xff\xff\x90
// CHECK: \x901\xc0')
L1:
.space 200, 0x90
je L1
jb L2
ja L1
jg L2
js L1
jae L2
jns L1
.space 200, 0x90
L2:
xorl %eax, %eax

View File

@ -0,0 +1,37 @@
// RUN: llvm-mc -triple i386-apple-darwin9 %s -filetype=obj -o - | macho-dump --dump-section-data | FileCheck %s
// FIXME: This is a horrible way of checking the output, we need an llvm-mc
// based 'otool'.
// This is a case where llvm-mc computes a better layout than Darwin 'as'. This
// issue is that after the first jmp slides, the .align size must be
// recomputed -- otherwise the second jump will appear to be out-of-range for a
// 1-byte jump.
// CHECK: # Section 0
// CHECK: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
// CHECK: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
// CHECK: ('address', 0)
// CHECK: ('size', 306)
// CHECK: ('offset', 324)
// CHECK: ('alignment', 4)
// CHECK: ('reloc_offset', 0)
// CHECK: ('num_reloc', 0)
// CHECK: ('flags', 0x80000400)
// CHECK: ('reserved1', 0)
// CHECK: ('reserved2', 0)
// CHECK: ),
L0:
.space 0x8a, 0x90
jmp L0
.space (0xb3 - 0x8f), 0x90
jle L2
.space (0xcd - 0xb5), 0x90
.align 4, 0x90
L1:
.space (0x130 - 0xd0),0x90
jl L1
L2:
.zerofill __DATA,__bss,_sym,4,2