From f08fde41f34d739c157b1d75dadbb864e7957cab Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Fri, 12 Mar 2010 22:07:14 +0000 Subject: [PATCH] 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 --- include/llvm/MC/MCAssembler.h | 8 ++ lib/MC/MCAssembler.cpp | 126 ++++++++++++++++++++++++-- test/MC/MachO/relax-jumps.s | 31 +++++++ test/MC/MachO/relax-recompute-align.s | 37 ++++++++ 4 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 test/MC/MachO/relax-jumps.s create mode 100644 test/MC/MachO/relax-recompute-align.s diff --git a/include/llvm/MC/MCAssembler.h b/include/llvm/MC/MCAssembler.h index d614c697421..c371f1dd008 100644 --- a/include/llvm/MC/MCAssembler.h +++ b/include/llvm/MC/MCAssembler.h @@ -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 diff --git a/lib/MC/MCAssembler.cpp b/lib/MC/MCAssembler.cpp index ff930156a04..88be99cffdd 100644 --- a/lib/MC/MCAssembler.cpp +++ b/lib/MC/MCAssembler.cpp @@ -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,20 +1350,94 @@ 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(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 &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 diff --git a/test/MC/MachO/relax-jumps.s b/test/MC/MachO/relax-jumps.s new file mode 100644 index 00000000000..9c58aa76825 --- /dev/null +++ b/test/MC/MachO/relax-jumps.s @@ -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 diff --git a/test/MC/MachO/relax-recompute-align.s b/test/MC/MachO/relax-recompute-align.s new file mode 100644 index 00000000000..249402502f7 --- /dev/null +++ b/test/MC/MachO/relax-recompute-align.s @@ -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 -- 2.34.1