[clang] [CIR] Upstream basic alloca and load support (PR #128792)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 25 16:14:15 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Andy Kaylor (andykaylor)
<details>
<summary>Changes</summary>
This change implements basic support in ClangIR for local variables using the cir.alloca and cir.load operations.
---
Patch is 42.22 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128792.diff
18 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+44)
- (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+15)
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+143)
- (modified) clang/include/clang/CIR/MissingFeatures.h (+18)
- (added) clang/lib/CIR/CodeGen/Address.h (+76)
- (added) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+82)
- (added) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+128)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+66)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+17)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+61-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+5)
- (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+12)
- (added) clang/lib/CIR/CodeGen/CIRGenValue.h (+125)
- (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+2)
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+18)
- (added) clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp (+77)
- (modified) clang/lib/CIR/Dialect/IR/CMakeLists.txt (+1)
- (added) clang/test/CIR/CodeGen/basic.cpp (+13)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index f03241a875845..a62b00cc8ed33 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H
#define LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H
+#include "clang/AST/CharUnits.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
@@ -51,6 +52,49 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return cir::ConstPtrAttr::get(
getContext(), mlir::cast<cir::PointerType>(type), valueAttr);
}
+
+ mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
+ mlir::Type type, llvm::StringRef name,
+ mlir::IntegerAttr alignment,
+ mlir::Value dynAllocSize) {
+ return create<cir::AllocaOp>(loc, addrType, type, name, alignment,
+ dynAllocSize);
+ }
+
+ cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
+ bool isVolatile = false, uint64_t alignment = 0) {
+ mlir::IntegerAttr intAttr;
+ if (alignment)
+ intAttr = mlir::IntegerAttr::get(
+ mlir::IntegerType::get(ptr.getContext(), 64), alignment);
+
+ return create<cir::LoadOp>(loc, ptr);
+ }
+
+ //
+ // Block handling helpers
+ // ----------------------
+ //
+ static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
+ auto last =
+ std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
+ // TODO: Add LabelOp missing feature here
+ return mlir::isa<cir::AllocaOp>(&op);
+ });
+
+ if (last != block->rend())
+ return OpBuilder::InsertPoint(block, ++mlir::Block::iterator(&*last));
+ return OpBuilder::InsertPoint(block, block->begin());
+ };
+
+ mlir::IntegerAttr getSizeFromCharUnits(mlir::MLIRContext *ctx,
+ clang::CharUnits size) {
+ // Note that mlir::IntegerType is used instead of cir::IntType here
+ // because we don't need sign information for this to be useful, so keep
+ // it simple.
+ return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
+ size.getQuantity());
+ }
};
} // namespace cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 097616ba06749..ece04c225e322 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -54,6 +54,21 @@ def CIR_BoolAttr : CIR_Attr<"Bool", "bool", [TypedAttrInterface]> {
}];
}
+//===----------------------------------------------------------------------===//
+// UndefAttr
+//===----------------------------------------------------------------------===//
+
+def UndefAttr : CIR_Attr<"Undef", "undef", [TypedAttrInterface]> {
+ let summary = "Represent an undef constant";
+ let description = [{
+ The UndefAttr represents an undef constant, corresponding to LLVM's notion
+ of undef.
+ }];
+
+ let parameters = (ins AttributeSelfTypeParameter<"">:$type);
+ let assemblyFormat = [{}];
+}
+
//===----------------------------------------------------------------------===//
// IntegerAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 45d39807b35c8..1ff5e215c10a0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -115,6 +115,149 @@ def ConstantOp : CIR_Op<"const",
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// AllocaOp
+//===----------------------------------------------------------------------===//
+
+class AllocaTypesMatchWith<string summary, string lhsArg, string rhsArg,
+ string transform, string comparator = "std::equal_to<>()">
+ : PredOpTrait<summary, CPred<
+ comparator # "(" #
+ !subst("$_self", "$" # lhsArg # ".getType()", transform) #
+ ", $" # rhsArg # ")">> {
+ string lhs = lhsArg;
+ string rhs = rhsArg;
+ string transformer = transform;
+}
+
+def AllocaOp : CIR_Op<"alloca", [
+ AllocaTypesMatchWith<"'allocaType' matches pointee type of 'addr'",
+ "addr", "allocaType",
+ "cast<PointerType>($_self).getPointee()">,
+ DeclareOpInterfaceMethods<PromotableAllocationOpInterface>]> {
+ let summary = "Defines a scope-local variable";
+ let description = [{
+ The `cir.alloca` operation defines a scope-local variable.
+
+ The presence `init` attribute indicates that the local variable represented
+ by this alloca was originally initialized in C/C++ source code. In such
+ cases, the first use contains the initialization (a cir.store, a cir.call
+ to a ctor, etc).
+
+ The presence of the `const` attribute indicates that the local variable is
+ declared with C/C++ `const` keyword.
+
+ The `dynAllocSize` specifies the size to dynamically allocate on the stack
+ and ignores the allocation size based on the original type. This is useful
+ when handling VLAs and is omitted when declaring regular local variables.
+
+ The result type is a pointer to the input's type.
+
+ Example:
+
+ ```mlir
+ // int count = 3;
+ %0 = cir.alloca i32, !cir.ptr<i32>, ["count", init] {alignment = 4 : i64}
+
+ // int *ptr;
+ %1 = cir.alloca !cir.ptr<i32>, !cir.ptr<!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64}
+ ...
+ ```
+ }];
+
+ let arguments = (ins
+ Optional<PrimitiveInt>:$dynAllocSize,
+ TypeAttr:$allocaType,
+ StrAttr:$name,
+ UnitAttr:$init,
+ UnitAttr:$constant,
+ ConfinedAttr<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment,
+ OptionalAttr<ArrayAttr>:$annotations
+ );
+
+ let results = (outs Res<CIR_PointerType, "",
+ [MemAlloc<AutomaticAllocationScopeResource>]>:$addr);
+
+ let skipDefaultBuilders = 1;
+ let builders = [
+ OpBuilder<(ins "mlir::Type":$addr, "mlir::Type":$allocaType,
+ "llvm::StringRef":$name,
+ "mlir::IntegerAttr":$alignment)>,
+
+ OpBuilder<(ins "mlir::Type":$addr,
+ "mlir::Type":$allocaType,
+ "llvm::StringRef":$name,
+ "mlir::IntegerAttr":$alignment,
+ "mlir::Value":$dynAllocSize),
+ [{
+ if (dynAllocSize)
+ $_state.addOperands(dynAllocSize);
+ build($_builder, $_state, addr, allocaType, name, alignment);
+ }]>
+ ];
+
+ let extraClassDeclaration = [{
+ // Whether the alloca input type is a pointer.
+ bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
+
+ bool isDynamic() { return (bool)getDynAllocSize(); }
+ }];
+
+ let assemblyFormat = [{
+ $allocaType `,` qualified(type($addr)) `,`
+ ($dynAllocSize^ `:` type($dynAllocSize) `,`)?
+ `[` $name
+ (`,` `init` $init^)?
+ (`,` `const` $constant^)?
+ `]`
+ ($annotations^)? attr-dict
+ }];
+
+ let hasVerifier = 0;
+}
+
+//===----------------------------------------------------------------------===//
+// LoadOp
+//===----------------------------------------------------------------------===//
+
+def LoadOp : CIR_Op<"load", [
+ TypesMatchWith<"type of 'result' matches pointee type of 'addr'",
+ "addr", "result",
+ "cast<PointerType>($_self).getPointee()">,
+ DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
+
+ let summary = "Load value from memory adddress";
+ let description = [{
+ `cir.load` reads a value (lvalue to rvalue conversion) given an address
+ backed up by a `cir.ptr` type. A unit attribute `deref` can be used to
+ mark the resulting value as used by another operation to dereference
+ a pointer. A unit attribute `volatile` can be used to indicate a volatile
+ loading. Load can be marked atomic by using `atomic(<mem_order>)`.
+
+ `align` can be used to specify an alignment that's different from the
+ default, which is computed from `result`'s type ABI data layout.
+
+ Example:
+
+ ```mlir
+
+ // Read from local variable, address in %0.
+ %1 = cir.load %0 : !cir.ptr<i32>, i32
+ ```
+ }];
+
+ let arguments = (ins Arg<CIR_PointerType, "the address to load from",
+ [MemRead]>:$addr
+ );
+ let results = (outs CIR_AnyType:$result);
+
+ let assemblyFormat = [{
+ $addr `:` qualified(type($addr)) `,` type($result) attr-dict
+ }];
+
+ // FIXME: add verifier.
+}
+
//===----------------------------------------------------------------------===//
// ReturnOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index d4fcd52e7e6e3..97e919b5c2d74 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -36,6 +36,24 @@ struct MissingFeatures {
static bool opGlobalConstant() { return false; }
static bool opGlobalAlignment() { return false; }
static bool opGlobalLinkage() { return false; }
+
+ // Load attributes
+ static bool opLoadThreadLocal() { return false; }
+ static bool opLoadEmitScalarRangeCheck() { return false; }
+ static bool opLoadBooleanRepresentation() { return false; }
+
+ // AllocaOp handling
+ static bool opAllocaVarDeclContext() { return false; }
+ static bool opAllocaStaticLocal() { return false; }
+ static bool opAllocaNonGC() { return false; }
+ static bool opAllocaImpreciseLifetime() { return false; }
+ static bool opAllocaTLS() { return false; }
+ static bool opAllocaOpenMPThreadPrivate() { return false; }
+ static bool opAllocaEscapeByReference() { return false; }
+ static bool opAllocaReference() { return false; }
+
+ // Options for casts
+ static bool scalarConversionOpts() { return false; }
};
} // namespace cir
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
new file mode 100644
index 0000000000000..c68facde9a18c
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 class provides a simple wrapper for a pair of a pointer and an
+// alignment.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_CIR_ADDRESS_H
+#define LLVM_CLANG_LIB_CIR_ADDRESS_H
+
+#include "mlir/IR/Value.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "llvm/ADT/PointerIntPair.h"
+
+namespace clang::CIRGen {
+
+class Address {
+
+ // The boolean flag indicates whether the pointer is known to be non-null.
+ llvm::PointerIntPair<mlir::Value, 1, bool> pointerAndKnownNonNull;
+
+ /// The expected CIR type of the pointer. Carrying accurate element type
+ /// information in Address makes it more convenient to work with Address
+ /// values and allows frontend assertions to catch simple mistakes.
+ mlir::Type elementType;
+
+ clang::CharUnits alignment;
+
+protected:
+ Address(std::nullptr_t) : elementType(nullptr) {}
+
+public:
+ Address(mlir::Value pointer, mlir::Type elementType,
+ clang::CharUnits alignment)
+ : pointerAndKnownNonNull(pointer, false), elementType(elementType),
+ alignment(alignment) {
+ assert(mlir::isa<cir::PointerType>(pointer.getType()) &&
+ "Expected cir.ptr type");
+
+ assert(pointer && "Pointer cannot be null");
+ assert(elementType && "Element type cannot be null");
+ assert(!alignment.isZero() && "Alignment cannot be zero");
+
+ assert(mlir::cast<cir::PointerType>(pointer.getType()).getPointee() ==
+ elementType);
+ }
+
+ static Address invalid() { return Address(nullptr); }
+ bool isValid() const {
+ return pointerAndKnownNonNull.getPointer() != nullptr;
+ }
+
+ mlir::Value getPointer() const {
+ assert(isValid());
+ return pointerAndKnownNonNull.getPointer();
+ }
+
+ mlir::Type getElementType() const {
+ assert(isValid());
+ assert(mlir::cast<cir::PointerType>(
+ pointerAndKnownNonNull.getPointer().getType())
+ .getPointee() == elementType);
+ return elementType;
+ }
+};
+
+} // namespace clang::CIRGen
+
+#endif // LLVM_CLANG_LIB_CIR_ADDRESS_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
new file mode 100644
index 0000000000000..34e0b18594efe
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 Decl nodes as CIR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+#include "clang/AST/Expr.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+/// Emit code and set up symbol table for a variable declaration with auto,
+/// register, or no storage class specifier. These turn into simple stack
+/// objects, globals depending on target.
+void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) {
+ QualType ty = d.getType();
+ assert(ty.getAddressSpace() == LangAS::Default);
+
+ auto loc = getLoc(d.getSourceRange());
+
+ if (d.isEscapingByref())
+ cgm.errorNYI(d.getSourceRange(),
+ "emitAutoVarDecl: decl escaping by reference");
+
+ CharUnits alignment = getContext().getDeclAlign(&d);
+
+ // If the type is variably-modified, emit all the VLA sizes for it.
+ if (ty->isVariablyModifiedType())
+ cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: variably modified type");
+
+ Address address = Address::invalid();
+ if (!ty->isConstantSizeType())
+ cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: non-constant size type");
+
+ // A normal fixed sized variable becomes an alloca in the entry block,
+ mlir::Type allocaTy = convertTypeForMem(ty);
+ // Create the temp alloca and declare variable using it.
+ mlir::Value addrVal;
+ address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
+ /*ArraySize=*/nullptr);
+ setAddrOfLocalVar(&d, address);
+ // TODO: emit var init and cleanup
+}
+
+void CIRGenFunction::emitVarDecl(const VarDecl &d) {
+ if (d.hasExternalStorage()) {
+ // Don't emit it now, allow it to be emitted lazily on its first use.
+ return;
+ }
+
+ if (d.getStorageDuration() != SD_Automatic)
+ cgm.errorNYI(d.getSourceRange(), "emitVarDecl automatic storage duration");
+ if (d.getType().getAddressSpace() == LangAS::opencl_local)
+ cgm.errorNYI(d.getSourceRange(), "emitVarDecl openCL address space");
+
+ assert(d.hasLocalStorage());
+
+ assert(!cir::MissingFeatures::opAllocaVarDeclContext());
+ return emitAutoVarDecl(d);
+}
+
+void CIRGenFunction::emitDecl(const Decl &d) {
+ switch (d.getKind()) {
+ case Decl::Var: {
+ const VarDecl &vd = cast<VarDecl>(d);
+ assert(vd.isLocalVarDecl() &&
+ "Should not see file-scope variables inside a function!");
+ emitVarDecl(vd);
+ return;
+ }
+ default:
+ cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type");
+ }
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
new file mode 100644
index 0000000000000..8bb670d0d5bae
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 Expr nodes as CIR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Address.h"
+#include "CIRGenFunction.h"
+#include "CIRGenValue.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+using namespace cir;
+
+mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue,
+ SourceLocation loc) {
+ assert(!cir::MissingFeatures::opLoadThreadLocal());
+ assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck());
+ assert(!cir::MissingFeatures::opLoadBooleanRepresentation());
+
+ Address addr = lvalue.getAddress();
+ mlir::Type eltTy = addr.getElementType();
+
+ auto ptr = addr.getPointer();
+ if (mlir::isa<cir::VoidType>(eltTy))
+ cgm.errorNYI(loc, "emitLoadOfScalar: void type");
+
+ auto loadOp = builder.CIRBaseBuilderTy::createLoad(getLoc(loc), ptr,
+ false /*isVolatile*/);
+
+ return loadOp;
+}
+
+/// Given an expression that represents a value lvalue, this
+/// method emits the address of the lvalue, then loads the result as an rvalue,
+/// returning the rvalue.
+RValue CIRGenFunction::emitLoadOfLValue(LValue lv, SourceLocation loc) {
+ assert(!lv.getType()->isFunctionType());
+ assert(!(lv.getType()->isConstantMatrixType()) && "not implemented");
+
+ if (lv.isSimple())
+ return RValue::get(emitLoadOfScalar(lv, loc));
+
+ cgm.errorNYI(loc, "emitLoadOfLValue");
+}
+
+LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
+ const NamedDecl *nd = e->getDecl();
+ QualType ty = e->getType();
+
+ assert(e->isNonOdrUse() != NOUR_Unevaluated &&
+ "should not emit an unevaluated operand");
+
+ if (const auto *vd = dyn_cast<VarDecl>(nd)) {
+ // Checks for omitted feature handling
+ assert(!cir::MissingFeatures::opAllocaStaticLocal());
+ assert(!cir::MissingFeatures::opAllocaNonGC());
+ assert(!cir::MissingFeatures::opAllocaImpreciseLifetime());
+ assert(!cir::MissingFeatures::opAllocaTLS());
+ assert(!cir::MissingFeatures::opAllocaOpenMPThreadPrivate());
+ assert(!cir::MissingFeatures::opAllocaEscapeByReference());
+
+ // Check if this is a global variable
+ if (vd->hasLinkage() || vd->isStaticDataMember())
+ cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: global variable");
+
+ Address addr = Address::invalid();
+
+ // The variable should generally be present in the local decl map.
+ auto iter = LocalDeclMap.find(vd);
+ if (iter != LocalDeclMap.end()) {
+ addr = iter->second;
+ } else {
+ // Otherwise, it might be static local we haven't emitted yet for some
+ // reason; most likely, because it's in an outer function.
+ cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: static local");
+ }
+
+ return LValue::makeAddr(addr, ty);
+ }
+
+ cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
+}
+
+mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
+ mlir::Location loc, CharUnits alignment,
+ mlir::Value arraySize) {
+ mlir::Block *entryBlock = getCurFunctionEntryBlock();
+
+ // CIR uses its own alloca AS rather than follow the target data layout like
+ // original CodeGen. The data layout awareness should be done in the lowering
+ // pass instead.
+ a...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/128792
More information about the cfe-commits
mailing list