Index: lib/Target/ARM64/ARM64InstrFormats.td =================================================================== --- lib/Target/ARM64/ARM64InstrFormats.td +++ lib/Target/ARM64/ARM64InstrFormats.td @@ -102,12 +102,6 @@ let Name = "ArithmeticShifter"; } -// Shifter operand for arithmetic shifted encodings for ADD/SUB instructions. -def AddSubShifterOperand : AsmOperandClass { - let SuperClasses = [ArithmeticShifterOperand]; - let Name = "AddSubShifter"; -} - // Shifter operand for logical vector 128/64-bit shifted encodings. def LogicalVecShifterOperand : AsmOperandClass { let SuperClasses = [ShifterOperand]; @@ -556,18 +550,21 @@ let ParserMatchClass = MoveVecShifterOperand; } +def AddSubImmOperand : AsmOperandClass { + let Name = "AddSubImm"; + let ParserMethod = "tryParseAddSubImm"; + let DiagnosticType = "AddSubSecondSource"; +} // An ADD/SUB immediate shifter operand: +// second operand: // {7-6} - shift type: 00 = lsl // {5-0} - imm6: #0 or #12 -def addsub_shift : Operand { - let ParserMatchClass = AddSubShifterOperand; -} - class addsub_shifted_imm : Operand, ComplexPattern { let PrintMethod = "printAddSubImm"; let EncoderMethod = "getAddSubImmOpValue"; - let MIOperandInfo = (ops i32imm, addsub_shift); + let ParserMatchClass = AddSubImmOperand; + let MIOperandInfo = (ops i32imm, i32imm); } def addsub_shifted_imm32 : addsub_shifted_imm; @@ -577,7 +574,8 @@ : Operand, ComplexPattern { let PrintMethod = "printAddSubImm"; let EncoderMethod = "getAddSubImmOpValue"; - let MIOperandInfo = (ops i32imm, addsub_shift); + let ParserMatchClass = AddSubImmOperand; + let MIOperandInfo = (ops i32imm, i32imm); } def neg_addsub_shifted_imm32 : neg_addsub_shifted_imm; @@ -1522,7 +1520,7 @@ GPR64sp, GPR64sp, GPR64, 24>; // UXTX #0 } -multiclass AddSubS { +multiclass AddSubS { let isCompare = 1, Defs = [NZCV] in { // Add/Subtract immediate def Wri : BaseAddSubImm(NAME#"Wri") + WZR, GPR32sp:$src, addsub_shifted_imm32:$imm)>; + def : InstAlias(NAME#"Xri") + XZR, GPR64sp:$src, addsub_shifted_imm64:$imm)>; + def : InstAlias(NAME#"Wrx") + WZR, GPR32sp:$src1, GPR32:$src2, arith_extend:$sh)>; + def : InstAlias(NAME#"Xrx") + XZR, GPR64sp:$src1, GPR32:$src2, arith_extend:$sh)>; + def : InstAlias(NAME#"Xrx64") + XZR, GPR64sp:$src1, GPR64:$src2, arith_extendlsl64:$sh)>; + def : InstAlias(NAME#"Wrs") + WZR, GPR32:$src1, GPR32:$src2, arith_shift:$sh)>; + def : InstAlias(NAME#"Xrs") + XZR, GPR64:$src1, GPR64:$src2, arith_shift:$sh)>; + + // Compare shorthands + def : InstAlias(NAME#"Wrs") + WZR, GPR32:$src1, GPR32:$src2, 0)>; + def : InstAlias(NAME#"Xrs") + XZR, GPR64:$src1, GPR64:$src2, 0)>; + // Register/register aliases with no shift when SP is not used. def : AddSubRegAlias(NAME#"Wrs"), GPR32, GPR32, GPR32, 0>; Index: lib/Target/ARM64/ARM64InstrInfo.td =================================================================== --- lib/Target/ARM64/ARM64InstrInfo.td +++ lib/Target/ARM64/ARM64InstrInfo.td @@ -474,8 +474,8 @@ defm ADD : AddSub<0, "add", add>; defm SUB : AddSub<1, "sub">; -defm ADDS : AddSubS<0, "adds", ARM64add_flag>; -defm SUBS : AddSubS<1, "subs", ARM64sub_flag>; +defm ADDS : AddSubS<0, "adds", ARM64add_flag, "cmn">; +defm SUBS : AddSubS<1, "subs", ARM64sub_flag, "cmp">; // Use SUBS instead of SUB to enable CSE between SUBS and SUB. def : Pat<(sub GPR32sp:$Rn, addsub_shifted_imm32:$imm), Index: lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp =================================================================== --- lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp +++ lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp @@ -96,6 +96,7 @@ OperandMatchResultTy tryParseAdrpLabel(OperandVector &Operands); OperandMatchResultTy tryParseAdrLabel(OperandVector &Operands); OperandMatchResultTy tryParseFPImm(OperandVector &Operands); + OperandMatchResultTy tryParseAddSubImm(OperandVector &Operands); bool tryParseVectorRegister(OperandVector &Operands); public: @@ -142,6 +143,7 @@ private: enum KindTy { k_Immediate, + k_ShiftedImm, k_Memory, k_Register, k_VectorList, @@ -184,6 +186,11 @@ const MCExpr *Val; }; + struct ShiftedImmOp { + const MCExpr *Val; + unsigned ShiftAmount; + }; + struct FPImmOp { unsigned Val; // Encoded 8-bit representation. }; @@ -232,6 +239,7 @@ struct VectorListOp VectorList; struct VectorIndexOp VectorIndex; struct ImmOp Imm; + struct ShiftedImmOp ShiftedImm; struct FPImmOp FPImm; struct BarrierOp Barrier; struct SysRegOp SysReg; @@ -261,6 +269,9 @@ case k_Immediate: Imm = o.Imm; break; + case k_ShiftedImm: + ShiftedImm = o.ShiftedImm; + break; case k_FPImm: FPImm = o.FPImm; break; @@ -319,6 +330,16 @@ return Imm.Val; } + const MCExpr *getShiftedImmVal() const { + assert(Kind == k_ShiftedImm && "Invalid access!"); + return ShiftedImm.Val; + } + + unsigned getShiftedImmShift() const { + assert(Kind == k_ShiftedImm && "Invalid access!"); + return ShiftedImm.ShiftAmount; + } + unsigned getFPImm() const { assert(Kind == k_FPImm && "Invalid access!"); return FPImm.Val; @@ -549,6 +570,45 @@ return false; return ARM64_AM::isLogicalImmediate(MCE->getValue(), 64); } + bool isShiftedImm() const { return Kind == k_ShiftedImm; } + bool isAddSubImm() const { + if (!isShiftedImm() && !isImm()) + return false; + + const MCExpr *Expr; + + // An ADD/SUB shifter is either 'lsl #0' or 'lsl #12'. + if (isShiftedImm()) { + unsigned Shift = ShiftedImm.ShiftAmount; + Expr = ShiftedImm.Val; + if (Shift != 0 && Shift != 12) + return false; + } else { + Expr = getImm(); + } + + ARM64MCExpr::VariantKind ELFRefKind; + MCSymbolRefExpr::VariantKind DarwinRefKind; + int64_t Addend; + if (ARM64AsmParser::classifySymbolRef(Expr, ELFRefKind, + DarwinRefKind, Addend)) { + return DarwinRefKind == MCSymbolRefExpr::VK_PAGEOFF + || DarwinRefKind == MCSymbolRefExpr::VK_TLVPPAGEOFF + || (DarwinRefKind == MCSymbolRefExpr::VK_GOTPAGEOFF && Addend == 0) + || ELFRefKind == ARM64MCExpr::VK_LO12 + || ELFRefKind == ARM64MCExpr::VK_DTPREL_HI12 + || ELFRefKind == ARM64MCExpr::VK_DTPREL_LO12 + || ELFRefKind == ARM64MCExpr::VK_DTPREL_LO12_NC + || ELFRefKind == ARM64MCExpr::VK_TPREL_HI12 + || ELFRefKind == ARM64MCExpr::VK_TPREL_LO12 + || ELFRefKind == ARM64MCExpr::VK_TPREL_LO12_NC + || ELFRefKind == ARM64MCExpr::VK_TLSDESC_LO12; + } + + // Otherwise it should be a real immediate in range: + const MCConstantExpr *CE = cast(Expr); + return CE->getValue() >= 0 && CE->getValue() <= 0xfff; + } bool isSIMDImmType10() const { if (!isImm()) return false; @@ -805,17 +865,6 @@ return (Val == 0 || Val == 16 || Val == 32 || Val == 48); } - bool isAddSubShifter() const { - if (!isShifter()) - return false; - - // An ADD/SUB shifter is either 'lsl #0' or 'lsl #12'. - unsigned Val = Shifter.Val; - return ARM64_AM::getShiftType(Val) == ARM64_AM::LSL && - (ARM64_AM::getShiftValue(Val) == 0 || - ARM64_AM::getShiftValue(Val) == 12); - } - bool isLogicalVecShifter() const { if (!isShifter()) return false; @@ -1132,6 +1181,17 @@ addExpr(Inst, getImm()); } + void addAddSubImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + if (isShiftedImm()) { + addExpr(Inst, getShiftedImmVal()); + Inst.addOperand(MCOperand::CreateImm(getShiftedImmShift())); + } else { + addExpr(Inst, getImm()); + Inst.addOperand(MCOperand::CreateImm(0)); + } + } + void addAdrpLabelOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *MCE = dyn_cast(getImm()); @@ -1399,11 +1459,6 @@ Inst.addOperand(MCOperand::CreateImm(getShifter())); } - void addAddSubShifterOperands(MCInst &Inst, unsigned N) const { - assert(N == 1 && "Invalid number of operands!"); - Inst.addOperand(MCOperand::CreateImm(getShifter())); - } - void addLogicalVecShifterOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::CreateImm(getShifter())); @@ -1664,6 +1719,16 @@ return Op; } + static ARM64Operand *CreateShiftedImm(const MCExpr *Val, unsigned ShiftAmount, + SMLoc S, SMLoc E, MCContext &Ctx) { + ARM64Operand *Op = new ARM64Operand(k_ShiftedImm, Ctx); + Op->ShiftedImm .Val = Val; + Op->ShiftedImm.ShiftAmount = ShiftAmount; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + static ARM64Operand *CreateFPImm(unsigned Val, SMLoc S, MCContext &Ctx) { ARM64Operand *Op = new ARM64Operand(k_FPImm, Ctx); Op->FPImm.Val = Val; @@ -1781,6 +1846,14 @@ case k_Immediate: getImm()->print(OS); break; + case k_ShiftedImm: { + unsigned Shift = getShiftedImmShift(); + OS << "print(OS); + OS << ", " << ARM64_AM::getShiftName(ARM64_AM::getShiftType(Shift)) << " #" + << ARM64_AM::getShiftValue(Shift) << ">"; + break; + } case k_Memory: OS << ""; break; @@ -2232,6 +2305,72 @@ return MatchOperand_ParseFail; } +/// tryParseAddSubImm - Parse ADD/SUB shifted immediate operand +ARM64AsmParser::OperandMatchResultTy +ARM64AsmParser::tryParseAddSubImm(OperandVector &Operands) { + SMLoc S = getLoc(); + + if (Parser.getTok().is(AsmToken::Hash)) + Parser.Lex(); // Eat '#' + else if (Parser.getTok().isNot(AsmToken::Integer)) + // Operand should start from # or should be integer, emit error otherwise. + return MatchOperand_NoMatch; + + const MCExpr *Imm; + if (parseSymbolicImmVal(Imm)) + return MatchOperand_ParseFail; + else if (Parser.getTok().isNot(AsmToken::Comma)) { + uint64_t ShiftAmount = 0; + const MCConstantExpr *MCE = dyn_cast(Imm); + if (MCE) { + int64_t Val = MCE->getValue(); + if (Val > 0xfff && (Val & 0xfff) == 0) { + Imm = MCConstantExpr::Create(Val >> 12, getContext()); + ShiftAmount = 12; + } + } + SMLoc E = Parser.getTok().getLoc(); + Operands.push_back(ARM64Operand::CreateShiftedImm(Imm, ShiftAmount, S, E, + getContext())); + return MatchOperand_Success; + } + + // Eat ',' + Parser.Lex(); + + // The optional operand must be "lsl #N" where N is non-negative. + if (!Parser.getTok().is(AsmToken::Identifier) || + !Parser.getTok().getIdentifier().equals_lower("lsl")) { + Error(Parser.getTok().getLoc(), "only 'lsl #+N' valid after immediate"); + return MatchOperand_ParseFail; + } + + // Eat 'lsl' + Parser.Lex(); + + if (Parser.getTok().is(AsmToken::Hash)) { + Parser.Lex(); + } + + if (Parser.getTok().isNot(AsmToken::Integer)) { + Error(Parser.getTok().getLoc(), "only 'lsl #+N' valid after immediate"); + return MatchOperand_ParseFail; + } + + int64_t ShiftAmount = Parser.getTok().getIntVal(); + + if (ShiftAmount < 0) { + Error(Parser.getTok().getLoc(), "positive shift amount required"); + return MatchOperand_ParseFail; + } + Parser.Lex(); // Eat the number + + SMLoc E = Parser.getTok().getLoc(); + Operands.push_back(ARM64Operand::CreateShiftedImm(Imm, ShiftAmount, + S, E, getContext())); + return MatchOperand_Success; +} + /// parseCondCodeString - Parse a Condition Code string. unsigned ARM64AsmParser::parseCondCodeString(StringRef Cond) { unsigned CC = StringSwitch(Cond.lower()) @@ -2301,7 +2440,7 @@ // We expect a number here. bool Hash = getLexer().is(AsmToken::Hash); if (!Hash && getLexer().isNot(AsmToken::Integer)) { - TokError("immediate value expected for shifter operand"); + TokError("expected #imm after shift specifier"); return MatchOperand_ParseFail; } @@ -2322,7 +2461,7 @@ const MCConstantExpr *MCE = dyn_cast(ImmVal); if (!MCE) { - TokError("immediate value expected for shifter operand"); + TokError("expected #imm after shift specifier"); return MatchOperand_ParseFail; } @@ -3550,19 +3689,9 @@ case ARM64::SUBSXri: case ARM64::SUBWri: case ARM64::SUBXri: { - if (!Inst.getOperand(3).isImm()) - return Error(Loc[3], "immediate value expected"); - int64_t shifter = Inst.getOperand(3).getImm(); - if (shifter != 0 && shifter != 12) - return Error(Loc[3], "shift value out of range"); - // The imm12 operand can be an expression. Validate that it's legit. - // FIXME: We really, really want to allow arbitrary expressions here - // and resolve the value and validate the result at fixup time, but - // that's hard as we have long since lost any source information we - // need to generate good diagnostics by that point. - if ((Inst.getOpcode() == ARM64::ADDXri || - Inst.getOpcode() == ARM64::ADDWri) && - Inst.getOperand(2).isExpr()) { + // Annoyingly we can't do this in the isAddSubImm predicate, so there is + // some slight duplication here. + if (Inst.getOperand(2).isExpr()) { const MCExpr *Expr = Inst.getOperand(2).getExpr(); ARM64MCExpr::VariantKind ELFRefKind; MCSymbolRefExpr::VariantKind DarwinRefKind; @@ -3571,38 +3700,28 @@ return Error(Loc[2], "invalid immediate expression"); } - // Note that we don't range-check the addend. It's adjusted modulo page - // size when converted, so there is no "out of range" condition when using - // @pageoff. Any validity checking for the value was done in the is*() - // predicate function. + // Only allow these with ADDXri. if ((DarwinRefKind == MCSymbolRefExpr::VK_PAGEOFF || - DarwinRefKind == MCSymbolRefExpr::VK_TLVPPAGEOFF) && + DarwinRefKind == MCSymbolRefExpr::VK_TLVPPAGEOFF) && Inst.getOpcode() == ARM64::ADDXri) return false; - if (ELFRefKind == ARM64MCExpr::VK_LO12 || + + // Only allow these with ADDXri/ADDWri + if ((ELFRefKind == ARM64MCExpr::VK_LO12 || ELFRefKind == ARM64MCExpr::VK_DTPREL_HI12 || ELFRefKind == ARM64MCExpr::VK_DTPREL_LO12 || ELFRefKind == ARM64MCExpr::VK_DTPREL_LO12_NC || ELFRefKind == ARM64MCExpr::VK_TPREL_HI12 || ELFRefKind == ARM64MCExpr::VK_TPREL_LO12 || ELFRefKind == ARM64MCExpr::VK_TPREL_LO12_NC || - ELFRefKind == ARM64MCExpr::VK_TLSDESC_LO12) { + ELFRefKind == ARM64MCExpr::VK_TLSDESC_LO12) && + (Inst.getOpcode() == ARM64::ADDXri || + Inst.getOpcode() == ARM64::ADDWri)) return false; - } else if (DarwinRefKind == MCSymbolRefExpr::VK_GOTPAGEOFF) { - // @gotpageoff can only be used directly, not with an addend. - return Addend != 0; - } - // Otherwise, we're not sure, so don't allow it for now. + // Don't allow expressions in the immediate field otherwise return Error(Loc[2], "invalid immediate expression"); } - - // If it's anything but an immediate, it's not legit. - if (!Inst.getOperand(2).isImm()) - return Error(Loc[2], "invalid immediate expression"); - int64_t imm = Inst.getOperand(2).getImm(); - if (imm > 4095 || imm < 0) - return Error(Loc[2], "immediate value out of range"); return false; } case ARM64::LDRBpre: @@ -3834,10 +3953,8 @@ ARM64Operand::CreateToken("add", false, Op->getStartLoc(), Context); const MCExpr *Imm = MCConstantExpr::Create(0, Context); - Operands.push_back(ARM64Operand::CreateImm(Imm, Op2->getStartLoc(), + Operands.push_back(ARM64Operand::CreateShiftedImm(Imm, 0, Op2->getStartLoc(), Op2->getEndLoc(), Context)); - Operands.push_back(ARM64Operand::CreateShifter( - ARM64_AM::LSL, 0, Op2->getStartLoc(), Op2->getEndLoc(), Context)); delete Op; } @@ -3879,6 +3996,9 @@ case Match_AddSubRegExtendLarge: return Error(Loc, "expected 'sxtx' 'uxtx' or 'lsl' with optional integer in range [0, 4]"); + case Match_AddSubSecondSource: + return Error(Loc, + "expected compatible register, symbol or integer in range [0, 4095]"); case Match_InvalidMemoryIndexedSImm9: return Error(Loc, "index must be an integer in range [-256, 255]."); case Match_InvalidMemoryIndexed32SImm7: @@ -3947,35 +4067,6 @@ assert(Op->isToken() && "Leading operand should always be a mnemonic!"); StringRef Tok = Op->getToken(); - // Translate CMN/CMP pseudos to ADDS/SUBS with zero register destination. - // This needs to be done before the special handling of ADD/SUB immediates. - if (Tok == "cmp" || Tok == "cmn") { - // Replace the opcode with either ADDS or SUBS. - const char *Repl = StringSwitch(Tok) - .Case("cmp", "subs") - .Case("cmn", "adds") - .Default(nullptr); - assert(Repl && "Unknown compare instruction"); - delete Operands[0]; - Operands[0] = ARM64Operand::CreateToken(Repl, false, IDLoc, getContext()); - - // Insert WZR or XZR as destination operand. - ARM64Operand *RegOp = static_cast(Operands[1]); - unsigned ZeroReg; - if (RegOp->isReg() && - ARM64MCRegisterClasses[ARM64::GPR32allRegClassID].contains( - RegOp->getReg())) - ZeroReg = ARM64::WZR; - else - ZeroReg = ARM64::XZR; - Operands.insert( - Operands.begin() + 1, - ARM64Operand::CreateReg(ZeroReg, false, IDLoc, IDLoc, getContext())); - // Update since we modified it above. - ARM64Operand *Op = static_cast(Operands[0]); - Tok = Op->getToken(); - } - unsigned NumOperands = Operands.size(); if (Tok == "mov" && NumOperands == 3) { @@ -4050,39 +4141,7 @@ rewriteMOVR(Operands, getContext()); } } else if (NumOperands == 4) { - if (Tok == "add" || Tok == "adds" || Tok == "sub" || Tok == "subs") { - // Handle the uimm24 immediate form, where the shift is not specified. - ARM64Operand *Op3 = static_cast(Operands[3]); - if (Op3->isImm()) { - if (const MCConstantExpr *CE = - dyn_cast(Op3->getImm())) { - uint64_t Val = CE->getValue(); - if (Val >= (1 << 24)) { - Error(IDLoc, "immediate value is too large"); - return true; - } - if (Val < (1 << 12)) { - Operands.push_back(ARM64Operand::CreateShifter( - ARM64_AM::LSL, 0, IDLoc, IDLoc, getContext())); - } else if ((Val & 0xfff) == 0) { - delete Operands[3]; - CE = MCConstantExpr::Create(Val >> 12, getContext()); - Operands[3] = - ARM64Operand::CreateImm(CE, IDLoc, IDLoc, getContext()); - Operands.push_back(ARM64Operand::CreateShifter( - ARM64_AM::LSL, 12, IDLoc, IDLoc, getContext())); - } else { - Error(IDLoc, "immediate value is too large"); - return true; - } - } else { - Operands.push_back(ARM64Operand::CreateShifter( - ARM64_AM::LSL, 0, IDLoc, IDLoc, getContext())); - } - } - - // FIXME: Horible hack to handle the LSL -> UBFM alias. - } else if (NumOperands == 4 && Tok == "lsl") { + if (NumOperands == 4 && Tok == "lsl") { ARM64Operand *Op2 = static_cast(Operands[2]); ARM64Operand *Op3 = static_cast(Operands[3]); if (Op2->isReg() && Op3->isImm()) { @@ -4457,6 +4516,7 @@ // FALL THROUGH case Match_AddSubRegExtendSmall: case Match_AddSubRegExtendLarge: + case Match_AddSubSecondSource: case Match_InvalidMemoryIndexed8: case Match_InvalidMemoryIndexed16: case Match_InvalidMemoryIndexed32SImm7: