Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -99,6 +99,7 @@ OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands); OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); OperandMatchResultTy parseBareSymbol(OperandVector &Operands); + OperandMatchResultTy parseTPRelAddSymbol(OperandVector &Operands); OperandMatchResultTy parseJALOffset(OperandVector &Operands); bool parseOperand(OperandVector &Operands, StringRef Mnemonic); @@ -270,6 +271,16 @@ VK == RISCVMCExpr::VK_RISCV_CALL_PLT); } + bool isTPRelAddSymbol() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + // Must be of 'immediate' type but not a constant. + if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) + return false; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && + VK == RISCVMCExpr::VK_RISCV_TPREL_ADD; + } + bool isCSRSystemRegister() const { return isSystemRegister(); } /// Return true if the operand is a valid for the fence instruction e.g. @@ -897,6 +908,10 @@ SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); } + case Match_InvalidTPRelAddSymbol: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a symbol with %tprel_add modifier"); + } } llvm_unreachable("Unknown match type detected!"); @@ -1125,6 +1140,43 @@ return MatchOperand_Success; } +OperandMatchResultTy +RISCVAsmParser::parseTPRelAddSymbol(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + if (getLexer().getKind() != AsmToken::Percent) + return MatchOperand_NoMatch; + + getParser().Lex(); // Eat '%' + + if (getLexer().getKind() != AsmToken::Identifier) { + Error(getLoc(), "expected valid identifier for operand modifier"); + return MatchOperand_ParseFail; + } + StringRef Identifier = getParser().getTok().getIdentifier(); + RISCVMCExpr::VariantKind VK = RISCVMCExpr::getVariantKindForName(Identifier); + if (VK != RISCVMCExpr::VK_RISCV_TPREL_ADD) { + Error(getLoc(), "expected %tprel_add modifier"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat the identifier + if (getLexer().getKind() != AsmToken::LParen) { + Error(getLoc(), "expected '('"); + return MatchOperand_ParseFail; + } + getParser().Lex(); // Eat '(' + + const MCExpr *SubExpr; + if (getParser().parseParenExpression(SubExpr, E)) + return MatchOperand_ParseFail; + + const MCExpr *ModExpr = RISCVMCExpr::create(SubExpr, VK, getContext()); + Operands.push_back(RISCVOperand::createImm(ModExpr, S, E, isRV64())); + return MatchOperand_Success; +} + OperandMatchResultTy RISCVAsmParser::parseJALOffset(OperandVector &Operands) { // Parsing jal operands is fiddly due to the `jal foo` and `jal ra, foo` // both being acceptable forms. When parsing `jal ra, foo` this function Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -90,6 +90,7 @@ { "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_tprel_add", 0, 0, 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 }, Index: lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -94,6 +94,8 @@ 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_tprel_add: + return ELF::R_RISCV_TPREL_ADD; case RISCV::fixup_riscv_tls_gd_hi20: return ELF::R_RISCV_TLS_GD_HI20; case RISCV::fixup_riscv_jal: Index: lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -50,6 +50,9 @@ // 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_tprel_add - A fixup corresponding to %tprel_add(foo) for the + // add_tls instruction. Used to provide a hint to the linker. + fixup_riscv_tprel_add, // fixup_riscv_tls_gd_hi20 - 20-bit fixup corresponding to gd_hi(foo) for // instructions like auipc fixup_riscv_tls_gd_hi20, Index: lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -57,6 +57,10 @@ SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + void expandAddTPRel(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + /// TableGen'erated function for getting the binary encoding for an /// instruction. uint64_t getBinaryCodeForInstr(const MCInst &MI, @@ -131,6 +135,37 @@ support::endian::write(OS, Binary, support::little); } +// Expand PseudoAddTPRel to a simple ADD with the correct relocation. +void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + MCOperand DestReg = MI.getOperand(0); + MCOperand SrcReg = MI.getOperand(1); + MCOperand TPReg = MI.getOperand(2); + assert(TPReg.isReg() && TPReg.getReg() == RISCV::X4 && + "Expected thread pointer as second input to TP-relative add"); + + MCOperand SrcSymbol = MI.getOperand(3); + assert(SrcSymbol.isExpr() && + "Expected expression as third input to TP-relative add"); + + const RISCVMCExpr *Expr = dyn_cast(SrcSymbol.getExpr()); + assert(Expr && Expr->getKind() == RISCVMCExpr::VK_RISCV_TPREL_ADD && + "Expected tprel_add relocation on TP-relative symbol"); + + // Emit the correct tprel_add relocation for the symbol. + Fixups.push_back(MCFixup::create( + 0, Expr, MCFixupKind(RISCV::fixup_riscv_tprel_add), MI.getLoc())); + + // Emit a normal ADD instruction with the given operands. + MCInst TmpInst = MCInstBuilder(RISCV::ADD) + .addOperand(DestReg) + .addOperand(SrcReg) + .addOperand(TPReg); + uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); +} + void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { @@ -145,6 +180,12 @@ return; } + if (MI.getOpcode() == RISCV::PseudoAddTPRel) { + expandAddTPRel(MI, OS, Fixups, STI); + MCNumEmitted += 1; + return; + } + switch (Size) { default: llvm_unreachable("Unhandled encodeInstruction length!"); @@ -217,6 +258,7 @@ switch (RVExpr->getKind()) { case RISCVMCExpr::VK_RISCV_None: case RISCVMCExpr::VK_RISCV_Invalid: + case RISCVMCExpr::VK_RISCV_TPREL_ADD: llvm_unreachable("Unhandled fixup kind!"); case RISCVMCExpr::VK_RISCV_LO: if (MIFrm == RISCVII::InstFormatI) Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -33,6 +33,7 @@ VK_RISCV_TLS_GOT_HI, VK_RISCV_TPREL_LO, VK_RISCV_TPREL_HI, + VK_RISCV_TPREL_ADD, VK_RISCV_TLS_GD_HI, VK_RISCV_CALL, VK_RISCV_CALL_PLT, Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -64,6 +64,7 @@ case VK_RISCV_TLS_GOT_HI: case VK_RISCV_TPREL_LO: case VK_RISCV_TPREL_HI: + case VK_RISCV_TPREL_ADD: case VK_RISCV_TLS_GD_HI: return false; } @@ -86,6 +87,7 @@ .Case("tls_got_hi", VK_RISCV_TLS_GOT_HI) .Case("tprel_lo", VK_RISCV_TPREL_LO) .Case("tprel_hi", VK_RISCV_TPREL_HI) + .Case("tprel_add", VK_RISCV_TPREL_ADD) .Case("gd_hi", VK_RISCV_TLS_GD_HI) .Default(VK_RISCV_Invalid); } @@ -110,6 +112,8 @@ return "tprel_lo"; case VK_RISCV_TPREL_HI: return "tprel_hi"; + case VK_RISCV_TPREL_ADD: + return "tprel_add"; case VK_RISCV_TLS_GD_HI: return "gd_hi"; } @@ -161,10 +165,10 @@ MCValue Value; if (Kind == VK_RISCV_PCREL_HI || Kind == VK_RISCV_PCREL_LO || - Kind == VK_RISCV_GOT_HI || Kind == VK_RISCV_TLS_GOT_HI || + 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) + Kind == VK_RISCV_TPREL_ADD || Kind == VK_RISCV_TLS_GD_HI || + Kind == VK_RISCV_CALL || Kind == VK_RISCV_CALL_PLT) return false; if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr)) Index: lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- lib/Target/RISCV/RISCVISelLowering.cpp +++ lib/Target/RISCV/RISCVISelLowering.cpp @@ -470,8 +470,23 @@ return DAG.getNode(ISD::ADD, DL, Ty, Base, TPReg); } - // TODO: Implement LocalExec. - report_fatal_error("LocalExec TLS lowering not implemented"); + // Generate a sequence for accessing the address relative to the thread + // pointer, with the appropriate adjustment for the thread pointer offset. + // This generates the pattern (add (add_tprel (lui %tprel_hi(sym)) tp) + // %tprel_lo(sym)) + SDValue AddrHi = + DAG.getTargetGlobalAddress(GV, DL, Ty, 0, RISCVII::MO_TPREL_HI); + SDValue AddrAdd = + DAG.getTargetGlobalAddress(GV, DL, Ty, 0, RISCVII::MO_TPREL_ADD); + SDValue AddrLo = + DAG.getTargetGlobalAddress(GV, DL, Ty, 0, RISCVII::MO_TPREL_LO); + + SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, AddrHi), 0); + SDValue TPReg = DAG.getRegister(RISCV::X4, XLenVT); + SDValue MNAdd = SDValue( + DAG.getMachineNode(RISCV::PseudoAddTPRel, DL, Ty, MNHi, TPReg, AddrAdd), + 0); + return SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNAdd, AddrLo), 0); } SDValue RISCVTargetLowering::getDynamicTLSAddr(GlobalAddressSDNode *N, @@ -520,9 +535,11 @@ SDValue Addr; switch (Model) { - // TODO: Implement LocalExec. default: report_fatal_error("Unsupported TLS model"); + case TLSModel::LocalExec: + Addr = getStaticTLSAddr(N, DAG, /*UseGOT=*/false); + break; case TLSModel::InitialExec: Addr = getStaticTLSAddr(N, DAG, /*UseGOT=*/true); break; Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -186,6 +186,18 @@ let ParserMatchClass = BareSymbol; } +def TPRelAddSymbol : AsmOperandClass { + let Name = "TPRelAddSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidTPRelAddSymbol"; + let ParserMethod = "parseTPRelAddSymbol"; +} + +// A bare symbol with the %tprel_add variant. +def tprel_add_symbol : Operand { + let ParserMatchClass = TPRelAddSymbol; +} + def CSRSystemRegister : AsmOperandClass { let Name = "CSRSystemRegister"; let ParserMethod = "parseCSRSystemRegister"; @@ -689,6 +701,15 @@ def : PatGprGpr, SRL>; def : PatGprGpr, SRA>; +// This is a special case of the ADD instruction used to facilitate the use of a +// fourth operand to emit a relocation on a symbol relating to this instruction. +// The relocation does not affect any bits of the instruction itself but is used +// as a hint to the linker. +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0 in +def PseudoAddTPRel : Pseudo<(outs GPR:$rd), + (ins GPR:$rs1, GPR:$rs2, tprel_add_symbol:$src), [], + "add", "$rd, $rs1, $rs2, $src">; + /// FrameIndex calculations def : Pat<(add (i32 AddrFI:$Rs), simm12:$imm12), Index: lib/Target/RISCV/RISCVMCInstLower.cpp =================================================================== --- lib/Target/RISCV/RISCVMCInstLower.cpp +++ lib/Target/RISCV/RISCVMCInstLower.cpp @@ -61,6 +61,9 @@ case RISCVII::MO_TPREL_HI: Kind = RISCVMCExpr::VK_RISCV_TPREL_HI; break; + case RISCVII::MO_TPREL_ADD: + Kind = RISCVMCExpr::VK_RISCV_TPREL_ADD; + break; case RISCVII::MO_GD_HI: Kind = RISCVMCExpr::VK_RISCV_TLS_GD_HI; break; Index: lib/Target/RISCV/Utils/RISCVBaseInfo.h =================================================================== --- lib/Target/RISCV/Utils/RISCVBaseInfo.h +++ lib/Target/RISCV/Utils/RISCVBaseInfo.h @@ -57,6 +57,7 @@ MO_TLS_GOT_HI, MO_TPREL_LO, MO_TPREL_HI, + MO_TPREL_ADD, MO_GD_HI, MO_PLT, }; Index: test/CodeGen/RISCV/tls-models.ll =================================================================== --- test/CodeGen/RISCV/tls-models.ll +++ test/CodeGen/RISCV/tls-models.ll @@ -8,8 +8,7 @@ @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. +@le = thread_local(localexec) global i32 42 ; No model specified @@ -69,4 +68,15 @@ } -; TODO: Test localexec once implemented. +; localexec specified + +define i32* @f4() { +; RV32-PIC-LABEL: f4: +; RV32-PIC: # %bb.0: # %entry +; RV32-PIC-NEXT: lui a0, %tprel_hi(le) +; RV32-PIC-NEXT: add a0, a0, tp, %tprel_add(le) +; RV32-PIC-NEXT: addi a0, a0, %tprel_lo(le) +; RV32-PIC-NEXT: ret +entry: + ret i32* @le +} Index: test/MC/RISCV/rv32i-invalid.s =================================================================== --- test/MC/RISCV/rv32i-invalid.s +++ test/MC/RISCV/rv32i-invalid.s @@ -130,6 +130,10 @@ 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] +# TP-relative symbol names require a %tprel_add modifier. +add a0, a0, tp, zero # CHECK: :[[@LINE]]:17: error: operand must be a symbol with %tprel_add modifier +add a0, a0, tp, %hi(foo) # CHECK: :[[@LINE]]:18: error: expected %tprel_add modifier + # Unrecognized operand modifier addi t0, sp, %modifer(255) # CHECK: :[[@LINE]]:15: error: unrecognized operand modifier @@ -154,7 +158,6 @@ sub t0, t2, 1 # CHECK: :[[@LINE]]:13: error: invalid operand for instruction # Too many operands -add ra, zero, zero, zero # CHECK: :[[@LINE]]:21: error: invalid operand for instruction sltiu s2, s3, 0x50, 0x60 # CHECK: :[[@LINE]]:21: error: invalid operand for instruction # Memory operand not formatted correctly Index: test/MC/RISCV/rv32i-valid.s =================================================================== --- test/MC/RISCV/rv32i-valid.s +++ test/MC/RISCV/rv32i-valid.s @@ -211,6 +211,11 @@ # CHECK-ASM-AND-OBJ: add ra, zero, zero # CHECK-ASM: encoding: [0xb3,0x00,0x00,0x00] add x1, x0, x0 +# CHECK-ASM: add a0, a0, tp, %tprel_add(foo) +# CHECK-ASM: encoding: [0x33,0x05,0x45,0x00] +# CHECK-OBJ: add a0, a0, tp +# CHECK-OBJ: R_RISCV_TPREL_ADD foo +add a0, a0, tp, %tprel_add(foo) # CHECK-ASM-AND-OBJ: sub t0, t2, t1 # CHECK-ASM: encoding: [0xb3,0x82,0x63,0x40] sub t0, t2, t1