diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLSLOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLSLOps.td --- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLSLOps.td +++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLSLOps.td @@ -1069,4 +1069,59 @@ let verifier = [{ return ::verifyGLSLFrexpStructOp(*this); }]; } +def SPV_GLSLLdexpOp : + SPV_GLSLOp<"Ldexp", 53, [ + NoSideEffect, AllTypesMatch<["x", "y"]>]> { + let summary = "Builds y such that y = significand * 2^exponent"; + + let description = [{ + Builds a floating-point number from x and the corresponding + integral exponent of two in exp: + + significand * 2^exponent + + If this product is too large to be represented in the floating-point + type, the resulting value is undefined. If exp is greater than +128 + (single precision) or +1024 (double precision), the resulting value is + undefined. If exp is less than -126 (single precision) or -1022 (double precision), + the result may be flushed to zero. Additionally, splitting the value + into a significand and exponent using frexp and then reconstructing a + floating-point value using ldexp should yield the original input for + zero and all finite non-denormalized values. + + The operand x must be a scalar or vector whose component type is floating-point. + + The exp operand must be a scalar or vector with integer component type. + The number of components in x and exp must be the same. + + Result Type must be the same type as the type of x. Results are computed per + component. + + + + #### Example: + + ```mlir + %y = spv.GLSL.Ldexp %x : f32, %exp : i32 -> f32 + %y = spv.GLSL.Ldexp %x : vector<3xf32>, %exp : vector<3xi32> -> vector<3xf32> + ``` + }]; + + let arguments = (ins + SPV_ScalarOrVectorOf:$x, + SPV_ScalarOrVectorOf:$exp + ); + + let results = (outs + SPV_ScalarOrVectorOf:$y + ); + + let assemblyFormat = [{ + attr-dict $x `:` type($x) `,` $exp `:` type($exp) `->` type($y) + }]; + + let verifier = [{ return ::verify(*this); }]; +} + + #endif // MLIR_DIALECT_SPIRV_IR_GLSL_OPS diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp --- a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp +++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp @@ -3585,6 +3585,30 @@ "must have the same number of components as the operand type"); } +//===----------------------------------------------------------------------===// +// spv.GLSL.Ldexp +//===----------------------------------------------------------------------===// + +static LogicalResult verify(spirv::GLSLLdexpOp ldexpOp) { + Type significandType = ldexpOp.x().getType(); + Type exponentType = ldexpOp.exp().getType(); + + if (significandType.isa() != exponentType.isa()) + return ldexpOp.emitOpError("operands must both be scalars or vectors"); + + auto getNumElements = [](Type type) -> unsigned { + if (auto vectorType = type.dyn_cast()) + return vectorType.getNumElements(); + return 1; + }; + + if (getNumElements(significandType) != getNumElements(exponentType)) + return ldexpOp.emitOpError( + "operands must have the same number of elements"); + + return success(); +} + namespace mlir { namespace spirv { diff --git a/mlir/test/Dialect/SPIRV/IR/glsl-ops.mlir b/mlir/test/Dialect/SPIRV/IR/glsl-ops.mlir --- a/mlir/test/Dialect/SPIRV/IR/glsl-ops.mlir +++ b/mlir/test/Dialect/SPIRV/IR/glsl-ops.mlir @@ -420,3 +420,46 @@ %2 = spv.GLSL.FrexpStruct %arg0 : f32 -> !spv.struct<(f32, i64)> return } + +// ----- + +//===----------------------------------------------------------------------===// +// spv.GLSL.Ldexp +//===----------------------------------------------------------------------===// + +func @ldexp(%arg0 : f32, %arg1 : i32) -> () { + // CHECK: {{%.*}} = spv.GLSL.Ldexp {{%.*}} : f32, {{%.*}} : i32 -> f32 + %0 = spv.GLSL.Ldexp %arg0 : f32, %arg1 : i32 -> f32 + return +} + +// ----- +func @ldexp_vec(%arg0 : vector<3xf32>, %arg1 : vector<3xi32>) -> () { + // CHECK: {{%.*}} = spv.GLSL.Ldexp {{%.*}} : vector<3xf32>, {{%.*}} : vector<3xi32> -> vector<3xf32> + %0 = spv.GLSL.Ldexp %arg0 : vector<3xf32>, %arg1 : vector<3xi32> -> vector<3xf32> + return +} + +// ----- + +func @ldexp_wrong_type_scalar(%arg0 : f32, %arg1 : vector<2xi32>) -> () { + // expected-error @+1 {{operands must both be scalars or vectors}} + %0 = spv.GLSL.Ldexp %arg0 : f32, %arg1 : vector<2xi32> -> f32 + return +} + +// ----- + +func @ldexp_wrong_type_vec_1(%arg0 : vector<3xf32>, %arg1 : i32) -> () { + // expected-error @+1 {{operands must both be scalars or vectors}} + %0 = spv.GLSL.Ldexp %arg0 : vector<3xf32>, %arg1 : i32 -> vector<3xf32> + return +} + +// ----- + +func @ldexp_wrong_type_vec_2(%arg0 : vector<3xf32>, %arg1 : vector<2xi32>) -> () { + // expected-error @+1 {{operands must have the same number of elements}} + %0 = spv.GLSL.Ldexp %arg0 : vector<3xf32>, %arg1 : vector<2xi32> -> vector<3xf32> + return +} diff --git a/mlir/test/Target/SPIRV/glsl-ops.mlir b/mlir/test/Target/SPIRV/glsl-ops.mlir --- a/mlir/test/Target/SPIRV/glsl-ops.mlir +++ b/mlir/test/Target/SPIRV/glsl-ops.mlir @@ -1,7 +1,7 @@ // RUN: mlir-translate -test-spirv-roundtrip %s | FileCheck %s spv.module Logical GLSL450 requires #spv.vce { - spv.func @fmul(%arg0 : f32, %arg1 : f32) "None" { + spv.func @fmul(%arg0 : f32, %arg1 : f32, %arg2 : i32) "None" { // CHECK: {{%.*}} = spv.GLSL.Exp {{%.*}} : f32 %0 = spv.GLSL.Exp %arg0 : f32 // CHECK: {{%.*}} = spv.GLSL.FMax {{%.*}}, {{%.*}} : f32 @@ -30,6 +30,8 @@ %12 = spv.GLSL.Round %arg0 : f32 // CHECK: {{%.*}} = spv.GLSL.FrexpStruct {{%.*}} : f32 -> !spv.struct<(f32, i32)> %13 = spv.GLSL.FrexpStruct %arg0 : f32 -> !spv.struct<(f32, i32)> + // CHECK: {{%.*}} = spv.GLSL.Ldexp {{%.*}} : f32, {{%.*}} : i32 -> f32 + %14 = spv.GLSL.Ldexp %arg0 : f32, %arg2 : i32 -> f32 spv.Return }