Skip to content

Commit

Permalink
[cfi] Avoid branch veneers in jump tables when possible.
Browse files Browse the repository at this point in the history
Summary:
When jumptable encoding does not match target code encoding (arm vs
thumb), a veneer is inserted by the linker. We can not avoid this
in all cases, because entries within one jumptable must have the same
encoding, but we can make it less common by selecting the jumptable
encoding to match the majority of its targets.

This change only covers FullLTO, and not ThinLTO.

Reviewers: pcc

Subscribers: aemerson, mehdi_amini, javed.absar, kristof.beyls, llvm-commits, hiraditya

Differential Revision: https://reviews.llvm.org/D37171

llvm-svn: 312054
  • Loading branch information
eugenis committed Aug 29, 2017
1 parent 900b633 commit 7372b67
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 8 deletions.
64 changes: 56 additions & 8 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class LowerTypeTestsModule {
unsigned getJumpTableEntrySize();
Type *getJumpTableEntryType();
void createJumpTableEntry(raw_ostream &AsmOS, raw_ostream &ConstraintOS,
Triple::ArchType JumpTableArch,
SmallVectorImpl<Value *> &AsmArgs, Function *Dest);
void verifyTypeMDNode(GlobalObject *GO, MDNode *Type);
void buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds,
Expand Down Expand Up @@ -983,15 +984,16 @@ unsigned LowerTypeTestsModule::getJumpTableEntrySize() {
// constraints and arguments to AsmOS, ConstraintOS and AsmArgs.
void LowerTypeTestsModule::createJumpTableEntry(
raw_ostream &AsmOS, raw_ostream &ConstraintOS,
SmallVectorImpl<Value *> &AsmArgs, Function *Dest) {
Triple::ArchType JumpTableArch, SmallVectorImpl<Value *> &AsmArgs,
Function *Dest) {
unsigned ArgIndex = AsmArgs.size();

if (Arch == Triple::x86 || Arch == Triple::x86_64) {
if (JumpTableArch == Triple::x86 || JumpTableArch == Triple::x86_64) {
AsmOS << "jmp ${" << ArgIndex << ":c}@plt\n";
AsmOS << "int3\nint3\nint3\n";
} else if (Arch == Triple::arm || Arch == Triple::aarch64) {
} else if (JumpTableArch == Triple::arm || JumpTableArch == Triple::aarch64) {
AsmOS << "b $" << ArgIndex << "\n";
} else if (Arch == Triple::thumb) {
} else if (JumpTableArch == Triple::thumb) {
AsmOS << "b.w $" << ArgIndex << "\n";
} else {
report_fatal_error("Unsupported architecture for jump tables");
Expand Down Expand Up @@ -1078,15 +1080,57 @@ void LowerTypeTestsModule::replaceWeakDeclarationWithJumpTablePtr(
PlaceholderFn->eraseFromParent();
}

static bool isThumbFunction(Function *F, Triple::ArchType ModuleArch) {
Attribute TFAttr = F->getFnAttribute("target-features");
if (!TFAttr.hasAttribute(Attribute::None)) {
SmallVector<StringRef, 6> Features;
TFAttr.getValueAsString().split(Features, ',');
for (StringRef Feature : Features) {
if (Feature == "-thumb-mode")
return false;
else if (Feature == "+thumb-mode")
return true;
}
}

return ModuleArch == Triple::thumb;
}

// Each jump table must be either ARM or Thumb as a whole for the bit-test math
// to work. Pick one that matches the majority of members to minimize interop
// veneers inserted by the linker.
static Triple::ArchType
selectJumpTableArmEncoding(ArrayRef<GlobalTypeMember *> Functions,
Triple::ArchType ModuleArch) {
if (ModuleArch != Triple::arm && ModuleArch != Triple::thumb)
return ModuleArch;

unsigned ArmCount = 0, ThumbCount = 0;
for (const auto GTM : Functions) {
if (!GTM->isDefinition()) {
// PLT stubs are always ARM.
++ArmCount;
continue;
}

Function *F = cast<Function>(GTM->getGlobal());
++(isThumbFunction(F, ModuleArch) ? ThumbCount : ArmCount);
}

return ArmCount > ThumbCount ? Triple::arm : Triple::thumb;
}

void LowerTypeTestsModule::createJumpTable(
Function *F, ArrayRef<GlobalTypeMember *> Functions) {
std::string AsmStr, ConstraintStr;
raw_string_ostream AsmOS(AsmStr), ConstraintOS(ConstraintStr);
SmallVector<Value *, 16> AsmArgs;
AsmArgs.reserve(Functions.size() * 2);

Triple::ArchType JumpTableArch = selectJumpTableArmEncoding(Functions, Arch);

for (unsigned I = 0; I != Functions.size(); ++I)
createJumpTableEntry(AsmOS, ConstraintOS, AsmArgs,
createJumpTableEntry(AsmOS, ConstraintOS, JumpTableArch, AsmArgs,
cast<Function>(Functions[I]->getGlobal()));

// Try to emit the jump table at the end of the text segment.
Expand All @@ -1103,10 +1147,14 @@ void LowerTypeTestsModule::createJumpTable(
// attribute.
if (OS != Triple::Win32)
F->addFnAttr(llvm::Attribute::Naked);
// Thumb jump table assembly needs Thumb2. The following attribute is added by
// Clang for -march=armv7.
if (Arch == Triple::thumb)
if (JumpTableArch == Triple::arm)
F->addFnAttr("target-features", "-thumb-mode");
if (JumpTableArch == Triple::thumb) {
F->addFnAttr("target-features", "+thumb-mode");
// Thumb jump table assembly needs Thumb2. The following attribute is added
// by Clang for -march=armv7.
F->addFnAttr("target-cpu", "cortex-a8");
}

BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F);
IRBuilder<> IRB(BB);
Expand Down
41 changes: 41 additions & 0 deletions llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
; RUN: opt -S -mtriple=arm-unknown-linux-gnu -lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t < %s | FileCheck %s

target datalayout = "e-p:64:64"

define void @f1() "target-features"="+thumb-mode" !type !0 {
ret void
}

define void @g1() "target-features"="-thumb-mode" !type !0 {
ret void
}

define void @f2() "target-features"="+thumb-mode" !type !1 {
ret void
}

define void @g2() "target-features"="-thumb-mode" !type !1 {
ret void
}

define void @h2() "target-features"="-thumb-mode" !type !1 {
ret void
}

!0 = !{i32 0, !"typeid1"}
!1 = !{i32 0, !"typeid2"}

; CHECK: define private void {{.*}} #[[AT:.*]] section ".text.cfi" align 4 {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void asm sideeffect "b.w $0\0Ab.w $1\0A", "s,s"(void ()* @f1.cfi, void ()* @g1.cfi)
; CHECK-NEXT: unreachable
; CHECK-NEXT: }

; CHECK: define private void {{.*}} #[[AA:.*]] section ".text.cfi" align 4 {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void asm sideeffect "b $0\0Ab $1\0Ab $2\0A", "s,s,s"(void ()* @f2.cfi, void ()* @g2.cfi, void ()* @h2.cfi)
; CHECK-NEXT: unreachable
; CHECK-NEXT: }

; CHECK-DAG: attributes #[[AA]] = { naked "target-features"="-thumb-mode" }
; CHECK-DAG: attributes #[[AT]] = { naked "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }

0 comments on commit 7372b67

Please sign in to comment.