Skip to content

Commit

Permalink
[mlir][llvm] Iterative constant import from LLVM IR.
Browse files Browse the repository at this point in the history
Instead of importing constant expressions recursively, the revision
walks all dependencies of an LLVM constant iteratively. The actual
conversion then iterates over a list of constants and all intermediate
constant values are added to the value mapping. As a result, an LLVM IR
constant maps to exactly one MLIR operation per function. The revision
adapts the existing tests since the constant ordering changed for
aggregate types. Additionally, it adds extra tests that mix aggregate
constants and constant expressions.

Depends on D137416

Reviewed By: ftynse

Differential Revision: https://reviews.llvm.org/D137559
  • Loading branch information
gysit committed Nov 18, 2022
1 parent 6527201 commit bc270f9
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 184 deletions.
2 changes: 1 addition & 1 deletion mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
Expand Up @@ -376,7 +376,7 @@ class LLVM_IntrOpBase<Dialect dialect, string opName, string enumName,
Operation *op = $_builder.create<$_qualCppClassName>(
$_location,
resultTypes,
processValues(llvmOperands));
convertValues(llvmOperands));
}] # !if(!gt(numResults, 0), "$res = op->getResult(0);", "(void)op;");
}

Expand Down
2 changes: 1 addition & 1 deletion mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
Expand Up @@ -810,7 +810,7 @@ def LLVM_ReturnOp : LLVM_TerminatorOp<"return", [Pure]> {
builder.CreateRetVoid();
}];
string mlirBuilder = [{
$_builder.create<LLVM::ReturnOp>($_location, processValues(llvmOperands));
$_builder.create<LLVM::ReturnOp>($_location, convertValues(llvmOperands));
}];
}

Expand Down
327 changes: 192 additions & 135 deletions mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp

Large diffs are not rendered by default.

80 changes: 65 additions & 15 deletions mlir/test/Target/LLVMIR/Import/constant.ll
Expand Up @@ -81,6 +81,57 @@ define i32* @gep_const_expr() {

; // -----

@global = external global i32, align 8

; CHECK-LABEL: @const_expr_with_duplicate
define i64 @const_expr_with_duplicate() {
; CHECK: %[[ADDR:[0-9]+]] = llvm.mlir.addressof @global : !llvm.ptr<i32>
; CHECK: %[[IDX:[0-9]+]] = llvm.mlir.constant(7 : i32) : i32
; CHECK: %[[GEP:[0-9]+]] = llvm.getelementptr %[[ADDR]][%[[IDX]]] : (!llvm.ptr<i32>, i32) -> !llvm.ptr<i32>
; CHECK: %[[DUP:[0-9]+]] = llvm.ptrtoint %[[GEP]] : !llvm.ptr<i32> to i64

; Verify the duplicate sub expression is converted only once.
; CHECK: %[[SUM:[0-9]+]] = llvm.add %[[DUP]], %[[DUP]] : i64
; CHECK: llvm.return %[[SUM]] : i64
ret i64 add (i64 ptrtoint (i32* getelementptr (i32, i32* @global, i32 7) to i64),
i64 ptrtoint (i32* getelementptr (i32, i32* @global, i32 7) to i64))
}

; // -----

@global = external global i32, align 8

; CHECK-LABEL: @const_expr_with_aggregate()
define i64 @const_expr_with_aggregate() {
; Compute the vector elements.
; CHECK: %[[VAL1:[0-9]+]] = llvm.mlir.constant(33 : i64) : i64
; CHECK: %[[ADDR:[0-9]+]] = llvm.mlir.addressof @global : !llvm.ptr<i32>
; CHECK: %[[IDX1:[0-9]+]] = llvm.mlir.constant(7 : i32) : i32
; CHECK: %[[GEP1:[0-9]+]] = llvm.getelementptr %[[ADDR]][%[[IDX1]]] : (!llvm.ptr<i32>, i32) -> !llvm.ptr<i32>
; CHECK: %[[VAL2:[0-9]+]] = llvm.ptrtoint %[[GEP1]] : !llvm.ptr<i32> to i64

; Fill the vector.
; CHECK: %[[VEC1:[0-9]+]] = llvm.mlir.undef : vector<2xi64>
; CHECK: %[[IDX2:[0-9]+]] = llvm.mlir.constant(0 : i32) : i32
; CHECK: %[[VEC2:[0-9]+]] = llvm.insertelement %[[VAL1]], %[[VEC1]][%[[IDX2]] : i32] : vector<2xi64>
; CHECK: %[[IDX3:[0-9]+]] = llvm.mlir.constant(1 : i32) : i32
; CHECK: %[[VEC3:[0-9]+]] = llvm.insertelement %[[VAL2]], %[[VEC2]][%[[IDX3]] : i32] : vector<2xi64>
; CHECK: %[[IDX4:[0-9]+]] = llvm.mlir.constant(42 : i32) : i32

; Compute the extract index.
; CHECK: %[[GEP2:[0-9]+]] = llvm.getelementptr %[[ADDR]][%[[IDX4]]] : (!llvm.ptr<i32>, i32) -> !llvm.ptr<i32>
; CHECK: %[[IDX5:[0-9]+]] = llvm.ptrtoint %[[GEP2]] : !llvm.ptr<i32> to i64

; Extract the vector element.
; CHECK: %[[ELEM:[0-9]+]] = llvm.extractelement %[[VEC3]][%[[IDX5]] : i64] : vector<2xi64>
; CHECK: llvm.return %[[ELEM]] : i64
ret i64 extractelement (
<2 x i64> <i64 33, i64 ptrtoint (i32* getelementptr (i32, i32* @global, i32 7) to i64)>,
i64 ptrtoint (i32* getelementptr (i32, i32* @global, i32 42) to i64))
}

; // -----

; Verify the function constant import.

; Calling a function that has not been defined yet.
Expand Down Expand Up @@ -119,42 +170,41 @@ define i32 @function_address_after_def() {

; Verify the aggregate constant import.

; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"simple_agg_type", (i32, i8, i16, i32)>
; CHECK: %[[C0:.+]] = llvm.mlir.constant(9 : i32) : i32
; CHECK: %[[CHAIN0:.+]] = llvm.insertvalue %[[C0]], %[[ROOT]][0]
; CHECK: %[[C1:.+]] = llvm.mlir.constant(4 : i8) : i8
; CHECK: %[[CHAIN1:.+]] = llvm.insertvalue %[[C1]], %[[CHAIN0]][1]
; CHECK: %[[C2:.+]] = llvm.mlir.constant(8 : i16) : i16
; CHECK: %[[CHAIN2:.+]] = llvm.insertvalue %[[C2]], %[[CHAIN1]][2]
; CHECK: %[[C3:.+]] = llvm.mlir.constant(7 : i32) : i32
; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"simple_agg_type", (i32, i8, i16, i32)>
; CHECK: %[[CHAIN0:.+]] = llvm.insertvalue %[[C0]], %[[ROOT]][0]
; CHECK: %[[CHAIN1:.+]] = llvm.insertvalue %[[C1]], %[[CHAIN0]][1]
; CHECK: %[[CHAIN2:.+]] = llvm.insertvalue %[[C2]], %[[CHAIN1]][2]
; CHECK: %[[CHAIN3:.+]] = llvm.insertvalue %[[C3]], %[[CHAIN2]][3]
; CHECK: llvm.return %[[CHAIN3]]
%simple_agg_type = type {i32, i8, i16, i32}
@simple_agg = global %simple_agg_type {i32 9, i8 4, i16 8, i32 7}

; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"nested_agg_type", (struct<"simple_agg_type", (i32, i8, i16, i32)>, ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>)>
; CHECK: %[[NESTED:.+]] = llvm.mlir.undef : !llvm.struct<"simple_agg_type", (i32, i8, i16, i32)>
; CHECK: %[[C1:.+]] = llvm.mlir.constant(1 : i32) : i32
; CHECK: %[[CHAIN0:.+]] = llvm.insertvalue %[[C1]], %[[NESTED]][0]
; CHECK: %[[C2:.+]] = llvm.mlir.constant(2 : i8) : i8
; CHECK: %[[CHAIN1:.+]] = llvm.insertvalue %[[C2]], %[[CHAIN0]][1]
; CHECK: %[[C3:.+]] = llvm.mlir.constant(3 : i16) : i16
; CHECK: %[[CHAIN2:.+]] = llvm.insertvalue %[[C3]], %[[CHAIN1]][2]
; CHECK: %[[C4:.+]] = llvm.mlir.constant(4 : i32) : i32
; CHECK: %[[NESTED:.+]] = llvm.mlir.undef : !llvm.struct<"simple_agg_type", (i32, i8, i16, i32)>
; CHECK: %[[CHAIN0:.+]] = llvm.insertvalue %[[C1]], %[[NESTED]][0]
; CHECK: %[[CHAIN1:.+]] = llvm.insertvalue %[[C2]], %[[CHAIN0]][1]
; CHECK: %[[CHAIN2:.+]] = llvm.insertvalue %[[C3]], %[[CHAIN1]][2]
; CHECK: %[[CHAIN3:.+]] = llvm.insertvalue %[[C4]], %[[CHAIN2]][3]
; CHECK: %[[NULL:.+]] = llvm.mlir.null : !llvm.ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>
; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"nested_agg_type", (struct<"simple_agg_type", (i32, i8, i16, i32)>, ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>)>
; CHECK: %[[CHAIN4:.+]] = llvm.insertvalue %[[CHAIN3]], %[[ROOT]][0]
; CHECK: %[[NP:.+]] = llvm.mlir.null : !llvm.ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>
; CHECK: %[[CHAIN5:.+]] = llvm.insertvalue %[[NP]], %[[CHAIN4]][1]
; CHECK: %[[CHAIN5:.+]] = llvm.insertvalue %[[NULL]], %[[CHAIN4]][1]
; CHECK: llvm.return %[[CHAIN5]]
%nested_agg_type = type {%simple_agg_type, %simple_agg_type*}
@nested_agg = global %nested_agg_type { %simple_agg_type{i32 1, i8 2, i16 3, i32 4}, %simple_agg_type* null }

; CHECK: %[[NULL:.+]] = llvm.mlir.null : !llvm.ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>
; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.vec<2 x ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>>
; CHECK: %[[C0:.+]] = llvm.mlir.null : !llvm.ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>
; CHECK: %[[P0:.+]] = llvm.mlir.constant(0 : i32) : i32
; CHECK: %[[CHAIN0:.+]] = llvm.insertelement %[[C0]], %[[ROOT]][%[[P0]] : i32] : !llvm.vec<2 x ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>>
; CHECK: %[[C1:.+]] = llvm.mlir.null : !llvm.ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>
; CHECK: %[[CHAIN0:.+]] = llvm.insertelement %[[NULL]], %[[ROOT]][%[[P0]] : i32] : !llvm.vec<2 x ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>>
; CHECK: %[[P1:.+]] = llvm.mlir.constant(1 : i32) : i32
; CHECK: %[[CHAIN1:.+]] = llvm.insertelement %[[C1]], %[[CHAIN0]][%[[P1]] : i32] : !llvm.vec<2 x ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>>
; CHECK: %[[CHAIN1:.+]] = llvm.insertelement %[[NULL]], %[[CHAIN0]][%[[P1]] : i32] : !llvm.vec<2 x ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>>
; CHECK: llvm.return %[[CHAIN1]] : !llvm.vec<2 x ptr<struct<"simple_agg_type", (i32, i8, i16, i32)>>>
@vector_agg = global <2 x %simple_agg_type*> <%simple_agg_type* null, %simple_agg_type* null>
15 changes: 8 additions & 7 deletions mlir/test/Target/LLVMIR/Import/incorrect-constant-caching.ll
Expand Up @@ -8,19 +8,20 @@
; only wrote minimum level of checks.

%my_struct = type {i32, i8*}
; CHECK: llvm.mlir.addressof @str0 : !llvm.ptr<array<5 x i8>>
; CHECK: llvm.mlir.addressof @str1 : !llvm.ptr<array<5 x i8>>
; CHECK: llvm.mlir.undef : !llvm.array<2 x struct<"my_struct", (i32, ptr<i8>)>>
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.mlir.constant(8 : i32) : i32
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.addressof @str0 : !llvm.ptr<array<5 x i8>>
; CHECK: llvm.mlir.constant(0 : i32) : i32
; CHECK: llvm.getelementptr
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.insertvalue
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.mlir.constant(7 : i32) : i32
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.addressof @str1 : !llvm.ptr<array<5 x i8>>
; CHECK: llvm.getelementptr
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.insertvalue
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.undef : !llvm.array<2 x struct<"my_struct", (i32, ptr<i8>)>>
; CHECK: llvm.insertvalue
; CHECK: llvm.insertvalue
; CHECK: llvm.return
Expand Down
Expand Up @@ -5,23 +5,23 @@
; Thus, we only wrote minimum level of checks.

%my_struct = type {i32, i8*}
; CHECK: llvm.mlir.constant(8 : i32) : i32
; CHECK: llvm.mlir.addressof @str0 : !llvm.ptr<array<5 x i8>>
; CHECK: llvm.mlir.constant(0 : i32) : i32
; CHECK: llvm.mlir.constant(1 : i32) : i32
; CHECK: llvm.getelementptr
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.insertvalue
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.constant(7 : i32) : i32
; CHECK: llvm.mlir.addressof @str1 : !llvm.ptr<array<5 x i8>>
; CHECK: llvm.mlir.constant(2 : i32) : i32
; CHECK: llvm.mlir.constant(3 : i32) : i32
; CHECK: llvm.mlir.undef : !llvm.array<2 x struct<"my_struct", (i32, ptr<i8>)>>
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.mlir.constant(8 : i32) : i32
; CHECK: llvm.insertvalue
; CHECK: llvm.getelementptr
; CHECK: llvm.insertvalue
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.undef : !llvm.struct<"my_struct", (i32, ptr<i8>)>
; CHECK: llvm.mlir.constant(7 : i32) : i32
; CHECK: llvm.insertvalue
; CHECK: llvm.getelementptr
; CHECK: llvm.insertvalue
; CHECK: llvm.mlir.undef : !llvm.array<2 x struct<"my_struct", (i32, ptr<i8>)>>
; CHECK: llvm.insertvalue
; CHECK: llvm.insertvalue
; CHECK: llvm.return
Expand Down
23 changes: 10 additions & 13 deletions mlir/test/Target/LLVMIR/Import/intrinsic.ll
Expand Up @@ -127,21 +127,19 @@ define void @bitreverse_test(i32 %0, <8 x i32> %1) {
}
; CHECK-LABEL: llvm.func @ctlz_test
define void @ctlz_test(i32 %0, <8 x i32> %1) {
; CHECK: %[[falseval1:.+]] = llvm.mlir.constant(false) : i1
; CHECK: %[[falseval2:.+]] = llvm.mlir.constant(false) : i1
; CHECK: "llvm.intr.ctlz"(%{{.*}}, %[[falseval1]]) : (i32, i1) -> i32
; CHECK: %[[FALSE:.+]] = llvm.mlir.constant(false) : i1
; CHECK: "llvm.intr.ctlz"(%{{.*}}, %[[FALSE]]) : (i32, i1) -> i32
%3 = call i32 @llvm.ctlz.i32(i32 %0, i1 false)
; CHECK: "llvm.intr.ctlz"(%{{.*}}, %[[falseval2]]) : (vector<8xi32>, i1) -> vector<8xi32>
; CHECK: "llvm.intr.ctlz"(%{{.*}}, %[[FALSE]]) : (vector<8xi32>, i1) -> vector<8xi32>
%4 = call <8 x i32> @llvm.ctlz.v8i32(<8 x i32> %1, i1 false)
ret void
}
; CHECK-LABEL: llvm.func @cttz_test
define void @cttz_test(i32 %0, <8 x i32> %1) {
; CHECK: %[[falseval1:.+]] = llvm.mlir.constant(false) : i1
; CHECK: %[[falseval2:.+]] = llvm.mlir.constant(false) : i1
; CHECK: "llvm.intr.cttz"(%{{.*}}, %[[falseval1]]) : (i32, i1) -> i32
; CHECK: %[[FALSE:.+]] = llvm.mlir.constant(false) : i1
; CHECK: "llvm.intr.cttz"(%{{.*}}, %[[FALSE]]) : (i32, i1) -> i32
%3 = call i32 @llvm.cttz.i32(i32 %0, i1 false)
; CHECK: "llvm.intr.cttz"(%{{.*}}, %[[falseval2]]) : (vector<8xi32>, i1) -> vector<8xi32>
; CHECK: "llvm.intr.cttz"(%{{.*}}, %[[FALSE]]) : (vector<8xi32>, i1) -> vector<8xi32>
%4 = call <8 x i32> @llvm.cttz.v8i32(<8 x i32> %1, i1 false)
ret void
}
Expand Down Expand Up @@ -333,12 +331,11 @@ define void @masked_expand_compress_intrinsics(float* %0, <7 x i1> %1, <7 x floa

; CHECK-LABEL: llvm.func @memcpy_test
define void @memcpy_test(i32 %0, i8* %1, i8* %2) {
; CHECK: %[[falseval1:.+]] = llvm.mlir.constant(false) : i1
; CHECK: %[[constant:.+]] = llvm.mlir.constant(10 : i64) : i64
; CHECK: %[[falseval2:.+]] = llvm.mlir.constant(false) : i1
; CHECK: "llvm.intr.memcpy"(%{{.*}}, %{{.*}}, %{{.*}}, %[[falseval1]]) : (!llvm.ptr<i8>, !llvm.ptr<i8>, i32, i1) -> ()
; CHECK: %[[FALSE:.+]] = llvm.mlir.constant(false) : i1
; CHECK: %[[CST:.+]] = llvm.mlir.constant(10 : i64) : i64
; CHECK: "llvm.intr.memcpy"(%{{.*}}, %{{.*}}, %{{.*}}, %[[FALSE]]) : (!llvm.ptr<i8>, !llvm.ptr<i8>, i32, i1) -> ()
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %1, i8* %2, i32 %0, i1 false)
; CHECK: "llvm.intr.memcpy.inline"(%{{.*}}, %{{.*}}, %[[constant]], %[[falseval2]]) : (!llvm.ptr<i8>, !llvm.ptr<i8>, i64, i1) -> ()
; CHECK: "llvm.intr.memcpy.inline"(%{{.*}}, %{{.*}}, %[[CST]], %[[FALSE]]) : (!llvm.ptr<i8>, !llvm.ptr<i8>, i64, i1) -> ()
call void @llvm.memcpy.inline.p0i8.p0i8.i64(i8* %1, i8* %2, i64 10, i1 false)
ret void
}
Expand Down
4 changes: 2 additions & 2 deletions mlir/test/Target/LLVMIR/Import/zeroinitializer.ll
Expand Up @@ -4,10 +4,10 @@

; CHECK: llvm.mlir.global external @D()
; CHECK-SAME: !llvm.struct<"Domain", (ptr<ptr<struct<"Domain">>>, ptr<struct<"Domain">>)>
; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"Domain", (ptr<ptr<struct<"Domain">>>, ptr<struct<"Domain">>)>
; CHECK: %[[E0:.+]] = llvm.mlir.null : !llvm.ptr<ptr<struct<"Domain", (ptr<ptr<struct<"Domain">>>, ptr<struct<"Domain">>)>>>
; CHECK: %[[CHAIN:.+]] = llvm.insertvalue %[[E0]], %[[ROOT]][0]
; CHECK: %[[E1:.+]] = llvm.mlir.null : !llvm.ptr<struct<"Domain", (ptr<ptr<struct<"Domain">>>, ptr<struct<"Domain">>)>>
; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"Domain", (ptr<ptr<struct<"Domain">>>, ptr<struct<"Domain">>)>
; CHECK: %[[CHAIN:.+]] = llvm.insertvalue %[[E0]], %[[ROOT]][0]
; CHECK: %[[RES:.+]] = llvm.insertvalue %[[E1]], %[[CHAIN]][1]
; CHECK: llvm.return %[[RES]]
@D = global %Domain zeroinitializer
4 changes: 2 additions & 2 deletions mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp
Expand Up @@ -240,8 +240,8 @@ static LogicalResult emitOneMLIRBuilder(const Record &record, raw_ostream &os,
bool isVariadicOperand = isVariadicOperandName(op, name);
auto result =
isVariadicOperand
? formatv("processValues(llvmOperands.drop_front({0}))", idx)
: formatv("processValue(llvmOperands[{0}])", idx);
? formatv("convertValues(llvmOperands.drop_front({0}))", idx)
: formatv("convertValue(llvmOperands[{0}])", idx);
bs << result;
} else if (isResultName(op, name)) {
if (op.getNumResults() != 1)
Expand Down

0 comments on commit bc270f9

Please sign in to comment.