]> granicus.if.org Git - clang/commitdiff
IRgen: Assignment to Objective-C properties shouldn't reload the value (which
authorDaniel Dunbar <daniel@zuster.org>
Tue, 29 Jun 2010 22:00:45 +0000 (22:00 +0000)
committerDaniel Dunbar <daniel@zuster.org>
Tue, 29 Jun 2010 22:00:45 +0000 (22:00 +0000)
would trigger an extra method call).
 - While in the area, I also changed Clang to not emit an unnecessary load from
   'x' in cases like 'y = (x = 1)'.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107210 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGExprScalar.cpp
test/CodeGen/assign.c [new file with mode: 0644]
test/CodeGen/object-size.c
test/CodeGenCXX/member-init-assignment.cpp
test/CodeGenObjC/assign.m [new file with mode: 0644]

index e2b4d2dd1552d3051317e9218d66a548a795d00e..8c120faaec877aa66524c5144de487d1e8102f40 100644 (file)
@@ -334,7 +334,7 @@ public:
   BinOpInfo EmitBinOps(const BinaryOperator *E);
   LValue EmitCompoundAssignLValue(const CompoundAssignOperator *E,
                             Value *(ScalarExprEmitter::*F)(const BinOpInfo &),
-                                  Value *&BitFieldResult);
+                                  Value *&Result);
 
   Value *EmitCompoundAssign(const CompoundAssignOperator *E,
                             Value *(ScalarExprEmitter::*F)(const BinOpInfo &));
@@ -1342,9 +1342,8 @@ BinOpInfo ScalarExprEmitter::EmitBinOps(const BinaryOperator *E) {
 LValue ScalarExprEmitter::EmitCompoundAssignLValue(
                                               const CompoundAssignOperator *E,
                         Value *(ScalarExprEmitter::*Func)(const BinOpInfo &),
-                                                   Value *&BitFieldResult) {
+                                                   Value *&Result) {
   QualType LHSTy = E->getLHS()->getType();
-  BitFieldResult = 0;
   BinOpInfo OpInfo;
   
   if (E->getComputationResultType()->isAnyComplexType()) {
@@ -1353,7 +1352,7 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
     // actually need the imaginary part of the RHS for multiplication and
     // division.)
     CGF.ErrorUnsupported(E, "complex compound assignment");
-    llvm::UndefValue::get(CGF.ConvertType(E->getType()));
+    Result = llvm::UndefValue::get(CGF.ConvertType(E->getType()));
     return LValue();
   }
   
@@ -1370,7 +1369,7 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
                                     E->getComputationLHSType());
   
   // Expand the binary operator.
-  Value *Result = (this->*Func)(OpInfo);
+  Result = (this->*Func)(OpInfo);
   
   // Convert the result back to the LHS type.
   Result = EmitScalarConversion(Result, E->getComputationResultType(), LHSTy);
@@ -1379,30 +1378,35 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
   // 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()) {
-    if (!LHSLV.isVolatileQualified()) {
-      CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy,
-                                         &Result);
-      BitFieldResult = Result;
-      return LHSLV;
-    } else
-      CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy);
-  } else
+  if (LHSLV.isBitField())
+    CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy,
+                                       &Result);
+  else
     CGF.EmitStoreThroughLValue(RValue::get(Result), LHSLV, LHSTy);
+
   return LHSLV;
 }
 
 Value *ScalarExprEmitter::EmitCompoundAssign(const CompoundAssignOperator *E,
                       Value *(ScalarExprEmitter::*Func)(const BinOpInfo &)) {
   bool Ignore = TestAndClearIgnoreResultAssign();
-  Value *BitFieldResult;
-  LValue LHSLV = EmitCompoundAssignLValue(E, Func, BitFieldResult);
-  if (BitFieldResult)
-    return BitFieldResult;
-  
+  Value *RHS;
+  LValue LHS = EmitCompoundAssignLValue(E, Func, RHS);
+
+  // If the result is clearly ignored, return now.
   if (Ignore)
     return 0;
-  return EmitLoadOfLValue(LHSLV, E->getType());
+
+  // Objective-C property assignment never reloads the value following a store.
+  if (LHS.isPropertyRef() || LHS.isKVCRef())
+    return RHS;
+
+  // If the lvalue is non-volatile, return the computed value of the assignment.
+  if (!LHS.isVolatileQualified())
+    return RHS;
+
+  // Otherwise, reload the value.
+  return EmitLoadOfLValue(LHS, E->getType());
 }
 
 
@@ -1838,17 +1842,25 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
   // 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()) {
-    if (!LHS.isVolatileQualified()) {
-      CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(),
-                                         &RHS);
-      return RHS;
-    } else
-      CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType());
-  } else
+  if (LHS.isBitField())
+    CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(),
+                                       &RHS);
+  else
     CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS, E->getType());
+
+  // If the result is clearly ignored, return now.
   if (Ignore)
     return 0;
+
+  // Objective-C property assignment never reloads the value following a store.
+  if (LHS.isPropertyRef() || LHS.isKVCRef())
+    return RHS;
+
+  // If the lvalue is non-volatile, return the computed value of the assignment.
+  if (!LHS.isVolatileQualified())
+    return RHS;
+
+  // Otherwise, reload the value.
   return EmitLoadOfLValue(LHS, E->getType());
 }
 
@@ -2188,12 +2200,12 @@ LValue CodeGenFunction::EmitObjCIsaExpr(const ObjCIsaExpr *E) {
 LValue CodeGenFunction::EmitCompoundAssignOperatorLValue(
                                             const CompoundAssignOperator *E) {
   ScalarExprEmitter Scalar(*this);
-  Value *BitFieldResult = 0;
+  Value *Result = 0;
   switch (E->getOpcode()) {
 #define COMPOUND_OP(Op)                                                       \
     case BinaryOperator::Op##Assign:                                          \
       return Scalar.EmitCompoundAssignLValue(E, &ScalarExprEmitter::Emit##Op, \
-                                             BitFieldResult)
+                                             Result)
   COMPOUND_OP(Mul);
   COMPOUND_OP(Div);
   COMPOUND_OP(Rem);
diff --git a/test/CodeGen/assign.c b/test/CodeGen/assign.c
new file mode 100644 (file)
index 0000000..eab3d35
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm -o - %s | FileCheck %s
+
+// Check that we don't generate unnecessary reloads.
+//
+// CHECK: define void @f0()
+// CHECK:      [[x_0:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[y_0:%.*]] = alloca i32, align 4
+// CHECK-NEXT: store i32 1, i32* [[x_0]]
+// CHECK-NEXT: store i32 1, i32* [[x_0]]
+// CHECK-NEXT: store i32 1, i32* [[y_0]]
+// CHECK: }
+void f0() {
+  int x, y;
+  x = 1;
+  y = (x = 1);
+}
+
+// Check that we do generate reloads for volatile access.
+//
+// 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: }
+void f1() {
+  volatile int x, y;
+  x = 1;
+  y = (x = 1);
+}
index 3920ec5934de2330c397dcee0226c9c06114ee96..287d742b877d51f7fd86b47a08f38c74ef469752 100644 (file)
@@ -13,32 +13,38 @@ char gbuf[63];
 char *gp;
 int gi, gj;
 
+// CHECK: define void @test1
 void test1() {
   // CHECK:     = call i8* @__strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i64 4), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i64 59)
   strcpy(&gbuf[4], "Hi there");
 }
 
+// CHECK: define void @test2
 void test2() {
   // CHECK:     = call i8* @__strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i64 63)
   strcpy(gbuf, "Hi there");
 }
 
+// CHECK: define void @test3
 void test3() {
   // CHECK:     = call i8* @__strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i64 1, i64 37), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i64 0)
   strcpy(&gbuf[100], "Hi there");
 }
 
+// CHECK: define void @test4
 void test4() {
   // CHECK:     = call i8* @__strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i64 -1), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i64 0)
   strcpy((char*)(void*)&gbuf[-1], "Hi there");
 }
 
+// CHECK: define void @test5
 void test5() {
   // CHECK:     = load i8** @gp
   // CHECK-NEXT:= call i64 @llvm.objectsize.i64(i8* %{{.*}}, i1 false)
   strcpy(gp, "Hi there");
 }
 
+// CHECK: define void @test6
 void test6() {
   char buf[57];
 
@@ -46,6 +52,7 @@ void test6() {
   strcpy(&buf[4], "Hi there");
 }
 
+// CHECK: define void @test7
 void test7() {
   int i;
   // CHECK-NOT:   __strcpy_chk
@@ -53,6 +60,7 @@ void test7() {
   strcpy((++i, gbuf), "Hi there");
 }
 
+// CHECK: define void @test8
 void test8() {
   char *buf[50];
   // CHECK-NOT:   __strcpy_chk
@@ -60,12 +68,14 @@ void test8() {
   strcpy(buf[++gi], "Hi there");
 }
 
+// CHECK: define void @test9
 void test9() {
   // CHECK-NOT:   __strcpy_chk
   // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{.*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
   strcpy((char *)((++gi) + gj), "Hi there");
 }
 
+// CHECK: define void @test10
 char **p;
 void test10() {
   // CHECK-NOT:   __strcpy_chk
@@ -73,36 +83,42 @@ void test10() {
   strcpy(*(++p), "Hi there");
 }
 
+// CHECK: define void @test11
 void test11() {
   // CHECK-NOT:   __strcpy_chk
-  // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{.*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
+  // CHECK:       = call i8* @__inline_strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
   strcpy(gp = gbuf, "Hi there");
 }
 
+// CHECK: define void @test12
 void test12() {
   // CHECK-NOT:   __strcpy_chk
   // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{.*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
   strcpy(++gp, "Hi there");
 }
 
+// CHECK: define void @test13
 void test13() {
   // CHECK-NOT:   __strcpy_chk
   // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{.*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
   strcpy(gp++, "Hi there");
 }
 
+// CHECK: define void @test14
 void test14() {
   // CHECK-NOT:   __strcpy_chk
   // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{.*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
   strcpy(--gp, "Hi there");
 }
 
+// CHECK: define void @test15
 void test15() {
   // CHECK-NOT:   __strcpy_chk
   // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{..*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
   strcpy(gp--, "Hi there");
 }
 
+// CHECK: define void @test16
 void test16() {
   // CHECK-NOT:   __strcpy_chk
   // CHECK:       = call i8* @__inline_strcpy_chk(i8* %{{.*}}, i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
index c9b53118a3ce21d86ab072d3781d3871363ae9bf..57ab7ebd1f2f9fe05b04fe3b18a6500724993c90 100644 (file)
@@ -13,6 +13,5 @@ Foo::Foo(unsigned arg) : file_id(arg = 42)
 // CHECK: define void @_ZN3FooC2Ej
 // CHECK: [[ARG:%.*]] = alloca i32
 // CHECK: store i32 42, i32* [[ARG]]
-// CHECK: [[ARGVAL:%.*]] = load i32* [[ARG]]
-// CHECK: store i32 [[ARGVAL]], i32* %{{.*}}
+// CHECK: store i32 42, i32* %{{.*}}
 // CHECK: ret void
diff --git a/test/CodeGenObjC/assign.m b/test/CodeGenObjC/assign.m
new file mode 100644 (file)
index 0000000..37b406e
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm -o - %s | FileCheck %s
+
+struct s0 {
+  int x;
+};
+
+@interface C0
+@property int x0;
+@property _Complex int x1;
+@property struct s0 x2;
+@end
+
+// Check that we get exactly the message sends we expect, and no more.
+//
+// CHECK: define void @f0
+void f0(C0 *a) {
+// CHECK: objc_msgSend
+  int l0 = (a.x0 = 1);
+
+// CHECK: objc_msgSend
+  _Complex int l1 = (a.x1 = 1);
+
+// CHECK: objc_msgSend
+  struct s0 l2 = (a.x2 = (struct s0) { 1 });
+
+// CHECK: objc_msgSend
+// CHECK: objc_msgSend
+  int l3 = (a.x0 += 1);
+
+// CHECK-NOT: objc_msgSend
+// CHECK: }
+}