diff --git a/clang/lib/Basic/Targets/M68k.h b/clang/lib/Basic/Targets/M68k.h --- a/clang/lib/Basic/Targets/M68k.h +++ b/clang/lib/Basic/Targets/M68k.h @@ -44,6 +44,7 @@ bool hasFeature(StringRef Feature) const override; ArrayRef getGCCRegNames() const override; ArrayRef getGCCRegAliases() const override; + std::string convertConstraint(const char *&Constraint) const override; bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &info) const override; const char *getClobbers() const override; 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 @@ -18,7 +18,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/TargetParser.h" +#include #include +#include namespace clang { namespace targets { @@ -142,18 +144,61 @@ switch (*Name) { case 'a': // address register case 'd': // data register - case 'f': // floating point register info.setAllowsRegister(); return true; - case 'K': // the constant 1 - case 'L': // constant -1^20 .. 1^19 - case 'M': // constant 1-4: + case 'I': // constant integer in the range [1,8] + info.setRequiresImmediate(1, 8); return true; + case 'J': // constant signed 16-bit integer + info.setRequiresImmediate(std::numeric_limits::min(), + std::numeric_limits::max()); + return true; + case 'K': // constant that is NOT in the range of [-0x80, 0x80) + info.setRequiresImmediate(); + return true; + case 'L': // constant integer in the range [-8,-1] + info.setRequiresImmediate(-8, -1); + return true; + case 'M': // constant that is NOT in the range of [-0x100, 0x100] + info.setRequiresImmediate(); + return true; + case 'N': // constant integer in the range [24,31] + info.setRequiresImmediate(24, 31); + return true; + case 'O': // constant integer 16 + info.setRequiresImmediate(16); + return true; + case 'P': // constant integer in the range [8,15] + info.setRequiresImmediate(8, 15); + return true; + case 'C': + ++Name; + switch (*Name) { + case '0': // constant integer 0 + info.setRequiresImmediate(0); + return true; + case 'i': // constant integer + case 'j': // integer constant that doesn't fit in 16 bits + info.setRequiresImmediate(); + return true; + default: + break; + } + break; + default: + break; } - // FIXME: Support all constraints like 'N', 'O', 'P', 'R' return false; } +std::string M68kTargetInfo::convertConstraint(const char *&Constraint) const { + if (*Constraint == 'C') + // Two-character constraint; add "^" hint for later parsing + return std::string("^") + std::string(Constraint++, 2); + + return std::string(1, *Constraint); +} + const char *M68kTargetInfo::getClobbers() const { // FIXME: Is this really right? return ""; diff --git a/clang/test/Sema/inline-asm-validate-m68k.c b/clang/test/Sema/inline-asm-validate-m68k.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/inline-asm-validate-m68k.c @@ -0,0 +1,86 @@ +// REQUIRES: m68k-registered-target +// RUN: %clang_cc1 -triple m68k -fsyntax-only -verify %s -DINVALID +// RUN: %clang_cc1 -triple m68k -fsyntax-only -verify %s + +#ifdef INVALID + +// Invalid constraint usages that can be blocked by frontend + +void I() { + static const int BelowMin = 0; + static const int AboveMax = 9; + asm ("" :: "I"(BelowMin)); // expected-error{{value '0' out of range for constraint 'I'}} + asm ("" :: "I"(AboveMax)); // expected-error{{value '9' out of range for constraint 'I'}} +} + +void J() { + static const int BelowMin = -0x8001; + static const int AboveMax = 0x8000; + asm ("" :: "J"(BelowMin)); // expected-error{{value '-32769' out of range for constraint 'J'}} + asm ("" :: "J"(AboveMax)); // expected-error{{value '32768' out of range for constraint 'J'}} +} + +void L() { + static const int BelowMin = -9; + static const int AboveMax = 0; + asm ("" :: "L"(BelowMin)); // expected-error{{value '-9' out of range for constraint 'L'}} + asm ("" :: "L"(AboveMax)); // expected-error{{value '0' out of range for constraint 'L'}} +} + +void N() { + static const int BelowMin = 23; + static const int AboveMax = 32; + asm ("" :: "N"(BelowMin)); // expected-error{{value '23' out of range for constraint 'N'}} + asm ("" :: "N"(AboveMax)); // expected-error{{value '32' out of range for constraint 'N'}} +} + +void O() { + // Valid only if it's 16 + static const int IncorrectVal = 18; + asm ("" :: "O"(IncorrectVal)); // expected-error{{value '18' out of range for constraint 'O'}} +} + +void P() { + static const int BelowMin = 7; + static const int AboveMax = 16; + asm ("" :: "P"(BelowMin)); // expected-error{{value '7' out of range for constraint 'P'}} + asm ("" :: "P"(AboveMax)); // expected-error{{value '16' out of range for constraint 'P'}} +} + +void C0() { + // Valid only if it's 0 + static const int IncorrectVal = 1; + asm ("" :: "C0"(IncorrectVal)); // expected-error{{value '1' out of range for constraint 'C0'}} +} + +#else +// Valid constraint usages. +// Note that these constraints can not be fully validated by frontend. +// So we're only testing the availability of their letters here. +// expected-no-diagnostics + +void K() { + asm ("" :: "K"(0x80)); +} + +void M() { + asm ("" :: "M"(0x100)); +} +void Ci() { + asm ("" :: "Ci"(0)); +} + +void Cj() { + asm ("" :: "Cj"(0x8000)); +} + +// Register constraints +void a(int x) { + asm ("" :: "a"(x)); +} + +void d(int x) { + asm ("" :: "d"(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 @@ -38,6 +38,8 @@ void EmitInstrWithMacroNoAT(const MachineInstr *MI); + void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &OS); + public: const M68kSubtarget *Subtarget; const M68kMachineFunctionInfo *MMFI; @@ -53,6 +55,9 @@ virtual bool runOnMachineFunction(MachineFunction &MF) override; + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &OS) override; + void emitInstruction(const MachineInstr *MI) override; void emitFunctionBodyStart() override; void emitFunctionBodyEnd() 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 @@ -18,6 +18,7 @@ #include "M68k.h" #include "M68kMachineFunction.h" +#include "MCTargetDesc/M68kInstPrinter.h" #include "TargetInfo/M68kTargetInfo.h" #include "llvm/Support/TargetRegistry.h" @@ -33,6 +34,48 @@ return true; } +void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, + raw_ostream &OS) { + const MachineOperand &MO = MI->getOperand(OpNum); + switch (MO.getType()) { + case MachineOperand::MO_Register: + OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg()); + break; + case MachineOperand::MO_Immediate: + OS << '#' << MO.getImm(); + break; + case MachineOperand::MO_MachineBasicBlock: + MO.getMBB()->getSymbol()->print(OS, MAI); + break; + case MachineOperand::MO_GlobalAddress: + PrintSymbolOperand(MO, OS); + break; + case MachineOperand::MO_BlockAddress: + GetBlockAddressSymbol(MO.getBlockAddress())->print(OS, MAI); + break; + case MachineOperand::MO_ConstantPoolIndex: { + const DataLayout &DL = getDataLayout(); + OS << DL.getPrivateGlobalPrefix() << "CPI" << getFunctionNumber() << '_' + << MO.getIndex(); + break; + } + default: + llvm_unreachable("not implemented"); + } +} + +bool M68kAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &OS) { + // Print the operand if there is no operand modifier. + if (!ExtraCode || !ExtraCode[0]) { + printOperand(MI, OpNo, OS); + return false; + } + + // Fallback to the default implementation. + return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS); +} + void M68kAsmPrinter::emitInstruction(const MachineInstr *MI) { switch (MI->getOpcode()) { default: { 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 @@ -156,6 +156,17 @@ unsigned JTI, MCContext &Ctx) const override; + ConstraintType getConstraintType(StringRef ConstraintStr) const override; + + std::pair + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; + + // Lower operand with C_Immediate and C_Other constraint type + void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, + std::vector &Ops, + SelectionDAG &DAG) const override; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const override; 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 @@ -2689,6 +2689,193 @@ return MCSymbolRefExpr::create(MF->getJTISymbol(JTI, Ctx), Ctx); } +M68kTargetLowering::ConstraintType +M68kTargetLowering::getConstraintType(StringRef Constraint) const { + if (Constraint.size() > 0) { + switch (Constraint[0]) { + case 'a': + case 'd': + return C_RegisterClass; + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + return C_Immediate; + case 'C': + if (Constraint.size() == 2) + switch (Constraint[1]) { + case '0': + case 'i': + case 'j': + return C_Immediate; + default: + break; + } + break; + default: + break; + } + } + + return TargetLowering::getConstraintType(Constraint); +} + +void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op, + std::string &Constraint, + std::vector &Ops, + SelectionDAG &DAG) const { + SDValue Result; + + if (Constraint.size() == 1) { + // Constant constraints + switch (Constraint[0]) { + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': { + auto *C = dyn_cast(Op); + if (!C) + return; + + int64_t Val = C->getSExtValue(); + switch (Constraint[0]) { + case 'I': // constant integer in the range [1,8] + if (Val > 0 && Val <= 8) + break; + return; + case 'J': // constant signed 16-bit integer + if (isInt<16>(Val)) + break; + return; + case 'K': // constant that is NOT in the range of [-0x80, 0x80) + if (Val < -0x80 || Val >= 0x80) + break; + return; + case 'L': // constant integer in the range [-8,-1] + if (Val < 0 && Val >= -8) + break; + return; + case 'M': // constant that is NOT in the range of [-0x100, 0x100] + if (Val < -0x100 || Val >= 0x100) + break; + return; + case 'N': // constant integer in the range [24,31] + if (Val >= 24 && Val <= 31) + break; + return; + case 'O': // constant integer 16 + if (Val == 16) + break; + return; + case 'P': // constant integer in the range [8,15] + if (Val >= 8 && Val <= 15) + break; + return; + default: + llvm_unreachable("Unhandled constant constraint"); + } + + Result = DAG.getTargetConstant(Val, SDLoc(Op), Op.getValueType()); + break; + } + default: + break; + } + } + + if (Constraint.size() == 2) { + switch (Constraint[0]) { + case 'C': + // Constant constraints start with 'C' + switch (Constraint[1]) { + case '0': + case 'i': + case 'j': { + auto *C = dyn_cast(Op); + if (!C) + break; + + int64_t Val = C->getSExtValue(); + switch (Constraint[1]) { + case '0': // constant integer 0 + if (!Val) + break; + return; + case 'i': // constant integer + break; + case 'j': // integer constant that doesn't fit in 16 bits + if (!isInt<16>(C->getSExtValue())) + break; + return; + default: + llvm_unreachable("Unhandled constant constraint"); + } + + Result = DAG.getTargetConstant(Val, SDLoc(Op), Op.getValueType()); + break; + } + default: + break; + } + break; + default: + break; + } + } + + if (Result.getNode()) { + Ops.push_back(Result); + return; + } + + TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); +} + +std::pair +M68kTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, + MVT VT) const { + if (Constraint.size() == 1) { + switch (Constraint[0]) { + case 'r': + case 'd': + switch (VT.SimpleTy) { + case MVT::i8: + return std::make_pair(0U, &M68k::DR8RegClass); + case MVT::i16: + return std::make_pair(0U, &M68k::DR16RegClass); + case MVT::i32: + return std::make_pair(0U, &M68k::DR32RegClass); + default: + break; + } + break; + case 'a': + switch (VT.SimpleTy) { + case MVT::i16: + return std::make_pair(0U, &M68k::AR16RegClass); + case MVT::i32: + return std::make_pair(0U, &M68k::AR32RegClass); + default: + break; + } + break; + default: + break; + } + } + + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + /// Determines whether the callee is required to pop its own arguments. /// Callee pop is necessary to support tail calls. bool M68k::isCalleePop(CallingConv::ID CallingConv, bool IsVarArg, diff --git a/llvm/test/CodeGen/M68k/inline-asm.ll b/llvm/test/CodeGen/M68k/inline-asm.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/M68k/inline-asm.ll @@ -0,0 +1,122 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=m68k < %s -o - | FileCheck %s + +; 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. +define void @constant_constraints() { +; CHECK-LABEL: constant_constraints: +; CHECK: .cfi_startproc +; CHECK-NEXT: ; %bb.0: ; %entry +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #1, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #8, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #-32768, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #32767, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #-129, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #128, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #-8, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #-1, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #-257, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #256, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #24, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #31, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #16, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #8, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #15, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #0, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #1, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #-32769, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #32768, %d1 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: rts +entry: + call void asm sideeffect "move.l $0, %d1", "I"(i32 1) + call void asm sideeffect "move.l $0, %d1", "I"(i32 8) + call void asm sideeffect "move.l $0, %d1", "J"(i32 -32768) + call void asm sideeffect "move.l $0, %d1", "J"(i32 32767) + call void asm sideeffect "move.l $0, %d1", "K"(i32 -129) + call void asm sideeffect "move.l $0, %d1", "K"(i32 128) + call void asm sideeffect "move.l $0, %d1", "L"(i32 -8) + call void asm sideeffect "move.l $0, %d1", "L"(i32 -1) + call void asm sideeffect "move.l $0, %d1", "M"(i32 -257) + call void asm sideeffect "move.l $0, %d1", "M"(i32 256) + call void asm sideeffect "move.l $0, %d1", "N"(i32 24) + call void asm sideeffect "move.l $0, %d1", "N"(i32 31) + call void asm sideeffect "move.l $0, %d1", "O"(i32 16) + call void asm sideeffect "move.l $0, %d1", "P"(i32 8) + call void asm sideeffect "move.l $0, %d1", "P"(i32 15) + call void asm sideeffect "move.l $0, %d1", "^C0"(i32 0) + call void asm sideeffect "move.l $0, %d1", "^Ci"(i32 1) + call void asm sideeffect "move.l $0, %d1", "^Cj"(i32 -32769) + call void asm sideeffect "move.l $0, %d1", "^Cj"(i32 32768) + ret void +} + +define void @register_constraints() { +; CHECK-LABEL: register_constraints: +; CHECK: .cfi_startproc +; CHECK-NEXT: ; %bb.0: ; %entry +; CHECK-NEXT: sub.l #4, %sp +; CHECK-NEXT: .cfi_def_cfa_offset -8 +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #94, %d0 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: move.l %d0, (0,%sp) +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #87, %d0 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: move.l %d0, (0,%sp) +; CHECK-NEXT: ;APP +; CHECK-NEXT: move.l #66, %a0 +; CHECK-NEXT: ;NO_APP +; CHECK-NEXT: move.l %a0, (0,%sp) +; CHECK-NEXT: add.l #4, %sp +; CHECK-NEXT: rts +entry: + %out = alloca i32, align 4 + %0 = call i32 asm sideeffect "move.l #94, $0", "=r"() + store i32 %0, i32* %out, align 4 + %1 = call i32 asm sideeffect "move.l #87, $0", "=d"() + store i32 %1, i32* %out, align 4 + %2 = call i32 asm sideeffect "move.l #66, $0", "=a"() + store i32 %2, i32* %out, align 4 + ret void +} +