[mlir][LLVMIR] Parse some type attributes for LLVM function parameters

With the transition to opaque pointers, type information has been
transferred to function parameter attributes. This patch adds correct
parsing for some of those arguments and fixes some tests, that
previously used UnitAttr for those.

Differential Revision: https://reviews.llvm.org/D132366
This commit is contained in:
Alexander Batashev 2022-08-22 13:12:31 +03:00
parent 5218a542ac
commit 79c2094881
14 changed files with 204 additions and 41 deletions

View File

@ -756,7 +756,7 @@ public:
} }
inline bool functionArgIsSRet(unsigned index, mlir::func::FuncOp func) { inline bool functionArgIsSRet(unsigned index, mlir::func::FuncOp func) {
if (auto attr = func.getArgAttrOfType<mlir::UnitAttr>(index, "llvm.sret")) if (auto attr = func.getArgAttrOfType<mlir::TypeAttr>(index, "llvm.sret"))
return true; return true;
return false; return false;
} }
@ -782,16 +782,22 @@ public:
if (auto align = attr.getAlignment()) if (auto align = attr.getAlignment())
fixups.emplace_back( fixups.emplace_back(
FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::func::FuncOp func) { FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::func::FuncOp func) {
func.setArgAttr(argNo, "llvm.sret", rewriter->getUnitAttr()); auto elemType = fir::dyn_cast_ptrOrBoxEleTy(
func.getFunctionType().getInput(argNo));
func.setArgAttr(argNo, "llvm.sret",
mlir::TypeAttr::get(elemType));
func.setArgAttr(argNo, "llvm.align", func.setArgAttr(argNo, "llvm.align",
rewriter->getIntegerAttr( rewriter->getIntegerAttr(
rewriter->getIntegerType(32), align)); rewriter->getIntegerType(32), align));
}); });
else else
fixups.emplace_back( fixups.emplace_back(FixupTy::Codes::ReturnAsStore, argNo,
FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::func::FuncOp func) { [=](mlir::func::FuncOp func) {
func.setArgAttr(argNo, "llvm.sret", rewriter->getUnitAttr()); auto elemType = fir::dyn_cast_ptrOrBoxEleTy(
}); func.getFunctionType().getInput(argNo));
func.setArgAttr(argNo, "llvm.sret",
mlir::TypeAttr::get(elemType));
});
newInTys.push_back(argTy); newInTys.push_back(argTy);
return; return;
} else { } else {
@ -833,7 +839,10 @@ public:
fixups.emplace_back( fixups.emplace_back(
FixupTy::Codes::ArgumentAsLoad, argNo, FixupTy::Codes::ArgumentAsLoad, argNo,
[=](mlir::func::FuncOp func) { [=](mlir::func::FuncOp func) {
func.setArgAttr(argNo, "llvm.byval", rewriter->getUnitAttr()); auto elemType = fir::dyn_cast_ptrOrBoxEleTy(
func.getFunctionType().getInput(argNo));
func.setArgAttr(argNo, "llvm.byval",
mlir::TypeAttr::get(elemType));
func.setArgAttr(argNo, "llvm.align", func.setArgAttr(argNo, "llvm.align",
rewriter->getIntegerAttr( rewriter->getIntegerAttr(
rewriter->getIntegerType(32), align)); rewriter->getIntegerType(32), align));
@ -841,8 +850,10 @@ public:
else else
fixups.emplace_back(FixupTy::Codes::ArgumentAsLoad, newInTys.size(), fixups.emplace_back(FixupTy::Codes::ArgumentAsLoad, newInTys.size(),
[=](mlir::func::FuncOp func) { [=](mlir::func::FuncOp func) {
auto elemType = fir::dyn_cast_ptrOrBoxEleTy(
func.getFunctionType().getInput(argNo));
func.setArgAttr(argNo, "llvm.byval", func.setArgAttr(argNo, "llvm.byval",
rewriter->getUnitAttr()); mlir::TypeAttr::get(elemType));
}); });
} else { } else {
if (auto align = attr.getAlignment()) if (auto align = attr.getAlignment())

View File

@ -16,7 +16,7 @@ func.func @_QFPf(%arg0: !fir.ref<tuple<!fir.ref<i32>>> {fir.host_assoc}) -> !fir
} }
// CHECK-LABEL: func.func @_QFPf // CHECK-LABEL: func.func @_QFPf
// CHECK-SAME: %{{.*}}: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.sret}, %arg1: !fir.ref<tuple<!fir.ref<i32>>> {fir.host_assoc, llvm.nest}) { // CHECK-SAME: %{{.*}}: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.sret = tuple<!fir.real<16>, !fir.real<16>>}, %arg1: !fir.ref<tuple<!fir.ref<i32>>> {fir.host_assoc, llvm.nest}) {
// ----- // -----

View File

@ -27,10 +27,10 @@ func.func @boxcharparams(%arg0 : !fir.boxchar<1>, %arg1 : !fir.boxchar<1>) -> i6
// Test that we rewrite the signatures and bodies of functions that return a // Test that we rewrite the signatures and bodies of functions that return a
// boxchar. // boxchar.
// INT32-LABEL: @boxcharsret // INT32-LABEL: @boxcharsret
// INT32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>> {llvm.sret}, [[ARG1:%[0-9A-Za-z]+]]: i32, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG3:%[0-9A-Za-z]+]]: i32) // INT32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>> {llvm.sret = !fir.char<1,?>}, [[ARG1:%[0-9A-Za-z]+]]: i32, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG3:%[0-9A-Za-z]+]]: i32)
// INT64-LABEL: @boxcharsret // INT64-LABEL: @boxcharsret
// INT64-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>> {llvm.sret}, [[ARG1:%[0-9A-Za-z]+]]: i64, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG3:%[0-9A-Za-z]+]]: i64) // INT64-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>> {llvm.sret = !fir.char<1,?>}, [[ARG1:%[0-9A-Za-z]+]]: i64, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG3:%[0-9A-Za-z]+]]: i64)
func.func @boxcharsret(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>) { func.func @boxcharsret(%arg0 : !fir.boxchar<1> {llvm.sret = !fir.char<1,?>}, %arg1 : !fir.boxchar<1>) {
// INT32-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG1]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1> // INT32-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG1]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1>
// INT32-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG2]], [[ARG3]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1> // INT32-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG2]], [[ARG3]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1>
// INT32-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64) // INT32-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
@ -57,10 +57,10 @@ func.func @boxcharsret(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar
// Test that we rewrite the signatures of functions with a sret parameter and // Test that we rewrite the signatures of functions with a sret parameter and
// several other parameters. // several other parameters.
// INT32-LABEL: @boxcharmultiple // INT32-LABEL: @boxcharmultiple
// INT32-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>> {llvm.sret}, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: i32) // INT32-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>> {llvm.sret = !fir.char<1,?>}, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: i32)
// INT64-LABEL: @boxcharmultiple // INT64-LABEL: @boxcharmultiple
// INT64-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>> {llvm.sret}, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: i64) // INT64-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>> {llvm.sret = !fir.char<1,?>}, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: i64)
func.func @boxcharmultiple(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>, %arg2 : !fir.boxchar<1>) { func.func @boxcharmultiple(%arg0 : !fir.boxchar<1> {llvm.sret = !fir.char<1,?>}, %arg1 : !fir.boxchar<1>, %arg2 : !fir.boxchar<1>) {
return return
} }

View File

@ -53,7 +53,7 @@ func.func @returncomplex4() -> !fir.complex<4> {
// Test that we rewrite the signature and body of a function that returns a // Test that we rewrite the signature and body of a function that returns a
// complex<8>. // complex<8>.
// I32-LABEL:func @returncomplex8 // I32-LABEL:func @returncomplex8
// I32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<8>, !fir.real<8>>> {llvm.align = 4 : i32, llvm.sret}) // I32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<8>, !fir.real<8>>> {llvm.align = 4 : i32, llvm.sret = tuple<!fir.real<8>, !fir.real<8>>})
// X64-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>> // X64-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>>
// AARCH64-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>> // AARCH64-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>>
// PPC-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>> // PPC-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>>
@ -96,7 +96,7 @@ func.func @returncomplex8() -> !fir.complex<8> {
} }
// Test that we rewrite the signature of a function that accepts a complex<4>. // Test that we rewrite the signature of a function that accepts a complex<4>.
// I32-LABEL: func private @paramcomplex4(!fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}) // I32-LABEL: func private @paramcomplex4(!fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>})
// X64-LABEL: func private @paramcomplex4(!fir.vector<2:!fir.real<4>>) // X64-LABEL: func private @paramcomplex4(!fir.vector<2:!fir.real<4>>)
// AARCH64-LABEL: func private @paramcomplex4(!fir.array<2x!fir.real<4>>) // AARCH64-LABEL: func private @paramcomplex4(!fir.array<2x!fir.real<4>>)
// PPC-LABEL: func private @paramcomplex4(!fir.real<4>, !fir.real<4>) // PPC-LABEL: func private @paramcomplex4(!fir.real<4>, !fir.real<4>)
@ -156,7 +156,7 @@ func.func @callcomplex4() {
} }
// Test that we rewrite the signature of a function that accepts a complex<8>. // Test that we rewrite the signature of a function that accepts a complex<8>.
// I32-LABEL: func private @paramcomplex8(!fir.ref<tuple<!fir.real<8>, !fir.real<8>>> {llvm.align = 4 : i32, llvm.byval}) // I32-LABEL: func private @paramcomplex8(!fir.ref<tuple<!fir.real<8>, !fir.real<8>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<8>, !fir.real<8>>})
// X64-LABEL: func private @paramcomplex8(!fir.real<8>, !fir.real<8>) // X64-LABEL: func private @paramcomplex8(!fir.real<8>, !fir.real<8>)
// AARCH64-LABEL: func private @paramcomplex8(!fir.array<2x!fir.real<8>>) // AARCH64-LABEL: func private @paramcomplex8(!fir.array<2x!fir.real<8>>)
// PPC-LABEL: func private @paramcomplex8(!fir.real<8>, !fir.real<8>) // PPC-LABEL: func private @paramcomplex8(!fir.real<8>, !fir.real<8>)
@ -212,14 +212,14 @@ func.func @callcomplex8() {
} }
// Test multiple complex<4> parameters and arguments // Test multiple complex<4> parameters and arguments
// I32-LABEL: func private @calleemultipleparamscomplex4(!fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}) // I32-LABEL: func private @calleemultipleparamscomplex4(!fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>}, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>}, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>})
// X64-LABEL: func private @calleemultipleparamscomplex4(!fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>) // X64-LABEL: func private @calleemultipleparamscomplex4(!fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>)
// AARCH64-LABEL: func private @calleemultipleparamscomplex4(!fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>) // AARCH64-LABEL: func private @calleemultipleparamscomplex4(!fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>)
// PPC-LABEL: func private @calleemultipleparamscomplex4(!fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>) // PPC-LABEL: func private @calleemultipleparamscomplex4(!fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>)
func.func private @calleemultipleparamscomplex4(!fir.complex<4>, !fir.complex<4>, !fir.complex<4>) -> () func.func private @calleemultipleparamscomplex4(!fir.complex<4>, !fir.complex<4>, !fir.complex<4>) -> ()
// I32-LABEL: func @multipleparamscomplex4 // I32-LABEL: func @multipleparamscomplex4
// I32-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, [[Z3:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}) // I32-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>}, [[Z3:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval = tuple<!fir.real<4>, !fir.real<4>>})
// X64-LABEL: func @multipleparamscomplex4 // X64-LABEL: func @multipleparamscomplex4
// X64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>, [[Z2:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>, [[Z3:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>) // X64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>, [[Z2:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>, [[Z3:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>)
// AARCH64-LABEL: func @multipleparamscomplex4 // AARCH64-LABEL: func @multipleparamscomplex4
@ -329,7 +329,7 @@ func.func @multipleparamscomplex4(%z1 : !fir.complex<4>, %z2 : !fir.complex<4>,
// and returns MLIR complex<f32>. // and returns MLIR complex<f32>.
// I32-LABEL: func private @mlircomplexf32 // I32-LABEL: func private @mlircomplexf32
// I32-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 4 : i32, llvm.byval}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 4 : i32, llvm.byval}) // I32-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 4 : i32, llvm.byval = tuple<f32, f32>}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 4 : i32, llvm.byval = tuple<f32, f32>})
// I32-SAME: -> i64 // I32-SAME: -> i64
// X64-LABEL: func private @mlircomplexf32 // X64-LABEL: func private @mlircomplexf32
// X64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.vector<2:f32>, [[Z2:%[0-9A-Za-z]+]]: !fir.vector<2:f32>) // X64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.vector<2:f32>, [[Z2:%[0-9A-Za-z]+]]: !fir.vector<2:f32>)

View File

@ -48,7 +48,7 @@ func.func @addrof() {
} }
// CHECK-LABEL: func.func @returncomplex16( // CHECK-LABEL: func.func @returncomplex16(
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.sret}) { // CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.sret = tuple<!fir.real<16>, !fir.real<16>>}) {
// CHECK: %[[VAL_1:.*]] = fir.undefined !fir.complex<16> // CHECK: %[[VAL_1:.*]] = fir.undefined !fir.complex<16>
// CHECK: %[[VAL_2:.*]] = arith.constant 2.000000e+00 : f128 // CHECK: %[[VAL_2:.*]] = arith.constant 2.000000e+00 : f128
// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (f128) -> !fir.real<16> // CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (f128) -> !fir.real<16>
@ -61,7 +61,7 @@ func.func @addrof() {
// CHECK: fir.store %[[VAL_8]] to %[[VAL_9]] : !fir.ref<!fir.complex<16>> // CHECK: fir.store %[[VAL_8]] to %[[VAL_9]] : !fir.ref<!fir.complex<16>>
// CHECK: return // CHECK: return
// CHECK: } // CHECK: }
// CHECK: func.func private @paramcomplex16(!fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}) // CHECK: func.func private @paramcomplex16(!fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>})
// CHECK-LABEL: func.func @callcomplex16() { // CHECK-LABEL: func.func @callcomplex16() {
// CHECK: %[[VAL_0:.*]] = fir.alloca tuple<!fir.real<16>, !fir.real<16>> // CHECK: %[[VAL_0:.*]] = fir.alloca tuple<!fir.real<16>, !fir.real<16>>
@ -74,10 +74,10 @@ func.func @addrof() {
// CHECK: fir.call @paramcomplex16(%[[VAL_4]]) : (!fir.ref<tuple<!fir.real<16>, !fir.real<16>>>) -> () // CHECK: fir.call @paramcomplex16(%[[VAL_4]]) : (!fir.ref<tuple<!fir.real<16>, !fir.real<16>>>) -> ()
// CHECK: return // CHECK: return
// CHECK: } // CHECK: }
// CHECK: func.func private @calleemultipleparamscomplex16(!fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}) // CHECK: func.func private @calleemultipleparamscomplex16(!fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>}, !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>}, !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>})
// CHECK-LABEL: func.func @multipleparamscomplex16( // CHECK-LABEL: func.func @multipleparamscomplex16(
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, %[[VAL_1:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, %[[VAL_2:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}) { // CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>}, %[[VAL_1:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>}, %[[VAL_2:.*]]: !fir.ref<tuple<!fir.real<16>, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval = tuple<!fir.real<16>, !fir.real<16>>}) {
// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<tuple<!fir.real<16>, !fir.real<16>>>) -> !fir.ref<!fir.complex<16>> // CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<tuple<!fir.real<16>, !fir.real<16>>>) -> !fir.ref<!fir.complex<16>>
// CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.complex<16>> // CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.complex<16>>
// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<tuple<!fir.real<16>, !fir.real<16>>>) -> !fir.ref<!fir.complex<16>> // CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<tuple<!fir.real<16>, !fir.real<16>>>) -> !fir.ref<!fir.complex<16>>
@ -98,7 +98,7 @@ func.func @addrof() {
// CHECK: } // CHECK: }
// CHECK-LABEL: func.func private @mlircomplexf128( // CHECK-LABEL: func.func private @mlircomplexf128(
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<tuple<f128, f128>> {llvm.align = 16 : i32, llvm.sret}, %[[VAL_1:.*]]: !fir.ref<tuple<f128, f128>> {llvm.align = 16 : i32, llvm.byval}, %[[VAL_2:.*]]: !fir.ref<tuple<f128, f128>> {llvm.align = 16 : i32, llvm.byval}) { // CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<tuple<f128, f128>> {llvm.align = 16 : i32, llvm.sret = tuple<f128, f128>}, %[[VAL_1:.*]]: !fir.ref<tuple<f128, f128>> {llvm.align = 16 : i32, llvm.byval = tuple<f128, f128>}, %[[VAL_2:.*]]: !fir.ref<tuple<f128, f128>> {llvm.align = 16 : i32, llvm.byval = tuple<f128, f128>}) {
// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<tuple<f128, f128>>) -> !fir.ref<complex<f128>> // CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<tuple<f128, f128>>) -> !fir.ref<complex<f128>>
// CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3]] : !fir.ref<complex<f128>> // CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3]] : !fir.ref<complex<f128>>
// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<tuple<f128, f128>>) -> !fir.ref<complex<f128>> // CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<tuple<f128, f128>>) -> !fir.ref<complex<f128>>

View File

@ -115,7 +115,7 @@ func.func @char1lensum(%arg0 : !fir.boxchar<1>, %arg1 : !fir.boxchar<1>) -> i64
// I32-LABEL: define void @char1copy(ptr sret(i8) %0, i32 %1, ptr %2, i32 %3) // I32-LABEL: define void @char1copy(ptr sret(i8) %0, i32 %1, ptr %2, i32 %3)
// I64-LABEL: define void @char1copy(ptr sret(i8) %0, i64 %1, ptr %2, i64 %3) // I64-LABEL: define void @char1copy(ptr sret(i8) %0, i64 %1, ptr %2, i64 %3)
// PPC-LABEL: define void @char1copy(ptr sret(i8) %0, i64 %1, ptr %2, i64 %3) // PPC-LABEL: define void @char1copy(ptr sret(i8) %0, i64 %1, ptr %2, i64 %3)
func.func @char1copy(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>) { func.func @char1copy(%arg0 : !fir.boxchar<1> {llvm.sret = !fir.char<1, ?>}, %arg1 : !fir.boxchar<1>) {
// I32-DAG: %[[p0:.*]] = insertvalue { ptr, i32 } undef, ptr %2, 0 // I32-DAG: %[[p0:.*]] = insertvalue { ptr, i32 } undef, ptr %2, 0
// I32-DAG: = insertvalue { ptr, i32 } %[[p0]], i32 %3, 1 // I32-DAG: = insertvalue { ptr, i32 } %[[p0]], i32 %3, 1
// I32-DAG: %[[p1:.*]] = insertvalue { ptr, i32 } undef, ptr %0, 0 // I32-DAG: %[[p1:.*]] = insertvalue { ptr, i32 } undef, ptr %0, 0

View File

@ -45,6 +45,10 @@ def LLVM_Dialect : Dialect {
static StringRef getLoopOptionsAttrName() { return "options"; } static StringRef getLoopOptionsAttrName() { return "options"; }
static StringRef getAccessGroupsAttrName() { return "access_groups"; } static StringRef getAccessGroupsAttrName() { return "access_groups"; }
static StringRef getStructAttrsAttrName() { return "llvm.struct_attrs"; } static StringRef getStructAttrsAttrName() { return "llvm.struct_attrs"; }
static StringRef getByValAttrName() { return "llvm.byval"; }
static StringRef getByRefAttrName() { return "llvm.byref"; }
static StringRef getStructRetAttrName() { return "llvm.sret"; }
static StringRef getInAllocaAttrName() { return "llvm.inalloca"; }
/// Verifies if the attribute is a well-formed value for "llvm.struct_attrs" /// Verifies if the attribute is a well-formed value for "llvm.struct_attrs"
static LogicalResult verifyStructAttr( static LogicalResult verifyStructAttr(

View File

@ -23,10 +23,13 @@
#include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h" #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
#include "mlir/Dialect/Utils/StaticValueUtils.h" #include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/IR/Attributes.h" #include "mlir/IR/Attributes.h"
#include "mlir/IR/BlockAndValueMapping.h" #include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h" #include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributeInterfaces.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/PatternMatch.h" #include "mlir/IR/PatternMatch.h"
#include "mlir/IR/TypeUtilities.h" #include "mlir/IR/TypeUtilities.h"
@ -34,6 +37,7 @@
#include "mlir/Support/MathExtras.h" #include "mlir/Support/MathExtras.h"
#include "mlir/Transforms/DialectConversion.h" #include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/Passes.h" #include "mlir/Transforms/Passes.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TypeSwitch.h" #include "llvm/ADT/TypeSwitch.h"
#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IRBuilder.h"
@ -311,10 +315,42 @@ protected:
SmallVector<Attribute, 4> newArgAttrs( SmallVector<Attribute, 4> newArgAttrs(
llvmType.cast<LLVM::LLVMFunctionType>().getNumParams()); llvmType.cast<LLVM::LLVMFunctionType>().getNumParams());
for (unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) { for (unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) {
// Some LLVM IR attribute have a type attached to them. During FuncOp ->
// LLVMFuncOp conversion these types may have changed. Account for that
// change by converting attributes' types as well.
SmallVector<NamedAttribute, 4> convertedAttrs;
auto attrsDict = argAttrDicts[i].cast<DictionaryAttr>();
convertedAttrs.reserve(attrsDict.size());
for (const NamedAttribute &attr : attrsDict) {
const auto convert = [&](const NamedAttribute &attr) {
return TypeAttr::get(getTypeConverter()->convertType(
attr.getValue().cast<TypeAttr>().getValue()));
};
if (attr.getName().getValue() ==
LLVM::LLVMDialect::getByValAttrName()) {
convertedAttrs.push_back(rewriter.getNamedAttr(
LLVM::LLVMDialect::getByValAttrName(), convert(attr)));
} else if (attr.getName().getValue() ==
LLVM::LLVMDialect::getByRefAttrName()) {
convertedAttrs.push_back(rewriter.getNamedAttr(
LLVM::LLVMDialect::getByRefAttrName(), convert(attr)));
} else if (attr.getName().getValue() ==
LLVM::LLVMDialect::getStructRetAttrName()) {
convertedAttrs.push_back(rewriter.getNamedAttr(
LLVM::LLVMDialect::getStructRetAttrName(), convert(attr)));
} else if (attr.getName().getValue() ==
LLVM::LLVMDialect::getInAllocaAttrName()) {
convertedAttrs.push_back(rewriter.getNamedAttr(
LLVM::LLVMDialect::getInAllocaAttrName(), convert(attr)));
} else {
convertedAttrs.push_back(attr);
}
}
auto mapping = result.getInputMapping(i); auto mapping = result.getInputMapping(i);
assert(mapping && "unexpected deletion of function argument"); assert(mapping && "unexpected deletion of function argument");
for (size_t j = 0; j < mapping->size; ++j) for (size_t j = 0; j < mapping->size; ++j)
newArgAttrs[mapping->inputNo + j] = argAttrDicts[i]; newArgAttrs[mapping->inputNo + j] =
DictionaryAttr::get(rewriter.getContext(), convertedAttrs);
} }
attributes.push_back( attributes.push_back(
rewriter.getNamedAttr(FunctionOpInterface::getArgDictAttrName(), rewriter.getNamedAttr(FunctionOpInterface::getArgDictAttrName(),

View File

@ -1195,6 +1195,36 @@ LogicalResult Importer::processFunction(llvm::Function *f) {
UnknownLoc::get(context), f->getName(), functionType, UnknownLoc::get(context), f->getName(), functionType,
convertLinkageFromLLVM(f->getLinkage()), dsoLocal, cconv); convertLinkageFromLLVM(f->getLinkage()), dsoLocal, cconv);
for (const auto &arg : llvm::enumerate(functionType.getParams())) {
llvm::SmallVector<NamedAttribute, 1> argAttrs;
if (auto *type = f->getParamByValType(arg.index())) {
auto mlirType = processType(type);
argAttrs.push_back(
NamedAttribute(b.getStringAttr(LLVMDialect::getByValAttrName()),
TypeAttr::get(mlirType)));
}
if (auto *type = f->getParamByRefType(arg.index())) {
auto mlirType = processType(type);
argAttrs.push_back(
NamedAttribute(b.getStringAttr(LLVMDialect::getByRefAttrName()),
TypeAttr::get(mlirType)));
}
if (auto *type = f->getParamStructRetType(arg.index())) {
auto mlirType = processType(type);
argAttrs.push_back(
NamedAttribute(b.getStringAttr(LLVMDialect::getStructRetAttrName()),
TypeAttr::get(mlirType)));
}
if (auto *type = f->getParamInAllocaType(arg.index())) {
auto mlirType = processType(type);
argAttrs.push_back(
NamedAttribute(b.getStringAttr(LLVMDialect::getInAllocaAttrName()),
TypeAttr::get(mlirType)));
}
fop.setArgAttrs(arg.index(), argAttrs);
}
if (FlatSymbolRefAttr personality = getPersonalityAsAttr(f)) if (FlatSymbolRefAttr personality = getPersonalityAsAttr(f))
fop->setAttr(b.getStringAttr("personality"), personality); fop->setAttr(b.getStringAttr("personality"), personality);
else if (f->hasPersonalityFn()) else if (f->hasPersonalityFn())

View File

@ -840,23 +840,57 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
.addAlignmentAttr(llvm::Align(attr.getInt()))); .addAlignmentAttr(llvm::Align(attr.getInt())));
} }
if (auto attr = func.getArgAttrOfType<UnitAttr>(argIdx, "llvm.sret")) { if (auto attr = func.getArgAttrOfType<TypeAttr>(
argIdx, LLVMDialect::getStructRetAttrName())) {
auto argTy = mlirArg.getType().dyn_cast<LLVM::LLVMPointerType>(); auto argTy = mlirArg.getType().dyn_cast<LLVM::LLVMPointerType>();
if (!argTy) if (!argTy)
return func.emitError( return func.emitError(
"llvm.sret attribute attached to LLVM non-pointer argument"); "llvm.sret attribute attached to LLVM non-pointer argument");
llvmArg.addAttrs( if (!argTy.isOpaque() && argTy.getElementType() != attr.getValue())
llvm::AttrBuilder(llvmArg.getContext()) return func.emitError("llvm.sret attribute attached to LLVM pointer "
.addStructRetAttr(convertType(argTy.getElementType()))); "argument of a different type");
llvmArg.addAttrs(llvm::AttrBuilder(llvmArg.getContext())
.addStructRetAttr(convertType(attr.getValue())));
} }
if (auto attr = func.getArgAttrOfType<UnitAttr>(argIdx, "llvm.byval")) { if (auto attr = func.getArgAttrOfType<TypeAttr>(
argIdx, LLVMDialect::getByValAttrName())) {
auto argTy = mlirArg.getType().dyn_cast<LLVM::LLVMPointerType>(); auto argTy = mlirArg.getType().dyn_cast<LLVM::LLVMPointerType>();
if (!argTy) if (!argTy)
return func.emitError( return func.emitError(
"llvm.byval attribute attached to LLVM non-pointer argument"); "llvm.byval attribute attached to LLVM non-pointer argument");
if (!argTy.isOpaque() && argTy.getElementType() != attr.getValue())
return func.emitError("llvm.byval attribute attached to LLVM pointer "
"argument of a different type");
llvmArg.addAttrs(llvm::AttrBuilder(llvmArg.getContext()) llvmArg.addAttrs(llvm::AttrBuilder(llvmArg.getContext())
.addByValAttr(convertType(argTy.getElementType()))); .addByValAttr(convertType(attr.getValue())));
}
if (auto attr = func.getArgAttrOfType<TypeAttr>(
argIdx, LLVMDialect::getByRefAttrName())) {
auto argTy = mlirArg.getType().dyn_cast<LLVM::LLVMPointerType>();
if (!argTy)
return func.emitError(
"llvm.byref attribute attached to LLVM non-pointer argument");
if (!argTy.isOpaque() && argTy.getElementType() != attr.getValue())
return func.emitError("llvm.byref attribute attached to LLVM pointer "
"argument of a different type");
llvmArg.addAttrs(llvm::AttrBuilder(llvmArg.getContext())
.addByRefAttr(convertType(attr.getValue())));
}
if (auto attr = func.getArgAttrOfType<TypeAttr>(
argIdx, LLVMDialect::getInAllocaAttrName())) {
auto argTy = mlirArg.getType().dyn_cast<LLVM::LLVMPointerType>();
if (!argTy)
return func.emitError(
"llvm.inalloca attribute attached to LLVM non-pointer argument");
if (!argTy.isOpaque() && argTy.getElementType() != attr.getValue())
return func.emitError(
"llvm.inalloca attribute attached to LLVM pointer "
"argument of a different type");
llvmArg.addAttrs(llvm::AttrBuilder(llvmArg.getContext())
.addInAllocaAttr(convertType(attr.getValue())));
} }
if (auto attr = func.getArgAttrOfType<UnitAttr>(argIdx, "llvm.nest")) { if (auto attr = func.getArgAttrOfType<UnitAttr>(argIdx, "llvm.nest")) {

View File

@ -93,9 +93,9 @@ module {
llvm.return llvm.return
} }
// CHECK: llvm.func @sretattr(%{{.*}}: !llvm.ptr<i32> {llvm.sret}) // CHECK: llvm.func @sretattr(%{{.*}}: !llvm.ptr<i32> {llvm.sret = i32})
// LOCINFO: llvm.func @sretattr(%{{.*}}: !llvm.ptr<i32> {llvm.sret} loc("some_source_loc")) // LOCINFO: llvm.func @sretattr(%{{.*}}: !llvm.ptr<i32> {llvm.sret = i32} loc("some_source_loc"))
llvm.func @sretattr(%arg0: !llvm.ptr<i32> {llvm.sret} loc("some_source_loc")) { llvm.func @sretattr(%arg0: !llvm.ptr<i32> {llvm.sret = i32} loc("some_source_loc")) {
llvm.return llvm.return
} }

View File

@ -0,0 +1,6 @@
; RUN: mlir-translate -import-llvm %s | FileCheck %s
; CHECK: llvm.func @foo(%arg0: !llvm.ptr {llvm.byval = i64}, %arg1: !llvm.ptr {llvm.byref = i64}, %arg2: !llvm.ptr {llvm.sret = i64}, %arg3: !llvm.ptr {llvm.inalloca = i64})
define void @foo(ptr byval(i64) %arg0, ptr byref(i64) %arg1, ptr sret(i64) %arg2, ptr inalloca(i64) %arg3) {
ret void
}

View File

@ -15,12 +15,19 @@ llvm.func @invalid_noalias(%arg0 : f32 {llvm.noalias}) -> f32 {
// ----- // -----
// expected-error @+1 {{llvm.sret attribute attached to LLVM non-pointer argument}} // expected-error @+1 {{llvm.sret attribute attached to LLVM non-pointer argument}}
llvm.func @invalid_sret(%arg0 : f32 {llvm.sret}) -> f32 { llvm.func @invalid_sret(%arg0 : f32 {llvm.sret = f32}) -> f32 {
llvm.return %arg0 : f32 llvm.return %arg0 : f32
} }
// ----- // -----
// expected-error @+1 {{llvm.sret attribute attached to LLVM pointer argument of a different type}}
llvm.func @invalid_sret(%arg0 : !llvm.ptr<f32> {llvm.sret = i32}) -> !llvm.ptr<f32> {
llvm.return %arg0 : !llvm.ptr<f32>
}
// -----
// expected-error @+1 {{llvm.nest attribute attached to LLVM non-pointer argument}} // expected-error @+1 {{llvm.nest attribute attached to LLVM non-pointer argument}}
llvm.func @invalid_nest(%arg0 : f32 {llvm.nest}) -> f32 { llvm.func @invalid_nest(%arg0 : f32 {llvm.nest}) -> f32 {
llvm.return %arg0 : f32 llvm.return %arg0 : f32
@ -28,12 +35,47 @@ llvm.func @invalid_nest(%arg0 : f32 {llvm.nest}) -> f32 {
// ----- // -----
// expected-error @+1 {{llvm.byval attribute attached to LLVM non-pointer argument}} // expected-error @+1 {{llvm.byval attribute attached to LLVM non-pointer argument}}
llvm.func @invalid_byval(%arg0 : f32 {llvm.byval}) -> f32 { llvm.func @invalid_byval(%arg0 : f32 {llvm.byval = f32}) -> f32 {
llvm.return %arg0 : f32 llvm.return %arg0 : f32
} }
// ----- // -----
// expected-error @+1 {{llvm.byval attribute attached to LLVM pointer argument of a different type}}
llvm.func @invalid_sret(%arg0 : !llvm.ptr<f32> {llvm.byval = i32}) -> !llvm.ptr<f32> {
llvm.return %arg0 : !llvm.ptr<f32>
}
// -----
// expected-error @+1 {{llvm.byref attribute attached to LLVM non-pointer argument}}
llvm.func @invalid_byval(%arg0 : f32 {llvm.byref = f32}) -> f32 {
llvm.return %arg0 : f32
}
// -----
// expected-error @+1 {{llvm.byref attribute attached to LLVM pointer argument of a different type}}
llvm.func @invalid_sret(%arg0 : !llvm.ptr<f32> {llvm.byref = i32}) -> !llvm.ptr<f32> {
llvm.return %arg0 : !llvm.ptr<f32>
}
// -----
// expected-error @+1 {{llvm.inalloca attribute attached to LLVM non-pointer argument}}
llvm.func @invalid_byval(%arg0 : f32 {llvm.inalloca = f32}) -> f32 {
llvm.return %arg0 : f32
}
// -----
// expected-error @+1 {{llvm.inalloca attribute attached to LLVM pointer argument of a different type}}
llvm.func @invalid_sret(%arg0 : !llvm.ptr<f32> {llvm.inalloca = i32}) -> !llvm.ptr<f32> {
llvm.return %arg0 : !llvm.ptr<f32>
}
// -----
// expected-error @+1 {{llvm.align attribute attached to LLVM non-pointer argument}} // expected-error @+1 {{llvm.align attribute attached to LLVM non-pointer argument}}
llvm.func @invalid_align(%arg0 : f32 {llvm.align = 4}) -> f32 { llvm.func @invalid_align(%arg0 : f32 {llvm.align = 4}) -> f32 {
llvm.return %arg0 : f32 llvm.return %arg0 : f32

View File

@ -1050,12 +1050,12 @@ llvm.func @llvm_noalias(%arg0: !llvm.ptr<f32> {llvm.noalias}) {
} }
// CHECK-LABEL: define void @byvalattr(ptr byval(i32) % // CHECK-LABEL: define void @byvalattr(ptr byval(i32) %
llvm.func @byvalattr(%arg0: !llvm.ptr<i32> {llvm.byval}) { llvm.func @byvalattr(%arg0: !llvm.ptr<i32> {llvm.byval = i32}) {
llvm.return llvm.return
} }
// CHECK-LABEL: define void @sretattr(ptr sret(i32) % // CHECK-LABEL: define void @sretattr(ptr sret(i32) %
llvm.func @sretattr(%arg0: !llvm.ptr<i32> {llvm.sret}) { llvm.func @sretattr(%arg0: !llvm.ptr<i32> {llvm.sret = i32}) {
llvm.return llvm.return
} }