Skip to content

Commit

Permalink
[CIR][CogeGen] Support aggregate copy via assignment (llvm#325)
Browse files Browse the repository at this point in the history
This PR adds a support of copies of aggregated data types via
assignment.
  • Loading branch information
gitoleg authored and lanza committed Jan 29, 2024
1 parent c74aed1 commit 48572c3
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 3 deletions.
11 changes: 11 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,17 @@ struct CallCleanupFunction final : EHScopeStack::Cleanup {
};
} // end anonymous namespace

/// Push the standard destructor for the given type as
/// at least a normal cleanup.
void CIRGenFunction::pushDestroy(QualType::DestructionKind dtorKind,
Address addr, QualType type) {
assert(dtorKind && "cannot push destructor for trivial type");

CleanupKind cleanupKind = getCleanupKind(dtorKind);
pushDestroy(cleanupKind, addr, type, getDestroyer(dtorKind),
cleanupKind & EHCleanup);
}

void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
QualType type, Destroyer *destroyer,
bool useEHCleanupForArray) {
Expand Down
118 changes: 115 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,75 @@ using namespace cir;
using namespace clang;

namespace {

// FIXME(cir): This should be a common helper between CIRGen
// and traditional CodeGen
/// Is the value of the given expression possibly a reference to or
/// into a __block variable?
static bool isBlockVarRef(const Expr *E) {
// Make sure we look through parens.
E = E->IgnoreParens();

// Check for a direct reference to a __block variable.
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
const VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl());
return (var && var->hasAttr<BlocksAttr>());
}

// More complicated stuff.

// Binary operators.
if (const BinaryOperator *op = dyn_cast<BinaryOperator>(E)) {
// For an assignment or pointer-to-member operation, just care
// about the LHS.
if (op->isAssignmentOp() || op->isPtrMemOp())
return isBlockVarRef(op->getLHS());

// For a comma, just care about the RHS.
if (op->getOpcode() == BO_Comma)
return isBlockVarRef(op->getRHS());

// FIXME: pointer arithmetic?
return false;

// Check both sides of a conditional operator.
} else if (const AbstractConditionalOperator *op
= dyn_cast<AbstractConditionalOperator>(E)) {
return isBlockVarRef(op->getTrueExpr())
|| isBlockVarRef(op->getFalseExpr());

// OVEs are required to support BinaryConditionalOperators.
} else if (const OpaqueValueExpr *op
= dyn_cast<OpaqueValueExpr>(E)) {
if (const Expr *src = op->getSourceExpr())
return isBlockVarRef(src);

// Casts are necessary to get things like (*(int*)&var) = foo().
// We don't really care about the kind of cast here, except
// we don't want to look through l2r casts, because it's okay
// to get the *value* in a __block variable.
} else if (const CastExpr *cast = dyn_cast<CastExpr>(E)) {
if (cast->getCastKind() == CK_LValueToRValue)
return false;
return isBlockVarRef(cast->getSubExpr());

// Handle unary operators. Again, just aggressively look through
// it, ignoring the operation.
} else if (const UnaryOperator *uop = dyn_cast<UnaryOperator>(E)) {
return isBlockVarRef(uop->getSubExpr());

// Look into the base of a field access.
} else if (const MemberExpr *mem = dyn_cast<MemberExpr>(E)) {
return isBlockVarRef(mem->getBase());

// Look into the base of a subscript.
} else if (const ArraySubscriptExpr *sub = dyn_cast<ArraySubscriptExpr>(E)) {
return isBlockVarRef(sub->getBase());
}

return false;
}

class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
CIRGenFunction &CGF;
AggValueSlot Dest;
Expand Down Expand Up @@ -117,8 +186,8 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {

// l-values
void VisitDeclRefExpr(DeclRefExpr *E) { buildAggLoadOfLValue(E); }
void VisitMemberExpr(MemberExpr *E) { llvm_unreachable("NYI"); }
void VisitUnaryDeref(UnaryOperator *E) { llvm_unreachable("NYI"); }
void VisitMemberExpr(MemberExpr *E) { buildAggLoadOfLValue(E); }
void VisitUnaryDeref(UnaryOperator *E) { buildAggLoadOfLValue(E); }
void VisitStringLiteral(StringLiteral *E) { llvm_unreachable("NYI"); }
void VisitCompoundLIteralExpr(CompoundLiteralExpr *E) {
llvm_unreachable("NYI");
Expand All @@ -136,7 +205,50 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *E) {
llvm_unreachable("NYI");
}
void VisitBinAssign(const BinaryOperator *E) { llvm_unreachable("NYI"); }
void VisitBinAssign(const BinaryOperator *E) {

// For an assignment to work, the value on the right has
// to be compatible with the value on the left.
assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(),
E->getRHS()->getType())
&& "Invalid assignment");

if (isBlockVarRef(E->getLHS()) &&
E->getRHS()->HasSideEffects(CGF.getContext())) {
llvm_unreachable("NYI");
}

LValue lhs = CGF.buildLValue(E->getLHS());

// If we have an atomic type, evaluate into the destination and then
// do an atomic copy.
if (lhs.getType()->isAtomicType() ||
CGF.LValueIsSuitableForInlineAtomic(lhs)) {
assert(!UnimplementedFeature::atomicTypes());
return;
}

// Codegen the RHS so that it stores directly into the LHS.
AggValueSlot lhsSlot = AggValueSlot::forLValue(
lhs, AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsAliased, AggValueSlot::MayOverlap);

// A non-volatile aggregate destination might have volatile member.
if (!lhsSlot.isVolatile() &&
CGF.hasVolatileMember(E->getLHS()->getType()))
assert(!UnimplementedFeature::atomicTypes());

CGF.buildAggExpr(E->getRHS(), lhsSlot);

// Copy into the destination if the assignment isn't ignored.
buildFinalDestCopy(E->getType(), lhs);

if (!Dest.isIgnored() && !Dest.isExternallyDestructed() &&
E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
CGF.pushDestroy(QualType::DK_nontrivial_c_struct, Dest.getAddress(),
E->getType());
}

void VisitBinComma(const BinaryOperator *E) { llvm_unreachable("NYI"); }
void VisitBinCmp(const BinaryOperator *E) { llvm_unreachable("NYI"); }
void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,10 @@ class CIRGenFunction : public CIRGenTypeCache {
llvm_unreachable("bad destruction kind");
}

CleanupKind getCleanupKind(QualType::DestructionKind kind) {
return (needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup);
}

void pushEHDestroy(QualType::DestructionKind dtorKind, Address addr,
QualType type);

Expand Down Expand Up @@ -1510,6 +1514,9 @@ class CIRGenFunction : public CIRGenTypeCache {

static Destroyer destroyCXXObject;

void pushDestroy(QualType::DestructionKind dtorKind,
Address addr, QualType type);

void pushDestroy(CleanupKind kind, Address addr, QualType type,
Destroyer *destroyer, bool useEHCleanupForArray);

Expand Down
62 changes: 62 additions & 0 deletions clang/test/CIR/CodeGen/agg-copy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

typedef struct {} S;

typedef struct {
int a;
int b;
S s;
} A;

// CHECK: cir.func @foo1
// CHECK: [[TMP0:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a1", init]
// CHECK: [[TMP1:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a2", init]
// CHECK: cir.store %arg0, [[TMP0]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
// CHECK: cir.store %arg1, [[TMP1]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
// CHECK: [[TMP2:%.*]] = cir.load [[TMP0]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
// CHECK: [[TMP3:%.*]] = cir.const(#cir.int<1> : !s32i) : !s32i
// CHECK: [[TMP4:%.*]] = cir.ptr_stride([[TMP2]] : !cir.ptr<!ty_22A22>, [[TMP3]] : !s32i), !cir.ptr<!ty_22A22>
// CHECK: [[TMP5:%.*]] = cir.load [[TMP1]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
// CHECK: [[TMP6:%.*]] = cir.const(#cir.int<1> : !s32i) : !s32i
// CHECK: [[TMP7:%.*]] = cir.ptr_stride([[TMP5]] : !cir.ptr<!ty_22A22>, [[TMP6]] : !s32i), !cir.ptr<!ty_22A22>
// CHECK: cir.copy [[TMP7]] to [[TMP4]] : !cir.ptr<!ty_22A22>
void foo1(A* a1, A* a2) {
a1[1] = a2[1];
}

// CHECK: cir.func @foo2
// CHECK: [[TMP0:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a1", init]
// CHECK: [[TMP1:%.*]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a2", init]
// CHECK: cir.store %arg0, [[TMP0]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
// CHECK: cir.store %arg1, [[TMP1]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
// CHECK: [[TMP2:%.*]] = cir.load [[TMP0]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
// CHECK: [[TMP3:%.*]] = cir.get_member [[TMP2]][2] {name = "s"} : !cir.ptr<!ty_22A22> -> !cir.ptr<!ty_22S22>
// CHECK: [[TMP4:%.*]] = cir.load [[TMP1]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
// CHECK: [[TMP5:%.*]] = cir.get_member [[TMP4]][2] {name = "s"} : !cir.ptr<!ty_22A22> -> !cir.ptr<!ty_22S22>
// CHECK: cir.copy [[TMP5]] to [[TMP3]] : !cir.ptr<!ty_22S22>
void foo2(A* a1, A* a2) {
a1->s = a2->s;
}

// CHECK: cir.global external @a = #cir.zero : !ty_22A22
// CHECK: cir.func @foo3
// CHECK: [[TMP0]] = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["__retval"] {alignment = 4 : i64}
// CHECK: [[TMP1]] = cir.get_global @a : cir.ptr <!ty_22A22>
// CHECK: cir.copy [[TMP1]] to [[TMP0]] : !cir.ptr<!ty_22A22>
// CHECK: [[TMP2]] = cir.load [[TMP0]] : cir.ptr <!ty_22A22>, !ty_22A22
// CHECK: cir.return [[TMP2]] : !ty_22A22
A a;
A foo3(void) {
return a;
}

// CHECK: cir.func @foo4
// CHECK: [[TMP0]] = cir.alloca !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>, ["a1", init]
// CHECK: [[TMP1]] = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["a2", init]
// CHECK: cir.store %arg0, [[TMP0]] : !cir.ptr<!ty_22A22>, cir.ptr <!cir.ptr<!ty_22A22>>
// CHECK: [[TMP2]] = cir.load deref [[TMP0]] : cir.ptr <!cir.ptr<!ty_22A22>>, !cir.ptr<!ty_22A22>
// CHECK: cir.copy [[TMP2]] to [[TMP1]] : !cir.ptr<!ty_22A22>
void foo4(A* a1) {
A a2 = *a1;
}

0 comments on commit 48572c3

Please sign in to comment.