From 1c1df765ff314814afe33dc0124b2cba6ba1e622 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 8 May 2024 15:35:17 -0400 Subject: [PATCH 1/2] Implement Builtin.freeze for integer and integer-vector types. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://llvm.org/docs/LangRef.html#freeze-instruction > If the argument is undef or poison, ‘freeze’ returns an arbitrary, but fixed, value of type ‘ty’. Otherwise, this instruction is a no-op and returns the input argument. All uses of a value returned by the same ‘freeze’ instruction are guaranteed to always observe the same value, while different ‘freeze’ instructions may yield different values. It's most importation for integer and integer-vector types because floating-point results are generally not poison (except in the case of conversion from poison integer values). However, we might want to implement this for other types as well in the future. --- include/swift/AST/Builtins.def | 2 ++ lib/IRGen/GenBuiltin.cpp | 3 ++ lib/SIL/IR/OperandOwnership.cpp | 1 + lib/SIL/IR/ValueOwnership.cpp | 1 + test/IRGen/builtin_freeze.swift | 52 +++++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 test/IRGen/builtin_freeze.swift diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index f1b4b6b23e076..f0369e86c9635 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -175,6 +175,8 @@ BUILTIN_UNARY_OPERATION(FNeg, "fneg", "n", FloatOrVector) BUILTIN_UNARY_OPERATION(AssumeNonNegative, "assumeNonNegative", "n", Integer) // It only works on i1. BUILTIN_UNARY_OPERATION(AssumeTrue, "assume", "", Integer) +// Converts poison/undef to an indeterminate but valid value. +BUILTIN_UNARY_OPERATION(Freeze, "freeze", "n", IntegerOrVector) // Binary predicates have type (T,T) -> i1 or (T, T) -> Vector for scalars // and vectors, respectively. diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index cac9788268130..c34954d3acbba 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -637,6 +637,9 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, // Don't generate any code for the builtin. return out.add(v); } + if (Builtin.ID == BuiltinValueKind::Freeze) { + return out.add(IGF.Builder.CreateFreeze(args.claimNext())); + } if (Builtin.ID == BuiltinValueKind::AllocRaw) { auto size = args.claimNext(); diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 9af138bc6195a..2eafb6aa2dd1b 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -810,6 +810,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFRem) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FSub) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFSub) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Fence) +BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Freeze) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Ifdef) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetObjCTypeEncoding) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_EQ) diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index c7ed8453476f0..24e21097abb4f 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -491,6 +491,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, FRem) CONSTANT_OWNERSHIP_BUILTIN(None, GenericFRem) CONSTANT_OWNERSHIP_BUILTIN(None, FSub) CONSTANT_OWNERSHIP_BUILTIN(None, GenericFSub) +CONSTANT_OWNERSHIP_BUILTIN(None, Freeze) CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_EQ) CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_NE) CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_SGE) diff --git a/test/IRGen/builtin_freeze.swift b/test/IRGen/builtin_freeze.swift new file mode 100644 index 0000000000000..a752f60c5c608 --- /dev/null +++ b/test/IRGen/builtin_freeze.swift @@ -0,0 +1,52 @@ +// RUN: %target-swift-frontend -O -module-name builtin_freeze -enable-experimental-feature BuiltinModule -primary-file %s -emit-ir -o - | %FileCheck %s --check-prefix=CHECK + +import Builtin + +func fptosi(_ x: Float) -> Int32 { + Int32(Builtin.fptosi_FPIEEE32_Int32(x._value)) + // CHECK: fptosi float %{{[0-9]+}} to i32 +} + +func fptosiWithFreeze(_ x: Float) -> Int32 { + Int32(Builtin.freeze_Int32(Builtin.fptosi_FPIEEE32_Int32(x._value))) + // CHECK: fptosi float %{{[0-9]+}} to i32 + // CHECK-NEXT: freeze i32 %{{[0-9]+}} +} + +func yuck() -> Int32 { + fptosi(0x1.0p32) + // CHECK: poison +} + +func yum() -> Int32 { + fptosiWithFreeze(0x1.0p32) + // CHECK-NOT: poison +} + +func fptosi(_ x: SIMD2) -> SIMD2 { + let maybePoison = Builtin.fptosi_Vec2xFPIEEE32_Vec2xInt32(x._storage._value) + var result = SIMD2() + result._storage._value = maybePoison + return result + // CHECK: fptosi <2 x float> %{{[0-9]+}} to <2 x i32> +} + +func fptosiWithFreeze(_ x: SIMD2) -> SIMD2 { + let maybePoison = Builtin.fptosi_Vec2xFPIEEE32_Vec2xInt32(x._storage._value) + let frozen = Builtin.freeze_Vec2xInt32(maybePoison) + var result = SIMD2() + result._storage._value = frozen + return result + // CHECK: fptosi <2 x float> %{{[0-9]+}} to <2 x i32> + // CHECK-NEXT: freeze <2 x i32> %{{[0-9]+}} +} + +func doubleYuck(_ x: SIMD2) -> SIMD2 { + fptosi(SIMD2(repeating: 0x1.0p32)) + // CHECK: poison +} + +func DoubleYum(_ x: SIMD2) -> SIMD2 { + fptosiWithFreeze(SIMD2(repeating: 0x1.0p32)) + // CHECK-NOT: poison +} From a860b26055caa9416729b936f76c859eb358baad Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 8 May 2024 21:35:09 -0400 Subject: [PATCH 2/2] Make builtin.freeze TrivialUse Also fix filecheck patterns for its test to work with asserts build. --- lib/SIL/IR/OperandOwnership.cpp | 2 +- test/IRGen/builtin_freeze.swift | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 2eafb6aa2dd1b..70baa169c7917 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -810,7 +810,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFRem) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FSub) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFSub) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Fence) -BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Freeze) +BUILTIN_OPERAND_OWNERSHIP(TrivialUse, Freeze) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Ifdef) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetObjCTypeEncoding) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_EQ) diff --git a/test/IRGen/builtin_freeze.swift b/test/IRGen/builtin_freeze.swift index a752f60c5c608..8efc01616730e 100644 --- a/test/IRGen/builtin_freeze.swift +++ b/test/IRGen/builtin_freeze.swift @@ -4,13 +4,13 @@ import Builtin func fptosi(_ x: Float) -> Int32 { Int32(Builtin.fptosi_FPIEEE32_Int32(x._value)) - // CHECK: fptosi float %{{[0-9]+}} to i32 + // CHECK: fptosi float %{{.+}} to i32 } func fptosiWithFreeze(_ x: Float) -> Int32 { Int32(Builtin.freeze_Int32(Builtin.fptosi_FPIEEE32_Int32(x._value))) - // CHECK: fptosi float %{{[0-9]+}} to i32 - // CHECK-NEXT: freeze i32 %{{[0-9]+}} + // CHECK: fptosi float %{{.+}} to i32 + // CHECK-NEXT: freeze i32 %{{.+}} } func yuck() -> Int32 { @@ -28,7 +28,7 @@ func fptosi(_ x: SIMD2) -> SIMD2 { var result = SIMD2() result._storage._value = maybePoison return result - // CHECK: fptosi <2 x float> %{{[0-9]+}} to <2 x i32> + // CHECK: fptosi <2 x float> %{{.+}} to <2 x i32> } func fptosiWithFreeze(_ x: SIMD2) -> SIMD2 { @@ -37,8 +37,8 @@ func fptosiWithFreeze(_ x: SIMD2) -> SIMD2 { var result = SIMD2() result._storage._value = frozen return result - // CHECK: fptosi <2 x float> %{{[0-9]+}} to <2 x i32> - // CHECK-NEXT: freeze <2 x i32> %{{[0-9]+}} + // CHECK: fptosi <2 x float> %{{.+}} to <2 x i32> + // CHECK-NEXT: freeze <2 x i32> %{{.+}} } func doubleYuck(_ x: SIMD2) -> SIMD2 {