llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-mlir Author: adams381 <details> <summary>Changes</summary> Fourth PR in the split of #<!-- -->192119/#<!-- -->192124. Implements the Direct-with-coercion path in CallConvLowering and picks off andykaylor's five inline review comments from the original PR. The new cir.reinterpret_cast op is for same-bit-width in-register reinterpretation (vector<2 x float> <-> complex<float>). emitCoercion uses it when source and destination differ only in vector-vs-non-vector shape and have identical bit width, instead of going through memory. For everything else (records, or shape doesn't match) the helper still does alloca/store/ptr-cast/load. Andy's comments, in order: - Temporary alloca alignment is now max(srcAlign, dstAlign) from DataLayout instead of hardcoded. - The alloca lives in the entry block via InsertionGuard so it composes with HoistAllocas regardless of pipeline order. - isVolatile kept as UnitAttr-absence with an inline comment. - vector<->complex now uses cir.reinterpret_cast. - Memory path has three new .cir tests covering it. CallConvLowering needed splitting into three phases (function-def coercion / call-site rewriting / Ignore cleanup) because block-arg type changes from Direct-with-coerce confused the earlier ordering: Ignore'd args were getting alloca/load chains synthesized for call-site uses that were about to be dropped anyway. LowerToLLVM gets a stub for the new op: bitcast for same-shape converted types, error-with-message for aggregates. We don't produce aggregates from CallConvLowering today, so the error path is only reachable from hand-written IR; follow-up patch can add an extract/insert lowering if needed. Co-authored-by: Cursor <cursoragent@<!-- -->cursor.com> --- Patch is 100.75 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/195879.diff 32 Files Affected: - (modified) clang/docs/CIR/ABILowering.rst (+40) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+48) - (modified) clang/include/clang/CIR/Dialect/Passes.h (+1) - (modified) clang/include/clang/CIR/Dialect/Passes.td (+46-6) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+22) - (modified) clang/lib/CIR/Dialect/Transforms/CMakeLists.txt (+3) - (added) clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp (+198) - (added) clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp (+570) - (added) clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.h (+77) - (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/CMakeLists.txt (+2) - (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+39) - (added) clang/test/CIR/IR/reinterpret-cast.cir (+28) - (added) clang/test/CIR/Transforms/abi-lowering/Inputs/test-datalayout.cir (+17) - (added) clang/test/CIR/Transforms/abi-lowering/coerce-int-to-record.cir (+59) - (added) clang/test/CIR/Transforms/abi-lowering/coerce-record-to-int.cir (+50) - (added) clang/test/CIR/Transforms/abi-lowering/coerce-record-to-record-via-memory.cir (+34) - (added) clang/test/CIR/Transforms/abi-lowering/coerce-vector-to-complex-reinterpret.cir (+42) - (added) clang/test/CIR/Transforms/abi-lowering/datalayout-missing-error.cir (+17) - (added) clang/test/CIR/Transforms/abi-lowering/declaration-rewrite.cir (+34) - (added) clang/test/CIR/Transforms/abi-lowering/direct-passthrough-injection.cir (+42) - (added) clang/test/CIR/Transforms/abi-lowering/direct-passthrough-test-target.cir (+35) - (added) clang/test/CIR/Transforms/abi-lowering/extend-return.cir (+41) - (added) clang/test/CIR/Transforms/abi-lowering/extend-signed-arg.cir (+40) - (added) clang/test/CIR/Transforms/abi-lowering/extend-unsigned-arg.cir (+36) - (added) clang/test/CIR/Transforms/abi-lowering/ignore-arg.cir (+39) - (added) clang/test/CIR/Transforms/abi-lowering/ignore-return.cir (+48) - (modified) clang/tools/cir-opt/cir-opt.cpp (+4) - (added) mlir/include/mlir/ABI/Targets/Test/TestTarget.h (+98) - (modified) mlir/lib/ABI/CMakeLists.txt (+1) - (added) mlir/lib/ABI/Targets/Test/TestTarget.cpp (+251) - (modified) mlir/unittests/ABI/CMakeLists.txt (+1) - (added) mlir/unittests/ABI/TestTargetTest.cpp (+317) ``````````diff diff --git a/clang/docs/CIR/ABILowering.rst b/clang/docs/CIR/ABILowering.rst index 59f0bb4f9a646..74457be5c8e6d 100644 --- a/clang/docs/CIR/ABILowering.rst +++ b/clang/docs/CIR/ABILowering.rst @@ -531,6 +531,46 @@ options or configuration. The dependency direction is: the MLIR ABI pass depends on ``llvm/lib/ABI``; there is no reverse dependency from the ABI library to MLIR dialects. +CIR Pass Pipeline Position +-------------------------- + +For the CIR dialect, the calling-convention lowering pass is named +``cir-call-conv-lowering`` and runs late in the CIR-to-LLVM pipeline: + +1. ``cir-target-lowering`` legalizes target-specific operations (e.g. + atomic synchronization scopes). +2. ``cir-cxxabi-lowering`` lowers C++-specific high-level types (member + pointers, vtable lookups, etc.) to ABI-specific representations. +3. ``cir-call-conv-lowering`` rewrites function signatures and call sites + to match the target ABI's calling convention rules. + +``cir-call-conv-lowering`` requires a ``dlti.dl_spec`` attribute on the +module so it can query type sizes and alignments through MLIR's +``DataLayout``. When the attribute is missing, the pass emits a +diagnostic and fails rather than silently using a default layout. + +The pass places any temporary allocas it needs (for argument coercion, +sret slots, etc.) directly in the function entry block, so it does not +rely on a subsequent ``cir-hoist-allocas`` run to position them +correctly. This invariant means ``cir-hoist-allocas`` may run either +before or after ``cir-call-conv-lowering`` without changing observable +behavior. + +The pass takes one of two driver modes via pass options: + +- ``target=<name>`` selects a real ABI target. The first supported value + is ``test`` (the MLIR test target in ``mlir/lib/ABI/Targets/Test/``, + used for testing the rewriter without depending on the in-progress + LLVM ABI library targets). Real targets (``x86_64``, ``aarch64``, + ...) will be added as the LLVM ABI library ships them. +- ``classification-attr=<name>`` reads a pre-built ``FunctionClassifica + tion`` from a ``DictionaryAttr`` named ``<name>`` on each ``cir.func`` + and rewrites accordingly. This driver is for tests that need to + exercise rewriter behavior against arbitrary classifications without + routing through any real classifier. + +Exactly one of the two options must be set. + Open Questions ============== diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 97d623ba5e6d9..1f0cb759864f8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -266,6 +266,54 @@ def CIR_CastOp : CIR_Op<"cast", [ }]; } +//===----------------------------------------------------------------------===// +// ReinterpretCastOp +//===----------------------------------------------------------------------===// + +def CIR_ReinterpretCastOp : CIR_Op<"reinterpret_cast", [Pure]> { + let summary = "Reinterpret a value as a different same-bit-width type"; + let description = [{ + The `cir.reinterpret_cast` operation reinterprets the bits of its source + value as a different type, with no IR-level cost. It is used by the + calling-convention lowering pass to coerce between same-bit-width types + that have an LLVM-IR-level shape mismatch but identical in-register + representation -- for example, between `!cir.vector<2 x !cir.float>` and + `!cir.complex<!cir.float>`, both of which lower to the same LLVM IR + representation but have distinct CIR types. + + Unlike `cir.cast bitcast`, which is overloaded for pointer-to-pointer + bitcasts and several other use cases, `cir.reinterpret_cast` is reserved + for in-register value reinterpretation only. The result type must + differ from the source type; otherwise the op is meaningless and the + folder removes it. + + **Invariant** (not currently enforced by the verifier): the source and + destination types must have the same bit width per the module's + DataLayout, and they must use the same in-register lane order on the + target. Producers (e.g. CallConvLowering's coerce-in-registers path) + are responsible for ensuring this; a follow-up patch will move the + bit-width check into the verifier once the design question of + DataLayout-aware op verifiers is resolved. + + Example: + + ``` + %c = cir.reinterpret_cast %v + : !cir.vector<2 x !cir.float> -> !cir.complex<!cir.float> + ``` + }]; + + let arguments = (ins CIR_AnyType:$src); + let results = (outs CIR_AnyType:$result); + + let assemblyFormat = [{ + $src `:` type($src) `->` type($result) attr-dict + }]; + + let hasVerifier = 1; + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // DynamicCastOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h index d441dfcbc6c14..a68f7b621f5d8 100644 --- a/clang/include/clang/CIR/Dialect/Passes.h +++ b/clang/include/clang/CIR/Dialect/Passes.h @@ -27,6 +27,7 @@ std::unique_ptr<Pass> createCIRSimplifyPass(); std::unique_ptr<Pass> createCIREHABILoweringPass(); std::unique_ptr<Pass> createCXXABILoweringPass(); std::unique_ptr<Pass> createTargetLoweringPass(); +std::unique_ptr<Pass> createCallConvLoweringPass(); std::unique_ptr<Pass> createHoistAllocasPass(); std::unique_ptr<Pass> createLoweringPreparePass(); std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx); diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td index 32cd182aacec7..cb3c78d590a42 100644 --- a/clang/include/clang/CIR/Dialect/Passes.td +++ b/clang/include/clang/CIR/Dialect/Passes.td @@ -108,7 +108,7 @@ def TargetLowering : Pass<"cir-target-lowering", "mlir::ModuleOp"> { 1. The `TargetLowering` pass. 2. The `CXXABILowering` pass. - 3. The `CallConvLowering` pass (not implemented yet). + 3. The `CallConvLowering` pass. The `TargetLowering` pass acts more like a legalization pass. It ensures every operation in CIR conforms to the target's constraints. An example @@ -117,11 +117,11 @@ def TargetLowering : Pass<"cir-target-lowering", "mlir::ModuleOp"> { any atomic operations with a different synchronization scope would be transformed to use the system-wide scope in this pass. - The `CXXABILowering` pass and the (not yet implemented) `CallConvLowering` - pass transform the CIR according to the target's ABI requirements. The - former handles all ABI-related lowering except for calling convention - handling, which is handled specifically in the latter. Example - transformations that the `CXXABILowering` pass could make include: + The `CXXABILowering` pass and the `CallConvLowering` pass transform the + CIR according to the target's ABI requirements. The former handles all + ABI-related lowering except for calling convention handling, which is + handled specifically in the latter. Example transformations that the + `CXXABILowering` pass could make include: - Replace C/C++ types that have an ABI-defined layout with more fundamental types corresponding to the ABI requirements. For example, @@ -195,4 +195,44 @@ def IdiomRecognizer : Pass<"cir-idiom-recognizer", "mlir::ModuleOp"> { let dependentDialects = ["cir::CIRDialect"]; } +def CallConvLowering : Pass<"cir-call-conv-lowering", "mlir::ModuleOp"> { + let summary = "Lower CIR function signatures and call sites to match target ABI"; + let description = [{ + This pass rewrites `cir.func` signatures and `cir.call` sites to match the + target ABI's calling convention requirements (extension attributes, struct + coercion, sret/byval indirect passing, struct flattening, etc.). + + The pass requires a `dlti.dl_spec` attribute on the module so it can query + the data layout for sizes and alignments. + + Two driver modes select how each function's classification is computed: + + - `target=<name>` selects an ABI target. Currently only `"test"` (the + MLIR test target in `mlir/lib/ABI/Targets/Test/`) is supported. Real + targets (x86_64, AArch64, ...) will be added once the LLVM ABI library + ships them. + - `classification-attr=<name>` reads a `DictionaryAttr` named `<name>` + from each `cir.func` and parses it via the test-target injection + helper. Used by tests to inject arbitrary classifications without + depending on a real ABI target. + + Exactly one of the two options must be set. + + Pipeline position: this pass runs after `CXXABILowering` (so member + pointers and similar C++ types are already lowered) and after + `HoistAllocas` is unnecessary because this pass places its own temporary + allocas in the entry block. + }]; + let constructor = "mlir::createCallConvLoweringPass()"; + let dependentDialects = ["cir::CIRDialect"]; + let options = [ + Option<"target", "target", "std::string", /*default=*/"\"\"", + "Target whose ABI rules drive classification (currently: test)">, + Option<"classificationAttr", "classification-attr", "std::string", + /*default=*/"\"\"", + "Function attribute name carrying a pre-built FunctionClassification " + "DictionaryAttr (alternative to target=, used by tests)">, + ]; +} + #endif // CLANG_CIR_DIALECT_PASSES_TD diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 7386819d8fce9..80c3a3ecaea4a 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -821,6 +821,28 @@ static Value tryFoldCastChain(cir::CastOp op) { return {}; } +//===----------------------------------------------------------------------===// +// ReinterpretCastOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::ReinterpretCastOp::verify() { + // The op is meaningless for identical types -- the folder is the right + // way to remove it -- but we accept it at the verifier level so that + // peephole code (e.g. pattern rewriters that round-trip values) doesn't + // need a type-equality guard. Producers should still avoid emitting + // it for matching types. + // + // The same-bit-width invariant is documented on the op but not yet + // checked here; see the op description for the rationale. + return success(); +} + +OpFoldResult cir::ReinterpretCastOp::fold(FoldAdaptor adaptor) { + if (getSrc().getType() == getType()) + return getSrc(); + return {}; +} + OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) { if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getSrc())) { // Propagate poison value diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index 092ccfac7ddb7..b3fa7375eaafa 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(TargetLowering) add_clang_library(MLIRCIRTransforms + CallConvLoweringPass.cpp CIRCanonicalize.cpp CIRSimplify.cpp CXXABILowering.cpp @@ -19,7 +20,9 @@ add_clang_library(MLIRCIRTransforms clangAST clangBasic + MLIRABI MLIRAnalysis + MLIRDLTIDialect MLIRIR MLIRPass MLIRTransformUtils diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp new file mode 100644 index 0000000000000..5131bcfa4316a --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp @@ -0,0 +1,198 @@ +//===- CallConvLoweringPass.cpp - Lower CIR to ABI calling convention ----===// +// +// 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 pass walks every cir.func and cir.call in the module, computes a +// FunctionClassification for it (via either an ABI target or a pre-built +// classification injected as a function attribute), and dispatches to +// CIRABIRewriteContext to perform the actual IR rewriting. +// +// Two driver modes (mutually exclusive): +// +// target=test +// Use the MLIR test ABI target (mlir/lib/ABI/Targets/Test/) to classify +// each function. Predictable rules that approximate x86_64 SysV. Real +// targets (x86_64, AArch64) will be added once the LLVM ABI library +// ships them. +// +// classification-attr=<name> +// Read a DictionaryAttr named <name> from each cir.func and parse it via +// mlir::abi::test::parseClassificationAttr. Used by tests to inject any +// classification (including shapes the test target itself does not +// produce) without depending on a real ABI target. +// +// The pass requires a `dlti.dl_spec` attribute on the module so the +// classifier can query type sizes and alignments. +// +//===----------------------------------------------------------------------===// + +#include "PassDetail.h" +#include "TargetLowering/CIRABIRewriteContext.h" + +#include "mlir/ABI/ABIRewriteContext.h" +#include "mlir/ABI/Targets/Test/TestTarget.h" +#include "mlir/Dialect/DLTI/DLTI.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Interfaces/DataLayoutInterfaces.h" +#include "mlir/Pass/Pass.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/Passes.h" + +using namespace mlir; +using namespace mlir::abi; +using namespace cir; + +namespace mlir { +#define GEN_PASS_DEF_CALLCONVLOWERING +#include "clang/CIR/Dialect/Passes.h.inc" +} // namespace mlir + +namespace { + +struct CallConvLoweringPass + : public impl::CallConvLoweringBase<CallConvLoweringPass> { + using CallConvLoweringBase::CallConvLoweringBase; + void runOnOperation() override; +}; + +/// Classify \p func using whichever driver mode is configured. Returns +/// std::nullopt and emits an error on the function if classification fails +/// (e.g. injection-driver mode but the function is missing the attribute, +/// or the attribute is malformed). +std::optional<FunctionClassification> +classifyFunction(cir::FuncOp func, const DataLayout &dl, StringRef target, + StringRef classificationAttrName) { + ArrayRef<Type> argTypes = func.getFunctionType().getInputs(); + Type returnType = func.getFunctionType().getReturnType(); + + if (!classificationAttrName.empty()) { + auto attr = func->getAttrOfType<DictionaryAttr>(classificationAttrName); + if (!attr) { + func.emitOpError() + << "missing classification attribute '" << classificationAttrName + << "' (CallConvLowering driver mode 'classification-attr')"; + return std::nullopt; + } + return mlir::abi::test::parseClassificationAttr( + attr, [&]() { return func.emitOpError(); }); + } + + if (target == "test") + return mlir::abi::test::classify(argTypes, returnType, dl); + + func.emitOpError() << "unknown target '" << target << "' (supported: test)"; + return std::nullopt; +} + +/// Find the cir.func declaration matching a cir.call's callee, if any. +/// Returns nullptr if the callee is indirect or the symbol cannot be +/// resolved (in which case the call is left alone). +cir::FuncOp lookupCallee(cir::CallOp call, ModuleOp module) { + FlatSymbolRefAttr callee = call.getCalleeAttr(); + if (!callee) + return nullptr; + return module.lookupSymbol<cir::FuncOp>(callee.getValue()); +} + +void CallConvLoweringPass::runOnOperation() { + ModuleOp module = getOperation(); + MLIRContext *ctx = &getContext(); + + if (target.empty() == classificationAttr.empty()) { + module.emitOpError() << "CallConvLowering requires exactly one of " + "'target' or 'classification-attr' pass options"; + signalPassFailure(); + return; + } + + if (!module->hasAttr(DLTIDialect::kDataLayoutAttrName)) { + module.emitOpError() + << "CallConvLowering requires a DataLayout (dlti.dl_spec attribute " + "on the module)"; + signalPassFailure(); + return; + } + + DataLayout dl(module); + CIRABIRewriteContext rewriteCtx(module, dl); + + // Pre-compute classifications for every cir.func so that call-site + // rewriting can find them (call site uses callee's classification). + llvm::MapVector<cir::FuncOp, FunctionClassification> classifications; + bool anyFailed = false; + module.walk([&](cir::FuncOp f) { + auto fc = classifyFunction(f, dl, target, classificationAttr); + if (!fc) { + anyFailed = true; + return; + } + classifications.insert({f, std::move(*fc)}); + }); + if (anyFailed) { + signalPassFailure(); + return; + } + + OpBuilder rewriter(ctx); + + // Three-phase rewrite. Each phase needs the previous one to be complete + // across every function before it can run, so they're three separate + // sweeps over the module: + // + // 1. rewriteFunctionDefinition: in-body coercion only. Block-arg + // types for Direct-with-coerce / Extend args change here, and + // replaceAllUsesExcept routes existing uses (including in-body + // cir.call operands) over to the adapted (original-type) value. + // Ignore handling and signature finalization are deferred. + // 2. rewriteCallSite: each call site coerces args, drops Ignore'd + // args, and swaps the call to the lowered signature. Now Ignore'd + // block args have no remaining uses. + // 3. finalizeFunctionDefinition: erase the now-use-empty Ignore'd + // block args, drop Ignore'd return operands, finalize the function + // signature, and attach the llvm.signext / llvm.zeroext attrs. + // + // Splitting (1) and (3) avoids synthesizing dead alloca/load chains + // for Ignore'd args whose uses were going to be dropped by (2) anyway. + + for (auto &kv : classifications) { + if (failed(rewriteCtx.rewriteFunctionDefinition(kv.first, kv.second, + rewriter))) { + signalPassFailure(); + return; + } + } + + SmallVector<cir::CallOp> calls; + module.walk([&](cir::CallOp c) { calls.push_back(c); }); + for (cir::CallOp call : calls) { + cir::FuncOp callee = lookupCallee(call, module); + if (!callee) + continue; + auto it = classifications.find(callee); + if (it == classifications.end()) + continue; + if (failed(rewriteCtx.rewriteCallSite(call, it->second, rewriter))) { + signalPassFailure(); + return; + } + } + + for (auto &kv : classifications) { + if (failed(rewriteCtx.finalizeFunctionDefinition(kv.first, kv.second, + rewriter))) { + signalPassFailure(); + return; + } + } +} + +} // namespace + +std::unique_ptr<Pass> mlir::createCallConvLoweringPass() { + return std::make_unique<CallConvLoweringPass>(); +} diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp new file mode 100644 index 0000000000000..18937d694260b --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp @@ -0,0 +1,570 @@ +//===- CIRABIRewriteContext.cpp - CIR ABI rewrite context ----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "CIRABIRewriteContext.h" +#include "mlir/IR/Builders.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" + +using namespace cir; +using namespace mlir; +using namespace mlir::abi; + +namespace { + +bool needsRewrite(const FunctionClassification &fc) { + // Direct without coercion is a true pass-through; any other kind (or a + // coerced Direct) means the rewriter must touch the IR. Extend is + // technically attribute-only at the IR level but still counts because the + // attribute attachment changes observable behav... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/195879 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
