Index: lib/Target/ARM/ARMInstrNEON.td =================================================================== --- lib/Target/ARM/ARMInstrNEON.td +++ lib/Target/ARM/ARMInstrNEON.td @@ -39,6 +39,18 @@ let PrintMethod = "printNEONModImmOperand"; let ParserMatchClass = nImmVMOVI32AsmOperand; } +def nImmVMOVI32AsmOperandOnes : + AsmOperandClass { let Name = "NEONi32vmovOnes"; } +def nImmVMOVI32AllOnes : Operand { + let PrintMethod = "printNEONModImmOperand"; + let ParserMatchClass = nImmVMOVI32AsmOperandOnes; +} +def nImmVMVNI32AsmOperandOnes : + AsmOperandClass { let Name = "NEONi32splatOnes"; } +def nImmVMVNI32AllOnes : Operand { + let PrintMethod = "printNEONModImmOperand"; + let ParserMatchClass = nImmVMVNI32AsmOperandOnes; +} def nImmVMOVI32NegAsmOperand : AsmOperandClass { let Name = "NEONi32vmovNeg"; } def nImmVMOVI32Neg : Operand { let PrintMethod = "printNEONModImmOperand"; @@ -5302,6 +5314,24 @@ } // isReMaterializable +// Catch cases when assembler generator (usually gcc) emitted +// incorrect instruction but with clear intention to setup all ones, e.g.: +// "vmov.i32 d0, 0xffffffff" - is incorrect. +// But we have deal with such cases, just in case, if somebody decided +// to use gcc generated .s files. +def : NEONInstAlias<"vmov${p}.i32 $Vd, $Vm", + (VMOVv8i8 DPR:$Vd, nImmVMOVI32AllOnes:$Vm, pred:$p)>; +def : NEONInstAlias<"vmov${p}.i32 $Vd, $Vm", + (VMOVv16i8 QPR:$Vd, nImmVMOVI32AllOnes:$Vm, pred:$p)>; + +// Also catch cases when somebody wants to zeroize register with mvn: +// "vmvn.i32 d0, #0xffffffff" - this is also incorrect. But we must deal +// with it. +def : NEONInstAlias<"vmvn${p}.i32 $Vd, $Vm", + (VMOVv2i32 DPR:$Vd, nImmVMVNI32AllOnes:$Vm, pred:$p)>; +def : NEONInstAlias<"vmvn${p}.i32 $Vd, $Vm", + (VMOVv4i32 QPR:$Vd, nImmVMVNI32AllOnes:$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 // setting a register to zero. Index: lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -1632,6 +1632,14 @@ (Value >= 0x01000000 && Value <= 0xff000000); } + bool isNEONi32splatOnes() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast(getImm()); + // Must be a constant. + if (!CE) return false; + int64_t Value = CE->getValue(); + return Value == 0xffffffff; + } bool isNEONi32vmov() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); @@ -1647,6 +1655,14 @@ (Value >= 0x01ff && Value <= 0xffff && (Value & 0xff) == 0xff) || (Value >= 0x01ffff && Value <= 0xffffff && (Value & 0xffff) == 0xffff); } + bool isNEONi32vmovOnes() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast(getImm()); + // Must be a constant. + if (!CE) return false; + int64_t Value = CE->getValue(); + return Value == 0xffffffff; + } bool isNEONi32vmovNeg() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); @@ -2383,6 +2399,22 @@ Value = (Value >> 24) | 0x600; Inst.addOperand(MCOperand::CreateImm(Value)); } + void addNEONi32splatOnesOperands(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(Value == 0xffffffff && + "32 bit all-ones is the only possible value here."); + + assert((Inst.getOpcode() == ARM::VMOVv2i32 || + Inst.getOpcode() == ARM::VMOVv4i32) && + "All vmvn instructions that wants to set all-ones " + "always must be replaced with VMOVv2i32 or VMOVv4i32."); + + Inst.addOperand(MCOperand::CreateImm(0)); + } void addNEONi32vmovOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); @@ -2397,7 +2429,22 @@ Value = (Value >> 24) | 0x600; Inst.addOperand(MCOperand::CreateImm(Value)); } + void addNEONi32vmovOnesOperands(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(Value == 0xffffffff && + "32 bit all-ones is the only possible value here."); + + assert((Inst.getOpcode() == ARM::VMOVv8i8 || + Inst.getOpcode() == ARM::VMOVv16i8) && + "All vmov instructions that wants to set all-ones " + "always must be replaced with VMOVv8i8 or VMOVv16i8."); + + Inst.addOperand(MCOperand::CreateImm(0xeff)); + } 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-i32-allones.s =================================================================== --- /dev/null +++ test/MC/ARM/vmov-i32-allones.s @@ -0,0 +1,12 @@ +@ 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.i32 d2, #0x0 @ encoding: [0x10,0x20,0x80,0xf2] +@ CHECK: vmov.i32 q2, #0x0 @ encoding: [0x50,0x40,0x80,0xf2] + vmov.i32 d2, #0xffffffff + vmov.i32 q2, #0xffffffff + vmvn.i32 d2, #0xffffffff + vmvn.i32 q2, #0xffffffff