Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -462,7 +462,8 @@ IsValid = isInt<12>(Imm); return IsValid && ((IsConstantImm && VK == RISCVMCExpr::VK_RISCV_None) || VK == RISCVMCExpr::VK_RISCV_LO || - VK == RISCVMCExpr::VK_RISCV_PCREL_LO); + VK == RISCVMCExpr::VK_RISCV_PCREL_LO || + VK == RISCVMCExpr::VK_RISCV_TPREL_LO); } bool isSImm12Lsb0() const { return isBareSimmNLsb0<12>(); } @@ -488,10 +489,12 @@ bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); if (!IsConstantImm) { IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); - return IsValid && VK == RISCVMCExpr::VK_RISCV_HI; + return IsValid && (VK == RISCVMCExpr::VK_RISCV_HI || + VK == RISCVMCExpr::VK_RISCV_TPREL_HI); } else { return isUInt<20>(Imm) && (VK == RISCVMCExpr::VK_RISCV_None || - VK == RISCVMCExpr::VK_RISCV_HI); + VK == RISCVMCExpr::VK_RISCV_HI || + VK == RISCVMCExpr::VK_RISCV_TPREL_HI); } } @@ -505,11 +508,15 @@ if (!IsConstantImm) { IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); return IsValid && (VK == RISCVMCExpr::VK_RISCV_PCREL_HI || - VK == RISCVMCExpr::VK_RISCV_GOT_HI); + VK == RISCVMCExpr::VK_RISCV_GOT_HI || + VK == RISCVMCExpr::VK_RISCV_TLS_GOT_HI || + VK == RISCVMCExpr::VK_RISCV_TLS_GD_HI); } else { return isUInt<20>(Imm) && (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_PCREL_HI || - VK == RISCVMCExpr::VK_RISCV_GOT_HI); + VK == RISCVMCExpr::VK_RISCV_GOT_HI || + VK == RISCVMCExpr::VK_RISCV_TLS_GOT_HI || + VK == RISCVMCExpr::VK_RISCV_TLS_GD_HI); } } @@ -845,8 +852,8 @@ case Match_InvalidSImm12: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 11), (1 << 11) - 1, - "operand must be a symbol with %lo/%pcrel_lo modifier or an integer in " - "the range"); + "operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an " + "integer in the range"); case Match_InvalidSImm12Lsb0: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 11), (1 << 11) - 2, @@ -857,13 +864,14 @@ "immediate must be a multiple of 2 bytes in the range"); case Match_InvalidUImm20LUI: return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 20) - 1, - "operand must be a symbol with %hi() " - "modifier or an integer in the range"); + "operand must be a symbol with " + "%hi/%tprel_hi() modifier or an integer " + "in the range"); case Match_InvalidUImm20AUIPC: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 20) - 1, - "operand must be a symbol with %pcrel_hi/%got_hi() modifier or an " - "integer in the range"); + "operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() " + "modifier or an integer in the range"); case Match_InvalidSImm21Lsb0JAL: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 20), (1 << 20) - 2, Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -86,6 +86,11 @@ { "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel }, { "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, { "fixup_riscv_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_tls_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_tprel_hi20", 12, 20, 0 }, + { "fixup_riscv_tprel_lo12_i", 20, 12, 0 }, + { "fixup_riscv_tprel_lo12_s", 0, 32, 0 }, + { "fixup_riscv_tls_gd_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, { "fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, { "fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, { "fixup_riscv_rvc_jump", 2, 11, MCFixupKindInfo::FKF_IsPCRel }, Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -141,13 +141,18 @@ return Value; case RISCV::fixup_riscv_lo12_i: case RISCV::fixup_riscv_pcrel_lo12_i: + case RISCV::fixup_riscv_tprel_lo12_i: return Value & 0xfff; case RISCV::fixup_riscv_lo12_s: case RISCV::fixup_riscv_pcrel_lo12_s: + case RISCV::fixup_riscv_tprel_lo12_s: return (((Value >> 5) & 0x7f) << 25) | ((Value & 0x1f) << 7); case RISCV::fixup_riscv_hi20: case RISCV::fixup_riscv_pcrel_hi20: case RISCV::fixup_riscv_got_hi20: + case RISCV::fixup_riscv_tls_got_hi20: + case RISCV::fixup_riscv_tprel_hi20: + case RISCV::fixup_riscv_tls_gd_hi20: // Add 1 if bit 11 is 1, to compensate for low 12 bits being negative. return ((Value + 0x800) >> 12) & 0xfffff; case RISCV::fixup_riscv_jal: { Index: lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -86,6 +86,16 @@ return ELF::R_RISCV_PCREL_LO12_S; case RISCV::fixup_riscv_got_hi20: return ELF::R_RISCV_GOT_HI20; + case RISCV::fixup_riscv_tls_got_hi20: + return ELF::R_RISCV_TLS_GOT_HI20; + case RISCV::fixup_riscv_tprel_hi20: + return ELF::R_RISCV_TPREL_HI20; + case RISCV::fixup_riscv_tprel_lo12_i: + return ELF::R_RISCV_TPREL_LO12_I; + case RISCV::fixup_riscv_tprel_lo12_s: + return ELF::R_RISCV_TPREL_LO12_S; + case RISCV::fixup_riscv_tls_gd_hi20: + return ELF::R_RISCV_TLS_GD_HI20; case RISCV::fixup_riscv_jal: return ELF::R_RISCV_JAL; case RISCV::fixup_riscv_branch: Index: lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -38,6 +38,21 @@ // fixup_riscv_got_hi20 - 20-bit fixup corresponding to got_hi(foo) for // instructions like auipc fixup_riscv_got_hi20, + // fixup_riscv_tls_got_hi20 - 20-bit fixup corresponding to tls_got_hi(foo) + // for instructions like auipc + fixup_riscv_tls_got_hi20, + // fixup_riscv_tprel_hi20 - 20-bit fixup corresponding to tprel_hi(foo) for + // instructions like lui + fixup_riscv_tprel_hi20, + // fixup_riscv_tprel_lo12_i - 12-bit fixup corresponding to tprel_lo(foo) for + // instructions like addi + fixup_riscv_tprel_lo12_i, + // fixup_riscv_tprel_lo12_s - 12-bit fixup corresponding to tprel_lo(foo) for + // the S-type store instructions + fixup_riscv_tprel_lo12_s, + // fixup_riscv_tls_gd_hi20 - 20-bit fixup corresponding to gd_hi(foo) for + // instructions like auipc + fixup_riscv_tls_gd_hi20, // fixup_riscv_jal - 20-bit fixup for symbol references in the jal // instruction fixup_riscv_jal, Index: lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -244,6 +244,24 @@ case RISCVMCExpr::VK_RISCV_GOT_HI: FixupKind = RISCV::fixup_riscv_got_hi20; break; + case RISCVMCExpr::VK_RISCV_TLS_GOT_HI: + FixupKind = RISCV::fixup_riscv_tls_got_hi20; + break; + case RISCVMCExpr::VK_RISCV_TPREL_LO: + if (MIFrm == RISCVII::InstFormatI) + FixupKind = RISCV::fixup_riscv_tprel_lo12_i; + else if (MIFrm == RISCVII::InstFormatS) + FixupKind = RISCV::fixup_riscv_tprel_lo12_s; + else + llvm_unreachable( + "VK_RISCV_TPREL_LO used with unexpected instruction format"); + break; + case RISCVMCExpr::VK_RISCV_TPREL_HI: + FixupKind = RISCV::fixup_riscv_tprel_hi20; + break; + case RISCVMCExpr::VK_RISCV_TLS_GD_HI: + FixupKind = RISCV::fixup_riscv_tls_gd_hi20; + break; case RISCVMCExpr::VK_RISCV_CALL: FixupKind = RISCV::fixup_riscv_call; break; Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -30,6 +30,10 @@ VK_RISCV_PCREL_LO, VK_RISCV_PCREL_HI, VK_RISCV_GOT_HI, + VK_RISCV_TLS_GOT_HI, + VK_RISCV_TPREL_LO, + VK_RISCV_TPREL_HI, + VK_RISCV_TLS_GD_HI, VK_RISCV_CALL, VK_RISCV_CALL_PLT, VK_RISCV_Invalid @@ -60,8 +64,7 @@ return getSubExpr()->findAssociatedFragment(); } - // There are no TLS RISCVMCExprs at the moment. - void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override; bool evaluateAsConstant(int64_t &Res) const; Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -14,6 +14,7 @@ #include "RISCV.h" #include "RISCVMCExpr.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCStreamer.h" @@ -60,6 +61,10 @@ case VK_RISCV_PCREL_LO: case VK_RISCV_PCREL_HI: case VK_RISCV_GOT_HI: + case VK_RISCV_TLS_GOT_HI: + case VK_RISCV_TPREL_LO: + case VK_RISCV_TPREL_HI: + case VK_RISCV_TLS_GD_HI: return false; } } @@ -78,6 +83,10 @@ .Case("pcrel_lo", VK_RISCV_PCREL_LO) .Case("pcrel_hi", VK_RISCV_PCREL_HI) .Case("got_hi", VK_RISCV_GOT_HI) + .Case("tls_got_hi", VK_RISCV_TLS_GOT_HI) + .Case("tprel_lo", VK_RISCV_TPREL_LO) + .Case("tprel_hi", VK_RISCV_TPREL_HI) + .Case("gd_hi", VK_RISCV_TLS_GD_HI) .Default(VK_RISCV_Invalid); } @@ -95,14 +104,66 @@ return "pcrel_hi"; case VK_RISCV_GOT_HI: return "got_hi"; + case VK_RISCV_TLS_GOT_HI: + return "tls_got_hi"; + case VK_RISCV_TPREL_LO: + return "tprel_lo"; + case VK_RISCV_TPREL_HI: + return "tprel_hi"; + case VK_RISCV_TLS_GD_HI: + return "gd_hi"; } } +static void fixELFSymbolsInTLSFixupsImpl(const MCExpr *Expr, MCAssembler &Asm) { + switch (Expr->getKind()) { + case MCExpr::Target: + llvm_unreachable("Can't handle nested target expression"); + break; + case MCExpr::Constant: + break; + + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(Expr); + fixELFSymbolsInTLSFixupsImpl(BE->getLHS(), Asm); + fixELFSymbolsInTLSFixupsImpl(BE->getRHS(), Asm); + break; + } + + case MCExpr::SymbolRef: { + // We're known to be under a TLS fixup, so any symbol should be + // modified. There should be only one. + const MCSymbolRefExpr &SymRef = *cast(Expr); + cast(SymRef.getSymbol()).setType(ELF::STT_TLS); + break; + } + + case MCExpr::Unary: + fixELFSymbolsInTLSFixupsImpl(cast(Expr)->getSubExpr(), Asm); + break; + } +} + +void RISCVMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { + switch (getKind()) { + default: + return; + case VK_RISCV_TLS_GOT_HI: + case VK_RISCV_TPREL_HI: + case VK_RISCV_TLS_GD_HI: + break; + } + + fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); +} + bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const { MCValue Value; if (Kind == VK_RISCV_PCREL_HI || Kind == VK_RISCV_PCREL_LO || - Kind == VK_RISCV_GOT_HI || Kind == VK_RISCV_CALL || + Kind == VK_RISCV_GOT_HI || Kind == VK_RISCV_TLS_GOT_HI || + Kind == VK_RISCV_TPREL_HI || Kind == VK_RISCV_TPREL_LO || + Kind == VK_RISCV_TLS_GD_HI || Kind == VK_RISCV_CALL || Kind == VK_RISCV_CALL_PLT) return false; Index: lib/Target/RISCV/RISCVISelLowering.h =================================================================== --- lib/Target/RISCV/RISCVISelLowering.h +++ lib/Target/RISCV/RISCVISelLowering.h @@ -118,9 +118,15 @@ template SDValue getAddr(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const; + SDValue getStaticTLSAddr(GlobalAddressSDNode *N, SelectionDAG &DAG, + bool UseGOT) const; + SDValue getDynamicTLSAddr(GlobalAddressSDNode *N, SelectionDAG &DAG, + bool UseGOT) const; + SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerConstantPool(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; Index: lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- lib/Target/RISCV/RISCVISelLowering.cpp +++ lib/Target/RISCV/RISCVISelLowering.cpp @@ -146,6 +146,8 @@ setOperationAction(ISD::BlockAddress, XLenVT, Custom); setOperationAction(ISD::ConstantPool, XLenVT, Custom); + setOperationAction(ISD::GlobalTLSAddress, XLenVT, Custom); + if (Subtarget.hasStdExtA()) { setMaxAtomicSizeInBitsSupported(Subtarget.getXLen()); setMinCmpXchgSizeInBits(32); @@ -321,6 +323,8 @@ return lowerBlockAddress(Op, DAG); case ISD::ConstantPool: return lowerConstantPool(Op, DAG); + case ISD::GlobalTLSAddress: + return lowerGlobalTLSAddress(Op, DAG); case ISD::SELECT: return lowerSELECT(Op, DAG); case ISD::VASTART: @@ -445,6 +449,101 @@ return getAddr(N, DAG); } +SDValue RISCVTargetLowering::getStaticTLSAddr(GlobalAddressSDNode *N, + SelectionDAG &DAG, + bool UseGOT) const { + SDLoc DL(N); + EVT Ty = getPointerTy(DAG.getDataLayout()); + const GlobalValue *GV = N->getGlobal(); + MVT XLenVT = Subtarget.getXLenVT(); + + if (UseGOT) { + // Use PC-relative addressing to access the GOT for this TLS symbol, then + // load the address from the GOT and add the thread pointer. This generates + // the pattern (add (ld (WrapperPCRel %tls_got_hi(sym))) tp) + SDValue Addr = + DAG.getTargetGlobalAddress(GV, DL, Ty, 0, RISCVII::MO_TLS_GOT_HI); + SDValue Wrapper = DAG.getNode(RISCVISD::WRAPPER_PCREL, DL, Ty, Addr); + SDValue Base = + DAG.getLoad(Ty, DL, DAG.getEntryNode(), Wrapper, MachinePointerInfo()); + SDValue TPReg = DAG.getRegister(RISCV::X4, XLenVT); + return DAG.getNode(ISD::ADD, DL, Ty, Base, TPReg); + } + + // TODO: Implement LocalExec. + report_fatal_error("LocalExec TLS lowering not implemented"); +} + +SDValue RISCVTargetLowering::getDynamicTLSAddr(GlobalAddressSDNode *N, + SelectionDAG &DAG, + bool UseGOT) const { + SDLoc DL(N); + EVT Ty = getPointerTy(DAG.getDataLayout()); + IntegerType *CallTy = Type::getIntNTy(*DAG.getContext(), Ty.getSizeInBits()); + const GlobalValue *GV = N->getGlobal(); + + // Use a PC-relative addressing mode to access the global dynamic GOT address. + // This generates the pattern (WrapperPCRel %gd_hi(sym)). + SDValue Addr = DAG.getTargetGlobalAddress(GV, DL, Ty, 0, RISCVII::MO_GD_HI); + SDValue Wrapper = DAG.getNode(RISCVISD::WRAPPER_PCREL, DL, Ty, Addr); + + // Prepare argument list to generate call. + ArgListTy Args; + ArgListEntry Entry; + Entry.Node = Wrapper; + Entry.Ty = CallTy; + Args.push_back(Entry); + + // Setup call to __tls_get_addr. + TargetLowering::CallLoweringInfo CLI(DAG); + CLI.setDebugLoc(DL) + .setChain(DAG.getEntryNode()) + .setLibCallee(CallingConv::C, CallTy, + DAG.getExternalSymbol("__tls_get_addr", Ty), + std::move(Args)); + + return LowerCallTo(CLI).first; +} + +SDValue RISCVTargetLowering::lowerGlobalTLSAddress(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + GlobalAddressSDNode *N = cast(Op); + int64_t Offset = N->getOffset(); + MVT XLenVT = Subtarget.getXLenVT(); + + // Non-PIC TLS lowering should always use the LocalExec model. + TLSModel::Model Model = isPositionIndependent() + ? getTargetMachine().getTLSModel(N->getGlobal()) + : TLSModel::LocalExec; + + SDValue Addr; + switch (Model) { + // TODO: Implement LocalExec. + default: + report_fatal_error("Unsupported TLS model"); + case TLSModel::InitialExec: + Addr = getStaticTLSAddr(N, DAG, /*UseGOT=*/true); + break; + case TLSModel::LocalDynamic: + Addr = getDynamicTLSAddr(N, DAG, /*UseGOT=*/false); + break; + case TLSModel::GeneralDynamic: + Addr = getDynamicTLSAddr(N, DAG, /*UseGOT=*/true); + break; + } + + // In order to maximise the opportunity for common subexpression elimination, + // emit a separate ADD node for the global address offset instead of folding + // it in the global address node. Later peephole optimisations may choose to + // fold it back in when profitable. + if (Offset != 0) + return DAG.getNode(ISD::ADD, DL, Ty, Addr, + DAG.getConstant(Offset, DL, XLenVT)); + return Addr; +} + SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { SDValue CondV = Op.getOperand(0); SDValue TrueV = Op.getOperand(1); Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -824,6 +824,8 @@ def : Pat<(WrapperPCRel tglobaladdr:$addr), (PseudoAddrPCRel tglobaladdr:$addr)>; +def : Pat<(WrapperPCRel tglobaltlsaddr:$addr), + (PseudoAddrPCRel tglobaltlsaddr:$addr)>; def : Pat<(WrapperPCRel tblockaddress:$addr), (PseudoAddrPCRel tblockaddress:$addr)>; def : Pat<(WrapperPCRel tconstpool:$addr), Index: lib/Target/RISCV/RISCVMCInstLower.cpp =================================================================== --- lib/Target/RISCV/RISCVMCInstLower.cpp +++ lib/Target/RISCV/RISCVMCInstLower.cpp @@ -52,6 +52,18 @@ case RISCVII::MO_GOT_HI: Kind = RISCVMCExpr::VK_RISCV_GOT_HI; break; + case RISCVII::MO_TLS_GOT_HI: + Kind = RISCVMCExpr::VK_RISCV_TLS_GOT_HI; + break; + case RISCVII::MO_TPREL_LO: + Kind = RISCVMCExpr::VK_RISCV_TPREL_LO; + break; + case RISCVII::MO_TPREL_HI: + Kind = RISCVMCExpr::VK_RISCV_TPREL_HI; + break; + case RISCVII::MO_GD_HI: + Kind = RISCVMCExpr::VK_RISCV_TLS_GD_HI; + break; case RISCVII::MO_PLT: Kind = RISCVMCExpr::VK_RISCV_CALL_PLT; break; Index: lib/Target/RISCV/Utils/RISCVBaseInfo.h =================================================================== --- lib/Target/RISCV/Utils/RISCVBaseInfo.h +++ lib/Target/RISCV/Utils/RISCVBaseInfo.h @@ -54,6 +54,10 @@ MO_PCREL_LO, MO_PCREL_HI, MO_GOT_HI, + MO_TLS_GOT_HI, + MO_TPREL_LO, + MO_TPREL_HI, + MO_GD_HI, MO_PLT, }; } // namespace RISCVII Index: test/CodeGen/RISCV/tls-models.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/tls-models.ll @@ -0,0 +1,72 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv32 -relocation-model=pic < %s \ +; RUN: | FileCheck -check-prefix=RV32-PIC %s + +; Check that TLS symbols are lowered correctly based on the specified +; model. + +@unspecified = thread_local global i32 42 +@ld = thread_local(localdynamic) global i32 42 +@ie = thread_local(initialexec) global i32 42 + +; TODO: Test localexec once implemented. + + +; No model specified + +define i32* @f1() { +; RV32-PIC-LABEL: f1: +; RV32-PIC: # %bb.0: # %entry +; RV32-PIC-NEXT: addi sp, sp, -16 +; RV32-PIC-NEXT: sw ra, 12(sp) +; RV32-PIC-NEXT: .LBB0_1: # %entry +; RV32-PIC-NEXT: # Label of block must be emitted +; RV32-PIC-NEXT: auipc a0, %gd_hi(unspecified) +; RV32-PIC-NEXT: addi a0, a0, %pcrel_lo(.LBB0_1) +; RV32-PIC-NEXT: call __tls_get_addr@plt +; RV32-PIC-NEXT: lw ra, 12(sp) +; RV32-PIC-NEXT: addi sp, sp, 16 +; RV32-PIC-NEXT: ret +entry: + ret i32* @unspecified +} + + +; localdynamic specified + +define i32* @f2() { +; RV32-PIC-LABEL: f2: +; RV32-PIC: # %bb.0: # %entry +; RV32-PIC-NEXT: addi sp, sp, -16 +; RV32-PIC-NEXT: sw ra, 12(sp) +; RV32-PIC-NEXT: .LBB1_1: # %entry +; RV32-PIC-NEXT: # Label of block must be emitted +; RV32-PIC-NEXT: auipc a0, %gd_hi(ld) +; RV32-PIC-NEXT: addi a0, a0, %pcrel_lo(.LBB1_1) +; RV32-PIC-NEXT: call __tls_get_addr@plt +; RV32-PIC-NEXT: lw ra, 12(sp) +; RV32-PIC-NEXT: addi sp, sp, 16 +; RV32-PIC-NEXT: ret +entry: + ret i32* @ld +} + + +; initialexec specified + +define i32* @f3() { +; RV32-PIC-LABEL: f3: +; RV32-PIC: # %bb.0: # %entry +; RV32-PIC-NEXT: .LBB2_1: # %entry +; RV32-PIC-NEXT: # Label of block must be emitted +; RV32-PIC-NEXT: auipc a0, %tls_got_hi(ie) +; RV32-PIC-NEXT: addi a0, a0, %pcrel_lo(.LBB2_1) +; RV32-PIC-NEXT: lw a0, 0(a0) +; RV32-PIC-NEXT: add a0, a0, tp +; RV32-PIC-NEXT: ret +entry: + ret i32* @ie +} + + +; TODO: Test localexec once implemented. Index: test/MC/RISCV/rv32d-invalid.s =================================================================== --- test/MC/RISCV/rv32d-invalid.s +++ test/MC/RISCV/rv32d-invalid.s @@ -2,12 +2,12 @@ # Out of range immediates ## simm12 -fld ft1, -2049(a0) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -fsd ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +fld ft1, -2049(a0) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +fsd ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] # Memory operand not formatted correctly -fld ft1, a0, -200 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -fsd ft2, a1, 100 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +fld ft1, a0, -200 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +fsd ft2, a1, 100 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] # Invalid register names fld ft15, 100(a0) # CHECK: :[[@LINE]]:5: error: invalid operand for instruction Index: test/MC/RISCV/rv32f-invalid.s =================================================================== --- test/MC/RISCV/rv32f-invalid.s +++ test/MC/RISCV/rv32f-invalid.s @@ -2,12 +2,12 @@ # Out of range immediates ## simm12 -flw ft1, -2049(a0) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -fsw ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +flw ft1, -2049(a0) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +fsw ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] # Memory operand not formatted correctly -flw ft1, a0, -200 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -fsw ft2, a1, 100 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +flw ft1, a0, -200 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +fsw ft2, a1, 100 # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] # Invalid register names flw ft15, 100(a0) # CHECK: :[[@LINE]]:5: error: invalid operand for instruction Index: test/MC/RISCV/rv32i-invalid.s =================================================================== --- test/MC/RISCV/rv32i-invalid.s +++ test/MC/RISCV/rv32i-invalid.s @@ -17,8 +17,8 @@ csrrci x0, 43, -90 # CHECK: :[[@LINE]]:16: error: immediate must be an integer in the range [0, 31] ## simm12 -ori a0, a1, -2049 # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -andi ra, sp, 2048 # CHECK: :[[@LINE]]:14: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +ori a0, a1, -2049 # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +andi ra, sp, 2048 # CHECK: :[[@LINE]]:14: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] ## uimm12 csrrw a0, -1, a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095] @@ -38,9 +38,9 @@ bgeu t0, t1, -13 # CHECK: :[[@LINE]]:14: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094] ## uimm20 -lui a0, -1 # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] -lui s0, 1048576 # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] -auipc zero, -0xf # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %pcrel_hi/%got_hi() modifier or an integer in the range [0, 1048575] +lui a0, -1 # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] +lui s0, 1048576 # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] +auipc zero, -0xf # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() modifier or an integer in the range [0, 1048575] ## simm21_lsb0 jal gp, -1048578 # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574] @@ -67,11 +67,11 @@ csrrci x0, 43, %pcrel_lo(d) # CHECK: :[[@LINE]]:16: error: immediate must be an integer in the range [0, 31] ## simm12 -ori a0, a1, %hi(foo) # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -andi ra, sp, %pcrel_hi(123) # CHECK: :[[@LINE]]:14: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -xori a2, a3, %hi(345) # CHECK: :[[@LINE]]:14: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -add a1, a2, (a3) # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -add a1, a2, foo # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +ori a0, a1, %hi(foo) # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +andi ra, sp, %pcrel_hi(123) # CHECK: :[[@LINE]]:14: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +xori a2, a3, %hi(345) # CHECK: :[[@LINE]]:14: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +add a1, a2, (a3) # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +add a1, a2, foo # CHECK: :[[@LINE]]:13: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] ## uimm12 csrrw a0, %lo(1), a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095] @@ -104,8 +104,8 @@ bgeu t0, t1, %pcrel_lo(d) # CHECK: :[[@LINE]]:14: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094] ## uimm20 -lui a0, %lo(1) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] -auipc a1, %lo(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi() modifier or an integer in the range [0, 1048575] +lui a0, %lo(1) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] +auipc a1, %lo(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() modifier or an integer in the range [0, 1048575] ## simm21_lsb0 jal gp, %lo(1) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574] @@ -120,15 +120,15 @@ # Bare symbol names when an operand modifier is required and unsupported # operand modifiers. -lui a0, foo # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] -lui a0, %lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] -lui a0, %pcrel_lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] -lui a0, %pcrel_hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi() modifier or an integer in the range [0, 1048575] +lui a0, foo # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] +lui a0, %lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] +lui a0, %pcrel_lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] +lui a0, %pcrel_hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a symbol with %hi/%tprel_hi() modifier or an integer in the range [0, 1048575] -auipc a0, foo # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi() modifier or an integer in the range [0, 1048575] -auipc a0, %lo(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi() modifier or an integer in the range [0, 1048575] -auipc a0, %hi(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi() modifier or an integer in the range [0, 1048575] -auipc a0, %pcrel_lo(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi() modifier or an integer in the range [0, 1048575] +auipc a0, foo # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() modifier or an integer in the range [0, 1048575] +auipc a0, %lo(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() modifier or an integer in the range [0, 1048575] +auipc a0, %hi(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() modifier or an integer in the range [0, 1048575] +auipc a0, %pcrel_lo(foo) # CHECK: :[[@LINE]]:11: error: operand must be a symbol with %pcrel_hi/%got_hi/%tls_got_hi/%gd_hi() modifier or an integer in the range [0, 1048575] # Unrecognized operand modifier addi t0, sp, %modifer(255) # CHECK: :[[@LINE]]:15: error: unrecognized operand modifier @@ -158,7 +158,7 @@ sltiu s2, s3, 0x50, 0x60 # CHECK: :[[@LINE]]:21: error: invalid operand for instruction # Memory operand not formatted correctly -lw a4, a5, 111 # CHECK: :[[@LINE]]:8: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +lw a4, a5, 111 # CHECK: :[[@LINE]]:8: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] # Too few operands ori a0, a1 # CHECK: :[[@LINE]]:1: error: too few operands for instruction Index: test/MC/RISCV/rv32i-valid.s =================================================================== --- test/MC/RISCV/rv32i-valid.s +++ test/MC/RISCV/rv32i-valid.s @@ -35,6 +35,11 @@ # CHECK-OBJ: lui a0, 0 # CHECK-OBJ: R_RISCV_HI20 foo lui a0, %hi(foo) +# CHECK-ASM: lui a0, %tprel_hi(foo) +# CHECK-ASM: encoding: [0x37,0bAAAA0101,A,A] +# CHECK-OBJ: lui a0, 0 +# CHECK-OBJ: R_RISCV_TPREL_HI20 foo +lui a0, %tprel_hi(foo) # CHECK-ASM-AND-OBJ: auipc a0, 2 # CHECK-ASM: encoding: [0x17,0x25,0x00,0x00] @@ -58,6 +63,16 @@ # CHECK-OBJ: auipc a0, 0 # CHECK-OBJ: R_RISCV_GOT_HI20 foo auipc a0, %got_hi(foo) +# CHECK-ASM: auipc a0, %tls_got_hi(foo) +# CHECK-ASM: encoding: [0x17,0bAAAA0101,A,A] +# CHECK-OBJ: auipc a0, 0 +# CHECK-OBJ: R_RISCV_TLS_GOT_HI20 foo +auipc a0, %tls_got_hi(foo) +# CHECK-ASM: auipc a0, %gd_hi(foo) +# CHECK-ASM: encoding: [0x17,0bAAAA0101,A,A] +# CHECK-OBJ: auipc a0, 0 +# CHECK-OBJ: R_RISCV_TLS_GD_HI20 foo +auipc a0, %gd_hi(foo) # CHECK-ASM-AND-OBJ: jal a2, 1048574 # CHECK-ASM: encoding: [0x6f,0xf6,0xff,0x7f] Index: test/MC/RISCV/rv64i-invalid.s =================================================================== --- test/MC/RISCV/rv64i-invalid.s +++ test/MC/RISCV/rv64i-invalid.s @@ -7,8 +7,8 @@ sraiw a0, a0, -19 # CHECK: :[[@LINE]]:15: error: immediate must be an integer in the range [0, 31] ## simm12 -addiw a0, a1, -2049 # CHECK: :[[@LINE]]:15: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] -ld ra, 2048(sp) # CHECK: :[[@LINE]]:8: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +addiw a0, a1, -2049 # CHECK: :[[@LINE]]:15: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] +ld ra, 2048(sp) # CHECK: :[[@LINE]]:8: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] # Illegal operand modifier ## uimm5 @@ -17,4 +17,4 @@ sraiw a0, a0, %hi(2) # CHECK: :[[@LINE]]:15: error: immediate must be an integer in the range [0, 31] ## simm12 -addiw a0, a1, %hi(foo) # CHECK: :[[@LINE]]:15: error: operand must be a symbol with %lo/%pcrel_lo modifier or an integer in the range [-2048, 2047] +addiw a0, a1, %hi(foo) # CHECK: :[[@LINE]]:15: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047]