diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -257,7 +257,8 @@ let arguments = (ins LLVM_ScalarOrVectorOf:$base, Variadic>:$dynamicIndices, DenseI32ArrayAttr:$rawConstantIndices, - OptionalAttr:$elem_type); + OptionalAttr:$elem_type, + UnitAttr:$inbounds); let results = (outs LLVM_ScalarOrVectorOf:$res); let skipDefaultBuilders = 1; @@ -269,14 +270,17 @@ as indices. In the case of indexing within a structure, it is required to either use constant indices directly, or supply a constant SSA value. + An optional 'inbounds' attribute specifies the low-level pointer arithmetic + overflow behavior that LLVM uses after lowering the operation to LLVM IR. + Examples: ```mlir // GEP with an SSA value offset %0 = llvm.getelementptr %1[%2] : (!llvm.ptr, i64) -> !llvm.ptr - // GEP with a constant offset - %0 = llvm.getelementptr %1[3] : (!llvm.ptr) -> !llvm.ptr + // GEP with a constant offset and the inbounds attribute set + %0 = llvm.getelementptr inbounds %1[3] : (!llvm.ptr) -> !llvm.ptr // GEP with constant offsets into a structure %0 = llvm.getelementptr %1[0, 1] @@ -286,14 +290,16 @@ let builders = [ OpBuilder<(ins "Type":$resultType, "Type":$basePtrType, "Value":$basePtr, - "ValueRange":$indices, + "ValueRange":$indices, CArg<"bool", "false">:$inbounds, CArg<"ArrayRef", "{}">:$attributes)>, - OpBuilder<(ins "Type":$resultType, "Value":$basePtr, "ValueRange":$indices, + OpBuilder<(ins "Type":$resultType, "Value":$basePtr, + "ValueRange":$indices, CArg<"bool", "false">:$inbounds, CArg<"ArrayRef", "{}">:$attributes)>, - OpBuilder<(ins "Type":$resultType, "Value":$basePtr, "ArrayRef":$indices, + OpBuilder<(ins "Type":$resultType, "Value":$basePtr, + "ArrayRef":$indices, CArg<"bool", "false">:$inbounds, CArg<"ArrayRef", "{}">:$attributes)>, OpBuilder<(ins "Type":$resultType, "Type":$basePtrType, "Value":$basePtr, - "ArrayRef":$indices, + "ArrayRef":$indices, CArg<"bool", "false">:$inbounds, CArg<"ArrayRef", "{}">:$attributes)>, ]; let llvmBuilder = [{ @@ -310,9 +316,10 @@ } Type baseElementType = op.getSourceElementType(); llvm::Type *elementType = moduleTranslation.convertType(baseElementType); - $res = builder.CreateGEP(elementType, $base, indices); + $res = builder.CreateGEP(elementType, $base, indices, "", $inbounds); }]; let assemblyFormat = [{ + (`inbounds` $inbounds^)? $base `[` custom($dynamicIndices, $rawConstantIndices) `]` attr-dict `:` functional-type(operands, results) (`,` $elem_type^)? }]; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -418,7 +418,7 @@ } void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType, - Value basePtr, ArrayRef indices, + Value basePtr, ArrayRef indices, bool inbounds, ArrayRef attributes) { auto ptrType = extractVectorElementType(basePtr.getType()).cast(); @@ -426,7 +426,7 @@ "expected non-opaque pointer, provide elementType explicitly when " "opaque pointers are used"); build(builder, result, resultType, ptrType.getElementType(), basePtr, indices, - attributes); + inbounds, attributes); } /// Destructures the 'indices' parameter into 'rawConstantIndices' and @@ -481,7 +481,7 @@ void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType, Type elementType, Value basePtr, ArrayRef indices, - ArrayRef attributes) { + bool inbounds, ArrayRef attributes) { SmallVector rawConstantIndices; SmallVector dynamicIndices; destructureIndices(elementType, indices, rawConstantIndices, dynamicIndices); @@ -490,6 +490,10 @@ result.addAttributes(attributes); result.addAttribute(getRawConstantIndicesAttrName(result.name), builder.getDenseI32ArrayAttr(rawConstantIndices)); + if (inbounds) { + result.addAttribute(getInboundsAttrName(result.name), + builder.getUnitAttr()); + } if (extractVectorElementType(basePtr.getType()) .cast() .isOpaque()) @@ -499,17 +503,17 @@ } void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType, - Value basePtr, ValueRange indices, + Value basePtr, ValueRange indices, bool inbounds, ArrayRef attributes) { build(builder, result, resultType, basePtr, SmallVector(indices), - attributes); + inbounds, attributes); } void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType, Type elementType, Value basePtr, ValueRange indices, - ArrayRef attributes) { + bool inbounds, ArrayRef attributes) { build(builder, result, resultType, elementType, basePtr, - SmallVector(indices), attributes); + SmallVector(indices), inbounds, attributes); } static ParseResult diff --git a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp --- a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -1085,7 +1085,6 @@ return success(); } if (inst->getOpcode() == llvm::Instruction::GetElementPtr) { - // FIXME: Support inbounds GEPs. auto *gepInst = cast(inst); Type sourceElementType = convertType(gepInst->getSourceElementType()); FailureOr basePtr = convertValue(gepInst->getOperand(0)); @@ -1105,8 +1104,9 @@ } Type type = convertType(inst->getType()); - Value res = builder.create(loc, type, sourceElementType, - basePtr.value(), indices); + Value res = + builder.create(loc, type, sourceElementType, basePtr.value(), + indices, gepInst->isInBounds()); mapValue(inst, res); return success(); } @@ -1116,7 +1116,6 @@ LogicalResult Importer::processInstruction(llvm::Instruction *inst) { // FIXME: Support uses of SubtargetData. - // FIXME: Add support for inbounds GEPs. // FIXME: Add support for fast-math flags and call / operand attributes. // FIXME: Add support for the indirectbr, cleanupret, catchret, catchswitch, // callbr, vaarg, landingpad, catchpad, cleanuppad instructions. diff --git a/mlir/test/Dialect/LLVMIR/canonicalize.mlir b/mlir/test/Dialect/LLVMIR/canonicalize.mlir --- a/mlir/test/Dialect/LLVMIR/canonicalize.mlir +++ b/mlir/test/Dialect/LLVMIR/canonicalize.mlir @@ -102,11 +102,11 @@ // CHECK-LABEL: fold_gep_neg // CHECK-SAME: %[[a0:arg[0-9]+]] -// CHECK-NEXT: %[[RES:.*]] = llvm.getelementptr %[[a0]][0, 1] +// CHECK-NEXT: %[[RES:.*]] = llvm.getelementptr inbounds %[[a0]][0, 1] // CHECK-NEXT: llvm.return %[[RES]] llvm.func @fold_gep_neg(%x : !llvm.ptr) -> !llvm.ptr { %c0 = arith.constant 0 : i32 - %0 = llvm.getelementptr %x[%c0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(i32, i32)> + %0 = llvm.getelementptr inbounds %x[%c0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<(i32, i32)> llvm.return %0 : !llvm.ptr } diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -184,8 +184,8 @@ %ptr2: !llvm.ptr)>>) { // CHECK: llvm.getelementptr %{{.*}}[%{{.*}}, 1, 0] : (!llvm.ptr)>>, i64) -> !llvm.ptr llvm.getelementptr %ptr[%idx, 1, 0] : (!llvm.ptr)>>, i64) -> !llvm.ptr - // CHECK: llvm.getelementptr %{{.*}}[%{{.*}}, 0, %{{.*}}] : (!llvm.ptr)>>, i64, i64) -> !llvm.ptr - llvm.getelementptr %ptr2[%idx, 0, %idx] : (!llvm.ptr)>>, i64, i64) -> !llvm.ptr + // CHECK: llvm.getelementptr inbounds %{{.*}}[%{{.*}}, 0, %{{.*}}] : (!llvm.ptr)>>, i64, i64) -> !llvm.ptr + llvm.getelementptr inbounds %ptr2[%idx, 0, %idx] : (!llvm.ptr)>>, i64, i64) -> !llvm.ptr llvm.return } diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll --- a/mlir/test/Target/LLVMIR/Import/instructions.ll +++ b/mlir/test/Target/LLVMIR/Import/instructions.ll @@ -440,8 +440,8 @@ ; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]] define void @gep_static_idx(float* %ptr) { ; CHECK: %[[IDX:.+]] = llvm.mlir.constant(7 : i32) - ; CHECK: llvm.getelementptr %[[PTR]][%[[IDX]]] : (!llvm.ptr, i32) -> !llvm.ptr - %1 = getelementptr float, float* %ptr, i32 7 + ; CHECK: llvm.getelementptr inbounds %[[PTR]][%[[IDX]]] : (!llvm.ptr, i32) -> !llvm.ptr + %1 = getelementptr inbounds float, float* %ptr, i32 7 ret void } diff --git a/mlir/test/Target/LLVMIR/Import/opaque.ll b/mlir/test/Target/LLVMIR/Import/opaque.ll --- a/mlir/test/Target/LLVMIR/Import/opaque.ll +++ b/mlir/test/Target/LLVMIR/Import/opaque.ll @@ -38,8 +38,8 @@ ; CHECK-LABEL: @opaque_ptr_gep define ptr @opaque_ptr_gep(ptr %0, i32 %1) { - ; CHECK: = llvm.getelementptr %{{.*}}[%{{.*}}] : (!llvm.ptr, i32) -> !llvm.ptr, f32 - %3 = getelementptr float, ptr %0, i32 %1 + ; CHECK: = llvm.getelementptr inbounds %{{.*}}[%{{.*}}] : (!llvm.ptr, i32) -> !llvm.ptr, f32 + %3 = getelementptr inbounds float, ptr %0, i32 %1 ret ptr %3 } diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -996,8 +996,8 @@ %ptr2: !llvm.ptr)>>) { // CHECK: = getelementptr { i32, { i32, float } }, ptr %{{.*}}, i64 %{{.*}}, i32 1, i32 0 llvm.getelementptr %ptr[%idx, 1, 0] : (!llvm.ptr)>>, i64) -> !llvm.ptr - // CHECK: = getelementptr { [10 x float] }, ptr %{{.*}}, i64 %{{.*}}, i32 0, i64 %{{.*}} - llvm.getelementptr %ptr2[%idx, 0, %idx] : (!llvm.ptr)>>, i64, i64) -> !llvm.ptr + // CHECK: = getelementptr inbounds { [10 x float] }, ptr %{{.*}}, i64 %{{.*}}, i32 0, i64 %{{.*}} + llvm.getelementptr inbounds %ptr2[%idx, 0, %idx] : (!llvm.ptr)>>, i64, i64) -> !llvm.ptr llvm.return }