diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -414,6 +414,10 @@ LANGOPT(ArmSveVectorBits, 32, 0, "SVE vector size in bits") +COMPATIBLE_LANGOPT(ExtendArgs64, 1, 0, + "Controls how scalar integer arguments are extended in calls" + " to unprototyped and varargs functions") + #undef LANGOPT #undef COMPATIBLE_LANGOPT #undef BENIGN_LANGOPT diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1412,6 +1412,8 @@ bool isBigEndian() const { return BigEndian; } bool isLittleEndian() const { return !BigEndian; } + virtual bool supportsExtendArgs64() const { return false; } + /// Gets the default calling convention for the given target and /// declaration context. virtual CallingConv getDefaultCallingConv() const { diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1427,6 +1427,11 @@ PosFlag, NegFlag>, ShouldParseIf; +def fextend_args_64 : Flag<["-"], "fextend-arguments-64">, Group, + Flags<[CC1Option, NoArgumentUnused]>, + HelpText<"Controls how scalar integer arguments are extended in calls" + " to unprototyped and varargs functions">, + MarshallingInfoFlag>; def fbracket_depth_EQ : Joined<["-"], "fbracket-depth=">, Group, Flags<[CoreOption]>; def fsignaling_math : Flag<["-"], "fsignaling-math">, Group; def fno_signaling_math : Flag<["-"], "fno-signaling-math">, Group; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -336,6 +336,10 @@ bool setFPMath(StringRef Name) override; + bool supportsExtendArgs64() const override { + return getTriple().getArch() != llvm::Triple::x86; + } + CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { // Most of the non-ARM calling conventions are i386 conventions. switch (CC) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4908,6 +4908,9 @@ RenderFloatingPointOptions(TC, D, OFastEnabled, Args, CmdArgs, JA); + if (Args.getLastArg(options::OPT_fextend_args_64)) + CmdArgs.push_back("-fextend-arguments-64"); + if (Arg *A = Args.getLastArg(options::OPT_mdouble_EQ)) { if (TC.getArch() == llvm::Triple::avr) A->render(Args, CmdArgs); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -821,6 +821,15 @@ E = ImpCastExprToType(E, Context.DoubleTy, CK_FloatingCast).get(); } } + if (BTy && getLangOpts().ExtendArgs64 && + Context.getTargetInfo().supportsExtendArgs64() && Ty->isIntegerType()) { + E = (Ty->isUnsignedIntegerType()) + ? ImpCastExprToType(E, Context.UnsignedLongLongTy, CK_IntegralCast) + .get() + : ImpCastExprToType(E, Context.LongLongTy, CK_IntegralCast).get(); + assert(8 == Context.getTypeSizeInChars(Context.LongLongTy).getQuantity() && + "Unexpected typesize for LongLongTy"); + } // C++ performs lvalue-to-rvalue conversion as a default argument // promotion, even on class types, but note: diff --git a/clang/test/CodeGen/extend-arg-64.c b/clang/test/CodeGen/extend-arg-64.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/extend-arg-64.c @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fextend-arguments-64 \ +// RUN: %s -emit-llvm -o - | FileCheck %s -check-prefix=CHECKEXT + +// When the option isn't selected, no effect +// RUN: %clang_cc1 -triple x86_64-apple-darwin \ +// RUN: %s -emit-llvm -o - | FileCheck %s \ +// RUN: --implicit-check-not "ext {{.*}}to i64" + +// The option isn't supported on x86, no effect +// RUN: %clang_cc1 -triple i386-pc-linux-gnu -fextend-arguments-64 \ +// RUN: %s -emit-llvm -o - | FileCheck %s \ +// RUN: --implicit-check-not "ext {{.*}}to i64" + +// The option isn't supported on ppc, no effect +// RUN: %clang_cc1 -triple ppc64le -fextend-arguments-64 \ +// RUN: %s -emit-llvm -o - | FileCheck %s \ +// RUN: --implicit-check-not "ext {{.*}}to i64" + +int vararg(int, ...); + +unsigned int u32; +int s32; +unsigned short u16; +short s16; +unsigned char u8; +signed char s8; + +int test() { + // CHECK: define{{.*}} i32 @test{{.*}} + + // CHECKEXT: [[TAG_u32:%.*]] = load i32, i32* @u32{{.*}} + // CHECKEXT: [[CONV_u32:%.*]] = zext i32 [[TAG_u32]] to i64 + + // CHECKEXT: [[TAG_s32:%.*]] = load i32, i32* @s32 + // CHECKEXT: [[CONV_s32:%.*]] = sext i32 [[TAG_s32]] to i64 + + // CHECKEXT: [[TAG_u16:%.*]] = load i16, i16* @u16 + // CHECKEXT: [[CONV_u16:%.*]] = zext i16 [[TAG_u16]] to i64 + + // CHECKEXT: [[TAG_s16:%.*]] = load i16, i16* @s16 + // CHECKEXT: [[CONV_s16:%.*]] = sext i16 [[TAG_s16]] to i64 + + // CHECKEXT: [[TAG_u8:%.*]] = load i8, i8* @u8 + // CHECKEXT: [[CONV_u8:%.*]] = zext i8 [[TAG_u8]] to i64 + + // CHECKEXT: [[TAG_s8:%.*]] = load i8, i8* @s8 + // CHECKEXT: [[CONV_s8:%.*]] = sext i8 [[TAG_s8]] to i64 + // CHECKEXT: call{{.*}} @vararg(i32 %0, i64 [[CONV_u32]], i64 [[CONV_s32]], i64 [[CONV_u16]], i64 [[CONV_s16]], i64 [[CONV_u8]], i64 [[CONV_s8]] + + int sum = 0; + sum = vararg(sum, u32, s32, u16, s16, u8, s8); + return sum; +}