[clang] [CIR] Split BinOpOverflowOp into separate overflow-checked ops (PR #186653)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 15 02:48:32 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Henrich Lauko (xlauko)
<details>
<summary>Changes</summary>
Replace the monolithic cir.binop.overflow operation and its
BinOpOverflowKind enum with three individual operations:
cir.add.overflow, cir.sub.overflow, and cir.mul.overflow.
This follows the same pattern used when BinOp and UnaryOp were
previously split into per-operation ops (cir.add, cir.sub, etc.),
eliminating enum dispatch and enabling per-op traits like Commutative.
---
Patch is 44.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/186653.diff
6 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+62-47)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+44-36)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp (+6-6)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+36-48)
- (modified) clang/test/CIR/CodeGen/new.cpp (+7-7)
- (modified) clang/test/CIR/CodeGenBuiltins/builtins-overflow.cpp (+30-30)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e7cb11be08f01..2dfe8635b1ded 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2154,44 +2154,30 @@ def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
}
//===----------------------------------------------------------------------===//
-// BinOpOverflowOp
+// Checked Binary Arithmetic Operations (with overflow)
//===----------------------------------------------------------------------===//
-def CIR_BinOpOverflowKind : CIR_I32EnumAttr<
- "BinOpOverflowKind", "checked binary arithmetic operation kind", [
- I32EnumAttrCase<"Add", 0, "add">,
- I32EnumAttrCase<"Sub", 1, "sub">,
- I32EnumAttrCase<"Mul", 2, "mul">
-]>;
-
-def CIR_BinOpOverflowOp : CIR_Op<"binop.overflow", [Pure, SameTypeOperands]> {
- let summary = "Perform binary integral arithmetic with overflow checking";
- let description = [{
- `cir.binop.overflow` performs binary arithmetic operations with overflow
- checking on integral operands.
-
- The `kind` argument specifies the kind of arithmetic operation to perform.
- It can be either `add`, `sub`, or `mul`. The `lhs` and `rhs` arguments
- specify the input operands of the arithmetic operation. The types of `lhs`
- and `rhs` must be the same.
-
- `cir.binop.overflow` produces two SSA values. `result` is the result of the
- arithmetic operation truncated to its specified type. `overflow` is a
- boolean value indicating whether overflow happens during the operation.
-
- The exact semantic of this operation is as follows:
-
- - `lhs` and `rhs` are promoted to an imaginary integral type that has
- infinite precision.
- - The arithmetic operation is performed on the promoted operands.
- - The infinite-precision result is truncated to the type of `result`. The
- truncated result is assigned to `result`.
- - If the truncated result is equal to the un-truncated result, `overflow`
- is assigned to false. Otherwise, `overflow` is assigned to true.
- }];
+// Base class for binary arithmetic operations with overflow checking.
+//
+// Each op produces two SSA values: `result` is the arithmetic result truncated
+// to its specified type, and `overflow` is a boolean indicating whether
+// overflow occurred.
+//
+// The exact semantics are:
+// - `lhs` and `rhs` are promoted to an imaginary infinite-precision
+// integral type.
+// - The arithmetic operation is performed on the promoted operands.
+// - The infinite-precision result is truncated to the type of `result`.
+// - `overflow` is true if the truncated result differs from the
+// infinite-precision result.
+//
+// The type of `result` may differ from the operand type. This models C
+// builtins like `__builtin_*_overflow` where operands and the result can
+// have different widths or signedness.
+class CIR_BinOpOverflow<string mnemonic, list<Trait> traits = []>
+ : CIR_Op<mnemonic, !listconcat([Pure, SameTypeOperands], traits)> {
let arguments = (ins
- CIR_BinOpOverflowKind:$kind,
CIR_IntType:$lhs,
CIR_IntType:$rhs
);
@@ -2199,32 +2185,61 @@ def CIR_BinOpOverflowOp : CIR_Op<"binop.overflow", [Pure, SameTypeOperands]> {
let results = (outs CIR_IntType:$result, CIR_BoolType:$overflow);
let assemblyFormat = [{
- `(` $kind `,` $lhs `,` $rhs `)` `:` qualified(type($lhs)) `,`
- `(` qualified(type($result)) `,` qualified(type($overflow)) `)`
+ $lhs `,` $rhs `:` qualified(type($lhs)) `->` qualified(type($result))
attr-dict
}];
let builders = [
OpBuilder<(ins "cir::IntType":$resultTy,
- "cir::BinOpOverflowKind":$kind,
"mlir::Value":$lhs,
"mlir::Value":$rhs), [{
auto overflowTy = cir::BoolType::get($_builder.getContext());
- build($_builder, $_state, resultTy, overflowTy, kind, lhs, rhs);
+ build($_builder, $_state, resultTy, overflowTy, lhs, rhs);
}]>
];
- let extraLLVMLoweringPatternDecl = [{
- static std::string getLLVMIntrinName(cir::BinOpOverflowKind opKind,
- bool isSigned, unsigned width);
+}
- struct EncompassedTypeInfo {
- bool sign;
- unsigned width;
- };
+def CIR_AddOverflowOp : CIR_BinOpOverflow<"add.overflow", [Commutative]> {
+ let summary = "Integer addition with overflow checking";
+ let description = [{
+ `cir.add.overflow` performs addition with overflow checking on integral
+ operands. See `CIR_BinOpOverflow` for semantics.
+
+ Example:
- static EncompassedTypeInfo computeEncompassedTypeWidth(cir::IntType operandTy,
- cir::IntType resultTy);
+ ```mlir
+ %result, %overflow = cir.add.overflow %a, %b : !u32i -> !u32i
+ %result, %overflow = cir.add.overflow %a, %b : !cir.int<s, 33> -> !s32i
+ ```
+ }];
+}
+
+def CIR_SubOverflowOp : CIR_BinOpOverflow<"sub.overflow"> {
+ let summary = "Integer subtraction with overflow checking";
+ let description = [{
+ `cir.sub.overflow` performs subtraction with overflow checking on integral
+ operands. See `CIR_BinOpOverflow` for semantics.
+
+ Example:
+
+ ```mlir
+ %result, %overflow = cir.sub.overflow %a, %b : !u32i -> !u32i
+ ```
+ }];
+}
+
+def CIR_MulOverflowOp : CIR_BinOpOverflow<"mul.overflow", [Commutative]> {
+ let summary = "Integer multiplication with overflow checking";
+ let description = [{
+ `cir.mul.overflow` performs multiplication with overflow checking on
+ integral operands. See `CIR_BinOpOverflow` for semantics.
+
+ Example:
+
+ ```mlir
+ %result, %overflow = cir.mul.overflow %a, %b : !u32i -> !u32i
+ ```
}];
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index f155365ab5de2..3fd18d5204bad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -231,6 +231,16 @@ getIntegerWidthAndSignedness(const clang::ASTContext &astContext,
return {width, isSigned};
}
+/// Create a checked overflow arithmetic op and return its result and overflow
+/// flag.
+template <typename OpTy>
+static std::pair<mlir::Value, mlir::Value>
+emitOverflowOp(CIRGenBuilderTy &builder, mlir::Location loc,
+ cir::IntType resultTy, mlir::Value lhs, mlir::Value rhs) {
+ auto op = OpTy::create(builder, loc, resultTy, lhs, rhs);
+ return {op.getResult(), op.getOverflow()};
+}
+
// 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.
@@ -1900,38 +1910,36 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
&getMLIRContext(), encompassingInfo.width, encompassingInfo.isSigned);
auto resultCIRTy = mlir::cast<cir::IntType>(cgm.convertType(resultQTy));
- mlir::Value left = emitScalarExpr(leftArg);
- mlir::Value right = emitScalarExpr(rightArg);
+ mlir::Value x = emitScalarExpr(leftArg);
+ mlir::Value y = 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);
+ if (x.getType() != encompassingCIRTy)
+ x = builder.createCast(cir::CastKind::integral, x, encompassingCIRTy);
+ if (y.getType() != encompassingCIRTy)
+ y = builder.createCast(cir::CastKind::integral, y, encompassingCIRTy);
// Perform the operation on the extended values.
- cir::BinOpOverflowKind opKind;
+ mlir::Location loc = getLoc(e->getSourceRange());
+ mlir::Value result, overflow;
switch (builtinID) {
default:
llvm_unreachable("Unknown overflow builtin id.");
case Builtin::BI__builtin_add_overflow:
- opKind = cir::BinOpOverflowKind::Add;
+ std::tie(result, overflow) =
+ emitOverflowOp<cir::AddOverflowOp>(builder, loc, resultCIRTy, x, y);
break;
case Builtin::BI__builtin_sub_overflow:
- opKind = cir::BinOpOverflowKind::Sub;
+ std::tie(result, overflow) =
+ emitOverflowOp<cir::SubOverflowOp>(builder, loc, resultCIRTy, x, y);
break;
case Builtin::BI__builtin_mul_overflow:
- opKind = cir::BinOpOverflowKind::Mul;
+ std::tie(result, overflow) =
+ emitOverflowOp<cir::MulOverflowOp>(builder, loc, resultCIRTy, x, y);
break;
}
- mlir::Location loc = getLoc(e->getSourceRange());
- 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
@@ -1944,9 +1952,9 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// Finally, store the result using the pointer.
bool isVolatile =
resultArg->getType()->getPointeeType().isVolatileQualified();
- builder.createStore(loc, arithOp.getResult(), resultPtr, isVolatile);
+ builder.createStore(loc, result, resultPtr, isVolatile);
- return RValue::get(arithOp.getOverflow());
+ return RValue::get(overflow);
}
case Builtin::BI__builtin_uadd_overflow:
@@ -1968,14 +1976,19 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
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));
+ mlir::Value lhs = emitScalarExpr(e->getArg(0));
+ mlir::Value rhs = 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;
+ clang::QualType resultQTy =
+ resultArg->getType()->castAs<clang::PointerType>()->getPointeeType();
+ auto resultCIRTy = mlir::cast<cir::IntType>(cgm.convertType(resultQTy));
+
+ // Create the appropriate overflow-checked arithmetic operation.
+ mlir::Location loc = getLoc(e->getSourceRange());
+ mlir::Value result, overflow;
switch (builtinID) {
default:
llvm_unreachable("Unknown overflow builtin id.");
@@ -1985,7 +1998,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_sadd_overflow:
case Builtin::BI__builtin_saddl_overflow:
case Builtin::BI__builtin_saddll_overflow:
- arithKind = cir::BinOpOverflowKind::Add;
+ std::tie(result, overflow) =
+ emitOverflowOp<cir::AddOverflowOp>(builder, loc, resultCIRTy, x, y);
break;
case Builtin::BI__builtin_usub_overflow:
case Builtin::BI__builtin_usubl_overflow:
@@ -1993,7 +2007,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_ssub_overflow:
case Builtin::BI__builtin_ssubl_overflow:
case Builtin::BI__builtin_ssubll_overflow:
- arithKind = cir::BinOpOverflowKind::Sub;
+ std::tie(result, overflow) =
+ emitOverflowOp<cir::SubOverflowOp>(builder, loc, resultCIRTy, x, y);
break;
case Builtin::BI__builtin_umul_overflow:
case Builtin::BI__builtin_umull_overflow:
@@ -2001,24 +2016,17 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_smul_overflow:
case Builtin::BI__builtin_smull_overflow:
case Builtin::BI__builtin_smulll_overflow:
- arithKind = cir::BinOpOverflowKind::Mul;
+ std::tie(result, overflow) =
+ emitOverflowOp<cir::MulOverflowOp>(builder, loc, resultCIRTy, x, y);
break;
}
- clang::QualType resultQTy =
- resultArg->getType()->castAs<clang::PointerType>()->getPointeeType();
- auto resultCIRTy = mlir::cast<cir::IntType>(cgm.convertType(resultQTy));
-
- mlir::Location loc = getLoc(e->getSourceRange());
- 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);
+ builder.createStore(loc, emitToMemory(result, resultQTy), resultPtr,
+ isVolatile);
- return RValue::get(arithOp.getOverflow());
+ return RValue::get(overflow);
}
case Builtin::BIaddressof:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 50617a8d04f6d..e565b029da9df 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -581,9 +581,9 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
// allocation fails.
if (typeSizeMultiplier != 1) {
mlir::Value tsmV = cgf.getBuilder().getConstInt(loc, typeSizeMultiplier);
- auto mulOp = cir::BinOpOverflowOp::create(
- cgf.getBuilder(), loc, mlir::cast<cir::IntType>(cgf.sizeTy),
- cir::BinOpOverflowKind::Mul, size, tsmV);
+ auto mulOp = cir::MulOverflowOp::create(
+ cgf.getBuilder(), loc, mlir::cast<cir::IntType>(cgf.sizeTy), size,
+ tsmV);
if (hasOverflow)
hasOverflow =
@@ -617,9 +617,9 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
if (cookieSize != 0) {
sizeWithoutCookie = size;
mlir::Value cookieSizeV = cgf.getBuilder().getConstInt(loc, cookieSize);
- auto addOp = cir::BinOpOverflowOp::create(
- cgf.getBuilder(), loc, mlir::cast<cir::IntType>(cgf.sizeTy),
- cir::BinOpOverflowKind::Add, size, cookieSizeV);
+ auto addOp = cir::AddOverflowOp::create(
+ cgf.getBuilder(), loc, mlir::cast<cir::IntType>(cgf.sizeTy), size,
+ cookieSizeV);
if (hasOverflow)
hasOverflow =
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 4a1b4292b23dd..44d0dab36c7dd 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3136,22 +3136,29 @@ mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite(
return cmpOp.emitError() << "unsupported type for CmpOp: " << type;
}
-mlir::LogicalResult CIRToLLVMBinOpOverflowOpLowering::matchAndRewrite(
- cir::BinOpOverflowOp op, OpAdaptor adaptor,
- mlir::ConversionPatternRewriter &rewriter) const {
+/// Shared lowering logic for checked binary arithmetic overflow operations.
+/// The \p opStr parameter specifies the arithmetic operation name used in the
+/// LLVM intrinsic (e.g., "add", "sub", "mul").
+template <typename OpTy>
+static mlir::LogicalResult
+lowerBinOpOverflow(OpTy op, typename OpTy::Adaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter,
+ const mlir::TypeConverter *typeConverter,
+ llvm::StringRef opStr) {
mlir::Location loc = op.getLoc();
- cir::BinOpOverflowKind arithKind = op.getKind();
cir::IntType operandTy = op.getLhs().getType();
cir::IntType resultTy = op.getResult().getType();
- EncompassedTypeInfo encompassedTyInfo =
- computeEncompassedTypeWidth(operandTy, resultTy);
- mlir::IntegerType encompassedLLVMTy =
- rewriter.getIntegerType(encompassedTyInfo.width);
+ bool sign = operandTy.getIsSigned() || resultTy.getIsSigned();
+ unsigned width =
+ std::max(operandTy.getWidth() + (sign && operandTy.isUnsigned()),
+ resultTy.getWidth() + (sign && resultTy.isUnsigned()));
+
+ mlir::IntegerType encompassedLLVMTy = rewriter.getIntegerType(width);
mlir::Value lhs = adaptor.getLhs();
mlir::Value rhs = adaptor.getRhs();
- if (operandTy.getWidth() < encompassedTyInfo.width) {
+ if (operandTy.getWidth() < width) {
if (operandTy.isSigned()) {
lhs = mlir::LLVM::SExtOp::create(rewriter, loc, encompassedLLVMTy, lhs);
rhs = mlir::LLVM::SExtOp::create(rewriter, loc, encompassedLLVMTy, rhs);
@@ -3161,8 +3168,10 @@ mlir::LogicalResult CIRToLLVMBinOpOverflowOpLowering::matchAndRewrite(
}
}
- std::string intrinName = getLLVMIntrinName(arithKind, encompassedTyInfo.sign,
- encompassedTyInfo.width);
+ // The intrinsic name is `@llvm.{s|u}{op}.with.overflow.i{width}`
+ std::string intrinName = ("llvm." + llvm::Twine(sign ? 's' : 'u') + opStr +
+ ".with.overflow.i" + llvm::Twine(width))
+ .str();
auto intrinNameAttr = mlir::StringAttr::get(op.getContext(), intrinName);
mlir::IntegerType overflowLLVMTy = rewriter.getI1Type();
@@ -3180,8 +3189,8 @@ mlir::LogicalResult CIRToLLVMBinOpOverflowOpLowering::matchAndRewrite(
rewriter, loc, intrinRet, ArrayRef<int64_t>{1})
.getResult();
- if (resultTy.getWidth() < encompassedTyInfo.width) {
- mlir::Type resultLLVMTy = getTypeConverter()->convertType(resultTy);
+ if (resultTy.getWidth() < width) {
+ mlir::Type resultLLVMTy = typeConverter->convertType(resultTy);
auto truncResult =
mlir::LLVM::TruncOp::create(rewriter, loc, resultLLVMTy, result);
@@ -3202,7 +3211,7 @@ mlir::LogicalResult CIRToLLVMBinOpOverflowOpLowering::matchAndRewrite(
}
mlir::Type boolLLVMTy =
- getTypeConverter()->convertType(op.getOverflow().getType());
+ typeConverter->convertType(op.getOverflow().getType());
if (boolLLVMTy != rewriter.getI1Type())
overflow = mlir::LLVM::ZExtOp::create(rewriter, loc, boolLLVMTy, overflow);
@@ -3211,43 +3220,22 @@ mlir::LogicalResult CIRToLLVMBinOpOverflowOpLowering::matchAndRewrite(
return mlir::success();
}
-std::string CIRToLLVMBinOpOverflowOpLowering::getLLVMIntrinName(
- cir::BinOpOverflowKind opKind, bool isSigned, unsigned width) {
- // The intrinsic name is `@llvm.{s|u}{opKind}.with.overflow.i{width}`
-
- std::string name = "llvm.";
-
- if (isSigned)
- name.push_back('s');
- else
- name.push_back('u');
-
- switch (opKind) {
- case cir::BinOpOverflowKind::Add:
- name.append("add.");
- break;
- case cir::BinOpOverflowKind::Sub:
- name.append("sub.");
- break;
- case cir::BinOpOverflowKind::Mul:
- name.append("mul.");
- break;
- }
-
- name.append("with.overflow.i");
- name.append(std::to_string(width));
+mlir::LogicalResult CIRToLLVMAddOverflowOpLowering::matchAndRewrite(
+ cir::AddOverflowOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "add");
+}
- return name;
+mlir::LogicalResult CIRToLLVMSubOverflowOpLowering::matchAndRewrite(
+ cir::SubOverflowOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "sub");
}
-CIRToLLVMBinOpOverflowOpLowering::EncompassedTypeInfo
-CIRToLLVMBinOpOverflowOpLowering::computeEncompassedTypeWidth(
- cir::IntType operandTy, cir::IntType resultTy) {
- bool sign = operandTy.getIsSigned() || resultTy.getIsSigned();
- unsigned width =
- std::max(operandTy.getWidth() + (sign && operandTy.isUnsigned()),
- resultTy.getWidth() + (sign && resultTy.isUnsigned()));
- return {sign, width};
+mlir::LogicalResult CIRToLLVMMulOverflowOpLowering::matchAndRewrite(
+ cir::MulOverflowOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "mul");
}
mlir::LogicalResult CIRToLLVMShiftOpLoweri...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/186653
More information about the cfe-commits
mailing list