-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
[mlir][llvmir] Add llvm.intr.ldexp operation #133070
Conversation
@llvm/pr-subscribers-mlir-llvm @llvm/pr-subscribers-mlir Author: Letu Ren (FantasqueX) Changeshttps://llvm.org/docs/LangRef.html#llvm-ldexp-intrinsic
> Generally, the only supported type for the exponent is the one matching with the C type int. Full diff: https://github.com/llvm/llvm-project/pull/133070.diff 4 Files Affected:
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index ace880bd28590..d702247244808 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -106,10 +106,22 @@ def LLVM_IsFPClass : LLVM_OneResultIntrOp<"is.fpclass", [], [0], [Pure],
let arguments = (ins LLVM_ScalarOrVectorOf<LLVM_AnyFloat>:$in, I32Attr:$bit);
}
+class LLVM_PowFI<string func> :
+ LLVM_OneResultIntrOp<func, [], [0,1],
+ [Pure], /*requiresFastmath=*/1> {
+ let arguments =
+ (ins LLVM_ScalarOrVectorOf<LLVM_AnyFloat>:$val,
+ AnyI32:$power,
+ DefaultValuedAttr<LLVM_FastmathFlagsAttr, "{}">:$fastmathFlags);
+ let assemblyFormat = "`(` operands `)` attr-dict `:` "
+ "functional-type(operands, results)";
+}
+
def LLVM_CopySignOp : LLVM_BinarySameArgsIntrOpF<"copysign">;
def LLVM_ExpOp : LLVM_UnaryIntrOpF<"exp">;
def LLVM_Exp2Op : LLVM_UnaryIntrOpF<"exp2">;
def LLVM_Exp10Op : LLVM_UnaryIntrOpF<"exp10">;
+def LLVM_LoadExpOp: LLVM_PowFI<"ldexp">;
def LLVM_FAbsOp : LLVM_UnaryIntrOpF<"fabs">;
def LLVM_FCeilOp : LLVM_UnaryIntrOpF<"ceil">;
def LLVM_FFloorOp : LLVM_UnaryIntrOpF<"floor">;
@@ -130,15 +142,7 @@ def LLVM_RoundOp : LLVM_UnaryIntrOpF<"round">;
def LLVM_FTruncOp : LLVM_UnaryIntrOpF<"trunc">;
def LLVM_SqrtOp : LLVM_UnaryIntrOpF<"sqrt">;
def LLVM_PowOp : LLVM_BinarySameArgsIntrOpF<"pow">;
-def LLVM_PowIOp : LLVM_OneResultIntrOp<"powi", [], [0,1],
- [Pure], /*requiresFastmath=*/1> {
- let arguments =
- (ins LLVM_ScalarOrVectorOf<LLVM_AnyFloat>:$val,
- AnySignlessInteger:$power,
- DefaultValuedAttr<LLVM_FastmathFlagsAttr, "{}">:$fastmathFlags);
- let assemblyFormat = "`(` operands `)` attr-dict `:` "
- "functional-type(operands, results)";
-}
+def LLVM_PowIOp : LLVM_PowFI<"powi">;
def LLVM_RintOp : LLVM_UnaryIntrOpF<"rint">;
def LLVM_NearbyintOp : LLVM_UnaryIntrOpF<"nearbyint">;
class LLVM_IntRoundIntrOpBase<string func> :
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic.ll b/mlir/test/Target/LLVMIR/Import/intrinsic.ll
index 09f1eb5529228..3ba3ba26c54ae 100644
--- a/mlir/test/Target/LLVMIR/Import/intrinsic.ll
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic.ll
@@ -51,6 +51,15 @@ define void @exp10_test(float %0, <8 x float> %1) {
ret void
}
+; CHECK-LABEL: llvm.func @ldexp_test
+define void @ldexp_test(float %0, <8 x float> %1, i32 %2) {
+ ; CHECK: llvm.intr.ldexp(%{{.*}}, %{{.*}}) : (f32, i32) -> f32
+ %4 = call float @llvm.ldexp.f32.i32(float %0, i32 %2)
+ ; CHECK: llvm.intr.ldexp(%{{.*}}, %{{.*}}) : (vector<8xf32>, i32) -> vector<8xf32>
+ %5 = call <8 x float> @llvm.ldexp.v8f32.i32(<8 x float> %1, i32 %2)
+ ret void
+}
+
; CHECK-LABEL: llvm.func @log_test
define void @log_test(float %0, <8 x float> %1) {
; CHECK: llvm.intr.log(%{{.*}}) : (f32) -> f32
@@ -1060,6 +1069,8 @@ declare float @llvm.exp2.f32(float)
declare <8 x float> @llvm.exp2.v8f32(<8 x float>)
declare float @llvm.exp10.f32(float)
declare <8 x float> @llvm.exp10.v8f32(<8 x float>)
+declare float @llvm.ldexp.f32.i32(float, i32)
+declare <8 x float> @llvm.ldexp.v8f32.i32(<8 x float>, i32)
declare float @llvm.log.f32(float)
declare <8 x float> @llvm.log.v8f32(<8 x float>)
declare float @llvm.log10.f32(float)
diff --git a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir
index d54cd3cb11b51..cba47e3274386 100644
--- a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir
@@ -49,6 +49,15 @@ llvm.func @exp10_test(%arg0: f32, %arg1: vector<8xf32>) {
llvm.return
}
+// CHECK-LABEL: @ldexp_test
+llvm.func @ldexp_test(%arg0: f32, %arg1: vector<8xf32>, %arg2: i32) {
+ // CHECK: call float @llvm.ldexp.f32.i32(float %{{.*}}, i32 %{{.*}})
+ "llvm.intr.ldexp"(%arg0, %arg2) : (f32, i32) -> f32
+ // CHECK: call <8 x float> @llvm.ldexp.v8f32.i32(<8 x float> %{{.*}}, i32 %{{.*}})
+ "llvm.intr.ldexp"(%arg1, %arg2) : (vector<8xf32>, i32) -> vector<8xf32>
+ llvm.return
+}
+
// CHECK-LABEL: @log_test
llvm.func @log_test(%arg0: f32, %arg1: vector<8xf32>) {
// CHECK: call float @llvm.log.f32
diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
index 83566d6649932..3397dfbfdfe72 100644
--- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
@@ -147,7 +147,7 @@ llvm.func @ternary_float_intr_wrong_type(%arg0 : f32, %arg1 : f32, %arg2 : i32)
// -----
llvm.func @powi_intr_wrong_type(%arg0 : f32, %arg1 : f32) -> f32 {
- // expected-error @below{{op operand #1 must be signless integer, but got 'f32'}}
+ // expected-error @below{{op operand #1 must be 32-bit integer, but got 'f32'}}
%0 = "llvm.intr.powi"(%arg0, %arg1) : (f32, f32) -> f32
llvm.return %0 : f32
}
|
Cc @gysit |
The language ref actually has an example where the power is an i16 (https://llvm.org/docs/LangRef.html#llvm-powi-intrinsic). Where did you see that only i32 is supported. Is there a verifier? |
You are right. LLVM allows different width |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the intrinsic!
LGTM modulo the the AnySignlessInteger change.
I tried to explain in the review why we should stick to it.
(ins LLVM_ScalarOrVectorOf<LLVM_AnyFloat>:$val, | ||
AnyInteger:$power, | ||
DefaultValuedAttr<LLVM_FastmathFlagsAttr, "{}">:$fastmathFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(ins LLVM_ScalarOrVectorOf<LLVM_AnyFloat>:$val, | |
AnyInteger:$power, | |
DefaultValuedAttr<LLVM_FastmathFlagsAttr, "{}">:$fastmathFlags); | |
(ins LLVM_ScalarOrVectorOf<LLVM_AnyFloat>:$val, | |
AnySignlessInteger:$power, | |
DefaultValuedAttr<LLVM_FastmathFlagsAttr, "{}">:$fastmathFlags); |
Let's use a signless integer nevertheless.
LLVM IR and hence also LLVM dialect works only with signless integer values (such as i32 or i16, it does not support ui16 or si32). The rational behind this is that the type itself should not encode if a value is signed or unsigned (this is different from c/c++). Instead the operations/instructions define if an integer is interpreted as signed or unsigned. That is why there is a signed and unsigned division operation and both of them take signless integer operands. Does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got it. It's signless instead of unsigned. Thanks for your explanation!
https://llvm.org/docs/LangRef.html#llvm-ldexp-intrinsic
https://llvm.org/docs/LangRef.html#llvm-powi-intrinsic
ldexp
andpowi
are similar. According to LLVM IR reference, power should bei32
.