diff --git a/lld/test/wasm/func-attr-tombstone.s b/lld/test/wasm/func-attr-tombstone.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/func-attr-tombstone.s @@ -0,0 +1,34 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld %t.o -o %t.wasm +# RUN: obj2yaml %t.wasm | FileCheck %s + + .functype foo () -> () + .functype bar () -> () + .functype _start () -> () + .globl foo + .type foo,@function +foo: + .functype foo () -> () + end_function + + .section .text.bar,"",@ + .globl bar + .type bar,@function +bar: + .functype bar () -> () + end_function + + .globl _start + .type _start,@function +_start: + .functype _start () -> () + call foo + end_function + + .section .custom_section.llvm.func_attr.custom0,"",@ + .int32 foo@FUNCINDEX + .int32 bar@FUNCINDEX + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: llvm.func_attr.custom0 +# CHECK-NEXT: Payload: 00000000FFFFFFFF diff --git a/lld/test/wasm/func-attr.s b/lld/test/wasm/func-attr.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/func-attr.s @@ -0,0 +1,35 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld %t.o -o %t.wasm +# RUN: obj2yaml %t.wasm | FileCheck %s + + .functype foo () -> () + .functype bar () -> () + .functype _start () -> () + .globl foo + .type foo,@function +foo: + .functype foo () -> () + end_function + + .section .text.bar,"",@ + .globl bar + .type bar,@function +bar: + .functype bar () -> () + end_function + + .globl _start + .type _start,@function +_start: + .functype _start () -> () + call foo + call bar + end_function + + .section .custom_section.llvm.func_attr.custom0,"",@ + .int32 foo@FUNCINDEX + .int32 bar@FUNCINDEX + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: llvm.func_attr.custom0 +# CHECK-NEXT: Payload: '0000000001000000' diff --git a/lld/test/wasm/merge-func-attr-section.s b/lld/test/wasm/merge-func-attr-section.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/merge-func-attr-section.s @@ -0,0 +1,56 @@ +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %t/a.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %t/b.s -o %t2.o +# RUN: wasm-ld -o %t.wasm %t1.o %t2.o +# RUN: obj2yaml %t.wasm | FileCheck %s + +# Ensure two custom funct_attr sections are concatenated together. + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: llvm.func_attr.custom0 +# CHECK-NEXT: Payload: '000000000100000003000000' + +#--- a.s + +# Function index 3 (after linking) + .functype baz () -> () +# Function index 1 + .functype foo () -> () +# Function index 2 + .functype bar () -> () + .functype _start () -> () + .globl foo + .type foo,@function +foo: + .functype foo () -> () + end_function + + .globl bar + .type bar,@function +bar: + .functype bar () -> () + end_function + + .globl _start + .type _start,@function +_start: + .functype _start () -> () + call foo + call bar + call baz + end_function + + .section .custom_section.llvm.func_attr.custom0,"",@ + .int32 foo@FUNCINDEX + .int32 bar@FUNCINDEX + +#--- b.s + .functype baz () -> () + .globl baz + .type baz,@function +baz: + .functype baz () -> () + end_function + + .section .custom_section.llvm.func_attr.custom0,"",@ + .int32 baz@FUNCINDEX diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -150,6 +150,7 @@ case R_WASM_TABLE_INDEX_I32: case R_WASM_MEMORY_ADDR_I32: case R_WASM_FUNCTION_OFFSET_I32: + case R_WASM_FUNCTION_INDEX_I32: case R_WASM_SECTION_OFFSET_I32: case R_WASM_GLOBAL_INDEX_I32: case R_WASM_MEMORY_ADDR_LOCREL_I32: @@ -518,14 +519,18 @@ // If they occur in DWARF debug symbols, we want to change the pc of the // function to -1 to avoid overlapping with a valid range. However for the // debug_ranges and debug_loc sections that would conflict with the existing - // meaning of -1 so we use -2. - // Returning 0 means there is no tombstone value for this section, and relocation - // will just use the addend. - if (!name.starts_with(".debug_")) - return 0; + // meaning of -1 so we use -2. if (name.equals(".debug_ranges") || name.equals(".debug_loc")) return UINT64_C(-2); - return UINT64_C(-1); + if (name.starts_with(".debug_")) + return UINT64_C(-1); + // If the function occurs in an function attribute section change it to -1 since + // 0 is a valid function index. + if (name.starts_with("llvm.func_attr.")) + return UINT64_C(-1); + // Returning 0 means there is no tombstone value for this section, and relocation + // will just use the addend. + return 0; } } // namespace wasm diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -193,6 +193,7 @@ case R_WASM_TYPE_INDEX_LEB: return typeMap[reloc.Index]; case R_WASM_FUNCTION_INDEX_LEB: + case R_WASM_FUNCTION_INDEX_I32: return getFunctionSymbol(reloc.Index)->getFunctionIndex(); case R_WASM_GLOBAL_INDEX_LEB: case R_WASM_GLOBAL_INDEX_I32: diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -259,7 +259,10 @@ Changes to the WebAssembly Backend ---------------------------------- -* ... +* Function annotations (``__attribute__((annotate()))``) + now generate custom sections in the Wasm output file. A custom section + for each unique name will be created that contains each function + index the annotation applies to. Changes to the Windows Target ----------------------------- diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -28,3 +28,4 @@ WASM_RELOC(R_WASM_MEMORY_ADDR_LOCREL_I32, 23) WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB64, 24) WASM_RELOC(R_WASM_MEMORY_ADDR_TLS_SLEB64, 25) +WASM_RELOC(R_WASM_FUNCTION_INDEX_I32, 26) diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -331,6 +331,7 @@ VK_WASM_MBREL, // Memory address relative to __memory_base VK_WASM_TBREL, // Table index relative to __table_base VK_WASM_GOT_TLS, // Wasm global index of TLS symbol. + VK_WASM_FUNCINDEX, // Wasm function index. VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -362,6 +362,7 @@ case VK_WASM_TLSREL: return "TLSREL"; case VK_WASM_TBREL: return "TBREL"; case VK_WASM_GOT_TLS: return "GOT@TLS"; + case VK_WASM_FUNCINDEX: return "FUNCINDEX"; case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo"; case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi"; case VK_AMDGPU_REL32_LO: return "rel32@lo"; @@ -505,6 +506,7 @@ .Case("mbrel", VK_WASM_MBREL) .Case("tlsrel", VK_WASM_TLSREL) .Case("got@tls", VK_WASM_GOT_TLS) + .Case("funcindex", VK_WASM_FUNCINDEX) .Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO) .Case("gotpcrel32@hi", VK_AMDGPU_GOTPCREL32_HI) .Case("rel32@lo", VK_AMDGPU_REL32_LO) diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -671,6 +671,7 @@ // Provisional value is same as the index return getRelocationIndexValue(RelEntry); case wasm::R_WASM_FUNCTION_INDEX_LEB: + case wasm::R_WASM_FUNCTION_INDEX_I32: case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_I32: case wasm::R_WASM_TAG_INDEX_LEB: @@ -791,6 +792,7 @@ case wasm::R_WASM_TABLE_INDEX_I32: case wasm::R_WASM_MEMORY_ADDR_I32: case wasm::R_WASM_FUNCTION_OFFSET_I32: + case wasm::R_WASM_FUNCTION_INDEX_I32: case wasm::R_WASM_SECTION_OFFSET_I32: case wasm::R_WASM_GLOBAL_INDEX_I32: case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32: diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -948,6 +948,7 @@ Reloc.Index = readVaruint32(Ctx); switch (type) { case wasm::R_WASM_FUNCTION_INDEX_LEB: + case wasm::R_WASM_FUNCTION_INDEX_I32: case wasm::R_WASM_TABLE_INDEX_SLEB: case wasm::R_WASM_TABLE_INDEX_SLEB64: case wasm::R_WASM_TABLE_INDEX_I32: @@ -1045,6 +1046,7 @@ Reloc.Type == wasm::R_WASM_MEMORY_ADDR_LOCREL_I32 || Reloc.Type == wasm::R_WASM_SECTION_OFFSET_I32 || Reloc.Type == wasm::R_WASM_FUNCTION_OFFSET_I32 || + Reloc.Type == wasm::R_WASM_FUNCTION_INDEX_I32 || Reloc.Type == wasm::R_WASM_GLOBAL_INDEX_I32) Size = 4; if (Reloc.Type == wasm::R_WASM_TABLE_INDEX_I64 || diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -91,6 +91,8 @@ return wasm::R_WASM_TYPE_INDEX_LEB; case MCSymbolRefExpr::VK_None: break; + case MCSymbolRefExpr::VK_WASM_FUNCINDEX: + return wasm::R_WASM_FUNCTION_INDEX_I32; default: report_fatal_error("unknown VariantKind"); break; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -66,6 +66,7 @@ void emitEndOfAsmFile(Module &M) override; void EmitProducerInfo(Module &M); void EmitTargetFeatures(Module &M); + void EmitFunctionAttributes(Module &M); void emitSymbolType(const MCSymbolWasm *Sym); void emitGlobalVariable(const GlobalVariable *GV) override; void emitJumpTableInfo() override; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -27,6 +27,8 @@ #include "WebAssemblyTargetMachine.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -438,6 +440,7 @@ EmitProducerInfo(M); EmitTargetFeatures(M); + EmitFunctionAttributes(M); } void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { @@ -556,6 +559,49 @@ OutStreamer->popSection(); } +void WebAssemblyAsmPrinter::EmitFunctionAttributes(Module &M) { + auto V = M.getNamedGlobal("llvm.global.annotations"); + if (!V) + return; + + // Group all the custom attributes by name. + StringMap> CustomSections; + const ConstantArray *CA = cast(V->getOperand(0)); + for (Value *Op : CA->operands()) { + auto *CS = cast(Op); + // The first field is a pointer to the annotated variable. + Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts(); + // Only annotated functions are supported for now. + if (!isa(AnnotatedVar)) + continue; + auto *F = cast(AnnotatedVar); + + // The second field is a pointer to a global annotation string. + auto *GV = cast(CS->getOperand(1)->stripPointerCasts()); + StringRef AnnotationString; + getConstantStringInfo(GV, AnnotationString); + std::string Name = "annotate." + AnnotationString.str(); + auto *Sym = cast(getSymbol(F)); + CustomSections[Name].push_back(Sym); + } + + // Emit a custom section for each unique attribute. + for (const auto &[Name, Symbols] : CustomSections) { + MCSectionWasm *CustomSection = OutContext.getWasmSection( + ".custom_section.llvm.func_attr." + Name, SectionKind::getMetadata()); + OutStreamer->pushSection(); + OutStreamer->switchSection(CustomSection); + + for (auto &Sym : Symbols) { + OutStreamer->emitValue( + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_WASM_FUNCINDEX, + OutContext), + 4); + } + OutStreamer->popSection(); + } +} + void WebAssemblyAsmPrinter::emitConstantPool() { emitDecls(*MMI->getModule()); assert(MF->getConstantPool()->getConstants().empty() && diff --git a/llvm/test/CodeGen/WebAssembly/func-attr-annotate.ll b/llvm/test/CodeGen/WebAssembly/func-attr-annotate.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/func-attr-annotate.ll @@ -0,0 +1,31 @@ +; RUN: llc < %s -asm-verbose=false -wasm-keep-registers | FileCheck %s + +target triple = "wasm32-unknown-unknown" + +@.str = private unnamed_addr constant [8 x i8] c"custom0\00", section "llvm.metadata" +@.str.1 = private unnamed_addr constant [7 x i8] c"main.c\00", section "llvm.metadata" +@.str.2 = private unnamed_addr constant [8 x i8] c"custom1\00", section "llvm.metadata" +@.str.3 = private unnamed_addr constant [8 x i8] c"custom2\00", section "llvm.metadata" +@llvm.global.annotations = appending global [3 x { ptr, ptr, ptr, i32, ptr }] [{ ptr, ptr, ptr, i32, ptr } { ptr @test0, ptr @.str, ptr @.str.1, i32 4, ptr null }, { ptr, ptr, ptr, i32, ptr } { ptr @test1, ptr @.str, ptr @.str.1, i32 5, ptr null }, { ptr, ptr, ptr, i32, ptr } { ptr @test2, ptr @.str.2, ptr @.str.1, i32 6, ptr null }], section "llvm.metadata" + +define void @test0() { + ret void +} + +define void @test1() { + ret void +} + +define void @test2() { + ret void +} + +define void @test3() { + ret void +} + +; CHECK: .section .custom_section.llvm.func_attr.annotate.custom0,"",@ +; CHECK-NEXT: .int32 test0@FUNCINDEX +; CHECK-NEXT: .int32 test1@FUNCINDEX +; CHECK: .section .custom_section.llvm.func_attr.annotate.custom1,"",@ +; CHECK-NEXT: .int32 test2@FUNCINDEX diff --git a/llvm/test/MC/WebAssembly/func-attr.s b/llvm/test/MC/WebAssembly/func-attr.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/func-attr.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s +# Check that it also comiled to object for format. +# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o - < %s | obj2yaml | FileCheck -check-prefix=CHECK-OBJ %s + +foo: + .globl foo + .functype foo () -> () + end_function + + .section .custom_section.llvm.func_attr.custom0,"",@ + .int32 foo@FUNCINDEX + +# CHECK: .section .custom_section.llvm.func_attr.custom0,"",@ +# CHECK-NEXT: .int32 foo@FUNCINDEX + +# CHECK-OBJ: - Type: CUSTOM +# CHECK-OBJ-NEXT: Relocations: +# CHECK-OBJ-NEXT: - Type: R_WASM_FUNCTION_INDEX_I32 +# CHECK-OBJ-NEXT: Index: 0 +# CHECK-OBJ-NEXT: Offset: 0x0 +# CHECK-OBJ-NEXT: Name: llvm.func_attr.custom0