Skip to content

Commit d51bb76

Browse files
committed
[CIR] Upstream initial support for unary op
This adds support for the cir.unary operation.
1 parent cbbcc3d commit d51bb76

File tree

11 files changed

+903
-1
lines changed

11 files changed

+903
-1
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,55 @@ def BrOp : CIR_Op<"br",
468468
}];
469469
}
470470

471+
//===----------------------------------------------------------------------===//
472+
// UnaryOp
473+
//===----------------------------------------------------------------------===//
474+
475+
def UnaryOpKind_Inc : I32EnumAttrCase<"Inc", 1, "inc">;
476+
def UnaryOpKind_Dec : I32EnumAttrCase<"Dec", 2, "dec">;
477+
def UnaryOpKind_Plus : I32EnumAttrCase<"Plus", 3, "plus">;
478+
def UnaryOpKind_Minus : I32EnumAttrCase<"Minus", 4, "minus">;
479+
def UnaryOpKind_Not : I32EnumAttrCase<"Not", 5, "not">;
480+
481+
def UnaryOpKind : I32EnumAttr<
482+
"UnaryOpKind",
483+
"unary operation kind",
484+
[UnaryOpKind_Inc,
485+
UnaryOpKind_Dec,
486+
UnaryOpKind_Plus,
487+
UnaryOpKind_Minus,
488+
UnaryOpKind_Not,
489+
]> {
490+
let cppNamespace = "::cir";
491+
}
492+
493+
// FIXME: Pure won't work when we add overloading.
494+
def UnaryOp : CIR_Op<"unary", [Pure, SameOperandsAndResultType]> {
495+
let summary = "Unary operations";
496+
let description = [{
497+
`cir.unary` performs the unary operation according to
498+
the specified opcode kind: [inc, dec, plus, minus, not].
499+
500+
It requires one input operand and has one result, both types
501+
should be the same.
502+
503+
```mlir
504+
%7 = cir.unary(inc, %1) : i32 -> i32
505+
%8 = cir.unary(dec, %2) : i32 -> i32
506+
```
507+
}];
508+
509+
let results = (outs CIR_AnyType:$result);
510+
let arguments = (ins Arg<UnaryOpKind, "unary op kind">:$kind, Arg<CIR_AnyType>:$input);
511+
512+
let assemblyFormat = [{
513+
`(` $kind `,` $input `)` `:` type($input) `,` type($result) attr-dict
514+
}];
515+
516+
let hasVerifier = 1;
517+
let hasFolder = 1;
518+
}
519+
471520
//===----------------------------------------------------------------------===//
472521
// GlobalOp
473522
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ struct MissingFeatures {
7272
static bool opFuncLinkage() { return false; }
7373
static bool opFuncVisibility() { return false; }
7474

75+
// Unary operator handling
76+
static bool opUnarySignedOverflow() { return false; }
77+
static bool opUnaryPromotionType() { return false; }
78+
7579
// Misc
7680
static bool scalarConversionOpts() { return false; }
7781
static bool tryEmitAsConstant() { return false; }
@@ -86,6 +90,11 @@ struct MissingFeatures {
8690
static bool aggValueSlot() { return false; }
8791

8892
static bool unsizedTypes() { return false; }
93+
static bool sanitizers() { return false; }
94+
static bool CGFPOptionsRAII() { return false; }
95+
96+
// Missing types
97+
static bool vectorType() { return false; }
8998
};
9099

91100
} // namespace cir

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,54 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
165165
return LValue();
166166
}
167167

168+
LValue CIRGenFunction::emitUnaryOpLValue(const UnaryOperator *e) {
169+
UnaryOperatorKind op = e->getOpcode();
170+
171+
// __extension__ doesn't affect lvalue-ness.
172+
if (op == UO_Extension)
173+
return emitLValue(e->getSubExpr());
174+
175+
switch (op) {
176+
case UO_Deref: {
177+
cgm.errorNYI(e->getSourceRange(), "UnaryOp dereference");
178+
return LValue();
179+
}
180+
case UO_Real:
181+
case UO_Imag: {
182+
cgm.errorNYI(e->getSourceRange(), "UnaryOp real/imag");
183+
return LValue();
184+
}
185+
case UO_PreInc:
186+
case UO_PreDec: {
187+
bool isInc = e->isIncrementOp();
188+
LValue lv = emitLValue(e->getSubExpr());
189+
190+
assert(e->isPrefix() && "Prefix operator in unexpected state!");
191+
192+
if (e->getType()->isAnyComplexType()) {
193+
cgm.errorNYI(e->getSourceRange(), "UnaryOp complex inc/dec");
194+
return LValue();
195+
} else {
196+
emitScalarPrePostIncDec(e, lv, isInc, /*isPre=*/true);
197+
}
198+
199+
return lv;
200+
}
201+
case UO_Extension:
202+
llvm_unreachable("UnaryOperator extension should be handled above!");
203+
case UO_Plus:
204+
case UO_Minus:
205+
case UO_Not:
206+
case UO_LNot:
207+
case UO_AddrOf:
208+
case UO_PostInc:
209+
case UO_PostDec:
210+
case UO_Coawait:
211+
llvm_unreachable("UnaryOperator of non-lvalue kind!");
212+
}
213+
llvm_unreachable("Unknown unary operator kind!");
214+
}
215+
168216
/// Emit code to compute the specified expression which
169217
/// can have any type. The result is returned as an RValue struct.
170218
RValue CIRGenFunction::emitAnyExpr(const Expr *e) {

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,222 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
9292

9393
mlir::Value VisitCastExpr(CastExpr *E);
9494

95+
// Unary Operators.
96+
mlir::Value VisitUnaryPostDec(const UnaryOperator *e) {
97+
LValue lv = cgf.emitLValue(e->getSubExpr());
98+
return emitScalarPrePostIncDec(e, lv, false, false);
99+
}
100+
mlir::Value VisitUnaryPostInc(const UnaryOperator *e) {
101+
LValue lv = cgf.emitLValue(e->getSubExpr());
102+
return emitScalarPrePostIncDec(e, lv, true, false);
103+
}
104+
mlir::Value VisitUnaryPreDec(const UnaryOperator *e) {
105+
LValue lv = cgf.emitLValue(e->getSubExpr());
106+
return emitScalarPrePostIncDec(e, lv, false, true);
107+
}
108+
mlir::Value VisitUnaryPreInc(const UnaryOperator *e) {
109+
LValue lv = cgf.emitLValue(e->getSubExpr());
110+
return emitScalarPrePostIncDec(e, lv, true, true);
111+
}
112+
mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv,
113+
bool isInc, bool isPre) {
114+
if (cgf.getLangOpts().OpenMP)
115+
cgf.cgm.errorNYI(e->getSourceRange(), "inc/dec OpenMP");
116+
117+
QualType type = e->getSubExpr()->getType();
118+
119+
mlir::Value value;
120+
mlir::Value input;
121+
122+
if (type->getAs<AtomicType>()) {
123+
cgf.cgm.errorNYI(e->getSourceRange(), "Atomic inc/dec");
124+
// TODO(cir): This is not correct, but it will produce reasonable code
125+
// until atomic operations are implemented.
126+
value = cgf.emitLoadOfLValue(lv, e->getExprLoc()).getScalarVal();
127+
input = value;
128+
} else {
129+
value = cgf.emitLoadOfLValue(lv, e->getExprLoc()).getScalarVal();
130+
input = value;
131+
}
132+
133+
// NOTE: When possible, more frequent cases are handled first.
134+
135+
// Special case of integer increment that we have to check first: bool++.
136+
// Due to promotion rules, we get:
137+
// bool++ -> bool = bool + 1
138+
// -> bool = (int)bool + 1
139+
// -> bool = ((int)bool + 1 != 0)
140+
// An interesting aspect of this is that increment is always true.
141+
// Decrement does not have this property.
142+
if (isInc && type->isBooleanType()) {
143+
value = builder.create<cir::ConstantOp>(cgf.getLoc(e->getExprLoc()),
144+
cgf.convertType(type),
145+
builder.getCIRBoolAttr(true));
146+
} else if (type->isIntegerType()) {
147+
QualType promotedType;
148+
bool canPerformLossyDemotionCheck = false;
149+
if (cgf.getContext().isPromotableIntegerType(type)) {
150+
promotedType = cgf.getContext().getPromotedIntegerType(type);
151+
assert(promotedType != type && "Shouldn't promote to the same type.");
152+
canPerformLossyDemotionCheck = true;
153+
canPerformLossyDemotionCheck &=
154+
cgf.getContext().getCanonicalType(type) !=
155+
cgf.getContext().getCanonicalType(promotedType);
156+
canPerformLossyDemotionCheck &=
157+
type->isIntegerType() && promotedType->isIntegerType();
158+
159+
// TODO(cir): Currently, we store bitwidths in CIR types only for
160+
// integers. This might also be required for other types.
161+
auto srcCirTy = mlir::dyn_cast<cir::IntType>(cgf.convertType(type));
162+
auto promotedCirTy =
163+
mlir::dyn_cast<cir::IntType>(cgf.convertType(type));
164+
assert(srcCirTy && promotedCirTy && "Expected integer type");
165+
166+
assert(
167+
(!canPerformLossyDemotionCheck ||
168+
type->isSignedIntegerOrEnumerationType() ||
169+
promotedType->isSignedIntegerOrEnumerationType() ||
170+
srcCirTy.getWidth() == promotedCirTy.getWidth()) &&
171+
"The following check expects that if we do promotion to different "
172+
"underlying canonical type, at least one of the types (either "
173+
"base or promoted) will be signed, or the bitwidths will match.");
174+
}
175+
176+
assert(!cir::MissingFeatures::sanitizers());
177+
if (e->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
178+
value = emitIncDecConsiderOverflowBehavior(e, value, isInc);
179+
} else {
180+
cir::UnaryOpKind kind =
181+
e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
182+
// NOTE(CIR): clang calls CreateAdd but folds this to a unary op
183+
value = emitUnaryOp(e, kind, input);
184+
}
185+
} else if (const PointerType *ptr = type->getAs<PointerType>()) {
186+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec pointer");
187+
return {};
188+
} else if (type->isVectorType()) {
189+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec vector");
190+
return {};
191+
} else if (type->isRealFloatingType()) {
192+
assert(!cir::MissingFeatures::CGFPOptionsRAII());
193+
194+
if (type->isHalfType() &&
195+
!cgf.getContext().getLangOpts().NativeHalfType) {
196+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec half");
197+
return {};
198+
}
199+
200+
if (mlir::isa<cir::SingleType, cir::DoubleType>(value.getType())) {
201+
// Create the inc/dec operation.
202+
// NOTE(CIR): clang calls CreateAdd but folds this to a unary op
203+
cir::UnaryOpKind kind =
204+
(isInc ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec);
205+
value = emitUnaryOp(e, kind, value);
206+
} else {
207+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec other fp type");
208+
return {};
209+
}
210+
} else if (type->isFixedPointType()) {
211+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec other fixed point");
212+
return {};
213+
} else {
214+
assert(type->castAs<ObjCObjectPointerType>());
215+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec ObjectiveC pointer");
216+
return {};
217+
}
218+
219+
CIRGenFunction::SourceLocRAIIObject sourceloc{
220+
cgf, cgf.getLoc(e->getSourceRange())};
221+
222+
// Store the updated result through the lvalue
223+
if (lv.isBitField()) {
224+
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec bitfield");
225+
return {};
226+
} else {
227+
cgf.emitStoreThroughLValue(RValue::get(value), lv);
228+
}
229+
230+
// If this is a postinc, return the value read from memory, otherwise use
231+
// the updated value.
232+
return isPre ? value : input;
233+
}
234+
235+
mlir::Value emitIncDecConsiderOverflowBehavior(const UnaryOperator *e,
236+
mlir::Value inVal,
237+
bool isInc) {
238+
assert(!cir::MissingFeatures::opUnarySignedOverflow());
239+
cir::UnaryOpKind kind =
240+
e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
241+
switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
242+
case LangOptions::SOB_Defined:
243+
return emitUnaryOp(e, kind, inVal);
244+
case LangOptions::SOB_Undefined:
245+
assert(!cir::MissingFeatures::sanitizers());
246+
return emitUnaryOp(e, kind, inVal);
247+
break;
248+
case LangOptions::SOB_Trapping:
249+
if (!e->canOverflow())
250+
return emitUnaryOp(e, kind, inVal);
251+
cgf.cgm.errorNYI(e->getSourceRange(), "inc/def overflow SOB_Trapping");
252+
return {};
253+
}
254+
llvm_unreachable("Unexpected signed overflow behavior kind");
255+
}
256+
257+
mlir::Value VisitUnaryPlus(const UnaryOperator *e,
258+
QualType promotionType = QualType()) {
259+
if (!promotionType.isNull())
260+
cgf.cgm.errorNYI(e->getSourceRange(), "VisitUnaryPlus: promotionType");
261+
assert(!cir::MissingFeatures::opUnaryPromotionType());
262+
mlir::Value result = VisitPlus(e);
263+
return result;
264+
}
265+
266+
mlir::Value VisitPlus(const UnaryOperator *e) {
267+
// This differs from gcc, though, most likely due to a bug in gcc.
268+
ignoreResultAssign = false;
269+
270+
assert(!cir::MissingFeatures::opUnaryPromotionType());
271+
mlir::Value operand = Visit(e->getSubExpr());
272+
273+
return emitUnaryOp(e, cir::UnaryOpKind::Plus, operand);
274+
}
275+
276+
mlir::Value VisitUnaryMinus(const UnaryOperator *e,
277+
QualType promotionType = QualType()) {
278+
if (!promotionType.isNull())
279+
cgf.cgm.errorNYI(e->getSourceRange(), "VisitUnaryMinus: promotionType");
280+
assert(!cir::MissingFeatures::opUnaryPromotionType());
281+
mlir::Value result = VisitMinus(e);
282+
return result;
283+
}
284+
285+
mlir::Value VisitMinus(const UnaryOperator *e) {
286+
ignoreResultAssign = false;
287+
288+
assert(!cir::MissingFeatures::opUnaryPromotionType());
289+
mlir::Value operand = Visit(e->getSubExpr());
290+
291+
assert(!cir::MissingFeatures::opUnarySignedOverflow());
292+
293+
// NOTE: LLVM codegen will lower this directly to either a FNeg
294+
// or a Sub instruction. In CIR this will be handled later in LowerToLLVM.
295+
return emitUnaryOp(e, cir::UnaryOpKind::Minus, operand);
296+
}
297+
298+
mlir::Value emitUnaryOp(const UnaryOperator *e, cir::UnaryOpKind kind,
299+
mlir::Value input) {
300+
return builder.create<cir::UnaryOp>(
301+
cgf.getLoc(e->getSourceRange().getBegin()), input.getType(), kind,
302+
input);
303+
}
304+
305+
mlir::Value VisitUnaryNot(const UnaryOperator *e) {
306+
ignoreResultAssign = false;
307+
mlir::Value op = Visit(e->getSubExpr());
308+
return emitUnaryOp(e, cir::UnaryOpKind::Not, op);
309+
}
310+
95311
/// Emit a conversion from the specified type to the specified destination
96312
/// type, both of which are CIR scalar types.
97313
/// TODO: do we need ScalarConversionOpts here? Should be done in another
@@ -148,3 +364,10 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
148364
}
149365
return {};
150366
}
367+
368+
mlir::Value CIRGenFunction::emitScalarPrePostIncDec(const UnaryOperator *E,
369+
LValue LV, bool isInc,
370+
bool isPre) {
371+
return ScalarExprEmitter(*this, builder)
372+
.emitScalarPrePostIncDec(E, LV, isInc, isPre);
373+
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
305305
std::string("l-value not implemented for '") +
306306
e->getStmtClassName() + "'");
307307
return LValue();
308+
case Expr::UnaryOperatorClass:
309+
return emitUnaryOpLValue(cast<UnaryOperator>(e));
308310
case Expr::DeclRefExprClass:
309311
return emitDeclRefLValue(cast<DeclRefExpr>(e));
310312
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ class CIRGenFunction : public CIRGenTypeCache {
206206
LValue lvalue, bool capturedByInit = false);
207207

208208
LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
209+
LValue emitUnaryOpLValue(const clang::UnaryOperator *e);
209210

210211
/// Determine whether the given initializer is trivial in the sense
211212
/// that it requires no code to be generated.
@@ -305,6 +306,9 @@ class CIRGenFunction : public CIRGenTypeCache {
305306
// TODO: Add symbol table support
306307
}
307308

309+
mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv,
310+
bool isInc, bool isPre);
311+
308312
/// Emit the computation of the specified expression of scalar type.
309313
mlir::Value emitScalarExpr(const clang::Expr *e);
310314
cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn,

0 commit comments

Comments
 (0)