diff --git a/lib/Target/AArch64/AArch64InstrFormats.td b/lib/Target/AArch64/AArch64InstrFormats.td index 446149b4fb0..5007172f153 100644 --- a/lib/Target/AArch64/AArch64InstrFormats.td +++ b/lib/Target/AArch64/AArch64InstrFormats.td @@ -448,13 +448,19 @@ def logical_imm64_XFORM : SDNodeXFormgetTargetConstant(enc, MVT::i32); }]>; -def LogicalImm32Operand : AsmOperandClass { - let Name = "LogicalImm32"; - let DiagnosticType = "LogicalSecondSource"; -} -def LogicalImm64Operand : AsmOperandClass { - let Name = "LogicalImm64"; - let DiagnosticType = "LogicalSecondSource"; +let DiagnosticType = "LogicalSecondSource" in { + def LogicalImm32Operand : AsmOperandClass { + let Name = "LogicalImm32"; + } + def LogicalImm64Operand : AsmOperandClass { + let Name = "LogicalImm64"; + } + def LogicalImm32NotOperand : AsmOperandClass { + let Name = "LogicalImm32Not"; + } + def LogicalImm64NotOperand : AsmOperandClass { + let Name = "LogicalImm64Not"; + } } def logical_imm32 : Operand, PatLeaf<(imm), [{ return AArch64_AM::isLogicalImmediate(N->getZExtValue(), 32); @@ -468,6 +474,12 @@ def logical_imm64 : Operand, PatLeaf<(imm), [{ let PrintMethod = "printLogicalImm64"; let ParserMatchClass = LogicalImm64Operand; } +def logical_imm32_not : Operand { + let ParserMatchClass = LogicalImm32NotOperand; +} +def logical_imm64_not : Operand { + let ParserMatchClass = LogicalImm64NotOperand; +} // imm0_65535 predicate - True if the immediate is in the range [0,65535]. def Imm0_65535Operand : AsmImmRange<0, 65535>; @@ -1935,22 +1947,32 @@ class LogicalRegAlias : InstAlias; -let AddedComplexity = 6 in -multiclass LogicalImm opc, string mnemonic, SDNode OpNode> { +multiclass LogicalImm opc, string mnemonic, SDNode OpNode, + string Alias> { + let AddedComplexity = 6 in def Wri : BaseLogicalImm { let Inst{31} = 0; let Inst{22} = 0; // 64-bit version has an additional bit of immediate. } + let AddedComplexity = 6 in def Xri : BaseLogicalImm { let Inst{31} = 1; } + + def : InstAlias(NAME # "Wri") GPR32sp:$Rd, GPR32:$Rn, + logical_imm32_not:$imm), 0>; + def : InstAlias(NAME # "Xri") GPR64sp:$Rd, GPR64:$Rn, + logical_imm64_not:$imm), 0>; } -multiclass LogicalImmS opc, string mnemonic, SDNode OpNode> { +multiclass LogicalImmS opc, string mnemonic, SDNode OpNode, + string Alias> { let isCompare = 1, Defs = [NZCV] in { def Wri : BaseLogicalImm { @@ -1962,6 +1984,13 @@ multiclass LogicalImmS opc, string mnemonic, SDNode OpNode> { let Inst{31} = 1; } } // end Defs = [NZCV] + + def : InstAlias(NAME # "Wri") GPR32:$Rd, GPR32:$Rn, + logical_imm32_not:$imm), 0>; + def : InstAlias(NAME # "Xri") GPR64:$Rd, GPR64:$Rn, + logical_imm64_not:$imm), 0>; } class BaseLogicalRegPseudo diff --git a/lib/Target/AArch64/AArch64InstrInfo.td b/lib/Target/AArch64/AArch64InstrInfo.td index d7c6d78831f..1211fba60c2 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.td +++ b/lib/Target/AArch64/AArch64InstrInfo.td @@ -671,10 +671,10 @@ def CRC32CXrr : BaseCRC32<1, 0b11, 1, GPR64, int_aarch64_crc32cx, "crc32cx">; //===----------------------------------------------------------------------===// // (immediate) -defm ANDS : LogicalImmS<0b11, "ands", AArch64and_flag>; -defm AND : LogicalImm<0b00, "and", and>; -defm EOR : LogicalImm<0b10, "eor", xor>; -defm ORR : LogicalImm<0b01, "orr", or>; +defm ANDS : LogicalImmS<0b11, "ands", AArch64and_flag, "bics">; +defm AND : LogicalImm<0b00, "and", and, "bic">; +defm EOR : LogicalImm<0b10, "eor", xor, "eon">; +defm ORR : LogicalImm<0b01, "orr", or, "orn">; // FIXME: these aliases *are* canonical sometimes (when movz can't be // used). Actually, it seems to be working right now, but putting logical_immXX diff --git a/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 69f51daa99b..c42d11e0f3d 100644 --- a/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -633,6 +633,23 @@ public: return false; return AArch64_AM::isLogicalImmediate(MCE->getValue(), 64); } + bool isLogicalImm32Not() const { + if (!isImm()) + return false; + const MCConstantExpr *MCE = dyn_cast(getImm()); + if (!MCE) + return false; + int64_t Val = ~MCE->getValue() & 0xFFFFFFFF; + return AArch64_AM::isLogicalImmediate(Val, 32); + } + bool isLogicalImm64Not() const { + if (!isImm()) + return false; + const MCConstantExpr *MCE = dyn_cast(getImm()); + if (!MCE) + return false; + return AArch64_AM::isLogicalImmediate(~MCE->getValue(), 64); + } bool isShiftedImm() const { return Kind == k_ShiftedImm; } bool isAddSubImm() const { if (!isShiftedImm() && !isImm()) @@ -1377,6 +1394,22 @@ public: Inst.addOperand(MCOperand::CreateImm(encoding)); } + void addLogicalImm32NotOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *MCE = cast(getImm()); + int64_t Val = ~MCE->getValue() & 0xFFFFFFFF; + uint64_t encoding = AArch64_AM::encodeLogicalImmediate(Val, 32); + Inst.addOperand(MCOperand::CreateImm(encoding)); + } + + void addLogicalImm64NotOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *MCE = cast(getImm()); + uint64_t encoding = + AArch64_AM::encodeLogicalImmediate(~MCE->getValue(), 64); + Inst.addOperand(MCOperand::CreateImm(encoding)); + } + void addSIMDImmType10Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *MCE = dyn_cast(getImm()); diff --git a/test/MC/AArch64/alias-logicalimm.s b/test/MC/AArch64/alias-logicalimm.s new file mode 100644 index 00000000000..28ec40beac4 --- /dev/null +++ b/test/MC/AArch64/alias-logicalimm.s @@ -0,0 +1,41 @@ +// RUN: llvm-mc -triple=aarch64-none-linux-gnu < %s | FileCheck %s + +// CHECK: and x0, x1, #0xfffffffffffffffd +// CHECK: and x0, x1, #0xfffffffffffffffd + and x0, x1, #~2 + bic x0, x1, #2 + +// CHECK: and w0, w1, #0xfffffffd +// CHECK: and w0, w1, #0xfffffffd + and w0, w1, #~2 + bic w0, w1, #2 + +// CHECK: ands x0, x1, #0xfffffffffffffffd +// CHECK: ands x0, x1, #0xfffffffffffffffd + ands x0, x1, #~2 + bics x0, x1, #2 + +// CHECK: ands w0, w1, #0xfffffffd +// CHECK: ands w0, w1, #0xfffffffd + ands w0, w1, #~2 + bics w0, w1, #2 + +// CHECK: orr x0, x1, #0xfffffffffffffffd +// CHECK: orr x0, x1, #0xfffffffffffffffd + orr x0, x1, #~2 + orn x0, x1, #2 + +// CHECK: orr w2, w1, #0xfffffffc +// CHECK: orr w2, w1, #0xfffffffc + orr w2, w1, #~3 + orn w2, w1, #3 + +// CHECK: eor x0, x1, #0xfffffffffffffffd +// CHECK: eor x0, x1, #0xfffffffffffffffd + eor x0, x1, #~2 + eon x0, x1, #2 + +// CHECK: eor w2, w1, #0xfffffffc +// CHECK: eor w2, w1, #0xfffffffc + eor w2, w1, #~3 + eon w2, w1, #3 diff --git a/test/MC/AArch64/basic-a64-diagnostics.s b/test/MC/AArch64/basic-a64-diagnostics.s index 2726d00ab9c..5293131711b 100644 --- a/test/MC/AArch64/basic-a64-diagnostics.s +++ b/test/MC/AArch64/basic-a64-diagnostics.s @@ -2985,13 +2985,17 @@ orn wsp, w3, w5 bics x20, sp, x9, lsr #0 orn x2, x6, sp, lsl #3 -// CHECK-ERROR: error: invalid operand for instruction +// FIXME: the diagnostic we get for 'orn wsp, w3, w5' is from the orn alias, +// which is a better match than the genuine ORNWri, whereas it would be better +// to get the ORNWri diagnostic when the alias did not match, i.e. the +// alias' diagnostics should have a lower priority. +// CHECK-ERROR: error: expected compatible register or logical immediate // CHECK-ERROR-NEXT: orn wsp, w3, w5 -// CHECK-ERROR-NEXT: ^ +// CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: invalid operand for instruction // CHECK-ERROR-NEXT: bics x20, sp, x9, lsr #0 // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected compatible register or logical immediate // CHECK-ERROR-NEXT: orn x2, x6, sp, lsl #3 // CHECK-ERROR-NEXT: ^