Two implementations of MCStreamer
:
MCAsmStreamer
is a straightforward impl that prints out a directive for each method. (e.g EmitValue -> .byte)MCObjectStreamer
implements a full assembler.
// Inheritance relations
MCStreamer (contain an instance of `MCTargetStreamer` as member `TargetStreamer`)
-> MCAsmStreamer (final)
-> MCObjectStreamer
-> MCELFStreamer
-> MipsELFStreamer
-> MCWasmStreamer
// MCTargetStreamer for directives.
// llvm/include/llvm/MCStreamer.h
MCTargetStreamer (contain an instance `MCStreamer` as member `Streamer`)
-> ARMTargetStreamer // include/llvm/MCStreamer.h
-> MipsTargetStreamer // lib/Target/Mips/MipsTargetStreamer.h
-> MipsTargetAsmStreamer // lib/Target/Mips/MipsTargetStreamer.h
-> MipsTargetELFStreamer // lib/Target/Mips/MipsTargetStreamer.h
-> RISCVTargetStreamer // lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
MipsAsmPrinter::EmitInstruction()
acts as a driver to emit an instruction. It will finally calls the MCObjectStreamer
to write object (e.g. .o
) files or MCAsmStreamer
to write assembly (e.g .s
) files.
// llvm/lib/Target/Mips/MipsAsmPrinter.cpp
void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) {
MipsTargetStreamer &TS = getTargetStreamer();
unsigned Opc = MI->getOpcode();
...
MachineBasicBlock::const_instr_iterator I = MI->getIterator();
MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
do {
...
MCInst TmpInst0;
MCInstLowering.Lower(&*I, TmpInst0);
DebugLL("Instruction lowered: From MachineInstr: "; I->dump());
DebugLLS("\t... TO MCInst"; TmpInst0.dump());
EmitToStreamer(*OutStreamer, TmpInst0);
DebugLL("Instruction emitted to streamer: "; TmpInst0.dump());
} while ((++I != E) && I->isInsideBundle()); // Delay slot check
}
MCObjectStreamer::EmitInstruction()
will:
MCStreamer::EmitInstruction(Inst, STI);
(to do what??? by visitXXXX
)MCELFStreamer::EmitInstToData()
to emit the instruction as a piece of “data” in the .text section.MCELFStreamer::EmitInstToData()
will call Assembler.getEmitter().encodeInstruction()
to encode the instruction and store it in as SmallString<256> Code
.Assembler.getEmitter().encodeInstruction()
is implemented by target arch in MipsMCCodeEmitter::encodeInstruction()
. This is where the instruction in binary form got emitted to a streamer: EmitInstruction(Binary, Size, STI, OS);
Binary
is got via Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI)
getBinaryCodeForInstr()
is a TableGen’erated function for getting the binary encoding for an instruction.void MCObjectStreamer::EmitInstruction(const MCInst &Inst,
const MCSubtargetInfo &STI) {
getAssembler().getBackend().handleCodePaddingInstructionBegin(Inst);
EmitInstructionImpl(Inst, STI);
getAssembler().getBackend().handleCodePaddingInstructionEnd(Inst);
}
void MCObjectStreamer::EmitInstructionImpl(const MCInst &Inst,
const MCSubtargetInfo &STI) {
MCStreamer::EmitInstruction(Inst, STI);
MCSection *Sec = getCurrentSectionOnly();
Sec->setHasInstructions(true);
// Now that a machine instruction has been assembled into this section, make
// a line entry for any .loc directive that has been seen.
MCDwarfLineEntry::Make(this, getCurrentSectionOnly());
// If this instruction doesn't need relaxation, just emit it as data.
MCAssembler &Assembler = getAssembler();
if (!Assembler.getBackend().mayNeedRelaxation(Inst, STI)) {
EmitInstToData(Inst, STI);
return;
}
// Otherwise, relax and emit it as data if either:
// Mips does not have relax:
// MipsAsmBackend::mayNeedRelaxation() always return false
}
MCObjectStreamer::EmitInstruction()
calls a child overloaded method for ELF (or ASM) MCELFStreamer::EmitInstToData()
:
void MCELFStreamer::EmitInstToData(const MCInst &Inst,
const MCSubtargetInfo &STI) {
MCAssembler &Assembler = getAssembler();
SmallVector<MCFixup, 4> Fixups;
SmallString<256> Code;
raw_svector_ostream VecOS(Code);
Assembler.getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI);
for (unsigned i = 0, e = Fixups.size(); i != e; ++i)
fixSymbolsInTLSFixups(Fixups[i].getValue());
// There are several possibilities here:
//
// If bundling is disabled, append the encoded instruction to the current data
// fragment (or create a new such fragment if the current fragment is not a
// data fragment, or the Subtarget has changed).
//
// If bundling is enabled:
// - If we're not in a bundle-locked group, emit the instruction into a
// fragment of its own. If there are no fixups registered for the
// instruction, emit a MCCompactEncodedInstFragment. Otherwise, emit a
// MCDataFragment.
// - If we're in a bundle-locked group, append the instruction to the current
// data fragment because we want all the instructions in a group to get into
// the same fragment. Be careful not to do that for the first instruction in
// the group, though.
MCDataFragment *DF;
if (Assembler.isBundlingEnabled()) {
MCSection &Sec = *getCurrentSectionOnly();
if (Assembler.getRelaxAll() && isBundleLocked()) {
// If the -mc-relax-all flag is used and we are bundle-locked, we re-use
// the current bundle group.
DF = BundleGroups.back();
CheckBundleSubtargets(DF->getSubtargetInfo(), &STI);
}
else if (Assembler.getRelaxAll() && !isBundleLocked())
// When not in a bundle-locked group and the -mc-relax-all flag is used,
// we create a new temporary fragment which will be later merged into
// the current fragment.
DF = new MCDataFragment();
else if (isBundleLocked() && !Sec.isBundleGroupBeforeFirstInst()) {
// If we are bundle-locked, we re-use the current fragment.
// The bundle-locking directive ensures this is a new data fragment.
DF = cast<MCDataFragment>(getCurrentFragment());
CheckBundleSubtargets(DF->getSubtargetInfo(), &STI);
}
else if (!isBundleLocked() && Fixups.size() == 0) {
// Optimize memory usage by emitting the instruction to a
// MCCompactEncodedInstFragment when not in a bundle-locked group and
// there are no fixups registered.
MCCompactEncodedInstFragment *CEIF = new MCCompactEncodedInstFragment();
insert(CEIF);
CEIF->getContents().append(Code.begin(), Code.end());
CEIF->setHasInstructions(STI);
return;
} else {
DF = new MCDataFragment();
insert(DF);
}
if (Sec.getBundleLockState() == MCSection::BundleLockedAlignToEnd) {
// If this fragment is for a group marked "align_to_end", set a flag
// in the fragment. This can happen after the fragment has already been
// created if there are nested bundle_align groups and an inner one
// is the one marked align_to_end.
DF->setAlignToBundleEnd(true);
}
// We're now emitting an instruction in a bundle group, so this flag has
// to be turned off.
Sec.setBundleGroupBeforeFirstInst(false);
} else {
DF = getOrCreateDataFragment(&STI);
}
// Add the fixups and data.
for (unsigned i = 0, e = Fixups.size(); i != e; ++i) {
Fixups[i].setOffset(Fixups[i].getOffset() + DF->getContents().size());
DF->getFixups().push_back(Fixups[i]);
}
DF->setHasInstructions(STI);
DF->getContents().append(Code.begin(), Code.end());
if (Assembler.isBundlingEnabled() && Assembler.getRelaxAll()) {
if (!isBundleLocked()) {
mergeFragment(getOrCreateDataFragment(&STI), DF);
delete DF;
}
}
}
The instruction finally got emitted in target dependent encodeInstruction()
method.
Mips as an example below:
/// encodeInstruction - Emit the instruction.
/// Size the instruction with Desc.getSize().
void MipsMCCodeEmitter::
encodeInstruction(const MCInst &MI, raw_ostream &OS,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const
{
// Non-pseudo instructions that get changed for direct object
// only based on operand values.
// If this list of instructions get much longer we will move
// the check to a function call. Until then, this is more efficient.
MCInst TmpInst = MI;
switch (MI.getOpcode()) {
// If shift amount is >= 32 it the inst needs to be lowered further
case Mips::DSLL:
case Mips::DSRL:
case Mips::DSRA:
case Mips::DROTR:
LowerLargeShift(TmpInst);
break;
// Compact branches, enforce encoding restrictions.
case Mips::BEQC:
case Mips::BNEC:
case Mips::BEQC64:
case Mips::BNEC64:
case Mips::BOVC:
case Mips::BOVC_MMR6:
case Mips::BNVC:
case Mips::BNVC_MMR6:
LowerCompactBranch(TmpInst);
}
unsigned long N = Fixups.size();
uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
// Check for unimplemented opcodes.
// Unfortunately in MIPS both NOP and SLL will come in with Binary == 0
// so we have to special check for them.
const unsigned Opcode = TmpInst.getOpcode();
if ((Opcode != Mips::NOP) && (Opcode != Mips::SLL) &&
(Opcode != Mips::SLL_MM) && (Opcode != Mips::SLL_MMR6) && !Binary)
llvm_unreachable("unimplemented opcode in encodeInstruction()");
int NewOpcode = -1;
if (isMicroMips(STI)) {
if (isMips32r6(STI)) {
NewOpcode = Mips::MipsR62MicroMipsR6(Opcode, Mips::Arch_micromipsr6);
if (NewOpcode == -1)
NewOpcode = Mips::Std2MicroMipsR6(Opcode, Mips::Arch_micromipsr6);
}
else
NewOpcode = Mips::Std2MicroMips(Opcode, Mips::Arch_micromips);
// Check whether it is Dsp instruction.
if (NewOpcode == -1)
NewOpcode = Mips::Dsp2MicroMips(Opcode, Mips::Arch_mmdsp);
if (NewOpcode != -1) {
if (Fixups.size() > N)
Fixups.pop_back();
TmpInst.setOpcode (NewOpcode);
Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
}
if (((MI.getOpcode() == Mips::MOVEP_MM) ||
(MI.getOpcode() == Mips::MOVEP_MMR6))) {
unsigned RegPair = getMovePRegPairOpValue(MI, 0, Fixups, STI);
Binary = (Binary & 0xFFFFFC7F) | (RegPair << 7);
}
}
const MCInstrDesc &Desc = MCII.get(TmpInst.getOpcode());
// Get byte count of instruction
unsigned Size = Desc.getSize();
if (!Size)
llvm_unreachable("Desc.getSize() returns 0");
EmitInstruction(Binary, Size, STI, OS);
}
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?