diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -51,6 +51,7 @@ "Number of RISC-V Compressed instructions emitted"); namespace { +struct InsnMatchEntry; struct RISCVOperand; struct ParserOptionsSet { @@ -169,6 +170,9 @@ bool parseDirectiveOption(); bool parseDirectiveAttribute(); + bool parseDirectiveInsn(SMLoc L); + bool parseInsnOpcode(OperandVector &Operands); + bool parseInsnOperand(OperandVector &Operands, const InsnMatchEntry *Entry); void setFeatureBits(uint64_t Feature, StringRef FeatureString) { if (!(getSTI().getFeatureBits()[Feature])) { @@ -504,6 +508,24 @@ return (isRV64() && isUInt<5>(Imm)) || isUInt<4>(Imm); } + bool isUImm2() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<2>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm3() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<3>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isUImm5() const { int64_t Imm; RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; @@ -513,6 +535,15 @@ return IsConstantImm && isUInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } + bool isUImm7() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<7>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isSImm5() const { if (!isImm()) return false; @@ -1835,8 +1866,10 @@ if (IDVal == ".option") return parseDirectiveOption(); - else if (IDVal == ".attribute") + if (IDVal == ".attribute") return parseDirectiveAttribute(); + if (IDVal == ".insn") + return parseDirectiveInsn(DirectiveID.getLoc()); return true; } @@ -2200,6 +2233,248 @@ return false; } +namespace { +// Used for the .insn directives; contains information needed to parse the +// operands in the directive. +struct InsnMatchEntry { + StringRef Format; + uint32_t Opcode; + MatchClassKind OperandKinds[7]; +}; + +// For equal_range comparison. +struct CompareInsn { + bool operator()(const InsnMatchEntry &LHS, StringRef RHS) { + return LHS.Format < RHS; + } + bool operator()(StringRef LHS, const InsnMatchEntry &RHS) { + return LHS < RHS.Format; + } + bool operator()(const InsnMatchEntry &LHS, const InsnMatchEntry &RHS) { + return LHS.Format < RHS.Format; + } +}; +} // namespace + +// Table initializing information for parsing the .insn directive. +static const InsnMatchEntry InsnMatchTable[] = { + /* Format, Opcode, NumOperands, OperandKinds */ + {"b", + RISCV::InsnB, + {MCK_UImm7, MCK_UImm3, MCK_AnyReg, MCK_AnyReg, MCK_SImm13Lsb0}}, + {"i", + RISCV::InsnI, + {MCK_UImm7, MCK_UImm3, MCK_AnyReg, MCK_AnyReg, MCK_SImm12}}, + {"i", + RISCV::InsnI_Mem, + {MCK_UImm7, MCK_UImm3, MCK_AnyReg, MCK_SImm12, MCK__40_, MCK_AnyReg, + MCK__41_}}, + {"j", RISCV::InsnJ, {MCK_UImm7, MCK_AnyReg, MCK_SImm21Lsb0JAL}}, + {"r", + RISCV::InsnR, + {MCK_UImm7, MCK_UImm3, MCK_UImm7, MCK_AnyReg, MCK_AnyReg, MCK_AnyReg}}, + // Accept r with 4 registers as an alias for r4. + {"r", + RISCV::InsnR4, + {MCK_UImm7, MCK_UImm3, MCK_UImm2, MCK_AnyReg, MCK_AnyReg, MCK_AnyReg, + MCK_AnyReg}}, + {"r4", + RISCV::InsnR4, + {MCK_UImm7, MCK_UImm3, MCK_UImm2, MCK_AnyReg, MCK_AnyReg, MCK_AnyReg, + MCK_AnyReg}}, + {"s", + RISCV::InsnS, + {MCK_UImm7, MCK_UImm3, MCK_AnyReg, MCK_SImm12, MCK__40_, MCK_AnyReg, + MCK__41_}}, + // Accept sb as an alias for b. + {"sb", + RISCV::InsnB, + {MCK_UImm7, MCK_UImm3, MCK_AnyReg, MCK_AnyReg, MCK_SImm13Lsb0}}, + {"u", RISCV::InsnU, {MCK_UImm7, MCK_AnyReg, MCK_UImm20LUI}}, + // Accept uj as an alias for j. + {"uj", RISCV::InsnJ, {MCK_UImm7, MCK_AnyReg, MCK_SImm21Lsb0JAL}}, +}; + +// Parse opcode for .insn directive. +// TODO: Handle named opcodes. +bool RISCVAsmParser::parseInsnOpcode(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + + const MCExpr *Expr; + SMLoc StartLoc = Parser.getTok().getLoc(); + + // Expect immediate expression. + if (Parser.parseExpression(Expr)) + return Error(StartLoc, "unexpected token in directive"); + + SMLoc EndLoc = + SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + + Operands.push_back(RISCVOperand::createImm(Expr, StartLoc, EndLoc, isRV64())); + return false; +} + +bool RISCVAsmParser::parseInsnOperand(OperandVector &Operands, + const InsnMatchEntry *Entry) { + unsigned NextOpNum = Operands.size(); + if (NextOpNum < array_lengthof(Entry->OperandKinds)) { + MatchClassKind Kind = Entry->OperandKinds[NextOpNum]; + if (Kind == MCK_SImm21Lsb0JAL) + return parseJALOffset(Operands) != MatchOperand_Success; + } + + // Attempt to parse token as a register. + if (parseRegister(Operands, true) == MatchOperand_Success) + return false; + + // Attempt to parse token as an immediate + if (parseImmediate(Operands) == MatchOperand_Success) { + // Parse memory base register if present + if (getLexer().is(AsmToken::LParen)) + return parseMemOpBaseReg(Operands) != MatchOperand_Success; + return false; + } + + // Finally we have exhausted all options and must declare defeat. + Error(getLoc(), "unknown operand"); + return true; +} + +/// parseDirectiveInsn +/// ::= .insn [ format, encoding, (operands (, operands)*) ] +bool RISCVAsmParser::parseDirectiveInsn(SMLoc L) { + MCAsmParser &Parser = getParser(); + + // Expect instruction format as identifier. + StringRef Format; + SMLoc ErrorLoc = Parser.getTok().getLoc(); + if (Parser.parseIdentifier(Format)) + return Error(ErrorLoc, "expected instruction format"); + + SmallVector, 8> Operands; + + // Find entry for this format in InsnMatchTable. + auto EntryRange = + std::equal_range(std::begin(InsnMatchTable), std::end(InsnMatchTable), + Format, CompareInsn()); + + // If first == second, couldn't find a match in the table. + if (EntryRange.first == EntryRange.second) + return Error(ErrorLoc, "unrecognized format"); + + // Format should match from equal_range. + assert(EntryRange.first->Format == Format); + + // First operand is an opcode. + if (parseInsnOpcode(Operands)) + return true; + + while (getLexer().is(AsmToken::Comma)) { + // Consume comma token + getLexer().Lex(); + + // Parse next operand. Assume we can determine custom operands from the + // first table entry. + if (parseInsnOperand(Operands, EntryRange.first)) + return true; + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "unexpected token"); + } + + // Reject any directives with too many operands. + size_t MaxOperands = array_lengthof(EntryRange.first->OperandKinds); + if (Operands.size() > MaxOperands) + return Error( + static_cast(*Operands[MaxOperands]).getStartLoc(), + "invalid operand for directive"); + + // Build the instruction with the parsed operands. + MCInst Inst; + + // Keep track of which operand had an error for later diagnostic. + uint64_t ErrorInfo = ~0ULL; + for (const InsnMatchEntry *it = EntryRange.first, *ie = EntryRange.second; + it != ie; ++it) { + // Format should match from equal_range. + assert(it->Format == Format); + bool OperandsValid = true; + for (unsigned FormalIdx = 0, ActualIdx = 0; FormalIdx != MaxOperands; + ++FormalIdx) { + MatchClassKind Formal = it->OperandKinds[FormalIdx]; + // If we've handled all the parsed operands, we're done. If the formal + // operand is invalid, we successfuly matched all parsed operands. + // Otherwise, we didn't have enough parsed operands for this match. + if (ActualIdx >= Operands.size()) { + OperandsValid = (Formal == InvalidMatchClass); + if (!OperandsValid) + ErrorInfo = ActualIdx; + break; + } + // Verify the operand. + MCParsedAsmOperand &Actual = *Operands[ActualIdx]; + unsigned Diag = validateOperandClass(Actual, Formal); + if (Diag == Match_Success) { + ++ActualIdx; + continue; + } + // If this is the first match we're checking, update the error info. + // If we're checking an alternate match, update the error info if we + // failed on a later operand since that means the earlier failure was + // valid for a different match. + if ((it == EntryRange.first || ErrorInfo <= ActualIdx)) + ErrorInfo = ActualIdx; + OperandsValid = false; + break; + } + + if (!OperandsValid) + continue; + + Inst.clear(); + Inst.setOpcode(it->Opcode); + + for (size_t i = 0; i < Operands.size(); ++i) { + RISCVOperand &Operand = static_cast(*Operands[i]); + MatchClassKind Kind = it->OperandKinds[i]; + + // Verify operand. + unsigned Res = validateOperandClass(Operand, Kind); + if (Res != Match_Success) + return Error(Operand.getStartLoc(), "unexpected operand type"); + + // Add operands to instruction. + if (Operand.isReg()) { + Operand.addRegOperands(Inst, 1); + } else if (Operand.isImm()) { + Operand.addImmOperands(Inst, 1); + } else if (Operand.isToken()) { + // Ignore tokens. + } else { + llvm_unreachable("unexpected operand type"); + } + } + + // Emit as a regular instruction. + Parser.getStreamer().emitInstruction(Inst, getSTI()); + return false; + } + + if (ErrorInfo != ~0ULL) { + if (ErrorInfo >= Operands.size()) + return Error(ErrorLoc, "too few operands for directive"); + + SMLoc OpLoc = + static_cast(*Operands[ErrorInfo]).getStartLoc(); + if (OpLoc != SMLoc()) + ErrorLoc = OpLoc; + } + return Error(ErrorLoc, "invalid operand for directive"); +} + void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { MCInst CInst; bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -168,8 +168,11 @@ namespace RISCVOp { enum OperandType : unsigned { OPERAND_FIRST_RISCV_IMM = MCOI::OPERAND_FIRST_TARGET, - OPERAND_UIMM4 = OPERAND_FIRST_RISCV_IMM, + OPERAND_UIMM2 = OPERAND_FIRST_RISCV_IMM, + OPERAND_UIMM3, + OPERAND_UIMM4, OPERAND_UIMM5, + OPERAND_UIMM7, OPERAND_UIMM12, OPERAND_SIMM12, OPERAND_UIMM20, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -358,7 +358,7 @@ } } else if (Kind == MCExpr::SymbolRef && cast(Expr)->getKind() == MCSymbolRefExpr::VK_None) { - if (Desc.getOpcode() == RISCV::JAL) { + if (MIFrm == RISCVII::InstFormatJ) { FixupKind = RISCV::fixup_riscv_jal; } else if (MIFrm == RISCVII::InstFormatB) { FixupKind = RISCV::fixup_riscv_branch; diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td --- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td +++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td @@ -409,3 +409,135 @@ let Inst{11-7} = rd; let Opcode = opcode.Value; } + +//===----------------------------------------------------------------------===// +// Instruction classes for .insn directives +//===----------------------------------------------------------------------===// + +class DirectiveInsnR + : RVInst { + bits<7> opcode; + bits<7> funct7; + bits<3> funct3; + + bits<5> rs2; + bits<5> rs1; + bits<5> rd; + + let Inst{31-25} = funct7; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = asmstr; +} + +class DirectiveInsnR4 + : RVInst { + bits<7> opcode; + bits<2> funct2; + bits<3> funct3; + + bits<5> rs3; + bits<5> rs2; + bits<5> rs1; + bits<5> rd; + + let Inst{31-27} = rs3; + let Inst{26-25} = funct2; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = asmstr; +} + +class DirectiveInsnI + : RVInst { + bits<7> opcode; + bits<3> funct3; + + bits<12> imm12; + bits<5> rs1; + bits<5> rd; + + let Inst{31-20} = imm12; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = asmstr; +} + +class DirectiveInsnS + : RVInst { + bits<7> opcode; + bits<3> funct3; + + bits<12> imm12; + bits<5> rs2; + bits<5> rs1; + + let Inst{31-25} = imm12{11-5}; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = imm12{4-0}; + let Opcode = opcode; + + let AsmString = asmstr; +} + +class DirectiveInsnB + : RVInst { + bits<7> opcode; + bits<3> funct3; + + bits<12> imm12; + bits<5> rs2; + bits<5> rs1; + + let Inst{31} = imm12{11}; + let Inst{30-25} = imm12{9-4}; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-8} = imm12{3-0}; + let Inst{7} = imm12{10}; + let Opcode = opcode; + + let AsmString = asmstr; +} + +class DirectiveInsnU + : RVInst { + bits<7> opcode; + + bits<20> imm20; + bits<5> rd; + + let Inst{31-12} = imm20; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = asmstr; +} + +class DirectiveInsnJ + : RVInst { + bits<7> opcode; + + bits<20> imm20; + bits<5> rd; + + let Inst{31-12} = imm20; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = asmstr; +} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1337,6 +1337,67 @@ } //===----------------------------------------------------------------------===// +// .insn directive instructions +//===----------------------------------------------------------------------===// + +def uimm2 : Operand { + let ParserMatchClass = UImmAsmOperand<2>; + let DecoderMethod = "decodeUImmOperand<2>"; + let OperandType = "OPERAND_UIMM2"; + let OperandNamespace = "RISCVOp"; +} + +def uimm3 : Operand { + let ParserMatchClass = UImmAsmOperand<3>; + let DecoderMethod = "decodeUImmOperand<3>"; + let OperandType = "OPERAND_UIMM3"; + let OperandNamespace = "RISCVOp"; +} + +def uimm7 : Operand { + let ParserMatchClass = UImmAsmOperand<7>; + let DecoderMethod = "decodeUImmOperand<7>"; + let OperandType = "OPERAND_UIMM7"; + let OperandNamespace = "RISCVOp"; +} + +let isCodeGenOnly = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1, + hasNoSchedulingInfo = 1 in { + def InsnR : DirectiveInsnR<(outs), (ins uimm7:$opcode, uimm3:$funct3, + uimm7:$funct7, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2), + ".insn r $opcode, $funct3, $funct7, $rd, $rs1, $rs2">; + def InsnR4 : DirectiveInsnR4<(outs), (ins uimm7:$opcode, uimm3:$funct3, + uimm2:$funct2, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2, + AnyReg:$rs3), + ".insn r4 $opcode, $funct3, $funct2, $rd, $rs1, $rs2, $rs3">; + def InsnI : DirectiveInsnI<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rd, AnyReg:$rs1, + simm12:$imm12), + ".insn i $opcode, $funct3, $rd, $rs1, $imm12">; + // Alternate form using memory op syntax. + def InsnI_Mem : DirectiveInsnI<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rd, simm12:$imm12, + AnyReg:$rs1), + ".insn i $opcode, $funct3, $rd, ${imm12}(${rs1})">; + def InsnB : DirectiveInsnB<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rs1, AnyReg:$rs2, + simm13_lsb0:$imm12), + ".insn b $opcode, $funct3, $rs1, $rs2, $imm12">; + def InsnU : DirectiveInsnU<(outs), (ins uimm7:$opcode, AnyReg:$rd, + uimm20_lui:$imm20), + ".insn u $opcode, $rd, $imm20">; + def InsnJ : DirectiveInsnJ<(outs), (ins uimm7:$opcode, AnyReg:$rd, + simm21_lsb0_jal:$imm20), + ".insn j $opcode, $rd, $imm20">; + def InsnS : DirectiveInsnS<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rs2, simm12:$imm12, + AnyReg:$rs1), + ".insn i $opcode, $funct3, $rs2, ${imm12}(${rs1})">; +} + +//===----------------------------------------------------------------------===// // Standard extensions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -557,3 +557,13 @@ def FFLAGS : RISCVReg<0, "fflags">; def FRM : RISCVReg<0, "frm">; def FCSR : RISCVReg<0, "fcsr">; + +// Any type register. Used for .insn directives when we don't know what the +// register types could be. +let isAllocatable = 0 in +def AnyReg : RegisterClass<"RISCV", [XLenVT], 32, + (add (sequence "X%u", 0, 31), + (sequence "F%u_D", 0, 31), + (sequence "V%u", 0, 31))> { + let RegInfos = XLenRI; +} diff --git a/llvm/test/MC/RISCV/insn-invalid.s b/llvm/test/MC/RISCV/insn-invalid.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/insn-invalid.s @@ -0,0 +1,12 @@ +# RUN: not llvm-mc -triple riscv32 < %s 2>&1 | FileCheck %s + +# Too many operands +.insn i 0x13, 0, a0, a1, 13, 14 # CHECK: :[[@LINE]]:33: error: invalid operand for directive +.insn r 0x43, 0, 0, fa0, fa1, fa2, fa3, fa4 # CHECK: :[[@LINE]]:44: error: invalid operand for directive + +# Too few operands +.insn r 0x33, 0, 0, a0, a1 # CHECK: :[[@LINE]]:7: error: too few operands for directive +.insn i 0x13, 0, a0, a1 # CHECK: :[[@LINE]]:7: error: too few operands for directive + +.insn r 0x33, 0, 0, a0, 13 # CHECK: :[[@LINE]]:28: error: invalid operand for directive +.insn i 0x13, 0, a0, a1, a2 # CHECK: :[[@LINE]]:28: error: invalid operand for directive diff --git a/llvm/test/MC/RISCV/insn.s b/llvm/test/MC/RISCV/insn.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/insn.s @@ -0,0 +1,77 @@ +# RUN: llvm-mc %s -triple=riscv32 -mattr=+f -riscv-no-aliases -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK-ASM %s +# RUN: llvm-mc %s -triple riscv64 -mattr=+f -riscv-no-aliases -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK-ASM %s +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+f < %s \ +# RUN: | llvm-objdump --mattr=+f -M no-aliases -d -r - \ +# RUN: | FileCheck -check-prefixes=CHECK-OBJ %s +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+f < %s \ +# RUN: | llvm-objdump --mattr=+f -M no-aliases -d -r - \ +# RUN: | FileCheck -check-prefixes=CHECK-OBJ %s + +target: + +# CHECK-ASM: .insn r 51, 0, 0, a0, a1, a2 +# CHECK-ASM: encoding: [0x33,0x85,0xc5,0x00] +# CHECK-OBJ: add a0, a1, a2 +.insn r 0x33, 0, 0, a0, a1, a2 + +# CHECK-ASM: .insn i 19, 0, a0, a1, 13 +# CHECK-ASM: encoding: [0x13,0x85,0xd5,0x00] +# CHECK-OBJ: addi a0, a1, 13 +.insn i 0x13, 0, a0, a1, 13 + +# CHECK-ASM: .insn i 103, 0, a0, 10(a1) +# CHECK-ASM: encoding: [0x67,0x85,0xa5,0x00] +# CHECK-OBJ: jalr a0, 10(a1) +.insn i 0x67, 0, a0, 10(a1) + +# CHECK-ASM: .insn i 3, 0, a0, 4(a1) +# CHECK-ASM: encoding: [0x03,0x85,0x45,0x00] +# CHECK-OBJ: lb a0, 4(a1) +.insn i 0x3, 0, a0, 4(a1) + +# CHECK-ASM: .insn b 99, 0, a0, a1, target +# CHECK-ASM: [0x63'A',A,0xb5'A',A] +# CHECK-OBJ: beq a0, a1, 0x0 +.insn sb 0x63, 0, a0, a1, target + +# CHECK-ASM: .insn b 99, 0, a0, a1, target +# CHECK-ASM: [0x63'A',A,0xb5'A',A] +# CHECK-OBJ: beq a0, a1, 0x0 +.insn b 0x63, 0, a0, a1, target + +# CHECK-ASM: .insn i 35, 0, a0, 4(a1) +# CHECK-ASM: encoding: [0x23,0x82,0xa5,0x00] +# CHECK-OBJ: sb a0, 4(a1) +.insn s 0x23, 0, a0, 4(a1) + +# CHECK-ASM: .insn u 55, a0, 4095 +# CHECK-ASM: encoding: [0x37,0xf5,0xff,0x00] +# CHECK-OBJ: lui a0, 4095 +.insn u 0x37, a0, 0xfff + +# CHECK-ASM: .insn j 111, a0, target +# CHECK-ASM: encoding: [0x6f,0bAAAA0101,A,A] +# CHECK-OBJ: jal a0, 0x0 +.insn uj 0x6f, a0, target + +# CHECK-ASM: .insn j 111, a0, target +# CHECK-ASM: encoding: [0x6f,0bAAAA0101,A,A] +# CHECK-OBJ: jal a0, 0x0 +.insn j 0x6f, a0, target + +# CHECK-ASM: .insn r4 67, 0, 0, fa0, fa1, fa2, fa3 +# CHECK-ASM: encoding: [0x43,0x85,0xc5,0x68] +# CHECK-OBJ: fmadd.s fa0, fa1, fa2, fa3, rne +.insn r 0x43, 0, 0, fa0, fa1, fa2, fa3 + +# CHECK-ASM: .insn r4 67, 0, 0, fa0, fa1, fa2, fa3 +# CHECK-ASM: encoding: [0x43,0x85,0xc5,0x68] +# CHECK-OBJ: fmadd.s fa0, fa1, fa2, fa3, rne +.insn r4 0x43, 0, 0, fa0, fa1, fa2, fa3 + +# CHECK-ASM: .insn i 3, 5, t1, -2048(t2) +# CHECK-ASM: encoding: [0x03,0xd3,0x03,0x80] +# CHECK-OBJ: lhu t1, -2048(t2) +.insn i 0x3, 0x5, x6, %lo(2048)(x7)