Index: docs/AMDGPUUsage.rst =================================================================== --- docs/AMDGPUUsage.rst +++ docs/AMDGPUUsage.rst @@ -851,15 +851,16 @@ .. table:: AMDGPU ELF Symbols :name: amdgpu-elf-symbols-table - ===================== ============== ============= ================== - Name Type Section Description - ===================== ============== ============= ================== - *link-name* ``STT_OBJECT`` - ``.data`` Global variable - - ``.rodata`` - - ``.bss`` - *link-name*\ ``.kd`` ``STT_OBJECT`` - ``.rodata`` Kernel descriptor - *link-name* ``STT_FUNC`` - ``.text`` Kernel entry point - ===================== ============== ============= ================== + ===================== ================== ================ ================== + Name Type Section Description + ===================== ================== ================ ================== + *link-name* ``STT_OBJECT`` - ``.data`` Global variable + - ``.rodata`` + - ``.bss`` + *link-name*\ ``.kd`` ``STT_OBJECT`` - ``.rodata`` Kernel descriptor + *link-name* ``STT_FUNC`` - ``.text`` Kernel entry point + *link-name* ``STT_OBJECT`` - SHN_AMDGPU_LDS Global variable in LDS + ===================== ================== ================ ================== Global variable Global variables both used and defined by the compilation unit. @@ -871,10 +872,10 @@ will resolve relocations using the definition provided by another code object or explicitly defined by the runtime. - All global symbols, whether defined in the compilation unit or external, are - accessed by the machine code indirectly through a GOT table entry. This - allows them to be preemptable. The GOT table is only supported when the target - triple OS is ``amdhsa`` (see :ref:`amdgpu-target-triples`). + If the symbol resides in local/group memory (LDS) then its section is the + special processor-specific section name ``SHN_AMDGPU_LDS``, and the + ``st_value`` field describes alignment requirements as it does for common + symbols. .. TODO Add description of linked shared object symbols. Seems undefined symbols Index: include/llvm/BinaryFormat/ELF.h =================================================================== --- include/llvm/BinaryFormat/ELF.h +++ include/llvm/BinaryFormat/ELF.h @@ -1424,6 +1424,11 @@ GNU_PROPERTY_X86_FEATURE_2_XSAVEC = 1 << 9, }; +// AMDGPU-specific section indices. +enum { + SHN_AMDGPU_LDS = 0xff00, // Variable in LDS; symbol encoded like SHN_COMMON +}; + // AMD specific notes. (Code Object V2) enum { // Note types with values between 0 and 9 (inclusive) are reserved. Index: include/llvm/MC/MCSymbol.h =================================================================== --- include/llvm/MC/MCSymbol.h +++ include/llvm/MC/MCSymbol.h @@ -58,6 +58,7 @@ SymContentsOffset, SymContentsVariable, SymContentsCommon, + SymContentsTargetCommon, // Index stores the section index }; // Special sentinal value for the absolute pseudo fragment. @@ -108,7 +109,7 @@ /// This is actually a Contents enumerator, but is unsigned to avoid sign /// extension and achieve better bitpacking with MSVC. - unsigned SymbolContents : 2; + unsigned SymbolContents : 3; /// The alignment of the symbol, if it is 'common', or -1. /// @@ -344,10 +345,11 @@ /// /// \param Size - The size of the symbol. /// \param Align - The alignment of the symbol. - void setCommon(uint64_t Size, unsigned Align) { + /// \param Target - Is the symbol a target-specific common-like symbol. + void setCommon(uint64_t Size, unsigned Align, bool Target = false) { assert(getOffset() == 0); CommonSize = Size; - SymbolContents = SymContentsCommon; + SymbolContents = Target ? SymContentsTargetCommon : SymContentsCommon; assert((!Align || isPowerOf2_32(Align)) && "Alignment must be a power of 2"); @@ -367,20 +369,28 @@ /// /// \param Size - The size of the symbol. /// \param Align - The alignment of the symbol. + /// \param Target - Is the symbol a target-specific common-like symbol. /// \return True if symbol was already declared as a different type - bool declareCommon(uint64_t Size, unsigned Align) { + bool declareCommon(uint64_t Size, unsigned Align, bool Target = false) { assert(isCommon() || getOffset() == 0); if(isCommon()) { - if(CommonSize != Size || getCommonAlignment() != Align) - return true; + if (CommonSize != Size || getCommonAlignment() != Align || + isTargetCommon() != Target) + return true; } else - setCommon(Size, Align); + setCommon(Size, Align, Target); return false; } /// Is this a 'common' symbol. bool isCommon() const { - return SymbolContents == SymContentsCommon; + return SymbolContents == SymContentsCommon || + SymbolContents == SymContentsTargetCommon; + } + + /// Is this a target-specific common-like symbol. + bool isTargetCommon() const { + return SymbolContents == SymContentsTargetCommon; } MCFragment *getFragment(bool SetUsed = true) const { Index: lib/MC/ELFObjectWriter.cpp =================================================================== --- lib/MC/ELFObjectWriter.cpp +++ lib/MC/ELFObjectWriter.cpp @@ -463,7 +463,7 @@ uint64_t ELFWriter::SymbolValue(const MCSymbol &Sym, const MCAsmLayout &Layout) { - if (Sym.isCommon() && Sym.isExternal()) + if (Sym.isCommon() && (Sym.isTargetCommon() || Sym.isExternal())) return Sym.getCommonAlignment(); uint64_t Res; @@ -660,8 +660,12 @@ if (Symbol.isAbsolute()) { MSD.SectionIndex = ELF::SHN_ABS; } else if (Symbol.isCommon()) { - assert(!Local); - MSD.SectionIndex = ELF::SHN_COMMON; + if (Symbol.isTargetCommon()) { + MSD.SectionIndex = Symbol.getIndex(); + } else { + assert(!Local); + MSD.SectionIndex = ELF::SHN_COMMON; + } } else if (Symbol.isUndefined()) { if (isSignature && !Used) { MSD.SectionIndex = RevGroupMap.lookup(&Symbol); Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -555,6 +555,7 @@ ECase(SHN_COMMON); ECase(SHN_XINDEX); ECase(SHN_HIRESERVE); + ECase(SHN_AMDGPU_LDS); ECase(SHN_HEXAGON_SCOMMON); ECase(SHN_HEXAGON_SCOMMON_1); ECase(SHN_HEXAGON_SCOMMON_2); Index: lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp =================================================================== --- lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp +++ lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp @@ -915,6 +915,7 @@ bool ParseDirectiveHSAMetadata(); bool ParseDirectivePALMetadataBegin(); bool ParseDirectivePALMetadata(); + bool ParseDirectiveAMDGPULDS(); /// Common code to parse out a block of text (typically YAML) between start and /// end directives. @@ -3918,6 +3919,60 @@ return false; } +/// ParseDirectiveAMDGPULDS +/// ::= .amdgpu_lds identifier ',' size_expression [',' align_expression] +bool AMDGPUAsmParser::ParseDirectiveAMDGPULDS() { + if (getParser().checkForValidSection()) + return true; + + StringRef Name; + SMLoc NameLoc = getLexer().getLoc(); + if (getParser().parseIdentifier(Name)) + return TokError("expected identifier in directive"); + + MCSymbol *Symbol = getContext().getOrCreateSymbol(Name); + if (parseToken(AsmToken::Comma, "expected ','")) + return true; + + unsigned LocalMemorySize = AMDGPU::IsaInfo::getLocalMemorySize(&getSTI()); + + int64_t Size; + SMLoc SizeLoc = getLexer().getLoc(); + if (getParser().parseAbsoluteExpression(Size)) + return true; + if (Size < 0) + return Error(SizeLoc, "size must be non-negative"); + if (Size > LocalMemorySize) + return Error(SizeLoc, "size is too large"); + + int64_t Align = 4; + if (getLexer().is(AsmToken::Comma)) { + Lex(); + SMLoc AlignLoc = getLexer().getLoc(); + if (getParser().parseAbsoluteExpression(Align)) + return true; + if (Align < 0 || !isPowerOf2_64(Align)) + return Error(AlignLoc, "alignment must be a power of two"); + + // Alignment larger than the size of LDS is possible in theory, as long + // as the linker manages to place to symbol at address 0, but we do want + // to make sure the alignment fits nicely into a 32-bit integer. + if (Align >= 1u << 31) + return Error(AlignLoc, "alignment is too large"); + } + + if (parseToken(AsmToken::EndOfStatement, + "unexpected token in '.amdgpu_lds' directive")) + return true; + + Symbol->redefineIfPossible(); + if (!Symbol->isUndefined()) + return Error(NameLoc, "invalid symbol redefinition"); + + getTargetStreamer().emitAMDGPULDS(Symbol, Size, Align); + return false; +} + bool AMDGPUAsmParser::ParseDirective(AsmToken DirectiveID) { StringRef IDVal = DirectiveID.getString(); @@ -3951,6 +4006,9 @@ return ParseDirectiveHSAMetadata(); } + if (IDVal == ".amdgpu_lds") + return ParseDirectiveAMDGPULDS(); + if (IDVal == PALMD::AssemblerDirectiveBegin) return ParseDirectivePALMetadataBegin(); Index: lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.h =================================================================== --- lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.h +++ lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.h @@ -53,6 +53,9 @@ virtual void EmitAMDGPUSymbolType(StringRef SymbolName, unsigned Type) = 0; + virtual void emitAMDGPULDS(MCSymbol *Symbol, unsigned Size, + unsigned Align) = 0; + /// \returns True on success, false on failure. virtual bool EmitISAVersion(StringRef IsaVersionString) = 0; @@ -107,6 +110,8 @@ void EmitAMDGPUSymbolType(StringRef SymbolName, unsigned Type) override; + void emitAMDGPULDS(MCSymbol *Sym, unsigned Size, unsigned Align) override; + /// \returns True on success, false on failure. bool EmitISAVersion(StringRef IsaVersionString) override; @@ -152,6 +157,8 @@ void EmitAMDGPUSymbolType(StringRef SymbolName, unsigned Type) override; + void emitAMDGPULDS(MCSymbol *Sym, unsigned Size, unsigned Align) override; + /// \returns True on success, false on failure. bool EmitISAVersion(StringRef IsaVersionString) override; Index: lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp =================================================================== --- lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp +++ lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp @@ -206,6 +206,12 @@ } } +void AMDGPUTargetAsmStreamer::emitAMDGPULDS(MCSymbol *Symbol, unsigned Size, + unsigned Align) { + OS << "\t.amdgpu_lds " << Symbol->getName() << ", " << Size << ", " << Align + << '\n'; +} + bool AMDGPUTargetAsmStreamer::EmitISAVersion(StringRef IsaVersionString) { OS << "\t.amd_amdgpu_isa \"" << IsaVersionString << "\"\n"; return true; @@ -497,6 +503,27 @@ Symbol->setType(Type); } +void AMDGPUTargetELFStreamer::emitAMDGPULDS(MCSymbol *Symbol, unsigned Size, + unsigned Align) { + assert(isPowerOf2_32(Align)); + + MCSymbolELF *SymbolELF = cast(Symbol); + SymbolELF->setType(ELF::STT_OBJECT); + + if (!SymbolELF->isBindingSet()) { + SymbolELF->setBinding(ELF::STB_GLOBAL); + SymbolELF->setExternal(true); + } + + if (SymbolELF->declareCommon(Size, Align, true)) { + report_fatal_error("Symbol: " + Symbol->getName() + + " redeclared as different type"); + } + + SymbolELF->setIndex(ELF::SHN_AMDGPU_LDS); + SymbolELF->setSize(MCConstantExpr::create(Size, getContext())); +} + bool AMDGPUTargetELFStreamer::EmitISAVersion(StringRef IsaVersionString) { // Create two labels to mark the beginning and end of the desc field // and a MCExpr to calculate the size of the desc field. Index: test/MC/AMDGPU/elf-lds-error.s =================================================================== --- /dev/null +++ test/MC/AMDGPU/elf-lds-error.s @@ -0,0 +1,19 @@ +// RUN: not llvm-mc -triple amdgcn-- -mcpu gfx900 %s -o - 2>&1 | FileCheck %s + +// CHECK: :[[@LINE+1]]:27: error: size is too large + .amdgpu_lds huge, 200000 + +// CHECK: :[[@LINE+1]]:30: error: size must be non-negative + .amdgpu_lds negsize, -4 + +// CHECK: :[[@LINE+1]]:36: error: alignment must be a power of two + .amdgpu_lds zero_align, 5, 0 + +// CHECK: :[[@LINE+1]]:39: error: alignment must be a power of two + .amdgpu_lds non_pot_align, 0, 12 + +// CHECK: :[[@LINE+1]]:36: error: alignment is too large + .amdgpu_lds huge_align, 0, 1099511627776 + +// CHECK: :[[@LINE+1]]:9: error: unknown directive + .amdgpu_ldsnowhitespace, 8 Index: test/MC/AMDGPU/elf-lds.s =================================================================== --- /dev/null +++ test/MC/AMDGPU/elf-lds.s @@ -0,0 +1,82 @@ +// RUN: llvm-mc -filetype=obj -triple amdgcn-- -mcpu gfx900 %s -o - | llvm-readobj -t -r | FileCheck %s + + .text + .globl test_kernel + .p2align 8 + .type test_kernel,@function +test_kernel: + s_mov_b32 s0, lds0@abs32@lo + v_lshl_add_u32 v3, v0, 2, s0 + ds_read2_b32 v[1:2], v3 offset1:1 + + s_mov_b32 s0, lds4@abs32@lo + v_lshl_add_u32 v3, v0, 2, s0 + ds_write_b32 v3, v1 + s_endpgm +.Lfunc_end: + .size test_kernel, .Lfunc_end-test_kernel + + .globl lds0 + .amdgpu_lds lds0, 192, 16 + + .globl lds1 + .amdgpu_lds lds1,387,8 + + ; Weird whitespace cases + .globl lds2 + .amdgpu_lds lds2, 12 + + ; No alignment or .globl directive, not mentioned anywhere + .amdgpu_lds lds3, 16 + + ; No alignment or .globl directive, size 0, but mentioned in .text + .amdgpu_lds lds4, 0 + +// CHECK: Relocations [ +// CHECK: Section (3) .rel.text { +// CHECK-NEXT: 0x4 R_AMDGPU_ABS32 lds0 0x0 +// CHECK-NEXT: 0x1C R_AMDGPU_ABS32 lds4 0x0 +// CHECK-NEXT: } +// CHECK: ] + +// CHECK: Symbol { +// CHECK: Name: lds0 (54) +// CHECK-NEXT: Value: 0x10 +// CHECK-NEXT: Size: 192 +// CHECK-NEXT: Binding: Global (0x1) +// CHECK-NEXT: Type: Object (0x1) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Processor Specific (0xFF00) +// CHECK-NEXT: } + +// CHECK: Symbol { +// CHECK: Name: lds1 (49) +// CHECK-NEXT: Value: 0x8 +// CHECK-NEXT: Size: 387 +// CHECK-NEXT: Binding: Global (0x1) +// CHECK-NEXT: Type: Object (0x1) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Processor Specific (0xFF00) +// CHECK-NEXT: } + +// CHECK: Symbol { +// CHECK: Name: lds2 (44) +// CHECK-NEXT: Value: 0x4 +// CHECK-NEXT: Size: 12 +// CHECK-NEXT: Binding: Global (0x1) +// CHECK-NEXT: Type: Object (0x1) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Processor Specific (0xFF00) +// CHECK-NEXT: } + +// CHECK-NOT: Name: lds3 + +// CHECK: Symbol { +// CHECK: Name: lds4 (39) +// CHECK-NEXT: Value: 0x4 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global (0x1) +// CHECK-NEXT: Type: Object (0x1) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Processor Specific (0xFF00) +// CHECK-NEXT: } Index: tools/llvm-objcopy/ELF/Object.h =================================================================== --- tools/llvm-objcopy/ELF/Object.h +++ tools/llvm-objcopy/ELF/Object.h @@ -591,6 +591,7 @@ SYMBOL_SIMPLE_INDEX = 0, SYMBOL_ABS = ELF::SHN_ABS, SYMBOL_COMMON = ELF::SHN_COMMON, + SYMBOL_AMDGPU_LDS = ELF::SHN_AMDGPU_LDS, SYMBOL_HEXAGON_SCOMMON = ELF::SHN_HEXAGON_SCOMMON, SYMBOL_HEXAGON_SCOMMON_2 = ELF::SHN_HEXAGON_SCOMMON_2, SYMBOL_HEXAGON_SCOMMON_4 = ELF::SHN_HEXAGON_SCOMMON_4, Index: tools/llvm-objcopy/ELF/Object.cpp =================================================================== --- tools/llvm-objcopy/ELF/Object.cpp +++ tools/llvm-objcopy/ELF/Object.cpp @@ -597,6 +597,11 @@ case SHN_COMMON: return true; } + + if (Machine == EM_AMDGPU) { + return Index == SHN_AMDGPU_LDS; + } + if (Machine == EM_HEXAGON) { switch (Index) { case SHN_HEXAGON_SCOMMON: @@ -618,21 +623,17 @@ return SHN_XINDEX; return DefinedIn->Index; } - switch (ShndxType) { - // This means that we don't have a defined section but we do need to - // output a legitimate section index. - case SYMBOL_SIMPLE_INDEX: + + if (ShndxType == SYMBOL_SIMPLE_INDEX) { + // This means that we don't have a defined section but we do need to + // output a legitimate section index. return SHN_UNDEF; - case SYMBOL_ABS: - case SYMBOL_COMMON: - case SYMBOL_HEXAGON_SCOMMON: - case SYMBOL_HEXAGON_SCOMMON_2: - case SYMBOL_HEXAGON_SCOMMON_4: - case SYMBOL_HEXAGON_SCOMMON_8: - case SYMBOL_XINDEX: - return static_cast(ShndxType); } - llvm_unreachable("Symbol with invalid ShndxType encountered"); + + assert(ShndxType == SYMBOL_ABS || ShndxType == SYMBOL_COMMON || + (ShndxType >= ELF::SHN_LOPROC && ShndxType <= ELF::SHN_HIPROC) || + (ShndxType >= ELF::SHN_LOOS && ShndxType <= ELF::SHN_HIOS)); + return static_cast(ShndxType); } bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; }