RValue CodeGenFunction::EmitAnyExpr(const Expr *E, llvm::Value *AggLoc,
bool isAggLocVolatile, bool IgnoreResult) {
if (!hasAggregateLLVMType(E->getType()))
- return RValue::get(EmitScalarExpr(E));
+ return RValue::get(EmitScalarExpr(E, IgnoreResult));
else if (E->getType()->isAnyComplexType())
- return RValue::getComplex(EmitComplexExpr(E));
+ return RValue::getComplex(EmitComplexExpr(E, false, false,
+ IgnoreResult, IgnoreResult));
EmitAggExpr(E, AggLoc, isAggLocVolatile, IgnoreResult);
return RValue::getAggregate(AggLoc, isAggLocVolatile);
}
void AggExprEmitter::VisitBinComma(const BinaryOperator *E) {
- CGF.EmitAnyExpr(E->getLHS());
+ CGF.EmitAnyExpr(E->getLHS(), 0, false, true);
CGF.EmitAggExpr(E->getRHS(), DestPtr, VolatileDest);
}
}
void AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV) {
+ // FIXME: Ignore result?
// FIXME: Are initializers affected by volatile?
if (isa<ImplicitValueInitExpr>(E)) {
EmitNullInitializationToLValue(LV, E->getType());
: public StmtVisitor<ComplexExprEmitter, ComplexPairTy> {
CodeGenFunction &CGF;
CGBuilderTy &Builder;
+ // 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) : CGF(cgf), Builder(CGF.Builder) {
+ 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) {
}
// Utilities
//===--------------------------------------------------------------------===//
+ bool TestAndClearIgnoreReal() {
+ bool I = IgnoreReal;
+ IgnoreReal = false;
+ return I;
+ }
+ bool TestAndClearIgnoreImag() {
+ bool I = IgnoreImag;
+ 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
/// and returns the result.
}
ComplexPairTy VisitUnaryDeref(const Expr *E) { return EmitLoadOfLValue(E); }
ComplexPairTy VisitUnaryPlus (const UnaryOperator *E) {
+ TestAndClearIgnoreReal();
+ TestAndClearIgnoreImag();
+ TestAndClearIgnoreRealAssign();
+ TestAndClearIgnoreImagAssign();
return Visit(E->getSubExpr());
}
ComplexPairTy VisitUnaryMinus (const UnaryOperator *E);
llvm::SmallString<64> Name(SrcPtr->getNameStart(),
SrcPtr->getNameStart()+SrcPtr->getNameLen());
- Name += ".realp";
- llvm::Value *RealPtr = Builder.CreateStructGEP(SrcPtr, 0, Name.c_str());
+ llvm::Value *Real=0, *Imag=0;
- Name.pop_back(); // .realp -> .real
- llvm::Value *Real = Builder.CreateLoad(RealPtr, isVolatile, Name.c_str());
+ if (!IgnoreReal) {
+ Name += ".realp";
+ llvm::Value *RealPtr = Builder.CreateStructGEP(SrcPtr, 0, Name.c_str());
+
+ Name.pop_back(); // .realp -> .real
+ Real = Builder.CreateLoad(RealPtr, isVolatile, Name.c_str());
+ Name.resize(Name.size()-4); // .real -> .imagp
+ }
- Name.resize(Name.size()-4); // .real -> .imagp
- Name += "imagp";
+ if (!IgnoreImag) {
+ Name += "imagp";
- llvm::Value *ImagPtr = Builder.CreateStructGEP(SrcPtr, 1, Name.c_str());
+ llvm::Value *ImagPtr = Builder.CreateStructGEP(SrcPtr, 1, Name.c_str());
- Name.pop_back(); // .imagp -> .imag
- llvm::Value *Imag = Builder.CreateLoad(ImagPtr, isVolatile, Name.c_str());
+ Name.pop_back(); // .imagp -> .imag
+ Imag = Builder.CreateLoad(ImagPtr, isVolatile, Name.c_str());
+ }
return ComplexPairTy(Real, Imag);
}
}
ComplexPairTy ComplexExprEmitter::VisitUnaryMinus(const UnaryOperator *E) {
+ TestAndClearIgnoreReal();
+ TestAndClearIgnoreImag();
+ TestAndClearIgnoreRealAssign();
+ TestAndClearIgnoreImagAssign();
ComplexPairTy Op = Visit(E->getSubExpr());
llvm::Value *ResR = Builder.CreateNeg(Op.first, "neg.r");
llvm::Value *ResI = Builder.CreateNeg(Op.second, "neg.i");
}
ComplexPairTy ComplexExprEmitter::VisitUnaryNot(const UnaryOperator *E) {
+ TestAndClearIgnoreReal();
+ TestAndClearIgnoreImag();
+ TestAndClearIgnoreRealAssign();
+ TestAndClearIgnoreImagAssign();
// ~(a+ib) = a + i*-b
ComplexPairTy Op = Visit(E->getSubExpr());
llvm::Value *ResI = Builder.CreateNeg(Op.second, "conj.i");
ComplexExprEmitter::BinOpInfo
ComplexExprEmitter::EmitBinOps(const BinaryOperator *E) {
+ TestAndClearIgnoreReal();
+ TestAndClearIgnoreImag();
+ TestAndClearIgnoreRealAssign();
+ TestAndClearIgnoreImagAssign();
BinOpInfo Ops;
Ops.LHS = Visit(E->getLHS());
Ops.RHS = Visit(E->getRHS());
ComplexPairTy ComplexExprEmitter::
EmitCompoundAssign(const CompoundAssignOperator *E,
ComplexPairTy (ComplexExprEmitter::*Func)(const BinOpInfo&)){
+ TestAndClearIgnoreReal();
+ TestAndClearIgnoreImag();
+ bool ignreal = TestAndClearIgnoreRealAssign();
+ bool ignimag = TestAndClearIgnoreImagAssign();
QualType LHSTy = E->getLHS()->getType(), RHSTy = E->getRHS()->getType();
-
- // Load the LHS and RHS operands.
- LValue LHSLV = CGF.EmitLValue(E->getLHS());
BinOpInfo OpInfo;
+
+ // Load the RHS and LHS operands.
+ // __block variables need to have the rhs evaluated first, plus this should
+ // improve codegen a little. It is possible for the RHS to be complex or
+ // scalar.
OpInfo.Ty = E->getComputationResultType();
+ OpInfo.RHS = EmitCast(E->getRHS(), OpInfo.Ty);
+
+ LValue LHSLV = CGF.EmitLValue(E->getLHS());
+
// We know the LHS is a complex lvalue.
- OpInfo.LHS = EmitLoadOfComplex(LHSLV.getAddress(), LHSLV.isVolatileQualified());
- OpInfo.LHS = EmitComplexToComplexCast(OpInfo.LHS, LHSTy, OpInfo.Ty);
+ OpInfo.LHS=EmitLoadOfComplex(LHSLV.getAddress(),LHSLV.isVolatileQualified());
+ OpInfo.LHS=EmitComplexToComplexCast(OpInfo.LHS, LHSTy, OpInfo.Ty);
- // It is possible for the RHS to be complex or scalar.
- OpInfo.RHS = EmitCast(E->getRHS(), OpInfo.Ty);
-
// Expand the binary operator.
ComplexPairTy Result = (this->*Func)(OpInfo);
// Store the result value into the LHS lvalue.
EmitStoreOfComplex(Result, LHSLV.getAddress(), LHSLV.isVolatileQualified());
- return Result;
+ // And now return the LHS
+ IgnoreReal = ignreal;
+ IgnoreImag = ignimag;
+ IgnoreRealAssign = ignreal;
+ IgnoreImagAssign = ignimag;
+ return EmitLoadOfComplex(LHSLV.getAddress(), LHSLV.isVolatileQualified());
}
ComplexPairTy ComplexExprEmitter::VisitBinAssign(const BinaryOperator *E) {
+ TestAndClearIgnoreReal();
+ TestAndClearIgnoreImag();
+ bool ignreal = TestAndClearIgnoreRealAssign();
+ bool ignimag = TestAndClearIgnoreImagAssign();
assert(CGF.getContext().getCanonicalType(E->getLHS()->getType()) ==
CGF.getContext().getCanonicalType(E->getRHS()->getType()) &&
"Invalid assignment");
// Store into it.
EmitStoreOfComplex(Val, LHS.getAddress(), LHS.isVolatileQualified());
- return Val;
+ // And now return the LHS
+ IgnoreReal = ignreal;
+ IgnoreImag = ignimag;
+ IgnoreRealAssign = ignreal;
+ IgnoreImagAssign = ignimag;
+ return EmitLoadOfComplex(LHS.getAddress(), LHS.isVolatileQualified());
}
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");
}
ComplexPairTy ComplexExprEmitter::VisitInitListExpr(InitListExpr *E) {
+ bool Ignore = TestAndClearIgnoreReal();
+ (void)Ignore;
+ assert (Ignore == false && "init list ignored");
+ Ignore = TestAndClearIgnoreImag();
+ (void)Ignore;
+ assert (Ignore == false && "init list ignored");
if (E->getNumInits())
return Visit(E->getInit(0));
/// EmitComplexExpr - Emit the computation of the specified expression of
/// complex type, ignoring the result.
-ComplexPairTy CodeGenFunction::EmitComplexExpr(const Expr *E) {
+ComplexPairTy CodeGenFunction::EmitComplexExpr(const Expr *E, bool IgnoreReal,
+ bool IgnoreImag, bool IgnoreRealAssign, bool IgnoreImagAssign) {
assert(E && E->getType()->isAnyComplexType() &&
"Invalid complex expression to emit");
- return ComplexExprEmitter(*this).Visit(const_cast<Expr*>(E));
+ return ComplexExprEmitter(*this, IgnoreReal, IgnoreImag, IgnoreRealAssign,
+ IgnoreImagAssign)
+ .Visit(const_cast<Expr*>(E));
}
/// EmitComplexExprIntoAddr - Emit the computation of the specified expression
: public StmtVisitor<ScalarExprEmitter, Value*> {
CodeGenFunction &CGF;
CGBuilderTy &Builder;
+ bool IgnoreResultAssign;
public:
- ScalarExprEmitter(CodeGenFunction &cgf) : CGF(cgf),
- Builder(CGF.Builder) {
+ ScalarExprEmitter(CodeGenFunction &cgf, bool ira=false)
+ : CGF(cgf), Builder(CGF.Builder), IgnoreResultAssign(ira) {
}
//===--------------------------------------------------------------------===//
// Utilities
//===--------------------------------------------------------------------===//
+ bool TestAndClearIgnoreResultAssign() {
+ bool I = IgnoreResultAssign; IgnoreResultAssign = false;
+ return I; }
+
const llvm::Type *ConvertType(QualType T) { return CGF.ConvertType(T); }
LValue EmitLValue(const Expr *E) { return CGF.EmitLValue(E); }
Value *VisitPredefinedExpr(Expr *E) { return EmitLValue(E).getAddress(); }
Value *VisitInitListExpr(InitListExpr *E) {
+ bool Ignore = TestAndClearIgnoreResultAssign();
+ (void)Ignore;
+ assert (Ignore == false && "init list ignored");
unsigned NumInitElements = E->getNumInits();
if (E->hadArrayRangeDesignator()) {
}
Value *VisitUnaryDeref(const Expr *E) { return EmitLoadOfLValue(E); }
Value *VisitUnaryPlus(const UnaryOperator *E) {
+ // This differs from gcc, though, most likely due to a bug in gcc.
+ TestAndClearIgnoreResultAssign();
return Visit(E->getSubExpr());
}
Value *VisitUnaryMinus (const UnaryOperator *E);
}
Value *ScalarExprEmitter::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
+ TestAndClearIgnoreResultAssign();
+
// Emit subscript expressions in rvalue context's. For most cases, this just
// loads the lvalue formed by the subscript expr. However, we have to be
// careful, because the base of a vector subscript is occasionally an rvalue,
// have to handle a more broad range of conversions than explicit casts, as they
// handle things like function to ptr-to-function decay etc.
Value *ScalarExprEmitter::EmitCastExpr(const Expr *E, QualType DestTy) {
+ if (!DestTy->isVoidType())
+ TestAndClearIgnoreResultAssign();
+
// Handle cases where the source is an non-complex type.
if (!CGF.hasAggregateLLVMType(E->getType())) {
if (E->getType()->isAnyComplexType()) {
// Handle cases where the source is a complex type.
- return EmitComplexToScalarConversion(CGF.EmitComplexExpr(E), E->getType(),
- DestTy);
+ bool IgnoreImag = true;
+ bool IgnoreImagAssign = true;
+ bool IgnoreReal = IgnoreResultAssign;
+ bool IgnoreRealAssign = IgnoreResultAssign;
+ if (DestTy->isBooleanType())
+ IgnoreImagAssign = IgnoreImag = false;
+ else if (DestTy->isVoidType()) {
+ IgnoreReal = IgnoreImag = false;
+ IgnoreRealAssign = IgnoreImagAssign = true;
+ }
+ CodeGenFunction::ComplexPairTy V
+ = CGF.EmitComplexExpr(E, IgnoreReal, IgnoreImag, IgnoreRealAssign,
+ IgnoreImagAssign);
+ return EmitComplexToScalarConversion(V, E->getType(), DestTy);
}
// Okay, this is a cast from an aggregate. It must be a cast to void. Just
// evaluate the result and return.
- CGF.EmitAggExpr(E, 0, false);
+ CGF.EmitAggExpr(E, 0, false, true);
return 0;
}
Value *ScalarExprEmitter::VisitUnaryMinus(const UnaryOperator *E) {
+ TestAndClearIgnoreResultAssign();
Value *Op = Visit(E->getSubExpr());
return Builder.CreateNeg(Op, "neg");
}
Value *ScalarExprEmitter::VisitUnaryNot(const UnaryOperator *E) {
+ TestAndClearIgnoreResultAssign();
Value *Op = Visit(E->getSubExpr());
return Builder.CreateNot(Op, "neg");
}
Value *ScalarExprEmitter::VisitUnaryReal(const UnaryOperator *E) {
Expr *Op = E->getSubExpr();
if (Op->getType()->isAnyComplexType())
- return CGF.EmitComplexExpr(Op).first;
+ return CGF.EmitComplexExpr(Op, false, true, false, true).first;
return Visit(Op);
}
Value *ScalarExprEmitter::VisitUnaryImag(const UnaryOperator *E) {
Expr *Op = E->getSubExpr();
if (Op->getType()->isAnyComplexType())
- return CGF.EmitComplexExpr(Op).second;
+ return CGF.EmitComplexExpr(Op, true, false, true, false).second;
- // __imag on a scalar returns zero. Emit it the subexpr to ensure side
- // effects are evaluated.
- CGF.EmitScalarExpr(Op);
+ // __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);
return llvm::Constant::getNullValue(ConvertType(E->getType()));
}
//===----------------------------------------------------------------------===//
BinOpInfo ScalarExprEmitter::EmitBinOps(const BinaryOperator *E) {
+ TestAndClearIgnoreResultAssign();
BinOpInfo Result;
Result.LHS = Visit(E->getLHS());
Result.RHS = Visit(E->getRHS());
Value *ScalarExprEmitter::EmitCompoundAssign(const CompoundAssignOperator *E,
Value *(ScalarExprEmitter::*Func)(const BinOpInfo &)) {
+ bool Ignore = TestAndClearIgnoreResultAssign();
QualType LHSTy = E->getLHS()->getType(), RHSTy = E->getRHS()->getType();
BinOpInfo OpInfo;
// handled specially because the result is altered by the store,
// i.e., [C99 6.5.16p1] 'An assignment expression has the value of
// the left operand after the assignment...'.
- if (LHSLV.isBitfield())
- CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy,
- &Result);
- else
+ if (LHSLV.isBitfield()) {
+ if (!LHSLV.isVolatileQualified()) {
+ CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy,
+ &Result);
+ return Result;
+ } else
+ CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy);
+ } else
CGF.EmitStoreThroughLValue(RValue::get(Result), LHSLV, LHSTy);
-
- return Result;
+ if (Ignore)
+ return 0;
+ return EmitLoadOfLValue(LHSLV, E->getType());
}
Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E,unsigned UICmpOpc,
unsigned SICmpOpc, unsigned FCmpOpc) {
+ TestAndClearIgnoreResultAssign();
Value *Result;
QualType LHSTy = E->getLHS()->getType();
if (!LHSTy->isAnyComplexType() && !LHSTy->isVectorType()) {
}
Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
- // __block variables need to have the rhs evaluated first, plus
- // this should improve codegen just a little.
+ bool Ignore = TestAndClearIgnoreResultAssign();
+
+ // __block variables need to have the rhs evaluated first, plus this should
+ // improve codegen just a little.
Value *RHS = Visit(E->getRHS());
LValue LHS = EmitLValue(E->getLHS());
// because the result is altered by the store, i.e., [C99 6.5.16p1]
// 'An assignment expression has the value of the left operand after
// the assignment...'.
- if (LHS.isBitfield())
- CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(),
- &RHS);
- else
+ if (LHS.isBitfield()) {
+ if (!LHS.isVolatileQualified()) {
+ CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(),
+ &RHS);
+ return RHS;
+ } else
+ CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType());
+ } else
CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS, E->getType());
-
- // Return the RHS.
- return RHS;
+ if (Ignore)
+ return 0;
+ return EmitLoadOfLValue(LHS, E->getType());
}
Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
Value *ScalarExprEmitter::
VisitConditionalOperator(const ConditionalOperator *E) {
+ TestAndClearIgnoreResultAssign();
// If the condition constant folds and can be elided, try to avoid emitting
// the condition and the dead arm.
if (int Cond = CGF.ConstantFoldsToSimpleInteger(E->getCond())){
if (!ArgPtr)
return Builder.CreateVAArg(ArgValue, ConvertType(VE->getType()));
+ // FIXME Volatility.
return Builder.CreateLoad(ArgPtr);
}
// Entry Point into this File
//===----------------------------------------------------------------------===//
-/// EmitComplexExpr - Emit the computation of the specified expression of
-/// complex type, ignoring the result.
-Value *CodeGenFunction::EmitScalarExpr(const Expr *E) {
+/// EmitScalarExpr - Emit the computation of the specified expression of
+/// scalar type, ignoring the result.
+Value *CodeGenFunction::EmitScalarExpr(const Expr *E, bool IgnoreResultAssign) {
assert(E && !hasAggregateLLVMType(E->getType()) &&
"Invalid scalar expression to emit");
- return ScalarExprEmitter(*this).Visit(const_cast<Expr*>(E));
+ return ScalarExprEmitter(*this, IgnoreResultAssign)
+ .Visit(const_cast<Expr*>(E));
}
/// EmitScalarConversion - Emit a conversion from the specified type to the
/// EmitScalarExpr - Emit the computation of the specified expression of LLVM
/// scalar type, returning the result.
- llvm::Value *EmitScalarExpr(const Expr *E);
+ llvm::Value *EmitScalarExpr(const Expr *E , bool IgnoreResultAssign=false);
/// EmitScalarConversion - Emit a conversion from the specified type to the
/// specified destination type, both of which are LLVM scalar types.
/// EmitComplexExpr - Emit the computation of the specified expression of
/// complex type, returning the result.
- ComplexPairTy EmitComplexExpr(const Expr *E);
+ ComplexPairTy EmitComplexExpr(const Expr *E, bool IgnoreReal = false,
+ bool IgnoreImag = false,
+ bool IgnoreRealAssign = false,
+ bool IgnoreImagAssign = false);
/// EmitComplexExprIntoAddr - Emit the computation of the specified expression
/// of complex type, storing into the specified Value*.
--- /dev/null
+// RUN: clang-cc -Wno-unused-value -emit-llvm < %s -o %t &&
+// RUN: grep volatile %t | count 145 &&
+// RUN: grep memcpy %t | count 4
+
+volatile int i, j, k;
+volatile int ar[5];
+volatile char c;
+volatile _Complex int ci;
+volatile struct S {
+#ifdef __cplusplus
+ void operator =(volatile struct S&o) volatile;
+#endif
+ int i;
+} a, b;
+
+//void operator =(volatile struct S&o1, volatile struct S&o2) volatile;
+#include <stdio.h>
+
+int main() {
+ // A use.
+ i;
+ // A use of the real part
+ (float)(ci);
+ // A use.
+ (void)ci;
+ // A use.
+ (void)a;
+ // Not a use.
+ (void)(ci=ci);
+ // Not a use.
+ (void)(i=j);
+ ci+=ci;
+ (ci += ci) + ci;
+ asm("nop");
+ (i += j) + k;
+ asm("nop");
+ // A use
+ (i += j) + 1;
+ asm("nop");
+ ci+ci;
+ // A use.
+ __real i;
+ // A use.
+ +ci;
+ asm("nop");
+ // Not a use.
+ (void)(i=i);
+ (float)(i=i);
+ // A use.
+ (void)i;
+ i=i;
+ i=i=i;
+#ifndef __cplusplus
+ // Not a use.
+ (void)__builtin_choose_expr(0, i=i, j=j);
+#endif
+ // A use.
+ k ? (i=i) : (j=j);
+ (void)(i,(i=i));
+ i=i,i;
+ (i=j,k=j);
+ (i=j,k);
+ (i,j);
+ i=c=k;
+ i+=k;
+ // A use of both.
+ ci;
+#ifndef __cplusplus
+ // A use of _real.
+ (int)ci;
+ // A use of both.
+ (_Bool)ci;
+#endif
+ ci=ci;
+ ci=ci=ci;
+ __imag ci = __imag ci = __imag ci;
+ // Not a use.
+ __real (i = j);
+ // Not a use.
+ __imag i;
+
+ // ============================================================
+ // Test cases we get wrong.
+
+ // ============================================================
+ // Test cases where we intentionally differ from gcc, due to suspected bugs in
+ // gcc.
+
+ // Not a use. gcc forgets to do the assignment.
+ ((a=a),a);
+
+ // Not a use. gcc gets this wrong, it doesn't emit the copy!
+ // (void)(a=a);
+
+ // Not a use. gcc got this wrong in 4.2 and omitted the side effects
+ // entirely, but it is fixed in 4.4.0.
+ __imag (i = j);
+
+#ifndef __cplusplus
+ // A use of the real part
+ (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.
+ (int)(ci=ci);
+#endif
+
+ // A use.
+ (float)(i=i);
+ // A use. gcc treats this as not a use, that's probably a bug due to tree
+ // folding ignoring volatile.
+ (int)(i=i);
+
+ // A use.
+ -(i=j);
+ // A use. gcc treats this a not a use, that's probably a bug due to tree
+ // folding ignoring volatile.
+ +(i=k);
+
+ // A use. gcc treats this a not a use, that's probably a bug due to tree
+ // folding ignoring volatile.
+ __real (ci=ci);
+
+ // A use.
+ i + 0;
+ // A use.
+ (i=j) + i;
+ // A use. gcc treats this as not a use, that's probably a bug due to tree
+ // folding ignoring volatile.
+ (i=j) + 0;
+
+#ifdef __cplusplus
+ (i,j)=k;
+ (j=k,i)=i;
+ struct { int x; } s, s1;
+ printf("s is at %p\n", &s);
+ printf("s is at %p\n", &(s = s1));
+ printf("s.x is at %p\n", &((s = s1).x));
+#endif
+}