Track whether an AggValueSlot is potentially aliased, and do not

emit call results into potentially aliased slots.  This allows us
to properly mark indirect return slots as noalias, at the cost
of requiring an extra memcpy when assigning an aggregate call
result into a l-value.  It also brings us into compliance with
the x86-64 ABI.

llvm-svn: 138599
This commit is contained in:
John McCall 2011-08-25 23:04:34 +00:00
parent 5cc730cdef
commit a5efa7386a
17 changed files with 108 additions and 71 deletions

View File

@ -907,6 +907,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
// Name the struct return argument. // Name the struct return argument.
if (CGM.ReturnTypeUsesSRet(FI)) { if (CGM.ReturnTypeUsesSRet(FI)) {
AI->setName("agg.result"); AI->setName("agg.result");
AI->addAttr(llvm::Attribute::NoAlias);
++AI; ++AI;
} }

View File

@ -401,7 +401,8 @@ static void EmitBaseInitializer(CodeGenFunction &CGF,
AggValueSlot AggSlot = AggValueSlot AggSlot =
AggValueSlot::forAddr(V, Qualifiers(), AggValueSlot::forAddr(V, Qualifiers(),
AggValueSlot::IsDestructed, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased);
CGF.EmitAggExpr(BaseInit->getInit(), AggSlot); CGF.EmitAggExpr(BaseInit->getInit(), AggSlot);
@ -441,7 +442,8 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF,
AggValueSlot Slot = AggValueSlot Slot =
AggValueSlot::forAddr(Dest, LHS.getQuals(), AggValueSlot::forAddr(Dest, LHS.getQuals(),
AggValueSlot::IsDestructed, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased);
CGF.EmitAggExpr(MemberInit->getInit(), Slot); CGF.EmitAggExpr(MemberInit->getInit(), Slot);
} }
@ -1330,7 +1332,8 @@ CodeGenFunction::EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor
AggValueSlot AggSlot = AggValueSlot AggSlot =
AggValueSlot::forAddr(ThisPtr, Qualifiers(), AggValueSlot::forAddr(ThisPtr, Qualifiers(),
AggValueSlot::IsDestructed, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased);
EmitAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot); EmitAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot);

View File

@ -1046,7 +1046,8 @@ void CodeGenFunction::EmitExprAsInit(const Expr *init,
// TODO: how can we delay here if D is captured by its initializer? // TODO: how can we delay here if D is captured by its initializer?
EmitAggExpr(init, AggValueSlot::forLValue(lvalue, EmitAggExpr(init, AggValueSlot::forLValue(lvalue,
AggValueSlot::IsDestructed, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers)); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased));
} }
} }

View File

@ -47,7 +47,8 @@ static void EmitDeclInit(CodeGenFunction &CGF, const VarDecl &D,
CGF.EmitComplexExprIntoAddr(Init, DeclPtr, lv.isVolatile()); CGF.EmitComplexExprIntoAddr(Init, DeclPtr, lv.isVolatile());
} else { } else {
CGF.EmitAggExpr(Init, AggValueSlot::forLValue(lv,AggValueSlot::IsDestructed, CGF.EmitAggExpr(Init, AggValueSlot::forLValue(lv,AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers)); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased));
} }
} }

View File

@ -360,7 +360,8 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
= AggValueSlot::IsDestructed_t(InitializedDecl != 0); = AggValueSlot::IsDestructed_t(InitializedDecl != 0);
AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Qualifiers(), AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Qualifiers(),
isDestructed, isDestructed,
AggValueSlot::DoesNotNeedGCBarriers); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased);
} }
if (InitializedDecl) { if (InitializedDecl) {

View File

@ -35,11 +35,18 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
AggValueSlot Dest; AggValueSlot Dest;
bool IgnoreResult; bool IgnoreResult;
/// We want to use 'dest' as the return slot except under two
/// conditions:
/// - The destination slot requires garbage collection, so we
/// need to use the GC API.
/// - The destination slot is potentially aliased.
bool shouldUseDestForReturnSlot() const {
return !(Dest.requiresGCollection() || Dest.isPotentiallyAliased());
}
ReturnValueSlot getReturnValueSlot() const { ReturnValueSlot getReturnValueSlot() const {
// If the destination slot requires garbage collection, we can't if (!shouldUseDestForReturnSlot())
// use the real return value slot, because we have to use the GC return ReturnValueSlot();
// API.
if (Dest.requiresGCollection()) return ReturnValueSlot();
return ReturnValueSlot(Dest.getAddr(), Dest.isVolatile()); return ReturnValueSlot(Dest.getAddr(), Dest.isVolatile());
} }
@ -69,7 +76,7 @@ public:
void EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore = false); void EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore = false);
void EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore = false); void EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore = false);
void EmitGCMove(const Expr *E, RValue Src); void EmitMoveFromReturnSlot(const Expr *E, RValue Src);
AggValueSlot::NeedsGCBarriers_t needsGC(QualType T) { AggValueSlot::NeedsGCBarriers_t needsGC(QualType T) {
if (CGF.getLangOptions().getGCMode() && TypeRequiresGCollection(T)) if (CGF.getLangOptions().getGCMode() && TypeRequiresGCollection(T))
@ -179,23 +186,27 @@ bool AggExprEmitter::TypeRequiresGCollection(QualType T) {
return Record->hasObjectMember(); return Record->hasObjectMember();
} }
/// \brief Perform the final move to DestPtr if RequiresGCollection is set. /// \brief Perform the final move to DestPtr if for some reason
/// getReturnValueSlot() didn't use it directly.
/// ///
/// The idea is that you do something like this: /// The idea is that you do something like this:
/// RValue Result = EmitSomething(..., getReturnValueSlot()); /// RValue Result = EmitSomething(..., getReturnValueSlot());
/// EmitGCMove(E, Result); /// EmitMoveFromReturnSlot(E, Result);
/// If GC doesn't interfere, this will cause the result to be emitted ///
/// directly into the return value slot. If GC does interfere, a final /// If nothing interferes, this will cause the result to be emitted
/// move will be performed. /// directly into the return value slot. Otherwise, a final move
void AggExprEmitter::EmitGCMove(const Expr *E, RValue Src) { /// will be performed.
if (Dest.requiresGCollection()) { void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) {
CharUnits size = CGF.getContext().getTypeSizeInChars(E->getType()); if (shouldUseDestForReturnSlot()) {
llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); // Logically, Dest.getAddr() should equal Src.getAggregateAddr().
llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size.getQuantity()); // The possibility of undef rvalues complicates that a lot,
CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF, Dest.getAddr(), // though, so we can't really assert.
Src.getAggregateAddr(), return;
SizeVal);
} }
// Otherwise, do a final copy,
assert(Dest.getAddr() != Src.getAggregateAddr());
EmitFinalDestCopy(E, Src, /*Ignore*/ true);
} }
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
@ -316,7 +327,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
LValue LV = CGF.EmitLValue(E->getSubExpr()); LValue LV = CGF.EmitLValue(E->getSubExpr());
assert(LV.isPropertyRef()); assert(LV.isPropertyRef());
RValue RV = CGF.EmitLoadOfPropertyRefLValue(LV, getReturnValueSlot()); RValue RV = CGF.EmitLoadOfPropertyRefLValue(LV, getReturnValueSlot());
EmitGCMove(E, RV); EmitMoveFromReturnSlot(E, RV);
break; break;
} }
@ -381,12 +392,12 @@ void AggExprEmitter::VisitCallExpr(const CallExpr *E) {
} }
RValue RV = CGF.EmitCallExpr(E, getReturnValueSlot()); RValue RV = CGF.EmitCallExpr(E, getReturnValueSlot());
EmitGCMove(E, RV); EmitMoveFromReturnSlot(E, RV);
} }
void AggExprEmitter::VisitObjCMessageExpr(ObjCMessageExpr *E) { void AggExprEmitter::VisitObjCMessageExpr(ObjCMessageExpr *E) {
RValue RV = CGF.EmitObjCMessageExpr(E, getReturnValueSlot()); RValue RV = CGF.EmitObjCMessageExpr(E, getReturnValueSlot());
EmitGCMove(E, RV); EmitMoveFromReturnSlot(E, RV);
} }
void AggExprEmitter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { void AggExprEmitter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
@ -600,6 +611,7 @@ AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV) {
CGF.EmitAggExpr(E, AggValueSlot::forLValue(LV, CGF.EmitAggExpr(E, AggValueSlot::forLValue(LV,
AggValueSlot::IsDestructed, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased,
Dest.isZeroed())); Dest.isZeroed()));
} else if (LV.isSimple()) { } else if (LV.isSimple()) {
CGF.EmitScalarInit(E, /*D=*/0, LV, /*Captured=*/false); CGF.EmitScalarInit(E, /*D=*/0, LV, /*Captured=*/false);

View File

@ -820,7 +820,8 @@ void CodeGenFunction::GenerateObjCCtorDtorMethod(ObjCImplementationDecl *IMP,
LoadObjCSelf(), Ivar, 0); LoadObjCSelf(), Ivar, 0);
EmitAggExpr(IvarInit->getInit(), EmitAggExpr(IvarInit->getInit(),
AggValueSlot::forLValue(LV, AggValueSlot::IsDestructed, AggValueSlot::forLValue(LV, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers)); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased));
} }
// constructor returns 'self'. // constructor returns 'self'.
CodeGenTypes &Types = CGM.getTypes(); CodeGenTypes &Types = CGM.getTypes();

View File

@ -786,7 +786,8 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
} else { } else {
EmitAggExpr(RV, AggValueSlot::forAddr(ReturnValue, Qualifiers(), EmitAggExpr(RV, AggValueSlot::forAddr(ReturnValue, Qualifiers(),
AggValueSlot::IsDestructed, AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers)); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased));
} }
EmitBranchThroughCleanup(ReturnBlock); EmitBranchThroughCleanup(ReturnBlock);

View File

@ -347,9 +347,14 @@ class AggValueSlot {
/// be set. /// be set.
bool ZeroedFlag : 1; bool ZeroedFlag : 1;
/// AliasedFlag - This generally defaults to false, but can be true
/// if the memory is known not to be aliased.
bool AliasedFlag : 1;
public: public:
enum IsZeroed_t { IsNotZeroed, IsZeroed }; enum IsAliased_t { IsNotAliased, IsAliased };
enum IsDestructed_t { IsNotDestructed, IsDestructed }; enum IsDestructed_t { IsNotDestructed, IsDestructed };
enum IsZeroed_t { IsNotZeroed, IsZeroed };
enum NeedsGCBarriers_t { DoesNotNeedGCBarriers, NeedsGCBarriers }; enum NeedsGCBarriers_t { DoesNotNeedGCBarriers, NeedsGCBarriers };
/// ignored - Returns an aggregate value slot indicating that the /// ignored - Returns an aggregate value slot indicating that the
@ -358,7 +363,10 @@ public:
AggValueSlot AV; AggValueSlot AV;
AV.Addr = 0; AV.Addr = 0;
AV.Quals = Qualifiers(); AV.Quals = Qualifiers();
AV.LifetimeFlag = AV.RequiresGCollection = AV.ZeroedFlag = 0; AV.LifetimeFlag = AV.RequiresGCollection = AV.ZeroedFlag = false;
// If there's ever an address here, it will be a temporary.
AV.AliasedFlag = false;
return AV; return AV;
} }
@ -375,6 +383,7 @@ public:
static AggValueSlot forAddr(llvm::Value *addr, Qualifiers quals, static AggValueSlot forAddr(llvm::Value *addr, Qualifiers quals,
IsDestructed_t isDestructed, IsDestructed_t isDestructed,
NeedsGCBarriers_t needsGC, NeedsGCBarriers_t needsGC,
IsAliased_t isAliased = IsAliased,
IsZeroed_t isZeroed = IsNotZeroed) { IsZeroed_t isZeroed = IsNotZeroed) {
AggValueSlot AV; AggValueSlot AV;
AV.Addr = addr; AV.Addr = addr;
@ -382,14 +391,16 @@ public:
AV.LifetimeFlag = isDestructed; AV.LifetimeFlag = isDestructed;
AV.RequiresGCollection = needsGC; AV.RequiresGCollection = needsGC;
AV.ZeroedFlag = isZeroed; AV.ZeroedFlag = isZeroed;
AV.AliasedFlag = isAliased;
return AV; return AV;
} }
static AggValueSlot forLValue(LValue LV, IsDestructed_t isDestructed, static AggValueSlot forLValue(LValue LV, IsDestructed_t isDestructed,
NeedsGCBarriers_t needsGC, NeedsGCBarriers_t needsGC,
IsAliased_t isAliased = IsAliased,
IsZeroed_t isZeroed = IsNotZeroed) { IsZeroed_t isZeroed = IsNotZeroed) {
return forAddr(LV.getAddress(), LV.getQuals(), return forAddr(LV.getAddress(), LV.getQuals(),
isDestructed, needsGC, isZeroed); isDestructed, needsGC, isAliased, isZeroed);
} }
IsDestructed_t isLifetimeExternallyManaged() const { IsDestructed_t isLifetimeExternallyManaged() const {
@ -421,6 +432,10 @@ public:
return Addr == 0; return Addr == 0;
} }
IsAliased_t isPotentiallyAliased() const {
return IsAliased_t(AliasedFlag);
}
RValue asRValue() const { RValue asRValue() const {
return RValue::getAggregate(getAddr(), isVolatile()); return RValue::getAggregate(getAddr(), isVolatile());
} }

View File

@ -1466,7 +1466,8 @@ public:
AggValueSlot CreateAggTemp(QualType T, const Twine &Name = "tmp") { AggValueSlot CreateAggTemp(QualType T, const Twine &Name = "tmp") {
return AggValueSlot::forAddr(CreateMemTemp(T, Name), T.getQualifiers(), return AggValueSlot::forAddr(CreateMemTemp(T, Name), T.getQualifiers(),
AggValueSlot::IsNotDestructed, AggValueSlot::IsNotDestructed,
AggValueSlot::DoesNotNeedGCBarriers); AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased);
} }
/// Emit a cast to void* in the appropriate address space. /// Emit a cast to void* in the appropriate address space.

View File

@ -28,13 +28,13 @@ struct s4 { struct s4_0 { int f0; } f0; };
struct s4 f4(void) {} struct s4 f4(void) {}
// APCS-GNU: define void @f5( // APCS-GNU: define void @f5(
// APCS-GNU: struct.s5* sret // APCS-GNU: struct.s5* noalias sret
// AAPCS: define arm_aapcscc i32 @f5() // AAPCS: define arm_aapcscc i32 @f5()
struct s5 { struct { } f0; int f1; }; struct s5 { struct { } f0; int f1; };
struct s5 f5(void) {} struct s5 f5(void) {}
// APCS-GNU: define void @f6( // APCS-GNU: define void @f6(
// APCS-GNU: struct.s6* sret // APCS-GNU: struct.s6* noalias sret
// AAPCS: define arm_aapcscc i32 @f6() // AAPCS: define arm_aapcscc i32 @f6()
struct s6 { int f0[1]; }; struct s6 { int f0[1]; };
struct s6 f6(void) {} struct s6 f6(void) {}
@ -45,7 +45,7 @@ struct s7 { struct { int : 0; } f0; };
struct s7 f7(void) {} struct s7 f7(void) {}
// APCS-GNU: define void @f8( // APCS-GNU: define void @f8(
// APCS-GNU: struct.s8* sret // APCS-GNU: struct.s8* noalias sret
// AAPCS: define arm_aapcscc void @f8() // AAPCS: define arm_aapcscc void @f8()
struct s8 { struct { int : 0; } f0[1]; }; struct s8 { struct { int : 0; } f0[1]; };
struct s8 f8(void) {} struct s8 f8(void) {}
@ -61,7 +61,7 @@ struct s10 { int f0; int : 0; int : 0; };
struct s10 f10(void) {} struct s10 f10(void) {}
// APCS-GNU: define void @f11( // APCS-GNU: define void @f11(
// APCS-GNU: struct.s11* sret // APCS-GNU: struct.s11* noalias sret
// AAPCS: define arm_aapcscc i32 @f11() // AAPCS: define arm_aapcscc i32 @f11()
struct s11 { int : 0; int f0; }; struct s11 { int : 0; int f0; };
struct s11 f11(void) {} struct s11 f11(void) {}
@ -72,7 +72,7 @@ union u12 { char f0; short f1; int f2; };
union u12 f12(void) {} union u12 f12(void) {}
// APCS-GNU: define void @f13( // APCS-GNU: define void @f13(
// APCS-GNU: struct.s13* sret // APCS-GNU: struct.s13* noalias sret
// FIXME: This should return a float. // FIXME: This should return a float.
// AAPCS-FIXME: darm_aapcscc efine float @f13() // AAPCS-FIXME: darm_aapcscc efine float @f13()
@ -80,7 +80,7 @@ struct s13 { float f0; };
struct s13 f13(void) {} struct s13 f13(void) {}
// APCS-GNU: define void @f14( // APCS-GNU: define void @f14(
// APCS-GNU: union.u14* sret // APCS-GNU: union.u14* noalias sret
// AAPCS: define arm_aapcscc i32 @f14() // AAPCS: define arm_aapcscc i32 @f14()
union u14 { float f0; }; union u14 { float f0; };
union u14 f14(void) {} union u14 f14(void) {}
@ -104,13 +104,13 @@ struct s18 { short f0; char f1 : 4; };
struct s18 f18(void) {} struct s18 f18(void) {}
// APCS-GNU: define void @f19( // APCS-GNU: define void @f19(
// APCS-GNU: struct.s19* sret // APCS-GNU: struct.s19* noalias sret
// AAPCS: define arm_aapcscc i32 @f19() // AAPCS: define arm_aapcscc i32 @f19()
struct s19 { int f0; struct s8 f1; }; struct s19 { int f0; struct s8 f1; };
struct s19 f19(void) {} struct s19 f19(void) {}
// APCS-GNU: define void @f20( // APCS-GNU: define void @f20(
// APCS-GNU: struct.s20* sret // APCS-GNU: struct.s20* noalias sret
// AAPCS: define arm_aapcscc i32 @f20() // AAPCS: define arm_aapcscc i32 @f20()
struct s20 { struct s8 f1; int f0; }; struct s20 { struct s8 f1; int f0; };
struct s20 f20(void) {} struct s20 f20(void) {}
@ -128,10 +128,10 @@ struct s21 f21(void) {}
// APCS-GNU: define i128 @f27() // APCS-GNU: define i128 @f27()
// AAPCS: define arm_aapcscc i16 @f22() // AAPCS: define arm_aapcscc i16 @f22()
// AAPCS: define arm_aapcscc i32 @f23() // AAPCS: define arm_aapcscc i32 @f23()
// AAPCS: define arm_aapcscc void @f24({{.*}} sret // AAPCS: define arm_aapcscc void @f24({{.*}} noalias sret
// AAPCS: define arm_aapcscc void @f25({{.*}} sret // AAPCS: define arm_aapcscc void @f25({{.*}} noalias sret
// AAPCS: define arm_aapcscc void @f26({{.*}} sret // AAPCS: define arm_aapcscc void @f26({{.*}} noalias sret
// AAPCS: define arm_aapcscc void @f27({{.*}} sret // AAPCS: define arm_aapcscc void @f27({{.*}} noalias sret
_Complex char f22(void) {} _Complex char f22(void) {}
_Complex short f23(void) {} _Complex short f23(void) {}
_Complex int f24(void) {} _Complex int f24(void) {}
@ -149,7 +149,7 @@ struct s28 f28() {}
struct s29 { _Complex short f0; }; struct s29 { _Complex short f0; };
struct s29 f29() {} struct s29 f29() {}
// APCS-GNU: define void @f30({{.*}} sret // APCS-GNU: define void @f30({{.*}} noalias sret
// AAPCS: define arm_aapcscc void @f30({{.*}} sret // AAPCS: define arm_aapcscc void @f30({{.*}} noalias sret
struct s30 { _Complex int f0; }; struct s30 { _Complex int f0; };
struct s30 f30() {} struct s30 f30() {}

View File

@ -8,7 +8,7 @@
#include <arm_neon.h> #include <arm_neon.h>
// CHECK: define void @f0(%struct.int8x16x2_t* sret %agg.result, <16 x i8> %{{.*}}, <16 x i8> %{{.*}}) // CHECK: define void @f0(%struct.int8x16x2_t* noalias sret %agg.result, <16 x i8> %{{.*}}, <16 x i8> %{{.*}})
int8x16x2_t f0(int8x16_t a0, int8x16_t a1) { int8x16x2_t f0(int8x16_t a0, int8x16_t a1) {
return vzipq_s8(a0, a1); return vzipq_s8(a0, a1);
} }
@ -24,7 +24,7 @@ typedef float T_float32x16 __attribute__ ((__vector_size__ (64)));
T_float32x2 f1_0(T_float32x2 a0) { return a0; } T_float32x2 f1_0(T_float32x2 a0) { return a0; }
// CHECK: define <4 x float> @f1_1(<4 x float> %{{.*}}) // CHECK: define <4 x float> @f1_1(<4 x float> %{{.*}})
T_float32x4 f1_1(T_float32x4 a0) { return a0; } T_float32x4 f1_1(T_float32x4 a0) { return a0; }
// CHECK: define void @f1_2(<8 x float>* sret %{{.*}}, <8 x float> %{{.*}}) // CHECK: define void @f1_2(<8 x float>* noalias sret %{{.*}}, <8 x float> %{{.*}})
T_float32x8 f1_2(T_float32x8 a0) { return a0; } T_float32x8 f1_2(T_float32x8 a0) { return a0; }
// CHECK: define void @f1_3(<16 x float>* sret %{{.*}}, <16 x float> %{{.*}}) // CHECK: define void @f1_3(<16 x float>* noalias sret %{{.*}}, <16 x float> %{{.*}})
T_float32x16 f1_3(T_float32x16 a0) { return a0; } T_float32x16 f1_3(T_float32x16 a0) { return a0; }

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o %t -fblocks // RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - -fblocks | FileCheck %s
void (^f)(void) = ^{}; void (^f)(void) = ^{};
// rdar://6768379 // rdar://6768379
@ -12,7 +12,7 @@ struct s0 {
int a[64]; int a[64];
}; };
// RUN: grep 'internal void @__f2_block_invoke_0(.struct.s0\* sret .*, .*, .* byval .*)' %t // CHECK: define internal void @__f2_block_invoke_0(%struct.s0* noalias sret {{%.*}}, i8* {{%.*}}, %struct.s0* byval align 4 {{.*}})
struct s0 f2(struct s0 a0) { struct s0 f2(struct s0 a0) {
return ^(struct s0 a1){ return a1; }(a0); return ^(struct s0 a1){ return a1; }(a0);
} }

View File

@ -71,7 +71,7 @@ struct s10 {
// Small vectors and 1 x {i64,double} are returned in registers // Small vectors and 1 x {i64,double} are returned in registers
// CHECK: i32 @f11() // CHECK: i32 @f11()
// CHECK: void @f12(<2 x i32>* sret %agg.result) // CHECK: void @f12(<2 x i32>* noalias sret %agg.result)
// CHECK: i64 @f13() // CHECK: i64 @f13()
// CHECK: i64 @f14() // CHECK: i64 @f14()
// CHECK: <2 x i64> @f15() // CHECK: <2 x i64> @f15()
@ -93,11 +93,11 @@ T16 f16(void) { while (1) {} }
// 128-bits). // 128-bits).
// CHECK: i32 @f17() // CHECK: i32 @f17()
// CHECK: void @f18(%{{.*}}* sret %agg.result) // CHECK: void @f18(%{{.*}}* noalias sret %agg.result)
// CHECK: void @f19(%{{.*}}* sret %agg.result) // CHECK: void @f19(%{{.*}}* noalias sret %agg.result)
// CHECK: void @f20(%{{.*}}* sret %agg.result) // CHECK: void @f20(%{{.*}}* noalias sret %agg.result)
// CHECK: void @f21(%{{.*}}* sret %agg.result) // CHECK: void @f21(%{{.*}}* noalias sret %agg.result)
// CHECK: void @f22(%{{.*}}* sret %agg.result) // CHECK: void @f22(%{{.*}}* noalias sret %agg.result)
struct { T11 a; } f17(void) { while (1) {} } struct { T11 a; } f17(void) { while (1) {} }
struct { T12 a; } f18(void) { while (1) {} } struct { T12 a; } f18(void) { while (1) {} }
struct { T13 a; } f19(void) { while (1) {} } struct { T13 a; } f19(void) { while (1) {} }
@ -116,11 +116,11 @@ struct { struct {} a; struct { float a[1]; } b; } f25(void) { while (1) {} }
// Small structures are handled recursively // Small structures are handled recursively
// CHECK: i32 @f26() // CHECK: i32 @f26()
// CHECK: void @f27(%struct.s27* sret %agg.result) // CHECK: void @f27(%struct.s27* noalias sret %agg.result)
struct s26 { struct { char a, b; } a; struct { char a, b; } b; } f26(void) { while (1) {} } struct s26 { struct { char a, b; } a; struct { char a, b; } b; } f26(void) { while (1) {} }
struct s27 { struct { char a, b, c; } a; struct { char a; } b; } f27(void) { while (1) {} } struct s27 { struct { char a, b, c; } a; struct { char a; } b; } f27(void) { while (1) {} }
// CHECK: void @f28(%struct.s28* sret %agg.result) // CHECK: void @f28(%struct.s28* noalias sret %agg.result)
struct s28 { int a; int b[]; } f28(void) { while (1) {} } struct s28 { int a; int b[]; } f28(void) { while (1) {} }
// CHECK: define i16 @f29() // CHECK: define i16 @f29()
@ -150,7 +150,7 @@ struct s36 { struct { int : 0; } a[2][10]; char b; char c; } f36(void) { while (
// CHECK: define float @f37() // CHECK: define float @f37()
struct s37 { float c[1][1]; } f37(void) { while (1) {} } struct s37 { float c[1][1]; } f37(void) { while (1) {} }
// CHECK: define void @f38(%struct.s38* sret %agg.result) // CHECK: define void @f38(%struct.s38* noalias sret %agg.result)
struct s38 { char a[3]; short b; } f38(void) { while (1) {} } struct s38 { char a[3]; short b; } f38(void) { while (1) {} }
// CHECK: define void @f39(%struct.s39* byval align 16 %x) // CHECK: define void @f39(%struct.s39* byval align 16 %x)

View File

@ -42,7 +42,7 @@ void f7(e7 a0) {
// Test merging/passing of upper eightbyte with X87 class. // Test merging/passing of upper eightbyte with X87 class.
// //
// CHECK: define void @f8_1(%union.u8* sret %agg.result) // CHECK: define void @f8_1(%union.u8* noalias sret %agg.result)
// CHECK: define void @f8_2(%union.u8* byval align 16 %a0) // CHECK: define void @f8_2(%union.u8* byval align 16 %a0)
union u8 { union u8 {
long double a; long double a;
@ -58,7 +58,7 @@ struct s9 { int a; int b; int : 0; } f9(void) { while (1) {} }
struct s10 { int a; int b; int : 0; }; struct s10 { int a; int b; int : 0; };
void f10(struct s10 a0) {} void f10(struct s10 a0) {}
// CHECK: define void @f11(%union.anon* sret %agg.result) // CHECK: define void @f11(%union.anon* noalias sret %agg.result)
union { long double a; float b; } f11() { while (1) {} } union { long double a; float b; } f11() { while (1) {} }
// CHECK: define i32 @f12_0() // CHECK: define i32 @f12_0()
@ -69,7 +69,7 @@ void f12_1(struct s12 a0) {}
// Check that sret parameter is accounted for when checking available integer // Check that sret parameter is accounted for when checking available integer
// registers. // registers.
// CHECK: define void @f13(%struct.s13_0* sret %agg.result, i32 %a, i32 %b, i32 %c, i32 %d, {{.*}}* byval align 8 %e, i32 %f) // CHECK: define void @f13(%struct.s13_0* noalias sret %agg.result, i32 %a, i32 %b, i32 %c, i32 %d, {{.*}}* byval align 8 %e, i32 %f)
struct s13_0 { long long f0[3]; }; struct s13_0 { long long f0[3]; };
struct s13_1 { long long f0[2]; }; struct s13_1 { long long f0[2]; };

View File

@ -395,7 +395,7 @@ namespace Elision {
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[I]]) // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[I]])
} }
// CHECK: define void @_ZN7Elision5test2Ev([[A]]* sret // CHECK: define void @_ZN7Elision5test2Ev([[A]]* noalias sret
A test2() { A test2() {
// CHECK: call void @_ZN7Elision3fooEv() // CHECK: call void @_ZN7Elision3fooEv()
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[RET:%.*]]) // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[RET:%.*]])
@ -403,7 +403,7 @@ namespace Elision {
return (foo(), A()); return (foo(), A());
} }
// CHECK: define void @_ZN7Elision5test3EiNS_1AE([[A]]* sret // CHECK: define void @_ZN7Elision5test3EiNS_1AE([[A]]* noalias sret
A test3(int v, A x) { A test3(int v, A x) {
if (v < 5) if (v < 5)
// CHECK: call void @_ZN7Elision1AC1Ev([[A]]* [[RET:%.*]]) // CHECK: call void @_ZN7Elision1AC1Ev([[A]]* [[RET:%.*]])
@ -444,7 +444,7 @@ namespace Elision {
} }
// rdar://problem/8433352 // rdar://problem/8433352
// CHECK: define void @_ZN7Elision5test5Ev([[A]]* sret // CHECK: define void @_ZN7Elision5test5Ev([[A]]* noalias sret
struct B { A a; B(); }; struct B { A a; B(); };
A test5() { A test5() {
// CHECK: [[AT0:%.*]] = alloca [[A]], align 8 // CHECK: [[AT0:%.*]] = alloca [[A]], align 8

View File

@ -6,7 +6,7 @@ struct S {
short s; short s;
}; };
// CHECK: define void @_Z1fv(%struct.S* sret % // CHECK: define void @_Z1fv(%struct.S* noalias sret %
S f() { return S(); } S f() { return S(); }
// CHECK: define void @_Z1f1S(%struct.S*) // CHECK: define void @_Z1f1S(%struct.S*)
void f(S) { } void f(S) { }
@ -18,7 +18,7 @@ public:
double c; double c;
}; };
// CHECK: define void @_Z1gv(%class.C* sret % // CHECK: define void @_Z1gv(%class.C* noalias sret %
C g() { return C(); } C g() { return C(); }
// CHECK: define void @_Z1f1C(%class.C*) // CHECK: define void @_Z1f1C(%class.C*)
@ -103,13 +103,13 @@ struct s7_1 { double x; };
struct s7 : s7_0, s7_1 { }; struct s7 : s7_0, s7_1 { };
s7 f7() { return s7(); } s7 f7() { return s7(); }
// CHECK: define void @_Z2f8v(%struct.s8* sret %agg.result) // CHECK: define void @_Z2f8v(%struct.s8* noalias sret %agg.result)
struct s8_0 { }; struct s8_0 { };
struct s8_1 { double x; }; struct s8_1 { double x; };
struct s8 { s8_0 a; s8_1 b; }; struct s8 { s8_0 a; s8_1 b; };
s8 f8() { return s8(); } s8 f8() { return s8(); }
// CHECK: define void @_Z2f9v(%struct.s9* sret %agg.result) // CHECK: define void @_Z2f9v(%struct.s9* noalias sret %agg.result)
struct s9_0 { unsigned : 0; }; struct s9_0 { unsigned : 0; };
struct s9_1 { double x; }; struct s9_1 { double x; };
struct s9 { s9_0 a; s9_1 b; }; struct s9 { s9_0 a; s9_1 b; };