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

[flang] Pass to add frame pointer attribute #74598

Merged
merged 17 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions flang/include/flang/Frontend/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 10 additions & 0 deletions flang/include/flang/Optimizer/Transforms/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <memory>
Expand Down Expand Up @@ -83,6 +84,15 @@ std::unique_ptr<mlir::Pass> createVScaleAttrPass();
std::unique_ptr<mlir::Pass>
createVScaleAttrPass(std::pair<unsigned, unsigned> vscaleAttr);

struct FunctionAttrTypes {
mlir::LLVM::framePointerKind::FramePointerKind framePointerKind =
mlir::LLVM::framePointerKind::FramePointerKind::None;
};

std::unique_ptr<mlir::Pass> createFunctionAttrPass();
std::unique_ptr<mlir::Pass>
createFunctionAttrPass(FunctionAttrTypes &functionAttr);

// declarative passes
#define GEN_PASS_REGISTRATION
#include "flang/Optimizer/Transforms/Passes.h.inc"
Expand Down
21 changes: 21 additions & 0 deletions flang/include/flang/Optimizer/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 15 additions & 0 deletions flang/include/flang/Tools/CLOptions.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}

Expand Down
3 changes: 3 additions & 0 deletions flang/include/flang/Tools/CrossToolHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct MLIRToLLVMPassPipelineConfig {
LoopVersioning = opts.LoopVersioning;
DebugInfo = opts.getDebugInfo();
AliasAnalysis = opts.AliasAnalysis;
FramePointerKind = opts.getFramePointer();
}

llvm::OptimizationLevel OptLevel; ///< optimisation level
Expand All @@ -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.
};
Expand Down
17 changes: 17 additions & 0 deletions flang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<llvm::FramePointerKind> val =
llvm::StringSwitch<std::optional<llvm::FramePointerKind>>(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());

Expand Down
1 change: 1 addition & 0 deletions flang/lib/Optimizer/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_flang_library(FIRTransforms
OMPFunctionFiltering.cpp
OMPMarkDeclareTarget.cpp
VScaleAttr.cpp
FunctionAttr.cpp

DEPENDS
FIRDialect
Expand Down
62 changes: 62 additions & 0 deletions flang/lib/Optimizer/Transforms/FunctionAttr.cpp
Original file line number Diff line number Diff line change
@@ -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
Radu2k marked this conversation as resolved.
Show resolved Hide resolved
#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<FunctionAttrPass> {
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<mlir::Pass>
fir::createFunctionAttrPass(fir::FunctionAttrTypes &functionAttr) {
FunctionAttrOptions opts;
// Frame pointer
opts.framePointerKind = functionAttr.framePointerKind;

return std::make_unique<FunctionAttrPass>(opts);
}

std::unique_ptr<mlir::Pass> fir::createFunctionAttrPass() {
return std::make_unique<FunctionAttrPass>();
}
13 changes: 8 additions & 5 deletions flang/test/Driver/frame-pointer-forwarding.f90
Original file line number Diff line number Diff line change
@@ -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"
23 changes: 23 additions & 0 deletions flang/test/Driver/func-attr.f90
Original file line number Diff line number Diff line change
@@ -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'
2 changes: 1 addition & 1 deletion flang/test/Driver/mlir-debug-pass-pipeline.f90
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,5 @@
! ALL-NEXT: ExternalNameConversion
! DEBUG-NEXT: AddDebugFoundation
! NO-DEBUG-NOT: AddDebugFoundation
! ALL-NEXT: FIRToLLVMLowering
! ALL: FIRToLLVMLowering
! ALL-NOT: LLVMIRLoweringPass
4 changes: 2 additions & 2 deletions flang/test/Driver/save-mlir-temps.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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(){{.*}}{
Copy link
Contributor

Choose a reason for hiding this comment

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

What do these extra braces capture?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was added due to failing the test when it was a default parameter. I do not see how having {{.*}} instead of a whitespace should be an issue if no parameters are checked.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain why it was failing? I just don't see how this particular change is related to what this PR is implementing. It would help if you included code "before" and "after" this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this is target dependent, frame pointer will be added or not. It is either adding {{.*}} or adding -fomit-frame-pointer.


! MLIR-FIR-NOT: func.func
! MLIR-LLVMIR: llvm.func @{{.*}}main() {
! MLIR-LLVMIR: llvm.func @{{.*}}main(){{.*}}{

end program
8 changes: 4 additions & 4 deletions flang/test/Fir/box-offset-codegen.fir
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ func.func @scalar_addr(%scalar : !fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_
return %addr : !fir.llvm_ptr<!fir.ref<!fir.type<t>>>
}
// 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]]

Expand All @@ -16,7 +16,7 @@ func.func @scalar_tdesc(%scalar : !fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm
return %tdesc : !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
}
// 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]]

Expand All @@ -25,7 +25,7 @@ func.func @array_addr(%array : !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.ty
return %addr : !fir.llvm_ptr<!fir.ptr<!fir.array<?x!fir.type<t>>>>
}
// 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]]

Expand All @@ -34,6 +34,6 @@ func.func @array_tdesc(%array : !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.t
return %tdesc : !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
}
// 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]]
8 changes: 4 additions & 4 deletions flang/test/Fir/polymorphic.fir
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down Expand Up @@ -87,7 +87,7 @@ func.func @_QMunlimitedPsub1(%arg0: !fir.class<!fir.array<?xnone>> {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
Expand Down Expand Up @@ -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
Expand All @@ -175,7 +175,7 @@ func.func @_QMmod2Pinitp(%arg0: !fir.ref<!fir.class<!fir.ptr<none>>> {fir.bindc_
func.func private @_FortranAPointerAssociate(!fir.ref<!fir.box<none>>, !fir.box<none>) -> 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]]
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Fir/tbaa-codegen.fir
Original file line number Diff line number Diff line change
Expand Up @@ -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 %{{.*}}
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Fir/tbaa-codegen2.fir
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down