[AtomicExpandPass] Widen partword atomicrmw or/xor/and before tryExpandAtomicRMW

This patch performs a widening transformation of bitwise atomicrmw 
{or,xor,and} and applies it prior to tryExpandAtomicRMW. This operates 
similarly to convertCmpXchgToIntegerType. For these operations, the i8/i16 
atomicrmw can be implemented in terms of the 32-bit atomicrmw by appropriately 
manipulating the operands. There is no functional change for the handling of 
partword or/xor, but the transformation for partword 'and' is new.

The advantage of performing this transformation early is that the same 
code-path can be used regardless of the approach used to expand the atomicrmw 
(AtomicExpansionKind). i.e. the same logic is used for 
AtomicExpansionKind::CmpXchg and can also be used by the intrinsic-based 
expansion in D47882.

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

llvm-svn: 340027
This commit is contained in:
Alex Bradbury 2018-08-17 14:03:37 +00:00
parent 1962621a7e
commit 3291f9aa81
2 changed files with 73 additions and 4 deletions

View File

@ -88,6 +88,7 @@ namespace {
void expandPartwordAtomicRMW(
AtomicRMWInst *I,
TargetLoweringBase::AtomicExpansionKind ExpansionKind);
AtomicRMWInst *widenPartwordAtomicRMW(AtomicRMWInst *AI);
void expandPartwordCmpXchg(AtomicCmpXchgInst *I);
AtomicCmpXchgInst *convertCmpXchgToIntegerType(AtomicCmpXchgInst *CI);
@ -306,6 +307,16 @@ bool AtomicExpand::runOnFunction(Function &F) {
if (isIdempotentRMW(RMWI) && simplifyIdempotentRMW(RMWI)) {
MadeChange = true;
} else {
unsigned MinCASSize = TLI->getMinCmpXchgSizeInBits() / 8;
unsigned ValueSize = getAtomicOpSize(RMWI);
AtomicRMWInst::BinOp Op = RMWI->getOperation();
if (ValueSize < MinCASSize &&
(Op == AtomicRMWInst::Or || Op == AtomicRMWInst::Xor ||
Op == AtomicRMWInst::And)) {
RMWI = widenPartwordAtomicRMW(RMWI);
MadeChange = true;
}
MadeChange |= tryExpandAtomicRMW(RMWI);
}
} else if (CASI) {
@ -659,12 +670,10 @@ static Value *performMaskedAtomicOp(AtomicRMWInst::BinOp Op,
}
case AtomicRMWInst::Or:
case AtomicRMWInst::Xor:
// Or/Xor won't affect any other bits, so can just be done
// directly.
return performAtomicOp(Op, Builder, Loaded, Shifted_Inc);
case AtomicRMWInst::And:
llvm_unreachable("Or/Xor/And handled by widenPartwordAtomicRMW");
case AtomicRMWInst::Add:
case AtomicRMWInst::Sub:
case AtomicRMWInst::And:
case AtomicRMWInst::Nand: {
// The other arithmetic ops need to be masked into place.
Value *NewVal = performAtomicOp(Op, Builder, Loaded, Shifted_Inc);
@ -733,6 +742,41 @@ void AtomicExpand::expandPartwordAtomicRMW(
AI->eraseFromParent();
}
// Widen the bitwise atomicrmw (or/xor/and) to the minimum supported width.
AtomicRMWInst *AtomicExpand::widenPartwordAtomicRMW(AtomicRMWInst *AI) {
IRBuilder<> Builder(AI);
AtomicRMWInst::BinOp Op = AI->getOperation();
assert((Op == AtomicRMWInst::Or || Op == AtomicRMWInst::Xor ||
Op == AtomicRMWInst::And) &&
"Unable to widen operation");
PartwordMaskValues PMV =
createMaskInstrs(Builder, AI, AI->getType(), AI->getPointerOperand(),
TLI->getMinCmpXchgSizeInBits() / 8);
Value *ValOperand_Shifted =
Builder.CreateShl(Builder.CreateZExt(AI->getValOperand(), PMV.WordType),
PMV.ShiftAmt, "ValOperand_Shifted");
Value *NewOperand;
if (Op == AtomicRMWInst::And)
NewOperand =
Builder.CreateOr(PMV.Inv_Mask, ValOperand_Shifted, "AndOperand");
else
NewOperand = ValOperand_Shifted;
AtomicRMWInst *NewAI = Builder.CreateAtomicRMW(Op, PMV.AlignedAddr,
NewOperand, AI->getOrdering());
Value *FinalOldResult = Builder.CreateTrunc(
Builder.CreateLShr(NewAI, PMV.ShiftAmt), PMV.ValueType);
AI->replaceAllUsesWith(FinalOldResult);
AI->eraseFromParent();
return NewAI;
}
void AtomicExpand::expandPartwordCmpXchg(AtomicCmpXchgInst *CI) {
// The basic idea here is that we're expanding a cmpxchg of a
// smaller memory size up to a word-sized cmpxchg. To do this, we

View File

@ -147,6 +147,31 @@ entry:
ret i16 %ret
}
; CHECK-LABEL: @test_or_i16(
; (I'm going to just assert on the bits that differ from add, above.)
; CHECK:atomicrmw.start:
; CHECK: %new = or i32 %loaded, %ValOperand_Shifted
; CHECK: %6 = cmpxchg i32* %AlignedAddr, i32 %loaded, i32 %new monotonic monotonic
; CHECK:atomicrmw.end:
define i16 @test_or_i16(i16* %arg, i16 %val) {
entry:
%ret = atomicrmw or i16* %arg, i16 %val seq_cst
ret i16 %ret
}
; CHECK-LABEL: @test_and_i16(
; (I'm going to just assert on the bits that differ from add, above.)
; CHECK: %AndOperand = or i32 %Inv_Mask, %ValOperand_Shifted
; CHECK:atomicrmw.start:
; CHECK: %new = and i32 %loaded, %AndOperand
; CHECK: %6 = cmpxchg i32* %AlignedAddr, i32 %loaded, i32 %new monotonic monotonic
; CHECK:atomicrmw.end:
define i16 @test_and_i16(i16* %arg, i16 %val) {
entry:
%ret = atomicrmw and i16* %arg, i16 %val seq_cst
ret i16 %ret
}
; CHECK-LABEL: @test_min_i16(
; CHECK:atomicrmw.start:
; CHECK: %6 = lshr i32 %loaded, %ShiftAmt