mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 04:32:44 +01:00
2b2723c653
Implement fetch_<op>/fetch_and_<op>/exchange/compare-and-exchange instructions for BPF. Specially, the following gcc intrinsics are implemented. __sync_fetch_and_add (32, 64) __sync_fetch_and_sub (32, 64) __sync_fetch_and_and (32, 64) __sync_fetch_and_or (32, 64) __sync_fetch_and_xor (32, 64) __sync_lock_test_and_set (32, 64) __sync_val_compare_and_swap (32, 64) For __sync_fetch_and_sub, internally, it is implemented as a negation followed by __sync_fetch_and_add. For __sync_lock_test_and_set, despite its name, it actually does an atomic exchange and return the old content. https://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html For intrinsics like __sync_{add,sub}_and_fetch and __sync_bool_compare_and_swap, the compiler is able to generate codes using __sync_fetch_and_{add,sub} and __sync_val_compare_and_swap. Similar to xadd, atomic xadd, xor and xxor (atomic_<op>) instructions are added for atomic operations which do not have return values. LLVM will check the return value for __sync_fetch_and_{add,and,or,xor}. If the return value is used, instructions atomic_fetch_<op> will be used. Otherwise, atomic_<op> instructions will be used. All new instructions only support 64bit and 32bit with alu32 mode. old xadd instruction still supports 32bit without alu32 mode. For encoding, please take a look at test atomics_2.ll. Differential Revision: https://reviews.llvm.org/D72184
252 lines
8.2 KiB
C++
252 lines
8.2 KiB
C++
//===-------------- BPFMIChecking.cpp - MI Checking Legality -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This pass performs checking to signal errors for certain illegal usages at
|
|
// MachineInstruction layer. Specially, the result of XADD{32,64} insn should
|
|
// not be used. The pass is done at the PreEmit pass right before the
|
|
// machine code is emitted at which point the register liveness information
|
|
// is still available.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "BPF.h"
|
|
#include "BPFInstrInfo.h"
|
|
#include "BPFTargetMachine.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "bpf-mi-checking"
|
|
|
|
namespace {
|
|
|
|
struct BPFMIPreEmitChecking : public MachineFunctionPass {
|
|
|
|
static char ID;
|
|
MachineFunction *MF;
|
|
const TargetRegisterInfo *TRI;
|
|
|
|
BPFMIPreEmitChecking() : MachineFunctionPass(ID) {
|
|
initializeBPFMIPreEmitCheckingPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
private:
|
|
// Initialize class variables.
|
|
void initialize(MachineFunction &MFParm);
|
|
|
|
bool processAtomicInsts(void);
|
|
|
|
public:
|
|
|
|
// Main entry point for this pass.
|
|
bool runOnMachineFunction(MachineFunction &MF) override {
|
|
if (!skipFunction(MF.getFunction())) {
|
|
initialize(MF);
|
|
return processAtomicInsts();
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Initialize class variables.
|
|
void BPFMIPreEmitChecking::initialize(MachineFunction &MFParm) {
|
|
MF = &MFParm;
|
|
TRI = MF->getSubtarget<BPFSubtarget>().getRegisterInfo();
|
|
LLVM_DEBUG(dbgs() << "*** BPF PreEmit checking pass ***\n\n");
|
|
}
|
|
|
|
// Make sure all Defs of XADD are dead, meaning any result of XADD insn is not
|
|
// used.
|
|
//
|
|
// NOTE: BPF backend hasn't enabled sub-register liveness track, so when the
|
|
// source and destination operands of XADD are GPR32, there is no sub-register
|
|
// dead info. If we rely on the generic MachineInstr::allDefsAreDead, then we
|
|
// will raise false alarm on GPR32 Def.
|
|
//
|
|
// To support GPR32 Def, ideally we could just enable sub-registr liveness track
|
|
// on BPF backend, then allDefsAreDead could work on GPR32 Def. This requires
|
|
// implementing TargetSubtargetInfo::enableSubRegLiveness on BPF.
|
|
//
|
|
// However, sub-register liveness tracking module inside LLVM is actually
|
|
// designed for the situation where one register could be split into more than
|
|
// one sub-registers for which case each sub-register could have their own
|
|
// liveness and kill one of them doesn't kill others. So, tracking liveness for
|
|
// each make sense.
|
|
//
|
|
// For BPF, each 64-bit register could only have one 32-bit sub-register. This
|
|
// is exactly the case which LLVM think brings no benefits for doing
|
|
// sub-register tracking, because the live range of sub-register must always
|
|
// equal to its parent register, therefore liveness tracking is disabled even
|
|
// the back-end has implemented enableSubRegLiveness. The detailed information
|
|
// is at r232695:
|
|
//
|
|
// Author: Matthias Braun <matze@braunis.de>
|
|
// Date: Thu Mar 19 00:21:58 2015 +0000
|
|
// Do not track subregister liveness when it brings no benefits
|
|
//
|
|
// Hence, for BPF, we enhance MachineInstr::allDefsAreDead. Given the solo
|
|
// sub-register always has the same liveness as its parent register, LLVM is
|
|
// already attaching a implicit 64-bit register Def whenever the there is
|
|
// a sub-register Def. The liveness of the implicit 64-bit Def is available.
|
|
// For example, for "lock *(u32 *)(r0 + 4) += w9", the MachineOperand info could
|
|
// be:
|
|
//
|
|
// $w9 = XADDW32 killed $r0, 4, $w9(tied-def 0),
|
|
// implicit killed $r9, implicit-def dead $r9
|
|
//
|
|
// Even though w9 is not marked as Dead, the parent register r9 is marked as
|
|
// Dead correctly, and it is safe to use such information or our purpose.
|
|
static bool hasLiveDefs(const MachineInstr &MI, const TargetRegisterInfo *TRI) {
|
|
const MCRegisterClass *GPR64RegClass =
|
|
&BPFMCRegisterClasses[BPF::GPRRegClassID];
|
|
std::vector<unsigned> GPR32LiveDefs;
|
|
std::vector<unsigned> GPR64DeadDefs;
|
|
|
|
for (const MachineOperand &MO : MI.operands()) {
|
|
bool RegIsGPR64;
|
|
|
|
if (!MO.isReg() || MO.isUse())
|
|
continue;
|
|
|
|
RegIsGPR64 = GPR64RegClass->contains(MO.getReg());
|
|
if (!MO.isDead()) {
|
|
// It is a GPR64 live Def, we are sure it is live. */
|
|
if (RegIsGPR64)
|
|
return true;
|
|
// It is a GPR32 live Def, we are unsure whether it is really dead due to
|
|
// no sub-register liveness tracking. Push it to vector for deferred
|
|
// check.
|
|
GPR32LiveDefs.push_back(MO.getReg());
|
|
continue;
|
|
}
|
|
|
|
// Record any GPR64 dead Def as some unmarked GPR32 could be alias of its
|
|
// low 32-bit.
|
|
if (RegIsGPR64)
|
|
GPR64DeadDefs.push_back(MO.getReg());
|
|
}
|
|
|
|
// No GPR32 live Def, safe to return false.
|
|
if (GPR32LiveDefs.empty())
|
|
return false;
|
|
|
|
// No GPR64 dead Def, so all those GPR32 live Def can't have alias, therefore
|
|
// must be truely live, safe to return true.
|
|
if (GPR64DeadDefs.empty())
|
|
return true;
|
|
|
|
// Otherwise, return true if any aliased SuperReg of GPR32 is not dead.
|
|
for (auto I : GPR32LiveDefs)
|
|
for (MCSuperRegIterator SR(I, TRI); SR.isValid(); ++SR)
|
|
if (!llvm::is_contained(GPR64DeadDefs, *SR))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool BPFMIPreEmitChecking::processAtomicInsts(void) {
|
|
for (MachineBasicBlock &MBB : *MF) {
|
|
for (MachineInstr &MI : MBB) {
|
|
if (MI.getOpcode() != BPF::XADDW &&
|
|
MI.getOpcode() != BPF::XADDD &&
|
|
MI.getOpcode() != BPF::XADDW32)
|
|
continue;
|
|
|
|
LLVM_DEBUG(MI.dump());
|
|
if (hasLiveDefs(MI, TRI)) {
|
|
DebugLoc Empty;
|
|
const DebugLoc &DL = MI.getDebugLoc();
|
|
if (DL != Empty)
|
|
report_fatal_error("line " + std::to_string(DL.getLine()) +
|
|
": Invalid usage of the XADD return value", false);
|
|
else
|
|
report_fatal_error("Invalid usage of the XADD return value", false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check return values of atomic_fetch_and_{add,and,or,xor}.
|
|
// If the return is not used, the atomic_fetch_and_<op> instruction
|
|
// is replaced with atomic_<op> instruction.
|
|
MachineInstr *ToErase = nullptr;
|
|
bool Changed = false;
|
|
const BPFInstrInfo *TII = MF->getSubtarget<BPFSubtarget>().getInstrInfo();
|
|
for (MachineBasicBlock &MBB : *MF) {
|
|
for (MachineInstr &MI : MBB) {
|
|
if (ToErase) {
|
|
ToErase->eraseFromParent();
|
|
ToErase = nullptr;
|
|
}
|
|
|
|
if (MI.getOpcode() != BPF::XFADDW32 && MI.getOpcode() != BPF::XFADDD &&
|
|
MI.getOpcode() != BPF::XFANDW32 && MI.getOpcode() != BPF::XFANDD &&
|
|
MI.getOpcode() != BPF::XFXORW32 && MI.getOpcode() != BPF::XFXORD &&
|
|
MI.getOpcode() != BPF::XFORW32 && MI.getOpcode() != BPF::XFORD)
|
|
continue;
|
|
|
|
if (hasLiveDefs(MI, TRI))
|
|
continue;
|
|
|
|
LLVM_DEBUG(dbgs() << "Transforming "; MI.dump());
|
|
unsigned newOpcode;
|
|
switch (MI.getOpcode()) {
|
|
case BPF::XFADDW32:
|
|
newOpcode = BPF::XADDW32;
|
|
break;
|
|
case BPF::XFADDD:
|
|
newOpcode = BPF::XADDD;
|
|
break;
|
|
case BPF::XFANDW32:
|
|
newOpcode = BPF::XANDW32;
|
|
break;
|
|
case BPF::XFANDD:
|
|
newOpcode = BPF::XANDD;
|
|
break;
|
|
case BPF::XFXORW32:
|
|
newOpcode = BPF::XXORW32;
|
|
break;
|
|
case BPF::XFXORD:
|
|
newOpcode = BPF::XXORD;
|
|
break;
|
|
case BPF::XFORW32:
|
|
newOpcode = BPF::XORW32;
|
|
break;
|
|
case BPF::XFORD:
|
|
newOpcode = BPF::XORD;
|
|
break;
|
|
default:
|
|
llvm_unreachable("Incorrect Atomic Instruction Opcode");
|
|
}
|
|
|
|
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(newOpcode))
|
|
.add(MI.getOperand(0))
|
|
.add(MI.getOperand(1))
|
|
.add(MI.getOperand(2))
|
|
.add(MI.getOperand(3));
|
|
|
|
ToErase = &MI;
|
|
Changed = true;
|
|
}
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
|
|
} // end default namespace
|
|
|
|
INITIALIZE_PASS(BPFMIPreEmitChecking, "bpf-mi-pemit-checking",
|
|
"BPF PreEmit Checking", false, false)
|
|
|
|
char BPFMIPreEmitChecking::ID = 0;
|
|
FunctionPass* llvm::createBPFMIPreEmitCheckingPass()
|
|
{
|
|
return new BPFMIPreEmitChecking();
|
|
}
|