Author: Morris Hafner Date: 2025-04-11T10:26:29-07:00 New Revision: 566c30e324a0e87269fc72e5a0219d4c3e1d9fbf
URL: https://github.com/llvm/llvm-project/commit/566c30e324a0e87269fc72e5a0219d4c3e1d9fbf DIFF: https://github.com/llvm/llvm-project/commit/566c30e324a0e87269fc72e5a0219d4c3e1d9fbf.diff LOG: [CIR] Upstream binary assignments and comma (#135115) This patch adds `VisitBinAssign` and `VisitBinComma` to the ClangIR `ScalarExprEmitter` to enable assignments and the comma operator. --------- Co-authored-by: Morris Hafner <mhaf...@nvidia.com> Added: clang/test/CIR/CodeGen/binassign.c clang/test/CIR/CodeGen/comma.c clang/test/CIR/IR/binassign.cir Modified: clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenDecl.cpp clang/lib/CIR/CodeGen/CIRGenExpr.cpp clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h Removed: ################################################################################ diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index c39590421b647..d6a28d4324b32 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -143,6 +143,7 @@ struct MissingFeatures { static bool openMP() { return false; } static bool emitCheckedInBoundsGEP() { return false; } static bool preservedAccessIndexRegion() { return false; } + static bool bitfields() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index b8e72f299acb6..58797c5dd253a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -277,3 +277,11 @@ void CIRGenFunction::emitDecl(const Decl &d) { cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type"); } } + +void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs, + SourceLocation loc) { + if (!sanOpts.has(SanitizerKind::NullabilityAssign)) + return; + + assert(!cir::MissingFeatures::sanitizers()); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index b38ed4d0a14e8..f0732a8ea60af 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -257,6 +257,13 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, assert(!cir::MissingFeatures::opTBAA()); } +mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src, + LValue dst) { + assert(!cir::MissingFeatures::bitfields()); + cgm.errorNYI("bitfields"); + return {}; +} + mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) { // Bool has a diff erent representation in memory than in registers, // but in ClangIR, it is simply represented as a cir.bool value. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index c2b4110a772a0..38104f8533c7d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -21,6 +21,7 @@ #include "mlir/IR/Value.h" #include <cassert> +#include <utility> using namespace clang; using namespace clang::CIRGen; @@ -818,6 +819,65 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { VISITCOMP(EQ) VISITCOMP(NE) #undef VISITCOMP + + mlir::Value VisitBinAssign(const BinaryOperator *e) { + const bool ignore = std::exchange(ignoreResultAssign, false); + + mlir::Value rhs; + LValue lhs; + + switch (e->getLHS()->getType().getObjCLifetime()) { + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Weak: + assert(!cir::MissingFeatures::objCLifetime()); + break; + case Qualifiers::OCL_None: + // __block variables need to have the rhs evaluated first, plus this + // should improve codegen just a little. + rhs = Visit(e->getRHS()); + assert(!cir::MissingFeatures::sanitizers()); + // TODO(cir): This needs to be emitCheckedLValue() once we support + // sanitizers + lhs = cgf.emitLValue(e->getLHS()); + + // Store the value into the LHS. Bit-fields are handled 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 (lhs.isBitField()) { + rhs = cgf.emitStoreThroughBitfieldLValue(RValue::get(rhs), lhs); + } else { + cgf.emitNullabilityCheck(lhs, rhs, e->getExprLoc()); + CIRGenFunction::SourceLocRAIIObject loc{ + cgf, cgf.getLoc(e->getSourceRange())}; + cgf.emitStoreThroughLValue(RValue::get(rhs), lhs); + } + } + + // If the result is clearly ignored, return now. + if (ignore) + return nullptr; + + // The result of an assignment in C is the assigned r-value. + if (!cgf.getLangOpts().CPlusPlus) + return rhs; + + // If the lvalue is non-volatile, return the computed value of the + // assignment. + if (!lhs.isVolatile()) + return rhs; + + // Otherwise, reload the value. + return emitLoadOfLValue(lhs, e->getExprLoc()); + } + + mlir::Value VisitBinComma(const BinaryOperator *e) { + cgf.emitIgnoredExpr(e->getLHS()); + // NOTE: We don't need to EnsureInsertPoint() like LLVM codegen. + return Visit(e->getRHS()); + } }; LValue ScalarExprEmitter::emitCompoundAssignLValue( diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 17aa68492f983..8082592598c19 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -579,6 +579,8 @@ class CIRGenFunction : public CIRGenTypeCache { /// is 'Ty'. void emitStoreThroughLValue(RValue src, LValue dst, bool isInit = false); + mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult); + /// Given a value and its clang type, returns the value casted to its memory /// representation. /// Note: CIR defers most of the special casting to the final lowering passes @@ -593,6 +595,11 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s); + /// Given an assignment `*lhs = rhs`, emit a test that checks if \p rhs is + /// nonnull, if 1\p LHS is marked _Nonnull. + void emitNullabilityCheck(LValue lhs, mlir::Value rhs, + clang::SourceLocation loc); + /// ---------------------- /// CIR build helpers /// ----------------- diff --git a/clang/test/CIR/CodeGen/binassign.c b/clang/test/CIR/CodeGen/binassign.c new file mode 100644 index 0000000000000..5a7f0b7fd3f79 --- /dev/null +++ b/clang/test/CIR/CodeGen/binassign.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c23 -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=c23 -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 binary_assign(void) { + bool b; + char c; + float f; + int i; + + b = true; + c = 65; + f = 3.14f; + i = 42; +} + +// CIR-LABEL: cir.func @binary_assign() { +// CIR: %[[B:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"] +// CIR: %[[C:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"] +// CIR: %[[F:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"] +// CIR: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store %[[TRUE]], %[[B]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: %[[CHAR_INI_INIT:.*]] = cir.const #cir.int<65> : !s32i +// CIR: %[[CHAR_VAL:.*]] = cir.cast(integral, %[[CHAR_INI_INIT]] : !s32i), !s8i +// CIR: cir.store %[[CHAR_VAL]], %[[C]] : !s8i, !cir.ptr<!s8i> +// CIR: %[[FLOAT_VAL:.*]] = cir.const #cir.fp<3.140000e+00> : !cir.float +// CIR: cir.store %[[FLOAT_VAL]], %[[F]] : !cir.float, !cir.ptr<!cir.float> +// CIR: %[[INT_VAL:.*]] = cir.const #cir.int<42> : !s32i +// CIR: cir.store %[[INT_VAL]], %[[I]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.return + +// LLVM-LABEL: define {{.*}}void @binary_assign() { +// LLVM: %[[B_PTR:.*]] = alloca i8 +// LLVM: %[[C_PTR:.*]] = alloca i8 +// LLVM: %[[F_PTR:.*]] = alloca float +// LLVM: %[[I_PTR:.*]] = alloca i32 +// LLVM: store i8 1, ptr %[[B_PTR]] +// LLVM: store i8 65, ptr %[[C_PTR]] +// LLVM: store float 0x40091EB860000000, ptr %[[F_PTR]] +// LLVM: store i32 42, ptr %[[I_PTR]] +// LLVM: ret void + +// OGCG-LABEL: define {{.*}}void @binary_assign() +// OGCG: %[[B_PTR:.*]] = alloca i8 +// OGCG: %[[C_PTR:.*]] = alloca i8 +// OGCG: %[[F_PTR:.*]] = alloca float +// OGCG: %[[I_PTR:.*]] = alloca i32 +// OGCG: store i8 1, ptr %[[B_PTR]] +// OGCG: store i8 65, ptr %[[C_PTR]] +// OGCG: store float 0x40091EB860000000, ptr %[[F_PTR]] +// OGCG: store i32 42, ptr %[[I_PTR]] +// OGCG: ret void diff --git a/clang/test/CIR/CodeGen/comma.c b/clang/test/CIR/CodeGen/comma.c new file mode 100644 index 0000000000000..7ce53eaf1a238 --- /dev/null +++ b/clang/test/CIR/CodeGen/comma.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c23 -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=c23 -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 comma(void) { + bool b; + char c; + float f; + int i; + + b = true, c = 65, f = 3.14f, i = 42; + + i = 100, 200; +} + +// CIR-LABEL: cir.func @comma() { +// CIR: %[[B:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"] +// CIR: %[[C:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"] +// CIR: %[[F:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"] +// CIR: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store %[[TRUE]], %[[B]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: %[[CHAR_INI_INIT:.*]] = cir.const #cir.int<65> : !s32i +// CIR: %[[CHAR_VAL:.*]] = cir.cast(integral, %[[CHAR_INI_INIT]] : !s32i), !s8i +// CIR: cir.store %[[CHAR_VAL]], %[[C]] : !s8i, !cir.ptr<!s8i> +// CIR: %[[FLOAT_VAL:.*]] = cir.const #cir.fp<3.140000e+00> : !cir.float +// CIR: cir.store %[[FLOAT_VAL]], %[[F]] : !cir.float, !cir.ptr<!cir.float> +// CIR: %[[INT_VAL:.*]] = cir.const #cir.int<42> : !s32i +// CIR: cir.store %[[INT_VAL]], %[[I]] : !s32i, !cir.ptr<!s32i> +// CIR: %[[HUNDRED:.*]] = cir.const #cir.int<100> : !s32i +// CIR: cir.store %[[HUNDRED]], %[[I]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.return + +// LLVM-LABEL: define {{.*}}void @comma() { +// LLVM: %[[B_PTR:.*]] = alloca i8 +// LLVM: %[[C_PTR:.*]] = alloca i8 +// LLVM: %[[F_PTR:.*]] = alloca float +// LLVM: %[[I_PTR:.*]] = alloca i32 +// LLVM: store i8 1, ptr %[[B_PTR]] +// LLVM: store i8 65, ptr %[[C_PTR]] +// LLVM: store float 0x40091EB860000000, ptr %[[F_PTR]] +// LLVM: store i32 42, ptr %[[I_PTR]] +// LLVM: store i32 100, ptr %[[I_PTR]] +// LLVM: ret void + +// OGCG-LABEL: define {{.*}}void @comma() +// OGCG: %[[B_PTR:.*]] = alloca i8 +// OGCG: %[[C_PTR:.*]] = alloca i8 +// OGCG: %[[F_PTR:.*]] = alloca float +// OGCG: %[[I_PTR:.*]] = alloca i32 +// OGCG: store i8 1, ptr %[[B_PTR]] +// OGCG: store i8 65, ptr %[[C_PTR]] +// OGCG: store float 0x40091EB860000000, ptr %[[F_PTR]] +// OGCG: store i32 42, ptr %[[I_PTR]] +// OGCG: store i32 100, ptr %[[I_PTR]] +// OGCG: ret void diff --git a/clang/test/CIR/IR/binassign.cir b/clang/test/CIR/IR/binassign.cir new file mode 100644 index 0000000000000..24ed95d3c29c7 --- /dev/null +++ b/clang/test/CIR/IR/binassign.cir @@ -0,0 +1,45 @@ +// RUN: cir-opt %s | cir-opt | FileCheck %s + +!s32i = !cir.int<s, 32> +!s8i = !cir.int<s, 8> +#true = #cir.bool<true> : !cir.bool +module { + cir.func @binary_assign() { + %0 = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"] {alignment = 1 : i64} + %1 = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"] {alignment = 1 : i64} + %2 = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"] {alignment = 4 : i64} + %3 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] {alignment = 4 : i64} + %4 = cir.const #true + cir.store %4, %0 : !cir.bool, !cir.ptr<!cir.bool> + %5 = cir.const #cir.int<65> : !s32i + %6 = cir.cast(integral, %5 : !s32i), !s8i + cir.store %6, %1 : !s8i, !cir.ptr<!s8i> + %7 = cir.const #cir.fp<3.140000e+00> : !cir.float + cir.store %7, %2 : !cir.float, !cir.ptr<!cir.float> + %8 = cir.const #cir.int<42> : !s32i + cir.store %8, %3 : !s32i, !cir.ptr<!s32i> + cir.return + } +} + +// CHECK: !s32i = !cir.int<s, 32> +// CHECK: !s8i = !cir.int<s, 8> +// CHECK: #true = #cir.bool<true> : !cir.bool +// CHECK: module { +// CHECK: cir.func @binary_assign() { +// CHECK: %0 = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"] {alignment = 1 : i64} +// CHECK: %1 = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"] {alignment = 1 : i64} +// CHECK: %2 = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"] {alignment = 4 : i64} +// CHECK: %3 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] {alignment = 4 : i64} +// CHECK: %4 = cir.const #true +// CHECK: cir.store %4, %0 : !cir.bool, !cir.ptr<!cir.bool> +// CHECK: %5 = cir.const #cir.int<65> : !s32i +// CHECK: %6 = cir.cast(integral, %5 : !s32i), !s8i +// CHECK: cir.store %6, %1 : !s8i, !cir.ptr<!s8i> +// CHECK: %7 = cir.const #cir.fp<3.140000e+00> : !cir.float +// CHECK: cir.store %7, %2 : !cir.float, !cir.ptr<!cir.float> +// CHECK: %8 = cir.const #cir.int<42> : !s32i +// CHECK: cir.store %8, %3 : !s32i, !cir.ptr<!s32i> +// CHECK: cir.return +// CHECK: } +// CHECK: } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits