[clang] [llvm] [Clang][InstCombine] Recognise div/ldiv/lldiv libcalls (PR #215868)
Magnus Strømme via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 12 22:04:35 PDT 2026
https://github.com/Magnushst updated https://github.com/llvm/llvm-project/pull/215868
>From 4288af13448f6b23ea2e379017a69e168384c06a Mon Sep 17 00:00:00 2001
From: Magnushst <magnus.henrik at hotmail.com>
Date: Thu, 13 Aug 2026 06:57:03 +0200
Subject: [PATCH 1/2] [InstCombine] Add baseline tests for div-family libcalls
---
clang/test/CodeGen/div-builtins.c | 50 ++++++++++
.../Transforms/InstCombine/div-libcalls.ll | 99 +++++++++++++++++++
2 files changed, 149 insertions(+)
create mode 100644 clang/test/CodeGen/div-builtins.c
create mode 100644 llvm/test/Transforms/InstCombine/div-libcalls.ll
diff --git a/clang/test/CodeGen/div-builtins.c b/clang/test/CodeGen/div-builtins.c
new file mode 100644
index 0000000000000..b82db6467bef0
--- /dev/null
+++ b/clang/test/CodeGen/div-builtins.c
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -emit-llvm -o - %s | FileCheck --check-prefix=OPT %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -emit-llvm \
+// RUN: -fno-builtin-div -fno-builtin-ldiv -fno-builtin-lldiv -o - %s | \
+// RUN: FileCheck --check-prefix=DISABLED %s
+
+typedef struct {
+ int quot;
+ int rem;
+} div_t;
+
+typedef struct {
+ long quot;
+ long rem;
+} ldiv_t;
+
+typedef struct {
+ long long quot;
+ long long rem;
+} lldiv_t;
+
+div_t div(int, int);
+ldiv_t ldiv(long, long);
+lldiv_t lldiv(long long, long long);
+
+div_t test_div(int numer, int denom) {
+ return div(numer, denom);
+}
+
+// OPT-LABEL: define{{.*}} i64 @test_div(
+// OPT: call i64 @div(
+// DISABLED-LABEL: define{{.*}} i64 @test_div(
+// DISABLED: call i64 @div(
+
+ldiv_t test_ldiv(long numer, long denom) {
+ return ldiv(numer, denom);
+}
+
+// OPT-LABEL: define{{.*}} { i64, i64 } @test_ldiv(
+// OPT: call { i64, i64 } @ldiv(
+// DISABLED-LABEL: define{{.*}} { i64, i64 } @test_ldiv(
+// DISABLED: call { i64, i64 } @ldiv(
+
+lldiv_t test_lldiv(long long numer, long long denom) {
+ return lldiv(numer, denom);
+}
+
+// OPT-LABEL: define{{.*}} { i64, i64 } @test_lldiv(
+// OPT: call { i64, i64 } @lldiv(
+// DISABLED-LABEL: define{{.*}} { i64, i64 } @test_lldiv(
+// DISABLED: call { i64, i64 } @lldiv(
diff --git a/llvm/test/Transforms/InstCombine/div-libcalls.ll b/llvm/test/Transforms/InstCombine/div-libcalls.ll
new file mode 100644
index 0000000000000..7b946ea58c22b
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-libcalls.ll
@@ -0,0 +1,99 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=instcombine -data-layout=e-p:64:64 %s | FileCheck %s --check-prefixes=CHECK,LITTLE
+; RUN: opt -S -passes=instcombine -data-layout=E-p:64:64 %s | FileCheck %s --check-prefixes=CHECK,BIG
+
+%div_sret = type { i32, i32 }
+
+; The LLVM mangling escape lets one module exercise incompatible ABI forms
+; that all resolve to the same libcall name.
+declare i64 @div(i32, i32)
+declare { i64 } @"\01div"(i32, i32)
+declare { i64, i64 } @ldiv(i64, i64)
+declare void @"\01ldiv"(ptr sret(%div_sret), i32, i32)
+declare [2 x i64] @lldiv(i64, i64)
+declare { i32, i64 } @"\01lldiv"(i64, i64)
+
+define i64 @div_packed(i32 %numer, i32 %denom) {
+; CHECK-LABEL: define i64 @div_packed(
+; CHECK-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[RESULT:%.*]] = call i64 @div(i32 [[NUMER]], i32 [[DENOM]])
+; CHECK-NEXT: ret i64 [[RESULT]]
+;
+ %result = call i64 @div(i32 %numer, i32 %denom)
+ ret i64 %result
+}
+
+define { i64 } @div_packed_wrapper(i32 %numer, i32 %denom) {
+; CHECK-LABEL: define { i64 } @div_packed_wrapper(
+; CHECK-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[RESULT:%.*]] = call { i64 } @"\01div"(i32 [[NUMER]], i32 [[DENOM]])
+; CHECK-NEXT: ret { i64 } [[RESULT]]
+;
+ %result = call { i64 } @"\01div"(i32 %numer, i32 %denom)
+ ret { i64 } %result
+}
+
+define { i64, i64 } @ldiv_struct(i64 %numer, i64 %denom) {
+; CHECK-LABEL: define { i64, i64 } @ldiv_struct(
+; CHECK-SAME: i64 [[NUMER:%.*]], i64 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[DIV_REM_INSERT:%.*]] = call { i64, i64 } @ldiv(i64 [[NUMER]], i64 [[DENOM]])
+; CHECK-NEXT: ret { i64, i64 } [[DIV_REM_INSERT]]
+;
+ %result = call { i64, i64 } @ldiv(i64 %numer, i64 %denom)
+ ret { i64, i64 } %result
+}
+
+define [2 x i64] @lldiv_array(i64 %numer, i64 %denom) {
+; CHECK-LABEL: define [2 x i64] @lldiv_array(
+; CHECK-SAME: i64 [[NUMER:%.*]], i64 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[DIV_REM_INSERT:%.*]] = call [2 x i64] @lldiv(i64 [[NUMER]], i64 [[DENOM]])
+; CHECK-NEXT: ret [2 x i64] [[DIV_REM_INSERT]]
+;
+ %result = call [2 x i64] @lldiv(i64 %numer, i64 %denom)
+ ret [2 x i64] %result
+}
+
+define void @ldiv_sret(ptr %result, i32 %numer, i32 %denom) {
+; CHECK-LABEL: define void @ldiv_sret(
+; CHECK-SAME: ptr [[RESULT:%.*]], i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; CHECK-NEXT: call void @"\01ldiv"(ptr sret([[DIV_SRET:%.*]]) [[RESULT]], i32 [[NUMER]], i32 [[DENOM]])
+; CHECK-NEXT: ret void
+;
+ call void @"\01ldiv"(ptr sret(%div_sret) %result, i32 %numer, i32 %denom)
+ ret void
+}
+
+define { i32, i64 } @invalid_lldiv_return(i64 %numer, i64 %denom) {
+; CHECK-LABEL: define { i32, i64 } @invalid_lldiv_return(
+; CHECK-SAME: i64 [[NUMER:%.*]], i64 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[RESULT:%.*]] = call { i32, i64 } @"\01lldiv"(i64 [[NUMER]], i64 [[DENOM]])
+; CHECK-NEXT: ret { i32, i64 } [[RESULT]]
+;
+ %result = call { i32, i64 } @"\01lldiv"(i64 %numer, i64 %denom)
+ ret { i32, i64 } %result
+}
+
+define i32 @invalid_call_type(i32 %numer, i32 %denom) {
+; CHECK-LABEL: define i32 @invalid_call_type(
+; CHECK-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[RESULT:%.*]] = call i32 @div(i32 [[NUMER]], i32 [[DENOM]])
+; CHECK-NEXT: ret i32 [[RESULT]]
+;
+ %result = call i32 @div(i32 %numer, i32 %denom)
+ ret i32 %result
+}
+
+define i64 @no_builtin(i32 %numer, i32 %denom) {
+; CHECK-LABEL: define i64 @no_builtin(
+; CHECK-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; CHECK-NEXT: [[RESULT:%.*]] = call i64 @div(i32 [[NUMER]], i32 [[DENOM]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT: ret i64 [[RESULT]]
+;
+ %result = call i64 @div(i32 %numer, i32 %denom) #0
+ ret i64 %result
+}
+
+attributes #0 = { nobuiltin }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; BIG: {{.*}}
+; LITTLE: {{.*}}
>From 871425710ec98acf6f89ca90fd7a5531804e7824 Mon Sep 17 00:00:00 2001
From: Magnushst <magnus.henrik at hotmail.com>
Date: Thu, 13 Aug 2026 06:57:52 +0200
Subject: [PATCH 2/2] [Clang][InstCombine] Recognise div/ldiv/lldiv libcalls
---
clang/lib/Frontend/CompilerInvocation.cpp | 8 +-
clang/test/CodeGen/div-builtins.c | 12 ++-
.../llvm/Analysis/TargetLibraryInfo.td | 9 ++
.../llvm/Transforms/Utils/SimplifyLibCalls.h | 1 +
llvm/lib/Analysis/TargetLibraryInfo.cpp | 84 ++++++++++++++++++-
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 64 ++++++++++++++
.../Transforms/InstCombine/div-libcalls.ll | 67 +++++++++++----
.../tools/llvm-tli-checker/ps4-tli-check.yaml | 11 ++-
.../Analysis/TargetLibraryInfoTest.cpp | 3 +
9 files changed, 236 insertions(+), 23 deletions(-)
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 56b39dc672aa6..652a58a268260 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -923,7 +923,13 @@ static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts,
static void getAllNoBuiltinFuncValues(ArgList &Args,
std::vector<std::string> &Funcs) {
std::vector<std::string> Values = Args.getAllArgValues(OPT_fno_builtin_);
- auto BuiltinEnd = llvm::partition(Values, Builtin::Context::isBuiltinFunc);
+ auto IsBuiltinFunc = [](StringRef Name) {
+ // These libcalls have target-specific aggregate return types that cannot be
+ // represented by Clang's builtin type encoding.
+ return Builtin::Context::isBuiltinFunc(Name) || Name == "div" ||
+ Name == "ldiv" || Name == "lldiv";
+ };
+ auto BuiltinEnd = llvm::partition(Values, IsBuiltinFunc);
Funcs.insert(Funcs.end(), Values.begin(), BuiltinEnd);
}
diff --git a/clang/test/CodeGen/div-builtins.c b/clang/test/CodeGen/div-builtins.c
index b82db6467bef0..6b7f12e46b05f 100644
--- a/clang/test/CodeGen/div-builtins.c
+++ b/clang/test/CodeGen/div-builtins.c
@@ -27,7 +27,9 @@ div_t test_div(int numer, int denom) {
}
// OPT-LABEL: define{{.*}} i64 @test_div(
-// OPT: call i64 @div(
+// OPT: sdiv i32
+// OPT: srem i32
+// OPT-NOT: call
// DISABLED-LABEL: define{{.*}} i64 @test_div(
// DISABLED: call i64 @div(
@@ -36,7 +38,9 @@ ldiv_t test_ldiv(long numer, long denom) {
}
// OPT-LABEL: define{{.*}} { i64, i64 } @test_ldiv(
-// OPT: call { i64, i64 } @ldiv(
+// OPT: sdiv i64
+// OPT: srem i64
+// OPT-NOT: call
// DISABLED-LABEL: define{{.*}} { i64, i64 } @test_ldiv(
// DISABLED: call { i64, i64 } @ldiv(
@@ -45,6 +49,8 @@ lldiv_t test_lldiv(long long numer, long long denom) {
}
// OPT-LABEL: define{{.*}} { i64, i64 } @test_lldiv(
-// OPT: call { i64, i64 } @lldiv(
+// OPT: sdiv i64
+// OPT: srem i64
+// OPT-NOT: call
// DISABLED-LABEL: define{{.*}} { i64, i64 } @test_lldiv(
// DISABLED: call { i64, i64 } @lldiv(
diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.td b/llvm/include/llvm/Analysis/TargetLibraryInfo.td
index 6b17e70b87f55..fd13ca95eabb5 100644
--- a/llvm/include/llvm/Analysis/TargetLibraryInfo.td
+++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.td
@@ -726,6 +726,9 @@ def cosl : TargetLibCall<"cosl", LDbl, [LDbl]>;
/// char *ctermid(char *s);
def ctermid : TargetLibCall<"ctermid", Ptr, [Ptr]>;
+/// div_t div(int numer, int denom);
+def div : TargetLibCall<"div", ? /* Checked manually. */>;
+
/// double erf(double x);
def erf : TargetLibCall<"erf", Dbl, [Dbl]>;
@@ -1092,9 +1095,15 @@ def ldexpf : TargetLibCall<"ldexpf", Flt, [Flt, Int]>;
/// long double ldexpl(long double x, int n);
def ldexpl : TargetLibCall<"ldexpl", LDbl, [LDbl, Int]>;
+/// ldiv_t ldiv(long int numer, long int denom);
+def ldiv : TargetLibCall<"ldiv", ? /* Checked manually. */>;
+
/// long long int llabs(long long int j);
def llabs : TargetLibCall<"llabs", LLong, [LLong]>;
+/// lldiv_t lldiv(long long int numer, long long int denom);
+def lldiv : TargetLibCall<"lldiv", ? /* Checked manually. */>;
+
/// double log(double x);
def log : TargetLibCall<"log", Dbl, [Dbl]>;
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 5bdac325b5659..0a306f12da34d 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -222,6 +222,7 @@ class LibCallSimplifier {
Value *optimizeFFS(CallInst *CI, IRBuilderBase &B);
Value *optimizeFls(CallInst *CI, IRBuilderBase &B);
Value *optimizeAbs(CallInst *CI, IRBuilderBase &B);
+ Value *optimizeDiv(CallInst *CI, IRBuilderBase &B);
Value *optimizeIsDigit(CallInst *CI, IRBuilderBase &B);
Value *optimizeIsAscii(CallInst *CI, IRBuilderBase &B);
Value *optimizeToAscii(CallInst *CI, IRBuilderBase &B);
diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp
index 763c04b9b06f7..78f0f38f33502 100644
--- a/llvm/lib/Analysis/TargetLibraryInfo.cpp
+++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp
@@ -1079,12 +1079,82 @@ static bool isValidProtoForSizeReturningNew(const FunctionType &FTy, LibFunc F,
return FTy.getReturnType() == SizedPtrTy;
}
+static bool isValidDivAggregateType(Type *Ty, Type *ElementTy) {
+ if (auto *StructTy = dyn_cast<StructType>(Ty))
+ return StructTy->getNumElements() == 2 &&
+ StructTy->getElementType(0) == ElementTy &&
+ StructTy->getElementType(1) == ElementTy;
+
+ if (auto *ArrayTy = dyn_cast<ArrayType>(Ty))
+ return ArrayTy->getNumElements() == 2 &&
+ ArrayTy->getElementType() == ElementTy;
+
+ return false;
+}
+
+static bool isValidDivReturnType(Type *Ty, Type *ElementTy) {
+ if (auto *IntegerTy = dyn_cast<IntegerType>(Ty))
+ return IntegerTy->getBitWidth() == 2 * ElementTy->getIntegerBitWidth();
+
+ if (auto *StructTy = dyn_cast<StructType>(Ty))
+ if (StructTy->getNumElements() == 1)
+ if (auto *IntegerTy = dyn_cast<IntegerType>(StructTy->getElementType(0)))
+ return IntegerTy->getBitWidth() == 2 * ElementTy->getIntegerBitWidth();
+
+ return isValidDivAggregateType(Ty, ElementTy);
+}
+
+static bool isValidProtoForDiv(const FunctionType &FTy, LibFunc F,
+ unsigned IntBits) {
+ if (FTy.isFunctionVarArg())
+ return false;
+
+ unsigned ArgOffset = 0;
+ if (FTy.getReturnType()->isVoidTy()) {
+ if (FTy.getNumParams() != 3 || !FTy.getParamType(0)->isPointerTy())
+ return false;
+ ArgOffset = 1;
+ } else if (FTy.getNumParams() != 2) {
+ return false;
+ }
+
+ auto *ElementTy = dyn_cast<IntegerType>(FTy.getParamType(ArgOffset));
+ if (!ElementTy || FTy.getParamType(ArgOffset + 1) != ElementTy)
+ return false;
+
+ FuncArgTypeID ElementTypeID;
+ switch (F) {
+ case LibFunc_div:
+ ElementTypeID = Int;
+ break;
+ case LibFunc_ldiv:
+ ElementTypeID = Long;
+ break;
+ case LibFunc_lldiv:
+ ElementTypeID = LLong;
+ break;
+ default:
+ llvm_unreachable("not a div-family libcall");
+ }
+ if (!matchType(ElementTypeID, ElementTy, IntBits, /*SizeTBits=*/0))
+ return false;
+
+ Type *ReturnTy = FTy.getReturnType();
+ if (ReturnTy->isVoidTy())
+ return true;
+ return isValidDivReturnType(ReturnTy, ElementTy);
+}
+
bool TargetLibraryInfoImpl::isValidProtoForLibFunc(const FunctionType &FTy,
LibFunc F,
const Module &M) const {
unsigned NumParams = FTy.getNumParams();
switch (F) {
+ case LibFunc_div:
+ case LibFunc_ldiv:
+ case LibFunc_lldiv:
+ return isValidProtoForDiv(FTy, F, getIntSize());
// Special handling for <complex.h> functions:
case LibFunc_cabs:
case LibFunc_cabsf:
@@ -1206,7 +1276,19 @@ bool TargetLibraryInfoImpl::getLibFunc(const Function &FDecl,
return false;
F = FDecl.LibFuncCache;
- return isValidProtoForLibFunc(*FDecl.getFunctionType(), F, *M);
+ if (!isValidProtoForLibFunc(*FDecl.getFunctionType(), F, *M))
+ return false;
+
+ if ((F == LibFunc_div || F == LibFunc_ldiv || F == LibFunc_lldiv) &&
+ FDecl.getReturnType()->isVoidTy()) {
+ const Argument *SRet = FDecl.getArg(0);
+ if (!SRet->hasStructRetAttr())
+ return false;
+ return isValidDivAggregateType(SRet->getParamStructRetType(),
+ FDecl.getFunctionType()->getParamType(1));
+ }
+
+ return true;
}
bool TargetLibraryInfoImpl::getLibFunc(unsigned int Opcode, Type *Ty,
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index c0140e431ffb6..680901cd3856a 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -41,6 +41,7 @@
#include "llvm/Transforms/Utils/SizeOpts.h"
#include <cmath>
+#include <utility>
using namespace llvm;
using namespace PatternMatch;
@@ -3282,6 +3283,65 @@ Value *LibCallSimplifier::optimizeAbs(CallInst *CI, IRBuilderBase &B) {
return B.CreateSelect(IsNeg, NegX, X);
}
+static Value *createDivResult(IRBuilderBase &B, const DataLayout &DL,
+ Type *ResultTy, Value *Quot, Value *Rem) {
+ if (auto *IntegerResultTy = dyn_cast<IntegerType>(ResultTy)) {
+ Value *Low = Quot;
+ Value *High = Rem;
+ if (DL.isBigEndian())
+ std::swap(Low, High);
+
+ Low = B.CreateZExt(Low, IntegerResultTy, "div.low");
+ High = B.CreateZExt(High, IntegerResultTy, "div.high");
+ High = B.CreateShl(High, Quot->getType()->getIntegerBitWidth(),
+ "div.high.shift");
+ return B.CreateOr(Low, High, "div.result");
+ }
+
+ if (auto *StructResultTy = dyn_cast<StructType>(ResultTy);
+ StructResultTy && StructResultTy->getNumElements() == 1) {
+ Value *Packed =
+ createDivResult(B, DL, StructResultTy->getElementType(0), Quot, Rem);
+ return B.CreateInsertValue(PoisonValue::get(ResultTy), Packed, 0,
+ "div.result.insert");
+ }
+
+ Value *Result = PoisonValue::get(ResultTy);
+ if (isa<StructType, ArrayType>(ResultTy)) {
+ Result = B.CreateInsertValue(Result, Quot, 0, "div.quot.insert");
+ return B.CreateInsertValue(Result, Rem, 1, "div.rem.insert");
+ }
+
+ llvm_unreachable("unexpected div result type");
+}
+
+Value *LibCallSimplifier::optimizeDiv(CallInst *CI, IRBuilderBase &B) {
+ Function *Callee = CI->getCalledFunction();
+ if (!Callee || CI->getFunctionType() != Callee->getFunctionType())
+ return nullptr;
+
+ bool UsesSRet = CI->getType()->isVoidTy();
+ unsigned ArgOffset = UsesSRet ? 1 : 0;
+ if (UsesSRet) {
+ if (!CI->hasStructRetAttr() ||
+ CI->getParamStructRetType(0) != Callee->getParamStructRetType(0))
+ return nullptr;
+ }
+
+ Value *Numer = CI->getArgOperand(ArgOffset);
+ Value *Denom = CI->getArgOperand(ArgOffset + 1);
+ Value *Quot = B.CreateSDiv(Numer, Denom, "div.quot");
+ Value *Rem = B.CreateSRem(Numer, Denom, "div.rem");
+
+ if (!UsesSRet)
+ return createDivResult(B, DL, CI->getType(), Quot, Rem);
+
+ Type *ResultTy = CI->getParamStructRetType(0);
+ Value *Result = createDivResult(B, DL, ResultTy, Quot, Rem);
+ B.CreateStore(Result, CI->getArgOperand(0));
+ return CI;
+}
+
Value *LibCallSimplifier::optimizeIsDigit(CallInst *CI, IRBuilderBase &B) {
// isdigit(c) -> (c-'0') <u 10
Value *Op = CI->getArgOperand(0);
@@ -4316,6 +4376,10 @@ Value *LibCallSimplifier::optimizeCall(CallInst *CI, IRBuilderBase &Builder) {
case LibFunc_labs:
case LibFunc_llabs:
return optimizeAbs(CI, Builder);
+ case LibFunc_div:
+ case LibFunc_ldiv:
+ case LibFunc_lldiv:
+ return optimizeDiv(CI, Builder);
case LibFunc_isdigit:
return optimizeIsDigit(CI, Builder);
case LibFunc_isascii:
diff --git a/llvm/test/Transforms/InstCombine/div-libcalls.ll b/llvm/test/Transforms/InstCombine/div-libcalls.ll
index 7b946ea58c22b..71943d49b9218 100644
--- a/llvm/test/Transforms/InstCombine/div-libcalls.ll
+++ b/llvm/test/Transforms/InstCombine/div-libcalls.ll
@@ -14,20 +14,52 @@ declare [2 x i64] @lldiv(i64, i64)
declare { i32, i64 } @"\01lldiv"(i64, i64)
define i64 @div_packed(i32 %numer, i32 %denom) {
-; CHECK-LABEL: define i64 @div_packed(
-; CHECK-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
-; CHECK-NEXT: [[RESULT:%.*]] = call i64 @div(i32 [[NUMER]], i32 [[DENOM]])
-; CHECK-NEXT: ret i64 [[RESULT]]
+; LITTLE-LABEL: define i64 @div_packed(
+; LITTLE-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; LITTLE-NEXT: [[DIV_QUOT:%.*]] = sdiv i32 [[NUMER]], [[DENOM]]
+; LITTLE-NEXT: [[DIV_REM:%.*]] = srem i32 [[NUMER]], [[DENOM]]
+; LITTLE-NEXT: [[DIV_LOW:%.*]] = zext i32 [[DIV_QUOT]] to i64
+; LITTLE-NEXT: [[DIV_HIGH:%.*]] = zext i32 [[DIV_REM]] to i64
+; LITTLE-NEXT: [[DIV_HIGH_SHIFT:%.*]] = shl nuw i64 [[DIV_HIGH]], 32
+; LITTLE-NEXT: [[DIV_RESULT:%.*]] = or disjoint i64 [[DIV_HIGH_SHIFT]], [[DIV_LOW]]
+; LITTLE-NEXT: ret i64 [[DIV_RESULT]]
+;
+; BIG-LABEL: define i64 @div_packed(
+; BIG-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; BIG-NEXT: [[DIV_QUOT:%.*]] = sdiv i32 [[NUMER]], [[DENOM]]
+; BIG-NEXT: [[DIV_REM:%.*]] = srem i32 [[NUMER]], [[DENOM]]
+; BIG-NEXT: [[DIV_LOW:%.*]] = zext i32 [[DIV_REM]] to i64
+; BIG-NEXT: [[DIV_HIGH:%.*]] = zext i32 [[DIV_QUOT]] to i64
+; BIG-NEXT: [[DIV_HIGH_SHIFT:%.*]] = shl nuw i64 [[DIV_HIGH]], 32
+; BIG-NEXT: [[DIV_RESULT:%.*]] = or disjoint i64 [[DIV_HIGH_SHIFT]], [[DIV_LOW]]
+; BIG-NEXT: ret i64 [[DIV_RESULT]]
;
%result = call i64 @div(i32 %numer, i32 %denom)
ret i64 %result
}
define { i64 } @div_packed_wrapper(i32 %numer, i32 %denom) {
-; CHECK-LABEL: define { i64 } @div_packed_wrapper(
-; CHECK-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
-; CHECK-NEXT: [[RESULT:%.*]] = call { i64 } @"\01div"(i32 [[NUMER]], i32 [[DENOM]])
-; CHECK-NEXT: ret { i64 } [[RESULT]]
+; LITTLE-LABEL: define { i64 } @div_packed_wrapper(
+; LITTLE-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; LITTLE-NEXT: [[DIV_QUOT:%.*]] = sdiv i32 [[NUMER]], [[DENOM]]
+; LITTLE-NEXT: [[DIV_REM:%.*]] = srem i32 [[NUMER]], [[DENOM]]
+; LITTLE-NEXT: [[DIV_LOW:%.*]] = zext i32 [[DIV_QUOT]] to i64
+; LITTLE-NEXT: [[DIV_HIGH:%.*]] = zext i32 [[DIV_REM]] to i64
+; LITTLE-NEXT: [[DIV_HIGH_SHIFT:%.*]] = shl nuw i64 [[DIV_HIGH]], 32
+; LITTLE-NEXT: [[DIV_RESULT:%.*]] = or disjoint i64 [[DIV_HIGH_SHIFT]], [[DIV_LOW]]
+; LITTLE-NEXT: [[DIV_RESULT_INSERT:%.*]] = insertvalue { i64 } poison, i64 [[DIV_RESULT]], 0
+; LITTLE-NEXT: ret { i64 } [[DIV_RESULT_INSERT]]
+;
+; BIG-LABEL: define { i64 } @div_packed_wrapper(
+; BIG-SAME: i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
+; BIG-NEXT: [[DIV_QUOT:%.*]] = sdiv i32 [[NUMER]], [[DENOM]]
+; BIG-NEXT: [[DIV_REM:%.*]] = srem i32 [[NUMER]], [[DENOM]]
+; BIG-NEXT: [[DIV_LOW:%.*]] = zext i32 [[DIV_REM]] to i64
+; BIG-NEXT: [[DIV_HIGH:%.*]] = zext i32 [[DIV_QUOT]] to i64
+; BIG-NEXT: [[DIV_HIGH_SHIFT:%.*]] = shl nuw i64 [[DIV_HIGH]], 32
+; BIG-NEXT: [[DIV_RESULT:%.*]] = or disjoint i64 [[DIV_HIGH_SHIFT]], [[DIV_LOW]]
+; BIG-NEXT: [[DIV_RESULT_INSERT:%.*]] = insertvalue { i64 } poison, i64 [[DIV_RESULT]], 0
+; BIG-NEXT: ret { i64 } [[DIV_RESULT_INSERT]]
;
%result = call { i64 } @"\01div"(i32 %numer, i32 %denom)
ret { i64 } %result
@@ -36,7 +68,10 @@ define { i64 } @div_packed_wrapper(i32 %numer, i32 %denom) {
define { i64, i64 } @ldiv_struct(i64 %numer, i64 %denom) {
; CHECK-LABEL: define { i64, i64 } @ldiv_struct(
; CHECK-SAME: i64 [[NUMER:%.*]], i64 [[DENOM:%.*]]) {
-; CHECK-NEXT: [[DIV_REM_INSERT:%.*]] = call { i64, i64 } @ldiv(i64 [[NUMER]], i64 [[DENOM]])
+; CHECK-NEXT: [[DIV_QUOT:%.*]] = sdiv i64 [[NUMER]], [[DENOM]]
+; CHECK-NEXT: [[DIV_REM:%.*]] = srem i64 [[NUMER]], [[DENOM]]
+; CHECK-NEXT: [[DIV_QUOT_INSERT:%.*]] = insertvalue { i64, i64 } poison, i64 [[DIV_QUOT]], 0
+; CHECK-NEXT: [[DIV_REM_INSERT:%.*]] = insertvalue { i64, i64 } [[DIV_QUOT_INSERT]], i64 [[DIV_REM]], 1
; CHECK-NEXT: ret { i64, i64 } [[DIV_REM_INSERT]]
;
%result = call { i64, i64 } @ldiv(i64 %numer, i64 %denom)
@@ -46,7 +81,10 @@ define { i64, i64 } @ldiv_struct(i64 %numer, i64 %denom) {
define [2 x i64] @lldiv_array(i64 %numer, i64 %denom) {
; CHECK-LABEL: define [2 x i64] @lldiv_array(
; CHECK-SAME: i64 [[NUMER:%.*]], i64 [[DENOM:%.*]]) {
-; CHECK-NEXT: [[DIV_REM_INSERT:%.*]] = call [2 x i64] @lldiv(i64 [[NUMER]], i64 [[DENOM]])
+; CHECK-NEXT: [[DIV_QUOT:%.*]] = sdiv i64 [[NUMER]], [[DENOM]]
+; CHECK-NEXT: [[DIV_REM:%.*]] = srem i64 [[NUMER]], [[DENOM]]
+; CHECK-NEXT: [[DIV_QUOT_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DIV_QUOT]], 0
+; CHECK-NEXT: [[DIV_REM_INSERT:%.*]] = insertvalue [2 x i64] [[DIV_QUOT_INSERT]], i64 [[DIV_REM]], 1
; CHECK-NEXT: ret [2 x i64] [[DIV_REM_INSERT]]
;
%result = call [2 x i64] @lldiv(i64 %numer, i64 %denom)
@@ -56,7 +94,11 @@ define [2 x i64] @lldiv_array(i64 %numer, i64 %denom) {
define void @ldiv_sret(ptr %result, i32 %numer, i32 %denom) {
; CHECK-LABEL: define void @ldiv_sret(
; CHECK-SAME: ptr [[RESULT:%.*]], i32 [[NUMER:%.*]], i32 [[DENOM:%.*]]) {
-; CHECK-NEXT: call void @"\01ldiv"(ptr sret([[DIV_SRET:%.*]]) [[RESULT]], i32 [[NUMER]], i32 [[DENOM]])
+; CHECK-NEXT: [[DIV_QUOT:%.*]] = sdiv i32 [[NUMER]], [[DENOM]]
+; CHECK-NEXT: [[DIV_REM:%.*]] = srem i32 [[NUMER]], [[DENOM]]
+; CHECK-NEXT: store i32 [[DIV_QUOT]], ptr [[RESULT]], align 4
+; CHECK-NEXT: [[RESULT_REPACK1:%.*]] = getelementptr inbounds nuw i8, ptr [[RESULT]], i64 4
+; CHECK-NEXT: store i32 [[DIV_REM]], ptr [[RESULT_REPACK1]], align 4
; CHECK-NEXT: ret void
;
call void @"\01ldiv"(ptr sret(%div_sret) %result, i32 %numer, i32 %denom)
@@ -94,6 +136,3 @@ define i64 @no_builtin(i32 %numer, i32 %denom) {
}
attributes #0 = { nobuiltin }
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; BIG: {{.*}}
-; LITTLE: {{.*}}
diff --git a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
index 63520a0b98089..1dc06fdbe76b9 100644
--- a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
+++ b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
@@ -32,7 +32,7 @@
# RUN: FileCheck %s --check-prefix=AVAIL --input-file %t3.txt
# RUN: FileCheck %s --check-prefix=UNAVAIL --input-file %t3.txt
#
-# CHECK: << Total TLI yes SDK no: 18
+# CHECK: << Total TLI yes SDK no: 21
# CHECK: >> Total TLI no SDK yes: 0
# CHECK: == Total TLI yes SDK yes: 277
#
@@ -40,13 +40,16 @@
# WRONG_DETAIL: >> TLI no SDK yes: '_ZdaPvj' aka operator delete[](void*, unsigned int)
# WRONG_DETAIL-COUNT-8: << TLI yes SDK no : {{.*}}__hot_cold_t
# WRONG_DETAIL-COUNT-4: << TLI yes SDK no : '__size_returning_new{{.*}}
+# WRONG_DETAIL: << TLI yes SDK no : 'div'
# WRONG_DETAIL: << TLI yes SDK no : 'fmaximum_num'
# WRONG_DETAIL: << TLI yes SDK no : 'fmaximum_numf'
# WRONG_DETAIL: << TLI yes SDK no : 'fmaximum_numl'
# WRONG_DETAIL: << TLI yes SDK no : 'fminimum_num'
# WRONG_DETAIL: << TLI yes SDK no : 'fminimum_numf'
# WRONG_DETAIL: << TLI yes SDK no : 'fminimum_numl'
-# WRONG_SUMMARY: << Total TLI yes SDK no: 19{{$}}
+# WRONG_DETAIL: << TLI yes SDK no : 'ldiv'
+# WRONG_DETAIL: << TLI yes SDK no : 'lldiv'
+# WRONG_SUMMARY: << Total TLI yes SDK no: 22{{$}}
# WRONG_SUMMARY: >> Total TLI no SDK yes: 1{{$}}
# WRONG_SUMMARY: == Total TLI yes SDK yes: 276
#
@@ -54,8 +57,8 @@
## the exact count first; the two directives should add up to that.
## Yes, this means additions to TLI will fail this test, but the argument
## to -COUNT can't be an expression.
-# AVAIL: TLI knows 528 symbols, 295 available
-# AVAIL-COUNT-295: {{^}} available
+# AVAIL: TLI knows 531 symbols, 298 available
+# AVAIL-COUNT-298: {{^}} available
# AVAIL-NOT: {{^}} available
# UNAVAIL-COUNT-233: not available
# UNAVAIL-NOT: not available
diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
index afeefd36a11e7..7a8a6b34f4df6 100644
--- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
+++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
@@ -174,6 +174,7 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare x86_fp80 @coshl(x86_fp80)\n"
"declare x86_fp80 @cosl(x86_fp80)\n"
"declare ptr @ctermid(ptr)\n"
+ "declare i64 @div(i32, i32)\n"
"declare double @exp(double)\n"
"declare double @exp2(double)\n"
"declare float @exp2f(float)\n"
@@ -258,7 +259,9 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
"declare double @ldexp(double, i32)\n"
"declare float @ldexpf(float, i32)\n"
"declare x86_fp80 @ldexpl(x86_fp80, i32)\n"
+ "declare { i64, i64 } @ldiv(i64, i64)\n"
"declare i64 @llabs(i64)\n"
+ "declare [2 x i64] @lldiv(i64, i64)\n"
"declare double @log(double)\n"
"declare double @log10(double)\n"
"declare float @log10f(float)\n"
More information about the cfe-commits
mailing list