diff --git a/llvm/docs/PCSectionsMetadata.rst b/llvm/docs/PCSectionsMetadata.rst new file mode 100644 --- /dev/null +++ b/llvm/docs/PCSectionsMetadata.rst @@ -0,0 +1,54 @@ +========================= +LLVM PC Sections Metadata +========================= + +.. contents:: + :local: + +Introduction +============ + +PC Sections Metadata can be attached to instructions and functions, for which +addresses, viz. program counters (PC), are to be emitted in specially encoded +binary sections. Metadata is assigned as an ``MDNode`` of the ``MD_pcsections`` +kind; the following section describes the metadata format. + +Metadata Format +=============== + +An arbitrary number of interleaved ``MDString`` and constant operators can be +added, where a new ``MDString`` always denotes a section name, followed by an +arbitrary number of auxiliary constant data encoded along the PC of the +instruction or function. The first operator must be a ``MDString`` denoting the +first section. + +.. code-block:: none + + !0 = metadata !{ + metadata !"" + [ , iXX ... ] + [ metadata !" + [ , iXX ... ] + ... ] + } + +The occurrence of "section#1", "section#2", ..., "section#N" in the metadata +causes the backend to emit the PC for the associated instruction or function to +be emitted to all named sections. For each emitted PC in a section #N, the +constants "aux-consts#N" will be emitted after the PC. + +Binary Encoding +=============== + +*Instructions* result in emitting a single PC, and *functions* result in +emission of the start of the function and a 32-bit size. This is followed by +the auxiliary constants that followed the respective section name in the +``MD_pcsections`` metadata. + +To avoid relocations in the final binary, each PC address stored at ``entry`` +is a relative relocation, computed as ``pc - entry``. To decode, a user has to +compute ``entry + *entry``. + +The size of each entry depends on the code model. With large and medium sized +code models, the entry size matches pointer size. For any smaller code model +the entry size is just 32 bits. diff --git a/llvm/docs/Reference.rst b/llvm/docs/Reference.rst --- a/llvm/docs/Reference.rst +++ b/llvm/docs/Reference.rst @@ -33,6 +33,7 @@ MarkedUpDisassembly MIRLangRef OptBisect + PCSectionsMetadata PDB/index PointerAuth ScudoHardenedAllocator diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -47,3 +47,4 @@ LLVM_FIXED_MD_KIND(MD_exclude, "exclude", 33) LLVM_FIXED_MD_KIND(MD_memprof, "memprof", 34) LLVM_FIXED_MD_KIND(MD_callsite, "callsite", 35) +LLVM_FIXED_MD_KIND(MD_pcsections, "pcsections", 36) diff --git a/llvm/include/llvm/IR/MDBuilder.h b/llvm/include/llvm/IR/MDBuilder.h --- a/llvm/include/llvm/IR/MDBuilder.h +++ b/llvm/include/llvm/IR/MDBuilder.h @@ -112,6 +112,14 @@ /// prologue for the "function" santizier. MDNode *createRTTIPointerPrologue(Constant *PrologueSig, Constant *RTTI); + //===------------------------------------------------------------------===// + // PC sections metadata. + //===------------------------------------------------------------------===// + + /// Return metadata for PC sections. + MDNode *createPCSections(ArrayRef Sections, + ArrayRef> AuxData); + //===------------------------------------------------------------------===// // AA metadata. //===------------------------------------------------------------------===// diff --git a/llvm/lib/IR/MDBuilder.cpp b/llvm/lib/IR/MDBuilder.cpp --- a/llvm/lib/IR/MDBuilder.cpp +++ b/llvm/lib/IR/MDBuilder.cpp @@ -158,6 +158,28 @@ return MDNode::get(Context, Ops); } +MDNode * +MDBuilder::createPCSections(ArrayRef Sections, + ArrayRef> AuxData) { + SmallVector Ops; + + for (size_t i = 0; i < Sections.size(); i++) { + const StringRef &S = Sections[i]; + Ops.push_back(createString(S)); + + // If auxiliary data for this section exists, append it. + if (i < AuxData.size()) { + const ArrayRef &AuxConsts = AuxData[i]; + for (Constant *C : AuxConsts) { + if (C) + Ops.push_back(createConstant(C)); + } + } + } + + return MDNode::get(Context, Ops); +} + MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) { SmallVector Args(1, nullptr); if (Extra) diff --git a/llvm/test/CodeGen/AArch64/pcsections.ll b/llvm/test/CodeGen/AArch64/pcsections.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/pcsections.ll @@ -0,0 +1,115 @@ +; BROKEN: llc -O0 < %s | FileCheck %s --check-prefixes=CHECK,CHECK-UNOPT,DEFCM +; BROKEN: llc -O1 < %s | FileCheck %s --check-prefixes=CHECK,CHECK-OPT,DEFCM +; BROKEN: llc -O2 < %s | FileCheck %s --check-prefixes=CHECK,CHECK-OPT,DEFCM +; BROKEN: llc -O3 < %s | FileCheck %s --check-prefixes=CHECK,CHECK-OPT,DEFCM +; BROKEN: llc -O1 -code-model=large < %s | FileCheck %s --check-prefixes=CHECK,CHECK-OPT,LARGE + +target triple = "aarch64-unknown-linux-gnu" + +@foo = dso_local global i64 0, align 8 +@bar = dso_local global i64 0, align 8 + +define i64 @multiple() !pcsections !0 { +; CHECK-LABEL: multiple: +; CHECK: .Lfunc_begin0: +; CHECK: // %bb.0: // %entry +; CHECK: .Lpcsection0: +; CHECK-NEXT: ldr +; CHECK-NEXT: ret +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base0: +; DEFCM-NEXT: .word .Lfunc_begin0-.Lpcsection_base0 +; LARGE-NEXT: .xword .Lfunc_begin0-.Lpcsection_base0 +; CHECK-NEXT: .word .Lfunc_end0-.Lfunc_begin0 +; CHECK-NEXT: .section section_aux_42,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base1: +; DEFCM-NEXT: .word .Lpcsection0-.Lpcsection_base1 +; LARGE-NEXT: .xword .Lpcsection0-.Lpcsection_base1 +; CHECK-NEXT: .word 42 +; CHECK-NEXT: .section section_aux_21264,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base2: +; DEFCM-NEXT: .word .Lpcsection0-.Lpcsection_base2 +; LARGE-NEXT: .xword .Lpcsection0-.Lpcsection_base2 +; CHECK-NEXT: .word 21264 +; CHECK-NEXT: .text +entry: + %0 = load i64, i64* @bar, align 8, !pcsections !1 + ret i64 %0 +} + +define i64 @test_simple_atomic() { +; CHECK-LABEL: test_simple_atomic: +; CHECK: .Lpcsection1: +; CHECK-NEXT: ldr +; CHECK-NOT: .Lpcsection2 +; CHECK: ldr +; CHECK: add +; CHECK-NEXT: ret +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base3: +; DEFCM-NEXT: .word .Lpcsection1-.Lpcsection_base3 +; LARGE-NEXT: .xword .Lpcsection1-.Lpcsection_base3 +; CHECK-NEXT: .text +entry: + %0 = load atomic i64, i64* @foo monotonic, align 8, !pcsections !0 + %1 = load i64, i64* @bar, align 8 + %add = add nsw i64 %1, %0 + ret i64 %add +} + +define i64 @test_complex_atomic() { +; CHECK-LABEL: test_complex_atomic: +; --- +; CHECK-OPT: .Lpcsection2: +; CHECK-OPT-NEXT: ldxr +; CHECK-OPT: .Lpcsection3: +; CHECK-OPT-NEXT: add +; CHECK-OPT: .Lpcsection4: +; CHECK-OPT-NEXT: stxr +; CHECK-OPT: .Lpcsection5: +; CHECK-OPT-NEXT: cbnz +; --- +; CHECK-UNOPT: .Lpcsection2: +; CHECK-UNOPT-NEXT: ldr +; CHECK-UNOPT: .Lpcsection4: +; CHECK-UNOPT-NEXT: add +; CHECK-UNOPT: .Lpcsection5: +; CHECK-UNOPT-NEXT: ldaxr +; CHECK-UNOPT: .Lpcsection6: +; CHECK-UNOPT-NEXT: cmp +; CHECK-UNOPT: .Lpcsection8: +; CHECK-UNOPT-NEXT: stlxr +; CHECK-UNOPT: .Lpcsection9: +; CHECK-UNOPT-NEXT: cbnz +; CHECK-UNOPT: .Lpcsection12: +; CHECK-UNOPT-NEXT: b +; --- +; CHECK-NOT: .Lpcsection +; CHECK: ldr +; CHECK: ret +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base4: +; DEFCM-NEXT: .word .Lpcsection2-.Lpcsection_base4 +; LARGE-NEXT: .xword .Lpcsection2-.Lpcsection_base4 +; CHECK-NEXT: .Lpcsection_base5: +; DEFCM-NEXT: .word .Lpcsection3-.Lpcsection_base5 +; LARGE-NEXT: .xword .Lpcsection3-.Lpcsection_base5 +; CHECK-NEXT: .Lpcsection_base6: +; DEFCM-NEXT: .word .Lpcsection4-.Lpcsection_base6 +; LARGE-NEXT: .xword .Lpcsection4-.Lpcsection_base6 +; CHECK-NEXT: .Lpcsection_base7: +; DEFCM-NEXT: .word .Lpcsection5-.Lpcsection_base7 +; LARGE-NEXT: .xword .Lpcsection5-.Lpcsection_base7 +; CHECK-UNOPT: .word .Lpcsection12-.Lpcsection_base14 +; CHECK-NEXT: .text +entry: + %0 = atomicrmw add i64* @foo, i64 1 monotonic, align 8, !pcsections !0 + %1 = load i64, i64* @bar, align 8 + %inc = add nsw i64 %1, 1 + store i64 %inc, i64* @bar, align 8 + %add = add nsw i64 %1, %0 + ret i64 %add +} + +!0 = !{!"section_no_aux"} +!1 = !{!"section_aux_42", i32 42, !"section_aux_21264", i32 21264} diff --git a/llvm/test/CodeGen/X86/pcsections.ll b/llvm/test/CodeGen/X86/pcsections.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/pcsections.ll @@ -0,0 +1,122 @@ +; BROKEN: llc -O0 < %s | FileCheck %s --check-prefixes=CHECK,DEFCM +; BROKEN: llc -O1 < %s | FileCheck %s --check-prefixes=CHECK,DEFCM +; BROKEN: llc -O2 < %s | FileCheck %s --check-prefixes=CHECK,DEFCM +; BROKEN: llc -O3 < %s | FileCheck %s --check-prefixes=CHECK,DEFCM +; BROKEN: llc -O1 -code-model=large < %s | FileCheck %s --check-prefixes=CHECK,LARGE + +target triple = "x86_64-unknown-linux-gnu" + +@foo = dso_local global i64 0, align 8 +@bar = dso_local global i64 0, align 8 + +define void @empty_no_aux() !pcsections !0 { +; CHECK-LABEL: empty_no_aux: +; CHECK-NEXT: .Lfunc_begin0 +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: retq +; CHECK-NEXT: .Lfunc_end0: +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base0: +; DEFCM-NEXT: .long .Lfunc_begin0-.Lpcsection_base0 +; LARGE-NEXT: .quad .Lfunc_begin0-.Lpcsection_base0 +; CHECK-NEXT: .long .Lfunc_end0-.Lfunc_begin0 +; CHECK-NEXT: .text +entry: + ret void +} + +define void @empty_aux() !pcsections !1 { +; CHECK-LABEL: empty_aux: +; CHECK-NEXT: .Lfunc_begin1 +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: retq +; CHECK-NEXT: .Lfunc_end1: +; CHECK: .section section_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base1: +; DEFCM-NEXT: .long .Lfunc_begin1-.Lpcsection_base1 +; LARGE-NEXT: .quad .Lfunc_begin1-.Lpcsection_base1 +; CHECK-NEXT: .long .Lfunc_end1-.Lfunc_begin1 +; CHECK-NEXT: .long 10 +; CHECK-NEXT: .long 20 +; CHECK-NEXT: .long 30 +; CHECK-NEXT: .text +entry: + ret void +} + +define i64 @multiple() !pcsections !0 { +; CHECK-LABEL: multiple: +; CHECK-NEXT: .Lfunc_begin2 +; CHECK: # %bb.0: # %entry +; CHECK: .Lpcsection0: +; CHECK-NEXT: movq +; CHECK-NEXT: retq +; CHECK-NEXT: .Lfunc_end2: +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base2: +; DEFCM-NEXT: .long .Lfunc_begin2-.Lpcsection_base2 +; LARGE-NEXT: .quad .Lfunc_begin2-.Lpcsection_base2 +; CHECK-NEXT: .long .Lfunc_end2-.Lfunc_begin2 +; CHECK-NEXT: .section section_aux_42,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base3: +; DEFCM-NEXT: .long .Lpcsection0-.Lpcsection_base3 +; LARGE-NEXT: .quad .Lpcsection0-.Lpcsection_base3 +; CHECK-NEXT: .long 42 +; CHECK-NEXT: .section section_aux_21264,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base4: +; DEFCM-NEXT: .long .Lpcsection0-.Lpcsection_base4 +; LARGE-NEXT: .quad .Lpcsection0-.Lpcsection_base4 +; CHECK-NEXT: .long 21264 +; CHECK-NEXT: .text +entry: + %0 = load i64, i64* @bar, align 8, !pcsections !2 + ret i64 %0 +} + +define i64 @test_simple_atomic() { +; CHECK-LABEL: test_simple_atomic: +; CHECK: .Lpcsection1: +; CHECK-NEXT: movq +; CHECK-NOT: .Lpcsection +; CHECK: addq +; CHECK-NEXT: retq +; CHECK-NEXT: .Lfunc_end3: +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base5: +; DEFCM-NEXT: .long .Lpcsection1-.Lpcsection_base5 +; LARGE-NEXT: .quad .Lpcsection1-.Lpcsection_base5 +; CHECK-NEXT: .text +entry: + %0 = load atomic i64, i64* @foo monotonic, align 8, !pcsections !0 + %1 = load i64, i64* @bar, align 8 + %add = add nsw i64 %1, %0 + ret i64 %add +} + +define i64 @test_complex_atomic() { +; CHECK-LABEL: test_complex_atomic: +; CHECK: movl $1 +; CHECK-NEXT: .Lpcsection2: +; CHECK-NEXT: lock xaddq +; CHECK-NOT: .Lpcsection +; CHECK: movq +; CHECK: addq +; CHECK: retq +; CHECK-NEXT: .Lfunc_end4: +; CHECK: .section section_no_aux,"awo",@progbits,.text +; CHECK-NEXT: .Lpcsection_base6: +; DEFCM-NEXT: .long .Lpcsection2-.Lpcsection_base6 +; LARGE-NEXT: .quad .Lpcsection2-.Lpcsection_base6 +; CHECK-NEXT: .text +entry: + %0 = atomicrmw add i64* @foo, i64 1 monotonic, align 8, !pcsections !0 + %1 = load i64, i64* @bar, align 8 + %inc = add nsw i64 %1, 1 + store i64 %inc, i64* @bar, align 8 + %add = add nsw i64 %1, %0 + ret i64 %add +} + +!0 = !{!"section_no_aux"} +!1 = !{!"section_aux", i32 10, i32 20, i32 30} +!2 = !{!"section_aux_42", i32 42, !"section_aux_21264", i32 21264}