Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -397,6 +397,15 @@ return {Width, Signed}; } +// Check if the target supports checked multiplication with 128-bit operands. +static bool has128BitMulOverflowSupport(const llvm::Triple &Triple) { + if (!Triple.isArch64Bit()) + return false; + return StringSwitch(llvm::Triple::getArchTypePrefix(Triple.getArch())) + .Cases("x86", "wasm", "mips", true) + .Default(false); +} + Value *CodeGenFunction::EmitVAStartEnd(Value *ArgValue, bool IsStart) { llvm::Type *DestType = Int8PtrTy; if (ArgValue->getType() != DestType) @@ -2248,11 +2257,21 @@ WidthAndSignedness EncompassingInfo = EncompassingIntegerType({LeftInfo, RightInfo, ResultInfo}); + llvm::Type *ResultLLVMTy = CGM.getTypes().ConvertType(ResultQTy); + + if (BuiltinID == Builtin::BI__builtin_mul_overflow) { + if ((EncompassingInfo.Width > 64 && + !has128BitMulOverflowSupport(getTarget().getTriple())) || + (EncompassingInfo.Width > 128)) { + CGM.ErrorUnsupported(E, + "__builtin_mul_overflow with mixed-sign operands"); + return RValue::get(llvm::UndefValue::get(ResultLLVMTy)); + } + } + llvm::Type *EncompassingLLVMTy = llvm::IntegerType::get(CGM.getLLVMContext(), EncompassingInfo.Width); - llvm::Type *ResultLLVMTy = CGM.getTypes().ConvertType(ResultQTy); - llvm::Intrinsic::ID IntrinsicId; switch (BuiltinID) { default: Index: test/CodeGen/builtins-overflow-unsupported.c =================================================================== --- /dev/null +++ test/CodeGen/builtins-overflow-unsupported.c @@ -0,0 +1,14 @@ +// RUN: not %clang_cc1 -triple "i686-unknown-unknown" -emit-llvm -x c %s -o - 2>&1 | FileCheck %s --check-prefix=M32 +// RUN: not %clang_cc1 -triple "x86_64-unknown-unknown" -emit-llvm -x c %s -o - 2>&1 | FileCheck %s --check-prefix=M64 + +signed long long try_smul_i65(signed long long a, unsigned long long b) { + // M32: [[@LINE+1]]:10: error: cannot compile this __builtin_mul_overflow with mixed-sign operands yet + return __builtin_mul_overflow(a, b, &b); +} + +#if defined(__LP64__) +__int128_t try_smul_i29(__int128_t a, __uint128_t b) { + // M64: [[@LINE+1]]:10: error: cannot compile this __builtin_mul_overflow with mixed-sign operands yet + return __builtin_mul_overflow(a, b, &b); +} +#endif Index: test/CodeGen/builtins-overflow.c =================================================================== --- test/CodeGen/builtins-overflow.c +++ test/CodeGen/builtins-overflow.c @@ -2,7 +2,9 @@ // rdar://13421498 // RUN: %clang_cc1 -triple "i686-unknown-unknown" -emit-llvm -x c %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple "x86_64-unknown-unknown" -emit-llvm -x c %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple "x86_64-unknown-unknown" -emit-llvm -x c %s -o - | FileCheck %s --check-prefixes=CHECK,M64 +// RUN: %clang_cc1 -triple "wasm64-unknown-unknown" -emit-llvm -x c %s -o - | FileCheck %s --check-prefixes=M64 +// RUN: %clang_cc1 -triple "mips64-unknown-unknown" -emit-llvm -x c %s -o - | FileCheck %s --check-prefixes=M64 // RUN: %clang_cc1 -triple "x86_64-mingw32" -emit-llvm -x c %s -o - | FileCheck %s extern unsigned UnsignedErrorCode; @@ -338,3 +340,20 @@ return LongLongErrorCode; return result; } + +#if defined(__LP64__) +signed long long test_mixed_sign_mul_i64(signed long long a, unsigned long long b) { + // M64-LABEL: define i64 @test_mixed_sign_mul_i64 + // M64: sext i64 {{.*}} to i65 + // M64-NEXT: zext i64 {{.*}} to i65 + // M64-NEXT: call { i65, i1 } @llvm.smul.with.overflow.i65 + // M64-NEXT: [[OFLOW_1:%.*]] = extractvalue { i65, i1 } {{.*}}, 1 + // M64-NEXT: [[RES:%.*]] = extractvalue { i65, i1 } {{.*}}, 0 + // M64-NEXT: [[RES_TRUNC:%.*]] = trunc i65 {{.*}} to i64 + // M64-NEXT: [[RES_EXT:%.*]] = zext i64 {{.*}} to i65 + // M64-NEXT: [[OFLOW_2:%.*]] = icmp ne i65 [[RES]], [[RES_EXT]] + // M64-NEXT: or i1 [[OFLOW_1]], [[OFLOW_2]] + // M64-NEXT: store i64 [[RES_TRUNC]] + return __builtin_mul_overflow(a, b, &b); +} +#endif