diff options
Diffstat (limited to 'lib/Target/XCore')
| -rw-r--r-- | lib/Target/XCore/AsmPrinter/XCoreAsmPrinter.cpp | 23 | ||||
| -rw-r--r-- | lib/Target/XCore/XCoreISelDAGToDAG.cpp | 17 | ||||
| -rw-r--r-- | lib/Target/XCore/XCoreISelLowering.cpp | 81 | ||||
| -rw-r--r-- | lib/Target/XCore/XCoreISelLowering.h | 12 | ||||
| -rw-r--r-- | lib/Target/XCore/XCoreInstrInfo.cpp | 13 | ||||
| -rw-r--r-- | lib/Target/XCore/XCoreInstrInfo.td | 38 | ||||
| -rw-r--r-- | lib/Target/XCore/XCoreTargetObjectFile.h | 3 |
7 files changed, 137 insertions, 50 deletions
diff --git a/lib/Target/XCore/AsmPrinter/XCoreAsmPrinter.cpp b/lib/Target/XCore/AsmPrinter/XCoreAsmPrinter.cpp index d18f55de81..82e23a13e8 100644 --- a/lib/Target/XCore/AsmPrinter/XCoreAsmPrinter.cpp +++ b/lib/Target/XCore/AsmPrinter/XCoreAsmPrinter.cpp @@ -27,6 +27,7 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Target/TargetData.h" @@ -62,6 +63,11 @@ namespace { } void printMemOperand(const MachineInstr *MI, int opNum); + void printInlineJT(const MachineInstr *MI, int opNum, + const std::string &directive = ".jmptable"); + void printInlineJT32(const MachineInstr *MI, int opNum) { + printInlineJT(MI, opNum, ".jmptable32"); + } void printOperand(const MachineInstr *MI, int opNum); bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, unsigned AsmVariant, const char *ExtraCode); @@ -257,6 +263,23 @@ void XCoreAsmPrinter::printMemOperand(const MachineInstr *MI, int opNum) printOperand(MI, opNum+1); } +void XCoreAsmPrinter:: +printInlineJT(const MachineInstr *MI, int opNum, const std::string &directive) +{ + unsigned JTI = MI->getOperand(opNum).getIndex(); + const MachineFunction *MF = MI->getParent()->getParent(); + const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); + const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables(); + const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs; + O << "\t" << directive << " "; + for (unsigned i = 0, e = JTBBs.size(); i != e; ++i) { + MachineBasicBlock *MBB = JTBBs[i]; + if (i > 0) + O << ","; + O << *MBB->getSymbol(OutContext); + } +} + void XCoreAsmPrinter::printOperand(const MachineInstr *MI, int opNum) { const MachineOperand &MO = MI->getOperand(opNum); switch (MO.getType()) { diff --git a/lib/Target/XCore/XCoreISelDAGToDAG.cpp b/lib/Target/XCore/XCoreISelDAGToDAG.cpp index 383fd91d2e..b1ab13272f 100644 --- a/lib/Target/XCore/XCoreISelDAGToDAG.cpp +++ b/lib/Target/XCore/XCoreISelDAGToDAG.cpp @@ -65,8 +65,6 @@ namespace { bool SelectADDRcpii(SDNode *Op, SDValue Addr, SDValue &Base, SDValue &Offset); - virtual void InstructionSelect(); - virtual const char *getPassName() const { return "XCore DAG->DAG Pattern Instruction Selection"; } @@ -147,15 +145,6 @@ bool XCoreDAGToDAGISel::SelectADDRcpii(SDNode *Op, SDValue Addr, return false; } -/// InstructionSelect - This callback is invoked by -/// SelectionDAGISel when it has created a SelectionDAG for us to codegen. -void XCoreDAGToDAGISel::InstructionSelect() { - // Select target instructions for the DAG. - SelectRoot(*CurDAG); - - CurDAG->RemoveDeadNodes(); -} - SDNode *XCoreDAGToDAGISel::Select(SDNode *N) { DebugLoc dl = N->getDebugLoc(); EVT NVT = N->getValueType(0); @@ -164,7 +153,11 @@ SDNode *XCoreDAGToDAGISel::Select(SDNode *N) { default: break; case ISD::Constant: { if (Predicate_immMskBitp(N)) { - SDValue MskSize = Transform_msksize_xform(N); + // Transformation function: get the size of a mask + int64_t MaskVal = cast<ConstantSDNode>(N)->getZExtValue(); + assert(isMask_32(MaskVal)); + // Look for the first non-zero bit + SDValue MskSize = getI32Imm(32 - CountLeadingZeros_32(MaskVal)); return CurDAG->getMachineNode(XCore::MKMSK_rus, dl, MVT::i32, MskSize); } diff --git a/lib/Target/XCore/XCoreISelLowering.cpp b/lib/Target/XCore/XCoreISelLowering.cpp index bf8c38fbdd..e6515d821b 100644 --- a/lib/Target/XCore/XCoreISelLowering.cpp +++ b/lib/Target/XCore/XCoreISelLowering.cpp @@ -29,6 +29,7 @@ #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAGISel.h" #include "llvm/CodeGen/ValueTypes.h" @@ -53,6 +54,8 @@ getTargetNodeName(unsigned Opcode) const case XCoreISD::RETSP : return "XCoreISD::RETSP"; case XCoreISD::LADD : return "XCoreISD::LADD"; case XCoreISD::LSUB : return "XCoreISD::LSUB"; + case XCoreISD::BR_JT : return "XCoreISD::BR_JT"; + case XCoreISD::BR_JT32 : return "XCoreISD::BR_JT32"; default : return NULL; } } @@ -106,9 +109,8 @@ XCoreTargetLowering::XCoreTargetLowering(XCoreTargetMachine &XTM) setOperationAction(ISD::TRAP, MVT::Other, Legal); - // Expand jump tables for now - setOperationAction(ISD::BR_JT, MVT::Other, Expand); - setOperationAction(ISD::JumpTable, MVT::i32, Custom); + // Jump tables. + setOperationAction(ISD::BR_JT, MVT::Other, Custom); setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); setOperationAction(ISD::BlockAddress, MVT::i32 , Custom); @@ -157,7 +159,7 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) { case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG); case ISD::BlockAddress: return LowerBlockAddress(Op, DAG); case ISD::ConstantPool: return LowerConstantPool(Op, DAG); - case ISD::JumpTable: return LowerJumpTable(Op, DAG); + case ISD::BR_JT: return LowerBR_JT(Op, DAG); case ISD::LOAD: return LowerLOAD(Op, DAG); case ISD::STORE: return LowerSTORE(Op, DAG); case ISD::SELECT_CC: return LowerSELECT_CC(Op, DAG); @@ -315,14 +317,27 @@ LowerConstantPool(SDValue Op, SelectionDAG &DAG) } SDValue XCoreTargetLowering:: -LowerJumpTable(SDValue Op, SelectionDAG &DAG) +LowerBR_JT(SDValue Op, SelectionDAG &DAG) { - // FIXME there isn't really debug info here + SDValue Chain = Op.getOperand(0); + SDValue Table = Op.getOperand(1); + SDValue Index = Op.getOperand(2); DebugLoc dl = Op.getDebugLoc(); - EVT PtrVT = Op.getValueType(); - JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); - SDValue JTI = DAG.getTargetJumpTable(JT->getIndex(), PtrVT); - return DAG.getNode(XCoreISD::DPRelativeWrapper, dl, MVT::i32, JTI); + JumpTableSDNode *JT = cast<JumpTableSDNode>(Table); + unsigned JTI = JT->getIndex(); + MachineFunction &MF = DAG.getMachineFunction(); + const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); + SDValue TargetJT = DAG.getTargetJumpTable(JT->getIndex(), MVT::i32); + + unsigned NumEntries = MJTI->getJumpTables()[JTI].MBBs.size(); + if (NumEntries <= 32) { + return DAG.getNode(XCoreISD::BR_JT, dl, MVT::Other, Chain, TargetJT, Index); + } + assert((NumEntries >> 31) == 0); + SDValue ScaledIndex = DAG.getNode(ISD::SHL, dl, MVT::i32, Index, + DAG.getConstant(1, MVT::i32)); + return DAG.getNode(XCoreISD::BR_JT32, dl, MVT::Other, Chain, TargetJT, + ScaledIndex); } static bool @@ -390,7 +405,12 @@ LowerLOAD(SDValue Op, SelectionDAG &DAG) if (Offset % 4 == 0) { // We've managed to infer better alignment information than the load // already has. Use an aligned load. - return DAG.getLoad(getPointerTy(), dl, Chain, BasePtr, NULL, 4); + // + // FIXME: No new alignment information is actually passed here. + // Should the offset really be 4? + // + return DAG.getLoad(getPointerTy(), dl, Chain, BasePtr, NULL, 4, + false, false, 0); } // Lower to // ldw low, base[offset >> 2] @@ -407,9 +427,9 @@ LowerLOAD(SDValue Op, SelectionDAG &DAG) SDValue HighAddr = DAG.getNode(ISD::ADD, dl, MVT::i32, Base, HighOffset); SDValue Low = DAG.getLoad(getPointerTy(), dl, Chain, - LowAddr, NULL, 4); + LowAddr, NULL, 4, false, false, 0); SDValue High = DAG.getLoad(getPointerTy(), dl, Chain, - HighAddr, NULL, 4); + HighAddr, NULL, 4, false, false, 0); SDValue LowShifted = DAG.getNode(ISD::SRL, dl, MVT::i32, Low, LowShift); SDValue HighShifted = DAG.getNode(ISD::SHL, dl, MVT::i32, High, HighShift); SDValue Result = DAG.getNode(ISD::OR, dl, MVT::i32, LowShifted, HighShifted); @@ -423,12 +443,13 @@ LowerLOAD(SDValue Op, SelectionDAG &DAG) int SVOffset = LD->getSrcValueOffset(); SDValue Low = DAG.getExtLoad(ISD::ZEXTLOAD, dl, MVT::i32, Chain, BasePtr, LD->getSrcValue(), SVOffset, MVT::i16, - LD->isVolatile(), 2); + LD->isVolatile(), LD->isNonTemporal(), 2); SDValue HighAddr = DAG.getNode(ISD::ADD, dl, MVT::i32, BasePtr, DAG.getConstant(2, MVT::i32)); SDValue High = DAG.getExtLoad(ISD::EXTLOAD, dl, MVT::i32, Chain, HighAddr, LD->getSrcValue(), SVOffset + 2, - MVT::i16, LD->isVolatile(), 2); + MVT::i16, LD->isVolatile(), + LD->isNonTemporal(), 2); SDValue HighShifted = DAG.getNode(ISD::SHL, dl, MVT::i32, High, DAG.getConstant(16, MVT::i32)); SDValue Result = DAG.getNode(ISD::OR, dl, MVT::i32, Low, HighShifted); @@ -452,7 +473,7 @@ LowerLOAD(SDValue Op, SelectionDAG &DAG) false, false, 0, CallingConv::C, false, /*isReturnValueUsed=*/true, DAG.getExternalSymbol("__misaligned_load", getPointerTy()), - Args, DAG, dl, DAG.GetOrdering(Chain.getNode())); + Args, DAG, dl); SDValue Ops[] = { CallResult.first, CallResult.second }; @@ -487,12 +508,14 @@ LowerSTORE(SDValue Op, SelectionDAG &DAG) DAG.getConstant(16, MVT::i32)); SDValue StoreLow = DAG.getTruncStore(Chain, dl, Low, BasePtr, ST->getSrcValue(), SVOffset, MVT::i16, - ST->isVolatile(), 2); + ST->isVolatile(), ST->isNonTemporal(), + 2); SDValue HighAddr = DAG.getNode(ISD::ADD, dl, MVT::i32, BasePtr, DAG.getConstant(2, MVT::i32)); SDValue StoreHigh = DAG.getTruncStore(Chain, dl, High, HighAddr, ST->getSrcValue(), SVOffset + 2, - MVT::i16, ST->isVolatile(), 2); + MVT::i16, ST->isVolatile(), + ST->isNonTemporal(), 2); return DAG.getNode(ISD::TokenFactor, dl, MVT::Other, StoreLow, StoreHigh); } @@ -513,7 +536,7 @@ LowerSTORE(SDValue Op, SelectionDAG &DAG) false, false, 0, CallingConv::C, false, /*isReturnValueUsed=*/true, DAG.getExternalSymbol("__misaligned_store", getPointerTy()), - Args, DAG, dl, DAG.GetOrdering(Chain.getNode())); + Args, DAG, dl); return CallResult.second; } @@ -561,15 +584,16 @@ LowerVAARG(SDValue Op, SelectionDAG &DAG) const Value *V = cast<SrcValueSDNode>(Node->getOperand(2))->getValue(); EVT VT = Node->getValueType(0); SDValue VAList = DAG.getLoad(getPointerTy(), dl, Node->getOperand(0), - Node->getOperand(1), V, 0); + Node->getOperand(1), V, 0, false, false, 0); // Increment the pointer, VAList, to the next vararg SDValue Tmp3 = DAG.getNode(ISD::ADD, dl, getPointerTy(), VAList, DAG.getConstant(VT.getSizeInBits(), getPointerTy())); // Store the incremented VAList to the legalized pointer - Tmp3 = DAG.getStore(VAList.getValue(1), dl, Tmp3, Node->getOperand(1), V, 0); + Tmp3 = DAG.getStore(VAList.getValue(1), dl, Tmp3, Node->getOperand(1), V, 0, + false, false, 0); // Load the actual argument out of the pointer VAList - return DAG.getLoad(VT, dl, Tmp3, VAList, NULL, 0); + return DAG.getLoad(VT, dl, Tmp3, VAList, NULL, 0, false, false, 0); } SDValue XCoreTargetLowering:: @@ -582,7 +606,8 @@ LowerVASTART(SDValue Op, SelectionDAG &DAG) XCoreFunctionInfo *XFI = MF.getInfo<XCoreFunctionInfo>(); SDValue Addr = DAG.getFrameIndex(XFI->getVarArgsFrameIndex(), MVT::i32); const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); - return DAG.getStore(Op.getOperand(0), dl, Addr, Op.getOperand(1), SV, 0); + return DAG.getStore(Op.getOperand(0), dl, Addr, Op.getOperand(1), SV, 0, + false, false, 0); } SDValue XCoreTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) { @@ -877,7 +902,8 @@ XCoreTargetLowering::LowerCCCArguments(SDValue Chain, // Create the SelectionDAG nodes corresponding to a load //from this parameter SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); - InVals.push_back(DAG.getLoad(VA.getLocVT(), dl, Chain, FIN, NULL, 0)); + InVals.push_back(DAG.getLoad(VA.getLocVT(), dl, Chain, FIN, NULL, 0, + false, false, 0)); } } @@ -908,7 +934,8 @@ XCoreTargetLowering::LowerCCCArguments(SDValue Chain, RegInfo.addLiveIn(ArgRegs[i], VReg); SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, MVT::i32); // Move argument from virt reg -> stack - SDValue Store = DAG.getStore(Val.getValue(1), dl, Val, FIN, NULL, 0); + SDValue Store = DAG.getStore(Val.getValue(1), dl, Val, FIN, NULL, 0, + false, false, 0); MemOps.push_back(Store); } if (!MemOps.empty()) @@ -1134,10 +1161,8 @@ static inline bool isImmUs4(int64_t val) bool XCoreTargetLowering::isLegalAddressingMode(const AddrMode &AM, const Type *Ty) const { - // Be conservative with void - // FIXME: Can we be more aggressive? if (Ty->getTypeID() == Type::VoidTyID) - return false; + return AM.Scale == 0 && isImmUs(AM.BaseOffs) && isImmUs4(AM.BaseOffs); const TargetData *TD = TM.getTargetData(); unsigned Size = TD->getTypeAllocSize(Ty); diff --git a/lib/Target/XCore/XCoreISelLowering.h b/lib/Target/XCore/XCoreISelLowering.h index f7b620e5fb..0c638aff48 100644 --- a/lib/Target/XCore/XCoreISelLowering.h +++ b/lib/Target/XCore/XCoreISelLowering.h @@ -28,7 +28,7 @@ namespace llvm { namespace XCoreISD { enum NodeType { // Start the numbering where the builtin ops and target ops leave off. - FIRST_NUMBER = ISD::BUILTIN_OP_END+XCore::INSTRUCTION_LIST_END, + FIRST_NUMBER = ISD::BUILTIN_OP_END, // Branch and link (call) BL, @@ -52,7 +52,13 @@ namespace llvm { LADD, // Corresponds to LSUB instruction - LSUB + LSUB, + + // Jumptable branch. + BR_JT, + + // Jumptable branch using long branches for each entry. + BR_JT32 }; } @@ -122,7 +128,7 @@ namespace llvm { SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG); SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG); SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG); - SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG); + SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG); SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG); SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG); SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG); diff --git a/lib/Target/XCore/XCoreInstrInfo.cpp b/lib/Target/XCore/XCoreInstrInfo.cpp index 5a548449d8..722e7470f0 100644 --- a/lib/Target/XCore/XCoreInstrInfo.cpp +++ b/lib/Target/XCore/XCoreInstrInfo.cpp @@ -145,6 +145,11 @@ static inline bool IsCondBranch(unsigned BrOpc) { return IsBRF(BrOpc) || IsBRT(BrOpc); } +static inline bool IsBR_JT(unsigned BrOpc) { + return BrOpc == XCore::BR_JT + || BrOpc == XCore::BR_JT32; +} + /// GetCondFromBranchOpc - Return the XCore CC that matches /// the correspondent Branch instruction opcode. static XCore::CondCode GetCondFromBranchOpc(unsigned BrOpc) @@ -271,6 +276,14 @@ XCoreInstrInfo::AnalyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, return false; } + // Likewise if it ends with a branch table followed by an unconditional branch. + if (IsBR_JT(SecondLastInst->getOpcode()) && IsBRU(LastInst->getOpcode())) { + I = LastInst; + if (AllowModify) + I->eraseFromParent(); + return true; + } + // Otherwise, can't handle this. return true; } diff --git a/lib/Target/XCore/XCoreInstrInfo.td b/lib/Target/XCore/XCoreInstrInfo.td index 10dc18cfea..46805d59ae 100644 --- a/lib/Target/XCore/XCoreInstrInfo.td +++ b/lib/Target/XCore/XCoreInstrInfo.td @@ -34,6 +34,15 @@ def XCoreBranchLink : SDNode<"XCoreISD::BL",SDT_XCoreBranchLink, def XCoreRetsp : SDNode<"XCoreISD::RETSP", SDTNone, [SDNPHasChain, SDNPOptInFlag]>; +def SDT_XCoreBR_JT : SDTypeProfile<0, 2, + [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; + +def XCoreBR_JT : SDNode<"XCoreISD::BR_JT", SDT_XCoreBR_JT, + [SDNPHasChain]>; + +def XCoreBR_JT32 : SDNode<"XCoreISD::BR_JT32", SDT_XCoreBR_JT, + [SDNPHasChain]>; + def SDT_XCoreAddress : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, SDTCisPtrTy<0>]>; @@ -185,6 +194,15 @@ def MEMii : Operand<i32> { let MIOperandInfo = (ops i32imm, i32imm); } +// Jump tables. +def InlineJT : Operand<i32> { + let PrintMethod = "printInlineJT"; +} + +def InlineJT32 : Operand<i32> { + let PrintMethod = "printInlineJT32"; +} + //===----------------------------------------------------------------------===// // Instruction Class Templates //===----------------------------------------------------------------------===// @@ -624,7 +642,7 @@ defm RETSP : FU6_LU6<"retsp", XCoreRetsp>; // TODO extdp, kentsp, krestsp, blat, setsr // clrsr, getsr, kalli -let isBranch = 1, isTerminator = 1 in { +let isBranch = 1, isTerminator = 1, isBarrier = 1 in { def BRBU_u6 : _FU6< (outs), (ins brtarget:$target), @@ -756,24 +774,34 @@ def CLZ_l2r : _FL2R<(outs GRRegs:$dst), (ins GRRegs:$src), // One operand short // TODO edu, eeu, waitet, waitef, freer, tstart, msync, mjoin, syncr, clrtp -// bru, setdp, setcp, setv, setev, kcall +// setdp, setcp, setv, setev, kcall // dgetreg -let isBranch=1, isIndirectBranch=1, isTerminator=1 in +let isBranch=1, isIndirectBranch=1, isTerminator=1, isBarrier = 1 in def BAU_1r : _F1R<(outs), (ins GRRegs:$addr), "bau $addr", [(brind GRRegs:$addr)]>; +let isBranch=1, isIndirectBranch=1, isTerminator=1, isBarrier = 1 in +def BR_JT : PseudoInstXCore<(outs), (ins InlineJT:$t, GRRegs:$i), + "bru $i\n$t", + [(XCoreBR_JT tjumptable:$t, GRRegs:$i)]>; + +let isBranch=1, isIndirectBranch=1, isTerminator=1, isBarrier = 1 in +def BR_JT32 : PseudoInstXCore<(outs), (ins InlineJT32:$t, GRRegs:$i), + "bru $i\n$t", + [(XCoreBR_JT32 tjumptable:$t, GRRegs:$i)]>; + let Defs=[SP], neverHasSideEffects=1 in def SETSP_1r : _F1R<(outs), (ins GRRegs:$src), "set sp, $src", []>; -let isBarrier = 1, hasCtrlDep = 1 in +let hasCtrlDep = 1 in def ECALLT_1r : _F1R<(outs), (ins GRRegs:$src), "ecallt $src", []>; -let isBarrier = 1, hasCtrlDep = 1 in +let hasCtrlDep = 1 in def ECALLF_1r : _F1R<(outs), (ins GRRegs:$src), "ecallf $src", []>; diff --git a/lib/Target/XCore/XCoreTargetObjectFile.h b/lib/Target/XCore/XCoreTargetObjectFile.h index 7efb990b79..7424c78be3 100644 --- a/lib/Target/XCore/XCoreTargetObjectFile.h +++ b/lib/Target/XCore/XCoreTargetObjectFile.h @@ -10,13 +10,12 @@ #ifndef LLVM_TARGET_XCORE_TARGETOBJECTFILE_H #define LLVM_TARGET_XCORE_TARGETOBJECTFILE_H -#include "llvm/Target/TargetLoweringObjectFile.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" namespace llvm { class XCoreTargetObjectFile : public TargetLoweringObjectFileELF { public: - void Initialize(MCContext &Ctx, const TargetMachine &TM); // TODO: Classify globals as xcore wishes. |
