[clang] [clang] Implement -fstrict-bool (PR #160790)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 18 07:03:02 PST 2025
=?utf-8?q?Félix?= Cloutier <fcloutier at apple.com>,
=?utf-8?q?Félix?= Cloutier <fcloutier at apple.com>,
=?utf-8?q?Félix?= Cloutier <fcloutier at apple.com>,
=?utf-8?q?Félix?= Cloutier <fcloutier at apple.com>,
=?utf-8?q?Félix?= Cloutier <fcloutier at apple.com>,
=?utf-8?q?Félix?= Cloutier <fcloutier at apple.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/160790 at github.com>
https://github.com/apple-fcloutier updated https://github.com/llvm/llvm-project/pull/160790
>From 93926f92102688806f550867ef0687006fbff6d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Mon, 18 Nov 2024 18:23:48 -0800
Subject: [PATCH 1/7] [clang] Implement -fstrict-bool
``bool`` values are stored as i8 in memory, and it is undefined
behavior for a ``bool`` value to be any value other than 0 or 1. Clang
exploits this with range metadata: ``bool`` load instructions at any
optimization level above -O0 are assumed to only have their lowest bit
set. This can create memory safety problems when other bits are set,
for instance through ``memcpy``.
This change allows users to configure this behavior in three ways:
* ``-fstrict-bool`` represents the status quo; range metadata is added at levels
above -O0 and allows the compiler to assume in-memory ``bool`` values are
always either 0 or 1.
* ``-fno-strict-bool[={truncate|nonzero}]`` disables range metadata on ``bool``
loaded values and offers two ways to interpret the loaded values. ``truncate``
means the value is true is the least significant bit is 1 and false otherwise;
``nonzero`` means the value is true if any bit is 1 and false otherwise.
The default is ``-fstrict-bool`` to not change the current behavior of Clang.
The default behavior of ``-fno-strict-bool`` is ``truncate``.
Radar-ID: 139397212
---
clang/docs/ReleaseNotes.rst | 1 +
clang/docs/UsersManual.rst | 18 +++++++++++++++
clang/include/clang/Basic/CodeGenOptions.def | 1 +
clang/include/clang/Basic/CodeGenOptions.h | 10 ++++++++
clang/include/clang/Options/Options.td | 21 +++++++++++++++++
clang/lib/CodeGen/CGExpr.cpp | 24 +++++++++++++++-----
clang/lib/Driver/ToolChains/Clang.cpp | 22 ++++++++++++++++++
clang/test/CodeGen/strict-bool.c | 23 +++++++++++++++++++
clang/test/Driver/strict-bool.c | 22 ++++++++++++++++++
9 files changed, 136 insertions(+), 6 deletions(-)
create mode 100644 clang/test/CodeGen/strict-bool.c
create mode 100644 clang/test/Driver/strict-bool.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index feaf92ad4415f..8ac6e5faf622e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -342,6 +342,7 @@ New Compiler Flags
- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
- New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
- The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``.
+- New option ``-f[no-]strict-bool`` added to control whether Clang can assume that ``bool`` values loaded from memory cannot have a bit pattern other than 0 or 1.
- New option ``-fmatrix-memory-layout`` added to control the memory layout of Clang matrix types. (e.g. ``-fmatrix-memory-layout=column-major`` or ``-fmatrix-memory-layout=row-major``).
Lanai Support
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index c624efb26f67d..b9d4e857d5159 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2321,6 +2321,24 @@ are listed below.
additional function arity information (for supported targets). See
:doc:`ControlFlowIntegrity` for more details.
+.. option:: -fstrict-bool
+
+ ``bool`` values are stored to memory as 8-bit values on most targets. Under
+ ``-fstrict-bool``, it is undefined behavior for a ``bool`` value stored in
+ memory to have any other bit pattern than 0 or 1. This creates some
+ optimization opportunities for the compiler, but it enables memory
+ corruption if that assumption is violated, for instance if any other value
+ is ``memcpy``ed over a ``bool``. This is enabled by default.
+
+.. option:: -fno-strict-bool[={truncate|nonzero}]
+
+ Disable optimizations based on the assumption that all ``bool`` values,
+ which are typically represented as 8-bit integers in memory, only ever
+ contain bit patterns 0 or 1. When =truncate is specified, a ``bool`` is
+ true if its least significant bit is set, and false otherwise. When =nonzero
+ is specified, a ``bool`` is true when any bit is set, and false otherwise.
+ The default is =truncate.
+
.. option:: -fstrict-vtable-pointers
Enable optimizations based on the strict rules for overwriting polymorphic
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index a059803c433e3..6f4cc374222f7 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -319,6 +319,7 @@ CODEGENOPT(SoftFloat , 1, 0, Benign) ///< -soft-float.
CODEGENOPT(SpeculativeLoadHardening, 1, 0, Benign) ///< Enable speculative load hardening.
CODEGENOPT(FineGrainedBitfieldAccesses, 1, 0, Benign) ///< Enable fine-grained bitfield accesses.
CODEGENOPT(StrictEnums , 1, 0, Benign) ///< Optimize based on strict enum definition.
+ENUM_CODEGENOPT(LoadBoolFromMem, BoolFromMem, 2, BoolFromMem::Strict, Benign) ///> Optimize based on in-memory bool values being 0 or 1.
CODEGENOPT(StrictVTablePointers, 1, 0, Benign) ///< Optimize based on the strict vtable pointers
CODEGENOPT(TimePasses , 1, 0, Benign) ///< Set when -ftime-report, -ftime-report=, -ftime-report-json, or -stats-file-timers is enabled.
CODEGENOPT(TimePassesPerRun , 1, 0, Benign) ///< Set when -ftime-report=per-pass-run is enabled.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index c60ca507ff917..3d050df1847dc 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -214,6 +214,16 @@ class CodeGenOptions : public CodeGenOptionsBase {
///< larger debug info than `Basic`.
};
+ enum BoolFromMem {
+ Strict, ///< In-memory bool values are assumed to be 0 or 1, and any other
+ ///< value is UB.
+ Truncate, ///< Convert in-memory bools to i1 by checking if the least
+ ///< significant bit is 1.
+ NonZero, ///< Convert in-memory bools to i1 by checking if any bit is set
+ ///< to 1.
+ NonStrictDefault = Truncate
+ };
+
/// The code model to use (-mcmodel).
std::string CodeModel;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 45c5322351a17..dfd25be63ceac 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4243,6 +4243,27 @@ def fno_debug_macro : Flag<["-"], "fno-debug-macro">, Group<f_Group>,
def fstrict_aliasing : Flag<["-"], "fstrict-aliasing">, Group<f_Group>,
Visibility<[ClangOption, CLOption, DXCOption]>,
HelpText<"Enable optimizations based on strict aliasing rules">;
+def fstrict_bool : Flag<["-"], "fstrict-bool">, Group<f_Group>,
+ Visibility<[ClangOption]>,
+ HelpText<"Enable optimizations based on bool bit patterns never being "
+ "anything other than 0 or 1">;
+def fno_strict_bool : Flag<["-"], "fno-strict-bool">, Group<f_Group>,
+ Visibility<[ClangOption]>,
+ HelpText<"Disable optimizations based on bool bit patterns never being "
+ "anything other than 0 or 1">;
+def fno_strict_bool_EQ : Joined<["-"], "fno-strict-bool=">, Group<f_Group>,
+ Visibility<[ClangOption]>,
+ HelpText<"Disable optimizations based on bool bit patterns never being "
+ "anything other than 0 or 1, specifying a conversion behavior.">,
+ Values<"truncate,nonzero">;
+def load_bool_from_mem : Joined<["-"], "load-bool-from-mem=">, Group<f_Group>,
+ Visibility<[CC1Option]>,
+ HelpText<"Specify how to convert a multi-bit bool loaded from memory to a "
+ "1-bit value">,
+ NormalizedValuesScope<"CodeGenOptions::BoolFromMem">,
+ Values<"strict,nonstrict,truncate,nonzero">,
+ NormalizedValues<["Strict", "NonStrictDefault", "Truncate", "NonZero"]>,
+ MarshallingInfoEnum<CodeGenOpts<"LoadBoolFromMem">, "Strict">;
def fstrict_enums : Flag<["-"], "fstrict-enums">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable optimizations based on the strict definition of an enum's "
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index e511c66835dd0..c385a86e72e82 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2040,9 +2040,9 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue,
lvalue.getTBAAInfo(), lvalue.isNontemporal());
}
-static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
- llvm::APInt &Min, llvm::APInt &End,
- bool StrictEnums, bool IsBool) {
+static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
+ llvm::APInt &End, bool StrictEnums, bool StrictBool,
+ bool IsBool) {
const auto *ED = Ty->getAsEnumDecl();
bool IsRegularCPlusPlusEnum =
CGF.getLangOpts().CPlusPlus && StrictEnums && ED && !ED->isFixed();
@@ -2050,6 +2050,8 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
return false;
if (IsBool) {
+ if (!StrictBool)
+ return false;
Min = llvm::APInt(CGF.getContext().getTypeSize(Ty), 0);
End = llvm::APInt(CGF.getContext().getTypeSize(Ty), 2);
} else {
@@ -2060,7 +2062,10 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
llvm::APInt Min, End;
+ bool IsStrictBool =
+ CGM.getCodeGenOpts().getLoadBoolFromMem() == CodeGenOptions::Strict;
if (!getRangeForType(*this, Ty, Min, End, CGM.getCodeGenOpts().StrictEnums,
+ IsStrictBool,
Ty->hasBooleanRepresentation() && !Ty->isVectorType()))
return nullptr;
@@ -2108,7 +2113,8 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
return false;
llvm::APInt Min, End;
- if (!getRangeForType(*this, Ty, Min, End, /*StrictEnums=*/true, IsBool))
+ if (!getRangeForType(*this, Ty, Min, End, /*StrictEnums=*/true,
+ /*StrictBool=*/true, IsBool))
return true;
SanitizerKind::SanitizerOrdinal Kind =
@@ -2258,8 +2264,14 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
llvm::Type *ResTy = ConvertType(Ty);
if (Ty->hasBooleanRepresentation() || Ty->isBitIntType() ||
- Ty->isExtVectorBoolType())
- return Builder.CreateTrunc(Value, ResTy, "loadedv");
+ Ty->isExtVectorBoolType()) {
+ if (CGM.getCodeGenOpts().getLoadBoolFromMem() == CodeGenOptions::NonZero) {
+ auto *NonZero = Builder.CreateICmpNE(
+ Value, llvm::Constant::getNullValue(Value->getType()), "loadedv.nz");
+ return Builder.CreateIntCast(NonZero, ResTy, false, "loadedv");
+ } else
+ return Builder.CreateTrunc(Value, ResTy, "loadedv");
+ }
return Value;
}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 310f3b58a211e..2673bd1af596a 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5763,6 +5763,28 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (!Args.hasFlag(options::OPT_fstruct_path_tbaa,
options::OPT_fno_struct_path_tbaa, true))
CmdArgs.push_back("-no-struct-path-tbaa");
+
+ if (Arg *A = Args.getLastArg(options::OPT_fstrict_bool,
+ options::OPT_fno_strict_bool,
+ options::OPT_fno_strict_bool_EQ)) {
+ StringRef BFM = "";
+ if (A->getOption().matches(options::OPT_fstrict_bool))
+ BFM = "strict";
+ else if (A->getOption().matches(options::OPT_fno_strict_bool))
+ BFM = "nonstrict";
+ else if (A->getValue() == StringRef("truncate"))
+ BFM = "truncate";
+ else if (A->getValue() == StringRef("nonzero"))
+ BFM = "nonzero";
+ else
+ D.Diag(diag::err_drv_invalid_value)
+ << A->getAsString(Args) << A->getValue();
+ CmdArgs.push_back(Args.MakeArgString("-load-bool-from-mem=" + BFM));
+ } else if (KernelOrKext) {
+ // If unspecified, assume -fno-strict-bool=truncate in the Darwin kernel.
+ CmdArgs.push_back("-load-bool-from-mem=truncate");
+ }
+
Args.addOptInFlag(CmdArgs, options::OPT_fstrict_enums,
options::OPT_fno_strict_enums);
Args.addOptOutFlag(CmdArgs, options::OPT_fstrict_return,
diff --git a/clang/test/CodeGen/strict-bool.c b/clang/test/CodeGen/strict-bool.c
new file mode 100644
index 0000000000000..f95cade48d7aa
--- /dev/null
+++ b/clang/test/CodeGen/strict-bool.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-STRICT
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-STRICT
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonstrict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-TRUNCATE
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-TRUNCATE
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonzero -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NONZERO
+
+struct has_bool {
+ _Bool b;
+};
+
+int foo(struct has_bool *b) {
+ // CHECK-STRICT: load i8, {{.*}}, !range ![[RANGE_BOOL:[0-9]+]]
+ // CHECK-STRICT-NOT: and i8
+
+ // CHECK-TRUNCATE: [[BOOL:%.+]] = load i8
+ // CHECK-TRUNCATE: and i8 [[BOOL]], 1
+
+ // CHECK-NONZERO: [[BOOL:%.+]] = load i8
+ // CHECK-NONZERO: cmp ne i8 [[BOOL]], 0
+ return b->b;
+}
+
+// CHECK_STRICT: ![[RANGE_BOOL]] = !{i8 0, i8 2}
diff --git a/clang/test/Driver/strict-bool.c b/clang/test/Driver/strict-bool.c
new file mode 100644
index 0000000000000..dc1a25872324b
--- /dev/null
+++ b/clang/test/Driver/strict-bool.c
@@ -0,0 +1,22 @@
+// RUN: %clang -### %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
+// RUN: %clang -### -fstrict-bool %s 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT
+// RUN: %clang -### -fno-strict-bool %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONSTRICT
+// RUN: %clang -### -fno-strict-bool=truncate %s 2>&1 | FileCheck %s --check-prefix=CHECK-TRUNCATE
+// RUN: %clang -### -fno-strict-bool=nonzero %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONZERO
+// RUN: %clang -### -fstrict-bool -fno-strict-bool %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONSTRICT
+// RUN: %clang -### -fno-strict-bool -fno-strict-bool=nonzero %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONZERO
+// RUN: %clang -### -fno-strict-bool=nonzero -fstrict-bool %s 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT
+
+// RUN: %clang -### -mkernel %s 2>&1 | FileCheck %s --check-prefix=CHECK-TRUNCATE
+// RUN: %clang -### -fapple-kext %s 2>&1 | FileCheck %s --check-prefix=CHECK-TRUNCATE
+// RUN: %clang -### -mkernel -fstrict-bool %s 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT
+// RUN: %clang -### -fstrict-bool -mkernel %s 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT
+
+// RUN: not %clang -### -fno-strict-bool=ow-ouch %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID
+
+// CHECK-NONE-NOT: -load-bool-from-mem
+// CHECK-STRICT: -load-bool-from-mem=strict
+// CHECK-NONSTRICT: -load-bool-from-mem=nonstrict
+// CHECK-TRUNCATE: -load-bool-from-mem=truncate
+// CHECK-NONZERO: -load-bool-from-mem=nonzero
+// CHECK-INVALID: invalid value 'ow-ouch' in '-fno-strict-bool=ow-ouch'
>From ba50d4feafa5a3bc9094ac80c035026d5617bc92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Fri, 26 Sep 2025 12:50:10 -0700
Subject: [PATCH 2/7] * CodeGenOptions::BoolFromMem is now an ``enum class``. *
Fix ``CodeGenFunction::EmitFromMemory`` breaking BitInt with
-fno-strict-bool=nonzero. Add a BitInt test invocation with
-fno-strict-bool=nonzero. * Add a note above ``getRangeForType`` warning
against extending its functionality without introducing -fstrict flags and
sanitizer options. * Add UBSan variants of the strict-bool tests showing that
UBSan wins.
---
clang/include/clang/Basic/CodeGenOptions.h | 2 +-
clang/lib/CodeGen/CGExpr.cpp | 31 ++++++++++++++--------
clang/test/CodeGen/ext-int.c | 1 +
clang/test/CodeGen/strict-bool.c | 19 ++++++++++---
4 files changed, 38 insertions(+), 15 deletions(-)
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 3d050df1847dc..4d89b0353f8bd 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -214,7 +214,7 @@ class CodeGenOptions : public CodeGenOptionsBase {
///< larger debug info than `Basic`.
};
- enum BoolFromMem {
+ enum class BoolFromMem {
Strict, ///< In-memory bool values are assumed to be 0 or 1, and any other
///< value is UB.
Truncate, ///< Convert in-memory bools to i1 by checking if the least
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index c385a86e72e82..e85e91e09e9c4 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2040,6 +2040,13 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue,
lvalue.getTBAAInfo(), lvalue.isNontemporal());
}
+// XXX: safety first! This method SHOULD NOT be extended to support additional
+// types, like BitInt types, without an opt-in bool controlled by a
+// CodeGenOptions setting (like -fstrict-bool) and a new UBSan check (like
+// SanitizerKind::Bool) as breaking that assumption would lead to memory
+// corruption. See link for examples of how having a bool that has a value
+// different from 0 or 1 in memory can lead to memory corruption.
+// https://discourse.llvm.org/t/defining-what-happens-when-a-bool-isn-t-0-or-1/86778
static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
llvm::APInt &End, bool StrictEnums, bool StrictBool,
bool IsBool) {
@@ -2062,8 +2069,8 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
llvm::APInt Min, End;
- bool IsStrictBool =
- CGM.getCodeGenOpts().getLoadBoolFromMem() == CodeGenOptions::Strict;
+ bool IsStrictBool = CGM.getCodeGenOpts().getLoadBoolFromMem() ==
+ CodeGenOptions::BoolFromMem::Strict;
if (!getRangeForType(*this, Ty, Min, End, CGM.getCodeGenOpts().StrictEnums,
IsStrictBool,
Ty->hasBooleanRepresentation() && !Ty->isVectorType()))
@@ -2263,15 +2270,17 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
}
llvm::Type *ResTy = ConvertType(Ty);
- if (Ty->hasBooleanRepresentation() || Ty->isBitIntType() ||
- Ty->isExtVectorBoolType()) {
- if (CGM.getCodeGenOpts().getLoadBoolFromMem() == CodeGenOptions::NonZero) {
- auto *NonZero = Builder.CreateICmpNE(
- Value, llvm::Constant::getNullValue(Value->getType()), "loadedv.nz");
- return Builder.CreateIntCast(NonZero, ResTy, false, "loadedv");
- } else
- return Builder.CreateTrunc(Value, ResTy, "loadedv");
- }
+ bool IsBitInt = Ty->isBitIntType();
+ bool HasBoolRep = Ty->hasBooleanRepresentation();
+ if (HasBoolRep && !IsBitInt &&
+ CGM.getCodeGenOpts().getLoadBoolFromMem() ==
+ CodeGenOptions::BoolFromMem::NonZero) {
+ auto *NonZero = Builder.CreateICmpNE(
+ Value, llvm::Constant::getNullValue(Value->getType()), "loadedv.nz");
+ return Builder.CreateIntCast(NonZero, ResTy, false, "loadedv");
+ }
+ if (HasBoolRep || IsBitInt || Ty->isExtVectorBoolType())
+ return Builder.CreateTrunc(Value, ResTy, "loadedv");
return Value;
}
diff --git a/clang/test/CodeGen/ext-int.c b/clang/test/CodeGen/ext-int.c
index aebacd6f22ffc..157d1990c060e 100644
--- a/clang/test/CodeGen/ext-int.c
+++ b/clang/test/CodeGen/ext-int.c
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c23 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK64,LIN64
+// RUN: %clang_cc1 -std=c23 -triple x86_64-gnu-linux -O3 -load-bool-from-mem=nonzero -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK64,LIN64
// RUN: %clang_cc1 -std=c23 -triple x86_64-windows-pc -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK64,WIN64
// RUN: %clang_cc1 -std=c23 -triple i386-gnu-linux -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LIN32
// RUN: %clang_cc1 -std=c23 -triple i386-windows-pc -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WIN32
diff --git a/clang/test/CodeGen/strict-bool.c b/clang/test/CodeGen/strict-bool.c
index f95cade48d7aa..c5dbe2dd0d7df 100644
--- a/clang/test/CodeGen/strict-bool.c
+++ b/clang/test/CodeGen/strict-bool.c
@@ -3,20 +3,33 @@
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonstrict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-TRUNCATE
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-TRUNCATE
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonzero -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NONZERO
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-UBSAN-STRICT
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-UBSAN-TRUNCATE
struct has_bool {
_Bool b;
};
int foo(struct has_bool *b) {
- // CHECK-STRICT: load i8, {{.*}}, !range ![[RANGE_BOOL:[0-9]+]]
- // CHECK-STRICT-NOT: and i8
+ // CHECK-STRICT: [[BOOL:%.+]] = load i8, ptr {{.+}}, !range ![[RANGE_BOOL:[0-9]+]]
+ // CHECK-STRICT-NOT: and i8 [[BOOL]], 1
+ // CHECK-STRICT-NOT: icmp ne i8 [[BOOL]], 0
+ // CHECK-TRUNCATE-NOT: !range
// CHECK-TRUNCATE: [[BOOL:%.+]] = load i8
// CHECK-TRUNCATE: and i8 [[BOOL]], 1
+ // CHECK-NONZERO-NOT: !range
// CHECK-NONZERO: [[BOOL:%.+]] = load i8
- // CHECK-NONZERO: cmp ne i8 [[BOOL]], 0
+ // CHECK-NONZERO: icmp ne i8 [[BOOL]], 0
+
+ // CHECK-UBSAN-STRICT-NOT: !range
+ // CHECK-UBSAN-STRICT: [[BOOL:%.+]] = load i8, ptr {{.+}}
+ // CHECK-UBSAN-STRICT: icmp ult i8 [[BOOL]], 2
+
+ // CHECK-UBSAN-TRUNCATE-NOT: !range
+ // CHECK-UBSAN-TRUNCATE: [[BOOL:%.+]] = load i8, ptr {{.+}}
+ // CHECK-UBSAN-TRUNCATE: icmp ult i8 [[BOOL]], 2
return b->b;
}
>From 91fa717134d855e13ffb9dd2a7904324cb9b7f5e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Mon, 29 Sep 2025 18:38:30 -0700
Subject: [PATCH 3/7] Review feedback
---
clang/docs/UsersManual.rst | 26 +++++++++++++--------
clang/lib/CodeGen/CGExpr.cpp | 11 ++++-----
clang/test/CodeGen/ext-int.c | 1 -
clang/test/CodeGen/strict-bool.c | 40 ++++++++++++++++++++++++++------
4 files changed, 53 insertions(+), 25 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index b9d4e857d5159..b65ce7a3c071a 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2323,21 +2323,27 @@ are listed below.
.. option:: -fstrict-bool
- ``bool`` values are stored to memory as 8-bit values on most targets. Under
- ``-fstrict-bool``, it is undefined behavior for a ``bool`` value stored in
- memory to have any other bit pattern than 0 or 1. This creates some
- optimization opportunities for the compiler, but it enables memory
- corruption if that assumption is violated, for instance if any other value
- is ``memcpy``ed over a ``bool``. This is enabled by default.
+ ``bool`` values are stored to memory as 8-bit values on most targets. C and
+ C++ specify that it is undefined behavior to put a value other than 0 or 1
+ in the storage of a ``bool`` value, and with ``-fstrict-bool``, Clang
+ leverages this knowledge for optimization opportunities. When this
+ assumption is violated, for instance if invalid data is ``memcpy``ed over a
+ ``bool``, the optimized code can lead to memory corruption.
+ ``-fstrict-bool`` is enabled by default.
.. option:: -fno-strict-bool[={truncate|nonzero}]
Disable optimizations based on the assumption that all ``bool`` values,
which are typically represented as 8-bit integers in memory, only ever
- contain bit patterns 0 or 1. When =truncate is specified, a ``bool`` is
- true if its least significant bit is set, and false otherwise. When =nonzero
- is specified, a ``bool`` is true when any bit is set, and false otherwise.
- The default is =truncate.
+ contain bit patterns 0 or 1. When ``=truncate`` is specified, a ``bool`` is
+ true if its least significant bit is set, and false otherwise. When
+ ``=nonzero`` is specified, a ``bool`` is true when any bit is set, and
+ false otherwise. The default is ``=truncate``, but this could change in
+ future releases.
+
+ ``-fno-strict-bool`` does not permit Clang to store a value other than 0 or
+ 1 in a ``bool``: it is a safety net against programmer mistakes, such as
+ ``memcpy``ing invalid data over a ``bool``.
.. option:: -fstrict-vtable-pointers
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index e85e91e09e9c4..78a3c47362bdf 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2270,16 +2270,13 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
}
llvm::Type *ResTy = ConvertType(Ty);
- bool IsBitInt = Ty->isBitIntType();
bool HasBoolRep = Ty->hasBooleanRepresentation();
- if (HasBoolRep && !IsBitInt &&
- CGM.getCodeGenOpts().getLoadBoolFromMem() ==
+ if (HasBoolRep && CGM.getCodeGenOpts().getLoadBoolFromMem() ==
CodeGenOptions::BoolFromMem::NonZero) {
- auto *NonZero = Builder.CreateICmpNE(
- Value, llvm::Constant::getNullValue(Value->getType()), "loadedv.nz");
- return Builder.CreateIntCast(NonZero, ResTy, false, "loadedv");
+ return Builder.CreateICmpNE(
+ Value, llvm::Constant::getNullValue(Value->getType()), "loadedv");
}
- if (HasBoolRep || IsBitInt || Ty->isExtVectorBoolType())
+ if (HasBoolRep || Ty->isBitIntType() || Ty->isExtVectorBoolType())
return Builder.CreateTrunc(Value, ResTy, "loadedv");
return Value;
diff --git a/clang/test/CodeGen/ext-int.c b/clang/test/CodeGen/ext-int.c
index 157d1990c060e..aebacd6f22ffc 100644
--- a/clang/test/CodeGen/ext-int.c
+++ b/clang/test/CodeGen/ext-int.c
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -std=c23 -triple x86_64-gnu-linux -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK64,LIN64
-// RUN: %clang_cc1 -std=c23 -triple x86_64-gnu-linux -O3 -load-bool-from-mem=nonzero -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK64,LIN64
// RUN: %clang_cc1 -std=c23 -triple x86_64-windows-pc -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK64,WIN64
// RUN: %clang_cc1 -std=c23 -triple i386-gnu-linux -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LIN32
// RUN: %clang_cc1 -std=c23 -triple i386-windows-pc -O3 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WIN32
diff --git a/clang/test/CodeGen/strict-bool.c b/clang/test/CodeGen/strict-bool.c
index c5dbe2dd0d7df..f8894345f46db 100644
--- a/clang/test/CodeGen/strict-bool.c
+++ b/clang/test/CodeGen/strict-bool.c
@@ -1,15 +1,17 @@
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-STRICT
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-STRICT
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonstrict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-TRUNCATE
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-TRUNCATE
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonzero -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NONZERO
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-UBSAN-STRICT
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-UBSAN-TRUNCATE
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-STRICT
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-STRICT
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonstrict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-TRUNCATE
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-TRUNCATE
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonzero -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-NONZERO
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-UBSAN-STRICT
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-UBSAN-TRUNCATE
struct has_bool {
_Bool b;
+ unsigned _BitInt(1) c;
};
+// CHECK: @foo
int foo(struct has_bool *b) {
// CHECK-STRICT: [[BOOL:%.+]] = load i8, ptr {{.+}}, !range ![[RANGE_BOOL:[0-9]+]]
// CHECK-STRICT-NOT: and i8 [[BOOL]], 1
@@ -33,4 +35,28 @@ int foo(struct has_bool *b) {
return b->b;
}
+// CHECK: @bar
+int bar(struct has_bool *c) {
+ // CHECK-STRICT: [[BITINT:%.+]] = load i8, ptr {{.+}}, !range ![[RANGE_BOOL:[0-9]+]]
+ // CHECK-STRICT-NOT: and i8 [[BITINT]], 1
+ // CHECK-STRICT-NOT: icmp ne i8 [[BITINT]], 0
+
+ // CHECK-TRUNCATE-NOT: !range
+ // CHECK-TRUNCATE: [[BITINT:%.+]] = load i8
+ // CHECK-TRUNCATE: and i8 [[BITINT]], 1
+
+ // CHECK-NONZERO-NOT: !range
+ // CHECK-NONZERO: [[BITINT:%.+]] = load i8
+ // CHECK-NONZERO: icmp ne i8 [[BITINT]], 0
+
+ // CHECK-UBSAN-STRICT-NOT: !range
+ // CHECK-UBSAN-STRICT: [[BITINT:%.+]] = load i8, ptr {{.+}}
+ // CHECK-UBSAN-STRICT: icmp ult i8 [[BITINT]], 2
+
+ // CHECK-UBSAN-TRUNCATE-NOT: !range
+ // CHECK-UBSAN-TRUNCATE: [[BITINT:%.+]] = load i8, ptr {{.+}}
+ // CHECK-UBSAN-TRUNCATE: icmp ult i8 [[BITINT]], 2
+ return c->c;
+}
+
// CHECK_STRICT: ![[RANGE_BOOL]] = !{i8 0, i8 2}
>From 2d6428b3500fbf42807b808ed0009155788e7837 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Mon, 29 Sep 2025 18:40:17 -0700
Subject: [PATCH 4/7] (clang-format)
---
clang/lib/CodeGen/CGExpr.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 78a3c47362bdf..5dffce5c8229d 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2272,7 +2272,7 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
llvm::Type *ResTy = ConvertType(Ty);
bool HasBoolRep = Ty->hasBooleanRepresentation();
if (HasBoolRep && CGM.getCodeGenOpts().getLoadBoolFromMem() ==
- CodeGenOptions::BoolFromMem::NonZero) {
+ CodeGenOptions::BoolFromMem::NonZero) {
return Builder.CreateICmpNE(
Value, llvm::Constant::getNullValue(Value->getType()), "loadedv");
}
>From c0a38538cb3db6ee918b4619eca06f47666bcc41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Mon, 8 Dec 2025 10:55:41 +0100
Subject: [PATCH 5/7] Address review feedback:
* Update release notes to clarify -fstrict-bool (the existing behavior) remains the default
* Update user manual to clarify that -fno-strict-bool does not cancel -fsanitize=bool
* Change default non-strict mode to be NonZero
* Update tests and comments
---
clang/docs/ReleaseNotes.rst | 3 ++-
clang/docs/UsersManual.rst | 22 +++++++++++++--------
clang/include/clang/Basic/CodeGenOptions.h | 2 +-
clang/lib/CodeGen/CGExpr.cpp | 23 +++++++++++-----------
clang/test/CodeGen/strict-bool.c | 2 +-
clang/test/Driver/strict-bool.c | 10 ++++++++--
6 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8ac6e5faf622e..4679b159f7a47 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -342,7 +342,8 @@ New Compiler Flags
- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
- New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
- The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``.
-- New option ``-f[no-]strict-bool`` added to control whether Clang can assume that ``bool`` values loaded from memory cannot have a bit pattern other than 0 or 1.
+- New option ``-f[no-]strict-bool`` added to control whether Clang can assume that ``bool`` values (stored as whole bytes in memory) can only contain the values 0 and 1.
+ Clang's previous behavior (making that assumption) is codified as ``-fstrict-bool`` and remains the default.
- New option ``-fmatrix-memory-layout`` added to control the memory layout of Clang matrix types. (e.g. ``-fmatrix-memory-layout=column-major`` or ``-fmatrix-memory-layout=row-major``).
Lanai Support
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index b65ce7a3c071a..f25a1f0585624 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2335,15 +2335,21 @@ are listed below.
Disable optimizations based on the assumption that all ``bool`` values,
which are typically represented as 8-bit integers in memory, only ever
- contain bit patterns 0 or 1. When ``=truncate`` is specified, a ``bool`` is
- true if its least significant bit is set, and false otherwise. When
+ contain bit patterns 0 or 1. When ``=truncate`` is specified, a ``bool``
+ is true if its least significant bit is set, and false otherwise. When
``=nonzero`` is specified, a ``bool`` is true when any bit is set, and
- false otherwise. The default is ``=truncate``, but this could change in
- future releases.
-
- ``-fno-strict-bool`` does not permit Clang to store a value other than 0 or
- 1 in a ``bool``: it is a safety net against programmer mistakes, such as
- ``memcpy``ing invalid data over a ``bool``.
+ false otherwise. The default is ``=nonzero``.
+
+ ``-fno-strict-bool`` does not permit developers to store a value other
+ than 0 or 1 in a ``bool``: it is a safety net against mistakes, such as
+ ``memcpy``ing invalid data over a ``bool``. Using invalid ``bool`` bit
+ patterns is still undefined behavior, even as this option limits the
+ negative consequences. In particular, enabling the UBSan
+ ``-fsanitize=bool`` check will continue to trap for invalid ``bool``
+ values when ``-fno-strict-bool`` is also specified, and program parts
+ that were compiled without ``-fno-strict-bool`` (or by different
+ compilers that have no equivalent option) will continue to behave
+ erratically.
.. option:: -fstrict-vtable-pointers
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 4d89b0353f8bd..4d39048e7c803 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -221,7 +221,7 @@ class CodeGenOptions : public CodeGenOptionsBase {
///< significant bit is 1.
NonZero, ///< Convert in-memory bools to i1 by checking if any bit is set
///< to 1.
- NonStrictDefault = Truncate
+ NonStrictDefault = NonZero
};
/// The code model to use (-mcmodel).
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 5dffce5c8229d..5ff3c74039438 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2040,12 +2040,12 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue,
lvalue.getTBAAInfo(), lvalue.isNontemporal());
}
-// XXX: safety first! This method SHOULD NOT be extended to support additional
-// types, like BitInt types, without an opt-in bool controlled by a
-// CodeGenOptions setting (like -fstrict-bool) and a new UBSan check (like
-// SanitizerKind::Bool) as breaking that assumption would lead to memory
-// corruption. See link for examples of how having a bool that has a value
-// different from 0 or 1 in memory can lead to memory corruption.
+// This method SHOULD NOT be extended to support additional types, like BitInt
+// types, without an opt-in bool controlled by a CodeGenOptions setting (like
+// -fstrict-bool) and a new UBSan check (like SanitizerKind::Bool) as breaking
+// that assumption would lead to memory corruption. See link for examples of how
+// having a bool that has a value different from 0 or 1 in memory can lead to
+// memory corruption.
// https://discourse.llvm.org/t/defining-what-happens-when-a-bool-isn-t-0-or-1/86778
static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
llvm::APInt &End, bool StrictEnums, bool StrictBool,
@@ -2069,11 +2069,12 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
llvm::APInt Min, End;
- bool IsStrictBool = CGM.getCodeGenOpts().getLoadBoolFromMem() ==
- CodeGenOptions::BoolFromMem::Strict;
- if (!getRangeForType(*this, Ty, Min, End, CGM.getCodeGenOpts().StrictEnums,
- IsStrictBool,
- Ty->hasBooleanRepresentation() && !Ty->isVectorType()))
+ bool IsBool = Ty->hasBooleanRepresentation() && !Ty->isVectorType();
+ bool StrictBoolEnabled = CGM.getCodeGenOpts().getLoadBoolFromMem() ==
+ CodeGenOptions::BoolFromMem::Strict;
+ if (!getRangeForType(*this, Ty, Min, End,
+ /*StrictEnums=*/CGM.getCodeGenOpts().StrictEnums,
+ /*StrictBool=*/StrictBoolEnabled, /*IsBool=*/IsBool))
return nullptr;
llvm::MDBuilder MDHelper(getLLVMContext());
diff --git a/clang/test/CodeGen/strict-bool.c b/clang/test/CodeGen/strict-bool.c
index f8894345f46db..b428a81a5cb13 100644
--- a/clang/test/CodeGen/strict-bool.c
+++ b/clang/test/CodeGen/strict-bool.c
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-STRICT
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-STRICT
-// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonstrict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-TRUNCATE
+// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonstrict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-NONZERO
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-TRUNCATE
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -load-bool-from-mem=nonzero -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-NONZERO
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-UBSAN-STRICT
diff --git a/clang/test/Driver/strict-bool.c b/clang/test/Driver/strict-bool.c
index dc1a25872324b..0106d35330d11 100644
--- a/clang/test/Driver/strict-bool.c
+++ b/clang/test/Driver/strict-bool.c
@@ -12,11 +12,17 @@
// RUN: %clang -### -mkernel -fstrict-bool %s 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT
// RUN: %clang -### -fstrict-bool -mkernel %s 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT
-// RUN: not %clang -### -fno-strict-bool=ow-ouch %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID
+// RUN: not %clang -### -fno-strict-bool= %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-FNO
+// RUN: not %clang -### -fno-strict-bool=ow-ouch %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-FNO
+// RUN: not %clang -### -fstrict-bool= %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID
+// RUN: not %clang -### -fstrict-bool=ow-ouch %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID
+// RUN: not %clang -### -fstrict-bool=truncate %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID
+// RUN: not %clang -### -fstrict-bool=nonzero %s 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID
// CHECK-NONE-NOT: -load-bool-from-mem
// CHECK-STRICT: -load-bool-from-mem=strict
// CHECK-NONSTRICT: -load-bool-from-mem=nonstrict
// CHECK-TRUNCATE: -load-bool-from-mem=truncate
// CHECK-NONZERO: -load-bool-from-mem=nonzero
-// CHECK-INVALID: invalid value 'ow-ouch' in '-fno-strict-bool=ow-ouch'
+// CHECK-INVALID: unknown argument{{:?}} '-fstrict-bool={{.*}}'
+// CHECK-INVALID-FNO: invalid value '{{.*}}' in '-f{{(no-)?}}strict-bool={{.*}}'
>From c93f0b5fdc0fc687298f272c1d5c8b52dae01cfc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Mon, 15 Dec 2025 19:55:36 +0000
Subject: [PATCH 6/7] Fix strict bool effect on unpacked vector loads (HLSL
only), reorganize logic to decide if converting with nonzero method
---
clang/lib/CodeGen/CGExpr.cpp | 28 +++++++++++++++++--------
clang/test/CodeGen/strict-bool.c | 14 +++++++++++--
clang/test/CodeGenHLSL/strict-bool.hlsl | 14 +++++++++++++
3 files changed, 45 insertions(+), 11 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/strict-bool.hlsl
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 5ff3c74039438..4eb1ed6e8f32c 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2067,6 +2067,17 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
return true;
}
+static bool IsOptimizedBuild(const CodeGenOptions &CGOpt) {
+ return CGOpt.OptimizationLevel > 0;
+}
+
+static bool ConvertBoolByCmpZero(const CodeGenOptions &CGOpt) {
+ if (CGOpt.getLoadBoolFromMem() == CodeGenOptions::BoolFromMem::NonZero)
+ return true;
+
+ return false;
+}
+
llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
llvm::APInt Min, End;
bool IsBool = Ty->hasBooleanRepresentation() && !Ty->isVectorType();
@@ -2086,7 +2097,7 @@ void CodeGenFunction::maybeAttachRangeForLoad(llvm::LoadInst *Load, QualType Ty,
if (EmitScalarRangeCheck(Load, Ty, Loc)) {
// In order to prevent the optimizer from throwing away the check, don't
// attach range metadata to the load.
- } else if (CGM.getCodeGenOpts().OptimizationLevel > 0) {
+ } else if (IsOptimizedBuild(CGM.getCodeGenOpts())) {
if (llvm::MDNode *RangeInfo = getRangeForLoadFromType(Ty)) {
Load->setMetadata(llvm::LLVMContext::MD_range, RangeInfo);
Load->setMetadata(llvm::LLVMContext::MD_noundef,
@@ -2271,13 +2282,12 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
}
llvm::Type *ResTy = ConvertType(Ty);
- bool HasBoolRep = Ty->hasBooleanRepresentation();
- if (HasBoolRep && CGM.getCodeGenOpts().getLoadBoolFromMem() ==
- CodeGenOptions::BoolFromMem::NonZero) {
+ bool HasBoolRep = Ty->hasBooleanRepresentation() || Ty->isExtVectorBoolType();
+ if (HasBoolRep && ConvertBoolByCmpZero(CGM.getCodeGenOpts())) {
return Builder.CreateICmpNE(
Value, llvm::Constant::getNullValue(Value->getType()), "loadedv");
}
- if (HasBoolRep || Ty->isBitIntType() || Ty->isExtVectorBoolType())
+ if (HasBoolRep || Ty->isBitIntType())
return Builder.CreateTrunc(Value, ResTy, "loadedv");
return Value;
@@ -2472,7 +2482,7 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
if (LV.isMatrixElt()) {
llvm::Value *Idx = LV.getMatrixIdx();
- if (CGM.getCodeGenOpts().OptimizationLevel > 0) {
+ if (IsOptimizedBuild(CGM.getCodeGenOpts())) {
const auto *const MatTy = LV.getType()->castAs<ConstantMatrixType>();
llvm::MatrixBuilder MB(Builder);
MB.CreateIndexAssumption(Idx, MatTy->getNumElementsFlattened());
@@ -2690,7 +2700,7 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
if (Dst.isMatrixElt()) {
llvm::Value *Idx = Dst.getMatrixIdx();
- if (CGM.getCodeGenOpts().OptimizationLevel > 0) {
+ if (IsOptimizedBuild(CGM.getCodeGenOpts())) {
const auto *const MatTy = Dst.getType()->castAs<ConstantMatrixType>();
llvm::MatrixBuilder MB(Builder);
MB.CreateIndexAssumption(Idx, MatTy->getNumElementsFlattened());
@@ -3920,7 +3930,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
llvm::AttributeList::FunctionIndex, B),
/*Local=*/true);
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
- NoMerge = NoMerge || !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
+ NoMerge = NoMerge || !IsOptimizedBuild(CGF.CGM.getCodeGenOpts()) ||
(CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
@@ -4312,7 +4322,7 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
TrapLocation, TrapCategory, TrapMessage);
}
- NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel ||
+ NoMerge = NoMerge || !IsOptimizedBuild(CGM.getCodeGenOpts()) ||
(CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
llvm::MDBuilder MDHelper(getLLVMContext());
diff --git a/clang/test/CodeGen/strict-bool.c b/clang/test/CodeGen/strict-bool.c
index b428a81a5cb13..b2a0f36b39ae1 100644
--- a/clang/test/CodeGen/strict-bool.c
+++ b/clang/test/CodeGen/strict-bool.c
@@ -6,12 +6,15 @@
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=strict -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-UBSAN-STRICT
// RUN: %clang_cc1 -triple armv7-apple-darwin -O1 -fsanitize=bool -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,CHECK-UBSAN-TRUNCATE
+typedef _Bool bool4_t __attribute__((ext_vector_type(4)));
+
struct has_bool {
_Bool b;
unsigned _BitInt(1) c;
+ bool4_t v;
};
-// CHECK: @foo
+// CHECK-LABEL: @foo
int foo(struct has_bool *b) {
// CHECK-STRICT: [[BOOL:%.+]] = load i8, ptr {{.+}}, !range ![[RANGE_BOOL:[0-9]+]]
// CHECK-STRICT-NOT: and i8 [[BOOL]], 1
@@ -35,7 +38,7 @@ int foo(struct has_bool *b) {
return b->b;
}
-// CHECK: @bar
+// CHECK-LABEL: @bar
int bar(struct has_bool *c) {
// CHECK-STRICT: [[BITINT:%.+]] = load i8, ptr {{.+}}, !range ![[RANGE_BOOL:[0-9]+]]
// CHECK-STRICT-NOT: and i8 [[BITINT]], 1
@@ -59,4 +62,11 @@ int bar(struct has_bool *c) {
return c->c;
}
+// CHECK-LABEL: @vec
+bool4_t vec(struct has_bool *c) {
+ // CHECK: [[BITS:%.+]] = load <{{[0-9]+}} x i1>
+ return c->v;
+}
+
+
// CHECK_STRICT: ![[RANGE_BOOL]] = !{i8 0, i8 2}
diff --git a/clang/test/CodeGenHLSL/strict-bool.hlsl b/clang/test/CodeGenHLSL/strict-bool.hlsl
new file mode 100644
index 0000000000000..deccf6ccf0786
--- /dev/null
+++ b/clang/test/CodeGenHLSL/strict-bool.hlsl
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -O1 -load-bool-from-mem=truncate -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK-TRUNCATE
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -O1 -load-bool-from-mem=nonzero -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK-NONZERO
+
+typedef bool bool8_t __attribute__((ext_vector_type(8)));
+extern bool8_t vec;
+
+bool8_t getvec(void) {
+ // CHECK-TRUNCATE: [[BOOL:%.+]] = load <8 x i32>
+ // CHECK-TRUNCATE: trunc <8 x i32> [[BOOL]] to <8 x i1>
+
+ // CHECK-NONZERO: [[BOOL:%.+]] = load <8 x i32>
+ // CHECK-NONZERO: icmp ne <8 x i32> [[BOOL]], zeroinitializer
+ return vec;
+}
>From 91aeaad6de6a9d963cc503fff3b146ec1c660b0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Thu, 18 Dec 2025 15:01:15 +0000
Subject: [PATCH 7/7] Trace down HLSL and ObjC cases loading booleans (without
range metadata) that would not check the -fno-strict-bool mode
---
clang/include/clang/Basic/CodeGenOptions.h | 18 ++
clang/lib/CodeGen/CGExpr.cpp | 39 ++--
clang/lib/CodeGen/CGObjC.cpp | 5 +-
.../CodeGenHLSL/builtins/BoolSwizzles.hlsl | 176 ++++++++++++++++++
.../CodeGenHLSL/builtins/ScalarSwizzles.hlsl | 160 ----------------
5 files changed, 216 insertions(+), 182 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/builtins/BoolSwizzles.hlsl
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 4d39048e7c803..3dab0f9585c66 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -670,6 +670,24 @@ class CodeGenOptions : public CodeGenOptionsBase {
bool isLoaderReplaceableFunctionName(StringRef FuncName) const {
return llvm::is_contained(LoaderReplaceableFunctionNames, FuncName);
}
+
+ /// Are we building at -O1 or higher?
+ bool isOptimizedBuild() const {
+ return OptimizationLevel > 0;
+ }
+
+ /// When loading a bool from a storage unit larger than i1, should it
+ /// be converted to i1 by comparing to 0 or by truncating to i1?
+ bool isConvertingBoolWithCmp0() const {
+ switch (getLoadBoolFromMem()) {
+ case BoolFromMem::Strict:
+ case BoolFromMem::Truncate:
+ return false;
+
+ case BoolFromMem::NonZero:
+ return true;
+ }
+ }
};
} // end namespace clang
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 4eb1ed6e8f32c..7c63a56126dcc 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2067,17 +2067,6 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty, llvm::APInt &Min,
return true;
}
-static bool IsOptimizedBuild(const CodeGenOptions &CGOpt) {
- return CGOpt.OptimizationLevel > 0;
-}
-
-static bool ConvertBoolByCmpZero(const CodeGenOptions &CGOpt) {
- if (CGOpt.getLoadBoolFromMem() == CodeGenOptions::BoolFromMem::NonZero)
- return true;
-
- return false;
-}
-
llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
llvm::APInt Min, End;
bool IsBool = Ty->hasBooleanRepresentation() && !Ty->isVectorType();
@@ -2097,7 +2086,7 @@ void CodeGenFunction::maybeAttachRangeForLoad(llvm::LoadInst *Load, QualType Ty,
if (EmitScalarRangeCheck(Load, Ty, Loc)) {
// In order to prevent the optimizer from throwing away the check, don't
// attach range metadata to the load.
- } else if (IsOptimizedBuild(CGM.getCodeGenOpts())) {
+ } else if (CGM.getCodeGenOpts().isOptimizedBuild()) {
if (llvm::MDNode *RangeInfo = getRangeForLoadFromType(Ty)) {
Load->setMetadata(llvm::LLVMContext::MD_range, RangeInfo);
Load->setMetadata(llvm::LLVMContext::MD_noundef,
@@ -2283,7 +2272,7 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
llvm::Type *ResTy = ConvertType(Ty);
bool HasBoolRep = Ty->hasBooleanRepresentation() || Ty->isExtVectorBoolType();
- if (HasBoolRep && ConvertBoolByCmpZero(CGM.getCodeGenOpts())) {
+ if (HasBoolRep && CGM.getCodeGenOpts().isConvertingBoolWithCmp0()) {
return Builder.CreateICmpNE(
Value, llvm::Constant::getNullValue(Value->getType()), "loadedv");
}
@@ -2482,7 +2471,7 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
if (LV.isMatrixElt()) {
llvm::Value *Idx = LV.getMatrixIdx();
- if (IsOptimizedBuild(CGM.getCodeGenOpts())) {
+ if (CGM.getCodeGenOpts().isOptimizedBuild()) {
const auto *const MatTy = LV.getType()->castAs<ConstantMatrixType>();
llvm::MatrixBuilder MB(Builder);
MB.CreateIndexAssumption(Idx, MatTy->getNumElementsFlattened());
@@ -2558,8 +2547,12 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) {
llvm::Type *LVTy = ConvertType(LV.getType());
if (Element->getType()->getPrimitiveSizeInBits() >
- LVTy->getPrimitiveSizeInBits())
- Element = Builder.CreateTrunc(Element, LVTy);
+ LVTy->getPrimitiveSizeInBits()) {
+ if (LV.getType()->hasBooleanRepresentation() && CGM.getCodeGenOpts().isConvertingBoolWithCmp0())
+ Element = Builder.CreateICmpNE(Element, llvm::Constant::getNullValue(Element->getType()));
+ else
+ Element = Builder.CreateTrunc(Element, LVTy);
+ }
return RValue::get(Element);
}
@@ -2573,8 +2566,12 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) {
Vec = Builder.CreateShuffleVector(Vec, Mask);
- if (LV.getType()->isExtVectorBoolType())
- Vec = Builder.CreateTrunc(Vec, ConvertType(LV.getType()), "truncv");
+ if (LV.getType()->isExtVectorBoolType()) {
+ if (CGM.getCodeGenOpts().isConvertingBoolWithCmp0())
+ Vec = Builder.CreateICmpNE(Vec, llvm::Constant::getNullValue(Vec->getType()));
+ else
+ Vec = Builder.CreateTrunc(Vec, ConvertType(LV.getType()), "truncv");
+ }
return RValue::get(Vec);
}
@@ -2700,7 +2697,7 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
if (Dst.isMatrixElt()) {
llvm::Value *Idx = Dst.getMatrixIdx();
- if (IsOptimizedBuild(CGM.getCodeGenOpts())) {
+ if (CGM.getCodeGenOpts().isOptimizedBuild()) {
const auto *const MatTy = Dst.getType()->castAs<ConstantMatrixType>();
llvm::MatrixBuilder MB(Builder);
MB.CreateIndexAssumption(Idx, MatTy->getNumElementsFlattened());
@@ -3930,7 +3927,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
llvm::AttributeList::FunctionIndex, B),
/*Local=*/true);
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
- NoMerge = NoMerge || !IsOptimizedBuild(CGF.CGM.getCodeGenOpts()) ||
+ NoMerge = NoMerge || !CGF.CGM.getCodeGenOpts().isOptimizedBuild() ||
(CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
@@ -4322,7 +4319,7 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
TrapLocation, TrapCategory, TrapMessage);
}
- NoMerge = NoMerge || !IsOptimizedBuild(CGM.getCodeGenOpts()) ||
+ NoMerge = NoMerge || !CGM.getCodeGenOpts().isOptimizedBuild() ||
(CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
llvm::MDBuilder MDHelper(getLLVMContext());
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
index 10aad2e26938d..a6decad4aa9c7 100644
--- a/clang/lib/CodeGen/CGObjC.cpp
+++ b/clang/lib/CodeGen/CGObjC.cpp
@@ -1209,7 +1209,10 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
uint64_t retTySize = CGM.getDataLayout().getTypeSizeInBits(retTy);
if (ivarSize > retTySize) {
bitcastType = llvm::Type::getIntNTy(getLLVMContext(), retTySize);
- ivarVal = Builder.CreateTrunc(ivarVal, bitcastType);
+ if (getterMethod->getReturnType()->hasBooleanRepresentation() && CGM.getCodeGenOpts().isConvertingBoolWithCmp0())
+ ivarVal = Builder.CreateICmpNE(ivarVal, llvm::Constant::getNullValue(ivarVal->getType()));
+ else
+ ivarVal = Builder.CreateTrunc(ivarVal, bitcastType);
}
Builder.CreateStore(ivarVal, ReturnValue.withElementType(bitcastType));
diff --git a/clang/test/CodeGenHLSL/builtins/BoolSwizzles.hlsl b/clang/test/CodeGenHLSL/builtins/BoolSwizzles.hlsl
new file mode 100644
index 0000000000000..9cdad86ee587c
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/BoolSwizzles.hlsl
@@ -0,0 +1,176 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -load-bool-from-mem=truncate -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck -check-prefixes=CHECK,CHECK-TR %s
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -load-bool-from-mem=nonzero -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck -check-prefixes=CHECK,CHECK-NZ %s
+
+// CHECK-LABEL: ToFourBools
+// CHECK: {{%.*}} = zext i1 {{.*}} to i32
+// CHECK: [[splat:%.*]] = insertelement <1 x i32> poison, i32 {{.*}}, i64 0
+// CHECK-NEXT: [[vec4:%.*]] = shufflevector <1 x i32> [[splat]], <1 x i32> poison, <4 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[vec2Ret:%.*]] = icmp ne <4 x i32> [[vec4]], zeroinitializer
+// CHECK-TR-NEXT: [[vec2Ret:%.*]] = trunc <4 x i32> [[vec4]] to <4 x i1>
+// CHECK-NEXT: ret <4 x i1> [[vec2Ret]]
+bool4 ToFourBools(bool V) {
+ return V.rrrr;
+}
+
+// CHECK-LABEL: FillTrue
+// CHECK: [[Tmp:%.*]] = alloca <1 x i32>, align 4
+// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4
+// CHECK-NEXT: [[Vec1:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
+// CHECK-NEXT: [[Vec2:%.*]] = shufflevector <1 x i32> [[Vec1]], <1 x i32> poison, <2 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[Vec2Ret:%.*]] = icmp ne <2 x i32> [[Vec2]], zeroinitializer
+// CHECK-TR-NEXT: [[Vec2Ret:%.*]] = trunc <2 x i32> [[Vec2]] to <2 x i1>
+// CHECK-NEXT: ret <2 x i1> [[Vec2Ret]]
+bool2 FillTrue() {
+ return true.xx;
+}
+
+// CHECK-LABEL: HowManyBools
+// CHECK: [[VAddr:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[Vec2Ptr:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT: [[Tmp:%.*]] = zext i1 {{.*}} to i32
+// CHECK-NEXT: store i32 [[Tmp]], ptr [[VAddr]], align 4
+// CHECK-NEXT: [[VVal:%.*]] = load i32, ptr [[VAddr]], align 4
+// CHECK-NEXT: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[VVal]], i64 0
+// CHECK-NEXT: [[Vec2:%.*]] = shufflevector <1 x i32> [[Splat]], <1 x i32> poison, <2 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[Trunc:%.*]] = icmp ne <2 x i32> [[Vec2]], zeroinitializer
+// CHECK-TR-NEXT: [[Trunc:%.*]] = trunc <2 x i32> [[Vec2]] to <2 x i1>
+// CHECK-NEXT: [[Ext:%.*]] = zext <2 x i1> [[Trunc]] to <2 x i32>
+// CHECK-NEXT: store <2 x i32> [[Ext]], ptr [[Vec2Ptr]], align 8
+// CHECK-NEXT: [[V2:%.*]] = load <2 x i32>, ptr [[Vec2Ptr]], align 8
+// CHECK-NEXT: [[V3:%.*]] = shufflevector <2 x i32> [[V2]], <2 x i32> poison, <2 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[LV1:%.*]] = icmp ne <2 x i32> [[V3]], zeroinitializer
+// CHECK-TR-NEXT: [[LV1:%.*]] = trunc <2 x i32> [[V3]] to <2 x i1>
+// CHECK-NEXT: ret <2 x i1> [[LV1]]
+bool2 HowManyBools(bool V) {
+ return V.rr.rr;
+}
+
+// CHECK-LABEL: AssignBool
+// CHECK: [[VAddr:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[XAddr:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[Zext:%.*]] = zext i1 %V to i32
+// CHECK-NEXT: store i32 [[Zext]], ptr [[VAddr]], align 4
+// CHECK-NEXT: [[X:%.*]] = load i32, ptr [[VAddr]], align 4
+// CHECK-NEXT: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[X]], i64 0
+// CHECK-NEXT: [[Y:%.*]] = extractelement <1 x i32> [[Splat]], i32 0
+// CHECK-NZ-NEXT: [[Z:%.*]] = icmp ne i32 [[Y]], 0
+// CHECK-TR-NEXT: [[Z:%.*]] = trunc i32 [[Y]] to i1
+// CHECK-NEXT: [[A:%.*]] = zext i1 [[Z]] to i32
+// CHECK-NEXT: store i32 [[A]], ptr [[XAddr]], align 4
+// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[VAddr]], align 4
+// CHECK-NEXT: [[Splat2:%.*]] = insertelement <1 x i32> poison, i32 [[B]], i64 0
+// CHECK-NEXT: [[C:%.*]] = extractelement <1 x i32> [[Splat2]], i32 0
+// CHECK-NZ-NEXT: [[D:%.*]] = icmp ne i32 [[C]], 0
+// CHECK-TR-NEXT: [[D:%.*]] = trunc i32 [[C]] to i1
+// CHECK-NEXT: br i1 [[D]], label %lor.end, label %lor.rhs
+
+// CHECK: lor.rhs:
+// CHECK-NEXT: [[E:%.*]] = load i32, ptr [[VAddr]], align 4
+// CHECK-NEXT: [[Splat3:%.*]] = insertelement <1 x i32> poison, i32 [[E]], i64 0
+// CHECK-NEXT: [[F:%.*]] = extractelement <1 x i32> [[Splat3]], i32 0
+// CHECK-NZ-NEXT: [[G:%.*]] = icmp ne i32 [[F]], 0
+// CHECK-TR-NEXT: [[G:%.*]] = trunc i32 [[F]] to i1
+// CHECK-NEXT: br label %lor.end
+
+// CHECK: lor.end:
+// CHECK-NEXT: [[H:%.*]] = phi i1 [ true, %entry ], [ [[G]], %lor.rhs ]
+// CHECK-NEXT: [[J:%.*]] = zext i1 %9 to i32
+// CHECK-NEXT: store i32 [[J]], ptr [[XAddr]], align 4
+// CHECK-NEXT: [[I:%.*]] = load i32, ptr [[XAddr]], align 4
+// CHECK-NZ-NEXT: [[LoadV:%.*]] = icmp ne i32 [[I]], 0
+// CHECK-TR-NEXT: [[LoadV:%.*]] = trunc i32 [[I]] to i1
+// CHECK-NEXT: ret i1 [[LoadV]]
+bool AssignBool(bool V) {
+ bool X = V.x;
+ X.x = V.x || V.x;
+ return X;
+}
+
+// CHECK-LABEL: AssignBool2
+// CHECK: [[VAdddr:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[X:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4
+// CHECK-NEXT: [[SV:%.*]] = zext i1 %V to i32
+// CHECK-NEXT: store i32 [[SV]], ptr [[VAddr]], align 4
+// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4
+// CHECK-NEXT: [[Y:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
+// CHECK-NEXT: [[Z:%.*]] = shufflevector <1 x i32> [[Y]], <1 x i32> poison, <2 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[LV:%.*]] = icmp ne <2 x i32> [[Z]], zeroinitializer
+// CHECK-TR-NEXT: [[LV:%.*]] = trunc <2 x i32> [[Z]] to <2 x i1>
+// CHECK-NEXT: [[A:%.*]] = zext <2 x i1> [[LV]] to <2 x i32>
+// CHECK-NEXT: store <2 x i32> [[A]], ptr [[X]], align 8
+// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[VAddr]], align 4
+// CHECK-NZ-NEXT: [[LV1:%.*]] = icmp ne i32 [[B]], 0
+// CHECK-TR-NEXT: [[LV1:%.*]] = trunc i32 [[B]] to i1
+// CHECK-NEXT: [[D:%.*]] = zext i1 [[LV1]] to i32
+// CHECK-NEXT: [[C:%.*]] = getelementptr <2 x i32>, ptr [[X]], i32 0, i32 1
+// CHECK-NEXT: store i32 [[D]], ptr [[C]], align 4
+// CHECK-NEXT: ret void
+void AssignBool2(bool V) {
+ bool2 X = true.xx;
+ X.y = V;
+}
+
+// CHECK-LABEL: AssignBool3
+// CHECK: [[VAddr:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT: [[X:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT: [[Y:%.*]] = zext <2 x i1> %V to <2 x i32>
+// CHECK-NEXT: store <2 x i32> [[Y]], ptr [[VAddr]], align 8
+// CHECK-NEXT: store <2 x i32> splat (i32 1), ptr [[X]], align 8
+// CHECK-NEXT: [[Z:%.*]] = load <2 x i32>, ptr [[VAddr]], align 8
+// CHECK-NZ-NEXT: [[LV:%.*]] = icmp ne <2 x i32> [[Z]], zeroinitializer
+// CHECK-TR-NEXT: [[LV:%.*]] = trunc <2 x i32> [[Z]] to <2 x i1>
+// CHECK-NEXT: [[B:%.*]] = zext <2 x i1> [[LV]] to <2 x i32>
+// CHECK-NEXT: [[V1:%.*]] = extractelement <2 x i32> [[B]], i32 0
+// CHECK-NEXT: store i32 [[V1]], ptr [[X]], align 4
+// CHECK-NEXT: [[V2:%.*]] = extractelement <2 x i32> [[B]], i32 1
+// CHECK-NEXT: [[X2:%.*]] = getelementptr <2 x i32>, ptr [[X]], i32 0, i32 1
+// CHECK-NEXT: store i32 [[V2]], ptr [[X2]], align 4
+// CHECK-NEXT: ret void
+
+void AssignBool3(bool2 V) {
+ bool2 X = {true,true};
+ X.xy = V;
+}
+
+// CHECK-LABEL: AccessBools
+// CHECK: [[X:%.*]] = alloca <4 x i32>, align 16
+// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4
+// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4
+// CHECK-NEXT: [[Y:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
+// CHECK-NEXT: [[Z:%.*]] = shufflevector <1 x i32> [[Y]], <1 x i32> poison, <4 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[LV:%.*]] = icmp ne <4 x i32> [[Z]], zeroinitializer
+// CHECK-TR-NEXT: [[LV:%.*]] = trunc <4 x i32> [[Vec2]] to <4 x i1>
+// CHECK-NEXT: [[A:%.*]] = zext <4 x i1> [[LV]] to <4 x i32>
+// CHECK-NEXT: store <4 x i32> [[A]], ptr [[X]], align 16
+// CHECK-NEXT: [[B:%.*]] = load <4 x i32>, ptr [[X]], align 16
+// CHECK-NEXT: [[C:%.*]] = shufflevector <4 x i32> [[B]], <4 x i32> poison, <2 x i32> <i32 2, i32 3>
+// CHECK-NZ-NEXT: [[LV1:%.*]] = icmp ne <2 x i32> [[C]], zeroinitializer
+// CHECK-TR-NEXT: [[LV1:%.*]] = trunc <2 x i32> [[C]] to <2 x i1>
+// CHECK-NEXT: ret <2 x i1> [[LV1]]
+bool2 AccessBools() {
+ bool4 X = true.xxxx;
+ return X.zw;
+}
+
+// CHECK-LABEL: define hidden void {{.*}}BoolSizeMismatch{{.*}}
+// CHECK: [[B:%.*]] = alloca <4 x i32>, align 16
+// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4
+// CHECK-NEXT: store <4 x i32> splat (i32 1), ptr [[B]], align 16
+// CHECK-NEXT: store <1 x i32> zeroinitializer, ptr [[Tmp]], align 4
+// CHECK-NEXT: [[L0:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
+// CHECK-NEXT: [[L1:%.*]] = shufflevector <1 x i32> [[L0]], <1 x i32> poison, <3 x i32> zeroinitializer
+// CHECK-NZ-NEXT: [[TruncV:%.*]] = icmp ne <3 x i32> [[L1]], zeroinitializer
+// CHECK-TR-NEXT: [[TruncV:%.*]] = trunc <3 x i32> [[Vec2]] to <3 x i1>
+// CHECK-NEXT: [[L2:%.*]] = zext <3 x i1> [[TruncV]] to <3 x i32>
+// CHECK-NEXT: [[V1:%.*]] = extractelement <3 x i32> [[L2]], i32 0
+// CHECK-NEXT: store i32 [[V1]], ptr %B, align 4
+// CHECK-NEXT: [[V2:%.*]] = extractelement <3 x i32> [[L2]], i32 1
+// CHECK-NEXT: [[B2:%.*]] = getelementptr <4 x i32>, ptr %B, i32 0, i32 1
+// CHECK-NEXT: store i32 [[V2]], ptr [[B2]], align 4
+// CHECK-NEXT: [[V3:%.*]] = extractelement <3 x i32> [[L2]], i32 2
+// CHECK-NEXT: [[B3:%.*]] = getelementptr <4 x i32>, ptr %B, i32 0, i32 2
+void BoolSizeMismatch() {
+ bool4 B = {true,true,true,true};
+ B.xyz = false.xxx;
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl b/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
index 270598265c660..e7ef500fcafae 100644
--- a/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
@@ -18,16 +18,6 @@ float4 ToFourFloats(float V){
return V.rrrr;
}
-// CHECK-LABEL: ToFourBools
-// CHECK: {{%.*}} = zext i1 {{.*}} to i32
-// CHECK: [[splat:%.*]] = insertelement <1 x i32> poison, i32 {{.*}}, i64 0
-// CHECK-NEXT: [[vec4:%.*]] = shufflevector <1 x i32> [[splat]], <1 x i32> poison, <4 x i32> zeroinitializer
-// CHECK-NEXT: [[vec2Ret:%.*]] = trunc <4 x i32> [[vec4]] to <4 x i1>
-// CHECK-NEXT: ret <4 x i1> [[vec2Ret]]
-bool4 ToFourBools(bool V) {
- return V.rrrr;
-}
-
// CHECK-LABEL: FillOne
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x i32>, align 4
// CHECK: store <1 x i32> splat (i32 1), ptr [[vec1Ptr]], align 4
@@ -103,17 +93,6 @@ vector<float, 1> FillOneHalfFloat(){
return .5f.r;
}
-// CHECK-LABEL: FillTrue
-// CHECK: [[Tmp:%.*]] = alloca <1 x i32>, align 4
-// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4
-// CHECK-NEXT: [[Vec1:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
-// CHECK-NEXT: [[Vec2:%.*]] = shufflevector <1 x i32> [[Vec1]], <1 x i32> poison, <2 x i32> zeroinitializer
-// CHECK-NEXT: [[Vec2Ret:%.*]] = trunc <2 x i32> [[Vec2]] to <2 x i1>
-// CHECK-NEXT: ret <2 x i1> [[Vec2Ret]]
-bool2 FillTrue() {
- return true.xx;
-}
-
// The initial codegen for this case is correct but a bit odd. The IR optimizer
// cleans this up very nicely.
@@ -131,25 +110,6 @@ float2 HowManyFloats(float V) {
return V.rr.rr;
}
-// CHECK-LABEL: HowManyBools
-// CHECK: [[VAddr:%.*]] = alloca i32, align 4
-// CHECK-NEXT: [[Vec2Ptr:%.*]] = alloca <2 x i32>, align 8
-// CHECK-NEXT: [[Tmp:%.*]] = zext i1 {{.*}} to i32
-// CHECK-NEXT: store i32 [[Tmp]], ptr [[VAddr]], align 4
-// CHECK-NEXT: [[VVal:%.*]] = load i32, ptr [[VAddr]], align 4
-// CHECK-NEXT: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[VVal]], i64 0
-// CHECK-NEXT: [[Vec2:%.*]] = shufflevector <1 x i32> [[Splat]], <1 x i32> poison, <2 x i32> zeroinitializer
-// CHECK-NEXT: [[Trunc:%.*]] = trunc <2 x i32> [[Vec2]] to <2 x i1>
-// CHECK-NEXT: [[Ext:%.*]] = zext <2 x i1> [[Trunc]] to <2 x i32>
-// CHECK-NEXT: store <2 x i32> [[Ext]], ptr [[Vec2Ptr]], align 8
-// CHECK-NEXT: [[V2:%.*]] = load <2 x i32>, ptr [[Vec2Ptr]], align 8
-// CHECK-NEXT: [[V3:%.*]] = shufflevector <2 x i32> [[V2]], <2 x i32> poison, <2 x i32> zeroinitializer
-// CHECK-NEXT: [[LV1:%.*]] = trunc <2 x i32> [[V3]] to <2 x i1>
-// CHECK-NEXT: ret <2 x i1> [[LV1]]
-bool2 HowManyBools(bool V) {
- return V.rr.rr;
-}
-
// This codegen is gnarly because `1.l` is a double, so this creates double
// vectors that need to be truncated down to floats. The optimizer cleans this
// up nicely too.
@@ -206,123 +166,3 @@ int AssignInt(int V){
X.x = V.x + V.x;
return X;
}
-
-// CHECK-LABEL: AssignBool
-// CHECK: [[VAddr:%.*]] = alloca i32, align 4
-// CHECK-NEXT: [[XAddr:%.*]] = alloca i32, align 4
-// CHECK-NEXT: [[Zext:%.*]] = zext i1 %V to i32
-// CHECK-NEXT: store i32 [[Zext]], ptr [[VAddr]], align 4
-// CHECK-NEXT: [[X:%.*]] = load i32, ptr [[VAddr]], align 4
-// CHECK-NEXT: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[X]], i64 0
-// CHECK-NEXT: [[Y:%.*]] = extractelement <1 x i32> [[Splat]], i32 0
-// CHECK-NEXT: [[Z:%.*]] = trunc i32 [[Y]] to i1
-// CHECK-NEXT: [[A:%.*]] = zext i1 [[Z]] to i32
-// CHECK-NEXT: store i32 [[A]], ptr [[XAddr]], align 4
-// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[VAddr]], align 4
-// CHECK-NEXT: [[Splat2:%.*]] = insertelement <1 x i32> poison, i32 [[B]], i64 0
-// CHECK-NEXT: [[C:%.*]] = extractelement <1 x i32> [[Splat2]], i32 0
-// CHECK-NEXT: [[D:%.*]] = trunc i32 [[C]] to i1
-// CHECK-NEXT: br i1 [[D]], label %lor.end, label %lor.rhs
-
-// CHECK: lor.rhs:
-// CHECK-NEXT: [[E:%.*]] = load i32, ptr [[VAddr]], align 4
-// CHECK-NEXT: [[Splat3:%.*]] = insertelement <1 x i32> poison, i32 [[E]], i64 0
-// CHECK-NEXT: [[F:%.*]] = extractelement <1 x i32> [[Splat3]], i32 0
-// CHECK-NEXT: [[G:%.*]] = trunc i32 [[F]] to i1
-// CHECK-NEXT: br label %lor.end
-
-// CHECK: lor.end:
-// CHECK-NEXT: [[H:%.*]] = phi i1 [ true, %entry ], [ [[G]], %lor.rhs ]
-// CHECK-NEXT: [[J:%.*]] = zext i1 %9 to i32
-// CHECK-NEXT: store i32 [[J]], ptr [[XAddr]], align 4
-// CHECK-NEXT: [[I:%.*]] = load i32, ptr [[XAddr]], align 4
-// CHECK-NEXT: [[LoadV:%.*]] = trunc i32 [[I]] to i1
-// CHECK-NEXT: ret i1 [[LoadV]]
-bool AssignBool(bool V) {
- bool X = V.x;
- X.x = V.x || V.x;
- return X;
-}
-
-// CHECK-LABEL: AssignBool2
-// CHECK: [[VAdddr:%.*]] = alloca i32, align 4
-// CHECK-NEXT: [[X:%.*]] = alloca <2 x i32>, align 8
-// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4
-// CHECK-NEXT: [[SV:%.*]] = zext i1 %V to i32
-// CHECK-NEXT: store i32 [[SV]], ptr [[VAddr]], align 4
-// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4
-// CHECK-NEXT: [[Y:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
-// CHECK-NEXT: [[Z:%.*]] = shufflevector <1 x i32> [[Y]], <1 x i32> poison, <2 x i32> zeroinitializer
-// CHECK-NEXT: [[LV:%.*]] = trunc <2 x i32> [[Z]] to <2 x i1>
-// CHECK-NEXT: [[A:%.*]] = zext <2 x i1> [[LV]] to <2 x i32>
-// CHECK-NEXT: store <2 x i32> [[A]], ptr [[X]], align 8
-// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[VAddr]], align 4
-// CHECK-NEXT: [[LV1:%.*]] = trunc i32 [[B]] to i1
-// CHECK-NEXT: [[D:%.*]] = zext i1 [[LV1]] to i32
-// CHECK-NEXT: [[C:%.*]] = getelementptr <2 x i32>, ptr [[X]], i32 0, i32 1
-// CHECK-NEXT: store i32 [[D]], ptr [[C]], align 4
-// CHECK-NEXT: ret void
-void AssignBool2(bool V) {
- bool2 X = true.xx;
- X.y = V;
-}
-
-// CHECK-LABEL: AssignBool3
-// CHECK: [[VAddr:%.*]] = alloca <2 x i32>, align 8
-// CHECK-NEXT: [[X:%.*]] = alloca <2 x i32>, align 8
-// CHECK-NEXT: [[Y:%.*]] = zext <2 x i1> %V to <2 x i32>
-// CHECK-NEXT: store <2 x i32> [[Y]], ptr [[VAddr]], align 8
-// CHECK-NEXT: store <2 x i32> splat (i32 1), ptr [[X]], align 8
-// CHECK-NEXT: [[Z:%.*]] = load <2 x i32>, ptr [[VAddr]], align 8
-// CHECK-NEXT: [[LV:%.*]] = trunc <2 x i32> [[Z]] to <2 x i1>
-// CHECK-NEXT: [[B:%.*]] = zext <2 x i1> [[LV]] to <2 x i32>
-// CHECK-NEXT: [[V1:%.*]] = extractelement <2 x i32> [[B]], i32 0
-// CHECK-NEXT: store i32 [[V1]], ptr [[X]], align 4
-// CHECK-NEXT: [[V2:%.*]] = extractelement <2 x i32> [[B]], i32 1
-// CHECK-NEXT: [[X2:%.*]] = getelementptr <2 x i32>, ptr [[X]], i32 0, i32 1
-// CHECK-NEXT: store i32 [[V2]], ptr [[X2]], align 4
-// CHECK-NEXT: ret void
-
-void AssignBool3(bool2 V) {
- bool2 X = {true,true};
- X.xy = V;
-}
-
-// CHECK-LABEL: AccessBools
-// CHECK: [[X:%.*]] = alloca <4 x i32>, align 16
-// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4
-// CHECK-NEXT: store <1 x i32> splat (i32 1), ptr [[Tmp]], align 4
-// CHECK-NEXT: [[Y:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
-// CHECK-NEXT: [[Z:%.*]] = shufflevector <1 x i32> [[Y]], <1 x i32> poison, <4 x i32> zeroinitializer
-// CHECK-NEXT: [[LV:%.*]] = trunc <4 x i32> [[Z]] to <4 x i1>
-// CHECK-NEXT: [[A:%.*]] = zext <4 x i1> [[LV]] to <4 x i32>
-// CHECK-NEXT: store <4 x i32> [[A]], ptr [[X]], align 16
-// CHECK-NEXT: [[B:%.*]] = load <4 x i32>, ptr [[X]], align 16
-// CHECK-NEXT: [[C:%.*]] = shufflevector <4 x i32> [[B]], <4 x i32> poison, <2 x i32> <i32 2, i32 3>
-// CHECK-NEXT: [[LV1:%.*]] = trunc <2 x i32> [[C]] to <2 x i1>
-// CHECK-NEXT: ret <2 x i1> [[LV1]]
-bool2 AccessBools() {
- bool4 X = true.xxxx;
- return X.zw;
-}
-
-// CHECK-LABEL: define hidden void {{.*}}BoolSizeMismatch{{.*}}
-// CHECK: [[B:%.*]] = alloca <4 x i32>, align 16
-// CHECK-NEXT: [[Tmp:%.*]] = alloca <1 x i32>, align 4
-// CHECK-NEXT: store <4 x i32> splat (i32 1), ptr [[B]], align 16
-// CHECK-NEXT: store <1 x i32> zeroinitializer, ptr [[Tmp]], align 4
-// CHECK-NEXT: [[L0:%.*]] = load <1 x i32>, ptr [[Tmp]], align 4
-// CHECK-NEXT: [[L1:%.*]] = shufflevector <1 x i32> [[L0]], <1 x i32> poison, <3 x i32> zeroinitializer
-// CHECK-NEXT: [[TruncV:%.*]] = trunc <3 x i32> [[L1]] to <3 x i1>
-// CHECK-NEXT: [[L2:%.*]] = zext <3 x i1> [[TruncV]] to <3 x i32>
-// CHECK-NEXT: [[V1:%.*]] = extractelement <3 x i32> [[L2]], i32 0
-// CHECK-NEXT: store i32 [[V1]], ptr %B, align 4
-// CHECK-NEXT: [[V2:%.*]] = extractelement <3 x i32> [[L2]], i32 1
-// CHECK-NEXT: [[B2:%.*]] = getelementptr <4 x i32>, ptr %B, i32 0, i32 1
-// CHECK-NEXT: store i32 [[V2]], ptr [[B2]], align 4
-// CHECK-NEXT: [[V3:%.*]] = extractelement <3 x i32> [[L2]], i32 2
-// CHECK-NEXT: [[B3:%.*]] = getelementptr <4 x i32>, ptr %B, i32 0, i32 2
-void BoolSizeMismatch() {
- bool4 B = {true,true,true,true};
- B.xyz = false.xxx;
-}
More information about the cfe-commits
mailing list