Skip to content

Commit

Permalink
[M68k] Add support for basic memory constraints in inline asm
Browse files Browse the repository at this point in the history
This patch adds support for 'm', 'Q', and 'U' memory constraints.

Differential Revision: https://reviews.llvm.org/D143529
  • Loading branch information
mshockwave committed Mar 8, 2023
1 parent aaa2503 commit 7335cd0
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 1 deletion.
6 changes: 6 additions & 0 deletions clang/lib/Basic/Targets/M68k.cpp
Expand Up @@ -192,6 +192,12 @@ bool M68kTargetInfo::validateAsmConstraint(
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;
}
Expand Down
8 changes: 8 additions & 0 deletions clang/test/Sema/inline-asm-validate-m68k.c
Expand Up @@ -82,5 +82,13 @@ void a(int x) {
void d(int x) {
asm ("" :: "d"(x));
}

// Memory constraints
void mem() {
int x;
asm ("" :: "m"(x));
asm ("" :: "Q"(x));
asm ("" :: "U"(x));
}
#endif

84 changes: 84 additions & 0 deletions llvm/lib/Target/M68k/M68kAsmPrinter.cpp
Expand Up @@ -76,6 +76,90 @@ bool M68kAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
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<MemAddrModeKind>(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());
Expand Down
12 changes: 11 additions & 1 deletion llvm/lib/Target/M68k/M68kAsmPrinter.h
Expand Up @@ -16,6 +16,7 @@

#include "M68kMCInstLower.h"
#include "M68kTargetMachine.h"
#include "MCTargetDesc/M68kMemOperandPrinter.h"

#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/MC/MCStreamer.h"
Expand All @@ -34,12 +35,19 @@ class raw_ostream;
class M68kSubtarget;
class M68kMachineFunctionInfo;

class LLVM_LIBRARY_VISIBILITY M68kAsmPrinter : public AsmPrinter {
class LLVM_LIBRARY_VISIBILITY M68kAsmPrinter
: public AsmPrinter,
public M68kMemOperandPrinter<M68kAsmPrinter, MachineInstr> {

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;
Expand All @@ -57,6 +65,8 @@ class LLVM_LIBRARY_VISIBILITY M68kAsmPrinter : public AsmPrinter {

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;
Expand Down
74 changes: 74 additions & 0 deletions llvm/lib/Target/M68k/M68kISelDAGToDAG.cpp
Expand Up @@ -227,6 +227,9 @@ class M68kDAGToDAGISel : public SelectionDAGISel {
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<SDValue> &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
Expand Down Expand Up @@ -931,3 +934,74 @@ bool M68kDAGToDAGISel::SelectARI(SDNode *Parent, SDValue N, SDValue &Base) {

return false;
}

bool M68kDAGToDAGISel::SelectInlineAsmMemoryOperand(
const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &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;
}
}
11 changes: 11 additions & 0 deletions llvm/lib/Target/M68k/M68kISelLowering.cpp
Expand Up @@ -201,6 +201,14 @@ M68kTargetLowering::getExceptionSelectorRegister(const Constant *) const {
return M68k::D1;
}

unsigned
M68kTargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const {
return StringSwitch<unsigned>(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
Expand Down Expand Up @@ -2764,6 +2772,9 @@ M68kTargetLowering::getConstraintType(StringRef Constraint) const {
break;
}
break;
case 'Q':
case 'U':
return C_Memory;
default:
break;
}
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/M68k/M68kISelLowering.h
Expand Up @@ -187,6 +187,8 @@ class M68kTargetLowering : public TargetLowering {
Register
getExceptionSelectorRegister(const Constant *PersonalityFn) const override;

unsigned getInlineAsmMemConstraint(StringRef ConstraintCode) const override;

private:
unsigned GetAlignedArgumentStackSize(unsigned StackSize,
SelectionDAG &DAG) const;
Expand Down
26 changes: 26 additions & 0 deletions llvm/lib/Target/M68k/MCTargetDesc/M68kBaseInfo.h
Expand Up @@ -48,6 +48,32 @@ enum { MemDisp = 0, MemBase = 1, MemIndex = 2, MemOuter = 3 };
/// ([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 |
Expand Down
32 changes: 32 additions & 0 deletions 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.
Expand Down Expand Up @@ -120,3 +122,33 @@ entry:
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
}

0 comments on commit 7335cd0

Please sign in to comment.