diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -258,6 +258,13 @@ Single }; + enum class ExtendArgsKind { + /// Integer arguments are sign or zero extended to 32/64 bits + /// during default argument promotions. + ExtendTo32, + ExtendTo64 + }; + public: /// The used language standard. LangStandard::Kind LangStd; 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") +ENUM_LANGOPT(ExtendIntArgs, ExtendArgsKind, 1, ExtendArgsKind::ExtendTo32, + "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 @@ -1411,6 +1411,9 @@ bool isBigEndian() const { return BigEndian; } bool isLittleEndian() const { return !BigEndian; } + /// Whether the option -fextend-arguments={32,64} is supported on the target. + virtual bool supportsExtendIntArgs() 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 @@ -1448,6 +1448,14 @@ PosFlag, NegFlag>, ShouldParseIf; +def fextend_args_EQ : Joined<["-"], "fextend-arguments=">, Group, + Flags<[CC1Option, NoArgumentUnused]>, + HelpText<"Controls how scalar integer arguments are extended in calls " + "to unprototyped and varargs functions">, + Values<"32,64">, + NormalizedValues<["ExtendTo32", "ExtendTo64"]>, + NormalizedValuesScope<"LangOptions::ExtendArgsKind">, + MarshallingInfoEnum,"ExtendTo32">; 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 supportsExtendIntArgs() 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 @@ -4907,6 +4907,20 @@ RenderFloatingPointOptions(TC, D, OFastEnabled, Args, CmdArgs, JA); + if (Arg *A = Args.getLastArg(options::OPT_fextend_args_EQ)) { + const llvm::Triple::ArchType Arch = TC.getArch(); + if (Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64) { + StringRef V = A->getValue(); + if (V == "64") + CmdArgs.push_back("-fextend-arguments=64"); + else if (V != "32") + D.Diag(diag::err_drv_invalid_argument_to_option) + << A->getValue() << A->getOption().getName(); + } else + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getOption().getName() << TripleStr; + } + 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,19 @@ E = ImpCastExprToType(E, Context.DoubleTy, CK_FloatingCast).get(); } } + if (BTy && + getLangOpts().getExtendIntArgs() == + LangOptions::ExtendArgsKind::ExtendTo64 && + Context.getTargetInfo().supportsExtendIntArgs() && Ty->isIntegerType() && + Context.getTypeSizeInChars(BTy) < + Context.getTypeSizeInChars(Context.LongLongTy)) { + 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,99 @@ +// 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, ...); +void knr(); + +unsigned int u32; +int s32; +unsigned short u16; +short s16; +unsigned char u8; +signed char s8; +long long ll; +_ExtInt(23) ei23; +float ff; +double dd; +__int128 i128; + +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); + knr(ll); + // CHECKEXT: load i64, i64* @ll + // CHECKEXT-NEXT: call void (i64, ...) bitcast {{.*}} @knr + + knr(ei23); + // CHECKEXT: load i23, i23* @ei23 + // CHECKEXT-NEXT: call void (i23, ...) bitcast{{.*}} @knr + + knr(ff); + // CHECKEXT: load float + // CHECKEXT-NEXT: fpext float {{.*}} to double + // CHECKEXT-NEXT: call{{.*}} void (double, ...) bitcast{{.*}} @knr + + knr(dd); + // CHECKEXT: load double + // CHECKEXT-NEXT: call{{.*}} void (double, ...) bitcast{{.*}} @knr + + knr(i128); + // CHECKEXT: load i128 + // CHECKEXT: call{{.*}} void (i64, i64, ...) bitcast{{.*}} @knr + + knr(u32, s32, u16, s16, u8, s8); + // 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{{.*}} void (i64, i64, i64, i64, i64, i64, ...) bitcast{{.*}} @knr + return sum; +} diff --git a/clang/test/Driver/fextend-args.c b/clang/test/Driver/fextend-args.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/fextend-args.c @@ -0,0 +1,17 @@ +// Options for intel arch +// RUN: %clang -### -target x86_64-apple-darwin -fextend-arguments=32 %s 2>&1 \ +// RUN: | FileCheck --implicit-check-not "-fextend-arguments=32" %s +// RUN: %clang -### -target x86_64-apple-darwin -fextend-arguments=64 %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-64 %s + +// Unsupported target +// RUN: not %clang -target aarch64-unknown-windows-msvc -fextend-arguments=32 %s 2>&1 \ +// RUN: | FileCheck -check-prefix=UNSUPPORTED-TARGET %s + +// Invalid option value +// RUN: not %clang -target x86_64-apple-darwin -fextend-arguments=0 %s 2>&1 \ +// RUN: | FileCheck -check-prefix=INVALID-VALUE %s + +// CHECK-64: "-cc1" {{.*}}"-fextend-arguments=64" +// UNSUPPORTED-TARGET: error: unsupported option +// INVALID-VALUE: error: invalid argument '0' to -fextend-arguments