Index: include/llvm/BinaryFormat/Wasm.h =================================================================== --- include/llvm/BinaryFormat/Wasm.h +++ include/llvm/BinaryFormat/Wasm.h @@ -235,6 +235,11 @@ WASM_SYMBOL_TABLE = 0x8, }; +// Flags used in the custom "linking" section in the WASM_SEGMENT_INFO +enum : unsigned { + WASM_SEGMENT_TLS = 0x2, +}; + // Kind codes used in the custom "linking" section in the WASM_COMDAT_INFO enum : unsigned { WASM_COMDAT_DATA = 0x0, Index: include/llvm/BinaryFormat/WasmRelocs.def =================================================================== --- include/llvm/BinaryFormat/WasmRelocs.def +++ include/llvm/BinaryFormat/WasmRelocs.def @@ -11,3 +11,4 @@ WASM_RELOC(R_WEBASSEMBLY_MEMORY_ADDR_I32, 5) WASM_RELOC(R_WEBASSEMBLY_TYPE_INDEX_LEB, 6) WASM_RELOC(R_WEBASSEMBLY_GLOBAL_INDEX_LEB, 7) +WASM_RELOC(R_WEBASSEMBLY_MEMORY_TLSOFF_LEB, 8) Index: include/llvm/MC/MCSectionWasm.h =================================================================== --- include/llvm/MC/MCSectionWasm.h +++ include/llvm/MC/MCSectionWasm.h @@ -68,7 +68,10 @@ bool isVirtualSection() const override; bool isWasmData() const { - return Kind.isGlobalWriteableData() || Kind.isReadOnly(); + return Kind.isWriteable() || Kind.isReadOnly(); + } + bool isWasmTlsData() const { + return Kind.isThreadLocal(); } bool isUnique() const { return UniqueID != ~0U; } Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -150,6 +150,7 @@ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + case wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB: return true; default: return false; @@ -524,6 +525,8 @@ // Ignore overflow. LLVM allows address arithmetic to silently wrap. return Segment.Offset + Ref.Offset + RelEntry.Addend; } + case wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB: + return 42; // XXX put a sensible dummy value in - not that it matters! default: llvm_unreachable("invalid relocation type"); } @@ -598,6 +601,7 @@ case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB: case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB: WritePatchableLEB(Stream, Value, Offset); break; case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: @@ -1057,7 +1061,7 @@ Segment.Section = &Section; addData(Segment.Data, Section); Segment.Alignment = Section.getAlignment(); - Segment.Flags = 0; + Segment.Flags = (Section.isWasmTlsData() ? wasm::WASM_SEGMENT_TLS : 0x0); DataSize += Segment.Data.size(); Section.setSegmentIndex(SegmentIndex); @@ -1148,7 +1152,7 @@ DataLocations[&WS] = Ref; DEBUG(dbgs() << " -> segment index: " << Ref.Segment); } else { - // A "true" Wasm global (currently just __stack_pointer) + // A "true" Wasm global (currently just __stack_pointer, __thread_pointer) if (WS.isDefined()) report_fatal_error("don't yet support defined globals"); Index: lib/Object/WasmObjectFile.cpp =================================================================== --- lib/Object/WasmObjectFile.cpp +++ lib/Object/WasmObjectFile.cpp @@ -586,6 +586,7 @@ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + case wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB: if (!isValidDataSymbol(Reloc.Index)) return make_error("Bad relocation data index", object_error::parse_failed); Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -95,6 +95,10 @@ MO_SYMBOL_FUNCTION = 0x1, MO_SYMBOL_GLOBAL = 0x2, MO_SYMBOL_MASK = 0x3, + + // Flags to indicate the type of the global reference + MO_GLOBAL_TPOFF = 0x10, + MO_GLOBAL_MASK = 0x10, }; } // end namespace WebAssemblyII Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -66,6 +66,11 @@ return RefA && RefA->getKind() == MCSymbolRefExpr::VK_WebAssembly_GLOBAL; } +static bool IsTlsOffset(const MCValue &Target) { + const MCSymbolRefExpr *RefA = Target.getSymA(); + return RefA && RefA->getKind() == MCSymbolRefExpr::VK_TPOFF; +} + unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target, const MCFixup &Fixup) const { @@ -77,6 +82,8 @@ case WebAssembly::fixup_code_sleb128_i32: if (IsFunction) return wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB; + if (IsTlsOffset(Target)) + return wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB; return wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB; case WebAssembly::fixup_code_sleb128_i64: llvm_unreachable("fixup_sleb128_i64 not implemented yet"); Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -85,11 +85,14 @@ SDValue LowerFrameIndex(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalTlsAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; + SDValue getThreadPointerNode(SDValue Op, SelectionDAG &DAG) const; }; namespace WebAssembly { Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -65,6 +65,7 @@ computeRegisterProperties(Subtarget->getRegisterInfo()); setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); + setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); setOperationAction(ISD::JumpTable, MVTPtr, Custom); setOperationAction(ISD::BlockAddress, MVTPtr, Custom); @@ -129,6 +130,7 @@ setOperationAction(ISD::FrameIndex, MVT::i32, Custom); setOperationAction(ISD::CopyToReg, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); // Expand these forms; we pattern-match the forms that we can handle in isel. for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) @@ -718,6 +720,8 @@ return LowerFrameIndex(Op, DAG); case ISD::GlobalAddress: return LowerGlobalAddress(Op, DAG); + case ISD::GlobalTLSAddress: + return LowerGlobalTlsAddress(Op, DAG); case ISD::ExternalSymbol: return LowerExternalSymbol(Op, DAG); case ISD::JumpTable: @@ -737,6 +741,8 @@ return LowerFRAMEADDR(Op, DAG); case ISD::CopyToReg: return LowerCopyToReg(Op, DAG); + case ISD::INTRINSIC_WO_CHAIN: + return LowerINTRINSIC_WO_CHAIN(Op, DAG); } } @@ -802,6 +808,22 @@ DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); } +SDValue WebAssemblyTargetLowering::LowerGlobalTlsAddress(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + GlobalAddressSDNode *GA = cast(Op); + EVT VT = Op.getValueType(); + assert(VT == MVT::i32); + + SDValue ThreadPointer = getThreadPointerNode(Op, DAG); + SDValue TlsOffset = DAG.getNode( + WebAssemblyISD::Wrapper, DL, VT, + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), + WebAssemblyII::MO_GLOBAL_TPOFF)); + + return DAG.getNode(ISD::ADD, DL, MVT::i32, ThreadPointer, TlsOffset); +} + SDValue WebAssemblyTargetLowering::LowerExternalSymbol( SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); @@ -869,6 +891,24 @@ MachinePointerInfo(SV), 0); } +SDValue WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntNo = cast(Op.getOperand(0))->getZExtValue(); + switch (IntNo) { + default: return SDValue(); // Don't custom lower most intrinsics. + case Intrinsic::thread_pointer: return getThreadPointerNode(Op, DAG); + } +} + +SDValue WebAssemblyTargetLowering::getThreadPointerNode(SDValue Op, + SelectionDAG &DAG) const { + SDValue TPNode = DAG.getTargetExternalSymbol("__thread_pointer", MVT::i32, + WebAssemblyII::MO_SYMBOL_GLOBAL); + return SDValue(DAG.getMachineNode(WebAssembly::GET_GLOBAL_I32, SDLoc(Op), + MVT::i32, TPNode), + 0); +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -256,6 +256,8 @@ (CONST_I32 tglobaladdr:$addr)>; def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), (CONST_I32 texternalsym:$addr)>; +def : Pat<(i32 (WebAssemblywrapper tglobaltlsaddr:$addr)), + (CONST_I32 tglobaltlsaddr:$addr)>; //===----------------------------------------------------------------------===// // Additional sets of instructions. Index: lib/Target/WebAssembly/WebAssemblyMCInstLower.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyMCInstLower.h +++ lib/Target/WebAssembly/WebAssemblyMCInstLower.h @@ -33,8 +33,8 @@ MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; - MCOperand LowerSymbolOperand(MCSymbol *Sym, int64_t Offset, - bool IsFunc, bool IsGlob) const; + MCOperand LowerSymbolOperand(MCSymbol *Sym, int64_t Offset, bool IsFunc, + bool IsGlob, bool IsTls) const; public: WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer) Index: lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -90,11 +90,12 @@ MCSymbolWasm *WasmSym = cast(Sym); const WebAssemblySubtarget &Subtarget = Printer.getSubtarget(); - // __stack_pointer is a global variable; all other external symbols used by - // CodeGen are functions. It's OK to hardcode knowledge of specific symbols - // here; this method is precisely there for fetching the signatures of known - // Clang-provided symbols. - if (strcmp(Name, "__stack_pointer") == 0) { + // __stack_pointer and __thread_pointer are global variables; all other + // external symbols used by CodeGen are functions. It's OK to hardcode + // knowledge of specific symbols here; this method is precisely there for + // fetching the signatures of known Clang-provided symbols. + if (strcmp(Name, "__stack_pointer") == 0 || + strcmp(Name, "__thread_pointer") == 0) { WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); WasmSym->setGlobalType(wasm::WasmGlobalType{ uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64 @@ -117,10 +118,12 @@ MCOperand WebAssemblyMCInstLower::LowerSymbolOperand(MCSymbol *Sym, int64_t Offset, bool IsFunc, - bool IsGlob) const { + bool IsGlob, + bool IsTls) const { MCSymbolRefExpr::VariantKind VK = IsFunc ? MCSymbolRefExpr::VK_WebAssembly_FUNCTION : - IsGlob ? MCSymbolRefExpr::VK_WebAssembly_GLOBAL + IsGlob ? MCSymbolRefExpr::VK_WebAssembly_GLOBAL : + IsTls ? MCSymbolRefExpr::VK_TPOFF : MCSymbolRefExpr::VK_None; const MCExpr *Expr = MCSymbolRefExpr::create(Sym, VK, Ctx); @@ -227,11 +230,12 @@ break; } case MachineOperand::MO_GlobalAddress: - assert(MO.getTargetFlags() == WebAssemblyII::MO_NO_FLAG && - "WebAssembly does not use target flags on GlobalAddresses"); + assert((MO.getTargetFlags() & ~WebAssemblyII::MO_GLOBAL_MASK) == 0 && + "WebAssembly uses only global flags on GlobalAddresses"); MCOp = LowerSymbolOperand(GetGlobalAddressSymbol(MO), MO.getOffset(), MO.getGlobal()->getValueType()->isFunctionTy(), - false); + false, + (MO.getTargetFlags() & WebAssemblyII::MO_GLOBAL_TPOFF) != 0); break; case MachineOperand::MO_ExternalSymbol: // The target flag indicates whether this is a symbol for a @@ -240,7 +244,8 @@ "WebAssembly uses only symbol flags on ExternalSymbols"); MCOp = LowerSymbolOperand(GetExternalSymbolSymbol(MO), /*Offset=*/0, (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_FUNCTION) != 0, - (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0); + (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0, + false); break; } Index: tools/llvm-readobj/WasmDumper.cpp =================================================================== --- tools/llvm-readobj/WasmDumper.cpp +++ tools/llvm-readobj/WasmDumper.cpp @@ -84,6 +84,7 @@ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + case wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB: HasAddend = true; break; default: Index: tools/yaml2obj/yaml2wasm.cpp =================================================================== --- tools/yaml2obj/yaml2wasm.cpp +++ tools/yaml2obj/yaml2wasm.cpp @@ -446,6 +446,7 @@ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + case wasm::R_WEBASSEMBLY_MEMORY_TLSOFF_LEB: encodeULEB128(Reloc.Addend, OS); } }