Add a conversion pass to convert higher-level type before translation.
This conversion extract meangingful information and pack it into a struct that
the translation (D101504) will be able to understand.
Reviewed By: ftynse
Differential Revision: https://reviews.llvm.org/D102170
--- /dev/null
+//===- ConvertOpenACCToLLVM.h - OpenACC conversion pass entrypoint --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+#ifndef MLIR_CONVERSION_OPENACCTOLLVM_CONVERTOPENACCTOLLVM_H
+#define MLIR_CONVERSION_OPENACCTOLLVM_CONVERTOPENACCTOLLVM_H
+
+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
+#include <memory>
+
+namespace mlir {
+class LLVMTypeConverter;
+class ModuleOp;
+template <typename T>
+class OperationPass;
+class RewritePatternSet;
+
+static constexpr unsigned kPtrBasePosInDataDescriptor = 0;
+static constexpr unsigned kPtrPosInDataDescriptor = 1;
+static constexpr unsigned kSizePosInDataDescriptor = 2;
+
+/// Helper class to produce LLVM dialect operations inserting
+/// elements to a Data descriptor. Wraps a Value pointing to the descriptor.
+/// The Value may be null, in which case none of the operations are valid.
+///
+/// The data descriptor holds information needed to perform data operations
+/// and movments with the runtime.
+/// `BasePointer`: base of the pointer being mapped.
+/// `Pointer`: actual pointer of the data being mapped.
+/// `Size`: size of the data being mapped.
+///
+/// Example:
+///
+/// ```c
+/// struct S {
+/// int x;
+/// int y;
+/// };
+/// ```
+///
+/// Mapping `s.y` will result if the following information in the data
+/// descriptor:
+/// - `BasePointer`: address of `s`
+/// - `Pointer`: address of `s.y`
+/// - `Size`: size of `s.y`
+///
+/// For a scalar variable BasePointer and Pointer will be the same.
+class DataDescriptor : public StructBuilder {
+public:
+ /// Construct a helper for the given descriptor value.
+ explicit DataDescriptor(Value descriptor);
+ /// Builds IR creating an `undef` value of the descriptor type.
+ static DataDescriptor undef(OpBuilder &builder, Location loc, Type basePtrTy,
+ Type ptrTy);
+
+ static bool isValid(Value descriptor);
+
+ void setPointer(OpBuilder &builder, Location loc, Value ptr);
+ void setBasePointer(OpBuilder &builder, Location loc, Value basePtr);
+ void setSize(OpBuilder &builder, Location loc, Value size);
+};
+
+/// Collect the patterns to convert from the OpenACC dialect LLVMIR dialect.
+void populateOpenACCToLLVMConversionPatterns(LLVMTypeConverter &converter,
+ RewritePatternSet &patterns);
+
+/// Create a pass to convert the OpenACC dialect into the LLVMIR dialect.
+std::unique_ptr<OperationPass<ModuleOp>> createConvertOpenACCToLLVMPass();
+
+} // namespace mlir
+
+#endif // MLIR_CONVERSION_OPENACCTOLLVM_CONVERTOPENACCTOLLVM_H
#include "mlir/Conversion/LinalgToSPIRV/LinalgToSPIRVPass.h"
#include "mlir/Conversion/LinalgToStandard/LinalgToStandard.h"
#include "mlir/Conversion/MathToLibm/MathToLibm.h"
+#include "mlir/Conversion/OpenACCToLLVM/ConvertOpenACCToLLVM.h"
#include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h"
#include "mlir/Conversion/PDLToPDLInterp/PDLToPDLInterp.h"
#include "mlir/Conversion/SCFToGPU/SCFToGPUPass.h"
}
//===----------------------------------------------------------------------===//
+// OpenACCToLLVM
+//===----------------------------------------------------------------------===//
+
+def ConvertOpenACCToLLVM : Pass<"convert-openacc-to-llvm", "ModuleOp"> {
+ let summary = "Convert the OpenACC ops to LLVM dialect";
+ let constructor = "mlir::createConvertOpenACCToLLVMPass()";
+ let dependentDialects = ["LLVM::LLVMDialect"];
+}
+
+//===----------------------------------------------------------------------===//
// OpenMPToLLVM
//===----------------------------------------------------------------------===//
Variadic<AnyType>:$createZeroOperands,
Variadic<AnyType>:$attachOperands);
+ let extraClassDeclaration = [{
+ /// The number of data operands.
+ unsigned getNumDataOperands();
+
+ /// The i-th data operand passed.
+ Value getDataOperand(unsigned i);
+ }];
+
let assemblyFormat = [{
( `if` `(` $ifCond^ `)` )?
( `async` `(` $asyncOperand^ `:` type($asyncOperand) `)` )?
Variadic<AnyType>:$detachOperands,
UnitAttr:$finalize);
+ let extraClassDeclaration = [{
+ /// The number of data operands.
+ unsigned getNumDataOperands();
+
+ /// The i-th data operand passed.
+ Value getDataOperand(unsigned i);
+ }];
+
let assemblyFormat = [{
( `if` `(` $ifCond^ `)` )?
( `async` `(` $asyncOperand^ `:` type($asyncOperand) `)` )?
Variadic<AnyType>:$deviceOperands,
UnitAttr:$ifPresent);
+ let extraClassDeclaration = [{
+ /// The number of data operands.
+ unsigned getNumDataOperands();
+
+ /// The i-th data operand passed.
+ Value getDataOperand(unsigned i);
+ }];
+
let assemblyFormat = [{
( `if` `(` $ifCond^ `)` )?
( `async` `(` $asyncOperand^ `:` type($asyncOperand) `)` )?
add_subdirectory(LinalgToSPIRV)
add_subdirectory(LinalgToStandard)
add_subdirectory(MathToLibm)
+add_subdirectory(OpenACCToLLVM)
add_subdirectory(OpenMPToLLVM)
add_subdirectory(PDLToPDLInterp)
add_subdirectory(SCFToGPU)
--- /dev/null
+add_mlir_conversion_library(MLIROpenACCToLLVM
+ OpenACCToLLVM.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/OpenACCToLLVM
+
+ DEPENDS
+ MLIRConversionPassIncGen
+
+ LINK_LIBS PUBLIC
+ MLIRIR
+ MLIRLLVMIR
+ MLIROpenACC
+ MLIRStandardToLLVM
+ MLIRTransforms
+ )
--- /dev/null
+//===- OpenACCToLLVM.cpp - Prepare OpenACC data for LLVM translation ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../PassDetail.h"
+#include "mlir/Conversion/OpenACCToLLVM/ConvertOpenACCToLLVM.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+
+using namespace mlir;
+
+//===----------------------------------------------------------------------===//
+// DataDescriptor implementation
+//===----------------------------------------------------------------------===//
+
+constexpr StringRef getStructName() { return "openacc_data"; }
+
+/// Construct a helper for the given descriptor value.
+DataDescriptor::DataDescriptor(Value descriptor) : StructBuilder(descriptor) {
+ assert(value != nullptr && "value cannot be null");
+}
+
+/// Builds IR creating an `undef` value of the data descriptor.
+DataDescriptor DataDescriptor::undef(OpBuilder &builder, Location loc,
+ Type basePtrTy, Type ptrTy) {
+ Type descriptorType = LLVM::LLVMStructType::getNewIdentified(
+ builder.getContext(), getStructName(),
+ {basePtrTy, ptrTy, builder.getI64Type()});
+ Value descriptor = builder.create<LLVM::UndefOp>(loc, descriptorType);
+ return DataDescriptor(descriptor);
+}
+
+/// Check whether the type is a valid data descriptor.
+bool DataDescriptor::isValid(Value descriptor) {
+ if (auto type = descriptor.getType().dyn_cast<LLVM::LLVMStructType>()) {
+ if (type.isIdentified() && type.getName().startswith(getStructName()) &&
+ type.getBody().size() == 3 &&
+ (type.getBody()[kPtrBasePosInDataDescriptor]
+ .isa<LLVM::LLVMPointerType>() ||
+ type.getBody()[kPtrBasePosInDataDescriptor]
+ .isa<LLVM::LLVMStructType>()) &&
+ type.getBody()[kPtrPosInDataDescriptor].isa<LLVM::LLVMPointerType>() &&
+ type.getBody()[kSizePosInDataDescriptor].isInteger(64))
+ return true;
+ }
+ return false;
+}
+
+/// Builds IR inserting the base pointer value into the descriptor.
+void DataDescriptor::setBasePointer(OpBuilder &builder, Location loc,
+ Value basePtr) {
+ setPtr(builder, loc, kPtrBasePosInDataDescriptor, basePtr);
+}
+
+/// Builds IR inserting the pointer value into the descriptor.
+void DataDescriptor::setPointer(OpBuilder &builder, Location loc, Value ptr) {
+ setPtr(builder, loc, kPtrPosInDataDescriptor, ptr);
+}
+
+/// Builds IR inserting the size value into the descriptor.
+void DataDescriptor::setSize(OpBuilder &builder, Location loc, Value size) {
+ setPtr(builder, loc, kSizePosInDataDescriptor, size);
+}
+
+//===----------------------------------------------------------------------===//
+// Conversion patterns
+//===----------------------------------------------------------------------===//
+
+template <typename Op>
+class LegalizeDataOpForLLVMTranslation : public ConvertOpToLLVMPattern<Op> {
+ using ConvertOpToLLVMPattern<Op>::ConvertOpToLLVMPattern;
+
+ LogicalResult
+ matchAndRewrite(Op op, ArrayRef<Value> operands,
+ ConversionPatternRewriter &builder) const override {
+ Location loc = op.getLoc();
+ TypeConverter *converter = ConvertToLLVMPattern::getTypeConverter();
+
+ unsigned numDataOperand = op.getNumDataOperands();
+
+ // Keep the non data operands without modification.
+ auto nonDataOperands =
+ operands.take_front(operands.size() - numDataOperand);
+ SmallVector<Value> convertedOperands;
+ convertedOperands.append(nonDataOperands.begin(), nonDataOperands.end());
+
+ // Go over the data operand and legalize them for translation.
+ for (unsigned idx = 0; idx < numDataOperand; ++idx) {
+ Value originalDataOperand = op.getDataOperand(idx);
+
+ // Traverse operands that were converted to MemRefDescriptors.
+ if (auto memRefType =
+ originalDataOperand.getType().dyn_cast<MemRefType>()) {
+ Type structType = converter->convertType(memRefType);
+ Value memRefDescriptor = builder
+ .create<LLVM::DialectCastOp>(
+ loc, structType, originalDataOperand)
+ .getResult();
+
+ // Calculate the size of the memref and get the pointer to the allocated
+ // buffer.
+ SmallVector<Value> sizes;
+ SmallVector<Value> strides;
+ Value sizeBytes;
+ ConvertToLLVMPattern::getMemRefDescriptorSizes(
+ loc, memRefType, {}, builder, sizes, strides, sizeBytes);
+ MemRefDescriptor descriptor(memRefDescriptor);
+ Value dataPtr = descriptor.alignedPtr(builder, loc);
+ auto ptrType = descriptor.getElementPtrType();
+
+ auto descr = DataDescriptor::undef(builder, loc, structType, ptrType);
+ descr.setBasePointer(builder, loc, memRefDescriptor);
+ descr.setPointer(builder, loc, dataPtr);
+ descr.setSize(builder, loc, sizeBytes);
+ convertedOperands.push_back(descr);
+ } else if (originalDataOperand.getType().isa<LLVM::LLVMPointerType>()) {
+ convertedOperands.push_back(originalDataOperand);
+ } else {
+ // Type not supported.
+ return builder.notifyMatchFailure(op, "unsupported type");
+ }
+ }
+
+ builder.replaceOpWithNewOp<Op>(op, TypeRange(), convertedOperands,
+ op.getOperation()->getAttrs());
+
+ return success();
+ }
+};
+
+void mlir::populateOpenACCToLLVMConversionPatterns(
+ LLVMTypeConverter &converter, OwningRewritePatternList &patterns) {
+ patterns.add<LegalizeDataOpForLLVMTranslation<acc::EnterDataOp>>(converter);
+ patterns.add<LegalizeDataOpForLLVMTranslation<acc::ExitDataOp>>(converter);
+ patterns.add<LegalizeDataOpForLLVMTranslation<acc::UpdateOp>>(converter);
+}
+
+namespace {
+struct ConvertOpenACCToLLVMPass
+ : public ConvertOpenACCToLLVMBase<ConvertOpenACCToLLVMPass> {
+ void runOnOperation() override;
+};
+} // namespace
+
+void ConvertOpenACCToLLVMPass::runOnOperation() {
+ auto op = getOperation();
+ auto *context = op.getContext();
+
+ // Convert to OpenACC operations with LLVM IR dialect
+ RewritePatternSet patterns(context);
+ LLVMTypeConverter converter(context);
+ populateOpenACCToLLVMConversionPatterns(converter, patterns);
+
+ ConversionTarget target(*context);
+ target.addLegalDialect<LLVM::LLVMDialect>();
+
+ auto allDataOperandsAreConverted = [](ValueRange operands) {
+ for (Value operand : operands) {
+ if (!DataDescriptor::isValid(operand) &&
+ !operand.getType().isa<LLVM::LLVMPointerType>())
+ return false;
+ }
+ return true;
+ };
+
+ target.addDynamicallyLegalOp<acc::EnterDataOp>(
+ [allDataOperandsAreConverted](acc::EnterDataOp op) {
+ return allDataOperandsAreConverted(op.copyinOperands()) &&
+ allDataOperandsAreConverted(op.createOperands()) &&
+ allDataOperandsAreConverted(op.createZeroOperands()) &&
+ allDataOperandsAreConverted(op.attachOperands());
+ });
+
+ target.addDynamicallyLegalOp<acc::ExitDataOp>(
+ [allDataOperandsAreConverted](acc::ExitDataOp op) {
+ return allDataOperandsAreConverted(op.copyoutOperands()) &&
+ allDataOperandsAreConverted(op.deleteOperands()) &&
+ allDataOperandsAreConverted(op.detachOperands());
+ });
+
+ target.addDynamicallyLegalOp<acc::UpdateOp>(
+ [allDataOperandsAreConverted](acc::UpdateOp op) {
+ return allDataOperandsAreConverted(op.hostOperands()) &&
+ allDataOperandsAreConverted(op.deviceOperands());
+ });
+
+ if (failed(applyPartialConversion(op, target, std::move(patterns))))
+ signalPassFailure();
+}
+
+std::unique_ptr<OperationPass<ModuleOp>>
+mlir::createConvertOpenACCToLLVMPass() {
+ return std::make_unique<ConvertOpenACCToLLVMPass>();
+}
return success();
}
+unsigned ExitDataOp::getNumDataOperands() {
+ return copyoutOperands().size() + deleteOperands().size() +
+ detachOperands().size();
+}
+
+Value ExitDataOp::getDataOperand(unsigned i) {
+ unsigned numOptional = ifCond() ? 1 : 0;
+ numOptional += asyncOperand() ? 1 : 0;
+ numOptional += waitDevnum() ? 1 : 0;
+ return getOperand(waitOperands().size() + numOptional + i);
+}
+
//===----------------------------------------------------------------------===//
-// DataEnterOp
+// EnterDataOp
//===----------------------------------------------------------------------===//
static LogicalResult verify(acc::EnterDataOp op) {
return success();
}
+unsigned EnterDataOp::getNumDataOperands() {
+ return copyinOperands().size() + createOperands().size() +
+ createZeroOperands().size() + attachOperands().size();
+}
+
+Value EnterDataOp::getDataOperand(unsigned i) {
+ unsigned numOptional = ifCond() ? 1 : 0;
+ numOptional += asyncOperand() ? 1 : 0;
+ numOptional += waitDevnum() ? 1 : 0;
+ return getOperand(waitOperands().size() + numOptional + i);
+}
+
//===----------------------------------------------------------------------===//
// InitOp
//===----------------------------------------------------------------------===//
return success();
}
+unsigned UpdateOp::getNumDataOperands() {
+ return hostOperands().size() + deviceOperands().size();
+}
+
+Value UpdateOp::getDataOperand(unsigned i) {
+ unsigned numOptional = asyncOperand() ? 1 : 0;
+ numOptional += waitDevnum() ? 1 : 0;
+ numOptional += ifCond() ? 1 : 0;
+ return getOperand(waitOperands().size() + deviceTypeOperands().size() +
+ numOptional + i);
+}
+
//===----------------------------------------------------------------------===//
// WaitOp
//===----------------------------------------------------------------------===//
--- /dev/null
+// RUN: mlir-opt -convert-openacc-to-llvm -split-input-file %s | FileCheck %s
+
+func @testenterdataop(%a: memref<10xf32>, %b: memref<10xf32>) -> () {
+ acc.enter_data copyin(%b : memref<10xf32>) create(%a : memref<10xf32>)
+ return
+}
+
+// CHECK: acc.enter_data copyin(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) create(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)
+
+// -----
+
+func @testenterdataop(%a: !llvm.ptr<f32>, %b: memref<10xf32>) -> () {
+ acc.enter_data copyin(%b : memref<10xf32>) create(%a : !llvm.ptr<f32>)
+ return
+}
+
+// CHECK: acc.enter_data copyin(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) create(%{{.*}} : !llvm.ptr<f32>)
+
+// -----
+
+func @testenterdataop(%a: memref<10xi64>, %b: memref<10xf32>) -> () {
+ acc.enter_data copyin(%b : memref<10xf32>) create_zero(%a : memref<10xi64>) attributes {async}
+ return
+}
+
+// CHECK: acc.enter_data copyin(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) create_zero(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<i64>, ptr<i64>, i64, array<1 x i64>, array<1 x i64>)>, ptr<i64>, i64)>) attributes {async}
+
+// -----
+
+func @testenterdataop(%a: memref<10xf32>, %b: memref<10xf32>) -> () {
+ %ifCond = constant true
+ acc.enter_data if(%ifCond) copyin(%b : memref<10xf32>) create(%a : memref<10xf32>)
+ return
+}
+
+// CHECK: acc.enter_data if(%{{.*}}) copyin(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) create(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)
+
+// -----
+
+func @testexitdataop(%a: memref<10xf32>, %b: memref<10xf32>) -> () {
+ acc.exit_data copyout(%b : memref<10xf32>) delete(%a : memref<10xf32>)
+ return
+}
+
+// CHECK: acc.exit_data copyout(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) delete(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)
+
+// -----
+
+func @testexitdataop(%a: !llvm.ptr<f32>, %b: memref<10xf32>) -> () {
+ acc.exit_data copyout(%b : memref<10xf32>) delete(%a : !llvm.ptr<f32>)
+ return
+}
+
+// CHECK: acc.exit_data copyout(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) delete(%{{.*}} : !llvm.ptr<f32>)
+
+// -----
+
+func @testexitdataop(%a: memref<10xi64>, %b: memref<10xf32>) -> () {
+ acc.exit_data copyout(%b : memref<10xf32>) delete(%a : memref<10xi64>) attributes {async}
+ return
+}
+
+// CHECK: acc.exit_data copyout(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) delete(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<i64>, ptr<i64>, i64, array<1 x i64>, array<1 x i64>)>, ptr<i64>, i64)>) attributes {async}
+
+// -----
+
+func @testexitdataop(%a: memref<10xf32>, %b: memref<10xf32>) -> () {
+ %ifCond = constant true
+ acc.exit_data if(%ifCond) copyout(%b : memref<10xf32>) delete(%a : memref<10xf32>)
+ return
+}
+
+// CHECK: acc.exit_data if(%{{.*}}) copyout(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) delete(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)
+
+// -----
+
+func @testupdateop(%a: memref<10xf32>, %b: memref<10xf32>) -> () {
+ acc.update host(%b : memref<10xf32>) device(%a : memref<10xf32>)
+ return
+}
+
+// CHECK: acc.update host(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) device(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)
+
+// -----
+
+func @testupdateop(%a: !llvm.ptr<f32>, %b: memref<10xf32>) -> () {
+ acc.update host(%b : memref<10xf32>) device(%a : !llvm.ptr<f32>)
+ return
+}
+
+// CHECK: acc.update host(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) device(%{{.*}} : !llvm.ptr<f32>)
+
+// -----
+
+func @testupdateop(%a: memref<10xi64>, %b: memref<10xf32>) -> () {
+ acc.update host(%b : memref<10xf32>) device(%a : memref<10xi64>) attributes {async}
+ return
+}
+
+// CHECK: acc.update host(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) device(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<i64>, ptr<i64>, i64, array<1 x i64>, array<1 x i64>)>, ptr<i64>, i64)>) attributes {async}
+
+// -----
+
+func @testupdateop(%a: memref<10xf32>, %b: memref<10xf32>) -> () {
+ %ifCond = constant true
+ acc.update if(%ifCond) host(%b : memref<10xf32>) device(%a : memref<10xf32>)
+ return
+}
+
+// CHECK: acc.update if(%{{.*}}) host(%{{.*}} : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>) device(%{{.*}} : !llvm.struct<"openacc_data.1", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)