Skip to content

Commit

Permalink
[BOLT][AArch64] Fix instrumentation deadloop
Browse files Browse the repository at this point in the history
According to ARMv8-a architecture reference manual B2.10.5 software
must avoid having any explicit memory accesses between exclusive load
and associated store instruction. Otherwise exclusive monitor might
clear the exclusivity without application-related cause which may
result in the deadloop. Disable instrumentation for such functions,
since between exclusive load and store there might be branches and we
would insert instrumentation snippet which contains loads and stores.

The better solution would be to analyze with BFS finding the exact BBs
between load and store and not instrumenting them. Or even better to
recognize such sequences and replace them with more complex one, e.g.
loading value non exclusively, and for the brach where exclusive store
is made make exclusive load and store sequentially, but for now just
disable instrumentation for such functions completely.

Differential Revision: https://reviews.llvm.org/D159520
  • Loading branch information
yota9 committed Sep 21, 2023
1 parent 2aa4a32 commit 846eb76
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 0 deletions.
5 changes: 5 additions & 0 deletions bolt/include/bolt/Core/MCPlusBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,11 @@ class MCPlusBuilder {
return Info->get(Inst.getOpcode()).mayStore();
}

virtual bool isAArch64Exclusive(const MCInst &Inst) const {
llvm_unreachable("not implemented");
return false;
}

virtual bool isCleanRegXOR(const MCInst &Inst) const {
llvm_unreachable("not implemented");
return false;
Expand Down
22 changes: 22 additions & 0 deletions bolt/lib/Passes/Instrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "bolt/Passes/Instrumentation.h"
#include "bolt/Core/ParallelUtilities.h"
#include "bolt/RuntimeLibs/InstrumentationRuntimeLibrary.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "bolt/Utils/Utils.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/RWMutex.h"
Expand Down Expand Up @@ -85,6 +86,24 @@ cl::opt<bool> InstrumentCalls("instrument-calls",
namespace llvm {
namespace bolt {

static bool hasAArch64ExclusiveMemop(BinaryFunction &Function) {
// FIXME ARMv8-a architecture reference manual says that software must avoid
// having any explicit memory accesses between exclusive load and associated
// store instruction. So for now skip instrumentation for functions that have
// these instructions, since it might lead to runtime deadlock.
BinaryContext &BC = Function.getBinaryContext();
for (const BinaryBasicBlock &BB : Function)
for (const MCInst &Inst : BB)
if (BC.MIB->isAArch64Exclusive(Inst)) {
if (opts::Verbosity >= 1)
outs() << "BOLT-INSTRUMENTER: Function " << Function
<< " has exclusive instructions, skip instrumentation\n";
return true;
}

return false;
}

uint32_t Instrumentation::getFunctionNameIndex(const BinaryFunction &Function) {
auto Iter = FuncToStringIdx.find(&Function);
if (Iter != FuncToStringIdx.end())
Expand Down Expand Up @@ -288,6 +307,9 @@ void Instrumentation::instrumentFunction(BinaryFunction &Function,
if (BC.isMachO() && Function.hasName("___GLOBAL_init_65535/1"))
return;

if (BC.isAArch64() && hasAArch64ExclusiveMemop(Function))
return;

SplitWorklistTy SplitWorklist;
SplitInstrsTy SplitInstrs;

Expand Down
28 changes: 28 additions & 0 deletions bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
return isLDRB(Inst) || isLDRH(Inst) || isLDRW(Inst) || isLDRX(Inst);
}

bool isAArch64Exclusive(const MCInst &Inst) const override {
return (Inst.getOpcode() == AArch64::LDXPX ||
Inst.getOpcode() == AArch64::LDXPW ||
Inst.getOpcode() == AArch64::LDXRX ||
Inst.getOpcode() == AArch64::LDXRW ||
Inst.getOpcode() == AArch64::LDXRH ||
Inst.getOpcode() == AArch64::LDXRB ||
Inst.getOpcode() == AArch64::STXPX ||
Inst.getOpcode() == AArch64::STXPW ||
Inst.getOpcode() == AArch64::STXRX ||
Inst.getOpcode() == AArch64::STXRW ||
Inst.getOpcode() == AArch64::STXRH ||
Inst.getOpcode() == AArch64::STXRB ||
Inst.getOpcode() == AArch64::LDAXPX ||
Inst.getOpcode() == AArch64::LDAXPW ||
Inst.getOpcode() == AArch64::LDAXRX ||
Inst.getOpcode() == AArch64::LDAXRW ||
Inst.getOpcode() == AArch64::LDAXRH ||
Inst.getOpcode() == AArch64::LDAXRB ||
Inst.getOpcode() == AArch64::STLXPX ||
Inst.getOpcode() == AArch64::STLXPW ||
Inst.getOpcode() == AArch64::STLXRX ||
Inst.getOpcode() == AArch64::STLXRW ||
Inst.getOpcode() == AArch64::STLXRH ||
Inst.getOpcode() == AArch64::STLXRB ||
Inst.getOpcode() == AArch64::CLREX);
}

bool isLoadFromStack(const MCInst &Inst) const {
if (!mayLoad(Inst))
return false;
Expand Down
39 changes: 39 additions & 0 deletions bolt/test/AArch64/exclusive-instrument.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// This test checks that the foo function having exclusive memory access
// instructions won't be instrumented.

// REQUIRES: system-linux,bolt-runtime,target=aarch64{{.*}}

// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
// RUN: %s -o %t.o
// RUN: %clang %cflags -fPIC -pie %t.o -o %t.exe -nostdlib -Wl,-q -Wl,-fini=dummy
// RUN: llvm-bolt %t.exe -o %t.bolt -instrument -v=1 | FileCheck %s

// CHECK: Function foo has exclusive instructions, skip instrumentation

.global foo
.type foo, %function
foo:
ldaxr w9, [x10]
cbnz w9, .Lret
stlxr w12, w11, [x9]
cbz w12, foo
clrex
.Lret:
ret
.size foo, .-foo

.global _start
.type _start, %function
_start:
cmp x0, #0
b.eq .Lexit
bl foo
.Lexit:
ret
.size _start, .-_start

.global dummy
.type dummy, %function
dummy:
ret
.size dummy, .-dummy

0 comments on commit 846eb76

Please sign in to comment.