llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Amr Hesham (AmrDeveloper) <details> <summary>Changes</summary> This change adds local initialization for ArrayType Issue #<!-- -->130197 --- Patch is 59.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132974.diff 25 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+35) - (modified) clang/include/clang/CIR/Dialect/IR/CIRDialect.h (+25) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+44) - (added) clang/include/clang/CIR/LoweringHelpers.h (+40) - (modified) clang/lib/CIR/CodeGen/Address.h (+10) - (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+4) - (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+1-1) - (added) clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp (+277) - (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+23) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+46) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+8-4) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+12) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+5) - (modified) clang/lib/CIR/CodeGen/CIRGenTypeCache.h (+13) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+24) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+4) - (modified) clang/lib/CIR/CodeGen/CIRGenValue.h (+42-2) - (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+1) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+35-7) - (modified) clang/lib/CIR/Lowering/CMakeLists.txt (+1) - (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+109) - (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+15) - (added) clang/lib/CIR/Lowering/LoweringHelpers.cpp (+150) - (modified) clang/test/CIR/CodeGen/array.cpp (+91-6) - (modified) clang/test/CIR/Lowering/array.cpp (+72-5) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index c6aea10d46b63..4c7e6ec061d46 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -32,6 +32,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::ConstantOp>(loc, attr.getType(), attr); } + // Creates constant null value for integral type ty. + cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) { + return create<cir::ConstantOp>(loc, ty, getZeroInitAttr(ty)); + } + cir::ConstantOp getBool(bool state, mlir::Location loc) { return create<cir::ConstantOp>(loc, getBoolTy(), getCIRBoolAttr(state)); } @@ -68,6 +73,36 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { getContext(), mlir::cast<cir::PointerType>(type), valueAttr); } + mlir::TypedAttr getConstNullPtrAttr(mlir::Type t) { + assert(mlir::isa<cir::PointerType>(t) && "expected cir.ptr"); + return getConstPtrAttr(t, 0); + } + + mlir::TypedAttr getZeroAttr(mlir::Type t) { + return cir::ZeroAttr::get(getContext(), t); + } + + mlir::TypedAttr getZeroInitAttr(mlir::Type ty) { + if (mlir::isa<cir::IntType>(ty)) + return cir::IntAttr::get(ty, 0); + if (auto fltType = mlir::dyn_cast<cir::SingleType>(ty)) + return cir::FPAttr::getZero(fltType); + if (auto fltType = mlir::dyn_cast<cir::DoubleType>(ty)) + return cir::FPAttr::getZero(fltType); + if (auto fltType = mlir::dyn_cast<cir::FP16Type>(ty)) + return cir::FPAttr::getZero(fltType); + if (auto fltType = mlir::dyn_cast<cir::BF16Type>(ty)) + return cir::FPAttr::getZero(fltType); + if (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty)) + return getZeroAttr(arrTy); + if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty)) + return getConstNullPtrAttr(ptrTy); + if (mlir::isa<cir::BoolType>(ty)) { + return getCIRBoolAttr(false); + } + llvm_unreachable("Zero initializer for given type is NYI"); + } + mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType, mlir::Type type, llvm::StringRef name, mlir::IntegerAttr alignment) { diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index da3b41371b9ab..ba7bbedf4714e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h @@ -32,6 +32,31 @@ #include "clang/CIR/Interfaces/CIRLoopOpInterface.h" #include "clang/CIR/Interfaces/CIROpInterfaces.h" +namespace mlir { +namespace OpTrait { + +namespace impl { +// These functions are out-of-line implementations of the methods in the +// corresponding trait classes. This avoids them being template +// instantiated/duplicated. +LogicalResult verifySameFirstOperandAndResultType(Operation *op); +} // namespace impl + +/// This class provides verification for ops that are known to have the same +/// first operand and result type. +/// +template <typename ConcreteType> +class SameFirstOperandAndResultType + : public TraitBase<ConcreteType, SameFirstOperandAndResultType> { +public: + static llvm::LogicalResult verifyTrait(Operation *op) { + return impl::verifySameFirstOperandAndResultType(op); + } +}; + +} // namespace OpTrait +} // namespace mlir + // TableGen'erated files for MLIR dialects require that a macro be defined when // they are included. GET_OP_CLASSES tells the file to define the classes for // the operations of that dialect. diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index d7d63e040a2ba..7784c8a51fdf3 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -79,6 +79,13 @@ class LLVMLoweringInfo { class CIR_Op<string mnemonic, list<Trait> traits = []> : Op<CIR_Dialect, mnemonic, traits>, LLVMLoweringInfo; +//===----------------------------------------------------------------------===// +// CIR Op Traits +//===----------------------------------------------------------------------===// + +def SameFirstOperandAndResultType : + NativeOpTrait<"SameFirstOperandAndResultType">; + //===----------------------------------------------------------------------===// // CastOp //===----------------------------------------------------------------------===// @@ -229,6 +236,43 @@ def CastOp : CIR_Op<"cast", let hasFolder = 1; } + +//===----------------------------------------------------------------------===// +// PtrStrideOp +//===----------------------------------------------------------------------===// + +def PtrStrideOp : CIR_Op<"ptr_stride", + [Pure, SameFirstOperandAndResultType]> { + let summary = "Pointer access with stride"; + let description = [{ + Given a base pointer as first operand, provides a new pointer after applying + a stride (second operand). + + ```mlir + %3 = cir.const 0 : i32 + %4 = cir.ptr_stride(%2 : !cir.ptr<i32>, %3 : i32), !cir.ptr<i32> + ``` + }]; + + let arguments = (ins CIR_PointerType:$base, PrimitiveInt:$stride); + let results = (outs CIR_PointerType:$result); + + let assemblyFormat = [{ + `(` $base `:` qualified(type($base)) `,` $stride `:` qualified(type($stride)) `)` + `,` qualified(type($result)) attr-dict + }]; + + let extraClassDeclaration = [{ + // Get type pointed by the base pointer. + mlir::Type getElementTy() { + return mlir::cast<cir::PointerType>(getBase().getType()).getPointee(); + } + }]; + + // SameFirstOperandAndResultType already checks all we need. + let hasVerifier = 0; +} + //===----------------------------------------------------------------------===// // ConstantOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/LoweringHelpers.h b/clang/include/clang/CIR/LoweringHelpers.h new file mode 100644 index 0000000000000..3077010ee5ffe --- /dev/null +++ b/clang/include/clang/CIR/LoweringHelpers.h @@ -0,0 +1,40 @@ +//====- LoweringHelpers.h - Lowering helper functions ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares helper functions for lowering from CIR to LLVM or MLIR. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_CIR_LOWERINGHELPERS_H +#define LLVM_CLANG_CIR_LOWERINGHELPERS_H + +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/Transforms/DialectConversion.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + +mlir::DenseElementsAttr +convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr, mlir::Type type); + +template <typename StorageTy> StorageTy getZeroInitFromType(mlir::Type ty); +template <> mlir::APInt getZeroInitFromType(mlir::Type ty); +template <> mlir::APFloat getZeroInitFromType(mlir::Type ty); + +template <typename AttrTy, typename StorageTy> +void convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr, + llvm::SmallVectorImpl<StorageTy> &values); + +template <typename AttrTy, typename StorageTy> +mlir::DenseElementsAttr +convertToDenseElementsAttr(cir::ConstArrayAttr attr, + const llvm::SmallVectorImpl<int64_t> &dims, + mlir::Type type); + +std::optional<mlir::Attribute> +lowerConstArrayAttr(cir::ConstArrayAttr constArr, + const mlir::TypeConverter *converter); + +#endif diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h index fba1ffd90877b..2cc8ada783197 100644 --- a/clang/lib/CIR/CodeGen/Address.h +++ b/clang/lib/CIR/CodeGen/Address.h @@ -70,6 +70,14 @@ class Address { return pointerAndKnownNonNull.getPointer(); } + mlir::Type getType() const { + assert(mlir::cast<cir::PointerType>( + pointerAndKnownNonNull.getPointer().getType()) + .getPointee() == elementType); + + return mlir::cast<cir::PointerType>(getPointer().getType()); + } + mlir::Type getElementType() const { assert(isValid()); assert(mlir::cast<cir::PointerType>( @@ -77,6 +85,8 @@ class Address { .getPointee() == elementType); return elementType; } + + clang::CharUnits getAlignment() const { return alignment; } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index fef290612149a..d1a81f85b66fc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -83,6 +83,10 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); } + bool isInt8Ty(mlir::Type i) { + return i == typeCache.UInt8Ty || i == typeCache.SInt8Ty; + } + // Creates constant nullptr for pointer type ty. cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) { assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer()); diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index a93e8dbcb42de..a0c6b832faa24 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -250,7 +250,7 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, return; } case cir::TEK_Aggregate: - cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: aggregate type"); + emitAggExpr(init, AggValueSlot::forLValue(lvalue)); return; } llvm_unreachable("bad evaluation kind"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp new file mode 100644 index 0000000000000..3bd898422f4d6 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -0,0 +1,277 @@ +//===--- CIRGenExprAgg.cpp - Emit CIR Code from Aggregate Expressions -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code to emit Aggregate Expr nodes as CIR code. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenBuilder.h" +#include "CIRGenFunction.h" +#include "CIRGenValue.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" + +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +#include <cstdint> + +using namespace clang; +using namespace clang::CIRGen; + +namespace { +class AggExprEmitter : public StmtVisitor<AggExprEmitter> { + + CIRGenFunction &cgf; + AggValueSlot dest; + + AggValueSlot ensureSlot(mlir::Location loc, QualType t) { + if (!dest.isIgnored()) + return dest; + llvm_unreachable("Slot for ignored address NTI"); + } + +public: + AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest) + : cgf(cgf), dest(dest) {} + + void emitArrayInit(Address destPtr, cir::ArrayType arrayTy, QualType arrayQTy, + Expr *exprToVisit, ArrayRef<Expr *> args, + Expr *arrayFiller); + + void emitInitializationToLValue(Expr *e, LValue lv); + + void emitNullInitializationToLValue(mlir::Location loc, LValue lv); + + void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); } + + void VisitInitListExpr(InitListExpr *e); + + void visitCXXParenListOrInitListExpr(Expr *e, ArrayRef<Expr *> args, + FieldDecl *initializedFieldInUnion, + Expr *arrayFiller); +}; + +} // namespace + +static bool isTrivialFiller(Expr *e) { + if (!e) + return true; + + if (isa<ImplicitValueInitExpr>(e)) + return true; + + if (auto *ile = dyn_cast<InitListExpr>(e)) { + if (ile->getNumInits()) + return false; + return isTrivialFiller(ile->getArrayFiller()); + } + + if (const auto *cons = dyn_cast_or_null<CXXConstructExpr>(e)) + return cons->getConstructor()->isDefaultConstructor() && + cons->getConstructor()->isTrivial(); + + return false; +} + +void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, + QualType arrayQTy, Expr *e, + ArrayRef<Expr *> args, Expr *arrayFiller) { + CIRGenBuilderTy &builder = cgf.getBuilder(); + const mlir::Location loc = cgf.getLoc(e->getSourceRange()); + + const uint64_t numInitElements = args.size(); + + const QualType elementType = + cgf.getContext().getAsArrayType(arrayQTy)->getElementType(); + + if (elementType.isDestructedType()) { + llvm_unreachable("dtorKind NYI"); + } + + const QualType elementPtrType = cgf.getContext().getPointerType(elementType); + + const mlir::Type cirElementType = cgf.convertType(elementType); + const cir::PointerType cirElementPtrType = + builder.getPointerTo(cirElementType); + + auto begin = builder.create<cir::CastOp>(loc, cirElementPtrType, + cir::CastKind::array_to_ptrdecay, + destPtr.getPointer()); + + const CharUnits elementSize = + cgf.getContext().getTypeSizeInChars(elementType); + const CharUnits elementAlign = + destPtr.getAlignment().alignmentOfArrayElement(elementSize); + + // The 'current element to initialize'. The invariants on this + // variable are complicated. Essentially, after each iteration of + // the loop, it points to the last initialized element, except + // that it points to the beginning of the array before any + // elements have been initialized. + mlir::Value element = begin; + + // Don't build the 'one' before the cycle to avoid + // emmiting the redundant `cir.const 1` instrs. + mlir::Value one; + + // Emit the explicit initializers. + for (uint64_t i = 0; i != numInitElements; ++i) { + // Advance to the next element. + if (i > 0) { + one = builder.create<cir::ConstantOp>( + loc, cgf.PtrDiffTy, cir::IntAttr::get(cgf.PtrDiffTy, i)); + element = + builder.create<cir::PtrStrideOp>(loc, cirElementPtrType, begin, one); + } + + const Address address = Address(element, cirElementType, elementAlign); + const LValue elementLV = LValue::makeAddr(address, elementType); + emitInitializationToLValue(args[i], elementLV); + } + + const uint64_t numArrayElements = arrayTy.getSize(); + + // Check whether there's a non-trivial array-fill expression. + const bool hasTrivialFiller = isTrivialFiller(arrayFiller); + + // Any remaining elements need to be zero-initialized, possibly + // using the filler expression. We can skip this if the we're + // emitting to zeroed memory. + if (numInitElements != numArrayElements && + !(dest.isZeroed() && hasTrivialFiller && + cgf.getTypes().isZeroInitializable(elementType))) { + // Advance to the start of the rest of the array. + if (numInitElements) { + one = builder.create<cir::ConstantOp>( + loc, cgf.PtrDiffTy, cir::IntAttr::get(cgf.PtrDiffTy, 1)); + element = builder.create<cir::PtrStrideOp>(loc, cirElementPtrType, + element, one); + } + + // Allocate the temporary variable + // to store the pointer to first unitialized element + auto tmpAddr = cgf.createTempAlloca( + cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp"); + LValue tmpLV = LValue::makeAddr(tmpAddr, elementPtrType); + cgf.emitStoreThroughLValue(RValue::get(element), tmpLV); + + // TODO:Replace this part later with cir::DoWhileOp + for (unsigned i = numInitElements; i != numArrayElements; ++i) { + auto currentElement = builder.createLoad(loc, tmpAddr.getPointer()); + + // Emit the actual filler expression. + const LValue elementLV = LValue::makeAddr( + Address(currentElement, cirElementType, elementAlign), elementType); + + if (arrayFiller) + emitInitializationToLValue(arrayFiller, elementLV); + else + emitNullInitializationToLValue(loc, elementLV); + + // Advance pointer and store them to temporary variable + one = builder.create<cir::ConstantOp>( + loc, cgf.PtrDiffTy, cir::IntAttr::get(cgf.PtrDiffTy, 1)); + + auto nextElement = builder.create<cir::PtrStrideOp>( + loc, cirElementPtrType, currentElement, one); + cgf.emitStoreThroughLValue(RValue::get(nextElement), tmpLV); + } + } +} + +void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) { + const QualType type = lv.getType(); + + if (isa<ImplicitValueInitExpr, CXXScalarValueInitExpr>(e)) { + const auto loc = e->getSourceRange().isValid() + ? cgf.getLoc(e->getSourceRange()) + : *cgf.currSrcLoc; + return emitNullInitializationToLValue(loc, lv); + } + + if (isa<NoInitExpr>(e)) + return; + + if (type->isReferenceType()) { + llvm_unreachable("NTI"); + } + + switch (cgf.getEvaluationKind(type)) { + case cir::TEK_Complex: + llvm_unreachable("TEK_Complex NYI"); + break; + case cir::TEK_Aggregate: + cgf.emitAggExpr(e, AggValueSlot::forLValue(lv)); + return; + case cir::TEK_Scalar: + if (lv.isSimple()) + cgf.emitScalarInit(e, cgf.getLoc(e->getSourceRange()), lv); + else + cgf.emitStoreThroughLValue(RValue::get(cgf.emitScalarExpr(e)), lv); + return; + } +} + +void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc, + LValue lv) { + const QualType type = lv.getType(); + + // If the destination slot is already zeroed out before the aggregate is + // copied into it, we don't have to emit any zeros here. + if (dest.isZeroed() && cgf.getTypes().isZeroInitializable(type)) + return; + + if (cgf.hasScalarEvaluationKind(type)) { + // For non-aggregates, we can store the appropriate null constant. + auto null = cgf.cgm.emitNullConstant(type, loc); + if (lv.isSimple()) { + cgf.emitStoreOfScalar(null, lv, /* isInitialization */ true); + return; + } + + llvm_unreachable("emitStoreThroughBitfieldLValue NYI"); + return; + } + + // There's a potential optimization opportunity in combining + // memsets; that would be easy for arrays, but relatively + // difficult for structures with the current code. + cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType()); +} + +void AggExprEmitter::VisitInitListExpr(InitListExpr *e) { + if (e->hadArrayRangeDesignator()) + llvm_unreachable("GNU array range designator extension"); + + if (e->isTransparent()) + return Visit(e->getInit(0)); + + visitCXXParenListOrInitListExpr( + e, e->inits(), e->getInitializedFieldInUnion(), e->getArrayFiller()); +} + +void AggExprEmitter::visitCXXParenListOrInitListExpr( + Expr *e, ArrayRef<Expr *> args, FieldDecl *initializedFieldInUnion, + Expr *arrayFiller) { + + const AggValueSlot dest = + ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType()); + + if (e->getType()->isConstantArrayType()) { + cir::ArrayType arrayTy = + cast<cir::ArrayType>(dest.getAddress().getElementType()); + emitArrayInit(dest.getAddress(), arrayTy, e->getType(), e, args, + arrayFiller); + return; + } + + llvm_unreachable("NYI"); +} + +void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) { + AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e)); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index fc49d6da97206..06fcc0e2c040a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -412,3 +412,26 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value, } llvm_unreachable("Unknown APValue kind"); } + +mlir::Value CIRGenModule::emitNullConstant(QualType t, mlir::Location loc) { + if (t->getAs<PointerType>()) { + return builder.getNullPtr(getTypes().convertTypeForM... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/132974 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits