Skip to content

Commit e92858c

Browse files
gitoleglanza
authored andcommitted
[CIR][CogeGen] Support aggregate copy via assignment (#325)
This PR adds a support of copies of aggregated data types via assignment.
1 parent 8f5ac0f commit e92858c

File tree

4 files changed

+195
-3
lines changed

4 files changed

+195
-3
lines changed

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,17 @@ struct CallCleanupFunction final : EHScopeStack::Cleanup {
890890
};
891891
} // end anonymous namespace
892892

893+
/// Push the standard destructor for the given type as
894+
/// at least a normal cleanup.
895+
void CIRGenFunction::pushDestroy(QualType::DestructionKind dtorKind,
896+
Address addr, QualType type) {
897+
assert(dtorKind && "cannot push destructor for trivial type");
898+
899+
CleanupKind cleanupKind = getCleanupKind(dtorKind);
900+
pushDestroy(cleanupKind, addr, type, getDestroyer(dtorKind),
901+
cleanupKind & EHCleanup);
902+
}
903+
893904
void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
894905
QualType type, Destroyer *destroyer,
895906
bool useEHCleanupForArray) {

clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,75 @@ using namespace cir;
2929
using namespace clang;
3030

3131
namespace {
32+
33+
// FIXME(cir): This should be a common helper between CIRGen
34+
// and traditional CodeGen
35+
/// Is the value of the given expression possibly a reference to or
36+
/// into a __block variable?
37+
static bool isBlockVarRef(const Expr *E) {
38+
// Make sure we look through parens.
39+
E = E->IgnoreParens();
40+
41+
// Check for a direct reference to a __block variable.
42+
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
43+
const VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl());
44+
return (var && var->hasAttr<BlocksAttr>());
45+
}
46+
47+
// More complicated stuff.
48+
49+
// Binary operators.
50+
if (const BinaryOperator *op = dyn_cast<BinaryOperator>(E)) {
51+
// For an assignment or pointer-to-member operation, just care
52+
// about the LHS.
53+
if (op->isAssignmentOp() || op->isPtrMemOp())
54+
return isBlockVarRef(op->getLHS());
55+
56+
// For a comma, just care about the RHS.
57+
if (op->getOpcode() == BO_Comma)
58+
return isBlockVarRef(op->getRHS());
59+
60+
// FIXME: pointer arithmetic?
61+
return false;
62+
63+
// Check both sides of a conditional operator.
64+
} else if (const AbstractConditionalOperator *op
65+
= dyn_cast<AbstractConditionalOperator>(E)) {
66+
return isBlockVarRef(op->getTrueExpr())
67+
|| isBlockVarRef(op->getFalseExpr());
68+
69+
// OVEs are required to support BinaryConditionalOperators.
70+
} else if (const OpaqueValueExpr *op
71+
= dyn_cast<OpaqueValueExpr>(E)) {
72+
if (const Expr *src = op->getSourceExpr())
73+
return isBlockVarRef(src);
74+
75+
// Casts are necessary to get things like (*(int*)&var) = foo().
76+
// We don't really care about the kind of cast here, except
77+
// we don't want to look through l2r casts, because it's okay
78+
// to get the *value* in a __block variable.
79+
} else if (const CastExpr *cast = dyn_cast<CastExpr>(E)) {
80+
if (cast->getCastKind() == CK_LValueToRValue)
81+
return false;
82+
return isBlockVarRef(cast->getSubExpr());
83+
84+
// Handle unary operators. Again, just aggressively look through
85+
// it, ignoring the operation.
86+
} else if (const UnaryOperator *uop = dyn_cast<UnaryOperator>(E)) {
87+
return isBlockVarRef(uop->getSubExpr());
88+
89+
// Look into the base of a field access.
90+
} else if (const MemberExpr *mem = dyn_cast<MemberExpr>(E)) {
91+
return isBlockVarRef(mem->getBase());
92+
93+
// Look into the base of a subscript.
94+
} else if (const ArraySubscriptExpr *sub = dyn_cast<ArraySubscriptExpr>(E)) {
95+
return isBlockVarRef(sub->getBase());
96+
}
97+
98+
return false;
99+
}
100+
32101
class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
33102
CIRGenFunction &CGF;
34103
AggValueSlot Dest;
@@ -117,8 +186,8 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
117186

118187
// l-values
119188
void VisitDeclRefExpr(DeclRefExpr *E) { buildAggLoadOfLValue(E); }
120-
void VisitMemberExpr(MemberExpr *E) { llvm_unreachable("NYI"); }
121-
void VisitUnaryDeref(UnaryOperator *E) { llvm_unreachable("NYI"); }
189+
void VisitMemberExpr(MemberExpr *E) { buildAggLoadOfLValue(E); }
190+
void VisitUnaryDeref(UnaryOperator *E) { buildAggLoadOfLValue(E); }
122191
void VisitStringLiteral(StringLiteral *E) { llvm_unreachable("NYI"); }
123192
void VisitCompoundLIteralExpr(CompoundLiteralExpr *E) {
124193
llvm_unreachable("NYI");
@@ -136,7 +205,50 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
136205
void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *E) {
137206
llvm_unreachable("NYI");
138207
}
139-
void VisitBinAssign(const BinaryOperator *E) { llvm_unreachable("NYI"); }
208+
void VisitBinAssign(const BinaryOperator *E) {
209+
210+
// For an assignment to work, the value on the right has
211+
// to be compatible with the value on the left.
212+
assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(),
213+
E->getRHS()->getType())
214+
&& "Invalid assignment");
215+
216+
if (isBlockVarRef(E->getLHS()) &&
217+
E->getRHS()->HasSideEffects(CGF.getContext())) {
218+
llvm_unreachable("NYI");
219+
}
220+
221+
LValue lhs = CGF.buildLValue(E->getLHS());
222+
223+
// If we have an atomic type, evaluate into the destination and then
224+
// do an atomic copy.
225+
if (lhs.getType()->isAtomicType() ||
226+
CGF.LValueIsSuitableForInlineAtomic(lhs)) {
227+
assert(!UnimplementedFeature::atomicTypes());
228+
return;
229+
}
230+
231+
// Codegen the RHS so that it stores directly into the LHS.
232+
AggValueSlot lhsSlot = AggValueSlot::forLValue(
233+
lhs, AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers,
234+
AggValueSlot::IsAliased, AggValueSlot::MayOverlap);
235+
236+
// A non-volatile aggregate destination might have volatile member.
237+
if (!lhsSlot.isVolatile() &&
238+
CGF.hasVolatileMember(E->getLHS()->getType()))
239+
assert(!UnimplementedFeature::atomicTypes());
240+
241+
CGF.buildAggExpr(E->getRHS(), lhsSlot);
242+
243+
// Copy into the destination if the assignment isn't ignored.
244+
buildFinalDestCopy(E->getType(), lhs);
245+
246+
if (!Dest.isIgnored() && !Dest.isExternallyDestructed() &&
247+
E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
248+
CGF.pushDestroy(QualType::DK_nontrivial_c_struct, Dest.getAddress(),
249+
E->getType());
250+
}
251+
140252
void VisitBinComma(const BinaryOperator *E) { llvm_unreachable("NYI"); }
141253
void VisitBinCmp(const BinaryOperator *E) { llvm_unreachable("NYI"); }
142254
void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,10 @@ class CIRGenFunction : public CIRGenTypeCache {
11971197
llvm_unreachable("bad destruction kind");
11981198
}
11991199

1200+
CleanupKind getCleanupKind(QualType::DestructionKind kind) {
1201+
return (needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup);
1202+
}
1203+
12001204
void pushEHDestroy(QualType::DestructionKind dtorKind, Address addr,
12011205
QualType type);
12021206

@@ -1510,6 +1514,9 @@ class CIRGenFunction : public CIRGenTypeCache {
15101514

15111515
static Destroyer destroyCXXObject;
15121516

1517+
void pushDestroy(QualType::DestructionKind dtorKind,
1518+
Address addr, QualType type);
1519+
15131520
void pushDestroy(CleanupKind kind, Address addr, QualType type,
15141521
Destroyer *destroyer, bool useEHCleanupForArray);
15151522

clang/test/CIR/CodeGen/agg-copy.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
typedef struct {} S;
5+
6+
typedef struct {
7+
int a;
8+
int b;
9+
S s;
10+
} A;
11+
12+
// CHECK: cir.func @foo1
13+
// CHECK: [[TMP0:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a1", init]
14+
// CHECK: [[TMP1:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a2", init]
15+
// CHECK: cir.store %arg0, [[TMP0]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
16+
// CHECK: cir.store %arg1, [[TMP1]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
17+
// CHECK: [[TMP2:%.*]] = cir.load [[TMP0]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
18+
// CHECK: [[TMP3:%.*]] = cir.const(#cir.int<1> : !s32i) : !s32i
19+
// CHECK: [[TMP4:%.*]] = cir.ptr_stride([[TMP2]] : !cir.ptr<!ty_22A22>, [[TMP3]] : !s32i), !cir.ptr<!ty_22A22>
20+
// CHECK: [[TMP5:%.*]] = cir.load [[TMP1]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
21+
// CHECK: [[TMP6:%.*]] = cir.const(#cir.int<1> : !s32i) : !s32i
22+
// CHECK: [[TMP7:%.*]] = cir.ptr_stride([[TMP5]] : !cir.ptr<!ty_22A22>, [[TMP6]] : !s32i), !cir.ptr<!ty_22A22>
23+
// CHECK: cir.copy [[TMP7]] to [[TMP4]] : !cir.ptr<!ty_22A22>
24+
void foo1(A* a1, A* a2) {
25+
a1[1] = a2[1];
26+
}
27+
28+
// CHECK: cir.func @foo2
29+
// CHECK: [[TMP0:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a1", init]
30+
// CHECK: [[TMP1:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a2", init]
31+
// CHECK: cir.store %arg0, [[TMP0]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
32+
// CHECK: cir.store %arg1, [[TMP1]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
33+
// CHECK: [[TMP2:%.*]] = cir.load [[TMP0]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
34+
// CHECK: [[TMP3:%.*]] = cir.get_member [[TMP2]][2] {name = "s"} : !cir.ptr<!ty_22A22> -> !cir.ptr<!ty_22S22>
35+
// CHECK: [[TMP4:%.*]] = cir.load [[TMP1]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
36+
// CHECK: [[TMP5:%.*]] = cir.get_member [[TMP4]][2] {name = "s"} : !cir.ptr<!ty_22A22> -> !cir.ptr<!ty_22S22>
37+
// CHECK: cir.copy [[TMP5]] to [[TMP3]] : !cir.ptr<!ty_22S22>
38+
void foo2(A* a1, A* a2) {
39+
a1->s = a2->s;
40+
}
41+
42+
// CHECK: cir.global external @a = #cir.zero : !ty_22A22
43+
// CHECK: cir.func @foo3
44+
// CHECK: [[TMP0]] = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["__retval"] {alignment = 4 : i64}
45+
// CHECK: [[TMP1]] = cir.get_global @a : cir.ptr <!ty_22A22>
46+
// CHECK: cir.copy [[TMP1]] to [[TMP0]] : !cir.ptr<!ty_22A22>
47+
// CHECK: [[TMP2]] = cir.load [[TMP0]] : cir.ptr <!ty_22A22>, !ty_22A22
48+
// CHECK: cir.return [[TMP2]] : !ty_22A22
49+
A a;
50+
A foo3(void) {
51+
return a;
52+
}
53+
54+
// CHECK: cir.func @foo4
55+
// CHECK: [[TMP0]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a1", init]
56+
// CHECK: [[TMP1]] = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["a2", init]
57+
// CHECK: cir.store %arg0, [[TMP0]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
58+
// CHECK: [[TMP2]] = cir.load deref [[TMP0]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
59+
// CHECK: cir.copy [[TMP2]] to [[TMP1]] : !cir.ptr<!ty_22A22>
60+
void foo4(A* a1) {
61+
A a2 = *a1;
62+
}

0 commit comments

Comments
 (0)