diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp --- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp +++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp @@ -11,6 +11,7 @@ #include "MCTargetDesc/LoongArchMCTargetDesc.h" #include "TargetInfo/LoongArchTargetInfo.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" @@ -29,6 +30,16 @@ namespace { class LoongArchAsmParser : public MCTargetAsmParser { SMLoc getLoc() const { return getParser().getTok().getLoc(); } + bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); } + + struct Inst { + unsigned Opc; + LoongArchMCExpr::VariantKind VK; + Inst(unsigned Opc, + LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None) + : Opc(Opc), VK(VK) {} + }; + using InstSeq = SmallVector; /// Parse a register as used in CFI directives. bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; @@ -69,12 +80,50 @@ bool parseOperand(OperandVector &Operands, StringRef Mnemonic); + // Helper to emit the sequence of instructions generated by the + // "emitLoadAddress*" functions. + void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, + const MCExpr *Symbol, SmallVectorImpl &Insts, + SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.abs $rd, sym". + void emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.pcrel $rd, sym". + void emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.pcrel $rd, $rj, sym". + void emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.got $rd, sym". + void emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.got $rd, $rj, sym". + void emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.tls.le $rd, sym". + void emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.tls.ie $rd, sym". + void emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.tls.ie $rd, $rj, sym". + void emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.tls.ld $rd, sym". + void emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.tls.ld $rd, $rj, sym". + void emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction "la.tls.gd $rd, sym". + void emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.tls.gd $rd, $rj, sym". + void emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + public: enum LoongArchMatchResultTy { Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, Match_RequiresMsbNotLessThanLsb, Match_RequiresOpnd2NotR0R1, Match_RequiresAMORdDifferRkRj, + Match_RequiresLAORdDifferRj, #define GET_OPERAND_DIAGNOSTIC_TYPES #include "LoongArchGenAsmMatcher.inc" #undef GET_OPERAND_DIAGNOSTIC_TYPES @@ -165,6 +214,16 @@ VK == LoongArchMCExpr::VK_LoongArch_None; } + bool isBareSymbol() const { + int64_t Imm; + LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None; + // Must be of 'immediate' type but not a constant. + if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) + return false; + return LoongArchAsmParser::classifySymbolRef(getImm(), VK) && + VK == LoongArchMCExpr::VK_LoongArch_None; + } + bool isUImm2() const { return isUImm<2>(); } bool isUImm2plus1() const { return isUImm<2, 1>(); } bool isUImm3() const { return isUImm<3>(); } @@ -659,10 +718,392 @@ return Error(Loc, "unexpected token"); } +void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, + const MCExpr *Symbol, + SmallVectorImpl &Insts, + SMLoc IDLoc, MCStreamer &Out) { + MCContext &Ctx = getContext(); + for (LoongArchAsmParser::Inst &Inst : Insts) { + unsigned Opc = Inst.Opc; + LoongArchMCExpr::VariantKind VK = Inst.VK; + const LoongArchMCExpr *LE = LoongArchMCExpr::create(Symbol, VK, Ctx); + switch (Opc) { + default: + llvm_unreachable("unexpected opcode"); + case LoongArch::PCALAU12I: + case LoongArch::LU12I_W: + Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addExpr(LE), + getSTI()); + break; + case LoongArch::ORI: + case LoongArch::ADDI_W: + case LoongArch::LD_W: + case LoongArch::LD_D: { + if (VK == LoongArchMCExpr::VK_LoongArch_None) { + Out.emitInstruction( + MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addImm(0), + getSTI()); + continue; + } + Out.emitInstruction( + MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(LE), + getSTI()); + break; + } + case LoongArch::LU32I_D: + Out.emitInstruction(MCInstBuilder(Opc) + .addReg(DestReg == TmpReg ? DestReg : TmpReg) + .addReg(DestReg == TmpReg ? DestReg : TmpReg) + .addExpr(LE), + getSTI()); + break; + case LoongArch::LU52I_D: + Out.emitInstruction( + MCInstBuilder(Opc).addReg(TmpReg).addReg(TmpReg).addExpr(LE), + getSTI()); + break; + case LoongArch::ADDI_D: + Out.emitInstruction( + MCInstBuilder(Opc) + .addReg(TmpReg) + .addReg(DestReg == TmpReg ? TmpReg : LoongArch::R0) + .addExpr(LE), + getSTI()); + break; + case LoongArch::ADD_D: + case LoongArch::LDX_D: + Out.emitInstruction( + MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addReg(TmpReg), + getSTI()); + break; + } + } +} + +void LoongArchAsmParser::emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.abs $rd, sym + // expands to: + // lu12i.w $rd, %abs_hi20(sym) + // ori $rd, $rd, %abs_lo12(sym) + // + // for 64bit appends: + // lu32i.d $rd, %abs64_lo20(sym) + // lu52i.d $rd, $rd, %abs64_hi12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOpcode() == LoongArch::PseudoLA_ABS + ? Inst.getOperand(1).getExpr() + : Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_ABS_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_ABS_LO12)); + + if (is64Bit()) { + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_ABS64_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_ABS64_HI12)); + } + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.pcrel $rd, sym + // expands to: + // pcalau12i $rd, %pc_hi20(sym) + // addi.w/d $rd, rd, %pc_lo12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + InstSeq Insts; + unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20)); + Insts.push_back( + LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_LoongArch_PCALA_LO12)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.pcrel $rd, $rj, sym + // expands to: + // pcalau12i $rd, %pc_hi20(sym) + // addi.d $rj, $r0, %pc_lo12(sym) + // lu32i.d $rj, %pc64_lo20(sym) + // lu52i.d $rj, $rj, %pc64_hi12(sym) + // add.d $rd, $rd, $rj + MCRegister DestReg = Inst.getOperand(0).getReg(); + MCRegister TmpReg = Inst.getOperand(1).getReg(); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_PCALA_LO12)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_HI12)); + Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D)); + + emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.got $rd, sym + // expands to: + // pcalau12i $rd, %got_pc_hi20(sym) + // ld.w/d $rd, $rd, %got_pc_lo12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + InstSeq Insts; + unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20)); + Insts.push_back( + LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.got $rd, $rj, sym + // expands to: + // pcalau12i $rd, %got_pc_hi20(sym) + // addi.d $rj, $r0, %got_pc_lo12(sym) + // lu32i.d $rj, %got64_pc_lo20(sym) + // lu52i.d $rj, $rj, %got64_pc_hi12(sym) + // ldx.d $rd, $rd, $rj + MCRegister DestReg = Inst.getOperand(0).getReg(); + MCRegister TmpReg = Inst.getOperand(1).getReg(); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12)); + Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D)); + + emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.le $rd, sym + // expands to: + // lu12i.w $rd, %le_hi20(sym) + // ori $rd, $rd, %le_lo12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.ie $rd, sym + // expands to: + // pcalau12i $rd, %ie_pc_hi20(sym) + // ld.w/d $rd, $rd, %ie_pc_lo12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + InstSeq Insts; + unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LD, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.ie $rd, $rj, sym + // expands to: + // pcalau12i $rd, %ie_pc_hi20(sym) + // addi.d $rj, $r0, %ie_pc_lo12(sym) + // lu32i.d $rj, %ie64_pc_lo20(sym) + // lu52i.d $rj, $rj, %ie64_pc_hi12(sym) + // ldx.d $rd, $rd, $rj + MCRegister DestReg = Inst.getOperand(0).getReg(); + MCRegister TmpReg = Inst.getOperand(1).getReg(); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12)); + Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D)); + + emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.ld $rd, sym + // expands to: + // pcalau12i $rd, %ld_pc_hi20(sym) + // addi.w/d $rd, $rd, %got_pc_lo12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + InstSeq Insts; + unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.ld $rd, $rj, sym + // expands to: + // pcalau12i $rd, %ld_pc_hi20(sym) + // addi.d $rj, $r0, %got_pc_lo12(sym) + // lu32i.d $rj, %got64_pc_lo20(sym) + // lu52i.d $rj, $rj, %got64_pc_hi12(sym) + // add.d $rd, $rd, $rj + MCRegister DestReg = Inst.getOperand(0).getReg(); + MCRegister TmpReg = Inst.getOperand(1).getReg(); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12)); + Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D)); + + emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.gd $rd, sym + // expands to: + // pcalau12i $rd, %gd_pc_hi20(sym) + // addi.w/d $rd, $rd, %got_pc_lo12(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + InstSeq Insts; + unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.gd $rd, $rj, sym + // expands to: + // pcalau12i $rd, %gd_pc_hi20(sym) + // addi.d $rj, $r0, %got_pc_lo12(sym) + // lu32i.d $rj, %got64_pc_lo20(sym) + // lu52i.d $rj, $rj, %got64_pc_hi12(sym) + // add.d $rd, $rd, $rj + MCRegister DestReg = Inst.getOperand(0).getReg(); + MCRegister TmpReg = Inst.getOperand(1).getReg(); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12)); + Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D)); + + emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); +} + bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands, MCStreamer &Out) { Inst.setLoc(IDLoc); + switch (Inst.getOpcode()) { + default: + break; + case LoongArch::PseudoLA_ABS: + case LoongArch::PseudoLA_ABS_LARGE: + emitLoadAddressAbs(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_PCREL: + emitLoadAddressPcrel(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_PCREL_LARGE: + emitLoadAddressPcrelLarge(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_GOT: + emitLoadAddressGot(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_GOT_LARGE: + emitLoadAddressGotLarge(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_LE: + emitLoadAddressTLSLE(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_IE: + emitLoadAddressTLSIE(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_IE_LARGE: + emitLoadAddressTLSIELarge(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_LD: + emitLoadAddressTLSLD(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_LD_LARGE: + emitLoadAddressTLSLDLarge(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_GD: + emitLoadAddressTLSGD(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_GD_LARGE: + emitLoadAddressTLSGDLarge(Inst, IDLoc, Out); + return false; + } Out.emitInstruction(Inst, getSTI()); return false; } @@ -679,6 +1120,17 @@ return Match_RequiresAMORdDifferRkRj; } break; + case LoongArch::PseudoLA_PCREL_LARGE: + case LoongArch::PseudoLA_GOT_LARGE: + case LoongArch::PseudoLA_TLS_IE_LARGE: + case LoongArch::PseudoLA_TLS_LD_LARGE: + case LoongArch::PseudoLA_TLS_GD_LARGE: { + unsigned Rd = Inst.getOperand(0).getReg(); + unsigned Rj = Inst.getOperand(1).getReg(); + if (Rd == Rj) + return Match_RequiresLAORdDifferRj; + break; + } case LoongArch::CSRXCHG: { unsigned Rj = Inst.getOperand(2).getReg(); if (Rj == LoongArch::R0 || Rj == LoongArch::R1) @@ -803,6 +1255,8 @@ case Match_RequiresAMORdDifferRkRj: return Error(Operands[1]->getStartLoc(), "$rd must be different from both $rk and $rj"); + case Match_RequiresLAORdDifferRj: + return Error(Operands[1]->getStartLoc(), "$rd must be different from $rj"); case Match_InvalidUImm2: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 2) - 1); @@ -888,6 +1342,10 @@ Operands, ErrorInfo, /*Lower=*/-(1 << 27), /*Upper=*/(1 << 27) - 4, "operand must be a bare symbol name or an immediate must be a multiple " "of 4 in the range"); + case Match_InvalidBareSymbol: { + SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a bare symbol name"); + } } llvm_unreachable("Unknown match type detected!"); } diff --git a/llvm/lib/Target/LoongArch/LoongArch.td b/llvm/lib/Target/LoongArch/LoongArch.td --- a/llvm/lib/Target/LoongArch/LoongArch.td +++ b/llvm/lib/Target/LoongArch/LoongArch.td @@ -85,6 +85,33 @@ AssemblerPredicate<(all_of FeatureExtLBT), "'LBT' (Loongson Binary Translation Extension)">; +// Expand la.global as la.pcrel +def LaGlobalWithPcrel + : SubtargetFeature<"la-global-with-pcrel", "HasLaGlobalWithPcrel", "true", + "Expand la.global as la.pcrel">; +def HasLaGlobalWithPcrel + : Predicate<"Subtarget->hasLaGlobalWithPcrel()">, + AssemblerPredicate<(all_of LaGlobalWithPcrel), + "Expand la.global as la.pcrel">; + +// Expand la.global as la.abs +def LaGlobalWithAbs + : SubtargetFeature<"la-global-with-abs", "HasLaGlobalWithAbs", "true", + "Expand la.global as la.abs">; +def HasLaGlobalWithAbs + : Predicate<"Subtarget->hasLaGlobalWithAbs()">, + AssemblerPredicate<(all_of LaGlobalWithAbs), + "Expand la.global as la.abs">; + +// Expand la.local as la.abs +def LaLocalWithAbs + : SubtargetFeature<"la-local-with-abs", "HasLaLocalWithAbs", "true", + "Expand la.local as la.abs">; +def HasLaLocalWithAbs + : Predicate<"Subtarget->hasLaLocalWithAbs()">, + AssemblerPredicate<(all_of LaLocalWithAbs), + "Expand la.local as la.abs">; + //===----------------------------------------------------------------------===// // Registers, instruction descriptions ... //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td @@ -239,6 +239,18 @@ let DecoderMethod = "decodeSImmOperand<26, 2>"; } +def BareSymbol : AsmOperandClass { + let Name = "BareSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidBareSymbol"; + let ParserMethod = "parseImmediate"; +} + +// A bare symbol used in "PseudoLA_*" instructions. +def bare_symbol : Operand { + let ParserMatchClass = BareSymbol; +} + // Standalone (codegen-only) immleaf patterns. // A 12-bit signed immediate plus one where the imm range will be [-2047, 2048]. @@ -953,23 +965,102 @@ def PseudoRET : Pseudo<(outs), (ins), [(loongarch_ret)]>, PseudoInstExpansion<(JIRL R0, R1, 0)>; -let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in -def PseudoLA_PCREL : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>; - -let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in -def PseudoLA_GOT : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>; - -let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in -def PseudoLA_TLS_LE : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>; - -let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in -def PseudoLA_TLS_IE : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>; - -let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in -def PseudoLA_TLS_LD : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>; +/// Load address (la*) macro instructions. + +// Define isCodeGenOnly = 0 to expose them to tablegened assembly parser. +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0, + isAsmParserOnly = 1 in { +def PseudoLA_ABS : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.abs", "$dst, $src">; +def PseudoLA_ABS_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.abs", "$dst, $src">; +def PseudoLA_PCREL : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.pcrel", "$dst, $src">; +def PseudoLA_PCREL_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.pcrel", "$dst, $tmp, $src">, + Requires<[IsLA64]>; +def PseudoLA_TLS_LE : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.tls.le", "$dst, $src">; +} +let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isCodeGenOnly = 0, + isAsmParserOnly = 1 in { +def PseudoLA_GOT : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.got", "$dst, $src">; +def PseudoLA_GOT_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.got", "$dst, $tmp, $src">, + Requires<[IsLA64]>; +def PseudoLA_TLS_IE : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.tls.ie", "$dst, $src">; +def PseudoLA_TLS_IE_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.tls.ie", "$dst, $tmp, $src">, + Requires<[IsLA64]>; +def PseudoLA_TLS_LD : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.tls.ld", "$dst, $src">; +def PseudoLA_TLS_LD_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.tls.ld", "$dst, $tmp, $src">, + Requires<[IsLA64]>; +def PseudoLA_TLS_GD : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.tls.gd", "$dst, $src">; +def PseudoLA_TLS_GD_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.tls.gd", "$dst, $tmp, $src">, + Requires<[IsLA64]>; +} -let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in -def PseudoLA_TLS_GD : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>; +// Load address inst alias: "la", "la.global" and "la.local". +// Default: +// la = la.global = la.got +// la.local = la.pcrel +// With feature "+la-global-with-pcrel": +// la = la.global = la.pcrel +// With feature "+la-global-with-abs": +// la = la.global = la.abs +// With feature "+la-local-with-abs": +// la.local = la.abs +// With features "+la-global-with-pcrel,+la-global-with-abs"(disorder): +// la = la.global = la.pcrel +// Note: To keep consistent with gnu-as behavior, the "la" can only have one +// register operand. +def : InstAlias<"la $dst, $src", (PseudoLA_GOT GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.global $dst, $src", + (PseudoLA_GOT GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.global $dst, $tmp, $src", + (PseudoLA_GOT_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>; +def : InstAlias<"la.local $dst, $src", + (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.local $dst, $tmp, $src", + (PseudoLA_PCREL_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>; + +// Note: Keep HasLaGlobalWithPcrel before HasLaGlobalWithAbs to ensure +// "la-global-with-pcrel" takes effect when bose "la-global-with-pcrel" and +// "la-global-with-abs" are enabled. +let Predicates = [HasLaGlobalWithPcrel] in { +def : InstAlias<"la $dst, $src", (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.global $dst, $src", + (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.global $dst, $tmp, $src", + (PseudoLA_PCREL_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>; +} // Predicates = [HasLaGlobalWithPcrel] + +let Predicates = [HasLaGlobalWithAbs] in { +def : InstAlias<"la $dst, $src", (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.global $dst, $src", + (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.global $dst, $tmp, $src", + (PseudoLA_ABS_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>; +} // Predicates = [HasLaGlobalWithAbs] + +let Predicates = [HasLaLocalWithAbs] in { +def : InstAlias<"la.local $dst, $src", + (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>; +def : InstAlias<"la.local $dst, $tmp, $src", + (PseudoLA_ABS_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>; +} // Predicates = [HasLaLocalWithAbs] /// BSTRINS and BSTRPICK diff --git a/llvm/lib/Target/LoongArch/LoongArchSubtarget.h b/llvm/lib/Target/LoongArch/LoongArchSubtarget.h --- a/llvm/lib/Target/LoongArch/LoongArchSubtarget.h +++ b/llvm/lib/Target/LoongArch/LoongArchSubtarget.h @@ -38,6 +38,9 @@ bool HasExtLASX = false; bool HasExtLVZ = false; bool HasExtLBT = false; + bool HasLaGlobalWithPcrel = false; + bool HasLaGlobalWithAbs = false; + bool HasLaLocalWithAbs = false; unsigned GRLen = 32; MVT GRLenVT = MVT::i32; LoongArchABI::ABI TargetABI = LoongArchABI::ABI_Unknown; @@ -84,6 +87,9 @@ bool hasExtLASX() const { return HasExtLASX; } bool hasExtLVZ() const { return HasExtLVZ; } bool hasExtLBT() const { return HasExtLBT; } + bool hasLaGlobalWithPcrel() const { return HasLaGlobalWithPcrel; } + bool hasLaGlobalWithAbs() const { return HasLaGlobalWithAbs; } + bool hasLaLocalWithAbs() const { return HasLaLocalWithAbs; } MVT getGRLenVT() const { return GRLenVT; } unsigned getGRLen() const { return GRLen; } LoongArchABI::ABI getTargetABI() const { return TargetABI; } diff --git a/llvm/test/MC/LoongArch/Macros/aliases-la-bad.s b/llvm/test/MC/LoongArch/Macros/aliases-la-bad.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Macros/aliases-la-bad.s @@ -0,0 +1,10 @@ +# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 | FileCheck %s + +la $a0, $a1, sym +# CHECK: :[[#@LINE-1]]:10: error: operand must be a bare symbol name + +la $a0, 1 +# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name + +la.global $a0, $a1, 1 +# CHECK: :[[#@LINE-1]]:21: error: operand must be a bare symbol name diff --git a/llvm/test/MC/LoongArch/Macros/aliases-la.s b/llvm/test/MC/LoongArch/Macros/aliases-la.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Macros/aliases-la.s @@ -0,0 +1,74 @@ +## Test la/la.global/la.local expand to different instructions sequence under +## different features. + +# RUN: llvm-mc --triple=loongarch64 %s \ +# RUN: | FileCheck %s --check-prefix=NORMAL +# RUN: llvm-mc --triple=loongarch64 --mattr=+la-global-with-pcrel < %s \ +# RUN: | FileCheck %s --check-prefix=GTOPCR +# RUN: llvm-mc --triple=loongarch64 --mattr=+la-global-with-abs < %s \ +# RUN: | FileCheck %s --check-prefix=GTOABS +# RUN: llvm-mc --triple=loongarch64 --mattr=+la-local-with-abs < %s \ +# RUN: | FileCheck %s --check-prefix=LTOABS + +la $a0, sym +# NORMAL: pcalau12i $a0, %got_pc_hi20(sym) +# NORMAL-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym) + +# GTOPCR: pcalau12i $a0, %pc_hi20(sym) +# GTOPCR-NEXT: addi.d $a0, $a0, %pc_lo12(sym) + +# GTOABS: lu12i.w $a0, %abs_hi20(sym) +# GTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym) +# GTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym) +# GTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym) + +la.global $a0, sym_global +# NORMAL: pcalau12i $a0, %got_pc_hi20(sym_global) +# NORMAL-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym_global) + +# GTOPCR: pcalau12i $a0, %pc_hi20(sym_global) +# GTOPCR-NEXT: addi.d $a0, $a0, %pc_lo12(sym_global) + +# GTOABS: lu12i.w $a0, %abs_hi20(sym_global) +# GTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_global) +# GTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_global) +# GTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_global) + +la.global $a0, $a1, sym_global_large +# NORMAL: pcalau12i $a0, %got_pc_hi20(sym_global_large) +# NORMAL-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_global_large) +# NORMAL-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_global_large) +# NORMAL-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_global_large) +# NORMAL-NEXT: ldx.d $a0, $a0, $a1 + +# GTOPCR: pcalau12i $a0, %pc_hi20(sym_global_large) +# GTOPCR-NEXT: addi.d $a1, $zero, %pc_lo12(sym_global_large) +# GTOPCR-NEXT: lu32i.d $a1, %pc64_lo20(sym_global_large) +# GTOPCR-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_global_large) +# GTOPCR-NEXT: add.d $a0, $a0, $a1 + +# GTOABS: lu12i.w $a0, %abs_hi20(sym_global_large) +# GTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_global_large) +# GTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_global_large) +# GTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_global_large) + +la.local $a0, sym_local +# NORMAL: pcalau12i $a0, %pc_hi20(sym_local) +# NORMAL-NEXT: addi.d $a0, $a0, %pc_lo12(sym_local) + +# LTOABS: lu12i.w $a0, %abs_hi20(sym_local) +# LTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_local) +# LTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_local) +# LTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_local) + +la.local $a0, $a1, sym_local_large +# NORMAL: pcalau12i $a0, %pc_hi20(sym_local_large) +# NORMAL-NEXT: addi.d $a1, $zero, %pc_lo12(sym_local_large) +# NORMAL-NEXT: lu32i.d $a1, %pc64_lo20(sym_local_large) +# NORMAL-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_local_large) +# NORMAL-NEXT: add.d $a0, $a0, $a1 + +# LTOABS: lu12i.w $a0, %abs_hi20(sym_local_large) +# LTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_local_large) +# LTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_local_large) +# LTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_local_large) diff --git a/llvm/test/MC/LoongArch/Macros/macros-la-bad.s b/llvm/test/MC/LoongArch/Macros/macros-la-bad.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Macros/macros-la-bad.s @@ -0,0 +1,13 @@ +# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 | FileCheck %s + +la.got $a0, 1 +# CHECK: :[[#@LINE-1]]:13: error: operand must be a bare symbol name + +la.pcrel $a0, $a1, 1 +# CHECK: :[[#@LINE-1]]:20: error: operand must be a bare symbol name + +la.abs $a0, $a1, sym +# CHECK: :[[#@LINE-1]]:14: error: operand must be a bare symbol name + +la.pcrel $a0, $a0, sym +# CHECK: :[[#@LINE-1]]:11: error: $rd must be different from $rj diff --git a/llvm/test/MC/LoongArch/Macros/macros-la.s b/llvm/test/MC/LoongArch/Macros/macros-la.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Macros/macros-la.s @@ -0,0 +1,66 @@ +# RUN: llvm-mc --triple=loongarch64 %s | FileCheck %s + +la.abs $a0, sym_abs +# CHECK: lu12i.w $a0, %abs_hi20(sym_abs) +# CHECK-NEXT: ori $a0, $a0, %abs_lo12(sym_abs) +# CHECK-NEXT: lu32i.d $a0, %abs64_lo20(sym_abs) +# CHECK-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_abs) + +la.pcrel $a0, sym_pcrel +# CHECK: pcalau12i $a0, %pc_hi20(sym_pcrel) +# CHECK-NEXT: addi.d $a0, $a0, %pc_lo12(sym_pcrel) + +la.pcrel $a0, $a1, sym_pcrel_large +# CHECK: pcalau12i $a0, %pc_hi20(sym_pcrel_large) +# CHECK-NEXT: addi.d $a1, $zero, %pc_lo12(sym_pcrel_large) +# CHECK-NEXT: lu32i.d $a1, %pc64_lo20(sym_pcrel_large) +# CHECK-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_pcrel_large) +# CHECK-NEXT: add.d $a0, $a0, $a1 + +la.got $a0, sym_got +# CHECK: pcalau12i $a0, %got_pc_hi20(sym_got) +# CHECK-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym_got) + +la.got $a0, $a1, sym_got_large +# CHECK: pcalau12i $a0, %got_pc_hi20(sym_got_large) +# CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_got_large) +# CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_got_large) +# CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_got_large) +# CHECK-NEXT: ldx.d $a0, $a0, $a1 + +la.tls.le $a0, sym_le +# CHECK: lu12i.w $a0, %le_hi20(sym_le) +# CHECK-NEXT: ori $a0, $a0, %le_lo12(sym_le) + +la.tls.ie $a0, sym_ie +# CHECK: pcalau12i $a0, %ie_pc_hi20(sym_ie) +# CHECK-NEXT: ld.d $a0, $a0, %ie_pc_lo12(sym_ie) + +la.tls.ie $a0, $a1, sym_ie_large +# CHECK: pcalau12i $a0, %ie_pc_hi20(sym_ie_large) +# CHECK-NEXT: addi.d $a1, $zero, %ie_pc_lo12(sym_ie_large) +# CHECK-NEXT: lu32i.d $a1, %ie64_pc_lo20(sym_ie_large) +# CHECK-NEXT: lu52i.d $a1, $a1, %ie64_pc_hi12(sym_ie_large) +# CHECK-NEXT: ldx.d $a0, $a0, $a1 + +la.tls.ld $a0, sym_ld +# CHECK: pcalau12i $a0, %ld_pc_hi20(sym_ld) +# CHECK-NEXT: addi.d $a0, $a0, %got_pc_lo12(sym_ld) + +la.tls.ld $a0, $a1, sym_ld_large +# CHECK: pcalau12i $a0, %ld_pc_hi20(sym_ld_large) +# CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_ld_large) +# CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_ld_large) +# CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_ld_large) +# CHECK-NEXT: add.d $a0, $a0, $a1 + +la.tls.gd $a0, sym_gd +# CHECK: pcalau12i $a0, %gd_pc_hi20(sym_gd) +# CHECK-NEXT: addi.d $a0, $a0, %got_pc_lo12(sym_gd) + +la.tls.gd $a0, $a1, sym_gd_large +# CHECK: pcalau12i $a0, %gd_pc_hi20(sym_gd_large) +# CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_gd_large) +# CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_gd_large) +# CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_gd_large) +# CHECK-NEXT: add.d $a0, $a0, $a1