Skip to content

Commit

Permalink
Reland "[Clang][LoongArch] Add inline asm support for constraints k/m…
Browse files Browse the repository at this point in the history
…/ZB/ZC"

Reference: https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html

k: A memory operand whose address is formed by a base register and
(optionally scaled) index register.

m: A memory operand whose address is formed by a base register and
offset that is suitable for use in instructions with the same
addressing mode as st.w and ld.w.

ZB: An address that is held in a general-purpose register. The offset
is zero.

ZC: A memory operand whose address is formed by a base register and
offset that is suitable for use in instructions with the same
addressing mode as ll.w and sc.w.

Note:
The INLINEASM SDNode flags in below tests are updated because the new
introduced enum `Constraint_k` is added before `Constraint_m`.
  llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll
  llvm/test/CodeGen/AMDGPU/GlobalISel/irtranslator-inline-asm.ll
  llvm/test/CodeGen/X86/callbr-asm-kill.mir

This patch passes `ninja check-all` on a X86 machine with all official
targets and the LoongArch target enabled.

Differential Revision: https://reviews.llvm.org/D134638
  • Loading branch information
SixWeining committed Oct 11, 2022
1 parent 4e62d02 commit 42b7079
Show file tree
Hide file tree
Showing 17 changed files with 570 additions and 6 deletions.
36 changes: 35 additions & 1 deletion clang/lib/Basic/Targets/LoongArch.cpp
Expand Up @@ -67,14 +67,19 @@ bool LoongArchTargetInfo::validateAsmConstraint(
const char *&Name, TargetInfo::ConstraintInfo &Info) const {
// See the GCC definitions here:
// https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html
// Note that the 'm' constraint is handled in TargetInfo.
switch (*Name) {
// TODO: handle 'k', 'm', "ZB", "ZC".
default:
return false;
case 'f':
// A floating-point register (if available).
Info.setAllowsRegister();
return true;
case 'k':
// A memory operand whose address is formed by a base register and
// (optionally scaled) index register.
Info.setAllowsMemory();
return true;
case 'l':
// A signed 16-bit constant.
Info.setRequiresImmediate(-32768, 32767);
Expand All @@ -87,7 +92,36 @@ bool LoongArchTargetInfo::validateAsmConstraint(
// An unsigned 12-bit constant (for logic instructions).
Info.setRequiresImmediate(0, 4095);
return true;
case 'Z':
// ZB: An address that is held in a general-purpose register. The offset is
// zero.
// ZC: A memory operand whose address is formed by a base register
// and offset that is suitable for use in instructions with the same
// addressing mode as ll.w and sc.w.
if (Name[1] == 'C' || Name[1] == 'B') {
Info.setAllowsMemory();
++Name; // Skip over 'Z'.
return true;
}
return false;
}
}

std::string
LoongArchTargetInfo::convertConstraint(const char *&Constraint) const {
std::string R;
switch (*Constraint) {
case 'Z':
// "ZC"/"ZB" are two-character constraints; add "^" hint for later
// parsing.
R = "^" + std::string(Constraint, 2);
++Constraint;
break;
default:
R = TargetInfo::convertConstraint(Constraint);
break;
}
return R;
}

void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/LoongArch.h
Expand Up @@ -55,6 +55,7 @@ class LLVM_LIBRARY_VISIBILITY LoongArchTargetInfo : public TargetInfo {

bool validateAsmConstraint(const char *&Name,
TargetInfo::ConstraintInfo &Info) const override;
std::string convertConstraint(const char *&Constraint) const override;

bool hasBitIntType() const override { return true; }
};
Expand Down
24 changes: 24 additions & 0 deletions clang/test/CodeGen/LoongArch/inline-asm-constraints.c
Expand Up @@ -15,6 +15,12 @@ void test_f(void) {
asm volatile ("" :: "f"(d));
}

void test_k(int *p, int idx) {
// CHECK-LABEL: define{{.*}} void @test_k(ptr noundef %p, i32 noundef{{.*}} %idx)
// CHECK: call void asm sideeffect "", "*k"(ptr elementtype(i32) %{{.*}})
asm volatile("" :: "k"(*(p+idx)));
}

void test_l(void) {
// CHECK-LABEL: define{{.*}} void @test_l()
// CHECK: call void asm sideeffect "", "l"(i32 32767)
Expand All @@ -23,6 +29,12 @@ void test_l(void) {
asm volatile ("" :: "l"(-32768));
}

void test_m(int *p) {
// CHECK-LABEL: define{{.*}} void @test_m(ptr noundef %p)
// CHECK: call void asm sideeffect "", "*m"(ptr nonnull elementtype(i32) %{{.*}})
asm volatile("" :: "m"(*(p+4)));
}

void test_I(void) {
// CHECK-LABEL: define{{.*}} void @test_I()
// CHECK: call void asm sideeffect "", "I"(i32 2047)
Expand All @@ -38,3 +50,15 @@ void test_K(void) {
// CHECK: call void asm sideeffect "", "K"(i32 0)
asm volatile ("" :: "K"(0));
}

void test_ZB(int *p) {
// CHECK-LABEL: define{{.*}} void @test_ZB(ptr noundef %p)
// CHECK: call void asm sideeffect "", "*^ZB"(ptr elementtype(i32) %p)
asm volatile ("" :: "ZB"(*p));
}

void test_ZC(int *p) {
// CHECK-LABEL: define{{.*}} void @test_ZC(ptr noundef %p)
// CHECK: call void asm sideeffect "", "*^ZC"(ptr elementtype(i32) %p)
asm volatile ("" :: "ZC"(*p));
}
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/InlineAsm.h
Expand Up @@ -252,6 +252,7 @@ class InlineAsm final : public Value {
Constraint_Unknown = 0,
Constraint_es,
Constraint_i,
Constraint_k,
Constraint_m,
Constraint_o,
Constraint_v,
Expand All @@ -269,6 +270,7 @@ class InlineAsm final : public Value {
Constraint_Uy,
Constraint_X,
Constraint_Z,
Constraint_ZB,
Constraint_ZC,
Constraint_Zy,

Expand Down Expand Up @@ -428,6 +430,8 @@ class InlineAsm final : public Value {
return "es";
case InlineAsm::Constraint_i:
return "i";
case InlineAsm::Constraint_k:
return "k";
case InlineAsm::Constraint_m:
return "m";
case InlineAsm::Constraint_o:
Expand Down Expand Up @@ -460,6 +464,8 @@ class InlineAsm final : public Value {
return "X";
case InlineAsm::Constraint_Z:
return "Z";
case InlineAsm::Constraint_ZB:
return "ZB";
case InlineAsm::Constraint_ZC:
return "ZC";
case InlineAsm::Constraint_Zy:
Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
Expand Up @@ -68,6 +68,35 @@ bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
return true;
}

bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
unsigned OpNo,
const char *ExtraCode,
raw_ostream &OS) {
// TODO: handle extra code.
if (ExtraCode)
return true;

const MachineOperand &BaseMO = MI->getOperand(OpNo);
// Base address must be a register.
if (!BaseMO.isReg())
return true;
// Print the base address register.
OS << "$" << LoongArchInstPrinter::getRegisterName(BaseMO.getReg());
// Print the offset register or immediate if has.
if (OpNo + 1 < MI->getNumOperands()) {
const MachineOperand &OffsetMO = MI->getOperand(OpNo + 1);
if (OffsetMO.isReg())
OS << ", $" << LoongArchInstPrinter::getRegisterName(OffsetMO.getReg());
else if (OffsetMO.isImm())
OS << ", " << OffsetMO.getImm();
else
return true;
}
return false;

return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS);
}

bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
AsmPrinter::runOnMachineFunction(MF);
return true;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
Expand Up @@ -38,6 +38,8 @@ class LLVM_LIBRARY_VISIBILITY LoongArchAsmPrinter : 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;

// tblgen'erated function.
bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
Expand Down
53 changes: 53 additions & 0 deletions llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp
Expand Up @@ -77,6 +77,59 @@ void LoongArchDAGToDAGISel::Select(SDNode *Node) {
SelectCode(Node);
}

bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand(
const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
switch (ConstraintID) {
default:
llvm_unreachable("unexpected asm memory constraint");
// Reg+Reg addressing.
case InlineAsm::Constraint_k:
OutOps.push_back(Op.getOperand(0));
OutOps.push_back(Op.getOperand(1));
return false;
// Reg+simm12 addressing.
case InlineAsm::Constraint_m: {
SDValue Base = Op;
SDValue Offset =
CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
if (CurDAG->isBaseWithConstantOffset(Op)) {
ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
if (isIntN(12, CN->getSExtValue())) {
Base = Op.getOperand(0);
Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op),
Op.getValueType());
}
}
OutOps.push_back(Base);
OutOps.push_back(Offset);
return false;
}
case InlineAsm::Constraint_ZB:
OutOps.push_back(Op);
// No offset.
return false;
// Reg+(simm14<<2) addressing.
case InlineAsm::Constraint_ZC: {
SDValue Base = Op;
SDValue Offset =
CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
if (CurDAG->isBaseWithConstantOffset(Op)) {
ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
if (isIntN(16, CN->getSExtValue()) &&
isAligned(Align(4ULL), CN->getZExtValue())) {
Base = Op.getOperand(0);
Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op),
Op.getValueType());
}
}
OutOps.push_back(Base);
OutOps.push_back(Offset);
return false;
}
}
return true;
}

bool LoongArchDAGToDAGISel::SelectBaseAddr(SDValue Addr, SDValue &Base) {
// If this is FrameIndex, select it directly. Otherwise just let it get
// selected to a register independently.
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.h
Expand Up @@ -38,6 +38,9 @@ class LoongArchDAGToDAGISel : public SelectionDAGISel {

void Select(SDNode *Node) override;

bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
std::vector<SDValue> &OutOps) override;

bool SelectBaseAddr(SDValue Addr, SDValue &Base);
bool selectNonFIBaseAddr(SDValue Addr, SDValue &Base);

Expand Down
15 changes: 14 additions & 1 deletion llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
Expand Up @@ -2114,14 +2114,27 @@ LoongArchTargetLowering::getConstraintType(StringRef Constraint) const {
case 'I':
case 'K':
return C_Immediate;
case 'k':
return C_Memory;
}
}

// TODO: handle 'k", "ZB" and "ZC".
if (Constraint == "ZC" || Constraint == "ZB")
return C_Memory;

// 'm' is handled here.
return TargetLowering::getConstraintType(Constraint);
}

unsigned LoongArchTargetLowering::getInlineAsmMemConstraint(
StringRef ConstraintCode) const {
return StringSwitch<unsigned>(ConstraintCode)
.Case("k", InlineAsm::Constraint_k)
.Case("ZB", InlineAsm::Constraint_ZB)
.Case("ZC", InlineAsm::Constraint_ZC)
.Default(TargetLowering::getInlineAsmMemConstraint(ConstraintCode));
}

std::pair<unsigned, const TargetRegisterClass *>
LoongArchTargetLowering::getRegForInlineAsmConstraint(
const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/LoongArch/LoongArchISelLowering.h
Expand Up @@ -170,6 +170,8 @@ class LoongArchTargetLowering : public TargetLowering {

ConstraintType getConstraintType(StringRef Constraint) const override;

unsigned getInlineAsmMemConstraint(StringRef ConstraintCode) const override;

std::pair<unsigned, const TargetRegisterClass *>
getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
StringRef Constraint, MVT VT) const override;
Expand Down
Expand Up @@ -204,7 +204,7 @@ define i32 @test_memory_constraint(i32* %a) nounwind {
; CHECK: bb.1 (%ir-block.0):
; CHECK: liveins: $x0
; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0
; CHECK: INLINEASM &"ldr $0, $1", 8 /* mayload attdialect */, 655370 /* regdef:GPR32common */, def %1, 196622 /* mem:m */, [[COPY]](p0)
; CHECK: INLINEASM &"ldr $0, $1", 8 /* mayload attdialect */, 655370 /* regdef:GPR32common */, def %1, 262158 /* mem:m */, [[COPY]](p0)
; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY %1
; CHECK: $w0 = COPY [[COPY1]](s32)
; CHECK: RET_ReallyLR implicit $w0
Expand Down
Expand Up @@ -203,7 +203,7 @@ define i32 @test_memory_constraint(i32 addrspace(3)* %a) nounwind {
; CHECK-NEXT: liveins: $vgpr0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p3) = COPY $vgpr0
; CHECK-NEXT: INLINEASM &"ds_read_b32 $0, $1", 8 /* mayload attdialect */, 1966090 /* regdef:VGPR_32 */, def %1, 196622 /* mem:m */, [[COPY]](p3)
; CHECK-NEXT: INLINEASM &"ds_read_b32 $0, $1", 8 /* mayload attdialect */, 1966090 /* regdef:VGPR_32 */, def %1, 262158 /* mem:m */, [[COPY]](p3)
; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY %1
; CHECK-NEXT: $vgpr0 = COPY [[COPY1]](s32)
; CHECK-NEXT: SI_RETURN implicit $vgpr0
Expand Down
49 changes: 49 additions & 0 deletions llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll
@@ -0,0 +1,49 @@
; RUN: llc --mtriple=loongarch64 --verify-machineinstrs < %s \
; RUN: | FileCheck %s --check-prefix=ASM
; RUN: llc --mtriple=loongarch64 --print-after-isel -o /dev/null 2>&1 < %s \
; RUN: | FileCheck %s --check-prefix=MACHINE-INSTR

;; Note amswap.w is not available on loongarch32.

define void @ZB(ptr %p) nounwind {
; ASM-LABEL: ZB:
; ASM: # %bb.0:
; ASM-NEXT: #APP
; ASM-NEXT: amswap.w $t0, $t1, $a0
; ASM-NEXT: #NO_APP
; ASM-NEXT: ret
;; Make sure machine instr with this "ZB" constraint is printed correctly.
; MACHINE-INSTR: INLINEASM{{.*}}[mem:ZB]
call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %p)
ret void
}

define void @ZB_constant_offset(ptr %p) nounwind {
; ASM-LABEL: ZB_constant_offset:
; ASM: # %bb.0:
; ASM-NEXT: addi.d $a0, $a0, 1
; ASM-NEXT: #APP
; ASM-NEXT: amswap.w $t0, $t1, $a0
; ASM-NEXT: #NO_APP
; ASM-NEXT: ret
%1 = getelementptr inbounds i8, ptr %p, i32 1
;; Make sure machine instr with this "ZB" constraint is printed correctly.
; MACHINE-INSTR: INLINEASM{{.*}}[mem:ZB]
call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %1)
ret void
}

define void @ZB_variable_offset(ptr %p, i32 signext %idx) nounwind {
; ASM-LABEL: ZB_variable_offset:
; ASM: # %bb.0:
; ASM-NEXT: add.d $a0, $a0, $a1
; ASM-NEXT: #APP
; ASM-NEXT: amswap.w $t0, $t1, $a0
; ASM-NEXT: #NO_APP
; ASM-NEXT: ret
%1 = getelementptr inbounds i8, ptr %p, i32 %idx
;; Make sure machine instr with this "ZB" constraint is printed correctly.
; MACHINE-INSTR: INLINEASM{{.*}}[mem:ZB]
call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %1)
ret void
}

0 comments on commit 42b7079

Please sign in to comment.