Skip to content

[CIR] Upstream BinAssign for ComplexType #144868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,10 +997,9 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
}

case cir::TEK_Complex: {
assert(!cir::MissingFeatures::complexType());
cgm.errorNYI(e->getSourceRange(), "complex l-values");
return {};
return emitComplexAssignmentLValue(e);
}

case cir::TEK_Aggregate:
cgm.errorNYI(e->getSourceRange(), "aggregate lvalues");
return {};
Expand Down
98 changes: 93 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ class ComplexExprEmitter : public StmtVisitor<ComplexExprEmitter, mlir::Value> {
// Utilities
//===--------------------------------------------------------------------===//

LValue emitBinAssignLValue(const BinaryOperator *e, mlir::Value &val);

mlir::Value emitCast(CastKind ck, Expr *op, QualType destTy);

mlir::Value emitConstant(const CIRGenFunction::ConstantEmission &constant,
Expr *e);

/// 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.
Expand All @@ -27,18 +34,18 @@ class ComplexExprEmitter : public StmtVisitor<ComplexExprEmitter, mlir::Value> {
}

mlir::Value emitLoadOfLValue(LValue lv, SourceLocation loc);

/// Store the specified real/imag parts into the
/// specified value pointer.
void emitStoreOfComplex(mlir::Location loc, mlir::Value val, LValue lv,
bool isInit);

mlir::Value VisitBinAssign(const BinaryOperator *e);
mlir::Value VisitCallExpr(const CallExpr *e);
mlir::Value VisitInitListExpr(InitListExpr *e);

mlir::Value VisitDeclRefExpr(DeclRefExpr *e);
mlir::Value VisitImplicitCastExpr(ImplicitCastExpr *e);
mlir::Value VisitInitListExpr(const InitListExpr *e);
mlir::Value VisitImaginaryLiteral(const ImaginaryLiteral *il);
};

} // namespace

static const ComplexType *getComplexType(QualType type) {
Expand All @@ -48,6 +55,46 @@ static const ComplexType *getComplexType(QualType type) {
return cast<ComplexType>(cast<AtomicType>(type)->getValueType());
}

LValue ComplexExprEmitter::emitBinAssignLValue(const BinaryOperator *e,
mlir::Value &value) {
assert(cgf.getContext().hasSameUnqualifiedType(e->getLHS()->getType(),
e->getRHS()->getType()) &&
"Invalid assignment");

// Emit the RHS. __block variables need the RHS evaluated first.
value = Visit(e->getRHS());

// Compute the address to store into.
LValue lhs = cgf.emitLValue(e->getLHS());

// Store the result value into the LHS lvalue.
emitStoreOfComplex(cgf.getLoc(e->getExprLoc()), value, lhs, /*isInit*/ false);
return lhs;
}

mlir::Value ComplexExprEmitter::emitCast(CastKind ck, Expr *op,
QualType destTy) {
switch (ck) {
case CK_LValueToRValue:
return Visit(op);
default:
cgf.cgm.errorNYI("ComplexType Cast");
break;
}
return {};
}

mlir::Value ComplexExprEmitter::emitConstant(
const CIRGenFunction::ConstantEmission &constant, Expr *e) {
assert(constant && "not a constant");
if (constant.isReference())
return emitLoadOfLValue(constant.getReferenceLValue(cgf, e),
e->getExprLoc());

mlir::TypedAttr valueAttr = constant.getValue();
return builder.getConstant(cgf.getLoc(e->getSourceRange()), valueAttr);
}

mlir::Value ComplexExprEmitter::emitLoadOfLValue(LValue lv,
SourceLocation loc) {
assert(lv.isSimple() && "non-simple complex l-value?");
Expand All @@ -70,14 +117,44 @@ void ComplexExprEmitter::emitStoreOfComplex(mlir::Location loc, mlir::Value val,
builder.createStore(loc, val, destAddr);
}

mlir::Value ComplexExprEmitter::VisitBinAssign(const BinaryOperator *e) {
mlir::Value value;
LValue lv = emitBinAssignLValue(e, value);

// The result of an assignment in C is the assigned r-value.
if (!cgf.getLangOpts().CPlusPlus)
return value;

// If the lvalue is non-volatile, return the computed value of the
// assignment.
if (!lv.isVolatile())
return value;

return emitLoadOfLValue(lv, e->getExprLoc());
}

mlir::Value ComplexExprEmitter::VisitCallExpr(const CallExpr *e) {
if (e->getCallReturnType(cgf.getContext())->isReferenceType())
return emitLoadOfLValue(e);

return cgf.emitCallExpr(e).getValue();
}

mlir::Value ComplexExprEmitter::VisitInitListExpr(InitListExpr *e) {
mlir::Value ComplexExprEmitter::VisitDeclRefExpr(DeclRefExpr *e) {
if (CIRGenFunction::ConstantEmission constant = cgf.tryEmitAsConstant(e))
return emitConstant(constant, e);
return emitLoadOfLValue(e);
}

mlir::Value ComplexExprEmitter::VisitImplicitCastExpr(ImplicitCastExpr *e) {
// Unlike for scalars, we don't have to worry about function->ptr demotion
// here.
if (e->changesVolatileQualification())
return emitLoadOfLValue(e);
return emitCast(e->getCastKind(), e->getSubExpr(), e->getType());
}

mlir::Value ComplexExprEmitter::VisitInitListExpr(const InitListExpr *e) {
mlir::Location loc = cgf.getLoc(e->getExprLoc());
if (e->getNumInits() == 2) {
mlir::Value real = cgf.emitScalarExpr(e->getInit(0));
Expand Down Expand Up @@ -127,6 +204,17 @@ ComplexExprEmitter::VisitImaginaryLiteral(const ImaginaryLiteral *il) {
return builder.create<cir::ConstantOp>(loc, complexAttr);
}

LValue CIRGenFunction::emitComplexAssignmentLValue(const BinaryOperator *e) {
assert(e->getOpcode() == BO_Assign && "Expected assign op");

mlir::Value value; // ignored
LValue lvalue = ComplexExprEmitter(*this).emitBinAssignLValue(e, value);
if (getLangOpts().OpenMP)
cgm.errorNYI("emitComplexAssignmentLValue OpenMP");

return lvalue;
}

mlir::Value CIRGenFunction::emitComplexExpr(const Expr *e) {
assert(e && getComplexType(e->getType()) &&
"Invalid complex expression to emit");
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,8 @@ class CIRGenFunction : public CIRGenTypeCache {
/// returning the result.
mlir::Value emitComplexExpr(const Expr *e);

LValue emitComplexAssignmentLValue(const BinaryOperator *e);

void emitCompoundStmt(const clang::CompoundStmt &s);

void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/CIR/CodeGen/CIRGenValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,7 @@ class LValue {
bool isSimple() const { return lvType == Simple; }
bool isVectorElt() const { return lvType == VectorElt; }
bool isBitField() const { return lvType == BitField; }

// TODO: Add support for volatile
bool isVolatile() const { return false; }
bool isVolatile() const { return quals.hasVolatile(); }

unsigned getVRQualifiers() const {
return quals.getCVRQualifiers() & ~clang::Qualifiers::Const;
Expand Down
26 changes: 26 additions & 0 deletions clang/test/CIR/CodeGen/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,29 @@ void foo14() {
// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 1
// OGCG: store i32 0, ptr %[[C_REAL_PTR]], align 4
// OGCG: store i32 2, ptr %[[C_IMAG_PTR]], align 4

void foo15() {
int _Complex a;
int _Complex b = a;
}

// CIR: %[[COMPLEX_A:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["a"]
// CIR: %[[COMPLEX_B:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["b", init]
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[COMPLEX_A]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
// CIR: cir.store{{.*}} %[[TMP_A]], %[[COMPLEX_B]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>

// LLVM: %[[COMPLEX_A:.*]] = alloca { i32, i32 }, i64 1, align 4
// LLVM: %[[COMPLEX_B:.*]] = alloca { i32, i32 }, i64 1, align 4
// LLVM: %[[TMP_A:.*]] = load { i32, i32 }, ptr %[[COMPLEX_A]], align 4
// LLVM: store { i32, i32 } %[[TMP_A]], ptr %[[COMPLEX_B]], align 4

// OGCG: %[[COMPLEX_A:.*]] = alloca { i32, i32 }, align 4
// OGCG: %[[COMPLEX_B:.*]] = alloca { i32, i32 }, align 4
// OGCG: %[[A_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_A]], i32 0, i32 0
// OGCG: %[[A_REAL:.*]] = load i32, ptr %[[A_REAL_PTR]], align 4
// OGCG: %[[A_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_A]], i32 0, i32 1
// OGCG: %[[A_IMAG:.*]] = load i32, ptr %[[A_IMAG_PTR]], align 4
// OGCG: %[[B_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 0
// OGCG: %[[B_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 1
// OGCG: store i32 %[[A_REAL]], ptr %[[B_REAL_PTR]], align 4
// OGCG: store i32 %[[A_IMAG]], ptr %[[B_IMAG_PTR]], align 4
Loading