-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Upstream overflow builtins #166643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[CIR] Upstream overflow builtins #166643
Changes from all commits
7abb216
d032089
7d73c08
848fbe7
d42d928
e2b3629
f77a61d
781e7bb
3b01b5a
05600b8
5c00c3d
afa0bda
28d8de4
1767a97
8e7c9a4
03f4133
7efb03f
1c61733
b18aedf
ebcb509
27a3db7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -58,6 +58,45 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e, | |||||
| return RValue::get(result); | ||||||
| } | ||||||
|
|
||||||
| namespace { | ||||||
| struct WidthAndSignedness { | ||||||
| unsigned width; | ||||||
| bool isSigned; | ||||||
| }; | ||||||
| } // namespace | ||||||
|
|
||||||
| static WidthAndSignedness | ||||||
| getIntegerWidthAndSignedness(const clang::ASTContext &astContext, | ||||||
| const clang::QualType type) { | ||||||
| assert(type->isIntegerType() && "Given type is not an integer."); | ||||||
| unsigned width = type->isBooleanType() ? 1 | ||||||
| : type->isBitIntType() ? astContext.getIntWidth(type) | ||||||
| : astContext.getTypeInfo(type).Width; | ||||||
| bool isSigned = type->isSignedIntegerType(); | ||||||
| return {width, isSigned}; | ||||||
| } | ||||||
|
|
||||||
| // Given one or more integer types, this function produces an integer type that | ||||||
| // encompasses them: any value in one of the given types could be expressed in | ||||||
| // the encompassing type. | ||||||
| static struct WidthAndSignedness | ||||||
| EncompassingIntegerType(ArrayRef<struct WidthAndSignedness> types) { | ||||||
| assert(types.size() > 0 && "Empty list of types."); | ||||||
|
|
||||||
| // If any of the given types is signed, we must return a signed type. | ||||||
| bool isSigned = llvm::any_of(types, [](const auto &t) { return t.isSigned; }); | ||||||
|
|
||||||
| // The encompassing type must have a width greater than or equal to the width | ||||||
| // of the specified types. Additionally, if the encompassing type is signed, | ||||||
| // its width must be strictly greater than the width of any unsigned types | ||||||
| // given. | ||||||
| unsigned width = 0; | ||||||
| for (const auto &type : types) | ||||||
| width = std::max(width, type.width + (isSigned && !type.isSigned)); | ||||||
|
|
||||||
| return {width, isSigned}; | ||||||
| } | ||||||
|
|
||||||
| RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) { | ||||||
| mlir::Value input = emitScalarExpr(e->getArg(0)); | ||||||
| mlir::Value amount = emitScalarExpr(e->getArg(1)); | ||||||
|
|
@@ -491,6 +530,154 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, | |||||
| cir::PrefetchOp::create(builder, loc, address, locality, isWrite); | ||||||
| return RValue::get(nullptr); | ||||||
| } | ||||||
| case Builtin::BI__builtin_add_overflow: | ||||||
| case Builtin::BI__builtin_sub_overflow: | ||||||
| case Builtin::BI__builtin_mul_overflow: { | ||||||
| const clang::Expr *LeftArg = e->getArg(0); | ||||||
| const clang::Expr *RightArg = e->getArg(1); | ||||||
| const clang::Expr *ResultArg = e->getArg(2); | ||||||
|
|
||||||
| clang::QualType ResultQTy = | ||||||
| ResultArg->getType()->castAs<clang::PointerType>()->getPointeeType(); | ||||||
|
|
||||||
| WidthAndSignedness LeftInfo = | ||||||
| getIntegerWidthAndSignedness(cgm.getASTContext(), LeftArg->getType()); | ||||||
| WidthAndSignedness RightInfo = | ||||||
| getIntegerWidthAndSignedness(cgm.getASTContext(), RightArg->getType()); | ||||||
| WidthAndSignedness ResultInfo = | ||||||
| getIntegerWidthAndSignedness(cgm.getASTContext(), ResultQTy); | ||||||
|
|
||||||
| // Note we compute the encompassing type with the consideration to the | ||||||
| // result type, so later in LLVM lowering we don't get redundant integral | ||||||
| // extension casts. | ||||||
| WidthAndSignedness EncompassingInfo = | ||||||
| EncompassingIntegerType({LeftInfo, RightInfo, ResultInfo}); | ||||||
|
|
||||||
| auto EncompassingCIRTy = cir::IntType::get( | ||||||
| &getMLIRContext(), EncompassingInfo.width, EncompassingInfo.isSigned); | ||||||
| auto ResultCIRTy = mlir::cast<cir::IntType>(cgm.convertType(ResultQTy)); | ||||||
|
|
||||||
| mlir::Value Left = emitScalarExpr(LeftArg); | ||||||
| mlir::Value Right = emitScalarExpr(RightArg); | ||||||
| Address ResultPtr = emitPointerWithAlignment(ResultArg); | ||||||
|
|
||||||
| // Extend each operand to the encompassing type, if necessary. | ||||||
| if (Left.getType() != EncompassingCIRTy) | ||||||
| Left = | ||||||
| builder.createCast(cir::CastKind::integral, Left, EncompassingCIRTy); | ||||||
| if (Right.getType() != EncompassingCIRTy) | ||||||
| Right = | ||||||
| builder.createCast(cir::CastKind::integral, Right, EncompassingCIRTy); | ||||||
|
|
||||||
| // Perform the operation on the extended values. | ||||||
| cir::BinOpOverflowKind OpKind; | ||||||
| switch (builtinID) { | ||||||
| default: | ||||||
| llvm_unreachable("Unknown overflow builtin id."); | ||||||
| case Builtin::BI__builtin_add_overflow: | ||||||
| OpKind = cir::BinOpOverflowKind::Add; | ||||||
| break; | ||||||
| case Builtin::BI__builtin_sub_overflow: | ||||||
| OpKind = cir::BinOpOverflowKind::Sub; | ||||||
| break; | ||||||
| case Builtin::BI__builtin_mul_overflow: | ||||||
| OpKind = cir::BinOpOverflowKind::Mul; | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| auto Loc = getLoc(e->getSourceRange()); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We don't use auto unless the type is obvious from the code (such as the result of https://llvm.org/docs/CodingStandards.html#use-auto-type-deduction-to-make-code-more-readable
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This still needs to be addressed. |
||||||
| auto arithOp = | ||||||
| cir::BinOpOverflowOp::create(builder, Loc, ResultCIRTy, OpKind, Left, Right); | ||||||
|
|
||||||
| // Here is a slight difference from the original clang CodeGen: | ||||||
| // - In the original clang CodeGen, the checked arithmetic result is | ||||||
| // first computed as a value of the encompassing type, and then it is | ||||||
| // truncated to the actual result type with a second overflow checking. | ||||||
| // - In CIRGen, the checked arithmetic operation directly produce the | ||||||
| // checked arithmetic result in its expected type. | ||||||
| // | ||||||
| // So we don't need a truncation and a second overflow checking here. | ||||||
|
|
||||||
| // Finally, store the result using the pointer. | ||||||
| bool isVolatile = | ||||||
| ResultArg->getType()->getPointeeType().isVolatileQualified(); | ||||||
| builder.createStore(Loc, emitToMemory(ArithOp.getResult(), ResultQTy), | ||||||
| ResultPtr, isVolatile); | ||||||
|
|
||||||
| return RValue::get(ArithOp.getOverflow()); | ||||||
| } | ||||||
|
|
||||||
| case Builtin::BI__builtin_uadd_overflow: | ||||||
| case Builtin::BI__builtin_uaddl_overflow: | ||||||
| case Builtin::BI__builtin_uaddll_overflow: | ||||||
| case Builtin::BI__builtin_usub_overflow: | ||||||
| case Builtin::BI__builtin_usubl_overflow: | ||||||
| case Builtin::BI__builtin_usubll_overflow: | ||||||
| case Builtin::BI__builtin_umul_overflow: | ||||||
| case Builtin::BI__builtin_umull_overflow: | ||||||
| case Builtin::BI__builtin_umulll_overflow: | ||||||
| case Builtin::BI__builtin_sadd_overflow: | ||||||
| case Builtin::BI__builtin_saddl_overflow: | ||||||
| case Builtin::BI__builtin_saddll_overflow: | ||||||
| case Builtin::BI__builtin_ssub_overflow: | ||||||
| case Builtin::BI__builtin_ssubl_overflow: | ||||||
| case Builtin::BI__builtin_ssubll_overflow: | ||||||
| case Builtin::BI__builtin_smul_overflow: | ||||||
| case Builtin::BI__builtin_smull_overflow: | ||||||
| case Builtin::BI__builtin_smulll_overflow: { | ||||||
| // Scalarize our inputs. | ||||||
| mlir::Value X = emitScalarExpr(e->getArg(0)); | ||||||
| mlir::Value Y = emitScalarExpr(e->getArg(1)); | ||||||
|
|
||||||
| const clang::Expr *ResultArg = e->getArg(2); | ||||||
| Address ResultPtr = emitPointerWithAlignment(ResultArg); | ||||||
|
|
||||||
| // Decide which of the arithmetic operation we are lowering to: | ||||||
| cir::BinOpOverflowKind ArithKind; | ||||||
| switch (builtinID) { | ||||||
| default: | ||||||
| llvm_unreachable("Unknown overflow builtin id."); | ||||||
| case Builtin::BI__builtin_uadd_overflow: | ||||||
| case Builtin::BI__builtin_uaddl_overflow: | ||||||
| case Builtin::BI__builtin_uaddll_overflow: | ||||||
| case Builtin::BI__builtin_sadd_overflow: | ||||||
| case Builtin::BI__builtin_saddl_overflow: | ||||||
| case Builtin::BI__builtin_saddll_overflow: | ||||||
| ArithKind = cir::BinOpOverflowKind::Add; | ||||||
| break; | ||||||
| case Builtin::BI__builtin_usub_overflow: | ||||||
| case Builtin::BI__builtin_usubl_overflow: | ||||||
| case Builtin::BI__builtin_usubll_overflow: | ||||||
| case Builtin::BI__builtin_ssub_overflow: | ||||||
| case Builtin::BI__builtin_ssubl_overflow: | ||||||
| case Builtin::BI__builtin_ssubll_overflow: | ||||||
| ArithKind = cir::BinOpOverflowKind::Sub; | ||||||
| break; | ||||||
| case Builtin::BI__builtin_umul_overflow: | ||||||
| case Builtin::BI__builtin_umull_overflow: | ||||||
| case Builtin::BI__builtin_umulll_overflow: | ||||||
| case Builtin::BI__builtin_smul_overflow: | ||||||
| case Builtin::BI__builtin_smull_overflow: | ||||||
| case Builtin::BI__builtin_smulll_overflow: | ||||||
| ArithKind = cir::BinOpOverflowKind::Mul; | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| clang::QualType ResultQTy = | ||||||
| ResultArg->getType()->castAs<clang::PointerType>()->getPointeeType(); | ||||||
| auto ResultCIRTy = mlir::cast<cir::IntType>(cgm.convertType(ResultQTy)); | ||||||
|
|
||||||
| auto Loc = getLoc(e->getSourceRange()); | ||||||
adams381 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| cir::BinOpOverflowOp ArithOp = | ||||||
| cir::BinOpOverflowOp::create(builder, Loc, ResultCIRTy, ArithKind, X, Y); | ||||||
|
|
||||||
| bool isVolatile = | ||||||
| ResultArg->getType()->getPointeeType().isVolatileQualified(); | ||||||
| builder.createStore(Loc, emitToMemory(ArithOp.getResult(), ResultQTy), | ||||||
| ResultPtr, isVolatile); | ||||||
|
|
||||||
| return RValue::get(ArithOp.getOverflow()); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // If this is an alias for a lib function (e.g. __builtin_sin), emit | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.