mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 21:53:12 +08:00
Simplify some complex emission and implement correct semantics for
assignment to volatiles in C. This in effect reverts some of mjs's work in and around r72572. Basically, the C++ standard is quite clear, except that it lies about volatile behavior approximating C's, whereas the C standard is almost actively misleading. llvm-svn: 119344
This commit is contained in:
@@ -171,11 +171,23 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
|
||||
return Cl::CL_LValue;
|
||||
|
||||
// GNU extensions, simply look through them.
|
||||
case UO_Real:
|
||||
case UO_Imag:
|
||||
case UO_Extension:
|
||||
return ClassifyInternal(Ctx, cast<UnaryOperator>(E)->getSubExpr());
|
||||
|
||||
// Treat _Real and _Imag basically as if they were member
|
||||
// expressions: l-value only if the operand is a true l-value.
|
||||
case UO_Real:
|
||||
case UO_Imag: {
|
||||
const Expr *Op = cast<UnaryOperator>(E)->getSubExpr()->IgnoreParens();
|
||||
Cl::Kinds K = ClassifyInternal(Ctx, Op);
|
||||
if (K != Cl::CL_LValue) return K;
|
||||
|
||||
if (isa<ObjCPropertyRefExpr>(Op) ||
|
||||
isa<ObjCImplicitSetterGetterRefExpr>(Op))
|
||||
return Cl::CL_SubObjCPropertySetting;
|
||||
return Cl::CL_LValue;
|
||||
}
|
||||
|
||||
// C++ [expr.pre.incr]p1: The result is the updated operand; it is an
|
||||
// lvalue, [...]
|
||||
// Not so in C.
|
||||
@@ -343,7 +355,7 @@ static Cl::Kinds ClassifyMemberExpr(ASTContext &Ctx, const MemberExpr *E) {
|
||||
if (E->isArrow())
|
||||
return Cl::CL_LValue;
|
||||
// ObjC property accesses are not lvalues, but get special treatment.
|
||||
Expr *Base = E->getBase();
|
||||
Expr *Base = E->getBase()->IgnoreParens();
|
||||
if (isa<ObjCPropertyRefExpr>(Base) ||
|
||||
isa<ObjCImplicitSetterGetterRefExpr>(Base))
|
||||
return Cl::CL_SubObjCPropertySetting;
|
||||
|
||||
@@ -87,8 +87,7 @@ RValue CodeGenFunction::EmitAnyExpr(const Expr *E, AggValueSlot AggSlot,
|
||||
if (!hasAggregateLLVMType(E->getType()))
|
||||
return RValue::get(EmitScalarExpr(E, IgnoreResult));
|
||||
else if (E->getType()->isAnyComplexType())
|
||||
return RValue::getComplex(EmitComplexExpr(E, false, false,
|
||||
IgnoreResult, IgnoreResult));
|
||||
return RValue::getComplex(EmitComplexExpr(E, IgnoreResult, IgnoreResult));
|
||||
|
||||
EmitAggExpr(E, AggSlot, IgnoreResult);
|
||||
return AggSlot.asRValue();
|
||||
|
||||
@@ -35,14 +35,9 @@ class ComplexExprEmitter
|
||||
// True is we should ignore the value of a
|
||||
bool IgnoreReal;
|
||||
bool IgnoreImag;
|
||||
// True if we should ignore the value of a=b
|
||||
bool IgnoreRealAssign;
|
||||
bool IgnoreImagAssign;
|
||||
public:
|
||||
ComplexExprEmitter(CodeGenFunction &cgf, bool ir=false, bool ii=false,
|
||||
bool irn=false, bool iin=false)
|
||||
: CGF(cgf), Builder(CGF.Builder), IgnoreReal(ir), IgnoreImag(ii),
|
||||
IgnoreRealAssign(irn), IgnoreImagAssign(iin) {
|
||||
ComplexExprEmitter(CodeGenFunction &cgf, bool ir=false, bool ii=false)
|
||||
: CGF(cgf), Builder(CGF.Builder), IgnoreReal(ir), IgnoreImag(ii) {
|
||||
}
|
||||
|
||||
|
||||
@@ -60,16 +55,6 @@ public:
|
||||
IgnoreImag = false;
|
||||
return I;
|
||||
}
|
||||
bool TestAndClearIgnoreRealAssign() {
|
||||
bool I = IgnoreRealAssign;
|
||||
IgnoreRealAssign = false;
|
||||
return I;
|
||||
}
|
||||
bool TestAndClearIgnoreImagAssign() {
|
||||
bool I = IgnoreImagAssign;
|
||||
IgnoreImagAssign = false;
|
||||
return I;
|
||||
}
|
||||
|
||||
/// EmitLoadOfLValue - Given an expression with complex type that represents a
|
||||
/// value l-value, this method emits the address of the l-value, then loads
|
||||
@@ -174,8 +159,6 @@ public:
|
||||
ComplexPairTy VisitUnaryPlus (const UnaryOperator *E) {
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
TestAndClearIgnoreRealAssign();
|
||||
TestAndClearIgnoreImagAssign();
|
||||
return Visit(E->getSubExpr());
|
||||
}
|
||||
ComplexPairTy VisitUnaryMinus (const UnaryOperator *E);
|
||||
@@ -211,6 +194,10 @@ public:
|
||||
};
|
||||
|
||||
BinOpInfo EmitBinOps(const BinaryOperator *E);
|
||||
LValue EmitCompoundAssignLValue(const CompoundAssignOperator *E,
|
||||
ComplexPairTy (ComplexExprEmitter::*Func)
|
||||
(const BinOpInfo &),
|
||||
ComplexPairTy &Val);
|
||||
ComplexPairTy EmitCompoundAssign(const CompoundAssignOperator *E,
|
||||
ComplexPairTy (ComplexExprEmitter::*Func)
|
||||
(const BinOpInfo &));
|
||||
@@ -220,15 +207,15 @@ public:
|
||||
ComplexPairTy EmitBinMul(const BinOpInfo &Op);
|
||||
ComplexPairTy EmitBinDiv(const BinOpInfo &Op);
|
||||
|
||||
ComplexPairTy VisitBinMul(const BinaryOperator *E) {
|
||||
return EmitBinMul(EmitBinOps(E));
|
||||
}
|
||||
ComplexPairTy VisitBinAdd(const BinaryOperator *E) {
|
||||
return EmitBinAdd(EmitBinOps(E));
|
||||
}
|
||||
ComplexPairTy VisitBinSub(const BinaryOperator *E) {
|
||||
return EmitBinSub(EmitBinOps(E));
|
||||
}
|
||||
ComplexPairTy VisitBinMul(const BinaryOperator *E) {
|
||||
return EmitBinMul(EmitBinOps(E));
|
||||
}
|
||||
ComplexPairTy VisitBinDiv(const BinaryOperator *E) {
|
||||
return EmitBinDiv(EmitBinOps(E));
|
||||
}
|
||||
@@ -251,6 +238,9 @@ public:
|
||||
// Logical and/or always return int, never complex.
|
||||
|
||||
// No comparisons produce a complex result.
|
||||
|
||||
LValue EmitBinAssignLValue(const BinaryOperator *E,
|
||||
ComplexPairTy &Val);
|
||||
ComplexPairTy VisitBinAssign (const BinaryOperator *E);
|
||||
ComplexPairTy VisitBinComma (const BinaryOperator *E);
|
||||
|
||||
@@ -383,8 +373,6 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastExpr::CastKind CK, Expr *Op,
|
||||
ComplexPairTy ComplexExprEmitter::VisitUnaryMinus(const UnaryOperator *E) {
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
TestAndClearIgnoreRealAssign();
|
||||
TestAndClearIgnoreImagAssign();
|
||||
ComplexPairTy Op = Visit(E->getSubExpr());
|
||||
|
||||
llvm::Value *ResR, *ResI;
|
||||
@@ -401,8 +389,6 @@ ComplexPairTy ComplexExprEmitter::VisitUnaryMinus(const UnaryOperator *E) {
|
||||
ComplexPairTy ComplexExprEmitter::VisitUnaryNot(const UnaryOperator *E) {
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
TestAndClearIgnoreRealAssign();
|
||||
TestAndClearIgnoreImagAssign();
|
||||
// ~(a+ib) = a + i*-b
|
||||
ComplexPairTy Op = Visit(E->getSubExpr());
|
||||
llvm::Value *ResI;
|
||||
@@ -516,8 +502,6 @@ ComplexExprEmitter::BinOpInfo
|
||||
ComplexExprEmitter::EmitBinOps(const BinaryOperator *E) {
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
TestAndClearIgnoreRealAssign();
|
||||
TestAndClearIgnoreImagAssign();
|
||||
BinOpInfo Ops;
|
||||
Ops.LHS = Visit(E->getLHS());
|
||||
Ops.RHS = Visit(E->getRHS());
|
||||
@@ -526,14 +510,12 @@ ComplexExprEmitter::EmitBinOps(const BinaryOperator *E) {
|
||||
}
|
||||
|
||||
|
||||
// Compound assignments.
|
||||
ComplexPairTy ComplexExprEmitter::
|
||||
EmitCompoundAssign(const CompoundAssignOperator *E,
|
||||
ComplexPairTy (ComplexExprEmitter::*Func)(const BinOpInfo&)){
|
||||
LValue ComplexExprEmitter::
|
||||
EmitCompoundAssignLValue(const CompoundAssignOperator *E,
|
||||
ComplexPairTy (ComplexExprEmitter::*Func)(const BinOpInfo&),
|
||||
ComplexPairTy &Val) {
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
bool ignreal = TestAndClearIgnoreRealAssign();
|
||||
bool ignimag = TestAndClearIgnoreImagAssign();
|
||||
QualType LHSTy = E->getLHS()->getType(), RHSTy = E->getRHS()->getType();
|
||||
|
||||
BinOpInfo OpInfo;
|
||||
@@ -569,6 +551,7 @@ EmitCompoundAssign(const CompoundAssignOperator *E,
|
||||
|
||||
// Truncate the result back to the LHS type.
|
||||
Result = EmitComplexToComplexCast(Result, OpInfo.Ty, LHSTy);
|
||||
Val = Result;
|
||||
|
||||
// Store the result value into the LHS lvalue.
|
||||
if (LHS.isPropertyRef())
|
||||
@@ -579,30 +562,41 @@ EmitCompoundAssign(const CompoundAssignOperator *E,
|
||||
else
|
||||
EmitStoreOfComplex(Result, LHS.getAddress(), LHS.isVolatileQualified());
|
||||
|
||||
// Restore the Ignore* flags.
|
||||
IgnoreReal = ignreal;
|
||||
IgnoreImag = ignimag;
|
||||
IgnoreRealAssign = ignreal;
|
||||
IgnoreImagAssign = ignimag;
|
||||
|
||||
// Objective-C property assignment never reloads the value following a store.
|
||||
if (LHS.isPropertyRef() || LHS.isKVCRef())
|
||||
return Result;
|
||||
|
||||
// Otherwise, reload the value.
|
||||
return EmitLoadOfComplex(LHS.getAddress(), LHS.isVolatileQualified());
|
||||
return LHS;
|
||||
}
|
||||
|
||||
ComplexPairTy ComplexExprEmitter::VisitBinAssign(const BinaryOperator *E) {
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
bool ignreal = TestAndClearIgnoreRealAssign();
|
||||
bool ignimag = TestAndClearIgnoreImagAssign();
|
||||
// Compound assignments.
|
||||
ComplexPairTy ComplexExprEmitter::
|
||||
EmitCompoundAssign(const CompoundAssignOperator *E,
|
||||
ComplexPairTy (ComplexExprEmitter::*Func)(const BinOpInfo&)){
|
||||
ComplexPairTy Val;
|
||||
LValue LV = EmitCompoundAssignLValue(E, Func, Val);
|
||||
|
||||
// The result of an assignment in C is the assigned r-value.
|
||||
if (!CGF.getContext().getLangOptions().CPlusPlus)
|
||||
return Val;
|
||||
|
||||
// Objective-C property assignment never reloads the value following a store.
|
||||
if (LV.isPropertyRef() || LV.isKVCRef())
|
||||
return Val;
|
||||
|
||||
// If the lvalue is non-volatile, return the computed value of the assignment.
|
||||
if (!LV.isVolatileQualified())
|
||||
return Val;
|
||||
|
||||
return EmitLoadOfComplex(LV.getAddress(), LV.isVolatileQualified());
|
||||
}
|
||||
|
||||
LValue ComplexExprEmitter::EmitBinAssignLValue(const BinaryOperator *E,
|
||||
ComplexPairTy &Val) {
|
||||
assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(),
|
||||
E->getRHS()->getType()) &&
|
||||
"Invalid assignment");
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
|
||||
// Emit the RHS.
|
||||
ComplexPairTy Val = Visit(E->getRHS());
|
||||
Val = Visit(E->getRHS());
|
||||
|
||||
// Compute the address to store into.
|
||||
LValue LHS = CGF.EmitLValue(E->getLHS());
|
||||
@@ -615,13 +609,26 @@ ComplexPairTy ComplexExprEmitter::VisitBinAssign(const BinaryOperator *E) {
|
||||
else
|
||||
EmitStoreOfComplex(Val, LHS.getAddress(), LHS.isVolatileQualified());
|
||||
|
||||
// Restore the Ignore* flags.
|
||||
IgnoreReal = ignreal;
|
||||
IgnoreImag = ignimag;
|
||||
IgnoreRealAssign = ignreal;
|
||||
IgnoreImagAssign = ignimag;
|
||||
return LHS;
|
||||
}
|
||||
|
||||
return Val;
|
||||
ComplexPairTy ComplexExprEmitter::VisitBinAssign(const BinaryOperator *E) {
|
||||
ComplexPairTy Val;
|
||||
LValue LV = EmitBinAssignLValue(E, Val);
|
||||
|
||||
// The result of an assignment in C is the assigned r-value.
|
||||
if (!CGF.getContext().getLangOptions().CPlusPlus)
|
||||
return Val;
|
||||
|
||||
// Objective-C property assignment never reloads the value following a store.
|
||||
if (LV.isPropertyRef() || LV.isKVCRef())
|
||||
return Val;
|
||||
|
||||
// If the lvalue is non-volatile, return the computed value of the assignment.
|
||||
if (!LV.isVolatileQualified())
|
||||
return Val;
|
||||
|
||||
return EmitLoadOfComplex(LV.getAddress(), LV.isVolatileQualified());
|
||||
}
|
||||
|
||||
ComplexPairTy ComplexExprEmitter::VisitBinComma(const BinaryOperator *E) {
|
||||
@@ -632,11 +639,8 @@ ComplexPairTy ComplexExprEmitter::VisitBinComma(const BinaryOperator *E) {
|
||||
|
||||
ComplexPairTy ComplexExprEmitter::
|
||||
VisitConditionalOperator(const ConditionalOperator *E) {
|
||||
|
||||
TestAndClearIgnoreReal();
|
||||
TestAndClearIgnoreImag();
|
||||
TestAndClearIgnoreRealAssign();
|
||||
TestAndClearIgnoreImagAssign();
|
||||
llvm::BasicBlock *LHSBlock = CGF.createBasicBlock("cond.true");
|
||||
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("cond.false");
|
||||
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("cond.end");
|
||||
@@ -724,12 +728,11 @@ ComplexPairTy ComplexExprEmitter::VisitVAArgExpr(VAArgExpr *E) {
|
||||
/// EmitComplexExpr - Emit the computation of the specified expression of
|
||||
/// complex type, ignoring the result.
|
||||
ComplexPairTy CodeGenFunction::EmitComplexExpr(const Expr *E, bool IgnoreReal,
|
||||
bool IgnoreImag, bool IgnoreRealAssign, bool IgnoreImagAssign) {
|
||||
bool IgnoreImag) {
|
||||
assert(E && E->getType()->isAnyComplexType() &&
|
||||
"Invalid complex expression to emit");
|
||||
|
||||
return ComplexExprEmitter(*this, IgnoreReal, IgnoreImag, IgnoreRealAssign,
|
||||
IgnoreImagAssign)
|
||||
return ComplexExprEmitter(*this, IgnoreReal, IgnoreImag)
|
||||
.Visit(const_cast<Expr*>(E));
|
||||
}
|
||||
|
||||
|
||||
@@ -1176,12 +1176,11 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) {
|
||||
|
||||
case CK_FloatingComplexToReal:
|
||||
case CK_IntegralComplexToReal:
|
||||
return CGF.EmitComplexExpr(E, false, true, false, true).first;
|
||||
return CGF.EmitComplexExpr(E, false, true).first;
|
||||
|
||||
case CK_FloatingComplexToBoolean:
|
||||
case CK_IntegralComplexToBoolean: {
|
||||
CodeGenFunction::ComplexPairTy V
|
||||
= CGF.EmitComplexExpr(E, false, false, false, false);
|
||||
CodeGenFunction::ComplexPairTy V = CGF.EmitComplexExpr(E);
|
||||
|
||||
// TODO: kill this function off, inline appropriate case here
|
||||
return EmitComplexToScalarConversion(V, E->getType(), DestTy);
|
||||
@@ -1471,21 +1470,38 @@ ScalarExprEmitter::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E) {
|
||||
|
||||
Value *ScalarExprEmitter::VisitUnaryReal(const UnaryOperator *E) {
|
||||
Expr *Op = E->getSubExpr();
|
||||
if (Op->getType()->isAnyComplexType())
|
||||
return CGF.EmitComplexExpr(Op, false, true, false, true).first;
|
||||
if (Op->getType()->isAnyComplexType()) {
|
||||
// If it's an l-value, load through the appropriate subobject l-value.
|
||||
// Note that we have to ask E because Op might be an l-value that
|
||||
// this won't work for, e.g. an Obj-C property.
|
||||
if (E->isLvalue(CGF.getContext()) == Expr::LV_Valid)
|
||||
return CGF.EmitLoadOfLValue(CGF.EmitLValue(E), E->getType())
|
||||
.getScalarVal();
|
||||
|
||||
// Otherwise, calculate and project.
|
||||
return CGF.EmitComplexExpr(Op, false, true).first;
|
||||
}
|
||||
|
||||
return Visit(Op);
|
||||
}
|
||||
|
||||
Value *ScalarExprEmitter::VisitUnaryImag(const UnaryOperator *E) {
|
||||
Expr *Op = E->getSubExpr();
|
||||
if (Op->getType()->isAnyComplexType())
|
||||
return CGF.EmitComplexExpr(Op, true, false, true, false).second;
|
||||
if (Op->getType()->isAnyComplexType()) {
|
||||
// If it's an l-value, load through the appropriate subobject l-value.
|
||||
// Note that we have to ask E because Op might be an l-value that
|
||||
// this won't work for, e.g. an Obj-C property.
|
||||
if (Op->isLvalue(CGF.getContext()) == Expr::LV_Valid)
|
||||
return CGF.EmitLoadOfLValue(CGF.EmitLValue(E), E->getType())
|
||||
.getScalarVal();
|
||||
|
||||
// Otherwise, calculate and project.
|
||||
return CGF.EmitComplexExpr(Op, true, false).second;
|
||||
}
|
||||
|
||||
// __imag on a scalar returns zero. Emit the subexpr to ensure side
|
||||
// effects are evaluated, but not the actual value.
|
||||
if (E->isLvalue(CGF.getContext()) == Expr::LV_Valid)
|
||||
CGF.EmitLValue(Op);
|
||||
else
|
||||
CGF.EmitScalarExpr(Op, true);
|
||||
CGF.EmitScalarExpr(Op, true);
|
||||
return llvm::Constant::getNullValue(ConvertType(E->getType()));
|
||||
}
|
||||
|
||||
@@ -1562,6 +1578,10 @@ Value *ScalarExprEmitter::EmitCompoundAssign(const CompoundAssignOperator *E,
|
||||
if (Ignore)
|
||||
return 0;
|
||||
|
||||
// The result of an assignment in C is the assigned r-value.
|
||||
if (!CGF.getContext().getLangOptions().CPlusPlus)
|
||||
return RHS;
|
||||
|
||||
// Objective-C property assignment never reloads the value following a store.
|
||||
if (LHS.isPropertyRef() || LHS.isKVCRef())
|
||||
return RHS;
|
||||
@@ -2050,6 +2070,10 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
|
||||
if (Ignore)
|
||||
return 0;
|
||||
|
||||
// The result of an assignment in C is the assigned r-value.
|
||||
if (!CGF.getContext().getLangOptions().CPlusPlus)
|
||||
return RHS;
|
||||
|
||||
// Objective-C property assignment never reloads the value following a store.
|
||||
if (LHS.isPropertyRef() || LHS.isKVCRef())
|
||||
return RHS;
|
||||
|
||||
@@ -1593,10 +1593,9 @@ public:
|
||||
|
||||
/// EmitComplexExpr - Emit the computation of the specified expression of
|
||||
/// complex type, returning the result.
|
||||
ComplexPairTy EmitComplexExpr(const Expr *E, bool IgnoreReal = false,
|
||||
bool IgnoreImag = false,
|
||||
bool IgnoreRealAssign = false,
|
||||
bool IgnoreImagAssign = false);
|
||||
ComplexPairTy EmitComplexExpr(const Expr *E,
|
||||
bool IgnoreReal = false,
|
||||
bool IgnoreImag = false);
|
||||
|
||||
/// EmitComplexExprIntoAddr - Emit the computation of the specified expression
|
||||
/// of complex type, storing into the specified Value*.
|
||||
|
||||
@@ -15,15 +15,15 @@ void f0() {
|
||||
y = (x = 1);
|
||||
}
|
||||
|
||||
// Check that we do generate reloads for volatile access.
|
||||
// This used to test that we generate reloads for volatile access,
|
||||
// but that does not appear to be correct behavior for C.
|
||||
//
|
||||
// CHECK: define void @f1()
|
||||
// CHECK: [[x_1:%.*]] = alloca i32, align 4
|
||||
// CHECK-NEXT: [[y_1:%.*]] = alloca i32, align 4
|
||||
// CHECK-NEXT: volatile store i32 1, i32* [[x_1]]
|
||||
// CHECK-NEXT: volatile store i32 1, i32* [[x_1]]
|
||||
// CHECK-NEXT: [[tmp_1:%.*]] = volatile load i32* [[x_1]]
|
||||
// CHECK-NEXT: volatile store i32 [[tmp_1]], i32* [[y_1]]
|
||||
// CHECK-NEXT: volatile store i32 1, i32* [[y_1]]
|
||||
// CHECK: }
|
||||
void f1() {
|
||||
volatile int x, y;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// RUN: %clang_cc1 -Wno-unused-value -emit-llvm < %s -o %t
|
||||
// RUN: grep volatile %t | count 145
|
||||
// RUN: grep memcpy %t | count 4
|
||||
// RUN: %clang_cc1 -Wno-unused-value -emit-llvm %s -o - | FileCheck %s
|
||||
|
||||
// CHECK: @i = common global [[INT:i[0-9]+]] 0
|
||||
volatile int i, j, k;
|
||||
volatile int ar[5];
|
||||
volatile char c;
|
||||
// CHECK: @ci = common global [[CINT:%.*]] zeroinitializer
|
||||
volatile _Complex int ci;
|
||||
volatile struct S {
|
||||
#ifdef __cplusplus
|
||||
@@ -16,67 +16,190 @@ volatile struct S {
|
||||
//void operator =(volatile struct S&o1, volatile struct S&o2) volatile;
|
||||
int printf(const char *, ...);
|
||||
|
||||
int main() {
|
||||
// A use.
|
||||
|
||||
// Note that these test results are very much specific to C!
|
||||
// Assignments in C++ yield l-values, not r-values, and the situations
|
||||
// that do implicit lvalue-to-rvalue conversion are substantially
|
||||
// reduced.
|
||||
|
||||
// CHECK: define void @test()
|
||||
void test() {
|
||||
// CHECK: volatile load [[INT]]* @i
|
||||
i;
|
||||
// A use of the real part
|
||||
// CHECK-NEXT: volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: sitofp [[INT]]
|
||||
(float)(ci);
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
(void)ci;
|
||||
// A use.
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: memcpy
|
||||
(void)a;
|
||||
// Not a use.
|
||||
// CHECK-NEXT: [[R:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: [[I:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: volatile store [[INT]] [[R]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: volatile store [[INT]] [[I]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
(void)(ci=ci);
|
||||
// Not a use.
|
||||
// CHECK-NEXT: [[T:%.*]] = volatile load [[INT]]* @j
|
||||
// CHECK-NEXT: volatile store [[INT]] [[T]], [[INT]]* @i
|
||||
(void)(i=j);
|
||||
// CHECK-NEXT: [[R1:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: [[I1:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: [[R2:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: [[I2:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// Not sure why they're ordered this way.
|
||||
// CHECK-NEXT: [[R:%.*]] = add [[INT]] [[R2]], [[R1]]
|
||||
// CHECK-NEXT: [[I:%.*]] = add [[INT]] [[I2]], [[I1]]
|
||||
// CHECK-NEXT: volatile store [[INT]] [[R]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: volatile store [[INT]] [[I]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
ci+=ci;
|
||||
|
||||
// CHECK-NEXT: [[R1:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: [[I1:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: [[R2:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: [[I2:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: [[R:%.*]] = add [[INT]] [[R2]], [[R1]]
|
||||
// CHECK-NEXT: [[I:%.*]] = add [[INT]] [[I2]], [[I1]]
|
||||
// CHECK-NEXT: volatile store [[INT]] [[R]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: volatile store [[INT]] [[I]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: [[R2:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 0)
|
||||
// CHECK-NEXT: [[I2:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// These additions can be elided
|
||||
// CHECK-NEXT: add [[INT]] [[R]], [[R2]]
|
||||
// CHECK-NEXT: add [[INT]] [[I]], [[I2]]
|
||||
(ci += ci) + ci;
|
||||
// CHECK-NEXT: call void asm
|
||||
asm("nop");
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add nsw [[INT]]
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add nsw [[INT]]
|
||||
(i += j) + k;
|
||||
// CHECK-NEXT: call void asm
|
||||
asm("nop");
|
||||
// A use
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add nsw [[INT]]
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: add nsw [[INT]]
|
||||
(i += j) + 1;
|
||||
// CHECK-NEXT: call void asm
|
||||
asm("nop");
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add [[INT]]
|
||||
// CHECK-NEXT: add [[INT]]
|
||||
ci+ci;
|
||||
// A use.
|
||||
|
||||
// CHECK-NEXT: volatile load
|
||||
__real i;
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
+ci;
|
||||
// CHECK-NEXT: call void asm
|
||||
asm("nop");
|
||||
// Not a use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
(void)(i=i);
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: sitofp
|
||||
(float)(i=i);
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
(void)i;
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
i=i;
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
i=i=i;
|
||||
#ifndef __cplusplus
|
||||
// Not a use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
(void)__builtin_choose_expr(0, i=i, j=j);
|
||||
#endif
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: icmp
|
||||
// CHECK-NEXT: br i1
|
||||
// CHECK: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: br label
|
||||
// CHECK: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: br label
|
||||
k ? (i=i) : (j=j);
|
||||
// CHECK: phi
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
(void)(i,(i=i));
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile load
|
||||
i=i,i;
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
(i=j,k=j);
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile load
|
||||
(i=j,k);
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
(i,j);
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: trunc
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: sext
|
||||
// CHECK-NEXT: volatile store
|
||||
i=c=k;
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add nsw [[INT]]
|
||||
// CHECK-NEXT: volatile store
|
||||
i+=k;
|
||||
// A use of both.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
ci;
|
||||
#ifndef __cplusplus
|
||||
// A use of _real.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
(int)ci;
|
||||
// A use of both.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: icmp ne
|
||||
// CHECK-NEXT: icmp ne
|
||||
// CHECK-NEXT: or i1
|
||||
(_Bool)ci;
|
||||
#endif
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
ci=ci;
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
ci=ci=ci;
|
||||
// CHECK-NEXT: [[T:%.*]] = volatile load [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: volatile store [[INT]] [[T]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
// CHECK-NEXT: volatile store [[INT]] [[T]], [[INT]]* getelementptr inbounds ([[CINT]]* @ci, i32 0, i32 1)
|
||||
__imag ci = __imag ci = __imag ci;
|
||||
// Not a use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
__real (i = j);
|
||||
// Not a use.
|
||||
// CHECK-NEXT: volatile load
|
||||
__imag i;
|
||||
|
||||
// ============================================================
|
||||
@@ -91,6 +214,9 @@ int main() {
|
||||
// gcc.
|
||||
|
||||
// Not a use. gcc forgets to do the assignment.
|
||||
// CHECK-NEXT: call void @llvm.memcpy{{.*}}, i1 true
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: call void @llvm.memcpy{{.*}}, i1 true
|
||||
((a=a),a);
|
||||
|
||||
// Not a use. gcc gets this wrong, it doesn't emit the copy!
|
||||
@@ -98,38 +224,72 @@ int main() {
|
||||
|
||||
// Not a use. gcc got this wrong in 4.2 and omitted the side effects
|
||||
// entirely, but it is fixed in 4.4.0.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
__imag (i = j);
|
||||
|
||||
#ifndef __cplusplus
|
||||
// A use of the real part
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: sitofp
|
||||
(float)(ci=ci);
|
||||
// Not a use, bug? gcc treats this as not a use, that's probably a bug due to
|
||||
// tree folding ignoring volatile.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
(int)(ci=ci);
|
||||
#endif
|
||||
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: sitofp
|
||||
(float)(i=i);
|
||||
// A use. gcc treats this as not a use, that's probably a bug due to tree
|
||||
// folding ignoring volatile.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
(int)(i=i);
|
||||
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: sub
|
||||
-(i=j);
|
||||
// A use. gcc treats this a not a use, that's probably a bug due to tree
|
||||
// folding ignoring volatile.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
+(i=k);
|
||||
|
||||
// A use. gcc treats this a not a use, that's probably a bug due to tree
|
||||
// folding ignoring volatile.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile store
|
||||
__real (ci=ci);
|
||||
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add
|
||||
i + 0;
|
||||
// A use.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: add
|
||||
(i=j) + i;
|
||||
// A use. gcc treats this as not a use, that's probably a bug due to tree
|
||||
// folding ignoring volatile.
|
||||
// CHECK-NEXT: volatile load
|
||||
// CHECK-NEXT: volatile store
|
||||
// CHECK-NEXT: add
|
||||
(i=j) + 0;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user