diff --git a/flang/include/flang/Frontend/CodeGenOptions.def b/flang/include/flang/Frontend/CodeGenOptions.def index 72e7bdab12a14..9d03ec88a56b8 100644 --- a/flang/include/flang/Frontend/CodeGenOptions.def +++ b/flang/include/flang/Frontend/CodeGenOptions.def @@ -38,6 +38,7 @@ CODEGENOPT(Underscoring, 1, 1) ENUM_CODEGENOPT(RelocationModel, llvm::Reloc::Model, 3, llvm::Reloc::PIC_) ///< Name of the relocation model to use. ENUM_CODEGENOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, llvm::codegenoptions::NoDebugInfo) ///< Level of debug info to generate ENUM_CODEGENOPT(VecLib, llvm::driver::VectorLibrary, 3, llvm::driver::VectorLibrary::NoLibrary) ///< Vector functions library to use +ENUM_CODEGENOPT(FramePointer, llvm::FramePointerKind, 2, llvm::FramePointerKind::None) ///< Enable the usage of frame pointers #undef CODEGENOPT #undef ENUM_CODEGENOPT diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h index 92bc7246eca70..6970da8698ae8 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -10,6 +10,7 @@ #define FORTRAN_OPTIMIZER_TRANSFORMS_PASSES_H #include "flang/Optimizer/Dialect/FIROps.h" +#include "mlir/Dialect/LLVMIR/LLVMAttrs.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassRegistry.h" #include @@ -83,6 +84,15 @@ std::unique_ptr createVScaleAttrPass(); std::unique_ptr createVScaleAttrPass(std::pair vscaleAttr); +struct FunctionAttrTypes { + mlir::LLVM::framePointerKind::FramePointerKind framePointerKind = + mlir::LLVM::framePointerKind::FramePointerKind::None; +}; + +std::unique_ptr createFunctionAttrPass(); +std::unique_ptr +createFunctionAttrPass(FunctionAttrTypes &functionAttr); + // declarative passes #define GEN_PASS_REGISTRATION #include "flang/Optimizer/Transforms/Passes.h.inc" diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index c3768fd2d689c..e3c45d41f04cc 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -349,4 +349,25 @@ def VScaleAttr : Pass<"vscale-attr", "mlir::func::FuncOp"> { let constructor = "::fir::createVScaleAttrPass()"; } +def FunctionAttr : Pass<"function-attr", "mlir::func::FuncOp"> { + let summary = "Pass that adds function attributes expected at LLVM IR level"; + let description = [{ This feature introduces a general attribute aimed at + customizing function characteristics. + Options include: + Add "frame-pointer" attribute to functions: Set an attribute for the frame + pointer on functions, to avoid saving the frame pointer in a register in + functions where it is unnecessary. This eliminates the need for + instructions to save, establish, and restore frame pointers, while also + freeing up an additional register in numerous functions. However, this + approach can make debugging unfeasible on certain machines. + }]; + let options = [ + Option<"framePointerKind", "frame-pointer", + "mlir::LLVM::framePointerKind::FramePointerKind", + /*default=*/"mlir::LLVM::framePointerKind::FramePointerKind{}", + "frame pointer">, + ]; + let constructor = "::fir::createFunctionAttrPass()"; +} + #endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc index d3e4dc6cd4a24..96d3869cd0939 100644 --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -10,6 +10,7 @@ /// debugging the test tools. This file must be included into the tool. #include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h" +#include "mlir/Dialect/LLVMIR/LLVMAttrs.h" #include "mlir/Pass/PassManager.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "mlir/Transforms/Passes.h" @@ -311,6 +312,20 @@ inline void createDefaultFIRCodeGenPassPipeline( if (config.VScaleMin != 0) pm.addPass(fir::createVScaleAttrPass({config.VScaleMin, config.VScaleMax})); + // Add function attributes + fir::FunctionAttrTypes functionAttrs; + + if (config.FramePointerKind != llvm::FramePointerKind::None) { + if (config.FramePointerKind == llvm::FramePointerKind::NonLeaf) + functionAttrs.framePointerKind = + mlir::LLVM::framePointerKind::FramePointerKind::NonLeaf; + else + functionAttrs.framePointerKind = + mlir::LLVM::framePointerKind::FramePointerKind::All; + + pm.addPass(fir::createFunctionAttrPass(functionAttrs)); + } + fir::addFIRToLLVMPass(pm, config); } diff --git a/flang/include/flang/Tools/CrossToolHelpers.h b/flang/include/flang/Tools/CrossToolHelpers.h index ddec70fa9824c..b346b30b158ae 100644 --- a/flang/include/flang/Tools/CrossToolHelpers.h +++ b/flang/include/flang/Tools/CrossToolHelpers.h @@ -35,6 +35,7 @@ struct MLIRToLLVMPassPipelineConfig { LoopVersioning = opts.LoopVersioning; DebugInfo = opts.getDebugInfo(); AliasAnalysis = opts.AliasAnalysis; + FramePointerKind = opts.getFramePointer(); } llvm::OptimizationLevel OptLevel; ///< optimisation level @@ -44,6 +45,8 @@ struct MLIRToLLVMPassPipelineConfig { bool AliasAnalysis = false; ///< Add TBAA tags to generated LLVMIR llvm::codegenoptions::DebugInfoKind DebugInfo = llvm::codegenoptions::NoDebugInfo; ///< Debug info generation. + llvm::FramePointerKind FramePointerKind = + llvm::FramePointerKind::None; ///< Add frame pointer to functions. unsigned VScaleMin = 0; ///< SVE vector range minimum. unsigned VScaleMax = 0; ///< SVE vector range maximum. }; diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index b3f32bb241d06..8569c068564fa 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -245,6 +245,23 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts, opts.AliasAnalysis = opts.OptimizationLevel > 0; + // -mframe-pointer=none/non-leaf/all option. + if (const llvm::opt::Arg *a = + args.getLastArg(clang::driver::options::OPT_mframe_pointer_EQ)) { + std::optional val = + llvm::StringSwitch>(a->getValue()) + .Case("none", llvm::FramePointerKind::None) + .Case("non-leaf", llvm::FramePointerKind::NonLeaf) + .Case("all", llvm::FramePointerKind::All) + .Default(std::nullopt); + + if (!val.has_value()) { + diags.Report(clang::diag::err_drv_invalid_value) + << a->getAsString(args) << a->getValue(); + } else + opts.setFramePointer(val.value()); + } + for (auto *a : args.filtered(clang::driver::options::OPT_fpass_plugin_EQ)) opts.LLVMPassPlugins.push_back(a->getValue()); diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt index 03b67104a93b5..fc067ad358539 100644 --- a/flang/lib/Optimizer/Transforms/CMakeLists.txt +++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt @@ -20,6 +20,7 @@ add_flang_library(FIRTransforms OMPFunctionFiltering.cpp OMPMarkDeclareTarget.cpp VScaleAttr.cpp + FunctionAttr.cpp DEPENDS FIRDialect diff --git a/flang/lib/Optimizer/Transforms/FunctionAttr.cpp b/flang/lib/Optimizer/Transforms/FunctionAttr.cpp new file mode 100644 index 0000000000000..55b908ba5d861 --- /dev/null +++ b/flang/lib/Optimizer/Transforms/FunctionAttr.cpp @@ -0,0 +1,62 @@ +//===- FunctionAttr.cpp ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +/// \file +/// This is a generic pass for adding attributes to functions. +//===----------------------------------------------------------------------===// +#include "flang/Optimizer/Transforms/Passes.h" +#include "mlir/Dialect/LLVMIR/LLVMAttrs.h" + +namespace fir { +#define GEN_PASS_DECL_FUNCTIONATTR +#define GEN_PASS_DEF_FUNCTIONATTR +#include "flang/Optimizer/Transforms/Passes.h.inc" +} // namespace fir + +#define DEBUG_TYPE "func-attr" + +namespace { + +class FunctionAttrPass : public fir::impl::FunctionAttrBase { +public: + FunctionAttrPass(const fir::FunctionAttrOptions &options) { + framePointerKind = options.framePointerKind; + } + FunctionAttrPass() {} + void runOnOperation() override; +}; + +} // namespace + +void FunctionAttrPass::runOnOperation() { + LLVM_DEBUG(llvm::dbgs() << "=== Begin " DEBUG_TYPE " ===\n"); + mlir::func::FuncOp func = getOperation(); + + LLVM_DEBUG(llvm::dbgs() << "Func-name:" << func.getSymName() << "\n"); + + mlir::MLIRContext *context = &getContext(); + if (framePointerKind != mlir::LLVM::framePointerKind::FramePointerKind::None) + func->setAttr("frame_pointer", mlir::LLVM::FramePointerKindAttr::get( + context, framePointerKind)); + + LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n"); +} + +std::unique_ptr +fir::createFunctionAttrPass(fir::FunctionAttrTypes &functionAttr) { + FunctionAttrOptions opts; + // Frame pointer + opts.framePointerKind = functionAttr.framePointerKind; + + return std::make_unique(opts); +} + +std::unique_ptr fir::createFunctionAttrPass() { + return std::make_unique(); +} diff --git a/flang/test/Driver/frame-pointer-forwarding.f90 b/flang/test/Driver/frame-pointer-forwarding.f90 index fd615987f82f4..751494cc6a601 100644 --- a/flang/test/Driver/frame-pointer-forwarding.f90 +++ b/flang/test/Driver/frame-pointer-forwarding.f90 @@ -1,9 +1,12 @@ ! Test that flang-new forwards -fno-omit-frame-pointer and -fomit-frame-pointer Flang frontend -! RUN: %flang -fno-omit-frame-pointer --target=x86-none-none -fsyntax-only -### %s -o %t 2>&1 | FileCheck %s -! CHECK: "-mframe-pointer=all" +! RUN: %flang --target=aarch64-none-none -fsyntax-only -### %s -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOVALUE +! CHECK-NOVALUE: "-fc1"{{.*}}"-mframe-pointer=non-leaf" + +! RUN: %flang -fomit-frame-pointer --target=aarch64-none-none -fsyntax-only -### %s -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-NONEFP +! CHECK-NONEFP: "-fc1"{{.*}}"-mframe-pointer=none" ! RUN: %flang -fno-omit-frame-pointer --target=aarch64-none-none -fsyntax-only -### %s -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-NONLEAFFP -! CHECK-NONLEAFFP: "-mframe-pointer=non-leaf" +! CHECK-NONLEAFFP: "-fc1"{{.*}}"-mframe-pointer=non-leaf" -! RUN: %flang -fomit-frame-pointer --target=aarch64-none-none -fsyntax-only -### %s -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-NONEFP -! CHECK-NONEFP: "-mframe-pointer=none" +! RUN: %flang -fno-omit-frame-pointer --target=x86-none-none -fsyntax-only -### %s -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-ALLFP +! CHECK-ALLFP: "-fc1"{{.*}}"-mframe-pointer=all" diff --git a/flang/test/Driver/func-attr.f90 b/flang/test/Driver/func-attr.f90 new file mode 100644 index 0000000000000..a29ff21d90374 --- /dev/null +++ b/flang/test/Driver/func-attr.f90 @@ -0,0 +1,23 @@ +! Test that -mframe-pointer can accept only specific values and when given an invalid value, check it raises an error. + +! RUN: %flang_fc1 -triple aarch64-none-none -mframe-pointer=none -emit-llvm -o - %s 2>&1| FileCheck %s --check-prefix=CHECK-NONEFP +! RUN: %flang_fc1 -triple aarch64-none-none -mframe-pointer=non-leaf -emit-llvm -o - %s 2>&1| FileCheck %s --check-prefix=CHECK-NONLEAFFP +! RUN: %flang_fc1 -triple aarch64-none-none -mframe-pointer=all -emit-llvm -o - %s 2>&1| FileCheck %s --check-prefix=CHECK-ALLFP +! RUN: not %flang_fc1 -triple aarch64-none-none -mframe-pointer=wrongval -emit-llvm -o - %s 2>&1| FileCheck %s --check-prefix=CHECK-WRONGVALUEFP + +! CHECK-NONEFP-LABEL: @func_() { + +! CHECK-NONLEAFFP-LABEL: @func_() +! CHECK-NONLEAFFP-SAME: #0 + +! CHECK-ALLFP-LABEL: @func_() +! CHECK-ALLFP-SAME: #0 + +subroutine func +end subroutine func + +! CHECK-NONEFP-NOT: attributes #0 = { "frame-pointer"="{{.*}}" } +! CHECK-NONLEAFFP: attributes #0 = { "frame-pointer"="non-leaf" } +! CHECK-ALLFP: attributes #0 = { "frame-pointer"="all" } + +! CHECK-WRONGVALUEFP:error: invalid value 'wrongval' in '-mframe-pointer=wrongval' diff --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90 index a3ff416f4d779..45b1717d7187d 100644 --- a/flang/test/Driver/mlir-debug-pass-pipeline.f90 +++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90 @@ -82,5 +82,5 @@ ! ALL-NEXT: ExternalNameConversion ! DEBUG-NEXT: AddDebugFoundation ! NO-DEBUG-NOT: AddDebugFoundation -! ALL-NEXT: FIRToLLVMLowering +! ALL: FIRToLLVMLowering ! ALL-NOT: LLVMIRLoweringPass diff --git a/flang/test/Driver/save-mlir-temps.f90 b/flang/test/Driver/save-mlir-temps.f90 index 50bc83030caa9..1c8935fbd7aac 100644 --- a/flang/test/Driver/save-mlir-temps.f90 +++ b/flang/test/Driver/save-mlir-temps.f90 @@ -51,9 +51,9 @@ ! Content to check from the MLIR outputs !-------------------------- ! MLIR-FIR-NOT: llvm.func -! MLIR-FIR: func.func @{{.*}}main() { +! MLIR-FIR: func.func @{{.*}}main(){{.*}}{ ! MLIR-FIR-NOT: func.func -! MLIR-LLVMIR: llvm.func @{{.*}}main() { +! MLIR-LLVMIR: llvm.func @{{.*}}main(){{.*}}{ end program diff --git a/flang/test/Fir/box-offset-codegen.fir b/flang/test/Fir/box-offset-codegen.fir index 600555cd94cea..389ceebcc065c 100644 --- a/flang/test/Fir/box-offset-codegen.fir +++ b/flang/test/Fir/box-offset-codegen.fir @@ -7,7 +7,7 @@ func.func @scalar_addr(%scalar : !fir.ref>>) -> !fir.llvm_ return %addr : !fir.llvm_ptr>> } // CHECK-LABEL: define ptr @scalar_addr( -// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK-SAME: ptr %[[BOX:.*]]){{.*}}{ // CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 0 // CHECK: ret ptr %[[VAL_0]] @@ -16,7 +16,7 @@ func.func @scalar_tdesc(%scalar : !fir.ref>>) -> !fir.llvm return %tdesc : !fir.llvm_ptr>> } // CHECK-LABEL: define ptr @scalar_tdesc( -// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK-SAME: ptr %[[BOX:.*]]){{.*}}{ // CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 7 // CHECK: ret ptr %[[VAL_0]] @@ -25,7 +25,7 @@ func.func @array_addr(%array : !fir.ref>>> } // CHECK-LABEL: define ptr @array_addr( -// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK-SAME: ptr %[[BOX:.*]]){{.*}}{ // CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 0 // CHECK: ret ptr %[[VAL_0]] @@ -34,6 +34,6 @@ func.func @array_tdesc(%array : !fir.ref>> } // CHECK-LABEL: define ptr @array_tdesc( -// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK-SAME: ptr %[[BOX:.*]]){{.*}}{ // CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 8 // CHECK: ret ptr %[[VAL_0]] diff --git a/flang/test/Fir/polymorphic.fir b/flang/test/Fir/polymorphic.fir index ce8e43b0be656..ecdcdeb575310 100644 --- a/flang/test/Fir/polymorphic.fir +++ b/flang/test/Fir/polymorphic.fir @@ -10,7 +10,7 @@ func.func @_QMpolymorphic_testPtest_allocate_unlimited_polymorphic_non_derived() return } -// CHECK-LABEL: define void @_QMpolymorphic_testPtest_allocate_unlimited_polymorphic_non_derived() { +// CHECK-LABEL: define void @_QMpolymorphic_testPtest_allocate_unlimited_polymorphic_non_derived(){{.*}}{ // CHECK: %[[MEM:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } // CHECK: %[[DESC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, i64 1 // CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr null, i64 0, i32 20180515, i8 0, i8 -1, i8 1, i8 1, ptr null, [1 x i64] undef }, ptr %[[MEM]] @@ -87,7 +87,7 @@ func.func @_QMunlimitedPsub1(%arg0: !fir.class> {fir.bindc_na } // CHECK-LABEL: define void @_QMunlimitedPsub1( -// CHECK-SAME: ptr %[[ARRAY:.*]]) { +// CHECK-SAME: ptr %[[ARRAY:.*]]){{.*}}{ // CHECK: %[[BOX:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } // CHECK: %{{.}} = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[ARRAY]], i32 0, i32 7, i32 0, i32 2 // CHECK: %[[TYPE_DESC_GEP:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[ARRAY]], i32 0, i32 8 @@ -151,7 +151,7 @@ func.func @_QQmain() { return } -// CHECK-LABEL: define void @_QQmain() { +// CHECK-LABEL: define void @_QQmain(){{.*}}{ // CHECK: %[[CLASS_NONE:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } // CHECK: %[[DESC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, i64 1 // CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr @_QMmod1Ea, i64 ptrtoint (ptr getelementptr (%_QMmod1TtK2, ptr null, i32 1) to i64), i32 20180515, i8 0, i8 42, i8 1, i8 1, ptr @_QMmod1E.dt.t.2, [1 x i64] undef }, ptr %[[CLASS_NONE]], align 8 @@ -175,7 +175,7 @@ func.func @_QMmod2Pinitp(%arg0: !fir.ref>> {fir.bindc_ func.func private @_FortranAPointerAssociate(!fir.ref>, !fir.box) -> none attributes {fir.runtime} // CHECK-LABEL: define void @_QMmod2Pinitp( -// CHECK-SAME: ptr %[[ARG0:.*]]) { +// CHECK-SAME: ptr %[[ARG0:.*]]){{.*}}{ // CHECK: %[[ALLOCA_CLASS_NONE:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } // CHECK: %[[LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[ARG0]] // CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD]], ptr %[[ALLOCA_CLASS_NONE]] diff --git a/flang/test/Fir/tbaa-codegen.fir b/flang/test/Fir/tbaa-codegen.fir index fd0eb9c7304ee..87bb15c0fea6c 100644 --- a/flang/test/Fir/tbaa-codegen.fir +++ b/flang/test/Fir/tbaa-codegen.fir @@ -28,7 +28,7 @@ module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.targ } // CHECK-LABEL: define void @_QPsimple( -// CHECK-SAME: ptr %[[ARG0:.*]]) { +// CHECK-SAME: ptr %[[ARG0:.*]]){{.*}}{ // [...] // load a(2): // CHECK: %[[VAL20:.*]] = getelementptr i8, ptr %{{.*}}, i64 %{{.*}} diff --git a/flang/test/Fir/tbaa-codegen2.fir b/flang/test/Fir/tbaa-codegen2.fir index d73a7b96a5386..e649c06731c6b 100644 --- a/flang/test/Fir/tbaa-codegen2.fir +++ b/flang/test/Fir/tbaa-codegen2.fir @@ -60,7 +60,7 @@ module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.targ } } // CHECK-LABEL: define void @_QPfunc( -// CHECK-SAME: ptr %[[ARG0:.*]]) { +// CHECK-SAME: ptr %[[ARG0:.*]]){{.*}}{ // [...] // CHECK: %[[VAL5:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %[[ARG0]], i32 0, i32 7, i32 0, i32 0 // box access: