From 8b30a9379f730875ba8fb2d0fe2d43611e0c20ff Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Thu, 26 Jan 2012 22:14:27 +0000 Subject: [PATCH] Make clz/ctz builtins defined for zero on ARM targets. rdar://10732455 ARM supports clz and ctz directly and both operations have well-defined results for zero. There is no disadvantage in performance to using the defined-at-zero versions of llvm.ctlz/cttz intrinsics. We're running into ARM-specific code written with the assumption that __builtin_clz(0) == 32, even though that value is technically undefined. The code is failing now because of llvm optimizations that are taking advantage of the undef behavior (specifically svn r147255). There's nothing wrong with that optimization on x86 where any incorrect assumptions about __builtin_clz(0) will quickly be exposed. For ARM, though, optimizations based on that undef behavior are likely to cause subtle bugs. Other targets with defined-at-zero clz/ctz support may want to override the default behavior as well. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149086 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/TargetInfo.h | 7 +++++++ lib/Basic/Targets.cpp | 1 + lib/CodeGen/CGBuiltin.cpp | 6 ++++-- test/CodeGen/builtin-count-zeros.c | 8 ++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 5e219262dd..b1e1cbdf66 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -357,6 +357,13 @@ public: virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const = 0; + /// isCLZForZeroUndef - The __builtin_clz* and __builtin_ctz* built-in + /// functions are specified to have undefined results for zero inputs, but + /// on targets that support these operations in a way that provides + /// well-defined results for zero without loss of performance, it is a good + /// idea to avoid optimizing based on that undef behavior. + virtual bool isCLZForZeroUndef() const { return true; } + /// getVAListDeclaration - Return the declaration to use for /// __builtin_va_list, which is target-specific. virtual const char *getVAListDeclaration() const = 0; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index f0e3bc4b74..a557142290 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -2778,6 +2778,7 @@ public: Records = BuiltinInfo; NumRecords = clang::ARM::LastTSBuiltin-Builtin::FirstTSBuiltin; } + virtual bool isCLZForZeroUndef() const { return false; } virtual const char *getVAListDeclaration() const { return "typedef void* __builtin_va_list;"; } diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index e63eeb3c76..f4a5a82802 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -238,7 +238,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Value *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType); llvm::Type *ResultType = ConvertType(E->getType()); - Value *Result = Builder.CreateCall2(F, ArgValue, Builder.getTrue()); + Value *ZeroUndef = Builder.getInt1(Target.isCLZForZeroUndef()); + Value *Result = Builder.CreateCall2(F, ArgValue, ZeroUndef); if (Result->getType() != ResultType) Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true, "cast"); @@ -253,7 +254,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Value *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType); llvm::Type *ResultType = ConvertType(E->getType()); - Value *Result = Builder.CreateCall2(F, ArgValue, Builder.getTrue()); + Value *ZeroUndef = Builder.getInt1(Target.isCLZForZeroUndef()); + Value *Result = Builder.CreateCall2(F, ArgValue, ZeroUndef); if (Result->getType() != ResultType) Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true, "cast"); diff --git a/test/CodeGen/builtin-count-zeros.c b/test/CodeGen/builtin-count-zeros.c index 5a0be2fb86..acd22e69a7 100644 --- a/test/CodeGen/builtin-count-zeros.c +++ b/test/CodeGen/builtin-count-zeros.c @@ -1,4 +1,8 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - | grep 'cttz' | count 2 -// RUN: %clang_cc1 -emit-llvm %s -o - | grep 'ctlz' | count 2 +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm-unknown-unknown -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-ARM int a(int a) {return __builtin_ctz(a) + __builtin_clz(a);} +// CHECK: call i32 @llvm.cttz.i32({{.*}}, i1 true) +// CHECK: call i32 @llvm.ctlz.i32({{.*}}, i1 true) +// CHECK-ARM: call i32 @llvm.cttz.i32({{.*}}, i1 false) +// CHECK-ARM: call i32 @llvm.ctlz.i32({{.*}}, i1 false) -- 2.40.0