LLVM 22.x tile for building compilers, language runtimes, and out-of-tree tooling
88
83%
Does it follow best practices?
Impact
96%
1.23xAverage score across 5 eval scenarios
Passed
No known issues
Reference: CodeGenerator | GlobalISel
LLVM IR (Module/Function)
│
▼ IR-level optimizations (NPM passes)
│
▼ SelectionDAGISel or IRTranslator ← IR → Machine IR
│
▼ Legalization ← expand illegal types/ops
│
▼ Register allocation ← virtual → physical regs
│
▼ Post-RA passes ← peephole, scheduling
│
▼ AsmPrinter / MCStreamer ← emit .s or .oTwo ISel paths exist in LLVM 22:
| Path | Status | Use when |
|---|---|---|
| SelectionDAG | Mature, widely used | Most existing targets |
| GlobalISel | Production-ready for major targets | New targets, AArch64, X86, RISC-V |
TargetMachine is the entry point for all codegen for a target.
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/TargetParser/Host.h" // sys::getDefaultTargetTriple()
// Look up target by triple
std::string Triple = sys::getDefaultTargetTriple();
std::string Error;
const Target *TheTarget = TargetRegistry::lookupTarget(Triple, Error);
if (!TheTarget) { errs() << Error; return 1; }
// Create TargetMachine
TargetOptions Options;
auto RM = std::optional<Reloc::Model>(Reloc::PIC_);
TargetMachine *TM = TheTarget->createTargetMachine(
Triple,
/*CPU=*/"generic", // or "native", or specific CPU like "cortex-a53"
/*Features=*/"", // feature string, e.g. "+avx2,-avx512f"
Options,
RM,
/*CM=*/std::nullopt,
CodeGenOptLevel::Default
);
// Set data layout on module
M->setDataLayout(TM->createDataLayout());
M->setTargetTriple(Triple);#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/LegacyPassManager.h" // only for addPassesToEmitFile in LLVM 22
// Emit to file (uses legacy PM internally — this API remains)
SmallVector<char, 0> ObjBuffer;
raw_svector_ostream OS(ObjBuffer);
legacy::PassManager PM;
if (TM->addPassesToEmitFile(PM, OS, nullptr,
CodeGenFileType::ObjectFile)) {
errs() << "Target does not support object emission\n";
return 1;
}
PM.run(*M);
// ObjBuffer now contains the ELF/Mach-O/COFF objectAfter instruction selection, IR is lowered to MachineFunction / MachineInstr / MachineBasicBlock.
// Access MachineFunction from a MachineFunctionPass
MachineFunction &MF = /* provided by pass */;
// Iterate machine basic blocks
for (MachineBasicBlock &MBB : MF) {
// Iterate machine instructions
for (MachineInstr &MI : MBB) {
unsigned Opcode = MI.getOpcode();
// Iterate operands
for (MachineOperand &MO : MI.operands()) {
if (MO.isReg()) { Register Reg = MO.getReg(); }
if (MO.isImm()) { int64_t Val = MO.getImm(); }
if (MO.isMBB()) { MachineBasicBlock *TBB = MO.getMBB(); }
}
}
}
// MachineRegisterInfo — virtual register info
MachineRegisterInfo &MRI = MF.getRegInfo();
Register VReg = MRI.createVirtualRegister(&MyISA::GPRRegClass);
Register PReg = MyISA::R0; // physical register
// MachineFrameInfo — stack frame
MachineFrameInfo &MFI = MF.getFrameInfo();
int FI = MFI.CreateStackObject(Size, Alignment, /*isSS=*/false);SelectionDAG lowers IR to a DAG of SDNodes, applies patterns from TableGen, and emits MachineInstrs.
| Class | Description |
|---|---|
SelectionDAG | The DAG; owns all SDNodes |
SDValue | A (node, result index) pair — the primary currency |
SDNode | A DAG node (operation + operands + types) |
SDLoc | Source location for an SDNode |
Override in <Target>ISelLowering.cpp:
// Constructor — set up legal types and operations
MyISATargetLowering::MyISATargetLowering(const TargetMachine &TM,
const MyISASubtarget &STI)
: TargetLowering(TM) {
addRegisterClass(MVT::i32, &MyISA::GPRRegClass);
computeRegisterProperties(STI.getRegisterInfo());
// Mark operations as legal / custom / expand / promote
setOperationAction(ISD::ADD, MVT::i32, Legal);
setOperationAction(ISD::SDIV, MVT::i32, Expand); // no hardware div
setOperationAction(ISD::ROTL, MVT::i32, Custom); // handle specially
setOperationAction(ISD::CTPOP, MVT::i32, Expand);
setLoadExtAction(ISD::EXTLOAD, MVT::i32, MVT::i8, Expand);
setTruncStoreAction(MVT::i32, MVT::i8, Expand);
}
// Lower a Custom operation
SDValue MyISATargetLowering::LowerOperation(SDValue Op,
SelectionDAG &DAG) const {
switch (Op.getOpcode()) {
case ISD::ROTL: return LowerROTL(Op, DAG);
default: llvm_unreachable("unimplemented custom lowering");
}
}
SDValue MyISATargetLowering::LowerROTL(SDValue Op, SelectionDAG &DAG) const {
SDLoc DL(Op);
SDValue LHS = Op.getOperand(0);
SDValue RHS = Op.getOperand(1);
// Build custom SDNode sequence or MyISAISD node
return DAG.getNode(MyISAISD::ROTL, DL, Op.getValueType(), LHS, RHS);
}SDValue MyISATargetLowering::LowerFormalArguments(
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
// Use TableGen-generated CC_MyISA to assign locations
SmallVector<CCValAssign, 16> ArgLocs;
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(),
ArgLocs, *DAG.getContext());
CCInfo.AnalyzeFormalArguments(Ins, CC_MyISA);
for (CCValAssign &VA : ArgLocs) {
if (VA.isRegLoc()) {
Register VReg = MF.getRegInfo().createVirtualRegister(&MyISA::GPRRegClass);
MF.getRegInfo().addLiveIn(VA.getLocReg(), VReg);
SDValue ArgVal = DAG.getCopyFromReg(Chain, DL, VReg, VA.getValVT());
InVals.push_back(ArgVal);
} else {
// Stack argument — load from frame
// ...
}
}
return Chain;
}GlobalISel operates on SSA Machine IR (MachineInstr with virtual registers) throughout, avoiding the SDNode intermediate representation.
IRTranslator → IR → generic MachineInstrs (G_ADD, G_LOAD, ...)
Legalizer → expand/narrow/widen illegal types and ops
RegBankSelect → assign register banks (GPR vs FPR vs vector)
InstructionSelect → generic MI → target MIG_ADD, G_SUB, G_MUL, G_SDIV, G_UDIV
G_AND, G_OR, G_XOR, G_SHL, G_ASHR, G_LSHR
G_LOAD, G_STORE
G_GEP (→ G_PTR_ADD in LLVM 22)
G_ICMP, G_FCMP
G_BR, G_BRCOND
G_PHI
G_CONSTANT, G_FCONSTANT
G_ZEXT, G_SEXT, G_TRUNC, G_BITCAST
G_CALL, G_RETURN_VALUE// In MyISALegalizerInfo.cpp
MyISALegalizerInfo::MyISALegalizerInfo(const MyISASubtarget &ST) {
using namespace TargetOpcode;
const LLT S32 = LLT::scalar(32);
const LLT S64 = LLT::scalar(64);
// i32 add is natively legal
getActionDefinitionsBuilder(G_ADD).legalFor({S32}).clampScalar(0, S32, S32);
// i64 add: widen to i64 only if supported, otherwise narrow to two i32
getActionDefinitionsBuilder(G_MUL)
.legalFor({S32})
.clampScalar(0, S32, S32);
// Division not supported — expand to library call
getActionDefinitionsBuilder(G_SDIV).libcall();
getLegacyLegalizerInfo().computeTables();
}// Implement selectImpl() in MyISAInstructionSelector.cpp
bool MyISAInstructionSelector::select(MachineInstr &I) {
if (selectImpl(I, *CoverageInfo)) // tries TableGen patterns
return true;
// Manual selection for ops without patterns
switch (I.getOpcode()) {
case TargetOpcode::G_CONSTANT: {
int64_t CVal = I.getOperand(1).getCImm()->getSExtValue();
MachineInstr *NewMI = BuildMI(*I.getParent(), I, I.getDebugLoc(),
TII.get(MyISA::MOVI))
.addDef(I.getOperand(0).getReg())
.addImm(CVal);
I.eraseFromParent();
return constrainSelectedInstRegOperands(*NewMI, TII, TRI, RBI);
}
}
return false;
}class MyMachinePass : public MachineFunctionPass {
public:
static char ID;
MyMachinePass() : MachineFunctionPass(ID) {}
bool runOnMachineFunction(MachineFunction &MF) override {
bool Changed = false;
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : MBB) {
// inspect / modify MI
}
}
return Changed;
}
StringRef getPassName() const override { return "My Machine Pass"; }
};
char MyMachinePass::ID = 0;Register in TargetPassConfig::addPreRegAlloc() or addPostRegAlloc().
SDValue after calling DAG.DeleteNode() or after DAG legalization — nodes can be replaced.computeRegisterProperties() in TargetLowering constructor — without it, legal type tables are incomplete.getLegacyLegalizerInfo().computeTables() at the end of LegalizerInfo constructor.BuildMI(MBB, InsertPt, DL, TII.get(Opc)) to build new MachineInstrs — never construct them directly.constrainSelectedInstRegOperands after manual instruction selection in GlobalISel.docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
skills
add-alias-analysis
add-attributes-metadata
add-calling-convention
add-debug-info
add-exception-handling
add-gc-statepoints
add-intrinsic
add-lto
add-sanitizer
add-vectorization-hint
frontend-to-ir
jit-setup
lit-filecheck
lower-struct-types
new-target
out-of-tree-setup
tessl-llvm
version-sync