The assembly backend is responsible for manipulating fixup values,
replacing them with values where information is available. The class
for this backend inherits from the MCAsmBackend
.
Note | |
---|---|
Information about the |
The getNumFixupKinds
function returns the number
of fixups which the backend supports. This was defined as part of the
Fixups
enum, so this function simply returns
this value.
unsigned getNumFixupKinds() const { return OR1K::NumTargetFixupKinds; }
The applyFixup
function takes a fixup and
provided data value and applies it to a given instruction.
To aid in this, a support function
adjustFixupValue
is created and called which
manipulates the fixup's value before it is applied. This is done for
example where a branch instruction does not store the exact location
to branch to but that value without the first two bits. In this case,
the value would be bitshifted by two before being applied.
With the fixup value adjusted appropriately, the instruction it is to be applied to is then reconstructed as a 64-bit unsigned integer. The fixup value is then shifted and masked into the correct location in the instruction before being applied. Once done, the now modified instruction is written back to the original data field.
The following example is from the OpenRISC 1000 implementation, with the data being loaded and manipulated in a big endian fashion.
void OR1KAsmBackend::applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize, uint64_t Value) const { MCFixupKind Kind = Fixup.getKind(); Value = adjustFixupValue((unsigned)Kind, Value); if (!Value) return; // This value doesn't change the encoding // Where in the object and where the number of bytes that need // fixing up unsigned Offset = Fixup.getOffset(); unsigned NumBytes = (getFixupKindInfo(Kind).TargetSize + 7) / 8; unsigned FullSize; switch((unsigned)Kind) { default: FullSize = 4; break; } // Grab current value, if any, from bits. uint64_t CurVal = 0; // Load instruction and apply value for (unsigned i = 0; i != NumBytes; ++i) { unsigned Idx = (FullSize - 1 - i); CurVal |= (uint64_t)((uint8_t)Data[Offset + Idx]) << (i*8); } uint64_t Mask = ((uint64_t)(-1) >> (64 - getFixupKindInfo(Kind).TargetSize)); CurVal |= Value & Mask; // Write out the fixed up bytes back to the code/data bits. for (unsigned i = 0; i != NumBytes; ++i) { unsigned Idx = (FullSize - 1 - i); Data[Offset + Idx] = (uint8_t)((CurVal >> (i*8)) & 0xff); } }
Where there are spaces in an instruction stream that need filling with
executable instructions, a series of NOP
s should
be inserted. This is done via the writeNopData
function, which specifies the size of memory that needs filling. If
valid instructions can be placed into the instruction stream they are
then created and emitted via the provided
MCObjectWriter
.
In the case of the OpenRISC 1000 implementation, the only NOP
instruction
is 32-bits long. Therefore if the space to fill is not a multiple of
4 bytes then the function returns false to indicate that it can't be
filled. Otherwise for each set of four bytes, the encoding of a NOP
is emitted via the ObjectWriters
Write32
function.
bool OR1KAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const { if ((Count % 4) != 0) return false; for (uint64_t i = 0; i < Count; i += 4) OW->Write32(0x15000000); return true; }
Another function which needs implementing is
relaxInstruction
, which takes an instruction and
relaxes it to a longer instruction with the same effects.
For targets where no instruction ever needs relaxation (e.g. all instructions are the same size), this function simply returns. Otherwise the longer instruction is created, copying and formatting operands as appropriate.
Likewise the mayNeedRelaxation
function returns
true/false depending on if the instruction may need to be relaxed. In
the case of the OpenRISC 1000 , no instruction ever needs relaxing, therefore
the function always returns false.
The fixupNeedsRelaxation
returns whether an
instruction needs to be relaxed based on the given fixup. As the case
with the previous two functions, if no instruction needs to be relaxed
this function will also always return false.
Finally, the getFixupKindInfo
function needs
overriding to provide information about target specific fixups,
including their offset, size and flags. This function starts with a
table containing details on each fixup.
If the fixup type provided is not target specific, the overridden function is called to get the result. Otherwise the entry is looked up in the table specified above and the relevant entry returned. If no entry exists in either table, then an error is raised.
Note | |
---|---|
The entries in this table must be in the same order as specified
in |
const MCFixupKindInfo &OR1KAsmBackend::getFixupKindInfo(MCFixupKind Kind) const{ const static MCFixupKindInfo Infos[OR1K::NumTargetFixupKinds] = { // This table *must* be in same the order of fixup_* kinds in // OR1KFixupKinds.h. // // name offset bits flags { "fixup_OR1K_32", 0, 32, 0 }, { "fixup_OR1K_16", 0, 16, 0 }, ... other fixups not shown ... }; if (Kind < FirstTargetFixupKind) return MCAsmBackend::getFixupKindInfo(Kind); assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && "Invalid kind!"); return Infos[Kind - FirstTargetFixupKind]; }
To enable and create the assembly backend,
create
is defined and returns a new
arch
AsmBackend
object,
based on a given target and triple.
arch
AsmBackend
MCAsmBackend *llvm::createarch
AsmBackend(const Target &T, StringRef TT) { Triple TheTriple(TT); return newarch
AsmBackend(T, Triple(TT).getOS()); }
Finally this function is set up with the MC target registry, associating the assembly backend with the target.
In
,
the assembly backend is added in the same way as other components.
arch
MCTargetDesc.cpp
// Register the ASM Backend TargetRegistry::RegisterMCAsmBackend(Thearch
Target, createarch
AsmBackend);