diff --git a/clang/include/clang/Basic/BuiltinsAArch64.def b/clang/include/clang/Basic/BuiltinsAArch64.def --- a/clang/include/clang/Basic/BuiltinsAArch64.def +++ b/clang/include/clang/Basic/BuiltinsAArch64.def @@ -243,6 +243,9 @@ TARGET_HEADER_BUILTIN(_WriteStatusReg, "viLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_AddressOfReturnAddress, "v*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(__mulh, "SLLiSLLiSLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(__umulh, "ULLiULLiULLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") + #undef BUILTIN #undef LANGBUILTIN #undef TARGET_HEADER_BUILTIN diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -9712,6 +9712,29 @@ return Builder.CreateCall(F); } + if (BuiltinID == AArch64::BI__mulh || BuiltinID == AArch64::BI__umulh) { + llvm::Type *ResType = ConvertType(E->getType()); + llvm::Type *Int128Ty = llvm::IntegerType::get(getLLVMContext(), 128); + + bool IsSigned = BuiltinID == AArch64::BI__mulh; + Value *LHS = + Builder.CreateIntCast(EmitScalarExpr(E->getArg(0)), Int128Ty, IsSigned); + Value *RHS = + Builder.CreateIntCast(EmitScalarExpr(E->getArg(1)), Int128Ty, IsSigned); + + Value *MulResult, *HigherBits; + if (IsSigned) { + MulResult = Builder.CreateNSWMul(LHS, RHS); + HigherBits = Builder.CreateAShr(MulResult, 64); + } else { + MulResult = Builder.CreateNUWMul(LHS, RHS); + HigherBits = Builder.CreateLShr(MulResult, 64); + } + HigherBits = Builder.CreateIntCast(HigherBits, ResType, IsSigned); + + return HigherBits; + } + // Handle MSVC intrinsics before argument evaluation to prevent double // evaluation. if (Optional MsvcIntId = translateAarch64ToMsvcIntrin(BuiltinID)) diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h --- a/clang/lib/Headers/intrin.h +++ b/clang/lib/Headers/intrin.h @@ -574,6 +574,9 @@ unsigned short __cdecl _byteswap_ushort(unsigned short val); unsigned long __cdecl _byteswap_ulong (unsigned long val); unsigned __int64 __cdecl _byteswap_uint64(unsigned __int64 val); + +__int64 __mulh(__int64 __a, __int64 __b); +unsigned __int64 __umulh(unsigned __int64 __a, unsigned __int64 __b); #endif /*----------------------------------------------------------------------------*\ diff --git a/clang/test/CodeGen/arm64-microsoft-intrinsics.c b/clang/test/CodeGen/arm64-microsoft-intrinsics.c --- a/clang/test/CodeGen/arm64-microsoft-intrinsics.c +++ b/clang/test/CodeGen/arm64-microsoft-intrinsics.c @@ -81,6 +81,28 @@ // CHECK-MSVC: fence syncscope("singlethread") // CHECK-LINUX: error: implicit declaration of function '_ReadWriteBarrier' +long long check_mulh(long long a, long long b) { + return __mulh(a, b); +} + +// CHECK-MSVC: %[[ARG1:.*]] = sext i64 {{.*}} to i128 +// CHECK-MSVC: %[[ARG2:.*]] = sext i64 {{.*}} to i128 +// CHECK-MSVC: %[[PROD:.*]] = mul nsw i128 %[[ARG1]], %[[ARG2]] +// CHECK-MSVC: %[[HIGH:.*]] = ashr i128 %[[PROD]], 64 +// CHECK-MSVC: %[[RES:.*]] = trunc i128 %[[HIGH]] to i64 +// CHECK-LINUX: error: implicit declaration of function '__mulh' + +unsigned long long check_umulh(unsigned long long a, unsigned long long b) { + return __umulh(a, b); +} + +// CHECK-MSVC: %[[ARG1:.*]] = zext i64 {{.*}} to i128 +// CHECK-MSVC: %[[ARG2:.*]] = zext i64 {{.*}} to i128 +// CHECK-MSVC: %[[PROD:.*]] = mul nuw i128 %[[ARG1]], %[[ARG2]] +// CHECK-MSVC: %[[HIGH:.*]] = lshr i128 %[[PROD]], 64 +// CHECK-MSVC: %[[RES:.*]] = trunc i128 %[[HIGH]] to i64 +// CHECK-LINUX: error: implicit declaration of function '__umulh' + unsigned __int64 check__getReg() { unsigned volatile __int64 reg; reg = __getReg(18);