Skip to content
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
24 changes: 24 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,30 @@ LValue CIRGenFunction::emitMaterializeTemporaryExpr(
return makeAddrLValue(object, m->getType(), AlignmentSource::Decl);
}

LValue
CIRGenFunction::getOrCreateOpaqueLValueMapping(const OpaqueValueExpr *e) {
assert(OpaqueValueMapping::shouldBindAsLValue(e));

auto it = opaqueLValues.find(e);
if (it != opaqueLValues.end())
return it->second;

assert(e->isUnique() && "LValue for a nonunique OVE hasn't been emitted");
return emitLValue(e->getSourceExpr());
}

RValue
CIRGenFunction::getOrCreateOpaqueRValueMapping(const OpaqueValueExpr *e) {
assert(!OpaqueValueMapping::shouldBindAsLValue(e));

auto it = opaqueRValues.find(e);
if (it != opaqueRValues.end())
return it->second;

assert(e->isUnique() && "RValue for a nonunique OVE hasn't been emitted");
return emitAnyExpr(e->getSourceExpr());
}

LValue CIRGenFunction::emitCompoundLiteralLValue(const CompoundLiteralExpr *e) {
if (e->isFileScope()) {
cgm.errorNYI(e->getSourceRange(), "emitCompoundLiteralLValue: FileScope");
Expand Down
22 changes: 18 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,12 @@ class ComplexExprEmitter : public StmtVisitor<ComplexExprEmitter, mlir::Value> {
return emitLoadOfLValue(me);
}
mlir::Value VisitOpaqueValueExpr(OpaqueValueExpr *e) {
cgf.cgm.errorNYI(e->getExprLoc(),
"ComplexExprEmitter VisitOpaqueValueExpr");
return {};
if (e->isGLValue())
return emitLoadOfLValue(cgf.getOrCreateOpaqueLValueMapping(e),
e->getExprLoc());

// Otherwise, assume the mapping is the scalar directly.
return cgf.getOrCreateOpaqueRValueMapping(e).getValue();
}

mlir::Value VisitPseudoObjectExpr(PseudoObjectExpr *e) {
Expand Down Expand Up @@ -960,21 +963,32 @@ mlir::Value ComplexExprEmitter::VisitBinComma(const BinaryOperator *e) {

mlir::Value ComplexExprEmitter::VisitAbstractConditionalOperator(
const AbstractConditionalOperator *e) {
mlir::Value condValue = Visit(e->getCond());
mlir::Location loc = cgf.getLoc(e->getSourceRange());

// Bind the common expression if necessary.
CIRGenFunction::OpaqueValueMapping binding(cgf, e);

CIRGenFunction::ConditionalEvaluation eval(cgf);

Expr *cond = e->getCond()->IgnoreParens();
mlir::Value condValue = cgf.evaluateExprAsBool(cond);

return builder
.create<cir::TernaryOp>(
loc, condValue,
/*thenBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
eval.beginEvaluation();
mlir::Value trueValue = Visit(e->getTrueExpr());
b.create<cir::YieldOp>(loc, trueValue);
eval.endEvaluation();
},
/*elseBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
eval.beginEvaluation();
mlir::Value falseValue = Visit(e->getFalseExpr());
b.create<cir::YieldOp>(loc, falseValue);
eval.endEvaluation();
})
.getResult();
}
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
return emitNullValue(e->getType(), cgf.getLoc(e->getSourceRange()));
}

mlir::Value VisitOpaqueValueExpr(OpaqueValueExpr *e) {
if (e->isGLValue())
return emitLoadOfLValue(cgf.getOrCreateOpaqueLValueMapping(e),
e->getExprLoc());

// Otherwise, assume the mapping is the scalar directly.
return cgf.getOrCreateOpaqueRValueMapping(e).getValue();
}

mlir::Value VisitCastExpr(CastExpr *e);
mlir::Value VisitCallExpr(const CallExpr *e);

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,14 @@ class CIRGenFunction : public CIRGenTypeCache {
Address getAddrOfBitFieldStorage(LValue base, const clang::FieldDecl *field,
mlir::Type fieldType, unsigned index);

/// Given an opaque value expression, return its LValue mapping if it exists,
/// otherwise create one.
LValue getOrCreateOpaqueLValueMapping(const OpaqueValueExpr *e);

/// Given an opaque value expression, return its RValue mapping if it exists,
/// otherwise create one.
RValue getOrCreateOpaqueRValueMapping(const OpaqueValueExpr *e);

/// Load the value for 'this'. This function is only valid while generating
/// code for an C++ member function.
/// FIXME(cir): this should return a mlir::Value!
Expand Down
156 changes: 156 additions & 0 deletions clang/test/CIR/CodeGen/opaque.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG

void foo() {
int a;
int b = 1 ?: a;
}

// CIR: %[[A_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"]
// CIR: %[[B_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
// CIR: %[[CONST_1:.*]] = cir.const #cir.int<1> : !s32i
// CIR: cir.store{{.*}} %[[CONST_1]], %[[B_ADDR]] : !s32i, !cir.ptr<!s32i>

// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4
// LLVM: %[[B_ADDR:.*]] = alloca i32, i64 1, align 4
// LLVM: store i32 1, ptr %[[B_ADDR]], align 4

// OGCG: %[[A_ADDR:.*]] = alloca i32, align 4
// OGCG: %[[B_ADDR:.*]] = alloca i32, align 4
// OGCG: store i32 1, ptr %[[B_ADDR]], align 4

void foo2() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this case supposed to hit the complex expression visitor? Looking at the AST I see only float opaque values.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right. I updated the test case and found that handling VisitAbstractConditionalOperator for ComplexType in the incubator is not fully right, so I updated the logic to be similar to classic CG. I will backport this PR after it is merged.

CC. @bcardosolopes

float _Complex a;
float _Complex b;
float _Complex c = a ?: b;
}

// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["a"]
// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["b"]
// CIR: %[[C_ADDR:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["c", init]
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
// CIR: %[[A_REAL:.*]] = cir.complex.real %[[TMP_A]] : !cir.complex<!cir.float> -> !cir.float
// CIR: %[[A_IMAG:.*]] = cir.complex.imag %[[TMP_A]] : !cir.complex<!cir.float> -> !cir.float
// CIR: %[[A_REAL_BOOL:.*]] = cir.cast(float_to_bool, %[[A_REAL]] : !cir.float), !cir.bool
// CIR: %[[A_IMAG_BOOL:.*]] = cir.cast(float_to_bool, %[[A_IMAG]] : !cir.float), !cir.bool
// CIR: %[[CONST_TRUE:.*]] = cir.const #true
// CIR: %[[COND:.*]] = cir.select if %[[A_REAL_BOOL]] then %[[CONST_TRUE]] else %[[A_IMAG_BOOL]] : (!cir.bool, !cir.bool, !cir.bool) -> !cir.bool
// CIR: %[[RESULT:.*]] = cir.ternary(%[[COND]], true {
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
// CIR: cir.yield %[[TMP_A]] : !cir.complex<!cir.float>
// CIR: }, false {
// CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[B_ADDR]] : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
// CIR: cir.yield %[[TMP_B]] : !cir.complex<!cir.float>
// CIR: }) : (!cir.bool) -> !cir.complex<!cir.float>
// CIR: cir.store{{.*}} %[[RESULT]], %[[C_ADDR]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>

// LLVM: %[[A_ADDR:.*]] = alloca { float, float }, i64 1, align 4
// LLVM: %[[B_ADDR:.*]] = alloca { float, float }, i64 1, align 4
// LLVM: %[[C_ADDR:.*]] = alloca { float, float }, i64 1, align 4
// LLVM: %[[TMP_A:.*]] = load { float, float }, ptr %[[A_ADDR]], align 4
// LLVM: %[[A_REAL:.*]] = extractvalue { float, float } %[[TMP_A]], 0
// LLVM: %[[A_IMAG:.*]] = extractvalue { float, float } %[[TMP_A]], 1
// LLVM: %[[A_REAL_BOOL:.*]] = fcmp une float %[[A_REAL]], 0.000000e+00
// LLVM: %[[A_IMAG_BOOL:.*]] = fcmp une float %[[A_IMAG]], 0.000000e+00
// LLVM: %[[COND:.*]] = or i1 %[[A_REAL_BOOL]], %[[A_IMAG_BOOL]]
// LLVM: br i1 %[[COND]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// LLVM: [[COND_TRUE]]:
// LLVM: %[[TMP_A:.*]] = load { float, float }, ptr %[[A_ADDR]], align 4
// LLVM: br label %[[COND_RESULT:.*]]
// LLVM: [[COND_FALSE]]:
// LLVM: %[[TMP_B:.*]] = load { float, float }, ptr %[[B_ADDR]], align 4
// LLVM: br label %[[COND_RESULT]]
// LLVM: [[COND_RESULT]]:
// LLVM: %[[RESULT:.*]] = phi { float, float } [ %[[TMP_B]], %[[COND_FALSE]] ], [ %[[TMP_A]], %[[COND_TRUE]] ]
// LLVM: br label %[[COND_END:.*]]
// LLVM: [[COND_END]]:
// LLVM: store { float, float } %[[RESULT]], ptr %[[C_ADDR]], align 4

// OGCG: %[[A_ADDR:.*]] = alloca { float, float }, align 4
// OGCG: %[[B_ADDR:.*]] = alloca { float, float }, align 4
// OGCG: %[[C_ADDR:.*]] = alloca { float, float }, align 4
// OGCG: %[[A_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[A_ADDR]], i32 0, i32 0
// OGCG: %[[A_REAL:.*]] = load float, ptr %[[A_REAL_PTR]], align 4
// OGCG: %[[A_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[A_ADDR]], i32 0, i32 1
// OGCG: %[[A_IMAG:.*]] = load float, ptr %[[A_IMAG_PTR]], align 4
// OGCG: %[[A_REAL_BOOL:.*]] = fcmp une float %[[A_REAL]], 0.000000e+00
// OGCG: %[[A_IMAG_BOOL:.*]] = fcmp une float %[[A_IMAG]], 0.000000e+00
// OGCG: %[[COND:.*]] = or i1 %[[A_REAL_BOOL]], %[[A_IMAG_BOOL]]
// OGCG: br i1 %tobool2, label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// OGCG: [[COND_TRUE]]:
// OGCG: %[[A_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[A_ADDR]], i32 0, i32 0
// OGCG: %[[A_REAL:.*]] = load float, ptr %[[A_REAL_PTR]], align 4
// OGCG: %[[A_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[A_ADDR]], i32 0, i32 1
// OGCG: %[[A_IMAG:.*]] = load float, ptr %[[A_IMAG_PTR]], align 4
// OGCG: br label %[[COND_END:.*]]
// OGCG: [[COND_FALSE]]:
// OGCG: %[[B_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[B_ADDR]], i32 0, i32 0
// OGCG: %[[B_REAL:.*]] = load float, ptr %[[B_REAL_PTR]], align 4
// OGCG: %[[B_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[B_ADDR]], i32 0, i32 1
// OGCG: %[[B_IMAG:.*]] = load float, ptr %[[B_IMAG_PTR]], align 4
// OGCG: br label %[[COND_END]]
// OGCG: [[COND_END]]:
// OGCG: %[[RESULT_REAL:.*]] = phi float [ %[[A_REAL]], %[[COND_TRUE]] ], [ %[[B_REAL]], %[[COND_FALSE]] ]
// OGCG: %[[RESULT_IMAG:.*]] = phi float [ %[[A_IMAG]], %[[COND_TRUE]] ], [ %[[B_IMAG]], %[[COND_FALSE]] ]
// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[C_ADDR]], i32 0, i32 0
// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[C_ADDR]], i32 0, i32 1
// OGCG: store float %[[RESULT_REAL]], ptr %[[C_REAL_PTR]], align 4
// OGCG: store float %[[RESULT_IMAG]], ptr %[[C_IMAG_PTR]], align 4

void foo3() {
int a;
int b;
int c = a ?: b;
}

// CIR: %[[A_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"]
// CIR: %[[B_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b"]
// CIR: %[[C_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["c", init]
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[A_BOOL:.*]] = cir.cast(int_to_bool, %[[TMP_A]] : !s32i), !cir.bool
// CIR: %[[RESULT:.*]] = cir.ternary(%[[A_BOOL]], true {
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, !s32i
// CIR: cir.yield %[[TMP_A]] : !s32i
// CIR: }, false {
// CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[B_ADDR]] : !cir.ptr<!s32i>, !s32i
// CIR: cir.yield %[[TMP_B]] : !s32i
// CIR: }) : (!cir.bool) -> !s32i
// CIR: cir.store{{.*}} %[[RESULT]], %[[C_ADDR]] : !s32i, !cir.ptr<!s32i>

// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4
// LLVM: %[[B_ADDR:.*]] = alloca i32, i64 1, align 4
// LLVM: %[[C_ADDR:.*]] = alloca i32, i64 1, align 4
// LLVM: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
// LLVM: %[[COND:.*]] = icmp ne i32 %[[TMP_A]], 0
// LLVM: br i1 %[[COND]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// LLVM: [[COND_TRUE]]:
// LLVM: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
// LLVM: br label %[[COND_RESULT:.*]]
// LLVM: [[COND_FALSE]]:
// LLVM: %[[TMP_B:.*]] = load i32, ptr %[[B_ADDR]], align 4
// LLVM: br label %[[COND_RESULT]]
// LLVM: [[COND_RESULT]]:
// LLVM: %[[RESULT:.*]] = phi i32 [ %[[TMP_B]], %[[COND_FALSE]] ], [ %[[TMP_A]], %[[COND_TRUE]] ]
// LLVM: br label %[[COND_END:.*]]
// LLVM: [[COND_END]]:
// LLVM: store i32 %[[RESULT]], ptr %[[C_ADDR]], align 4

// OGCG: %[[A_ADDR:.*]] = alloca i32, align 4
// OGCG: %[[B_ADDR:.*]] = alloca i32, align 4
// OGCG: %[[C_ADDR:.*]] = alloca i32, align 4
// OGCG: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
// OGCG: %[[A_BOOL:.*]] = icmp ne i32 %[[TMP_A]], 0
// OGCG: br i1 %[[A_BOOL]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// OGCG: [[COND_TRUE]]:
// OGCG: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
// OGCG: br label %[[COND_END:.*]]
// OGCG: [[COND_FALSE]]:
// OGCG: %[[TMP_B:.*]] = load i32, ptr %[[B_ADDR]], align 4
// OGCG: br label %[[COND_END]]
// OGCG: [[COND_END]]:
// OGCG: %[[RESULT:.*]] = phi i32 [ %[[TMP_A]], %[[COND_TRUE]] ], [ %[[TMP_B]], %[[COND_FALSE]] ]
// OGCG: store i32 %[[RESULT]], ptr %[[C_ADDR]], align 4
Loading