Skip to content

Commit f73ddec

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

File tree

12 files changed

+904
-2
lines changed

12 files changed

+904
-2
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
@@ -94,6 +94,222 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
9494

9595
mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *e);
9696

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

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)