rsmith created this revision.
rsmith added a reviewer: rjmccall.
Herald added subscribers: cfe-commits, jfb.
Herald added a project: clang.
rsmith requested review of this revision.
Herald added a reviewer: jdoerfert.
Herald added a subscriber: sstefan1.
C++ permits use of (cond ? x.a : y.b) as an lvalue even when a and/or b is a
bit-field. We previously failed to generate IR for such constructs.
Handle these cases with a new form of LValue representing a choice between two
other LValues. Whenever we want to generate a primitive operation on a
conditional lvalue, generate a branch to perform the operation.
This would be sufficient to also support lvalue conditionals between other
kinds of non-simple lvalue, such as a conditional between the imaginary
component of a _Complex T and a vector element, but Sema rejects all the other
combinations for the moment.
One case that's not yet supported is atomic compare-exchange operations on
bitfield conditional lvalues. These only arise through `#pragma omp atomic`
constructs, and would probably not be too hard to support, but we need to teach
the OpenMP code to cope with its subexpression being emitted multiple times.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D85710
Files:
clang/lib/CodeGen/CGAtomic.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CGStmtOpenMP.cpp
clang/lib/CodeGen/CGValue.h
clang/lib/CodeGen/CodeGenFunction.h
clang/test/CodeGenCXX/conditional-expr-lvalue.cpp
Index: clang/test/CodeGenCXX/conditional-expr-lvalue.cpp
===================================================================
--- clang/test/CodeGenCXX/conditional-expr-lvalue.cpp
+++ clang/test/CodeGenCXX/conditional-expr-lvalue.cpp
@@ -1,8 +1,15 @@
-// RUN: %clang_cc1 -emit-llvm-only %s
-void f(bool flag) {
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - -fopenmp > /tmp/tmp.ir
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - -fopenmp | FileCheck %s
+
+// CHECK-LABEL: define {{.*}} @{{.*}}simple_scalar_cond
+void simple_scalar_cond(bool flag) {
+ // CHECK: store i32 1, i32* %[[A:.*]],
int a = 1;
+ // CHECK: store i32 2, i32* %[[B:.*]],
int b = 2;
+ // CHECK: %[[LVAL:.*]] = phi i32* [ %[[A]], {{.*}} ], [ %[[B]], {{.*}} ]
+ // CHECK: store i32 3, i32* %[[LVAL]],
(flag ? a : b) = 3;
}
@@ -14,7 +21,173 @@
A sub() const;
void foo() const;
};
+ // CHECK-LABEL: define {{.*}} @{{.*}}test0
void foo(bool cond, const A &a) {
+ // CHECK: %[[TMP:.*]] = alloca %"struct.test0::A",
+ // CHECK: br i1
+ //
+ // CHECK: call void @_ZN5test01AC1ERKS0_(%"struct.test0::A"* %[[TMP]],
+ // CHECK: br label
+ //
+ // CHECK: call void @_ZNK5test01A3subEv(%"struct.test0::A"* sret align 1 %[[TMP]],
+ // CHECK: br label
+ //
+ // CHECK: call void @_ZNK5test01A3fooEv(%"struct.test0::A"* %[[TMP]])
(cond ? a : a.sub()).foo();
}
}
+
+namespace bitfields {
+ struct A {
+ int m : 3;
+ int n : 5;
+ } a1, a2;
+
+ // CHECK-LABEL: define {{.*}} @{{.*}}load
+ int load(bool b) {
+ // CHECK: br i1
+ //
+ // CHECK: load {{.*}}@_ZN9bitfields2a1E, i32 0, i32 0
+ // CHECK: shl {{.*}}, 5
+ // CHECK: ashr {{.*}}, 5
+ // CHECK: br
+ //
+ // CHECK: load {{.*}}@_ZN9bitfields2a2E, i32 0, i32 0
+ // CHECK: ashr {{.*}}, 3
+ // CHECK: br
+ return b ? a1.m : a2.n;
+ }
+
+ // CHECK-LABEL: define {{.*}} @{{.*}}store
+ void store(bool b, int k) {
+ // CHECK: br i1
+ // CHECK: %[[SEL:.*]] = phi i1
+ // CHECK: %[[A1:.*]] = phi i8* {{.*}} @_ZN9bitfields2a1E, {{.*}} [ undef,
+ // CHECK: %[[A2:.*]] = phi i8* {{.*}} @_ZN9bitfields2a2E, {{.*}} [ undef,
+ // CHECK: br i1 %[[SEL]]
+ //
+ // CHECK: load {{.*}}%[[A1]]
+ // CHECK: and {{.*}}, 7
+ // CHECK: and {{.*}}, -8
+ // CHECK: or
+ // CHECK: store
+ // CHECK: br label
+ //
+ // CHECK: load {{.*}}%[[A2]]
+ // CHECK: and {{.*}}, 31
+ // CHECK: shl {{.*}}, 3
+ // CHECK: and {{.*}}, 7
+ // CHECK: or
+ // CHECK: store
+ // CHECK: br label
+ (b ? a1.m : a2.n) = k;
+ }
+
+ // CHECK-LABEL: define {{.*}} @{{.*}}update
+ void update(bool b, int k) {
+ // 1: Branch on condition and evaluate the chosen lvalue.
+ //
+ // CHECK: br i1
+ // CHECK: %[[SEL:.*]] = phi i1
+ // CHECK: %[[A1:.*]] = phi i8* {{.*}} @_ZN9bitfields2a1E, {{.*}} [ undef,
+ // CHECK: %[[A2:.*]] = phi i8* {{.*}} @_ZN9bitfields2a2E, {{.*}} [ undef,
+ // CHECK: br i1 %[[SEL]]
+ //
+ // 2: Branch on condition and load the relevant value.
+ //
+ // CHECK: load {{.*}}%[[A1]]
+ // CHECK: shl {{.*}}, 5
+ // CHECK: ashr {{.*}}, 5
+ // CHECK: br
+ //
+ // CHECK: load {{.*}}%[[A2]]
+ // CHECK: ashr {{.*}}, 3
+ // CHECK: br
+ //
+ // 3: Add k.
+ //
+ // CHECK: add nsw
+ // CHECK: br i1
+ //
+ // 4: Branch on condition and store the new value.
+ //
+ // CHECK: load {{.*}}%[[A1]]
+ // CHECK: and {{.*}}, 7
+ // CHECK: and {{.*}}, -8
+ // CHECK: or
+ // CHECK: store
+ // CHECK: br label
+ //
+ // CHECK: load {{.*}}%[[A2]]
+ // CHECK: and {{.*}}, 31
+ // CHECK: shl {{.*}}, 3
+ // CHECK: and {{.*}}, 7
+ // CHECK: or
+ // CHECK: store
+ // CHECK: br label
+ (b ? a1.m : a2.n) += k;
+ }
+
+ // CHECK-LABEL: define {{.*}} @{{.*}}atomic_load
+ int atomic_load(bool b) {
+ // CHECK: br i1
+ // CHECK: %[[SEL:.*]] = phi i1
+ // CHECK: %[[A1:.*]] = phi i8* {{.*}} @_ZN9bitfields2a1E, {{.*}} [ undef,
+ // CHECK: %[[A2:.*]] = phi i8* {{.*}} @_ZN9bitfields2a2E, {{.*}} [ undef,
+ // CHECK: br i1 %[[SEL]]
+ //
+ // CHECK: %[[A1b:.*]] = getelementptr i8, i8* %[[A1]], i64 0
+ // CHECK: %[[A1c:.*]] = bitcast i8* %[[A1b]] to i32*
+ // CHECK: load atomic {{.*}}%[[A1c]]
+ // CHECK: shl i32 {{.*}}, 29
+ // CHECK: ashr i32 {{.*}}, 29
+ // CHECK: br label
+ //
+ // CHECK: %[[A2b:.*]] = getelementptr i8, i8* %[[A2]], i64 0
+ // CHECK: %[[A2c:.*]] = bitcast i8* %[[A2b]] to i32*
+ // CHECK: load atomic {{.*}}%[[A2c]]
+ // CHECK: shl i32 {{.*}}, 24
+ // CHECK: ashr i32 {{.*}}, 27
+ // CHECK: br label
+ //
+ // CHECK: call void @__kmpc_flush
+ int v;
+ #pragma omp atomic seq_cst read
+ v = (b ? a1.m : a2.n);
+ return v;
+ }
+
+ // CHECK-LABEL: define {{.*}} @{{.*}}atomic_store
+ void atomic_store(bool b, int k) {
+ // CHECK: br i1
+ // CHECK: %[[SEL:.*]] = phi i1
+ // CHECK: %[[A1:.*]] = phi i8* {{.*}} @_ZN9bitfields2a1E, {{.*}} [ undef,
+ // CHECK: %[[A2:.*]] = phi i8* {{.*}} @_ZN9bitfields2a2E, {{.*}} [ undef,
+ // CHECK: br i1 %[[SEL]]
+ //
+ // CHECK: %[[A1b:.*]] = getelementptr i8, i8* %[[A1]], i64 0
+ // CHECK: %[[A1c:.*]] = bitcast i8* %[[A1b]] to i32*
+ // CHECK: load atomic {{.*}}%[[A1c]]
+ // CHECK: and i32 {{.*}}, 7
+ // CHECK: and i32 {{.*}}, -8
+ // CHECK: or
+ // CHECK: cmpxchg
+ // CHECK: br i1
+ // CHECK: br label
+ //
+ // CHECK: %[[A2b:.*]] = getelementptr i8, i8* %[[A2]], i64 0
+ // CHECK: %[[A2c:.*]] = bitcast i8* %[[A2b]] to i32*
+ // CHECK: load atomic {{.*}}%[[A2c]]
+ // CHECK: and i32 {{.*}}, 31
+ // CHECK: shl i32 {{.*}}, 3
+ // CHECK: and i32 {{.*}}, -249
+ // CHECK: or
+ // CHECK: cmpxchg
+ // CHECK: br i1
+ // CHECK: br label
+ //
+ // CHECK: call void @__kmpc_flush
+ #pragma omp atomic seq_cst write
+ (b ? a1.m : a2.n) = k;
+ }
+}
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -229,6 +229,24 @@
}
};
+/// An input to a PHI node.
+template<typename T> struct PHIValue {
+ T Value;
+ llvm::BasicBlock *Pred;
+
+ template<typename Fn> static PHIValue make(llvm::BasicBlock *Pred, Fn F) {
+ return {F(), Pred};
+ }
+};
+template<> struct PHIValue<void> {
+ llvm::BasicBlock *Pred;
+
+ template<typename Fn> static PHIValue make(llvm::BasicBlock *Pred, Fn F) {
+ F();
+ return {Pred};
+ }
+};
+
/// CodeGenFunction - This class organizes the per-function state that is used
/// while generating LLVM code.
class CodeGenFunction : public CodeGenTypeCache {
@@ -1455,6 +1473,13 @@
// enter/leave scopes.
llvm::DenseMap<const Expr*, llvm::Value*> VLASizeMap;
+ struct ConditionalLValueStorageType {
+ ConditionalLValueStorageType(LValue A, LValue B) : Values{A, B} {}
+ LValue Values[2];
+ };
+ std::vector<std::unique_ptr<ConditionalLValueStorageType>>
+ ConditionalLValueStorage;
+
/// A block containing a single 'unreachable' instruction. Created
/// lazily by getUnreachableBlock().
llvm::BasicBlock *UnreachableBlock = nullptr;
@@ -2481,6 +2506,62 @@
AggValueSlot::DoesNotOverlap);
}
+ /// Make a conditional lvalue that selects between the two given LValues.
+ LValue MakeConditionalLValue(llvm::Value *Sel, LValue A, LValue B);
+
+ /// Emit a PHI between values.
+ llvm::Value *EmitPHI(llvm::ArrayRef<PHIValue<llvm::Value*>> Values);
+ llvm::Value *EmitPHI(PHIValue<llvm::Value*> A, PHIValue<llvm::Value*> B) {
+ return EmitPHI({A, B});
+ }
+
+ LValue EmitPHI(PHIValue<LValue> A, PHIValue<LValue> B);
+
+ RValue EmitPHI(llvm::ArrayRef<PHIValue<RValue>> Values);
+ RValue EmitPHI(PHIValue<RValue> A, PHIValue<RValue> B) {
+ return EmitPHI({A, B});
+ }
+
+ Address EmitPHI(llvm::ArrayRef<PHIValue<Address>> Values);
+ Address EmitPHI(PHIValue<Address> A, PHIValue<Address> B) {
+ return EmitPHI({A, B});
+ }
+
+ template<typename T, typename U>
+ std::pair<T, U> EmitPHI(PHIValue<std::pair<T, U>> A,
+ PHIValue<std::pair<T, U>> B) {
+ return {EmitPHI({A.Value.first, A.Pred}, {B.Value.first, B.Pred}),
+ EmitPHI({A.Value.second, A.Pred}, {B.Value.second, B.Pred})};
+ }
+
+ void EmitPHI(PHIValue<void> A, PHIValue<void> B) {}
+
+ /// Emit an atomic operation on both possible cases of an atomic lvalue.
+ template<typename Fn>
+ auto EmitBranchOnConditionalLValue(LValue CondLV, Fn F)
+ -> decltype(F(CondLV)) {
+ assert(CondLV.isConditional() && "not a conditional LValue");
+ using Result = decltype(F(CondLV));
+
+ llvm::BasicBlock *LHSBlock = createBasicBlock("cond.lval.lhs");
+ llvm::BasicBlock *RHSBlock = createBasicBlock("cond.lval.rhs");
+ llvm::BasicBlock *ContBlock = createBasicBlock("cond.lval.cont");
+ Builder.CreateCondBr(CondLV.getCondition(), RHSBlock, LHSBlock);
+
+ EmitBlock(LHSBlock);
+ PHIValue<Result> LHSValue = PHIValue<Result>::make(
+ LHSBlock, [&] { return F(CondLV.getConditionalValue(0)); });
+ EmitBranch(ContBlock);
+
+ EmitBlock(RHSBlock);
+ PHIValue<Result> RHSValue = PHIValue<Result>::make(
+ RHSBlock, [&] { return F(CondLV.getConditionalValue(1)); });
+ EmitBranch(ContBlock);
+
+ EmitBlock(ContBlock);
+ return EmitPHI(LHSValue, RHSValue);
+ }
+
/// Emit a cast to void* in the appropriate address space.
llvm::Value *EmitCastToVoidPtr(llvm::Value *value);
@@ -3631,7 +3712,7 @@
void EmitAtomicUpdate(LValue LVal, llvm::AtomicOrdering AO,
const llvm::function_ref<RValue(RValue)> &UpdateOp,
- bool IsVolatile);
+ bool IsVolatile, SourceLocation Loc);
/// EmitToMemory - Change a scalar value from its value
/// representation to its in-memory representation.
Index: clang/lib/CodeGen/CGValue.h
===================================================================
--- clang/lib/CodeGen/CGValue.h
+++ clang/lib/CodeGen/CGValue.h
@@ -159,6 +159,12 @@
void mergeForCast(const LValueBaseInfo &Info) {
setAlignmentSource(Info.getAlignmentSource());
}
+ static LValueBaseInfo mergeForConditional(LValueBaseInfo A,
+ LValueBaseInfo B) {
+ AlignmentSource Weakest = AlignmentSource(
+ std::max(unsigned(A.AlignSource), unsigned(B.AlignSource)));
+ return LValueBaseInfo(Weakest);
+ }
};
/// LValue - This represents an lvalue references. Because C/C++ allow
@@ -171,7 +177,8 @@
BitField, // This is a bitfield l-value, use getBitfield*.
ExtVectorElt, // This is an extended vector subset, use getExtVectorComp
GlobalReg, // This is a register l-value, use getGlobalReg()
- MatrixElt // This is a matrix element, use getVector*
+ MatrixElt, // This is a matrix element, use getVector*
+ Conditional // This is a conditional involving a non-simple lvalue.
} LVType;
llvm::Value *V;
@@ -185,6 +192,10 @@
// BitField start bit and size
const CGBitFieldInfo *BitFieldInfo;
+
+ // For a conditional lvalue, the two possible underlying lvalues.
+ // V is an i1 indicating which one of these is selected.
+ LValue (*CondVals)[2];
};
QualType Type;
@@ -256,6 +267,7 @@
bool isExtVectorElt() const { return LVType == ExtVectorElt; }
bool isGlobalReg() const { return LVType == GlobalReg; }
bool isMatrixElt() const { return LVType == MatrixElt; }
+ bool isConditional() const { return LVType == Conditional; }
bool isVolatileQualified() const { return Quals.hasVolatile(); }
bool isRestrictQualified() const { return Quals.hasRestrict(); }
@@ -386,6 +398,21 @@
// global register lvalue
llvm::Value *getGlobalReg() const { assert(isGlobalReg()); return V; }
+ Address getGlobalRegAddress() const {
+ return Address(getGlobalReg(), getAlignment());
+ }
+
+ // conditional lvalue
+ llvm::Value *getCondition() const {
+ assert(isConditional());
+ return V;
+ }
+
+ LValue getConditionalValue(bool Which) const {
+ assert(isConditional());
+ return (*CondVals)[Which];
+ }
+
static LValue MakeAddr(Address address, QualType type, ASTContext &Context,
LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo) {
Qualifiers qs = type.getQualifiers();
@@ -462,6 +489,23 @@
return R;
}
+ static LValue MakeConditional(llvm::Value *Cond, LValue (&Vals)[2],
+ TBAAAccessInfo TBAAInfo) {
+ LValue R;
+ R.LVType = Conditional;
+ R.V = Cond;
+ R.CondVals = &Vals;
+ // The properties of a conditional LValue should generally not matter,
+ // because any use should generate a branch and handle the two cases
+ // independently.
+ R.Initialize(Vals[0].getType(), Vals[0].getQuals(),
+ std::min(Vals[0].getAlignment(), Vals[1].getAlignment()),
+ LValueBaseInfo::mergeForConditional(Vals[0].getBaseInfo(),
+ Vals[1].getBaseInfo()),
+ TBAAInfo);
+ return R;
+ }
+
RValue asAggregateRValue(CodeGenFunction &CGF) const {
return RValue::getAggregate(getAddress(CGF), isVolatileQualified());
}
Index: clang/lib/CodeGen/CGStmtOpenMP.cpp
===================================================================
--- clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -5046,7 +5046,8 @@
EmitStoreThroughLValue(CommonGen(EmitLoadOfLValue(X, Loc)), X);
} else {
// Perform compare-and-swap procedure.
- EmitAtomicUpdate(X, AO, CommonGen, X.getType().isVolatileQualified());
+ EmitAtomicUpdate(X, AO, CommonGen, X.getType().isVolatileQualified(),
+ Loc);
}
}
return Res;
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -1244,7 +1244,7 @@
LV = EmitArraySubscriptExpr(cast<ArraySubscriptExpr>(E), /*Accessed*/true);
else
LV = EmitLValue(E);
- if (!isa<DeclRefExpr>(E) && !LV.isBitField() && LV.isSimple()) {
+ if (!isa<DeclRefExpr>(E) && LV.isSimple()) {
SanitizerSet SkippedChecks;
if (const auto *ME = dyn_cast<MemberExpr>(E)) {
bool IsBaseCXXThis = IsWrappedCXXThis(ME->getBase());
@@ -1914,8 +1914,16 @@
Builder.CreateExtractElement(Load, LV.getMatrixIdx(), "matrixext"));
}
- assert(LV.isBitField() && "Unknown LValue type!");
- return EmitLoadOfBitfieldLValue(LV, Loc);
+ if (LV.isBitField())
+ return EmitLoadOfBitfieldLValue(LV, Loc);
+
+ if (LV.isConditional()) {
+ return EmitBranchOnConditionalLValue(LV, [&](LValue LV) { //
+ return EmitLoadOfLValue(LV, Loc);
+ });
+ }
+
+ llvm_unreachable("unknown lvalue kind");
}
RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
@@ -2053,8 +2061,16 @@
return;
}
- assert(Dst.isBitField() && "Unknown LValue type");
- return EmitStoreThroughBitfieldLValue(Src, Dst);
+ if (Dst.isBitField())
+ return EmitStoreThroughBitfieldLValue(Src, Dst);
+
+ if (Dst.isConditional()) {
+ return EmitBranchOnConditionalLValue(Dst, [&](LValue LV) {
+ return EmitStoreThroughLValue(Src, LV, isInit);
+ });
+ }
+
+ llvm_unreachable("unknown lvalue type");
}
// There's special magic for assigning into an ARC-qualified l-value.
@@ -4427,6 +4443,193 @@
return CGF.EmitLValue(Operand);
}
+LValue CodeGenFunction::MakeConditionalLValue(llvm::Value *Sel, LValue A,
+ LValue B) {
+ assert(getContext().hasSameUnqualifiedType(A.getType(), B.getType()) &&
+ "conditional between lvalues of different types");
+
+ TBAAAccessInfo TBAAInfo =
+ CGM.mergeTBAAInfoForConditionalOperator(A.getTBAAInfo(), B.getTBAAInfo());
+
+ auto Vals = std::make_unique<ConditionalLValueStorageType>(A, B);
+ LValue Result = LValue::MakeConditional(Sel, Vals->Values, TBAAInfo);
+ ConditionalLValueStorage.push_back(std::move(Vals));
+ return Result;
+}
+
+/// Given an LValue that is usable in predecessor \p Pred of the current block,
+/// get a corresponding LValue that is usable in this block. Replace the value
+/// with 'undef' if we came from the other predecessor \p OtherPred.
+static LValue getLValueOrUndef(CodeGenFunction &CGF, LValue LVal,
+ llvm::BasicBlock *Pred,
+ llvm::BasicBlock *OtherPred) {
+ auto MakeValuePHI = [&](llvm::Value *V) {
+ llvm::PHINode *NewV = CGF.Builder.CreatePHI(V->getType(), 2);
+ NewV->addIncoming(V, Pred);
+ NewV->addIncoming(llvm::UndefValue::get(V->getType()), OtherPred);
+ return NewV;
+ };
+
+ auto MakeAddressPHI = [&](Address Addr) {
+ if (!Addr.isValid())
+ return Addr;
+ return Address(MakeValuePHI(Addr.getPointer()), Addr.getAlignment());
+ };
+
+ if (LVal.isSimple())
+ return LValue::MakeAddr(MakeAddressPHI(LVal.getAddress(CGF)),
+ LVal.getType(), CGF.getContext(),
+ LVal.getBaseInfo(), LVal.getTBAAInfo());
+ if (LVal.isVectorElt()) {
+ Address A = MakeAddressPHI(LVal.getVectorAddress());
+ return LValue::MakeVectorElt(A, MakeValuePHI(LVal.getVectorIdx()),
+ LVal.getType(), LVal.getBaseInfo(),
+ LVal.getTBAAInfo());
+ }
+ if (LVal.isExtVectorElt())
+ return LValue::MakeExtVectorElt(MakeAddressPHI(LVal.getExtVectorAddress()),
+ LVal.getExtVectorElts(), LVal.getType(),
+ LVal.getBaseInfo(), LVal.getTBAAInfo());
+ if (LVal.isBitField())
+ return LValue::MakeBitfield(MakeAddressPHI(LVal.getBitFieldAddress()),
+ LVal.getBitFieldInfo(), LVal.getType(),
+ LVal.getBaseInfo(), LVal.getTBAAInfo());
+ if (LVal.isGlobalReg())
+ return LValue::MakeGlobalReg(MakeAddressPHI(LVal.getGlobalRegAddress()),
+ LVal.getType());
+ if (LVal.isMatrixElt()) {
+ Address A = MakeAddressPHI(LVal.getMatrixAddress());
+ return LValue::MakeMatrixElt(A, MakeValuePHI(LVal.getMatrixIdx()),
+ LVal.getType(), LVal.getBaseInfo(),
+ LVal.getTBAAInfo());
+ }
+
+ if (LVal.isConditional()) {
+ LValue LHS =
+ getLValueOrUndef(CGF, LVal.getConditionalValue(0), Pred, OtherPred);
+ LValue RHS =
+ getLValueOrUndef(CGF, LVal.getConditionalValue(1), Pred, OtherPred);
+ return CGF.MakeConditionalLValue(MakeValuePHI(LVal.getCondition()),
+ LHS, RHS);
+ }
+
+ llvm_unreachable("unknown lvalue kind");
+}
+
+LValue CodeGenFunction::EmitPHI(PHIValue<LValue> A, PHIValue<LValue> B) {
+ // Easy case: both lvalues are simple. In this case, we can just emit a single
+ // phi and produce a simple lvalue result.
+ if (A.Value.isSimple() && B.Value.isSimple()) {
+ llvm::PHINode *Phi = Builder.CreatePHI(A.Value.getPointer(*this)->getType(),
+ 2, "cond-lvalue");
+ Phi->addIncoming(A.Value.getPointer(*this), A.Pred);
+ Phi->addIncoming(B.Value.getPointer(*this), B.Pred);
+
+ CharUnits Alignment =
+ std::min(A.Value.getAlignment(), B.Value.getAlignment());
+ AlignmentSource AlignSource =
+ std::max(A.Value.getBaseInfo().getAlignmentSource(),
+ B.Value.getBaseInfo().getAlignmentSource());
+ TBAAAccessInfo TBAAInfo = CGM.mergeTBAAInfoForConditionalOperator(
+ A.Value.getTBAAInfo(), B.Value.getTBAAInfo());
+
+ Address Result(Phi, Alignment);
+ return MakeAddrLValue(Result, A.Value.getType(),
+ LValueBaseInfo(AlignSource), TBAAInfo);
+ }
+
+ // Hard case: create a conditional lvalue merging the cases.
+ llvm::PHINode *Sel = Builder.CreatePHI(
+ llvm::Type::getInt1Ty(getLLVMContext()), 2, "cond.lval.sel");
+ Sel->addIncoming(llvm::ConstantInt::getFalse(getLLVMContext()), A.Pred);
+ Sel->addIncoming(llvm::ConstantInt::getTrue(getLLVMContext()), B.Pred);
+
+ // Make sure both LValues are usable in the continuation block.
+ LValue AVal = getLValueOrUndef(*this, A.Value, A.Pred, B.Pred);
+ LValue BVal = getLValueOrUndef(*this, B.Value, B.Pred, A.Pred);
+ return MakeConditionalLValue(Sel, AVal, BVal);
+}
+
+/// Apply 'Get' to each value in a collection and build a PHI node from the
+/// resulting values. Get should callable as if of type 'llvm::Value *Get(T)'.
+///
+/// The projection may produce null pointers for some inputs. If all inputs
+/// project to null pointers, a null pointer is returned. Otherwise, null
+/// inputs are replaced by undef values.
+template<typename T, typename Fn>
+static llvm::PHINode *makeProjectedPHI(CodeGenFunction &CGF,
+ ArrayRef<PHIValue<T>> Values, Fn Get) {
+ llvm::PHINode *Phi = nullptr;
+ for (auto &PhiV : Values) {
+ llvm::Value *V = Get(PhiV.Value);
+ if (V) {
+ if (!Phi) {
+ Phi = CGF.Builder.CreatePHI(V->getType(), Values.size());
+ for (auto &PrevPhiV : Values) {
+ if (PrevPhiV.Pred == PhiV.Pred)
+ break;
+ Phi->addIncoming(llvm::UndefValue::get(V->getType()), PrevPhiV.Pred);
+ }
+ }
+ Phi->addIncoming(V, PhiV.Pred);
+ } else if (Phi) {
+ Phi->addIncoming(llvm::UndefValue::get(Phi->getType()), PhiV.Pred);
+ }
+ }
+ return Phi;
+}
+
+llvm::Value *
+CodeGenFunction::EmitPHI(ArrayRef<PHIValue<llvm::Value *>> Values) {
+ return makeProjectedPHI(*this, Values, [](llvm::Value *V) { return V; });
+}
+
+RValue CodeGenFunction::EmitPHI(ArrayRef<PHIValue<RValue>> Values) {
+ assert(!Values.empty() && "no values to select between");
+ RValue First = Values.front().Value;
+
+ if (First.isScalar())
+ return RValue::get(makeProjectedPHI(
+ *this, Values, [](RValue V) { return V.getScalarVal(); }));
+
+ if (First.isComplex()) {
+ llvm::Value *Real = makeProjectedPHI(
+ *this, Values, [](RValue V) { return V.getComplexVal().first; });
+ llvm::Value *Imag = makeProjectedPHI(
+ *this, Values, [](RValue V) { return V.getComplexVal().second; });
+ return RValue::getComplex(Real, Imag);
+ }
+
+ if (First.isAggregate()) {
+ llvm::SmallVector<PHIValue<Address>, 2> Addresses;
+ bool Volatile = false;
+ for (auto &V : Values) {
+ Addresses.push_back({V.Value.getAggregateAddress(), V.Pred});
+ Volatile |= V.Value.isVolatileQualified();
+ }
+ Address Addr = EmitPHI(Addresses);
+ return RValue::getAggregate(Addr, Volatile);
+ }
+
+ llvm_unreachable("unknown rvalue kind");
+}
+
+Address CodeGenFunction::EmitPHI(ArrayRef<PHIValue<Address>> Values) {
+ CharUnits Alignment = CharUnits::Zero();
+ llvm::Value *Ptr =
+ makeProjectedPHI(*this, Values, [&](Address A) -> llvm::Value * {
+ if (!A.isValid())
+ return nullptr;
+ Alignment = Alignment.isZero() ? A.getAlignment()
+ : std::min(Alignment, A.getAlignment());
+ return A.getPointer();
+ });
+ if (!Ptr)
+ return Address::invalid();
+ assert(!Alignment.isZero() && "valid address had zero alignment?");
+ return Address(Ptr, Alignment);
+}
+
LValue CodeGenFunction::
EmitConditionalOperatorLValue(const AbstractConditionalOperator *expr) {
if (!expr->isGLValue()) {
@@ -4477,9 +4680,6 @@
EmitLValueOrThrowExpression(*this, expr->getTrueExpr());
eval.end(*this);
- if (lhs && !lhs->isSimple())
- return EmitUnsupportedLValue(expr, "conditional operator");
-
lhsBlock = Builder.GetInsertBlock();
if (lhs)
Builder.CreateBr(contBlock);
@@ -4490,30 +4690,16 @@
Optional<LValue> rhs =
EmitLValueOrThrowExpression(*this, expr->getFalseExpr());
eval.end(*this);
- if (rhs && !rhs->isSimple())
- return EmitUnsupportedLValue(expr, "conditional operator");
rhsBlock = Builder.GetInsertBlock();
EmitBlock(contBlock);
- if (lhs && rhs) {
- llvm::PHINode *phi =
- Builder.CreatePHI(lhs->getPointer(*this)->getType(), 2, "cond-lvalue");
- phi->addIncoming(lhs->getPointer(*this), lhsBlock);
- phi->addIncoming(rhs->getPointer(*this), rhsBlock);
- Address result(phi, std::min(lhs->getAlignment(), rhs->getAlignment()));
- AlignmentSource alignSource =
- std::max(lhs->getBaseInfo().getAlignmentSource(),
- rhs->getBaseInfo().getAlignmentSource());
- TBAAAccessInfo TBAAInfo = CGM.mergeTBAAInfoForConditionalOperator(
- lhs->getTBAAInfo(), rhs->getTBAAInfo());
- return MakeAddrLValue(result, expr->getType(), LValueBaseInfo(alignSource),
- TBAAInfo);
- } else {
- assert((lhs || rhs) &&
- "both operands of glvalue conditional are throw-expressions?");
- return lhs ? *lhs : *rhs;
- }
+ if (lhs && rhs)
+ return EmitPHI({*lhs, lhsBlock}, {*rhs, rhsBlock});
+
+ assert((lhs || rhs) &&
+ "both operands of glvalue conditional are throw-expressions?");
+ return lhs ? *lhs : *rhs;
}
/// EmitCastLValue - Casts are never lvalues unless that cast is to a reference
Index: clang/lib/CodeGen/CGAtomic.cpp
===================================================================
--- clang/lib/CodeGen/CGAtomic.cpp
+++ clang/lib/CodeGen/CGAtomic.cpp
@@ -43,7 +43,7 @@
AtomicInfo(CodeGenFunction &CGF, LValue &lvalue)
: CGF(CGF), AtomicSizeInBits(0), ValueSizeInBits(0),
EvaluationKind(TEK_Scalar), UseLibcall(true) {
- assert(!lvalue.isGlobalReg());
+ assert(!lvalue.isGlobalReg() && !lvalue.isConditional());
ASTContext &C = CGF.getContext();
if (lvalue.isSimple()) {
AtomicTy = lvalue.getType();
@@ -1522,20 +1522,35 @@
return Load;
}
+/// Determine whether some condition is true for all conditional cases in the
+/// given LValue.
+template<typename Fn>
+static bool forAllConditionals(LValue LV, Fn F) {
+ if (LV.isConditional()) {
+ return forAllConditionals(LV.getConditionalValue(0), F) &&
+ forAllConditionals(LV.getConditionalValue(1), F);
+ }
+ return F(LV);
+}
+
/// An LValue is a candidate for having its loads and stores be made atomic if
/// we are operating under /volatile:ms *and* the LValue itself is volatile and
/// performing such an operation can be performed without a libcall.
bool CodeGenFunction::LValueIsSuitableForInlineAtomic(LValue LV) {
if (!CGM.getCodeGenOpts().MSVolatile) return false;
- AtomicInfo AI(*this, LV);
bool IsVolatile = LV.isVolatile() || hasVolatileMember(LV.getType());
- // An atomic is inline if we don't need to use a libcall.
- bool AtomicIsInline = !AI.shouldUseLibcall();
+ if (!IsVolatile)
+ return false;
// MSVC doesn't seem to do this for types wider than a pointer.
if (getContext().getTypeSize(LV.getType()) >
getContext().getTypeSize(getContext().getIntPtrType()))
return false;
- return IsVolatile && AtomicIsInline;
+ // An atomic is inline if we don't need to use a libcall.
+ bool AtomicIsInline = forAllConditionals(LV, [&](LValue LV) {
+ AtomicInfo Atomics(*this, LV);
+ return !Atomics.shouldUseLibcall();
+ });
+ return AtomicIsInline;
}
RValue CodeGenFunction::EmitAtomicLoad(LValue LV, SourceLocation SL,
@@ -1587,6 +1602,12 @@
RValue CodeGenFunction::EmitAtomicLoad(LValue src, SourceLocation loc,
llvm::AtomicOrdering AO, bool IsVolatile,
AggValueSlot resultSlot) {
+ if (src.isConditional()) {
+ return EmitBranchOnConditionalLValue(src, [&](LValue LV) {
+ return EmitAtomicLoad(LV, loc, AO, IsVolatile, resultSlot);
+ });
+ }
+
AtomicInfo Atomics(*this, src);
return Atomics.EmitAtomicLoad(resultSlot, loc, /*AsValue=*/true, AO,
IsVolatile);
@@ -1989,6 +2010,12 @@
rvalue.getAggregateAddress().getElementType() ==
dest.getAddress(*this).getElementType());
+ if (dest.isConditional()) {
+ return EmitBranchOnConditionalLValue(dest, [&](LValue LV) {
+ return EmitAtomicStore(rvalue, LV, AO, IsVolatile, isInit);
+ });
+ }
+
AtomicInfo atomics(*this, dest);
LValue LVal = atomics.getAtomicLValue();
@@ -2062,6 +2089,14 @@
assert(!Desired.isAggregate() ||
Desired.getAggregateAddress().getElementType() ==
Obj.getAddress(*this).getElementType());
+
+ if (Obj.isConditional()) {
+ return EmitBranchOnConditionalLValue(Obj, [&](LValue LV) {
+ return EmitAtomicCompareExchange(LV, Expected, Desired, Loc, Success,
+ Failure, IsWeak, Slot);
+ });
+ }
+
AtomicInfo Atomics(*this, Obj);
return Atomics.EmitAtomicCompareExchange(Expected, Desired, Success, Failure,
@@ -2070,7 +2105,24 @@
void CodeGenFunction::EmitAtomicUpdate(
LValue LVal, llvm::AtomicOrdering AO,
- const llvm::function_ref<RValue(RValue)> &UpdateOp, bool IsVolatile) {
+ const llvm::function_ref<RValue(RValue)> &UpdateOp, bool IsVolatile,
+ SourceLocation Loc) {
+ if (LVal.isConditional()) {
+ // FIXME: We can mostly EmitBranchOnConditionalLValue here, but there are
+ // two problems with that:
+ // 1) That can result in emitting the same expression twice, which might
+ // not be correct for some expression forms.
+ // 2) UpdateOp might expect to be called only once. That happens for the
+ // various OpenMP atomic update expressions of the form
+ // { v = x; x = f(x); }
+ // which want to capture a single previous value of x.
+ CGM.Error(Loc, "cannot compile this OpenMP atomic bitfield conditional "
+ "lvalue update yet");
+ // Recover by pretending we only saw the first case in the conditional.
+ while (LVal.isConditional())
+ LVal = LVal.getConditionalValue(0);
+ }
+
AtomicInfo Atomics(*this, LVal);
Atomics.EmitAtomicUpdate(AO, UpdateOp, IsVolatile);
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits