Skip to content

Commit 566c30e

Browse files
authored
[CIR] Upstream binary assignments and comma (llvm#135115)
This patch adds `VisitBinAssign` and `VisitBinComma` to the ClangIR `ScalarExprEmitter` to enable assignments and the comma operator. --------- Co-authored-by: Morris Hafner <[email protected]>
1 parent 8fb6bb3 commit 566c30e

File tree

8 files changed

+243
-0
lines changed

8 files changed

+243
-0
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ struct MissingFeatures {
143143
static bool openMP() { return false; }
144144
static bool emitCheckedInBoundsGEP() { return false; }
145145
static bool preservedAccessIndexRegion() { return false; }
146+
static bool bitfields() { return false; }
146147

147148
// Missing types
148149
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,11 @@ void CIRGenFunction::emitDecl(const Decl &d) {
277277
cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type");
278278
}
279279
}
280+
281+
void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
282+
SourceLocation loc) {
283+
if (!sanOpts.has(SanitizerKind::NullabilityAssign))
284+
return;
285+
286+
assert(!cir::MissingFeatures::sanitizers());
287+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,13 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
257257
assert(!cir::MissingFeatures::opTBAA());
258258
}
259259

260+
mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
261+
LValue dst) {
262+
assert(!cir::MissingFeatures::bitfields());
263+
cgm.errorNYI("bitfields");
264+
return {};
265+
}
266+
260267
mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
261268
// Bool has a different representation in memory than in registers,
262269
// but in ClangIR, it is simply represented as a cir.bool value.

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "mlir/IR/Value.h"
2222

2323
#include <cassert>
24+
#include <utility>
2425

2526
using namespace clang;
2627
using namespace clang::CIRGen;
@@ -818,6 +819,65 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
818819
VISITCOMP(EQ)
819820
VISITCOMP(NE)
820821
#undef VISITCOMP
822+
823+
mlir::Value VisitBinAssign(const BinaryOperator *e) {
824+
const bool ignore = std::exchange(ignoreResultAssign, false);
825+
826+
mlir::Value rhs;
827+
LValue lhs;
828+
829+
switch (e->getLHS()->getType().getObjCLifetime()) {
830+
case Qualifiers::OCL_Strong:
831+
case Qualifiers::OCL_Autoreleasing:
832+
case Qualifiers::OCL_ExplicitNone:
833+
case Qualifiers::OCL_Weak:
834+
assert(!cir::MissingFeatures::objCLifetime());
835+
break;
836+
case Qualifiers::OCL_None:
837+
// __block variables need to have the rhs evaluated first, plus this
838+
// should improve codegen just a little.
839+
rhs = Visit(e->getRHS());
840+
assert(!cir::MissingFeatures::sanitizers());
841+
// TODO(cir): This needs to be emitCheckedLValue() once we support
842+
// sanitizers
843+
lhs = cgf.emitLValue(e->getLHS());
844+
845+
// Store the value into the LHS. Bit-fields are handled specially because
846+
// the result is altered by the store, i.e., [C99 6.5.16p1]
847+
// 'An assignment expression has the value of the left operand after the
848+
// assignment...'.
849+
if (lhs.isBitField()) {
850+
rhs = cgf.emitStoreThroughBitfieldLValue(RValue::get(rhs), lhs);
851+
} else {
852+
cgf.emitNullabilityCheck(lhs, rhs, e->getExprLoc());
853+
CIRGenFunction::SourceLocRAIIObject loc{
854+
cgf, cgf.getLoc(e->getSourceRange())};
855+
cgf.emitStoreThroughLValue(RValue::get(rhs), lhs);
856+
}
857+
}
858+
859+
// If the result is clearly ignored, return now.
860+
if (ignore)
861+
return nullptr;
862+
863+
// The result of an assignment in C is the assigned r-value.
864+
if (!cgf.getLangOpts().CPlusPlus)
865+
return rhs;
866+
867+
// If the lvalue is non-volatile, return the computed value of the
868+
// assignment.
869+
if (!lhs.isVolatile())
870+
return rhs;
871+
872+
// Otherwise, reload the value.
873+
return emitLoadOfLValue(lhs, e->getExprLoc());
874+
}
875+
876+
mlir::Value VisitBinComma(const BinaryOperator *e) {
877+
cgf.emitIgnoredExpr(e->getLHS());
878+
// NOTE: We don't need to EnsureInsertPoint() like LLVM codegen.
879+
return Visit(e->getRHS());
880+
}
821881
};
822882

823883
LValue ScalarExprEmitter::emitCompoundAssignLValue(

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,8 @@ class CIRGenFunction : public CIRGenTypeCache {
579579
/// is 'Ty'.
580580
void emitStoreThroughLValue(RValue src, LValue dst, bool isInit = false);
581581

582+
mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult);
583+
582584
/// Given a value and its clang type, returns the value casted to its memory
583585
/// representation.
584586
/// Note: CIR defers most of the special casting to the final lowering passes
@@ -593,6 +595,11 @@ class CIRGenFunction : public CIRGenTypeCache {
593595

594596
mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s);
595597

598+
/// Given an assignment `*lhs = rhs`, emit a test that checks if \p rhs is
599+
/// nonnull, if 1\p LHS is marked _Nonnull.
600+
void emitNullabilityCheck(LValue lhs, mlir::Value rhs,
601+
clang::SourceLocation loc);
602+
596603
/// ----------------------
597604
/// CIR build helpers
598605
/// -----------------

clang/test/CIR/CodeGen/binassign.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
void binary_assign(void) {
9+
bool b;
10+
char c;
11+
float f;
12+
int i;
13+
14+
b = true;
15+
c = 65;
16+
f = 3.14f;
17+
i = 42;
18+
}
19+
20+
// CIR-LABEL: cir.func @binary_assign() {
21+
// CIR: %[[B:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"]
22+
// CIR: %[[C:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"]
23+
// CIR: %[[F:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"]
24+
// CIR: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"]
25+
// CIR: %[[TRUE:.*]] = cir.const #true
26+
// CIR: cir.store %[[TRUE]], %[[B]] : !cir.bool, !cir.ptr<!cir.bool>
27+
// CIR: %[[CHAR_INI_INIT:.*]] = cir.const #cir.int<65> : !s32i
28+
// CIR: %[[CHAR_VAL:.*]] = cir.cast(integral, %[[CHAR_INI_INIT]] : !s32i), !s8i
29+
// CIR: cir.store %[[CHAR_VAL]], %[[C]] : !s8i, !cir.ptr<!s8i>
30+
// CIR: %[[FLOAT_VAL:.*]] = cir.const #cir.fp<3.140000e+00> : !cir.float
31+
// CIR: cir.store %[[FLOAT_VAL]], %[[F]] : !cir.float, !cir.ptr<!cir.float>
32+
// CIR: %[[INT_VAL:.*]] = cir.const #cir.int<42> : !s32i
33+
// CIR: cir.store %[[INT_VAL]], %[[I]] : !s32i, !cir.ptr<!s32i>
34+
// CIR: cir.return
35+
36+
// LLVM-LABEL: define {{.*}}void @binary_assign() {
37+
// LLVM: %[[B_PTR:.*]] = alloca i8
38+
// LLVM: %[[C_PTR:.*]] = alloca i8
39+
// LLVM: %[[F_PTR:.*]] = alloca float
40+
// LLVM: %[[I_PTR:.*]] = alloca i32
41+
// LLVM: store i8 1, ptr %[[B_PTR]]
42+
// LLVM: store i8 65, ptr %[[C_PTR]]
43+
// LLVM: store float 0x40091EB860000000, ptr %[[F_PTR]]
44+
// LLVM: store i32 42, ptr %[[I_PTR]]
45+
// LLVM: ret void
46+
47+
// OGCG-LABEL: define {{.*}}void @binary_assign()
48+
// OGCG: %[[B_PTR:.*]] = alloca i8
49+
// OGCG: %[[C_PTR:.*]] = alloca i8
50+
// OGCG: %[[F_PTR:.*]] = alloca float
51+
// OGCG: %[[I_PTR:.*]] = alloca i32
52+
// OGCG: store i8 1, ptr %[[B_PTR]]
53+
// OGCG: store i8 65, ptr %[[C_PTR]]
54+
// OGCG: store float 0x40091EB860000000, ptr %[[F_PTR]]
55+
// OGCG: store i32 42, ptr %[[I_PTR]]
56+
// OGCG: ret void

clang/test/CIR/CodeGen/comma.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
void comma(void) {
9+
bool b;
10+
char c;
11+
float f;
12+
int i;
13+
14+
b = true, c = 65, f = 3.14f, i = 42;
15+
16+
i = 100, 200;
17+
}
18+
19+
// CIR-LABEL: cir.func @comma() {
20+
// CIR: %[[B:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"]
21+
// CIR: %[[C:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"]
22+
// CIR: %[[F:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"]
23+
// CIR: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"]
24+
// CIR: %[[TRUE:.*]] = cir.const #true
25+
// CIR: cir.store %[[TRUE]], %[[B]] : !cir.bool, !cir.ptr<!cir.bool>
26+
// CIR: %[[CHAR_INI_INIT:.*]] = cir.const #cir.int<65> : !s32i
27+
// CIR: %[[CHAR_VAL:.*]] = cir.cast(integral, %[[CHAR_INI_INIT]] : !s32i), !s8i
28+
// CIR: cir.store %[[CHAR_VAL]], %[[C]] : !s8i, !cir.ptr<!s8i>
29+
// CIR: %[[FLOAT_VAL:.*]] = cir.const #cir.fp<3.140000e+00> : !cir.float
30+
// CIR: cir.store %[[FLOAT_VAL]], %[[F]] : !cir.float, !cir.ptr<!cir.float>
31+
// CIR: %[[INT_VAL:.*]] = cir.const #cir.int<42> : !s32i
32+
// CIR: cir.store %[[INT_VAL]], %[[I]] : !s32i, !cir.ptr<!s32i>
33+
// CIR: %[[HUNDRED:.*]] = cir.const #cir.int<100> : !s32i
34+
// CIR: cir.store %[[HUNDRED]], %[[I]] : !s32i, !cir.ptr<!s32i>
35+
// CIR: cir.return
36+
37+
// LLVM-LABEL: define {{.*}}void @comma() {
38+
// LLVM: %[[B_PTR:.*]] = alloca i8
39+
// LLVM: %[[C_PTR:.*]] = alloca i8
40+
// LLVM: %[[F_PTR:.*]] = alloca float
41+
// LLVM: %[[I_PTR:.*]] = alloca i32
42+
// LLVM: store i8 1, ptr %[[B_PTR]]
43+
// LLVM: store i8 65, ptr %[[C_PTR]]
44+
// LLVM: store float 0x40091EB860000000, ptr %[[F_PTR]]
45+
// LLVM: store i32 42, ptr %[[I_PTR]]
46+
// LLVM: store i32 100, ptr %[[I_PTR]]
47+
// LLVM: ret void
48+
49+
// OGCG-LABEL: define {{.*}}void @comma()
50+
// OGCG: %[[B_PTR:.*]] = alloca i8
51+
// OGCG: %[[C_PTR:.*]] = alloca i8
52+
// OGCG: %[[F_PTR:.*]] = alloca float
53+
// OGCG: %[[I_PTR:.*]] = alloca i32
54+
// OGCG: store i8 1, ptr %[[B_PTR]]
55+
// OGCG: store i8 65, ptr %[[C_PTR]]
56+
// OGCG: store float 0x40091EB860000000, ptr %[[F_PTR]]
57+
// OGCG: store i32 42, ptr %[[I_PTR]]
58+
// OGCG: store i32 100, ptr %[[I_PTR]]
59+
// OGCG: ret void

clang/test/CIR/IR/binassign.cir

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: cir-opt %s | cir-opt | FileCheck %s
2+
3+
!s32i = !cir.int<s, 32>
4+
!s8i = !cir.int<s, 8>
5+
#true = #cir.bool<true> : !cir.bool
6+
module {
7+
cir.func @binary_assign() {
8+
%0 = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"] {alignment = 1 : i64}
9+
%1 = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"] {alignment = 1 : i64}
10+
%2 = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"] {alignment = 4 : i64}
11+
%3 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] {alignment = 4 : i64}
12+
%4 = cir.const #true
13+
cir.store %4, %0 : !cir.bool, !cir.ptr<!cir.bool>
14+
%5 = cir.const #cir.int<65> : !s32i
15+
%6 = cir.cast(integral, %5 : !s32i), !s8i
16+
cir.store %6, %1 : !s8i, !cir.ptr<!s8i>
17+
%7 = cir.const #cir.fp<3.140000e+00> : !cir.float
18+
cir.store %7, %2 : !cir.float, !cir.ptr<!cir.float>
19+
%8 = cir.const #cir.int<42> : !s32i
20+
cir.store %8, %3 : !s32i, !cir.ptr<!s32i>
21+
cir.return
22+
}
23+
}
24+
25+
// CHECK: !s32i = !cir.int<s, 32>
26+
// CHECK: !s8i = !cir.int<s, 8>
27+
// CHECK: #true = #cir.bool<true> : !cir.bool
28+
// CHECK: module {
29+
// CHECK: cir.func @binary_assign() {
30+
// CHECK: %0 = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b"] {alignment = 1 : i64}
31+
// CHECK: %1 = cir.alloca !s8i, !cir.ptr<!s8i>, ["c"] {alignment = 1 : i64}
32+
// CHECK: %2 = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f"] {alignment = 4 : i64}
33+
// CHECK: %3 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] {alignment = 4 : i64}
34+
// CHECK: %4 = cir.const #true
35+
// CHECK: cir.store %4, %0 : !cir.bool, !cir.ptr<!cir.bool>
36+
// CHECK: %5 = cir.const #cir.int<65> : !s32i
37+
// CHECK: %6 = cir.cast(integral, %5 : !s32i), !s8i
38+
// CHECK: cir.store %6, %1 : !s8i, !cir.ptr<!s8i>
39+
// CHECK: %7 = cir.const #cir.fp<3.140000e+00> : !cir.float
40+
// CHECK: cir.store %7, %2 : !cir.float, !cir.ptr<!cir.float>
41+
// CHECK: %8 = cir.const #cir.int<42> : !s32i
42+
// CHECK: cir.store %8, %3 : !s32i, !cir.ptr<!s32i>
43+
// CHECK: cir.return
44+
// CHECK: }
45+
// CHECK: }

0 commit comments

Comments
 (0)