diff --git a/clang/test/CodeGen/X86/strictfp_builtins.c b/clang/test/CodeGen/X86/strictfp_builtins.c index 43e4060bef259..75ed3a2555b3d 100644 --- a/clang/test/CodeGen/X86/strictfp_builtins.c +++ b/clang/test/CodeGen/X86/strictfp_builtins.c @@ -27,7 +27,7 @@ void p(char *str, int x) { // CHECK-NEXT: [[LD_ADDR:%.*]] = alloca x86_fp80, align 16 // CHECK-NEXT: store x86_fp80 [[LD:%.*]], ptr [[LD_ADDR]], align 16 // CHECK-NEXT: [[TMP0:%.*]] = load x86_fp80, ptr [[LD_ADDR]], align 16 -// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f80(x86_fp80 [[TMP0]], i32 516) #[[ATTR3]] +// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f80(x86_fp80 [[TMP0]], i32 516) #[[ATTR4:[0-9]+]] // CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i32 // CHECK-NEXT: call void @p(ptr noundef @.str.1, i32 noundef [[TMP2]]) #[[ATTR3]] // CHECK-NEXT: ret void @@ -43,7 +43,7 @@ void test_long_double_isinf(long double ld) { // CHECK-NEXT: [[LD_ADDR:%.*]] = alloca x86_fp80, align 16 // CHECK-NEXT: store x86_fp80 [[LD:%.*]], ptr [[LD_ADDR]], align 16 // CHECK-NEXT: [[TMP0:%.*]] = load x86_fp80, ptr [[LD_ADDR]], align 16 -// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f80(x86_fp80 [[TMP0]], i32 504) #[[ATTR3]] +// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f80(x86_fp80 [[TMP0]], i32 504) #[[ATTR4]] // CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i32 // CHECK-NEXT: call void @p(ptr noundef @.str.2, i32 noundef [[TMP2]]) #[[ATTR3]] // CHECK-NEXT: ret void @@ -59,7 +59,7 @@ void test_long_double_isfinite(long double ld) { // CHECK-NEXT: [[LD_ADDR:%.*]] = alloca x86_fp80, align 16 // CHECK-NEXT: store x86_fp80 [[LD:%.*]], ptr [[LD_ADDR]], align 16 // CHECK-NEXT: [[TMP0:%.*]] = load x86_fp80, ptr [[LD_ADDR]], align 16 -// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f80(x86_fp80 [[TMP0]], i32 3) #[[ATTR3]] +// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f80(x86_fp80 [[TMP0]], i32 3) #[[ATTR4]] // CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i32 // CHECK-NEXT: call void @p(ptr noundef @.str.3, i32 noundef [[TMP2]]) #[[ATTR3]] // CHECK-NEXT: ret void diff --git a/clang/test/CodeGen/cx-complex-range.c b/clang/test/CodeGen/cx-complex-range.c index 88300041061aa..822f75d5157d3 100644 --- a/clang/test/CodeGen/cx-complex-range.c +++ b/clang/test/CodeGen/cx-complex-range.c @@ -1575,8 +1575,8 @@ _Complex float mulf(_Complex float a, _Complex float b) { // X86WINPRMTD_STRICT-NEXT: [[B_REAL:%.*]] = load double, ptr [[B_REALP]], align 8 // X86WINPRMTD_STRICT-NEXT: [[B_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[B]], i32 0, i32 1 // X86WINPRMTD_STRICT-NEXT: [[B_IMAG:%.*]] = load double, ptr [[B_IMAGP]], align 8 -// X86WINPRMTD_STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.fabs.f64(double [[B_REAL]]) #[[ATTR3]] -// X86WINPRMTD_STRICT-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[B_IMAG]]) #[[ATTR3]] +// X86WINPRMTD_STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.fabs.f64(double [[B_REAL]]) #[[ATTR4:[0-9]+]] +// X86WINPRMTD_STRICT-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[B_IMAG]]) #[[ATTR4]] // X86WINPRMTD_STRICT-NEXT: [[ABS_CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double [[TMP1]], metadata !"ugt", metadata !"fpexcept.strict") #[[ATTR3]] // X86WINPRMTD_STRICT-NEXT: br i1 [[ABS_CMP]], label [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI:%.*]], label [[ABS_RHSR_LESS_THAN_ABS_RHSI:%.*]] // X86WINPRMTD_STRICT: abs_rhsr_greater_or_equal_abs_rhsi: @@ -2658,8 +2658,8 @@ _Complex double muld(_Complex double a, _Complex double b) { // X86WINPRMTD_STRICT-NEXT: [[B_REAL:%.*]] = load double, ptr [[B_REALP]], align 8 // X86WINPRMTD_STRICT-NEXT: [[B_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[B]], i32 0, i32 1 // X86WINPRMTD_STRICT-NEXT: [[B_IMAG:%.*]] = load double, ptr [[B_IMAGP]], align 8 -// X86WINPRMTD_STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.fabs.f64(double [[B_REAL]]) #[[ATTR3]] -// X86WINPRMTD_STRICT-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[B_IMAG]]) #[[ATTR3]] +// X86WINPRMTD_STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.fabs.f64(double [[B_REAL]]) #[[ATTR4]] +// X86WINPRMTD_STRICT-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[B_IMAG]]) #[[ATTR4]] // X86WINPRMTD_STRICT-NEXT: [[ABS_CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double [[TMP1]], metadata !"ugt", metadata !"fpexcept.strict") #[[ATTR3]] // X86WINPRMTD_STRICT-NEXT: br i1 [[ABS_CMP]], label [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI:%.*]], label [[ABS_RHSR_LESS_THAN_ABS_RHSI:%.*]] // X86WINPRMTD_STRICT: abs_rhsr_greater_or_equal_abs_rhsi: @@ -2713,8 +2713,8 @@ _Complex double muld(_Complex double a, _Complex double b) { // PRMTD_STRICT-NEXT: [[B_REAL:%.*]] = load x86_fp80, ptr [[B_REALP]], align 16 // PRMTD_STRICT-NEXT: [[B_IMAGP:%.*]] = getelementptr inbounds nuw { x86_fp80, x86_fp80 }, ptr [[B]], i32 0, i32 1 // PRMTD_STRICT-NEXT: [[B_IMAG:%.*]] = load x86_fp80, ptr [[B_IMAGP]], align 16 -// PRMTD_STRICT-NEXT: [[TMP0:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[B_REAL]]) #[[ATTR4]] -// PRMTD_STRICT-NEXT: [[TMP1:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[B_IMAG]]) #[[ATTR4]] +// PRMTD_STRICT-NEXT: [[TMP0:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[B_REAL]]) #[[ATTR5:[0-9]+]] +// PRMTD_STRICT-NEXT: [[TMP1:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[B_IMAG]]) #[[ATTR5]] // PRMTD_STRICT-NEXT: [[ABS_CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f80(x86_fp80 [[TMP0]], x86_fp80 [[TMP1]], metadata !"ugt", metadata !"fpexcept.strict") #[[ATTR4]] // PRMTD_STRICT-NEXT: br i1 [[ABS_CMP]], label [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI:%.*]], label [[ABS_RHSR_LESS_THAN_ABS_RHSI:%.*]] // PRMTD_STRICT: abs_rhsr_greater_or_equal_abs_rhsi: @@ -3961,8 +3961,8 @@ _Complex long double mulld(_Complex long double a, _Complex long double b) { // X86WINPRMTD_STRICT-NEXT: [[C_IMAG:%.*]] = load float, ptr [[C_IMAGP]], align 4 // X86WINPRMTD_STRICT-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[C_REAL]], metadata !"fpexcept.strict") #[[ATTR3]] // X86WINPRMTD_STRICT-NEXT: [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[C_IMAG]], metadata !"fpexcept.strict") #[[ATTR3]] -// X86WINPRMTD_STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.fabs.f64(double [[CONV]]) #[[ATTR3]] -// X86WINPRMTD_STRICT-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[CONV1]]) #[[ATTR3]] +// X86WINPRMTD_STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.fabs.f64(double [[CONV]]) #[[ATTR4]] +// X86WINPRMTD_STRICT-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[CONV1]]) #[[ATTR4]] // X86WINPRMTD_STRICT-NEXT: [[ABS_CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double [[TMP1]], metadata !"ugt", metadata !"fpexcept.strict") #[[ATTR3]] // X86WINPRMTD_STRICT-NEXT: br i1 [[ABS_CMP]], label [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI:%.*]], label [[ABS_RHSR_LESS_THAN_ABS_RHSI:%.*]] // X86WINPRMTD_STRICT: abs_rhsr_greater_or_equal_abs_rhsi: @@ -4038,8 +4038,8 @@ _Complex long double mulld(_Complex long double a, _Complex long double b) { // PRMTD_STRICT-NEXT: [[C_IMAG:%.*]] = load float, ptr [[C_IMAGP]], align 4 // PRMTD_STRICT-NEXT: [[CONV:%.*]] = call x86_fp80 @llvm.experimental.constrained.fpext.f80.f32(float [[C_REAL]], metadata !"fpexcept.strict") #[[ATTR4]] // PRMTD_STRICT-NEXT: [[CONV1:%.*]] = call x86_fp80 @llvm.experimental.constrained.fpext.f80.f32(float [[C_IMAG]], metadata !"fpexcept.strict") #[[ATTR4]] -// PRMTD_STRICT-NEXT: [[TMP0:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[CONV]]) #[[ATTR4]] -// PRMTD_STRICT-NEXT: [[TMP1:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[CONV1]]) #[[ATTR4]] +// PRMTD_STRICT-NEXT: [[TMP0:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[CONV]]) #[[ATTR5]] +// PRMTD_STRICT-NEXT: [[TMP1:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[CONV1]]) #[[ATTR5]] // PRMTD_STRICT-NEXT: [[ABS_CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f80(x86_fp80 [[TMP0]], x86_fp80 [[TMP1]], metadata !"ugt", metadata !"fpexcept.strict") #[[ATTR4]] // PRMTD_STRICT-NEXT: br i1 [[ABS_CMP]], label [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI:%.*]], label [[ABS_RHSR_LESS_THAN_ABS_RHSI:%.*]] // PRMTD_STRICT: abs_rhsr_greater_or_equal_abs_rhsi: diff --git a/clang/test/CodeGen/strictfp-elementwise-bulitins.cpp b/clang/test/CodeGen/strictfp-elementwise-bulitins.cpp index 175ad22601839..1c1938721502d 100644 --- a/clang/test/CodeGen/strictfp-elementwise-bulitins.cpp +++ b/clang/test/CodeGen/strictfp-elementwise-bulitins.cpp @@ -20,7 +20,7 @@ float4 strict_fadd(float4 a, float4 b) { // CHECK-LABEL: define dso_local noundef <4 x float> @_Z22strict_elementwise_absDv4_f // CHECK-SAME: (<4 x float> noundef [[A:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { // CHECK-NEXT: entry: -// CHECK-NEXT: [[ELT_ABS:%.*]] = tail call <4 x float> @llvm.fabs.v4f32(<4 x float> [[A]]) #[[ATTR4]] +// CHECK-NEXT: [[ELT_ABS:%.*]] = tail call <4 x float> @llvm.fabs.v4f32(<4 x float> [[A]]) #[[ATTR5:[0-9]+]] // CHECK-NEXT: ret <4 x float> [[ELT_ABS]] // float4 strict_elementwise_abs(float4 a) { @@ -300,7 +300,7 @@ float4 strict_elementwise_trunc(float4 a) { // CHECK-LABEL: define dso_local noundef <4 x float> @_Z31strict_elementwise_canonicalizeDv4_f // CHECK-SAME: (<4 x float> noundef [[A:%.*]]) local_unnamed_addr #[[ATTR2]] { // CHECK-NEXT: entry: -// CHECK-NEXT: [[ELT_CANONICALIZE:%.*]] = tail call <4 x float> @llvm.canonicalize.v4f32(<4 x float> [[A]]) #[[ATTR4]] +// CHECK-NEXT: [[ELT_CANONICALIZE:%.*]] = tail call <4 x float> @llvm.canonicalize.v4f32(<4 x float> [[A]]) #[[ATTR5]] // CHECK-NEXT: ret <4 x float> [[ELT_CANONICALIZE]] // float4 strict_elementwise_canonicalize(float4 a) { @@ -310,7 +310,7 @@ float4 strict_elementwise_canonicalize(float4 a) { // CHECK-LABEL: define dso_local noundef <4 x float> @_Z27strict_elementwise_copysignDv4_fS_ // CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR2]] { // CHECK-NEXT: entry: -// CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x float> @llvm.copysign.v4f32(<4 x float> [[A]], <4 x float> [[B]]) #[[ATTR4]] +// CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x float> @llvm.copysign.v4f32(<4 x float> [[A]], <4 x float> [[B]]) #[[ATTR5]] // CHECK-NEXT: ret <4 x float> [[TMP0]] // float4 strict_elementwise_copysign(float4 a, float4 b) { diff --git a/clang/test/CodeGen/strictfp_builtins.c b/clang/test/CodeGen/strictfp_builtins.c index 58815c7de4fa9..c769a0cfc4584 100644 --- a/clang/test/CodeGen/strictfp_builtins.c +++ b/clang/test/CodeGen/strictfp_builtins.c @@ -31,21 +31,21 @@ void p(char *str, int x) { // CHECK-NEXT: [[D_ADDR:%.*]] = alloca double, align 8 // CHECK-NEXT: store double [[D:%.*]], ptr [[D_ADDR]], align 8 // CHECK-NEXT: [[TMP0:%.*]] = load double, ptr [[D_ADDR]], align 8 -// CHECK-NEXT: [[ISZERO:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double 0.000000e+00, metadata !"oeq", metadata !"fpexcept.strict") #[[ATTR4]] +// CHECK-NEXT: [[ISZERO:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double 0.000000e+00, metadata !"oeq", metadata !"fpexcept.strict") #[[ATTR5:[0-9]+]] [ "fp.except"(metadata !"strict") ] // CHECK-NEXT: br i1 [[ISZERO]], label [[FPCLASSIFY_END:%.*]], label [[FPCLASSIFY_NOT_ZERO:%.*]] // CHECK: fpclassify_end: // CHECK-NEXT: [[FPCLASSIFY_RESULT:%.*]] = phi i32 [ 4, [[ENTRY:%.*]] ], [ 0, [[FPCLASSIFY_NOT_ZERO]] ], [ 1, [[FPCLASSIFY_NOT_NAN:%.*]] ], [ [[TMP2:%.*]], [[FPCLASSIFY_NOT_INF:%.*]] ] // CHECK-NEXT: call void @p(ptr noundef @.str.1, i32 noundef [[FPCLASSIFY_RESULT]]) #[[ATTR4]] // CHECK-NEXT: ret void // CHECK: fpclassify_not_zero: -// CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double [[TMP0]], metadata !"uno", metadata !"fpexcept.strict") #[[ATTR4]] +// CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP0]], double [[TMP0]], metadata !"uno", metadata !"fpexcept.strict") #[[ATTR5]] [ "fp.except"(metadata !"strict") ] // CHECK-NEXT: br i1 [[CMP]], label [[FPCLASSIFY_END]], label [[FPCLASSIFY_NOT_NAN]] // CHECK: fpclassify_not_nan: -// CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[TMP0]]) #[[ATTR5:[0-9]+]] -// CHECK-NEXT: [[ISINF:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP1]], double 0x7FF0000000000000, metadata !"oeq", metadata !"fpexcept.strict") #[[ATTR4]] +// CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[TMP0]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[ISINF:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP1]], double 0x7FF0000000000000, metadata !"oeq", metadata !"fpexcept.strict") #[[ATTR5]] [ "fp.except"(metadata !"strict") ] // CHECK-NEXT: br i1 [[ISINF]], label [[FPCLASSIFY_END]], label [[FPCLASSIFY_NOT_INF]] // CHECK: fpclassify_not_inf: -// CHECK-NEXT: [[ISNORMAL:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP1]], double 0x10000000000000, metadata !"uge", metadata !"fpexcept.strict") #[[ATTR4]] +// CHECK-NEXT: [[ISNORMAL:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP1]], double 0x10000000000000, metadata !"uge", metadata !"fpexcept.strict") #[[ATTR5]] [ "fp.except"(metadata !"strict") ] // CHECK-NEXT: [[TMP2]] = select i1 [[ISNORMAL]], i32 2, i32 3 // CHECK-NEXT: br label [[FPCLASSIFY_END]] // @@ -156,8 +156,8 @@ void test_double_isfinite(double d) { // CHECK-NEXT: [[D_ADDR:%.*]] = alloca double, align 8 // CHECK-NEXT: store double [[D:%.*]], ptr [[D_ADDR]], align 8 // CHECK-NEXT: [[TMP0:%.*]] = load double, ptr [[D_ADDR]], align 8 -// CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[TMP0]]) #[[ATTR5]] -// CHECK-NEXT: [[ISINF:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP1]], double 0x7FF0000000000000, metadata !"oeq", metadata !"fpexcept.strict") #[[ATTR4]] +// CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[TMP0]]) #[[ATTR6]] +// CHECK-NEXT: [[ISINF:%.*]] = call i1 @llvm.experimental.constrained.fcmp.f64(double [[TMP1]], double 0x7FF0000000000000, metadata !"oeq", metadata !"fpexcept.strict") #[[ATTR5]] [ "fp.except"(metadata !"strict") ] // CHECK-NEXT: [[TMP2:%.*]] = bitcast double [[TMP0]] to i64 // CHECK-NEXT: [[TMP3:%.*]] = icmp slt i64 [[TMP2]], 0 // CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i32 -1, i32 1 diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl index 451d30b4d86f0..817662b705e02 100644 --- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl +++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl @@ -144,7 +144,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) { // STRICTFP-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[BLOCK_CAPTURE_ADDR1]], align 4 // STRICTFP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[TMP0]], i32 [[TMP1]] // STRICTFP-NEXT: [[TMP2:%.*]] = load float, ptr addrspace(1) [[ARRAYIDX]], align 4 -// STRICTFP-NEXT: [[TMP3:%.*]] = call float @llvm.experimental.constrained.fmuladd.f32(float 4.000000e+00, float [[TMP2]], float 1.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR5]] +// STRICTFP-NEXT: [[TMP3:%.*]] = call float @llvm.experimental.constrained.fmuladd.f32(float 4.000000e+00, float [[TMP2]], float 1.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6:[0-9]+]] [ "fp.control"(metadata !"rte"), "fp.except"(metadata !"strict") ] // STRICTFP-NEXT: [[BLOCK_CAPTURE_ADDR2:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr addrspace(4), ptr addrspace(1), i32, ptr addrspace(1) }>, ptr addrspace(4) [[DOTBLOCK_DESCRIPTOR]], i32 0, i32 3 // STRICTFP-NEXT: [[TMP4:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[BLOCK_CAPTURE_ADDR2]], align 4 // STRICTFP-NEXT: [[BLOCK_CAPTURE_ADDR3:%.*]] = getelementptr inbounds nuw <{ i32, i32, ptr addrspace(4), ptr addrspace(1), i32, ptr addrspace(1) }>, ptr addrspace(4) [[DOTBLOCK_DESCRIPTOR]], i32 0, i32 4 @@ -174,6 +174,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) { // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) } // STRICTFP: attributes #[[ATTR4]] = { convergent nounwind "stack-protector-buffer-size"="8" } // STRICTFP: attributes #[[ATTR5]] = { strictfp } +// STRICTFP: attributes #[[ATTR6]] = { strictfp memory(inaccessiblemem: readwrite) } //. // SPIR32: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} // SPIR32: [[META1:![0-9]+]] = !{i32 2, i32 0} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index deb87365ae8d7..d3e1335ebc454 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3057,6 +3057,49 @@ A "convergencectrl" operand bundle is only valid on a ``convergent`` operation. When present, the operand bundle must contain exactly one value of token type. See the :doc:`ConvergentOperations` document for details. +.. _ob_fp: + +Floating-point Operand Bundles +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These operand bundles are used for calls that involve floating-point +operations and interact with :ref:`floating-point environment ` or +depend on floating-point options, such as rounding mode, denormal modes, etc. +There are two kinds of such operand bundles, which represent the value of +floating-point control modes and the treatment of status bits respectively. + +An operand bundle tagged with "fp.control" contains information about the +control modes used for the operation execution. Currently, only rounding mode is +supported. It is represented by a metadata string value, which specifies the +rounding mode to be used for the operation evaluation. Possible values are: + +:: + + "rtz" - toward zero + "rte" - to nearest, ties to even + "rtp" - toward positive infinity + "rtn" - toward negative infinity + "rmm" - to nearest, ties away from zero + "dyn" - rounding mode is taken from control register + +If "fp.control" is absent, the default rounding rounding mode is taken from the +control register (dynamic rounding). In the particular case of +:ref:`default floating-point environment `, it must be rounding to +nearest, ties to even. + +An operand bundle tagged with "fp.except" may be associated with operations +that can read or write floating-point exception flags. It contains a single +metadata string value, which can have one of the following values: + +:: + + "ignore" + "strict" + "maytrap" + +It has the same meaning as the corresponding argument in +:ref:`constrained intrinsics `. + .. _moduleasm: Module-Level Inline Assembly @@ -3751,9 +3794,9 @@ round-to-nearest rounding mode, and subnormals are assumed to be preserved. Running LLVM code in an environment where these assumptions are not met typically leads to undefined behavior. The ``strictfp`` and ``denormal-fp-math`` attributes as well as :ref:`Constrained Floating-Point Intrinsics -` can be used to weaken LLVM's assumptions and ensure defined -behavior in non-default floating-point environments; see their respective -documentation for details. +` or :ref:`floating-point operand bundles` can be used to +weaken LLVM's assumptions and ensure defined behavior in non-default +floating-point environments; see their respective documentation for details. .. _floatnan: @@ -3805,7 +3848,8 @@ Floating-point math operations are allowed to treat all NaNs as if they were quiet NaNs. For example, "pow(1.0, SNaN)" may be simplified to 1.0. Code that requires different behavior than this should use the -:ref:`Constrained Floating-Point Intrinsics `. +:ref:`Constrained Floating-Point Intrinsics ` or +:ref:`floating-point operand bundles`. In particular, constrained intrinsics rule out the "Unchanged NaN propagation" case; they are guaranteed to return a QNaN. @@ -17252,7 +17296,7 @@ would, and handles error conditions in the same way. Since LLVM assumes the :ref:`default floating-point environment `, the rounding mode is assumed to be set to "nearest", so halfway cases are rounded to the even integer. Use :ref:`Constrained Floating-Point Intrinsics ` -to avoid that assumption. +or :ref:`floating-point operand bundles` to avoid that assumption. .. _int_nearbyint: @@ -17293,8 +17337,8 @@ This function returns the same values as the libm ``nearbyint`` functions would, and handles error conditions in the same way. Since LLVM assumes the :ref:`default floating-point environment `, the rounding mode is assumed to be set to "nearest", so halfway cases are rounded to the even -integer. Use :ref:`Constrained Floating-Point Intrinsics ` to -avoid that assumption. +integer. Use :ref:`Constrained Floating-Point Intrinsics ` or +:ref:`floating-point operand bundles` to avoid that assumption. .. _int_round: diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index c9543ff09217a..19fe1fcb15033 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -61,6 +61,7 @@ Changes to the LLVM IR removed: * `mul` +* Floating-point operand bundles have been added. Changes to LLVM infrastructure ------------------------------ diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h index c01de4a289a69..8d36d9681b470 100644 --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -567,6 +567,11 @@ namespace llvm { bool resolveFunctionType(Type *RetType, ArrayRef ArgList, FunctionType *&FuncTy); + void updateConstrainedIntrinsic(ValID &CalleeID, + SmallVectorImpl &Args, + SmallVectorImpl &Bundles, + AttrBuilder &FnAttrs); + // Constant Parsing. bool parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy = nullptr); diff --git a/llvm/include/llvm/IR/FPEnv.h b/llvm/include/llvm/IR/FPEnv.h index a0197377759da..58a0c1956598c 100644 --- a/llvm/include/llvm/IR/FPEnv.h +++ b/llvm/include/llvm/IR/FPEnv.h @@ -46,19 +46,23 @@ enum ExceptionBehavior : uint8_t { /// Returns a valid RoundingMode enumerator when given a string /// that is valid as input in constrained intrinsic rounding mode /// metadata. -std::optional convertStrToRoundingMode(StringRef); +std::optional convertStrToRoundingMode(StringRef, + bool InBundle = false); /// For any RoundingMode enumerator, returns a string valid as input in /// constrained intrinsic rounding mode metadata. -std::optional convertRoundingModeToStr(RoundingMode); +std::optional convertRoundingModeToStr(RoundingMode, + bool InBundle = false); /// Returns a valid ExceptionBehavior enumerator when given a string /// valid as input in constrained intrinsic exception behavior metadata. -std::optional convertStrToExceptionBehavior(StringRef); +std::optional +convertStrToExceptionBehavior(StringRef, bool InBundle = false); /// For any ExceptionBehavior enumerator, returns a string valid as /// input in constrained intrinsic exception behavior metadata. -std::optional convertExceptionBehaviorToStr(fp::ExceptionBehavior); +std::optional convertExceptionBehaviorToStr(fp::ExceptionBehavior, + bool InBundle = false); /// Returns true if the exception handling behavior and rounding mode /// match what is used in the default floating point environment. diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 933dbb306d1fc..a81870e9ff8d2 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -997,6 +997,16 @@ class IRBuilderBase { ArrayRef Args, FMFSource FMFSource = {}, const Twine &Name = ""); + /// Create a call to intrinsic \p ID with \p Args, mangled using \p Types and + /// with operand bundles. + /// If \p FMFSource is provided, copy fast-math-flags from that instruction to + /// the intrinsic. + CallInst *CreateIntrinsic(Intrinsic::ID ID, ArrayRef Types, + ArrayRef Args, + ArrayRef OpBundles, + Instruction *FMFSource = nullptr, + const Twine &Name = ""); + /// Create a call to intrinsic \p ID with \p RetTy and \p Args. If /// \p FMFSource is provided, copy fast-math-flags from that instruction to /// the intrinsic. @@ -2449,24 +2459,13 @@ class IRBuilderBase { CallInst *CreateCall(FunctionType *FTy, Value *Callee, ArrayRef Args = {}, const Twine &Name = "", MDNode *FPMathTag = nullptr) { - CallInst *CI = CallInst::Create(FTy, Callee, Args, DefaultOperandBundles); - if (IsFPConstrained) - setConstrainedFPCallAttr(CI); - if (isa(CI)) - setFPAttrs(CI, FPMathTag, FMF); - return Insert(CI, Name); + return CreateCall(FTy, Callee, Args, DefaultOperandBundles, Name, + FPMathTag); } CallInst *CreateCall(FunctionType *FTy, Value *Callee, ArrayRef Args, ArrayRef OpBundles, - const Twine &Name = "", MDNode *FPMathTag = nullptr) { - CallInst *CI = CallInst::Create(FTy, Callee, Args, OpBundles); - if (IsFPConstrained) - setConstrainedFPCallAttr(CI); - if (isa(CI)) - setFPAttrs(CI, FPMathTag, FMF); - return Insert(CI, Name); - } + const Twine &Name = "", MDNode *FPMathTag = nullptr); CallInst *CreateCall(FunctionCallee Callee, ArrayRef Args = {}, const Twine &Name = "", MDNode *FPMathTag = nullptr) { @@ -2688,6 +2687,13 @@ class IRBuilderBase { /// Create an assume intrinsic call that represents an dereferencable /// assumption on the provided pointer. CallInst *CreateDereferenceableAssumption(Value *PtrValue, Value *SizeValue); + + void + createFPRoundingBundle(SmallVectorImpl &Bundles, + std::optional Rounding = std::nullopt); + void createFPExceptionBundle( + SmallVectorImpl &Bundles, + std::optional Except = std::nullopt); }; /// This provides a uniform API for creating instructions and inserting diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index 8e47e3c7b3a7c..157f73601469b 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -25,6 +25,7 @@ #include "llvm/IR/CallingConv.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/FMF.h" +#include "llvm/IR/FPEnv.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/LLVMContext.h" @@ -1091,6 +1092,13 @@ template class OperandBundleDefT { using OperandBundleDef = OperandBundleDefT; using ConstOperandBundleDef = OperandBundleDefT; +void addFPRoundingBundle(LLVMContext &Ctx, + SmallVectorImpl &Bundles, + RoundingMode Rounding); +void addFPExceptionBundle(LLVMContext &Ctx, + SmallVectorImpl &Bundles, + fp::ExceptionBehavior Except); + //===----------------------------------------------------------------------===// // CallBase Class //===----------------------------------------------------------------------===// @@ -1150,6 +1158,8 @@ class CallBase : public Instruction { /// number of extra operands. unsigned getNumSubclassExtraOperandsDynamic() const; + MemoryEffects getFloatingPointMemoryEffects() const; + public: using Instruction::getContext; @@ -2155,6 +2165,15 @@ class CallBase : public Instruction { return false; } + /// Return rounding mode specified by operand bundles. + std::optional getRoundingMode() const; + + /// Return exception behavior specified by operand bundles. + std::optional getExceptionBehavior() const; + + // Does the called function have floating-point bundles? + bool hasFloatingPointBundles() const; + /// Used to keep track of an operand bundle. See the main comment on /// OperandBundleUser above. struct BundleOpInfo { diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h index 93750d6e3845e..5cf5d60b1e7c5 100644 --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -128,6 +128,10 @@ class IntrinsicInst : public CallInst { /// course of IR transformations static bool mayLowerToFunctionCall(Intrinsic::ID IID); + /// Check if the specified intrinsic can read or write FP environment. + /// Constrained intrinsics are not handled in it. + static bool canAccessFPEnvironment(Intrinsic::ID IID); + /// Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const CallInst *I) { if (const Function *CF = I->getCalledFunction()) @@ -139,6 +143,9 @@ class IntrinsicInst : public CallInst { } }; +std::optional getRoundingModeArg(const CallBase &I); +std::optional getExceptionBehaviorArg(const CallBase &I); + /// Check if \p ID corresponds to a lifetime intrinsic. static inline bool isLifetimeIntrinsic(Intrinsic::ID ID) { switch (ID) { @@ -723,8 +730,6 @@ class VPBinOpIntrinsic : public VPIntrinsic { class ConstrainedFPIntrinsic : public IntrinsicInst { public: unsigned getNonMetadataArgCount() const; - std::optional getRoundingMode() const; - std::optional getExceptionBehavior() const; bool isDefaultFPEnvironment() const; // Methods for support type inquiry through isa, cast, and dyn_cast: diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index bbd125fd38cf1..b2af2b2bd9148 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -96,6 +96,8 @@ class LLVMContext { OB_ptrauth = 7, // "ptrauth" OB_kcfi = 8, // "kcfi" OB_convergencectrl = 9, // "convergencectrl" + OB_fp_control = 10, // "fp.control" + OB_fp_except = 11, // "fp.except" }; /// getMDKindID - Return a unique non-zero ID for the specified metadata kind. diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h index 7f58f5236aedd..8e9ade26932b0 100644 --- a/llvm/include/llvm/Support/ModRef.h +++ b/llvm/include/llvm/Support/ModRef.h @@ -224,6 +224,10 @@ template class MemoryEffectsBase { return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory(); } + bool doesAccessInaccessibleMem() const { + return isModOrRefSet(getModRef(Location::InaccessibleMem)); + } + /// Whether this function only (at most) accesses errno memory. bool onlyAccessesErrnoMem() const { return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory(); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 37103937c92a7..3a90818018a04 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -6419,6 +6419,52 @@ bool isOldDbgFormatIntrinsic(StringRef Name) { FnID == Intrinsic::dbg_assign; } +void LLParser::updateConstrainedIntrinsic( + ValID &CalleeID, SmallVectorImpl &Args, + SmallVectorImpl &Bundles, AttrBuilder &FnAttrs) { + if (Args.empty()) + return; + + StringRef Name = CalleeID.StrVal; + if (!Name.consume_front("llvm.experimental.constrained.")) + return; + + for (auto &B : Bundles) { + if (B.getTag().starts_with("fp.")) + return; + } + + const auto getMetadataArgumentValue = [](Value *Arg) -> StringRef { + if (auto *MAV = dyn_cast(Arg)) { + if (const auto *MD = MAV->getMetadata()) { + if (auto MDStr = dyn_cast(MD)) + return MDStr->getString(); + } + } + return StringRef(); + }; + + if (Args.size() > 1) { + Value *V = Args[Args.size() - 2].V; + StringRef VStr = getMetadataArgumentValue(V); + if (!VStr.empty()) { + if (auto RM = convertStrToRoundingMode(VStr)) + addFPRoundingBundle(Context, Bundles, *RM); + } + } + + Value *V = Args.back().V; + StringRef VStr = getMetadataArgumentValue(V); + if (!VStr.empty()) { + if (auto EB = convertStrToExceptionBehavior(VStr)) + addFPExceptionBundle(Context, Bundles, *EB); + } + + MemoryEffects ME = MemoryEffects::inaccessibleMemOnly(); + FnAttrs.addAttribute(Attribute::getWithMemoryEffects(Context, ME)); + FnAttrs.addAttribute(Attribute::StrictFP); +} + /// FunctionHeader /// ::= OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility /// OptionalCallingConv OptRetAttrs OptUnnamedAddr Type GlobalName @@ -8146,6 +8192,8 @@ bool LLParser::parseCall(Instruction *&Inst, PerFunctionState &PFS, parseOptionalOperandBundles(BundleList, PFS)) return true; + updateConstrainedIntrinsic(CalleeID, ArgList, BundleList, FnAttrs); + // If RetType is a non-function pointer type, then this is the short syntax // for the call, which means that RetType is just the return type. Infer the // rest of the function argument types from the arguments that are present. diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index d687495c42de6..44b751c8829c3 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -7165,9 +7165,11 @@ Error BitcodeReader::materializeModule() { if (CallInst *CI = dyn_cast(U)) UpgradeIntrinsicCall(CI, I.second); } - if (!I.first->use_empty()) - I.first->replaceAllUsesWith(I.second); - I.first->eraseFromParent(); + if (I.second) { + if (!I.first->use_empty()) + I.first->replaceAllUsesWith(I.second); + I.first->eraseFromParent(); + } } UpgradedIntrinsics.clear(); diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index 57072715366c9..543345a5a63ee 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -1203,6 +1203,8 @@ static bool upgradeIntrinsicFunction1(Function *F, Function *&NewFn, F->getParent(), ID, F->getFunctionType()->getReturnType()); return true; } + if (Name.starts_with("experimental.constrained.")) + return true; break; // No other 'e*'. case 'f': if (Name.starts_with("flt.rounds")) { @@ -4326,6 +4328,53 @@ static void upgradeDbgIntrinsicToDbgRecord(StringRef Name, CallBase *CI) { CI->getParent()->insertDbgRecordBefore(DR, CI->getIterator()); } +static CallBase *upgradeConstrainedIntrinsicCall(CallBase *CB, Function *F, + IRBuilder<> &Builder) { + if (CB->hasFloatingPointBundles()) + return nullptr; + + SmallVector NewBundles; + + auto RM = getRoundingModeArg(*CB); + if (RM) { + auto CurrentRM = CB->getRoundingMode(); + assert(!CurrentRM && "unexpected rounding bundle"); + Builder.createFPRoundingBundle(NewBundles, RM); + } + + auto EB = getExceptionBehaviorArg(*CB); + if (EB) { + auto CurrentEB = CB->getExceptionBehavior(); + assert(!CurrentEB && "unexpected exception bundle"); + Builder.createFPExceptionBundle(NewBundles, EB); + } + + CallInst *NewCB = nullptr; + if (!NewBundles.empty()) { + SmallVector Args(CB->args()); + SmallVector Bundles; + CB->getOperandBundlesAsDefs(Bundles); + Bundles.append(NewBundles); + + Builder.SetInsertPoint(CB->getParent(), CB->getIterator()); + NewCB = Builder.CreateCall(F, Args, Bundles, CB->getName()); + NewCB->copyMetadata(*CB); + AttributeList Attrs = CB->getAttributes(); + NewCB->setAttributes(Attrs); + if (isa(CB)) { + FastMathFlags FMF = CB->getFastMathFlags(); + NewCB->setFastMathFlags(FMF); + } + + MemoryEffects ME = MemoryEffects::inaccessibleMemOnly(); + auto A = Attribute::getWithMemoryEffects(CB->getContext(), ME); + NewCB->addFnAttr(A); + NewCB->addFnAttr(Attribute::StrictFP); + } + + return NewCB; +} + /// Upgrade a call to an old intrinsic. All argument and return casting must be /// provided to seamlessly integrate with existing context. void llvm::UpgradeIntrinsicCall(CallBase *CI, Function *NewFn) { @@ -4354,6 +4403,7 @@ void llvm::UpgradeIntrinsicCall(CallBase *CI, Function *NewFn) { bool IsARM = Name.consume_front("arm."); bool IsAMDGCN = Name.consume_front("amdgcn."); bool IsDbg = Name.consume_front("dbg."); + bool IsConstrained = Name.consume_front("experimental.constrained."); Value *Rep = nullptr; if (!IsX86 && Name == "stackprotectorcheck") { @@ -4382,6 +4432,10 @@ void llvm::UpgradeIntrinsicCall(CallBase *CI, Function *NewFn) { } else { upgradeDbgIntrinsicToDbgRecord(Name, CI); } + } else if (IsConstrained) { + Rep = upgradeConstrainedIntrinsicCall(CI, F, Builder); + if (!Rep) + return; } else { llvm_unreachable("Unknown function for CallBase upgrade."); } @@ -4902,7 +4956,8 @@ void llvm::UpgradeCallsToIntrinsic(Function *F) { UpgradeIntrinsicCall(CB, NewFn); // Remove old function, no longer used, from the module. - F->eraseFromParent(); + if (NewFn) + F->eraseFromParent(); } } diff --git a/llvm/lib/IR/FPEnv.cpp b/llvm/lib/IR/FPEnv.cpp index 67f21d3756e93..91a962eb8190b 100644 --- a/llvm/lib/IR/FPEnv.cpp +++ b/llvm/lib/IR/FPEnv.cpp @@ -21,7 +21,18 @@ namespace llvm { -std::optional convertStrToRoundingMode(StringRef RoundingArg) { +std::optional convertStrToRoundingMode(StringRef RoundingArg, + bool InBundle) { + if (InBundle) + return StringSwitch>(RoundingArg) + .Case("dyn", RoundingMode::Dynamic) + .Case("rte", RoundingMode::NearestTiesToEven) + .Case("rmm", RoundingMode::NearestTiesToAway) + .Case("rtn", RoundingMode::TowardNegative) + .Case("rtp", RoundingMode::TowardPositive) + .Case("rtz", RoundingMode::TowardZero) + .Default(std::nullopt); + // For dynamic rounding mode, we use round to nearest but we will set the // 'exact' SDNodeFlag so that the value will not be rounded. return StringSwitch>(RoundingArg) @@ -34,26 +45,27 @@ std::optional convertStrToRoundingMode(StringRef RoundingArg) { .Default(std::nullopt); } -std::optional convertRoundingModeToStr(RoundingMode UseRounding) { +std::optional convertRoundingModeToStr(RoundingMode UseRounding, + bool InBundle) { std::optional RoundingStr; switch (UseRounding) { case RoundingMode::Dynamic: - RoundingStr = "round.dynamic"; + RoundingStr = InBundle ? "dyn" : "round.dynamic"; break; case RoundingMode::NearestTiesToEven: - RoundingStr = "round.tonearest"; + RoundingStr = InBundle ? "rte" : "round.tonearest"; break; case RoundingMode::NearestTiesToAway: - RoundingStr = "round.tonearestaway"; + RoundingStr = InBundle ? "rmm" : "round.tonearestaway"; break; case RoundingMode::TowardNegative: - RoundingStr = "round.downward"; + RoundingStr = InBundle ? "rtn" : "round.downward"; break; case RoundingMode::TowardPositive: - RoundingStr = "round.upward"; + RoundingStr = InBundle ? "rtp" : "round.upward"; break; case RoundingMode::TowardZero: - RoundingStr = "round.towardzero"; + RoundingStr = InBundle ? "rtz" : "round.towardzero"; break; default: break; @@ -62,7 +74,14 @@ std::optional convertRoundingModeToStr(RoundingMode UseRounding) { } std::optional -convertStrToExceptionBehavior(StringRef ExceptionArg) { +convertStrToExceptionBehavior(StringRef ExceptionArg, bool InBundle) { + if (InBundle) + return StringSwitch>(ExceptionArg) + .Case("ignore", fp::ebIgnore) + .Case("maytrap", fp::ebMayTrap) + .Case("strict", fp::ebStrict) + .Default(std::nullopt); + return StringSwitch>(ExceptionArg) .Case("fpexcept.ignore", fp::ebIgnore) .Case("fpexcept.maytrap", fp::ebMayTrap) @@ -71,17 +90,17 @@ convertStrToExceptionBehavior(StringRef ExceptionArg) { } std::optional -convertExceptionBehaviorToStr(fp::ExceptionBehavior UseExcept) { +convertExceptionBehaviorToStr(fp::ExceptionBehavior UseExcept, bool InBundle) { std::optional ExceptStr; switch (UseExcept) { case fp::ebStrict: - ExceptStr = "fpexcept.strict"; + ExceptStr = InBundle ? "strict" : "fpexcept.strict"; break; case fp::ebIgnore: - ExceptStr = "fpexcept.ignore"; + ExceptStr = InBundle ? "ignore" : "fpexcept.ignore"; break; case fp::ebMayTrap: - ExceptStr = "fpexcept.maytrap"; + ExceptStr = InBundle ? "maytrap" : "fpexcept.maytrap"; break; } return ExceptStr; diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp index 134459265cecb..5e711b104988d 100644 --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -86,6 +86,53 @@ IRBuilderBase::createCallHelper(Function *Callee, ArrayRef Ops, return CI; } +CallInst *IRBuilderBase::CreateCall(FunctionType *FTy, Value *Callee, + ArrayRef Args, + ArrayRef OpBundles, + const Twine &Name, MDNode *FPMathTag) { + ArrayRef ActualBundlesRef = OpBundles; + SmallVector ActualBundles; + + bool doesCallAccessFPEnv = false; + if (IsFPConstrained) { + if (const auto *Func = dyn_cast(Callee)) { + if (Intrinsic::ID ID = Func->getIntrinsicID()) { + if (IntrinsicInst::canAccessFPEnvironment(ID) || + Intrinsic::isConstrainedFPIntrinsic(ID)) { + bool NeedRound = true, NeedExcept = true; + doesCallAccessFPEnv = true; + for (const auto &Item : OpBundles) { + if (NeedRound && Item.getTag() == "fp.control") + NeedRound = false; + else if (NeedExcept && Item.getTag() == "fp.except") + NeedExcept = false; + ActualBundles.push_back(Item); + } + if (NeedRound && Intrinsic::hasConstrainedFPRoundingModeOperand(ID)) + createFPRoundingBundle(ActualBundles); + if (NeedExcept) + createFPExceptionBundle(ActualBundles); + ActualBundlesRef = ActualBundles; + } + } + } + } + + CallInst *CI = CallInst::Create(FTy, Callee, Args, ActualBundlesRef); + if (doesCallAccessFPEnv) { + MemoryEffects ME = MemoryEffects::inaccessibleMemOnly(); + if (CI->getAttributes().hasFnAttr(Attribute::Memory)) + ME |= CI->getAttributes().getMemoryEffects(); + auto A = Attribute::getWithMemoryEffects(getContext(), ME); + CI->addFnAttr(A); + } + if (IsFPConstrained) + setConstrainedFPCallAttr(CI); + if (isa(CI)) + setFPAttrs(CI, FPMathTag, FMF); + return Insert(CI, Name); +} + Value *IRBuilderBase::CreateVScale(Constant *Scaling, const Twine &Name) { assert(isa(Scaling) && "Expected constant integer"); if (cast(Scaling)->isZero()) @@ -907,6 +954,17 @@ CallInst *IRBuilderBase::CreateIntrinsic(Intrinsic::ID ID, return createCallHelper(Fn, Args, Name, FMFSource); } +CallInst *IRBuilderBase::CreateIntrinsic(Intrinsic::ID ID, + ArrayRef Types, + ArrayRef Args, + ArrayRef OpBundles, + Instruction *FMFSource, + const Twine &Name) { + Module *M = BB->getModule(); + Function *Fn = Intrinsic::getOrInsertDeclaration(M, ID, Types); + return createCallHelper(Fn, Args, Name, FMFSource, OpBundles); +} + CallInst *IRBuilderBase::CreateIntrinsic(Type *RetTy, Intrinsic::ID ID, ArrayRef Args, FMFSource FMFSource, @@ -943,8 +1001,11 @@ CallInst *IRBuilderBase::CreateConstrainedFPBinOp( FastMathFlags UseFMF = FMFSource.get(FMF); - CallInst *C = CreateIntrinsic(ID, {L->getType()}, - {L, R, RoundingV, ExceptV}, nullptr, Name); + SmallVector OpBundles; + createFPRoundingBundle(OpBundles, Rounding); + createFPExceptionBundle(OpBundles, Except); + CallInst *C = CreateIntrinsic(ID, {L->getType()}, {L, R, RoundingV, ExceptV}, + OpBundles, nullptr, Name); setConstrainedFPCallAttr(C); setFPAttrs(C, FPMathTag, UseFMF); return C; @@ -958,8 +1019,11 @@ CallInst *IRBuilderBase::CreateConstrainedFPUnroundedBinOp( FastMathFlags UseFMF = FMFSource.get(FMF); - CallInst *C = - CreateIntrinsic(ID, {L->getType()}, {L, R, ExceptV}, nullptr, Name); + SmallVector OpBundles; + createFPExceptionBundle(OpBundles, Except); + + CallInst *C = CreateIntrinsic(ID, {L->getType()}, {L, R, ExceptV}, OpBundles, + nullptr, Name); setConstrainedFPCallAttr(C); setFPAttrs(C, FPMathTag, UseFMF); return C; @@ -985,17 +1049,22 @@ CallInst *IRBuilderBase::CreateConstrainedFPCast( const Twine &Name, MDNode *FPMathTag, std::optional Rounding, std::optional Except) { Value *ExceptV = getConstrainedFPExcept(Except); + bool HasRounding = Intrinsic::hasConstrainedFPRoundingModeOperand(ID); FastMathFlags UseFMF = FMFSource.get(FMF); + SmallVector OpBundles; + createFPRoundingBundle(OpBundles, Rounding); + createFPExceptionBundle(OpBundles, Except); + CallInst *C; - if (Intrinsic::hasConstrainedFPRoundingModeOperand(ID)) { + if (HasRounding) { Value *RoundingV = getConstrainedFPRounding(Rounding); C = CreateIntrinsic(ID, {DestTy, V->getType()}, {V, RoundingV, ExceptV}, - nullptr, Name); + OpBundles, nullptr, Name); } else - C = CreateIntrinsic(ID, {DestTy, V->getType()}, {V, ExceptV}, nullptr, - Name); + C = CreateIntrinsic(ID, {DestTy, V->getType()}, {V, ExceptV}, OpBundles, + nullptr, Name); setConstrainedFPCallAttr(C); @@ -1027,8 +1096,11 @@ CallInst *IRBuilderBase::CreateConstrainedFPCmp( Value *PredicateV = getConstrainedFPPredicate(P); Value *ExceptV = getConstrainedFPExcept(Except); - CallInst *C = CreateIntrinsic(ID, {L->getType()}, - {L, R, PredicateV, ExceptV}, nullptr, Name); + SmallVector OpBundles; + createFPExceptionBundle(OpBundles, Except); + + CallInst *C = CreateIntrinsic(ID, {L->getType()}, {L, R, PredicateV, ExceptV}, + OpBundles, nullptr, Name); setConstrainedFPCallAttr(C); return C; } @@ -1038,14 +1110,19 @@ CallInst *IRBuilderBase::CreateConstrainedFPCall( std::optional Rounding, std::optional Except) { llvm::SmallVector UseArgs; + SmallVector OpBundles; append_range(UseArgs, Args); - if (Intrinsic::hasConstrainedFPRoundingModeOperand(Callee->getIntrinsicID())) + if (Intrinsic::hasConstrainedFPRoundingModeOperand( + Callee->getIntrinsicID())) { UseArgs.push_back(getConstrainedFPRounding(Rounding)); + createFPRoundingBundle(OpBundles, Rounding); + } UseArgs.push_back(getConstrainedFPExcept(Except)); + createFPExceptionBundle(OpBundles, Except); - CallInst *C = CreateCall(Callee, UseArgs, Name); + CallInst *C = CreateCall(Callee, UseArgs, OpBundles, Name); setConstrainedFPCallAttr(C); return C; } @@ -1294,6 +1371,20 @@ CallInst *IRBuilderBase::CreateDereferenceableAssumption(Value *PtrValue, {DereferenceableOpB}); } +void IRBuilderBase::createFPRoundingBundle( + SmallVectorImpl &Bundles, + std::optional Rounding) { + addFPRoundingBundle(Context, Bundles, + Rounding.value_or(DefaultConstrainedRounding)); +} + +void IRBuilderBase::createFPExceptionBundle( + SmallVectorImpl &Bundles, + std::optional Except) { + addFPExceptionBundle(Context, Bundles, + Except.value_or(DefaultConstrainedExcept)); +} + IRBuilderDefaultInserter::~IRBuilderDefaultInserter() = default; IRBuilderCallbackInserter::~IRBuilderCallbackInserter() = default; IRBuilderFolder::~IRBuilderFolder() = default; diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index b5d1bc81b9d95..b934a55ec844a 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -621,10 +621,45 @@ bool CallBase::hasClobberingOperandBundles() const { getIntrinsicID() != Intrinsic::assume; } +std::optional CallBase::getRoundingMode() const { + if (auto RoundingBundle = getOperandBundle(LLVMContext::OB_fp_control)) { + Value *V = RoundingBundle->Inputs.front(); + Metadata *MD = cast(V)->getMetadata(); + return convertStrToRoundingMode(cast(MD)->getString(), true); + } + return std::nullopt; +} + +std::optional CallBase::getExceptionBehavior() const { + if (auto ExceptionBundle = getOperandBundle(LLVMContext::OB_fp_except)) { + Value *V = ExceptionBundle->Inputs.front(); + Metadata *MD = cast(V)->getMetadata(); + return convertStrToExceptionBehavior(cast(MD)->getString(), true); + } + return std::nullopt; +} + +bool CallBase::hasFloatingPointBundles() const { + return getOperandBundle(LLVMContext::OB_fp_control) || + getOperandBundle(LLVMContext::OB_fp_except); +} + +MemoryEffects CallBase::getFloatingPointMemoryEffects() const { + if (Intrinsic::ID IntrID = getIntrinsicID()) + if (const BasicBlock *BB = getParent()) + if (const Function *F = BB->getParent()) + if (F->hasFnAttribute(Attribute::StrictFP)) + if (IntrinsicInst::canAccessFPEnvironment(IntrID)) { + return MemoryEffects::inaccessibleMemOnly(); + } + return MemoryEffects::none(); +} + MemoryEffects CallBase::getMemoryEffects() const { MemoryEffects ME = getAttributes().getMemoryEffects(); if (auto *Fn = dyn_cast(getCalledOperand())) { MemoryEffects FnME = Fn->getMemoryEffects(); + FnME |= getFloatingPointMemoryEffects(); if (hasOperandBundles()) { // TODO: Add a method to get memory effects for operand bundles instead. if (hasReadingOperandBundles()) @@ -725,6 +760,26 @@ bool CallBase::hasArgumentWithAdditionalReturnCaptureComponents() const { return false; } +void llvm::addFPRoundingBundle(LLVMContext &Ctx, + SmallVectorImpl &Bundles, + RoundingMode Rounding) { + std::optional RndStr = convertRoundingModeToStr(Rounding, true); + assert(RndStr && "Garbage rounding mode!"); + auto *RoundingMDS = MDString::get(Ctx, *RndStr); + auto *RM = MetadataAsValue::get(Ctx, RoundingMDS); + Bundles.emplace_back("fp.control", RM); +} + +void llvm::addFPExceptionBundle(LLVMContext &Ctx, + SmallVectorImpl &Bundles, + fp::ExceptionBehavior Except) { + std::optional ExcStr = convertExceptionBehaviorToStr(Except, true); + assert(ExcStr && "Garbage exception behavior!"); + auto *ExceptMDS = MDString::get(Ctx, *ExcStr); + auto *EB = MetadataAsValue::get(Ctx, ExceptMDS); + Bundles.emplace_back("fp.except", EB); +} + //===----------------------------------------------------------------------===// // CallInst Implementation //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp index 256bce1abe71f..b8ac9c8448c10 100644 --- a/llvm/lib/IR/IntrinsicInst.cpp +++ b/llvm/lib/IR/IntrinsicInst.cpp @@ -66,6 +66,39 @@ bool IntrinsicInst::mayLowerToFunctionCall(Intrinsic::ID IID) { } } +bool IntrinsicInst::canAccessFPEnvironment(Intrinsic::ID IID) { + switch (IID) { +#define FUNCTION(NAME, A, R, I) case Intrinsic::NAME: +#include "llvm/IR/ConstrainedOps.def" + return true; + default: + return false; + } +} + +std::optional llvm::getRoundingModeArg(const CallBase &I) { + unsigned NumOperands = I.arg_size(); + Metadata *MD = nullptr; + auto *MAV = dyn_cast(I.getArgOperand(NumOperands - 2)); + if (MAV) + MD = MAV->getMetadata(); + if (!MD || !isa(MD)) + return std::nullopt; + return convertStrToRoundingMode(cast(MD)->getString()); +} + +std::optional +llvm::getExceptionBehaviorArg(const CallBase &I) { + unsigned NumOperands = I.arg_size(); + Metadata *MD = nullptr; + auto *MAV = dyn_cast(I.getArgOperand(NumOperands - 1)); + if (MAV) + MD = MAV->getMetadata(); + if (!MD || !isa(MD)) + return std::nullopt; + return convertStrToExceptionBehavior(cast(MD)->getString()); +} + //===----------------------------------------------------------------------===// /// DbgVariableIntrinsic - This is the common base class for debug info /// intrinsics for variables. @@ -273,29 +306,6 @@ void InstrProfCallsite::setCallee(Value *Callee) { setArgOperand(4, Callee); } -std::optional ConstrainedFPIntrinsic::getRoundingMode() const { - unsigned NumOperands = arg_size(); - Metadata *MD = nullptr; - auto *MAV = dyn_cast(getArgOperand(NumOperands - 2)); - if (MAV) - MD = MAV->getMetadata(); - if (!MD || !isa(MD)) - return std::nullopt; - return convertStrToRoundingMode(cast(MD)->getString()); -} - -std::optional -ConstrainedFPIntrinsic::getExceptionBehavior() const { - unsigned NumOperands = arg_size(); - Metadata *MD = nullptr; - auto *MAV = dyn_cast(getArgOperand(NumOperands - 1)); - if (MAV) - MD = MAV->getMetadata(); - if (!MD || !isa(MD)) - return std::nullopt; - return convertStrToExceptionBehavior(cast(MD)->getString()); -} - bool ConstrainedFPIntrinsic::isDefaultFPEnvironment() const { std::optional Except = getExceptionBehavior(); if (Except) { diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp index 447e5d92e0b99..46a64a8c1ce8b 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -82,6 +82,16 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) { assert(Entry->second == BundleTagID && "operand bundle id drifted!"); } + auto *RoundingEntry = pImpl->getOrInsertBundleTag("fp.control"); + assert(RoundingEntry->second == LLVMContext::OB_fp_control && + "fp.control operand bundle id drifted!"); + (void)RoundingEntry; + + auto *ExceptionEntry = pImpl->getOrInsertBundleTag("fp.except"); + assert(ExceptionEntry->second == LLVMContext::OB_fp_except && + "fp.except operand bundle id drifted!"); + (void)ExceptionEntry; + SyncScope::ID SingleThreadSSID = pImpl->getOrInsertSyncScopeID("singlethread"); assert(SingleThreadSSID == SyncScope::SingleThread && diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 8432779c107de..53df888bb46bf 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -658,6 +658,9 @@ class Verifier : public InstVisitor, VerifierSupport { /// Verify the llvm.experimental.noalias.scope.decl declarations void verifyNoAliasScopeDecl(); + + /// Verify the call of a constrained intrinsic call. + void verifyConstrainedInstrinsicCall(const CallBase &CB); }; } // end anonymous namespace @@ -3728,7 +3731,9 @@ void Verifier::visitCallBase(CallBase &Call) { FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false, FoundPreallocatedBundle = false, FoundGCLiveBundle = false, FoundPtrauthBundle = false, FoundKCFIBundle = false, - FoundAttachedCallBundle = false; + FoundAttachedCallBundle = false, FoundFpeControlBundle = false, + FoundFpeExceptBundle = false; + for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) { OperandBundleUse BU = Call.getOperandBundleAt(i); uint32_t Tag = BU.getTagID(); @@ -3791,9 +3796,41 @@ void Verifier::visitCallBase(CallBase &Call) { "Multiple \"clang.arc.attachedcall\" operand bundles", Call); FoundAttachedCallBundle = true; verifyAttachedCallBundle(Call, BU); + } else if (Tag == LLVMContext::OB_fp_control) { + Check(!FoundFpeControlBundle, "Multiple fp.control operand bundles", + Call); + Check(BU.Inputs.size() == 1, + "Expected exactly one fp.control bundle operand", Call); + auto *V = dyn_cast(BU.Inputs.front()); + Check(V, "Value of fp.control bundle operand must be a metadata", Call); + auto *MDS = dyn_cast(V->getMetadata()); + Check(MDS, "Value of fp.control bundle operand must be a string", Call); + auto RM = convertStrToRoundingMode(MDS->getString(), true); + Check(RM.has_value(), + "Value of fp.control bundle operand is not a correct rounding mode", + Call); + FoundFpeControlBundle = true; + } else if (Tag == LLVMContext::OB_fp_except) { + Check(!FoundFpeExceptBundle, "Multiple fp.except operand bundles", Call); + Check(BU.Inputs.size() == 1, + "Expected exactly one fp.except bundle operand", Call); + auto *V = dyn_cast(BU.Inputs.front()); + Check(V, "Value of fp.except bundle operand must be a metadata", Call); + auto *MDS = dyn_cast(V->getMetadata()); + Check(MDS, "Value of fp.except bundle operand must be a string", Call); + auto EB = convertStrToExceptionBehavior(MDS->getString(), true); + Check(EB.has_value(), + "Value of fp.except bundle operand is not a correct exception " + "behavior", + Call); + FoundFpeExceptBundle = true; } } + // Verify if FP options specified in constrained intrinsic arguments agree + // with the options specified in operand bundles. + verifyConstrainedInstrinsicCall(Call); + // Verify that callee and callsite agree on whether to use pointer auth. Check(!(Call.getCalledFunction() && FoundPtrauthBundle), "Direct call cannot have a ptrauth bundle", Call); @@ -3820,6 +3857,53 @@ void Verifier::visitCallBase(CallBase &Call) { visitInstruction(Call); } +void Verifier::verifyConstrainedInstrinsicCall(const CallBase &CB) { + const auto *CFPI = dyn_cast(&CB); + if (!CFPI) + return; + + // FP metadata arguments must not conflict with the corresponding + // operand bundles. + if (std::optional RM = getRoundingModeArg(CB)) { + RoundingMode Rounding = *RM; + auto RoundingBundle = CB.getOperandBundle(LLVMContext::OB_fp_control); + Check(RoundingBundle, + "Constrained intrinsic has a rounding argument but the call does not", + CB); + if (RoundingBundle) { + std::optional RMByBundle = CB.getRoundingMode(); + Check(RMByBundle, "Invalid value of rounding mode bundle", CB); + if (RMByBundle) { + Check(*RMByBundle == Rounding, + "Rounding mode of the constrained intrinsic differs from that in " + "operand bundle", + CB); + } + } + } + + if (std::optional EB = getExceptionBehaviorArg(CB)) { + fp::ExceptionBehavior Excepts = *EB; + auto ExceptionBundle = CB.getOperandBundle(LLVMContext::OB_fp_except); + Check(ExceptionBundle, + "Constrained intrinsic has an exception handling argument but the " + "call does not", + CB); + if (ExceptionBundle) { + std::optional EBByBundle = + CB.getExceptionBehavior(); + Check(EBByBundle, "Invalid value of exception behavior bundle", CB); + if (EBByBundle) { + Check( + *EBByBundle == Excepts, + "Exception behavior of the constrained intrinsic differs from that " + "in operand bundle", + CB); + } + } + } +} + void Verifier::verifyTailCCMustTailAttrs(const AttrBuilder &Attrs, StringRef Context) { Check(!Attrs.contains(Attribute::InAlloca), diff --git a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp index 3f27166080d5a..57a013c4444d1 100644 --- a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp +++ b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp @@ -249,12 +249,14 @@ static bool markTails(Function &F, OptimizationRemarkEmitter *ORE) { if (II->getIntrinsicID() == Intrinsic::stackrestore) continue; - // Special-case operand bundles "clang.arc.attachedcall", "ptrauth", and - // "kcfi". - bool IsNoTail = CI->isNoTailCall() || - CI->hasOperandBundlesOtherThan( - {LLVMContext::OB_clang_arc_attachedcall, - LLVMContext::OB_ptrauth, LLVMContext::OB_kcfi}); + bool IsNoTail = + CI->isNoTailCall() || + CI->hasOperandBundlesOtherThan( + {LLVMContext::OB_clang_arc_attachedcall, LLVMContext::OB_ptrauth, + LLVMContext::OB_kcfi, + // A call with FP operand bundles should be treated in the same + // way as a call without them. + LLVMContext::OB_fp_control, LLVMContext::OB_fp_except}); if (!IsNoTail && CI->doesNotAccessMemory()) { // A call to a readnone function whose arguments are all things computed diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp index 58d400ac396be..c9c3871a51785 100644 --- a/llvm/lib/Transforms/Utils/CloneFunction.cpp +++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp @@ -436,7 +436,6 @@ struct PruningFunctionCloner { Instruction * PruningFunctionCloner::cloneInstruction(BasicBlock::const_iterator II) { const Instruction &OldInst = *II; - Instruction *NewInst = nullptr; if (HostFuncIsStrictFP) { Intrinsic::ID CIID = getConstrainedIntrinsicID(OldInst); if (CIID != Intrinsic::not_intrinsic) { @@ -490,18 +489,25 @@ PruningFunctionCloner::cloneInstruction(BasicBlock::const_iterator II) { // The last arguments of a constrained intrinsic are metadata that // represent rounding mode (absents in some intrinsics) and exception // behavior. The inlined function uses default settings. - if (Intrinsic::hasConstrainedFPRoundingModeOperand(CIID)) + SmallVector Bundles; + if (Intrinsic::hasConstrainedFPRoundingModeOperand(CIID)) { Args.push_back( MetadataAsValue::get(Ctx, MDString::get(Ctx, "round.tonearest"))); + addFPRoundingBundle(Ctx, Bundles, RoundingMode::NearestTiesToEven); + } Args.push_back( MetadataAsValue::get(Ctx, MDString::get(Ctx, "fpexcept.ignore"))); - - NewInst = CallInst::Create(IFn, Args, OldInst.getName() + ".strict"); + addFPExceptionBundle(Ctx, Bundles, fp::ExceptionBehavior::ebIgnore); + auto *NewConstrainedInst = + CallInst::Create(IFn, Args, Bundles, OldInst.getName() + ".strict"); + + MemoryEffects ME = MemoryEffects::inaccessibleMemOnly(); + auto A = Attribute::getWithMemoryEffects(Ctx, ME); + NewConstrainedInst->addFnAttr(A); + return NewConstrainedInst; } } - if (!NewInst) - NewInst = II->clone(); - return NewInst; + return OldInst.clone(); } /// The specified block is found to be reachable, clone it and diff --git a/llvm/test/Bitcode/auto-upgrade-constrained.ll b/llvm/test/Bitcode/auto-upgrade-constrained.ll new file mode 100644 index 0000000000000..7bb31df614054 --- /dev/null +++ b/llvm/test/Bitcode/auto-upgrade-constrained.ll @@ -0,0 +1,327 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: llvm-dis %s.bc -o - | FileCheck %s + +define float @test_fadd(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.fadd.f32(float %a, float %b, metadata !"round.dynamic", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fadd( +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0:[0-9]+]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"ignore") ] + + +define float @test_fsub(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.fsub.f32(float %a, float %b, metadata !"round.tonearest", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fsub( +; CHECK: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"rte"), "fp.except"(metadata !"ignore") ] + +define float @test_fmul(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.fmul.f32(float %a, float %b, metadata !"round.downward", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fmul( +; CHECK: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"rtn"), "fp.except"(metadata !"ignore") ] + +define float @test_fdiv(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.fdiv.f32(float %a, float %b, metadata !"round.upward", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fdiv( +; CHECK: call float @llvm.experimental.constrained.fdiv.f32(float {{.*}}, float {{.*}}, metadata !"round.upward", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"rtp"), "fp.except"(metadata !"ignore") ] + +define float @test_frem(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.frem.f32(float %a, float %b, metadata !"round.dynamic", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_frem( +; CHECK: call float @llvm.experimental.constrained.frem.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"ignore") ] + +define float @test_fma(float %a, float %b, float %c) strictfp { + %res = call float @llvm.experimental.constrained.fma.f32(float %a, float %b, float %c, metadata !"round.towardzero", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fma( +; CHECK: call float @llvm.experimental.constrained.fma.f32(float {{.*}}, float {{.*}}, float {{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"rtz"), "fp.except"(metadata !"ignore") ] + +define float @test_fmuladd(float %a, float %b, float %c) strictfp { + %res = call float @llvm.experimental.constrained.fmuladd.f32(float %a, float %b, float %c, metadata !"round.tonearestaway", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fmuladd( +; CHECK: call float @llvm.experimental.constrained.fmuladd.f32(float {{.*}}, float {{.*}}, float {{.*}}, metadata !"round.tonearestaway", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"rmm"), "fp.except"(metadata !"ignore") ] + +define i32 @test_fptosi(float %a) strictfp { + %res = call i32 @llvm.experimental.constrained.fptosi.i32.f32(float %a, metadata !"fpexcept.ignore") + ret i32 %res +} +; CHECK-LABEL: define i32 @test_fptosi( +; CHECK: call i32 @llvm.experimental.constrained.fptosi.i32.f32(float {{.*}}, metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.except"(metadata !"ignore") ] + +define i32 @test_fptoui(float %a) strictfp { + %res = call i32 @llvm.experimental.constrained.fptoui.f32.i32(float %a, metadata !"fpexcept.strict") + ret i32 %res +} +; CHECK-LABEL: define i32 @test_fptoui( +; CHECK: call i32 @llvm.experimental.constrained.fptoui.i32.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_sitofp(i32 %a) strictfp { + %res = call float @llvm.experimental.constrained.sitofp.i32.f32(i32 %a, metadata !"round.dynamic", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_sitofp( +; CHECK: call float @llvm.experimental.constrained.sitofp.f32.i32(i32 {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"ignore") ] + +define float @test_uitofp(i32 %a) strictfp { + %res = call float @llvm.experimental.constrained.uitofp.i32.f32(i32 %a, metadata !"round.dynamic", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_uitofp( +; CHECK: call float @llvm.experimental.constrained.uitofp.f32.i32(i32 {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"ignore") ] + +define float @test_fptrunc(double %a) strictfp { + %res = call float @llvm.experimental.constrained.fptrunc.f32.f64(double %a, metadata !"round.dynamic", metadata !"fpexcept.ignore") + ret float %res +} +; CHECK-LABEL: define float @test_fptrunc( +; CHECK: call float @llvm.experimental.constrained.fptrunc.f32.f64(double {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"ignore") ] + +define double @test_fpext(float %a) strictfp { + %res = call double @llvm.experimental.constrained.fpext.f64.f32(float %a, metadata !"fpexcept.ignore") + ret double %res +} +; CHECK-LABEL: define double @test_fpext( +; CHECK: call double @llvm.experimental.constrained.fpext.f64.f32(float {{.*}}, metadata !"fpexcept.ignore") #[[ATTR0]] [ "fp.except"(metadata !"ignore") ] + +define float @test_sqrt(float %a) strictfp { + %res = call float @llvm.experimental.constrained.sqrt.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_sqrt( +; CHECK: call float @llvm.experimental.constrained.sqrt.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_powi(float %a, i32 %b) strictfp { + %res = call float @llvm.experimental.constrained.powi.f32.i32(float %a, i32 %b, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_powi( +; CHECK: call float @llvm.experimental.constrained.powi.f32(float {{.*}}, i32 %b, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_ldexp(float %a, i32 %b) strictfp { + %res = call float @llvm.experimental.constrained.ldexp.f32.i32(float %a, i32 %b, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_ldexp( +; CHECK: call float @llvm.experimental.constrained.ldexp.f32.i32(float {{.*}}, i32 {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_asin(float %a) strictfp { + %res = call float @llvm.experimental.constrained.asin.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_asin( +; CHECK: call float @llvm.experimental.constrained.asin.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_acos(float %a) strictfp { + %res = call float @llvm.experimental.constrained.acos.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_acos( +; CHECK: call float @llvm.experimental.constrained.acos.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_atan(float %a) strictfp { + %res = call float @llvm.experimental.constrained.atan.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_atan( +; CHECK: call float @llvm.experimental.constrained.atan.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_sin(float %a) strictfp { + %res = call float @llvm.experimental.constrained.sin.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_sin( +; CHECK: call float @llvm.experimental.constrained.sin.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_cos(float %a) strictfp { + %res = call float @llvm.experimental.constrained.cos.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_cos( +; CHECK: call float @llvm.experimental.constrained.cos.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_tan(float %a) strictfp { + %res = call float @llvm.experimental.constrained.tan.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_tan( +; CHECK: call float @llvm.experimental.constrained.tan.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_sinh(float %a) strictfp { + %res = call float @llvm.experimental.constrained.sinh.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_sinh( +; CHECK: call float @llvm.experimental.constrained.sinh.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_cosh(float %a) strictfp { + %res = call float @llvm.experimental.constrained.cosh.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_cosh( +; CHECK: call float @llvm.experimental.constrained.cosh.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_tanh(float %a) strictfp { + %res = call float @llvm.experimental.constrained.tanh.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_tanh( +; CHECK: call float @llvm.experimental.constrained.tanh.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_pow(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.pow.f32(float %a, float %b, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_pow( +; CHECK: call float @llvm.experimental.constrained.pow.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_log(float %a) strictfp { + %res = call float @llvm.experimental.constrained.log.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_log( +; CHECK: call float @llvm.experimental.constrained.log.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_log10(float %a) strictfp { + %res = call float @llvm.experimental.constrained.log10.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_log10( +; CHECK: call float @llvm.experimental.constrained.log10.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_log2(float %a) strictfp { + %res = call float @llvm.experimental.constrained.log2.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_log2( +; CHECK: call float @llvm.experimental.constrained.log2.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_exp(float %a) strictfp { + %res = call float @llvm.experimental.constrained.exp.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_exp( +; CHECK: call float @llvm.experimental.constrained.exp.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_exp2(float %a) strictfp { + %res = call float @llvm.experimental.constrained.exp2.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_exp2( +; CHECK: call float @llvm.experimental.constrained.exp2.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_rint(float %a) strictfp { + %res = call float @llvm.experimental.constrained.rint.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_rint( +; CHECK: call float @llvm.experimental.constrained.rint.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_nearbyint(float %a) strictfp { + %res = call float @llvm.experimental.constrained.nearbyint.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_nearbyint( +; CHECK: call float @llvm.experimental.constrained.nearbyint.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define i32 @test_lrint(float %a) strictfp { + %res = call i32 @llvm.experimental.constrained.lrint.i32.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret i32 %res +} +; CHECK-LABEL: define i32 @test_lrint( +; CHECK: call i32 @llvm.experimental.constrained.lrint.i32.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define i32 @test_llrint(float %a) strictfp { + %res = call i32 @llvm.experimental.constrained.llrint.i32.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict") + ret i32 %res +} +; CHECK-LABEL: define i32 @test_llrint( +; CHECK: call i32 @llvm.experimental.constrained.llrint.i32.f32(float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.control"(metadata !"dyn"), "fp.except"(metadata !"strict") ] + +define float @test_maxnum(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.maxnum.f32(float %a, float %b, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_maxnum( +; CHECK: call float @llvm.experimental.constrained.maxnum.f32(float {{.*}}, float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_minnum(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.minnum.f32(float %a, float %b, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_minnum( +; CHECK: call float @llvm.experimental.constrained.minnum.f32(float {{.*}}, float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_maximum(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.maximum.f32(float %a, float %b, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_maximum( +; CHECK: call float @llvm.experimental.constrained.maximum.f32(float {{.*}}, float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_minimum(float %a, float %b) strictfp { + %res = call float @llvm.experimental.constrained.minimum.f32(float %a, float %b, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_minimum( +; CHECK: call float @llvm.experimental.constrained.minimum.f32(float {{.*}}, float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_ceil(float %a) strictfp { + %res = call float @llvm.experimental.constrained.ceil.f32(float %a, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_ceil( +; call float @llvm.experimental.constrained.ceil.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_floor(float %a) strictfp { + %res = call float @llvm.experimental.constrained.floor.f32(float %a, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_floor( +; call float @llvm.experimental.constrained.floor.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define i32 @test_lround(float %a) strictfp { + %res = call i32 @llvm.experimental.constrained.lround.i32.f32(float %a, metadata !"fpexcept.strict") + ret i32 %res +} +; CHECK-LABEL: define i32 @test_lround( +; CHECK: call i32 @llvm.experimental.constrained.lround.i32.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define i32 @test_llround(float %a) strictfp { + %res = call i32 @llvm.experimental.constrained.llround.i32.f32(float %a, metadata !"fpexcept.strict") + ret i32 %res +} +; CHECK-LABEL: define i32 @test_llround( +; CHECK: call i32 @llvm.experimental.constrained.llround.i32.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_round(float %a) strictfp { + %res = call float @llvm.experimental.constrained.round.f32(float %a, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_round( +; CHECK: call float @llvm.experimental.constrained.round.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_roundeven(float %a) strictfp { + %res = call float @llvm.experimental.constrained.roundeven.f32(float %a, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_roundeven( +; CHECK: call float @llvm.experimental.constrained.roundeven.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +define float @test_trunc(float %a) strictfp { + %res = call float @llvm.experimental.constrained.trunc.f32(float %a, metadata !"fpexcept.strict") + ret float %res +} +; CHECK-LABEL: define float @test_trunc( +; CHECK: call float @llvm.experimental.constrained.trunc.f32(float {{.*}}, metadata !"fpexcept.strict") #[[ATTR0]] [ "fp.except"(metadata !"strict") ] + +; CHECK: attributes #[[ATTR0]] = { strictfp memory(inaccessiblemem: readwrite) } diff --git a/llvm/test/Bitcode/auto-upgrade-constrained.ll.bc b/llvm/test/Bitcode/auto-upgrade-constrained.ll.bc new file mode 100644 index 0000000000000..75a84901b5cbc Binary files /dev/null and b/llvm/test/Bitcode/auto-upgrade-constrained.ll.bc differ diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll index d860104b9cb3d..01e5b3f6673ae 100644 --- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll +++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll @@ -13,6 +13,8 @@ ; CHECK-NEXT: @llvm.experimental.constrained.fadd.v4f32(<4 x float>, <4 x float>, metadata, metadata) strictfp diff --git a/llvm/test/Transforms/Inline/inline-strictfp.ll b/llvm/test/Transforms/Inline/inline-strictfp.ll index bc42fafd63943..5883002061c30 100644 --- a/llvm/test/Transforms/Inline/inline-strictfp.ll +++ b/llvm/test/Transforms/Inline/inline-strictfp.ll @@ -15,8 +15,8 @@ entry: %add = call float @llvm.experimental.constrained.fadd.f32(float %0, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 ret float %add ; CHECK-LABEL: @host_02 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0:[0-9]+]] +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] } @@ -34,8 +34,8 @@ entry: %add = call float @llvm.experimental.constrained.fadd.f32(float %0, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 ret float %add ; CHECK-LABEL: @host_04 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.downward", metadata !"fpexcept.maytrap") #0 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.downward", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] } @@ -77,8 +77,8 @@ entry: ret float %add ; CHECK-LABEL: @host_08 ; CHECK: call float @func_ext(float {{.*}}) #0 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] } @@ -97,8 +97,8 @@ entry: %add = call double @llvm.experimental.constrained.fadd.f64(double %0, double 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 ret double %add ; CHECK-LABEL: @host_10 -; CHECK: call double @llvm.experimental.constrained.fpext.f64.f32(float {{.*}}, metadata !"fpexcept.ignore") #0 -; CHECK: call double @llvm.experimental.constrained.fadd.f64(double {{.*}}, double 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 +; CHECK: call double @llvm.experimental.constrained.fpext.f64.f32(float {{.*}}, metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK: call double @llvm.experimental.constrained.fadd.f64(double {{.*}}, double 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] } ; fcmp does not depend on rounding mode and has metadata argument. @@ -114,8 +114,8 @@ entry: %cmp = call i1 @inlined_11(float %a, float %b) #0 ret i1 %cmp ; CHECK-LABEL: @host_12 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float %a, float %b, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 -; CHECK: call i1 @llvm.experimental.constrained.fcmp.f32(float {{.*}}, metadata !"oeq", metadata !"fpexcept.ignore") #0 +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float %a, float %b, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK: call i1 @llvm.experimental.constrained.fcmp.f32(float {{.*}}, metadata !"oeq", metadata !"fpexcept.ignore") #[[ATTR0]] } ; Intrinsic 'ceil' has constrained variant. @@ -131,11 +131,12 @@ entry: %add = call float @llvm.experimental.constrained.fadd.f32(float %0, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 ret float %add ; CHECK-LABEL: @host_14 -; CHECK: call float @llvm.experimental.constrained.ceil.f32(float %a, metadata !"fpexcept.ignore") #0 -; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 +; CHECK: call float @llvm.experimental.constrained.ceil.f32(float %a, metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float 2.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] } attributes #0 = { strictfp } +; CHECK: attributes #[[ATTR0]] = { strictfp memory(inaccessiblemem: readwrite) } declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) diff --git a/llvm/test/Verifier/fp-intrinsics.ll b/llvm/test/Verifier/fp-intrinsics.ll index 4934843d5a2ed..45b3eab9198a6 100644 --- a/llvm/test/Verifier/fp-intrinsics.ll +++ b/llvm/test/Verifier/fp-intrinsics.ll @@ -5,7 +5,7 @@ declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadat ; Test an illegal value for the rounding mode argument. ; CHECK: invalid rounding mode argument -; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.fadd.f64(double %a, double %b, metadata !"round.dynomic", metadata !"fpexcept.strict") #1 +; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.fadd.f64(double %a, double %b, metadata !"round.dynomic", metadata !"fpexcept.strict") #{{[0-9]+}} define double @f2(double %a, double %b) #0 { entry: %fadd = call double @llvm.experimental.constrained.fadd.f64( @@ -17,7 +17,7 @@ entry: ; Test an illegal value for the exception behavior argument. ; CHECK-NEXT: invalid exception behavior argument -; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.fadd.f64(double %a, double %b, metadata !"round.dynamic", metadata !"fpexcept.restrict") #1 +; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.fadd.f64(double %a, double %b, metadata !"round.dynamic", metadata !"fpexcept.restrict") #{{[0-9]+}} define double @f3(double %a, double %b) #0 { entry: %fadd = call double @llvm.experimental.constrained.fadd.f64( @@ -29,7 +29,7 @@ entry: ; Test an illegal value for the rounding mode argument. ; CHECK-NEXT: invalid rounding mode argument -; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.sqrt.f64(double %a, metadata !"round.dynomic", metadata !"fpexcept.strict") #1 +; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.sqrt.f64(double %a, metadata !"round.dynomic", metadata !"fpexcept.strict") #{{[0-9]+}} define double @f4(double %a) #0 { entry: %fadd = call double @llvm.experimental.constrained.sqrt.f64( @@ -41,7 +41,7 @@ entry: ; Test an illegal value for the exception behavior argument. ; CHECK-NEXT: invalid exception behavior argument -; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.sqrt.f64(double %a, metadata !"round.dynamic", metadata !"fpexcept.restrict") #1 +; CHECK-NEXT: %fadd = call double @llvm.experimental.constrained.sqrt.f64(double %a, metadata !"round.dynamic", metadata !"fpexcept.restrict") #{{[0-9]+}} define double @f5(double %a) #0 { entry: %fadd = call double @llvm.experimental.constrained.sqrt.f64( @@ -51,4 +51,94 @@ entry: ret double %fadd } +; Test multiple fp.control bundles. +; CHECK-NEXT: Multiple fp.control operand bundles +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.control"(metadata !"rtz"), "fp.control"(metadata !"rtz") ] +define double @f6(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.control"(metadata !"rtz"), "fp.control"(metadata !"rtz") ] + ret double %ftrunc +} + +; Test fp.control bundle that has more than one operands. +; CHECK-NEXT: Expected exactly one fp.control bundle operand +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.control"(metadata !"rtz", metadata !"rte") ] +define double @f7(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.control"(metadata !"rtz", metadata !"rte") ] + ret double %ftrunc +} + +; Test fp.control bundle that has non-metadata operand. +; CHECK-NEXT: Value of fp.control bundle operand must be a metadata +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.control"(i32 0) ] +define double @f8(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.control"(i32 0) ] + ret double %ftrunc +} + +; Test fp.control bundle that has non-string operand. +; CHECK-NEXT: Value of fp.control bundle operand must be a string +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.control"(metadata i64 3) ] +define double @f9(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.control"(metadata !{i64 3}) ] + ret double %ftrunc +} + +; Test fp.control bundle that specifies incorrect value. +; CHECK-NEXT: Value of fp.control bundle operand is not a correct rounding mode +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.control"(metadata !"qqq") ] +define double @f10(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.control"(metadata !"qqq") ] + ret double %ftrunc +} + +; Test multiple fp.except bundles. +; CHECK-NEXT: Multiple fp.except operand bundles +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.except"(metadata !"strict"), "fp.except"(metadata !"strict") ] +define double @f11(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.except"(metadata !"strict"), "fp.except"(metadata !"strict") ] + ret double %ftrunc +} + +; Test fp.except bundle that has more than one operands. +; CHECK-NEXT: Expected exactly one fp.except bundle operand +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.except"(metadata !"strict", metadata !"strict") ] +define double @f12(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.except"(metadata !"strict", metadata !"strict") ] + ret double %ftrunc +} + +; Test fp.except bundle that has non-metadata operand. +; CHECK-NEXT: Value of fp.except bundle operand must be a metadata +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.except"(i32 0) ] +define double @f13(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.except"(i32 0) ] + ret double %ftrunc +} + +; Test fp.except bundle that has non-string operand. +; CHECK-NEXT: Value of fp.except bundle operand must be a string +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.except"(metadata i64 3) ] +define double @f14(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.except"(metadata !{i64 3}) ] + ret double %ftrunc +} + +; Test fp.except bundle that specifies incorrect value. +; CHECK-NEXT: Value of fp.except bundle operand is not a correct exception behavior +; CHECK-NEXT: %ftrunc = call double @llvm.trunc.f64(double %a) #{{[0-9]+}} [ "fp.except"(metadata !"qqq") ] +define double @f15(double %a) #0 { +entry: + %ftrunc = call double @llvm.trunc.f64(double %a) #0 [ "fp.except"(metadata !"qqq") ] + ret double %ftrunc +} + attributes #0 = { strictfp } diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp index 3a55d88f03d49..6b38a585a9fd1 100644 --- a/llvm/unittests/IR/IRBuilderTest.cpp +++ b/llvm/unittests/IR/IRBuilderTest.cpp @@ -402,6 +402,44 @@ TEST_F(IRBuilderTest, ConstrainedFP) { EXPECT_FALSE(verifyModule(*M)); } +TEST_F(IRBuilderTest, StrictFPCall) { + F->addFnAttr(Attribute::StrictFP); + + IRBuilder<> Builder(BB); + Builder.setDefaultConstrainedExcept(fp::ebStrict); + Builder.setDefaultConstrainedRounding(RoundingMode::TowardZero); + Builder.setIsFPConstrained(true); + + GlobalVariable *GVDouble = new GlobalVariable( + *M, Type::getDoubleTy(Ctx), true, GlobalValue::ExternalLinkage, nullptr); + Value *FnArg = Builder.CreateLoad(GVDouble->getValueType(), GVDouble); + + // Function calls, that may depend on FP options, gets fp bundles in strictfp + // environment. + Function *Fn = Intrinsic::getOrInsertDeclaration( + M.get(), Intrinsic::experimental_constrained_roundeven, + {Type::getDoubleTy(Ctx)}); + Value *V = Builder.CreateConstrainedFPCall(Fn, {FnArg}); + auto *I = cast(V); + EXPECT_TRUE(I->getOperandBundle(LLVMContext::OB_fp_except).has_value()); + EXPECT_FALSE(I->getOperandBundle(LLVMContext::OB_fp_control).has_value()); + EXPECT_EQ(Intrinsic::experimental_constrained_roundeven, I->getIntrinsicID()); + EXPECT_EQ(fp::ebStrict, I->getExceptionBehavior()); + MemoryEffects ME = I->getMemoryEffects(); + EXPECT_TRUE(ME.doesAccessInaccessibleMem()); + + // Function calls, that do not depend on FP options, does not have + // fp bundles. + Fn = Intrinsic::getOrInsertDeclaration(M.get(), Intrinsic::fabs, + {Type::getDoubleTy(Ctx)}); + V = Builder.CreateCall(Fn, {FnArg}); + I = cast(V); + EXPECT_FALSE(I->getOperandBundle(LLVMContext::OB_fp_except).has_value()); + EXPECT_FALSE(I->getOperandBundle(LLVMContext::OB_fp_control).has_value()); + ME = I->getMemoryEffects(); + EXPECT_FALSE(ME.doesAccessInaccessibleMem()); +} + TEST_F(IRBuilderTest, ConstrainedFPIntrinsics) { IRBuilder<> Builder(BB); Value *V;