Skip to content
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

[CIR][CIRGen] Enable address space flags and re-categorize / fix usages #614

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,22 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr",
`CIR.ptr` is a type returned by any op generating a pointer in C++.
}];

let parameters = (ins "mlir::Type":$pointee);
let parameters = (ins "mlir::Type":$pointee, DefaultValuedParameter<"unsigned", "0">:$addrSpace);

let assemblyFormat = "`<` $pointee `>`";
let builders = [
TypeBuilderWithInferredContext<(ins "mlir::Type":$pointee, CArg<"unsigned", "0">:$addrSpace), [{
return Base::get(pointee.getContext(), pointee, addrSpace);
}]>,
TypeBuilder<(ins "mlir::Type":$pointee, CArg<"unsigned", "0">:$addrSpace), [{
return Base::get($_ctxt, pointee, addrSpace);
}]>,
];

let assemblyFormat = [{
`<` $pointee ( `,` `addrspace` `(` $addrSpace^ `)` )? `>`
}];

let skipDefaultBuilders = 1;

let extraClassDeclaration = [{
bool isVoidPtr() const {
Expand Down
11 changes: 5 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -643,9 +643,8 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {

mlir::Value createDynCastToVoid(mlir::Location loc, mlir::Value src,
bool vtableUseRelativeLayout) {
// TODO(cir): consider address space here.
assert(!UnimplementedFeature::addressSpace());
auto destTy = getVoidPtrTy();
auto srcTy = src.getType().cast<mlir::cir::PointerType>();
auto destTy = getVoidPtrTy(srcTy.getAddrSpace());
return create<mlir::cir::DynamicCastOp>(
loc, destTy, mlir::cir::DynamicCastKind::ptr, src,
mlir::cir::DynamicCastInfoAttr{}, vtableUseRelativeLayout);
Expand Down Expand Up @@ -875,11 +874,11 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
mlir::cir::GetRuntimeMemberOp createGetIndirectMember(mlir::Location loc,
mlir::Value objectPtr,
mlir::Value memberPtr) {
auto objectPtrTy = objectPtr.getType().cast<mlir::cir::PointerType>();
auto memberPtrTy = memberPtr.getType().cast<mlir::cir::DataMemberType>();

// TODO(cir): consider address space.
assert(!UnimplementedFeature::addressSpace());
auto resultTy = getPointerTo(memberPtrTy.getMemberTy());
auto resultTy =
getPointerTo(memberPtrTy.getMemberTy(), objectPtrTy.getAddrSpace());

return create<mlir::cir::GetRuntimeMemberOp>(loc, resultTy, objectPtr,
memberPtr);
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -842,8 +842,11 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
// FIXME(cir): It may make sense to allow AllocaOp of type `u8` to return a
// pointer of type `void *`. This will require a change to the allocaOp
// verifier.

LangAS AAS = getASTAllocaAddressSpace();
auto TargetAAS = CGM.getASTContext().getTargetAddressSpace(AAS);
Comment on lines +846 to +847
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clang's code sets the alloca's address space to the one described by the data-layout, and here you are using another path in the codegen - the targetCIRGenInfo/targetCodeGenInfo path. Not sure what's the right choice here, need to dive into a rabbit hole to understand the original intention of use for both.

In general, we are trying to stick as much as possible to clang's codegen structure to avoid re-making past mistakes :D

I'll note that it do seem like I missed this details in the alloca builder I implemented a while ago! I guess we should either understand how to pass the datalayout here or add some nice TODO comment for future us :)

Copy link
Collaborator Author

@seven-mile seven-mile May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the two methods to get the alloca addrspace, I think these lines of comment helps. I noticed that the code is creating alloca and cast it immediately, therefore I naively simplify it to a direct alloca😂. Sorry for the mistake.

// An alloca will always return a pointer to the alloca (stack) address
// space. This address space need not be the same as the AST / Language
// default (e.g. in C / C++ auto vars are in the generic address space). At
// the AST level this is handled within CreateTempAlloca et al., but for the
// builtin / dynamic alloca we have to handle it here. We use an explicit cast
// instead of passing an AS to CreateAlloca so as to not inhibit optimisation.
case Builtin::BIalloca:

Most of targets in current codebase indeed implement getASTAllocaAddressSpace with DataLayout internally. The intention here should be to ensure the generality. (I don't fully understand the case of automatic variables mentioned in the comment above.)

Anyway, shall we nicely have a addressSpaceCasting() mark here? For reference DataLayout directly, I know CIR uses the MLIR model, which has getAllocaMemorySpace method. It's not public through CIRDataLayout though, probably skipped at that time. This should be a workable path.😉

auto AllocaAddr = builder.createAlloca(
getLoc(E->getSourceRange()), builder.getUInt8PtrTy(),
getLoc(E->getSourceRange()), builder.getUInt8PtrTy(TargetAAS),
builder.getUInt8Ty(), "bi_alloca", SuitableAlignmentInBytes, Size);

// Initialize the allocated buffer if required.
Expand All @@ -855,16 +858,16 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
// default (e.g. in C / C++ auto vars are in the generic address space). At
// the AST level this is handled within CreateTempAlloca et al., but for the
// builtin / dynamic alloca we have to handle it here.
assert(!UnimplementedFeature::addressSpace());
LangAS AAS = getASTAllocaAddressSpace();
LangAS EAS = E->getType()->getPointeeType().getAddressSpace();
if (EAS != AAS) {
assert(false && "Non-default address space for alloca NYI");
// TODO(cir): perform a address space casting from AAS to EAS
assert(!UnimplementedFeature::addressSpaceCasting());
llvm_unreachable("Non-default address space for alloca NYI");
}

// Bitcast the alloca to the expected type.
return RValue::get(
builder.createBitcast(AllocaAddr, builder.getVoidPtrTy()));
builder.createBitcast(AllocaAddr, builder.getVoidPtrTy(TargetAAS)));
}

case Builtin::BI__builtin_add_overflow:
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,8 @@ void CIRGenFunction::initializeVTablePointer(mlir::Location loc,
// Finally, store the address point. Use the same CIR types as the field.
//
// vtable field is derived from `this` pointer, therefore they should be in
// the same addr space.
assert(!UnimplementedFeature::addressSpace());
// the same addr space. while vtable APs should use addr space for globals,
// which we should ensure when creating these ops in CXX ABI.
VTableField = builder.createElementBitCast(loc, VTableField,
VTableAddressPoint.getType());
builder.createStore(loc, VTableAddressPoint, VTableField);
Expand Down Expand Up @@ -1384,7 +1384,7 @@ CIRGenFunction::getAddressOfBaseClass(Address Value,

// Get the base pointer type.
auto BaseValueTy = convertType((PathEnd[-1])->getType());
assert(!UnimplementedFeature::addressSpace());
// TODO(cir): specify addr space in base ptr ty
// auto BasePtrTy = builder.getPointerTo(BaseValueTy);
// QualType DerivedTy = getContext().getRecordType(Derived);
// CharUnits DerivedAlign = CGM.getClassPointerAlignment(Derived);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &D,
Name = getStaticDeclName(*this, D);

mlir::Type LTy = getTypes().convertTypeForMem(Ty);
assert(!UnimplementedFeature::addressSpace());
assert(!UnimplementedFeature::addressSpaceInGlobalVar());

// OpenCL variables in local address space and CUDA shared
// variables cannot have an initializer.
Expand All @@ -491,7 +491,7 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &D,
setGVProperties(GV, &D);

// Make sure the result is of the correct type.
assert(!UnimplementedFeature::addressSpace());
assert(!UnimplementedFeature::addressSpaceCasting());

// Ensure that the static local gets initialized by making sure the parent
// function gets emitted eventually.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ void CIRGenModule::buildGlobalVarDeclInit(const VarDecl *D,
// For example, in the above CUDA code, the static local variable s has a
// "shared" address space qualifier, but the constructor of StructWithCtor
// expects "this" in the "generic" address space.
assert(!UnimplementedFeature::addressSpace());
assert(!UnimplementedFeature::addressSpaceCasting());

if (!T->isReferenceType()) {
bool NeedsDtor =
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ static void buildCatchDispatchBlock(CIRGenFunction &CGF,
assert(typeValue && "fell into catch-all case!");
// Check for address space mismatch: if (typeValue->getType() !=
// argTy)
assert(!UnimplementedFeature::addressSpace());
assert(!UnimplementedFeature::addressSpaceCasting());

bool nextIsEnd = false;
// If this is the last handler, we're at the end, and the next
Expand Down
16 changes: 10 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ static Address buildPointerWithAlignment(const Expr *E,
Addr = CGF.getBuilder().createElementBitCast(
CGF.getLoc(E->getSourceRange()), Addr, ElemTy);
if (CE->getCastKind() == CK_AddressSpaceConversion) {
assert(!UnimplementedFeature::addressSpace());
assert(!UnimplementedFeature::addressSpaceCasting());
llvm_unreachable("NYI");
}
return Addr;
Expand Down Expand Up @@ -1206,23 +1206,21 @@ RValue CIRGenFunction::buildCall(clang::QualType CalleeType,
// some trivial cases].
// That is, in the general case, we should assume that a call through an
// unprototyped function type works like a *non-variadic* call. The way we
// make this work is to cast to the exxact type fo the promoted arguments.
// make this work is to cast to the exact type fo the promoted arguments.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nittiest of picks - if you are already here:

Suggested change
// make this work is to cast to the exact type fo the promoted arguments.
// make this work is to cast to the exact type of the promoted arguments.

//
// Chain calls use the same code path to add the inviisble chain parameter to
// Chain calls use the same code path to add the invisible chain parameter to
// the function type.
if (isa<FunctionNoProtoType>(FnType) || Chain) {
assert(!UnimplementedFeature::chainCalls());
assert(!UnimplementedFeature::addressSpace());
auto CalleeTy = getTypes().GetFunctionType(FnInfo);
// get non-variadic function type
CalleeTy = mlir::cir::FuncType::get(CalleeTy.getInputs(),
CalleeTy.getReturnType(), false);
auto CalleePtrTy =
mlir::cir::PointerType::get(builder.getContext(), CalleeTy);

auto *Fn = Callee.getFunctionPointer();
mlir::Value Addr;
if (auto funcOp = llvm::dyn_cast<mlir::cir::FuncOp>(Fn)) {
assert(!UnimplementedFeature::addressSpaceInGlobalVar());
Addr = builder.create<mlir::cir::GetGlobalOp>(
Comment on lines +1223 to 1224
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would make sense to add an assert here that the address space is the default (0)? Future users may try to understand why some verifier fail downstream from the codegen, and it would be hard to trace it back to this very low-level path in the code :)

getLoc(E->getSourceRange()),
mlir::cir::PointerType::get(builder.getContext(),
Expand All @@ -1232,6 +1230,12 @@ RValue CIRGenFunction::buildCall(clang::QualType CalleeType,
Addr = Fn->getResult(0);
}

// Use the same addr space as Addr
auto CalleePtrAS =
Addr.getType().cast<mlir::cir::PointerType>().getAddrSpace();
auto CalleePtrTy = mlir::cir::PointerType::get(builder.getContext(),
CalleeTy, CalleePtrAS);
Comment on lines +1236 to +1237
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think you can hide the mlir::cir::PointerType::get(builder.getContext(), part in a CIRGenBuilder (or CIRBaseBuilder) method with a name along getPointerTy?

Can you use getPointerTo (from CIRBaseBuilder) instead? Hides some of the horrors ;)


Fn = builder.createBitcast(Addr, CalleePtrTy).getDefiningOp();
Callee.setFunctionPointer(Fn);
}
Expand Down
12 changes: 10 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1414,9 +1414,15 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
case CK_AnyPointerToBlockPointerCast:
case CK_BitCast: {
auto Src = Visit(const_cast<Expr *>(E));
mlir::Type SrcTy = Src.getType();
mlir::Type DstTy = CGF.convertType(DestTy);

assert(!UnimplementedFeature::addressSpace());
if (SrcTy.isa<mlir::cir::PointerType>() &&
DstTy.isa<mlir::cir::PointerType>()) {
assert((SrcTy.cast<mlir::cir::PointerType>().getAddrSpace() ==
DstTy.cast<mlir::cir::PointerType>().getAddrSpace()) &&
"Address-space cast must be used to convert address spaces");
}
if (CGF.SanOpts.has(SanitizerKind::CFIUnrelatedCast)) {
llvm_unreachable("NYI");
}
Expand Down Expand Up @@ -1449,8 +1455,10 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
return CGF.getBuilder().createBitcast(CGF.getLoc(E->getSourceRange()), Src,
DstTy);
}
case CK_AddressSpaceConversion:
case CK_AddressSpaceConversion: {
assert(!UnimplementedFeature::addressSpaceCasting());
llvm_unreachable("NYI");
}
case CK_AtomicToNonAtomic:
llvm_unreachable("NYI");
case CK_NonAtomicToAtomic:
Expand Down
11 changes: 7 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,8 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef MangledName, mlir::Type Ty,
return Entry;
}

// TODO(cir): auto DAddrSpace = GetGlobalVarAddressSpace(D);
// TODO(cir): auto DAddrSpace = getGlobalVarAddressSpace(D);
assert(!UnimplementedFeature::addressSpaceInGlobalVar());
// TODO(cir): do we need to strip pointer casts for Entry?

auto loc = getLoc(D->getSourceRange());
Expand Down Expand Up @@ -809,6 +810,7 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef MangledName, mlir::Type Ty,
}

// TODO(cir): address space cast when needed for DAddrSpace.
assert(!UnimplementedFeature::addressSpaceCasting());
return GV;
}

Expand Down Expand Up @@ -2378,8 +2380,8 @@ mlir::cir::FuncOp CIRGenModule::GetOrCreateCIRFunction(
return F;
}

// TODO(cir): Might need bitcast to different address space.
assert(!UnimplementedFeature::addressSpace());
// TODO(cir): Might need address-space cast to different address space.
assert(!UnimplementedFeature::addressSpaceCasting());
return F;
}

Expand Down Expand Up @@ -2803,9 +2805,10 @@ mlir::cir::GlobalOp CIRGenModule::getOrInsertGlobal(
// If the variable exists but has the wrong type, return a bitcast to the
// right type.
auto GVTy = GV.getSymType();
assert(!UnimplementedFeature::addressSpace());
assert(!UnimplementedFeature::addressSpaceInGlobalVar());
auto PTy = builder.getPointerTo(Ty);

assert(!UnimplementedFeature::addressSpaceCasting());
if (GVTy != PTy)
llvm_unreachable("NYI");

Expand Down
1 change: 0 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ struct CIRGenTypeCache {
// Address spaces are not yet fully supported, but the usage of the default
// alloca address space can be used for now only for comparison with the
// default address space.
assert(!UnimplementedFeature::addressSpace());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you should switch this with addressSpaceCasting :)

assert(ASTAllocaAddressSpace == clang::LangAS::Default);
return ASTAllocaAddressSpace;
}
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,9 +598,9 @@ mlir::Type CIRGenTypes::ConvertType(QualType T) {
const ReferenceType *RTy = cast<ReferenceType>(Ty);
QualType ETy = RTy->getPointeeType();
auto PointeeType = convertTypeForMem(ETy);
// TODO(cir): use Context.getTargetAddressSpace(ETy) on pointer
ResultType =
::mlir::cir::PointerType::get(Builder.getContext(), PointeeType);
ResultType = ::mlir::cir::PointerType::get(
Builder.getContext(), PointeeType,
Context.getTargetAddressSpace(ETy.getAddressSpace()));
assert(ResultType && "Cannot get pointer type?");
break;
}
Expand All @@ -615,9 +615,9 @@ mlir::Type CIRGenTypes::ConvertType(QualType T) {
// if (PointeeType->isVoidTy())
// PointeeType = Builder.getI8Type();

// FIXME: add address specifier to cir::PointerType?
ResultType =
::mlir::cir::PointerType::get(Builder.getContext(), PointeeType);
ResultType = ::mlir::cir::PointerType::get(
Builder.getContext(), PointeeType,
Context.getTargetAddressSpace(ETy.getAddressSpace()));
assert(ResultType && "Cannot get pointer type?");
break;
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ struct UnimplementedFeature {
static bool vectorConstants() { return false; }

// Address space related
static bool addressSpace() { return false; }
static bool addressSpace() { return true; }
static bool addressSpaceInGlobalVar() { return false; }
static bool addressSpaceCasting() { return false; }

// Clang codegen options
static bool strictVTablePointers() { return false; }
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/Dialect/IR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ struct MissingFeatures {
static bool setCallingConv() { return false; }

// Address space related
static bool addressSpace() { return false; }
static bool addressSpace() { return true; }
static bool addressSpaceCasting() { return false; }

// Sanitizers
static bool buildTypeCheck() { return false; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static mlir::Value buildDynamicCastAfterNullCheck(CIRBaseBuilderTy &builder,
auto castInfo = op.getInfo().value();

// TODO(cir): consider address space
assert(!MissingFeatures::addressSpace());
assert(!MissingFeatures::addressSpaceCasting());

auto srcPtr = builder.createBitcast(srcValue, builder.getVoidPtrTy());
auto srcRtti = builder.getConstant(loc, castInfo.getSrcRtti());
Expand Down Expand Up @@ -100,7 +100,7 @@ buildDynamicCastToVoidAfterNullCheck(CIRBaseBuilderTy &builder,
bool vtableUsesRelativeLayout = op.getRelativeLayout();

// TODO(cir): consider address space in this function.
assert(!MissingFeatures::addressSpace());
assert(!MissingFeatures::addressSpaceCasting());

mlir::Type vtableElemTy;
uint64_t vtableElemAlign;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3178,7 +3178,8 @@ void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
mlir::DataLayout &dataLayout) {
converter.addConversion([&](mlir::cir::PointerType type) -> mlir::Type {
// Drop pointee type since LLVM dialect only allows opaque pointers.
return mlir::LLVM::LLVMPointerType::get(type.getContext());
return mlir::LLVM::LLVMPointerType::get(type.getContext(),
type.getAddrSpace());
});
converter.addConversion([&](mlir::cir::ArrayType type) -> mlir::Type {
auto ty = converter.convertType(type.getEltType());
Expand Down
12 changes: 12 additions & 0 deletions clang/test/CIR/CodeGen/address-space.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

// CHECK: cir.func {{@.*foo.*}}(%arg0: !cir.ptr<!s32i>
void foo(int __attribute__((address_space(0))) *arg) {
return;
}

// CHECK: cir.func {{@.*bar.*}}(%arg0: !cir.ptr<!s32i, addrspace(1)>
void bar(int __attribute__((address_space(1))) *arg) {
return;
}
20 changes: 20 additions & 0 deletions clang/test/CIR/Lowering/address-space.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: cir-translate %s -cir-to-llvmir -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM

!s32i = !cir.int<s, 32>

module {
// LLVM: define void @foo(ptr %0)
cir.func @foo(%arg0: !cir.ptr<!s32i>) {
// LLVM-NEXT: alloca ptr,
%0 = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["arg", init] {alignment = 8 : i64}
cir.return
}

// LLVM: define void @bar(ptr addrspace(1) %0)
cir.func @bar(%arg0: !cir.ptr<!s32i, addrspace(1)>) {
// LLVM-NEXT: alloca ptr addrspace(1)
%0 = cir.alloca !cir.ptr<!s32i, addrspace(1)>, !cir.ptr<!cir.ptr<!s32i, addrspace(1)>>, ["arg", init] {alignment = 8 : i64}
cir.return
}
}
Loading