diff --git a/clang/lib/Basic/Targets/M68k.cpp b/clang/lib/Basic/Targets/M68k.cpp --- a/clang/lib/Basic/Targets/M68k.cpp +++ b/clang/lib/Basic/Targets/M68k.cpp @@ -192,6 +192,12 @@ break; } break; + case 'Q': // address register indirect addressing + case 'U': // address register indirect w/ constant offset addressing + // TODO: Handle 'S' (basically 'm' when pc-rel is enforced) when + // '-mpcrel' flag is properly handled by the driver. + info.setAllowsMemory(); + return true; default: break; } diff --git a/clang/test/Sema/inline-asm-validate-m68k.c b/clang/test/Sema/inline-asm-validate-m68k.c --- a/clang/test/Sema/inline-asm-validate-m68k.c +++ b/clang/test/Sema/inline-asm-validate-m68k.c @@ -82,5 +82,13 @@ void d(int x) { asm ("" :: "d"(x)); } + +// Memory constraints +void mem() { + int x; + asm ("" :: "m"(x)); + asm ("" :: "Q"(x)); + asm ("" :: "U"(x)); +} #endif diff --git a/llvm/lib/Target/M68k/M68kAsmPrinter.h b/llvm/lib/Target/M68k/M68kAsmPrinter.h --- a/llvm/lib/Target/M68k/M68kAsmPrinter.h +++ b/llvm/lib/Target/M68k/M68kAsmPrinter.h @@ -16,6 +16,7 @@ #include "M68kMCInstLower.h" #include "M68kTargetMachine.h" +#include "MCTargetDesc/M68kMemOperandPrinter.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/MC/MCStreamer.h" @@ -34,12 +35,19 @@ class M68kSubtarget; class M68kMachineFunctionInfo; -class LLVM_LIBRARY_VISIBILITY M68kAsmPrinter : public AsmPrinter { +class LLVM_LIBRARY_VISIBILITY M68kAsmPrinter + : public AsmPrinter, + public M68kMemOperandPrinter { + + friend class M68kMemOperandPrinter; void EmitInstrWithMacroNoAT(const MachineInstr *MI); void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &OS); + void printDisp(const MachineInstr *MI, unsigned OpNum, raw_ostream &OS); + void printAbsMem(const MachineInstr *MI, unsigned OpNum, raw_ostream &OS); + public: const M68kSubtarget *Subtarget; const M68kMachineFunctionInfo *MMFI; @@ -57,6 +65,8 @@ bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &OS) override; void emitInstruction(const MachineInstr *MI) override; void emitFunctionBodyStart() override; diff --git a/llvm/lib/Target/M68k/M68kAsmPrinter.cpp b/llvm/lib/Target/M68k/M68kAsmPrinter.cpp --- a/llvm/lib/Target/M68k/M68kAsmPrinter.cpp +++ b/llvm/lib/Target/M68k/M68kAsmPrinter.cpp @@ -76,6 +76,90 @@ return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS); } +void M68kAsmPrinter::printDisp(const MachineInstr *MI, unsigned opNum, + raw_ostream &O) { + // Print immediate displacement without the '#' predix + const MachineOperand &Op = MI->getOperand(opNum); + if (Op.isImm()) { + O << Op.getImm(); + return; + } + // Displacement is relocatable, so we're pretty permissive about what + // can be put here. + printOperand(MI, opNum, O); +} + +void M68kAsmPrinter::printAbsMem(const MachineInstr *MI, unsigned OpNum, + raw_ostream &O) { + const MachineOperand &MO = MI->getOperand(OpNum); + if (MO.isImm()) + O << format("$%0" PRIx64, (uint64_t)MO.getImm()); + else + PrintAsmMemoryOperand(MI, OpNum, nullptr, O); +} + +bool M68kAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNo, const char *ExtraCode, + raw_ostream &OS) { + const MachineOperand &MO = MI->getOperand(OpNo); + switch (MO.getType()) { + case MachineOperand::MO_Immediate: + // Immediate value that goes here is the addressing mode kind we set + // in M68kDAGToDAGISel::SelectInlineAsmMemoryOperand. + using namespace M68k; + // Skip the addressing mode kind operand. + ++OpNo; + // Decode MemAddrModeKind. + switch (static_cast(MO.getImm())) { + case MemAddrModeKind::j: + printARIMem(MI, OpNo, OS); + break; + case MemAddrModeKind::o: + printARIPIMem(MI, OpNo, OS); + break; + case MemAddrModeKind::e: + printARIPDMem(MI, OpNo, OS); + break; + case MemAddrModeKind::p: + printARIDMem(MI, OpNo, OS); + break; + case MemAddrModeKind::f: + case MemAddrModeKind::F: + printARIIMem(MI, OpNo, OS); + break; + case MemAddrModeKind::k: + printPCIMem(MI, 0, OpNo, OS); + break; + case MemAddrModeKind::q: + printPCDMem(MI, 0, OpNo, OS); + break; + case MemAddrModeKind::b: + printAbsMem(MI, OpNo, OS); + break; + default: + llvm_unreachable("Unrecognized memory addressing mode"); + } + return false; + case MachineOperand::MO_GlobalAddress: + PrintSymbolOperand(MO, OS); + return false; + case MachineOperand::MO_BlockAddress: + GetBlockAddressSymbol(MO.getBlockAddress())->print(OS, MAI); + return false; + case MachineOperand::MO_Register: + // This is a special case where it is treated as a memory reference, with + // the register holding the address value. Thus, we print it as ARI here. + if (M68kII::isAddressRegister(MO.getReg())) { + printARIMem(MI, OpNo, OS); + return false; + } + break; + default: + break; + } + return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); +} + void M68kAsmPrinter::emitInstruction(const MachineInstr *MI) { M68k_MC::verifyInstructionPredicates(MI->getOpcode(), getSubtargetInfo().getFeatureBits()); diff --git a/llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp b/llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp --- a/llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp +++ b/llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp @@ -227,6 +227,9 @@ bool SelectPCD(SDNode *Parent, SDValue N, SDValue &Imm); bool SelectPCI(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index); + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) override; + // If Address Mode represents Frame Index store FI in Disp and // Displacement bit size in Base. These values are read symmetrically by // M68kRegisterInfo::eliminateFrameIndex method @@ -931,3 +934,74 @@ return false; } + +bool M68kDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + // In order to tell AsmPrinter the exact addressing mode we select here, which + // might comprise of multiple SDValues (hence MachineOperands), a 32-bit + // immediate value is prepended to the list of selected SDValues to indicate + // the addressing mode kind. + using AMK = M68k::MemAddrModeKind; + auto addKind = [this](SDValue &Opnd, AMK Kind) -> bool { + Opnd = CurDAG->getTargetConstant(unsigned(Kind), SDLoc(), MVT::i32); + return true; + }; + + switch (ConstraintID) { + // Generic memory operand. + case InlineAsm::Constraint_m: { + // Try every supported (memory) addressing modes. + SDValue Operands[4]; + + // TODO: The ordering of the following SelectXXX is relatively...arbitrary, + // right now we simply sort them by descending complexity. Maybe we should + // adjust this by code model and/or relocation mode in the future. + if (SelectARII(nullptr, Op, Operands[1], Operands[2], Operands[3]) && + addKind(Operands[0], AMK::f)) { + OutOps.insert(OutOps.end(), &Operands[0], Operands + 4); + return false; + } + + if ((SelectPCI(nullptr, Op, Operands[1], Operands[2]) && + addKind(Operands[0], AMK::k)) || + (SelectARID(nullptr, Op, Operands[1], Operands[2]) && + addKind(Operands[0], AMK::p))) { + OutOps.insert(OutOps.end(), &Operands[0], Operands + 3); + return false; + } + + if ((SelectPCD(nullptr, Op, Operands[1]) && addKind(Operands[0], AMK::q)) || + (SelectARI(nullptr, Op, Operands[1]) && addKind(Operands[0], AMK::j)) || + (SelectAL(nullptr, Op, Operands[1]) && addKind(Operands[0], AMK::b))) { + OutOps.insert(OutOps.end(), {Operands[0], Operands[1]}); + return false; + } + + return true; + } + // 'Q': Address register indirect addressing. + case InlineAsm::Constraint_Q: { + SDValue AMKind, Base; + // 'j' addressing mode. + // TODO: Add support for 'o' and 'e' after their + // select functions are implemented. + if (SelectARI(nullptr, Op, Base) && addKind(AMKind, AMK::j)) { + OutOps.insert(OutOps.end(), {AMKind, Base}); + return false; + } + return true; + } + // 'U': Address register indirect w/ constant offset addressing. + case InlineAsm::Constraint_Um: { + SDValue AMKind, Base, Offset; + // 'p' addressing mode. + if (SelectARID(nullptr, Op, Offset, Base) && addKind(AMKind, AMK::p)) { + OutOps.insert(OutOps.end(), {AMKind, Offset, Base}); + return false; + } + return true; + } + default: + return true; + } +} diff --git a/llvm/lib/Target/M68k/M68kISelLowering.h b/llvm/lib/Target/M68k/M68kISelLowering.h --- a/llvm/lib/Target/M68k/M68kISelLowering.h +++ b/llvm/lib/Target/M68k/M68kISelLowering.h @@ -187,6 +187,8 @@ Register getExceptionSelectorRegister(const Constant *PersonalityFn) const override; + unsigned getInlineAsmMemConstraint(StringRef ConstraintCode) const override; + private: unsigned GetAlignedArgumentStackSize(unsigned StackSize, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/M68k/M68kISelLowering.cpp b/llvm/lib/Target/M68k/M68kISelLowering.cpp --- a/llvm/lib/Target/M68k/M68kISelLowering.cpp +++ b/llvm/lib/Target/M68k/M68kISelLowering.cpp @@ -201,6 +201,14 @@ return M68k::D1; } +unsigned +M68kTargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const { + return StringSwitch(ConstraintCode) + .Case("Q", InlineAsm::Constraint_Q) + .Case("U", InlineAsm::Constraint_Um) // We borrow Constraint_Um for 'U'. + .Default(TargetLowering::getInlineAsmMemConstraint(ConstraintCode)); +} + EVT M68kTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &Context, EVT VT) const { // M68k SETcc producess either 0x00 or 0xFF @@ -2764,6 +2772,9 @@ break; } break; + case 'Q': + case 'U': + return C_Memory; default: break; } diff --git a/llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h b/llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h --- a/llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h +++ b/llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h @@ -48,6 +48,32 @@ /// ([bd,PC,Xn],od) enum { PCRelDisp = 0, PCRelIndex = 1, PCRelOuter = 2 }; +enum class MemAddrModeKind : unsigned { + j = 1, // (An) + o, // (An)+ + e, // -(An) + p, // (d,An) + f, // (d,An,Xn.L) + F, // (d,An,Xn.W) + g, // (d,An,Xn.L,SCALE) + G, // (d,An,Xn.W,SCALE) + u, // ([bd,An],Xn.L,SCALE,od) + U, // ([bd,An],Xn.W,SCALE,od) + v, // ([bd,An,Xn.L,SCALE],od) + V, // ([bd,An,Xn.W,SCALE],od) + b, // abs.L + B, // abs.W + q, // (d,PC) + k, // (d,PC,Xn.L) + K, // (d,PC,Xn.W) + l, // (d,PC,Xn.L,SCALE) + L, // (d,PC,Xn.W,SCALE) + x, // ([bd,PC],Xn.L,SCALE,od) + X, // ([bd,PC],Xn.W,SCALE,od) + y, // ([bd,PC,Xn.L,SCALE],od) + Y // ([bd,PC,Xn.W,SCALE],od) +}; + // On a LE host: // MSB LSB MSB LSB // | 0x12 0x34 | 0xAB 0xCD | -> | 0xAB 0xCD | 0x12 0x34 | diff --git a/llvm/test/CodeGen/M68k/inline-asm.ll b/llvm/test/CodeGen/M68k/inline-asm.ll --- a/llvm/test/CodeGen/M68k/inline-asm.ll +++ b/llvm/test/CodeGen/M68k/inline-asm.ll @@ -1,6 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=m68k < %s -o - | FileCheck %s +@g = internal global i32 10, align 4 + ; This function is primarily testing constant constraints that can NOT ; be easily checked by Clang. For example, 'K' and 'M' are both ; constraints for values that are outside certain numerical range. @@ -120,3 +122,33 @@ ret void } +define void @memory_constraints() { +; CHECK-LABEL: memory_constraints: +; CHECK: .cfi_startproc +; CHECK-NEXT: ; %bb.0: ; %entry +; CHECK-NEXT: suba.l #4, %sp +; CHECK-NEXT: .cfi_def_cfa_offset -8 +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l (0,%sp), %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l (g,%pc), %d2 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: lea (0,%sp), %a0 +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l (%a0), %d3 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l (0,%sp), %d4 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: adda.l #4, %sp +; CHECK-NEXT: rts +entry: + %x = alloca i32, align 4 + call void asm sideeffect "move.l $0, %d1", "*m"(ptr elementtype(i32) %x) + call void asm sideeffect "move.l $0, %d2", "*m"(ptr elementtype(i32) @g) + call void asm sideeffect "move.l $0, %d3", "*Q"(ptr elementtype(i32) %x) + call void asm sideeffect "move.l $0, %d4", "*U"(ptr elementtype(i32) %x) + ret void +} +