LowerTypeTests: Mark CFI jump table sections as eligible for relaxation.

Use !elf_section_properties metadata to set the type and entry size to
the correct values, and set the preferred alignment to the entry size
to enable last jump table entry placement.

Reviewers: vitalybuka, MaskRay

Reviewed By: vitalybuka, MaskRay

Pull Request: https://github.com/llvm/llvm-project/pull/149261
This commit is contained in:
Peter Collingbourne authored and GitHub committed 2026-06-08 09:55:27 -07:00
1 parent 88bd366041
commit 081568eb66
12 files changed
+44 -26

No files matched your search

+2 -2
View File
@@ -63,12 +63,12 @@ int main(int argc, char **argv) {
switch (argv[1][0]) {
case 'a':
// A: runtime error: control flow integrity check for type 'int (S::*)()' failed during non-virtual pointer to member function call
// A: note: S::f1() {{(\(.cfi_jt\) )?}}defined here
// A: note: S::f1() {{.*}}defined here
(s.*bitcast<S_int>(&S::f1))();
break;
case 'b':
// B: runtime error: control flow integrity check for type 'int (T::*)()' failed during non-virtual pointer to member function call
// B: note: S::f2() {{(\(.cfi_jt\) )?}}defined here
// B: note: S::f2() {{.*}}defined here
(t.*bitcast<T_int>(&S::f2))();
break;
case 'c':
+20 -5
View File
@@ -31,6 +31,7 @@
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/TypeMetadataUtils.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
@@ -492,6 +493,10 @@ class LowerTypeTestsModule {
GlobalVariable *GlobalAnnotation;
DenseSet<Value *> FunctionAnnotations;
// Cross-DSO CFI emits jumptable entries for exported functions as well as
// address taken functions in case they are address taken in other modules.
bool CrossDsoCfi = M.getModuleFlag("Cross-DSO CFI") != nullptr;
bool shouldExportConstantsAsAbsoluteSymbols();
uint8_t *exportTypeId(StringRef TypeId, const TypeIdLowering &TIL);
TypeIdLowering importTypeId(StringRef TypeId);
@@ -1584,6 +1589,20 @@ createJumpTableDebugInfo(Function *F, ArrayRef<GlobalTypeMember *> Functions) {
void LowerTypeTestsModule::createJumpTable(
Function *F, ArrayRef<GlobalTypeMember *> Functions,
Triple::ArchType JumpTableArch) {
unsigned JumpTableEntrySize = getJumpTableEntrySize(JumpTableArch);
// Give the jumptable section this type in order to enable jumptable
// relaxation. Only do this if cross-DSO CFI is disabled because jumptable
// relaxation violates cross-DSO CFI's restrictions on the ordering of the
// jumptable relative to other sections.
if (!CrossDsoCfi)
F->setMetadata(LLVMContext::MD_elf_section_properties,
MDNode::get(F->getContext(),
ArrayRef<Metadata *>{
ConstantAsMetadata::get(ConstantInt::get(
Int64Ty, ELF::SHT_LLVM_CFI_JUMP_TABLE)),
ConstantAsMetadata::get(ConstantInt::get(
Int64Ty, JumpTableEntrySize))}));
BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F);
IRBuilder<> IRB(BB);
@@ -1611,7 +1630,7 @@ void LowerTypeTestsModule::createJumpTable(
IRB.CreateUnreachable();
// Align the whole table by entry size.
F->setAlignment(Align(getJumpTableEntrySize(JumpTableArch)));
F->setPreferredAlignment(Align(JumpTableEntrySize));
F->addFnAttr(Attribute::Naked);
if (JumpTableArch == Triple::arm)
F->addFnAttr("target-features", "-thumb-mode");
@@ -2168,10 +2187,6 @@ bool LowerTypeTestsModule::lower() {
unsigned CurUniqueId = 0;
SmallVector<MDNode *, 2> Types;
// Cross-DSO CFI emits jumptable entries for exported functions as well as
// address taken functions in case they are address taken in other modules.
const bool CrossDsoCfi = M.getModuleFlag("Cross-DSO CFI") != nullptr;
struct ExportedFunctionInfo {
CfiFunctionLinkage Linkage;
MDNode *FuncMD; // {name, linkage, type[, type...]}
@@ -153,7 +153,7 @@ attributes #6 = { noreturn nounwind }
;
; CHECK: Function Attrs: naked nocf_check noinline nounwind
; CHECK-LABEL: define internal void @_Z9nothrow_ei.cfi_jt
; CHECK-SAME: () #[[ATTR5:[0-9]+]] align 8 {
; CHECK-SAME: () #[[ATTR5:[0-9]+]] prefalign(8) {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr nonnull @_Z9nothrow_ei) #[[ATTR7:[0-9]+]]
; CHECK-NEXT: unreachable
@@ -221,7 +221,7 @@ attributes #8 = { noreturn nounwind }
;
; CHECK: Function Attrs: naked nocf_check noinline
; CHECK-LABEL: define internal void @_Z7throw_ei.cfi_jt(
; CHECK-SAME: ) #[[ATTR5:[0-9]+]] align 8 {
; CHECK-SAME: ) #[[ATTR5:[0-9]+]] prefalign(8) {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr nonnull @_Z7throw_ei) #[[ATTR6]]
; CHECK-NEXT: unreachable
@@ -33,14 +33,14 @@ define void @addrtaken() {
!0 = !{i32 0, !"typeid1"}
!1 = !{i32 0, !"typeid2"}
; CHECK: define private void {{.*}} #[[AT:.*]] align 4 {
; CHECK: define private void {{.*}} #[[AT:.*]] prefalign(4)
; CHECK-NEXT: entry:
; CHECK-NEXT: call void asm sideeffect "b.w $0\0A", "s"(ptr @f1.cfi)
; CHECK-NEXT: call void asm sideeffect "b.w $0\0A", "s"(ptr @g1.cfi)
; CHECK-NEXT: unreachable
; CHECK-NEXT: }
; CHECK: define private void {{.*}} #[[AA:.*]] align 4 {
; CHECK: define private void {{.*}} #[[AA:.*]] prefalign(4)
; CHECK-NEXT: entry:
; CHECK-NEXT: call void asm sideeffect "b $0\0A", "s"(ptr @f2.cfi)
; CHECK-NEXT: call void asm sideeffect "b $0\0A", "s"(ptr @g2.cfi)
@@ -39,10 +39,10 @@ define i1 @foo(ptr %p) {
ret i1 %z
}
; X64: define private void @[[JT1]]() #{{.*}} align 8 {
; X64: define private void @[[JT1]]() #{{.*}} prefalign(8)
; X64: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g.cfi)
; X64: define private void @[[JT0]]() #{{.*}} align 8 {
; X64: define private void @[[JT0]]() #{{.*}} prefalign(8)
; X64: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f.cfi)
; WASM32: ![[I1]] = !{i64 2}
@@ -37,5 +37,5 @@ declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) nounwind readnone
!0 = !{i64 0, !"type1"}
!1 = !{i64 0, !"type2"}
; X64: define private void @[[JT]]() #{{.*}} align {{.*}} {
; X64: define private void @[[JT]]() #{{.*}} prefalign({{.*}}) {{.*}} {
; X64: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @foo1)
@@ -134,11 +134,11 @@ define i1 @foo(ptr %p) {
ret i1 %x
}
; X86: define private void @[[JT]]() #{{.*}} align 8 {
; ARM: define private void @[[JT]]() #{{.*}} align 4 {
; RISCV: define private void @[[JT]]() #{{.*}} align 8 {
; LOONGARCH64: define private void @[[JT]]() #{{.*}} align 8 {
; HEXAGON: define private void @[[JT]]() #{{.*}} align 4 {
; X86: define private void @[[JT]]() #{{.*}} prefalign(8)
; ARM: define private void @[[JT]]() #{{.*}} prefalign(4)
; RISCV: define private void @[[JT]]() #{{.*}} prefalign(8)
; LOONGARCH64: define private void @[[JT]]() #{{.*}} prefalign(8)
; HEXAGON: define private void @[[JT]]() #{{.*}} prefalign(4)
; CHECK-LABEL: define internal void @__cfi_global_var_init() section ".text.startup" {
; CHECK-NEXT: entry:
@@ -167,4 +167,4 @@ define i1 @foo(ptr %p) {
; CHECK-NEXT: }
!0 = !{i32 0, !"typeid1"}
; CHECK: ![[SELPROF]] = !{!"unknown", !"lowertypetests"}
; CHECK: ![[SELPROF]] = !{!"unknown", !"lowertypetests"}
@@ -63,9 +63,9 @@ define i1 @foo(ptr %p) {
ret i1 %x
}
; JT4: define private void @[[JT]]() #[[ATTR:.*]] align 4 {
; JT8: define private void @[[JT]]() #[[ATTR:.*]] align 8 {
; JT16: define private void @[[JT]]() #[[ATTR:.*]] align 16 {
; JT4: define private void @[[JT]]() #[[ATTR:.*]] prefalign(4) !elf_section_properties ![[PROP:[0-9]*]] {
; JT8: define private void @[[JT]]() #[[ATTR:.*]] prefalign(8) !elf_section_properties ![[PROP:[0-9]*]] {
; JT16: define private void @[[JT]]() #[[ATTR:.*]] prefalign(16) !elf_section_properties ![[PROP:[0-9]*]] {
; X86: jmp ${0:c}@plt
; X86-SAME: int3
@@ -127,5 +127,9 @@ define i1 @foo(ptr %p) {
; LOONGARCH64: attributes #[[ATTR]] = { naked noinline }
; HEXAGON: attributes #[[ATTR]] = { naked noinline }
; JT4: ![[PROP]] = !{i64 1879002126, i64 4}
; JT8: ![[PROP]] = !{i64 1879002126, i64 8}
; JT16: ![[PROP]] = !{i64 1879002126, i64 16}
; WASM32: ![[I0]] = !{i64 1}
; WASM32: ![[I1]] = !{i64 2}
@@ -19,7 +19,7 @@ entry:
ret i1 %0
}
; CHECK: define private void @[[JT]]() #{{.*}} align {{.*}} {
; CHECK: define private void @[[JT]]() #{{.*}} prefalign({{.*}}) {{.*}} {
declare i1 @llvm.type.test(ptr, metadata) nounwind readnone
@@ -24,7 +24,6 @@ define i1 @foo(ptr %p) {
!0 = !{i32 0, !"typeid1"}
!1 = !{i32 8, !"cf-protection-branch", i32 1}
;.
; X86_32: @[[GLOB0:[0-9]+]] = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16
; X86_32: @[[GLOB1:[0-9]+]] = private constant [0 x i8] zeroinitializer
@@ -201,7 +201,7 @@ attributes #3 = { noreturn nounwind }
;
;
; LOWERTYPETESTS-LABEL: define private void @.cfi.jumptable
; LOWERTYPETESTS-SAME: () #[[ATTR3:[0-9]+]] align 8 {
; LOWERTYPETESTS-SAME: () #[[ATTR3:[0-9]+]] prefalign(8) !elf_section_properties {{.*}} {
; LOWERTYPETESTS-NEXT: entry:
; LOWERTYPETESTS-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f)
; LOWERTYPETESTS-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_thunk)