https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/135115
>From bcaeadf7fff01d2d92b0d234b40f8d5884974e4c Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 10 Apr 2025 03:53:43 +0200 Subject: [PATCH 1/3] [CIR] Upstream binary assignments and comma This patch adds `VisitBinAssign` and `VisitBinComma` to the ClangIR `ScalarExprEmitter` to enable assignments and the comma operator. --- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 8 +++ clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 7 +++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 60 ++++++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 8 +++ clang/test/CIR/CodeGen/binassign.c | 56 ++++++++++++++++++++ clang/test/CIR/CodeGen/comma.c | 53 +++++++++++++++++++ clang/test/CIR/IR/binassign.cir | 45 ++++++++++++++++ 8 files changed, 238 insertions(+) create mode 100644 clang/test/CIR/CodeGen/binassign.c create mode 100644 clang/test/CIR/CodeGen/comma.c create mode 100644 clang/test/CIR/IR/binassign.cir diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 3188429ea3b1b..19cd9c03635b3 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -113,6 +113,7 @@ struct MissingFeatures { static bool incrementProfileCounter() { return false; } static bool insertBuiltinUnpredictable() { return false; } static bool objCGC() { 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 4b6652ad0b9e6..66caea26c3636 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -219,6 +219,13 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, assert(!cir::MissingFeatures::opTBAA()); } +void CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src, LValue dst, + mlir::Value &result) { + assert(!cir::MissingFeatures::bitfields()); + cgm.errorNYI("bitfields"); + result = {}; +} + mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) { // Bool has a different 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 4042f5dc23e4b..955082047bd38 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; @@ -807,6 +808,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()) { + cgf.emitStoreThroughBitfieldLValue(RValue::get(rhs), lhs, rhs); + } 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 6ffa106f2a383..7fb34680d8a9f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -558,6 +558,9 @@ class CIRGenFunction : public CIRGenTypeCache { /// is 'Ty'. void emitStoreThroughLValue(RValue src, LValue dst, bool isInit = false); + void emitStoreThroughBitfieldLValue(RValue src, LValue dst, + mlir::Value &result); + /// 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 @@ -572,6 +575,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..208a496c26e44 --- /dev/null +++ b/clang/test/CIR/CodeGen/comma.c @@ -0,0 +1,53 @@ +// 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; +} + +// 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: 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: 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: 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: } >From eec7be18ad3edccd764871bfe6f1aaa362c62b5f Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 10 Apr 2025 17:30:14 +0200 Subject: [PATCH 2/3] Add one more comma test case --- clang/test/CIR/CodeGen/comma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/test/CIR/CodeGen/comma.c b/clang/test/CIR/CodeGen/comma.c index 208a496c26e44..7ce53eaf1a238 100644 --- a/clang/test/CIR/CodeGen/comma.c +++ b/clang/test/CIR/CodeGen/comma.c @@ -12,6 +12,8 @@ void comma(void) { int i; b = true, c = 65, f = 3.14f, i = 42; + + i = 100, 200; } // CIR-LABEL: cir.func @comma() { @@ -28,6 +30,8 @@ void comma(void) { // 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() { @@ -39,6 +43,7 @@ void comma(void) { // 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() @@ -50,4 +55,5 @@ void comma(void) { // 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 >From 9601ed4e3701172d47791a6b37f04b1d32f29d87 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Fri, 11 Apr 2025 16:12:57 +0200 Subject: [PATCH 3/3] Change signature of emitStoreThroughBitfieldLValue --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 5 ++--- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 66caea26c3636..825e9e0df28f5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -219,11 +219,10 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, assert(!cir::MissingFeatures::opTBAA()); } -void CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src, LValue dst, - mlir::Value &result) { +mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src, LValue dst) { assert(!cir::MissingFeatures::bitfields()); cgm.errorNYI("bitfields"); - result = {}; + return {}; } mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 955082047bd38..18baffe55d101 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -836,7 +836,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // 'An assignment expression has the value of the left operand after the // assignment...'. if (lhs.isBitField()) { - cgf.emitStoreThroughBitfieldLValue(RValue::get(rhs), lhs, rhs); + rhs = cgf.emitStoreThroughBitfieldLValue(RValue::get(rhs), lhs); } else { cgf.emitNullabilityCheck(lhs, rhs, e->getExprLoc()); CIRGenFunction::SourceLocRAIIObject loc{ diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 7fb34680d8a9f..8a32d2dbebe2e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -558,8 +558,7 @@ class CIRGenFunction : public CIRGenTypeCache { /// is 'Ty'. void emitStoreThroughLValue(RValue src, LValue dst, bool isInit = false); - void emitStoreThroughBitfieldLValue(RValue src, LValue dst, - mlir::Value &result); + mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult); /// Given a value and its clang type, returns the value casted to its memory /// representation. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits