[mips][msa] Enable inlinse assembly for MSA.

Like GCC, this re-uses the 'f' constraint and a new 'w' print-modifier:
  asm ("ldi.w %w0, 1", "=f"(result));

Unlike GCC, the 'w' print-modifer is not _required_ to produce the intended
output. This is a consequence of differences in the internal handling of
the registers in each compiler. To be source-compatible between the
compilers, users must use the 'w' print-modifier.

MSA registers (including control registers) are supported in clobber lists.

llvm-svn: 194476
This commit is contained in:
Daniel Sanders 2013-11-12 12:56:01 +00:00
parent 7c30260ab3
commit 8b59af15ed
6 changed files with 136 additions and 11 deletions

View File

@ -4916,7 +4916,7 @@ public:
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const {
static const char * const GCCRegNames[] = {
static const char *const GCCRegNames[] = {
// CPU register names
// Must match second column of GCCRegAliases
"$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
@ -4930,7 +4930,15 @@ public:
"$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
// Hi/lo and condition register names
"hi", "lo", "", "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
"$fcc5","$fcc6","$fcc7"
"$fcc5","$fcc6","$fcc7",
// MSA register names
"$w0", "$w1", "$w2", "$w3", "$w4", "$w5", "$w6", "$w7",
"$w8", "$w9", "$w10", "$w11", "$w12", "$w13", "$w14", "$w15",
"$w16", "$w17", "$w18", "$w19", "$w20", "$w21", "$w22", "$w23",
"$w24", "$w25", "$w26", "$w27", "$w28", "$w29", "$w30", "$w31",
// MSA control register names
"$msair", "$msacsr", "$msaaccess", "$msasave", "$msamodify",
"$msarequest", "$msamap", "$msaunmap"
};
Names = GCCRegNames;
NumNames = llvm::array_lengthof(GCCRegNames);

View File

@ -7,6 +7,8 @@
Includes:
- GPR
- FPU
- MSA
Any bad names will make the frontend choke.
*/
@ -113,4 +115,36 @@ main()
__asm__ __volatile__ ("fadd.s $f30,77":::"$f30");
__asm__ __volatile__ ("fadd.s $f31,77":::"$f31");
__asm__ __volatile__ ("ldi.w $w0,77":::"$w0");
__asm__ __volatile__ ("ldi.w $w1,77":::"$w1");
__asm__ __volatile__ ("ldi.w $w2,77":::"$w2");
__asm__ __volatile__ ("ldi.w $w3,77":::"$w3");
__asm__ __volatile__ ("ldi.w $w4,77":::"$w4");
__asm__ __volatile__ ("ldi.w $w5,77":::"$w5");
__asm__ __volatile__ ("ldi.w $w6,77":::"$w6");
__asm__ __volatile__ ("ldi.w $w7,77":::"$w7");
__asm__ __volatile__ ("ldi.w $w8,77":::"$w8");
__asm__ __volatile__ ("ldi.w $w9,77":::"$w9");
__asm__ __volatile__ ("ldi.w $w10,77":::"$w10");
__asm__ __volatile__ ("ldi.w $w11,77":::"$w10");
__asm__ __volatile__ ("ldi.w $w12,77":::"$w12");
__asm__ __volatile__ ("ldi.w $w13,77":::"$w13");
__asm__ __volatile__ ("ldi.w $w14,77":::"$w14");
__asm__ __volatile__ ("ldi.w $w15,77":::"$w15");
__asm__ __volatile__ ("ldi.w $w16,77":::"$w16");
__asm__ __volatile__ ("ldi.w $w17,77":::"$w17");
__asm__ __volatile__ ("ldi.w $w18,77":::"$w18");
__asm__ __volatile__ ("ldi.w $w19,77":::"$w19");
__asm__ __volatile__ ("ldi.w $w20,77":::"$w20");
__asm__ __volatile__ ("ldi.w $w21,77":::"$w21");
__asm__ __volatile__ ("ldi.w $w22,77":::"$w22");
__asm__ __volatile__ ("ldi.w $w23,77":::"$w23");
__asm__ __volatile__ ("ldi.w $w24,77":::"$w24");
__asm__ __volatile__ ("ldi.w $w25,77":::"$w25");
__asm__ __volatile__ ("ldi.w $w26,77":::"$w26");
__asm__ __volatile__ ("ldi.w $w27,77":::"$w27");
__asm__ __volatile__ ("ldi.w $w28,77":::"$w28");
__asm__ __volatile__ ("ldi.w $w29,77":::"$w29");
__asm__ __volatile__ ("ldi.w $w30,77":::"$w30");
__asm__ __volatile__ ("ldi.w $w31,77":::"$w31");
}

View File

@ -5,12 +5,16 @@
int printf(const char*, ...);
typedef int v4i32 __attribute__((vector_size(16)));
// CHECK: %{{[0-9]+}} = call i32 asm ".set noreorder;\0Alw $0,$1;\0A.set reorder;\0A", "=r,*m"(i32* getelementptr inbounds ([8 x i32]* @b, i32 {{[0-9]+}}, i32 {{[0-9]+}})) #2,
// CHECK: %{{[0-9]+}} = call i32 asm "lw $0,${1:D};\0A", "=r,*m"(i32* getelementptr inbounds ([8 x i32]* @b, i32 {{[0-9]+}}, i32 {{[0-9]+}})) #2,
// CHECK: %{{[0-9]+}} = call <4 x i32> asm "ldi.w ${0:w},1", "=f"
int b[8] = {0,1,2,3,4,5,6,7};
int main()
{
int i;
v4i32 v4i32_r;
// The first word. Notice, no 'D'
{asm (
@ -29,6 +33,9 @@ int main()
: "m" (*(b+4))
);}
// MSA registers
{asm ("ldi.w %w0,1" : "=f" (v4i32_r));}
printf("%d\n",i);
return 1;

View File

@ -461,6 +461,11 @@ bool MipsAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
return false;
}
}
case 'w':
// Print MSA registers for the 'f' constraint
// In LLVM, the 'w' modifier doesn't need to do anything.
// We can just call printOperand as normal.
break;
}
}

View File

@ -20,6 +20,7 @@
#include "MipsTargetMachine.h"
#include "MipsTargetObjectFile.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
@ -2777,7 +2778,7 @@ MipsTargetLowering::LowerReturn(SDValue Chain,
MipsTargetLowering::ConstraintType MipsTargetLowering::
getConstraintType(const std::string &Constraint) const
{
// Mips specific constrainy
// Mips specific constraints
// GCC config/mips/constraints.md
//
// 'd' : An address register. Equivalent to r
@ -2828,16 +2829,19 @@ MipsTargetLowering::getSingleConstraintMatchWeight(
if (type->isIntegerTy())
weight = CW_Register;
break;
case 'f':
if (type->isFloatTy())
case 'f': // FPU or MSA register
if (Subtarget->hasMSA() && type->isVectorTy() &&
cast<VectorType>(type)->getBitWidth() == 128)
weight = CW_Register;
else if (type->isFloatTy())
weight = CW_Register;
break;
case 'c': // $25 for indirect jumps
case 'l': // lo register
case 'x': // hilo register pair
if (type->isIntegerTy())
if (type->isIntegerTy())
weight = CW_SpecificReg;
break;
break;
case 'I': // signed 16 bit immediate
case 'J': // integer zero
case 'K': // unsigned 16 bit immediate
@ -2900,6 +2904,29 @@ parseRegForInlineAsmConstraint(const StringRef &C, MVT VT) const {
RC = TRI->getRegClass(Prefix == "hi" ?
Mips::HI32RegClassID : Mips::LO32RegClassID);
return std::make_pair(*(RC->begin()), RC);
} else if (Prefix.compare(0, 4, "$msa") == 0) {
// Parse $msa(ir|csr|access|save|modify|request|map|unmap)
// No numeric characters follow the name.
if (R.second)
return std::make_pair((unsigned)0, (const TargetRegisterClass *)0);
Reg = StringSwitch<unsigned long long>(Prefix)
.Case("$msair", Mips::MSAIR)
.Case("$msacsr", Mips::MSACSR)
.Case("$msaaccess", Mips::MSAAccess)
.Case("$msasave", Mips::MSASave)
.Case("$msamodify", Mips::MSAModify)
.Case("$msarequest", Mips::MSARequest)
.Case("$msamap", Mips::MSAMap)
.Case("$msaunmap", Mips::MSAUnmap)
.Default(0);
if (!Reg)
return std::make_pair((unsigned)0, (const TargetRegisterClass *)0);
RC = TRI->getRegClass(Mips::MSACtrlRegClassID);
return std::make_pair(Reg, RC);
}
if (!R.second)
@ -2917,8 +2944,10 @@ parseRegForInlineAsmConstraint(const StringRef &C, MVT VT) const {
assert(Reg % 2 == 0);
Reg >>= 1;
}
} else if (Prefix == "$fcc") { // Parse $fcc0-$fcc7.
} else if (Prefix == "$fcc") // Parse $fcc0-$fcc7.
RC = TRI->getRegClass(Mips::FCCRegClassID);
else if (Prefix == "$w") { // Parse $w0-$w31.
RC = getRegClassFor((VT == MVT::Other) ? MVT::v16i8 : VT);
} else { // Parse $0-$31.
assert(Prefix == "$");
RC = getRegClassFor((VT == MVT::Other) ? MVT::i32 : VT);
@ -2950,10 +2979,18 @@ getRegForInlineAsmConstraint(const std::string &Constraint, MVT VT) const
return std::make_pair(0U, &Mips::GPR64RegClass);
// This will generate an error message
return std::make_pair(0u, static_cast<const TargetRegisterClass*>(0));
case 'f':
if (VT == MVT::f32)
case 'f': // FPU or MSA register
if (VT == MVT::v16i8)
return std::make_pair(0U, &Mips::MSA128BRegClass);
else if (VT == MVT::v8i16 || VT == MVT::v8f16)
return std::make_pair(0U, &Mips::MSA128HRegClass);
else if (VT == MVT::v4i32 || VT == MVT::v4f32)
return std::make_pair(0U, &Mips::MSA128WRegClass);
else if (VT == MVT::v2i64 || VT == MVT::v2f64)
return std::make_pair(0U, &Mips::MSA128DRegClass);
else if (VT == MVT::f32)
return std::make_pair(0U, &Mips::FGR32RegClass);
if ((VT == MVT::f64) && (!Subtarget->isSingleFloat())) {
else if ((VT == MVT::f64) && (!Subtarget->isSingleFloat())) {
if (Subtarget->isFP64bit())
return std::make_pair(0U, &Mips::FGR64RegClass);
return std::make_pair(0U, &Mips::AFGR64RegClass);

View File

@ -0,0 +1,34 @@
; A basic inline assembly test
; RUN: llc -march=mips -mattr=+msa,+fp64 < %s | FileCheck %s
@v4i32_r = global <4 x i32> zeroinitializer, align 16
define void @test1() nounwind {
entry:
; CHECK-LABEL: test1:
%0 = call <4 x i32> asm "ldi.w ${0:w}, 1", "=f"()
; CHECK: ldi.w $w{{[1-3]?[0-9]}}, 1
store <4 x i32> %0, <4 x i32>* @v4i32_r
ret void
}
define void @test2() nounwind {
entry:
; CHECK-LABEL: test2:
%0 = load <4 x i32>* @v4i32_r
%1 = call <4 x i32> asm "addvi.w ${0:w}, ${1:w}, 1", "=f,f"(<4 x i32> %0)
; CHECK: addvi.w $w{{[1-3]?[0-9]}}, $w{{[1-3]?[0-9]}}, 1
store <4 x i32> %1, <4 x i32>* @v4i32_r
ret void
}
define void @test3() nounwind {
entry:
; CHECK-LABEL: test3:
%0 = load <4 x i32>* @v4i32_r
%1 = call <4 x i32> asm sideeffect "addvi.w ${0:w}, ${1:w}, 1", "=f,f,~{$w0}"(<4 x i32> %0)
; CHECK: addvi.w $w{{([1-9]|[1-3][0-9])}}, $w{{([1-9]|[1-3][0-9])}}, 1
store <4 x i32> %1, <4 x i32>* @v4i32_r
ret void
}