Index: lib/Target/ARM/ARMInstrNEON.td =================================================================== --- lib/Target/ARM/ARMInstrNEON.td +++ lib/Target/ARM/ARMInstrNEON.td @@ -39,6 +39,49 @@ let PrintMethod = "printNEONModImmOperand"; let ParserMatchClass = nImmVMOVI32AsmOperand; } + +def nImmVMOVI16AsmOperandByteReplicate : + AsmOperandClass { + let Name = "NEONi16vmovByteReplicate"; + let PredicateMethod = "isNEONi16ByteReplicate"; + let RenderMethod = "addNEONvmovByteReplicateOperands"; +} +def nImmVMOVI32AsmOperandByteReplicate : + AsmOperandClass { + let Name = "NEONi32vmovByteReplicate"; + let PredicateMethod = "isNEONi32ByteReplicate"; + let RenderMethod = "addNEONvmovByteReplicateOperands"; +} +def nImmVMVNI16AsmOperandByteReplicate : + AsmOperandClass { + let Name = "NEONi16invByteReplicate"; + let PredicateMethod = "isNEONi16ByteReplicate"; + let RenderMethod = "addNEONinvByteReplicateOperands"; +} +def nImmVMVNI32AsmOperandByteReplicate : + AsmOperandClass { + let Name = "NEONi32invByteReplicate"; + let PredicateMethod = "isNEONi32ByteReplicate"; + let RenderMethod = "addNEONinvByteReplicateOperands"; +} + +def nImmVMOVI16ByteReplicate : Operand { + let PrintMethod = "printNEONModImmOperand"; + let ParserMatchClass = nImmVMOVI16AsmOperandByteReplicate; +} +def nImmVMOVI32ByteReplicate : Operand { + let PrintMethod = "printNEONModImmOperand"; + let ParserMatchClass = nImmVMOVI32AsmOperandByteReplicate; +} +def nImmVMVNI16ByteReplicate : Operand { + let PrintMethod = "printNEONModImmOperand"; + let ParserMatchClass = nImmVMVNI16AsmOperandByteReplicate; +} +def nImmVMVNI32ByteReplicate : Operand { + let PrintMethod = "printNEONModImmOperand"; + let ParserMatchClass = nImmVMVNI32AsmOperandByteReplicate; +} + def nImmVMOVI32NegAsmOperand : AsmOperandClass { let Name = "NEONi32vmovNeg"; } def nImmVMOVI32Neg : Operand { let PrintMethod = "printNEONModImmOperand"; @@ -5301,6 +5344,35 @@ [(set QPR:$Vd, (v4f32 (NEONvmovFPImm timm:$SIMM)))]>; } // isReMaterializable +// Add support for bytes replication feature, so it could be GAS compatible. +// E.g. instructions below: +// "vmov.i32 d0, 0xffffffff" +// "vmov.i32 d0, 0xabababab" +// "vmov.i16 d0, 0xabab" +// are incorrect, but we could deal with such cases. +// For last two instructions, for example, it should emit: +// "vmov.i8 d0, 0xab" +def : NEONInstAlias<"vmov${p}.i16 $Vd, $Vm", + (VMOVv8i8 DPR:$Vd, nImmVMOVI16ByteReplicate:$Vm, pred:$p)>; +def : NEONInstAlias<"vmov${p}.i32 $Vd, $Vm", + (VMOVv8i8 DPR:$Vd, nImmVMOVI32ByteReplicate:$Vm, pred:$p)>; +def : NEONInstAlias<"vmov${p}.i16 $Vd, $Vm", + (VMOVv16i8 QPR:$Vd, nImmVMOVI16ByteReplicate:$Vm, pred:$p)>; +def : NEONInstAlias<"vmov${p}.i32 $Vd, $Vm", + (VMOVv16i8 QPR:$Vd, nImmVMOVI32ByteReplicate:$Vm, pred:$p)>; + +// Also add same support for VMVN instructions. So instruction: +// "vmvn.i32 d0, 0xabababab" +// actually means: +// "vmov.i8 d0, 0x54" +def : NEONInstAlias<"vmvn${p}.i16 $Vd, $Vm", + (VMOVv8i8 DPR:$Vd, nImmVMVNI16ByteReplicate:$Vm, pred:$p)>; +def : NEONInstAlias<"vmvn${p}.i32 $Vd, $Vm", + (VMOVv8i8 DPR:$Vd, nImmVMVNI32ByteReplicate:$Vm, pred:$p)>; +def : NEONInstAlias<"vmvn${p}.i16 $Vd, $Vm", + (VMOVv16i8 QPR:$Vd, nImmVMVNI16ByteReplicate:$Vm, pred:$p)>; +def : NEONInstAlias<"vmvn${p}.i32 $Vd, $Vm", + (VMOVv16i8 QPR:$Vd, nImmVMVNI32ByteReplicate:$Vm, pred:$p)>; // On some CPUs the two instructions "vmov.i32 dD, #0" and "vmov.i32 qD, #0" // require zero cycles to execute so they should be used wherever possible for Index: lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -1760,6 +1760,8 @@ } bool isNEONi16splat() const { + if (isNEONByteReplicate(2)) + return false; // Leave that for bytes replication and forbid by default. if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); @@ -1772,6 +1774,8 @@ } bool isNEONi32splat() const { + if (isNEONByteReplicate(4)) + return false; // Leave that for bytes replication and forbid by default. if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); @@ -1786,7 +1790,30 @@ (Value >= 0x01000000 && Value <= 0xff000000); } + bool isNEONByteReplicate(unsigned NumBytes) const { + if (!isImm()) + return false; + const MCConstantExpr *CE = dyn_cast(getImm()); + // Must be a constant. + if (!CE) + return false; + int64_t Value = CE->getValue(); + if (!Value) + return false; // Don't bother with zero. + + unsigned char B = Value & 0xff; + for (unsigned i = 1; i < NumBytes; ++i) { + Value >>= 8; + if ((Value & 0xff) != B) + return false; + } + return true; + } + bool isNEONi16ByteReplicate() const { return isNEONByteReplicate(2); } + bool isNEONi32ByteReplicate() const { return isNEONByteReplicate(4); } bool isNEONi32vmov() const { + if (isNEONByteReplicate(4)) + return false; // Let it to be classified as byte-replicate case. if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); @@ -2560,6 +2587,19 @@ Inst.addOperand(MCOperand::CreateImm(Value)); } + void addNEONinvByteReplicateOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + // The immediate encodes the type of constant as well as the value. + const MCConstantExpr *CE = dyn_cast(getImm()); + unsigned Value = CE->getValue(); + assert((Inst.getOpcode() == ARM::VMOVv8i8 || + Inst.getOpcode() == ARM::VMOVv16i8) && + "All vmvn instructions that wants to replicate non-zero byte " + "always must be replaced with VMOVv8i8 or VMOVv16i8."); + unsigned B = ((~Value) & 0xff); + B |= 0xe00; // cmode = 0b1110 + Inst.addOperand(MCOperand::CreateImm(B)); + } void addNEONi32vmovOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // The immediate encodes the type of constant as well as the value. @@ -2574,6 +2614,19 @@ Inst.addOperand(MCOperand::CreateImm(Value)); } + void addNEONvmovByteReplicateOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + // The immediate encodes the type of constant as well as the value. + const MCConstantExpr *CE = dyn_cast(getImm()); + unsigned Value = CE->getValue(); + assert((Inst.getOpcode() == ARM::VMOVv8i8 || + Inst.getOpcode() == ARM::VMOVv16i8) && + "All instructions that wants to replicate non-zero byte " + "always must be replaced with VMOVv8i8 or VMOVv16i8."); + unsigned B = Value & 0xff; + B |= 0xe00; // cmode = 0b1110 + Inst.addOperand(MCOperand::CreateImm(B)); + } void addNEONi32vmovNegOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // The immediate encodes the type of constant as well as the value. Index: test/MC/ARM/vmov-vmvn-byte-replicate.s =================================================================== --- /dev/null +++ test/MC/ARM/vmov-vmvn-byte-replicate.s @@ -0,0 +1,31 @@ +@ PR18921, "vmov" part. +@ RUN: llvm-mc -triple=armv7-linux-gnueabi -show-encoding < %s | FileCheck %s +.text + +@ CHECK: vmov.i8 d2, #0xff @ encoding: [0x1f,0x2e,0x87,0xf3] +@ CHECK: vmov.i8 q2, #0xff @ encoding: [0x5f,0x4e,0x87,0xf3] +@ CHECK: vmov.i8 d2, #0xab @ encoding: [0x1b,0x2e,0x82,0xf3] +@ CHECK: vmov.i8 q2, #0xab @ encoding: [0x5b,0x4e,0x82,0xf3] +@ CHECK: vmov.i8 q2, #0xab @ encoding: [0x5b,0x4e,0x82,0xf3] +@ CHECK: vmov.i8 q2, #0xab @ encoding: [0x5b,0x4e,0x82,0xf3] + +@ CHECK: vmov.i8 d2, #0x0 @ encoding: [0x10,0x2e,0x80,0xf2] +@ CHECK: vmov.i8 q2, #0x0 @ encoding: [0x50,0x4e,0x80,0xf2] +@ CHECK: vmov.i8 d2, #0x54 @ encoding: [0x14,0x2e,0x85,0xf2] +@ CHECK: vmov.i8 q2, #0x54 @ encoding: [0x54,0x4e,0x85,0xf2] +@ CHECK: vmov.i8 d2, #0x54 @ encoding: [0x14,0x2e,0x85,0xf2] +@ CHECK: vmov.i8 q2, #0x54 @ encoding: [0x54,0x4e,0x85,0xf2] + + vmov.i32 d2, #0xffffffff + vmov.i32 q2, #0xffffffff + vmov.i32 d2, #0xabababab + vmov.i32 q2, #0xabababab + vmov.i16 q2, #0xabab + vmov.i16 q2, #0xabab + + vmvn.i32 d2, #0xffffffff + vmvn.i32 q2, #0xffffffff + vmvn.i32 d2, #0xabababab + vmvn.i32 q2, #0xabababab + vmvn.i16 d2, #0xabab + vmvn.i16 q2, #0xabab Index: test/MC/ARM/vmov-vmvn-illegal-cases.s =================================================================== --- /dev/null +++ test/MC/ARM/vmov-vmvn-illegal-cases.s @@ -0,0 +1,30 @@ +@ RUN: not llvm-mc -triple=armv7-linux-gnueabi %s 2>&1 | FileCheck %s +.text + +@ CHECK: error: invalid operand for instruction +@ CHECK: vmov.i32 d2, #0xffffffab +@ CHECK: error: invalid operand for instruction +@ CHECK: vmov.i32 q2, #0xffffffab +@ CHECK: error: invalid operand for instruction +@ CHECK: vmov.i16 q2, #0xffab +@ CHECK: error: invalid operand for instruction +@ CHECK: vmov.i16 q2, #0xffab + +@ CHECK: error: invalid operand for instruction +@ CHECK: vmvn.i32 d2, #0xffffffab +@ CHECK: error: invalid operand for instruction +@ CHECK: vmvn.i32 q2, #0xffffffab +@ CHECK: error: invalid operand for instruction +@ CHECK: vmvn.i16 q2, #0xffab +@ CHECK: error: invalid operand for instruction +@ CHECK: vmvn.i16 q2, #0xffab + + vmov.i32 d2, #0xffffffab + vmov.i32 q2, #0xffffffab + vmov.i16 q2, #0xffab + vmov.i16 q2, #0xffab + + vmvn.i32 d2, #0xffffffab + vmvn.i32 q2, #0xffffffab + vmvn.i16 q2, #0xffab + vmvn.i16 q2, #0xffab Index: test/MC/ARM/vorr-vbic-illegal-cases.s =================================================================== --- /dev/null +++ test/MC/ARM/vorr-vbic-illegal-cases.s @@ -0,0 +1,42 @@ +@ RUN: not llvm-mc -triple=armv7-linux-gnueabi %s 2>&1 | FileCheck %s +.text + +@ CHECK: error: invalid operand for instruction +@ CHECK: vorr.i32 d2, #0xffffffff +@ CHECK: error: invalid operand for instruction +@ CHECK: vorr.i32 q2, #0xffffffff +@ CHECK: error: invalid operand for instruction +@ CHECK: vorr.i32 d2, #0xabababab +@ CHECK: error: invalid operand for instruction +@ CHECK: vorr.i32 q2, #0xabababab +@ CHECK: error: invalid operand for instruction +@ CHECK: vorr.i16 q2, #0xabab +@ CHECK: error: invalid operand for instruction +@ CHECK: vorr.i16 q2, #0xabab + +@ CHECK: error: invalid operand for instruction +@ CHECK: vbic.i32 d2, #0xffffffff +@ CHECK: error: invalid operand for instruction +@ CHECK: vbic.i32 q2, #0xffffffff +@ CHECK: error: invalid operand for instruction +@ CHECK: vbic.i32 d2, #0xabababab +@ CHECK: error: invalid operand for instruction +@ CHECK: vbic.i32 q2, #0xabababab +@ CHECK: error: invalid operand for instruction +@ CHECK: vbic.i16 d2, #0xabab +@ CHECK: error: invalid operand for instruction +@ CHECK: vbic.i16 q2, #0xabab + + vorr.i32 d2, #0xffffffff + vorr.i32 q2, #0xffffffff + vorr.i32 d2, #0xabababab + vorr.i32 q2, #0xabababab + vorr.i16 q2, #0xabab + vorr.i16 q2, #0xabab + + vbic.i32 d2, #0xffffffff + vbic.i32 q2, #0xffffffff + vbic.i32 d2, #0xabababab + vbic.i32 q2, #0xabababab + vbic.i16 d2, #0xabab + vbic.i16 q2, #0xabab