Skip to content

Commit a58ad3e

Browse files
authored
[clang] Add size filter for stack auto init (llvm#74777)
Add a clang flag, "-ftrivial-auto-var-init-max-size=" so that clang skips auto-init a variable if the auto-init memset size exceeds the flag setting (in bytes). Note that this skipping doesn't apply to runtime-sized variables like VLA. Considerations: "__attribute__((uninitialized))" can be used to manually opt variables out. However, there are thousands of large variables (e.g., >=1KB, most of them are arrays and used as buffers) in big codebase. Manually opting them out one by one is not efficient.
1 parent fa6c3df commit a58ad3e

File tree

7 files changed

+137
-0
lines changed

7 files changed

+137
-0
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,13 @@ def err_drv_trivial_auto_var_init_stop_after_missing_dependency : Error<
656656
def err_drv_trivial_auto_var_init_stop_after_invalid_value : Error<
657657
"'-ftrivial-auto-var-init-stop-after=*' only accepts positive integers">;
658658

659+
def err_drv_trivial_auto_var_init_max_size_missing_dependency : Error<
660+
"'-ftrivial-auto-var-init-max-size=*' is used without "
661+
"'-ftrivial-auto-var-init=zero' or '-ftrivial-auto-var-init=pattern'">;
662+
663+
def err_drv_trivial_auto_var_init_max_size_invalid_value : Error<
664+
"'-ftrivial-auto-var-init-max-size=*' only accepts positive integers (in bytes)">;
665+
659666
def warn_drv_msp430_hwmult_unsupported : Warning<
660667
"the given MCU does not support hardware multiply, but '-mhwmult' is set to "
661668
"%0">, InGroup<InvalidCommandLineArgument>;

clang/include/clang/Basic/LangOptions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKi
378378
"trivial automatic variable initialization")
379379
VALUE_LANGOPT(TrivialAutoVarInitStopAfter, 32, 0,
380380
"stop trivial automatic variable initialization after the specified number of instances. Must be greater than 0.")
381+
VALUE_LANGOPT(TrivialAutoVarInitMaxSize, 32, 0,
382+
"stop trivial automatic variable initialization if var size exceeds the specified size (in bytes). Must be greater than 0.")
381383
ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined,
382384
"signed integer overflow handling")
383385
ENUM_LANGOPT(ThreadModel , ThreadModelKind, 2, ThreadModelKind::POSIX, "Thread Model")

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,6 +3664,10 @@ def ftrivial_auto_var_init_stop_after : Joined<["-"], "ftrivial-auto-var-init-st
36643664
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
36653665
HelpText<"Stop initializing trivial automatic stack variables after the specified number of instances">,
36663666
MarshallingInfoInt<LangOpts<"TrivialAutoVarInitStopAfter">>;
3667+
def ftrivial_auto_var_init_max_size : Joined<["-"], "ftrivial-auto-var-init-max-size=">, Group<f_Group>,
3668+
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
3669+
HelpText<"Stop initializing trivial automatic stack variables if var size exceeds the specified number of instances (in bytes)">,
3670+
MarshallingInfoInt<LangOpts<"TrivialAutoVarInitMaxSize">>;
36673671
def fstandalone_debug : Flag<["-"], "fstandalone-debug">, Group<f_Group>,
36683672
Visibility<[ClangOption, CLOption, DXCOption]>,
36693673
HelpText<"Emit full debug info for all types used by the program">;

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,20 +1759,34 @@ void CodeGenFunction::emitZeroOrPatternForAutoVarInit(QualType type,
17591759
const VarDecl &D,
17601760
Address Loc) {
17611761
auto trivialAutoVarInit = getContext().getLangOpts().getTrivialAutoVarInit();
1762+
auto trivialAutoVarInitMaxSize =
1763+
getContext().getLangOpts().TrivialAutoVarInitMaxSize;
17621764
CharUnits Size = getContext().getTypeSizeInChars(type);
17631765
bool isVolatile = type.isVolatileQualified();
17641766
if (!Size.isZero()) {
1767+
// We skip auto-init variables by their alloc size. Take this as an example:
1768+
// "struct Foo {int x; char buff[1024];}" Assume the max-size flag is 1023.
1769+
// All Foo type variables will be skipped. Ideally, we only skip the buff
1770+
// array and still auto-init X in this example.
1771+
// TODO: Improve the size filtering to by member size.
1772+
auto allocSize = CGM.getDataLayout().getTypeAllocSize(Loc.getElementType());
17651773
switch (trivialAutoVarInit) {
17661774
case LangOptions::TrivialAutoVarInitKind::Uninitialized:
17671775
llvm_unreachable("Uninitialized handled by caller");
17681776
case LangOptions::TrivialAutoVarInitKind::Zero:
17691777
if (CGM.stopAutoInit())
17701778
return;
1779+
if (trivialAutoVarInitMaxSize > 0 &&
1780+
allocSize > trivialAutoVarInitMaxSize)
1781+
return;
17711782
emitStoresForZeroInit(CGM, D, Loc, isVolatile, Builder);
17721783
break;
17731784
case LangOptions::TrivialAutoVarInitKind::Pattern:
17741785
if (CGM.stopAutoInit())
17751786
return;
1787+
if (trivialAutoVarInitMaxSize > 0 &&
1788+
allocSize > trivialAutoVarInitMaxSize)
1789+
return;
17761790
emitStoresForPatternInit(CGM, D, Loc, isVolatile, Builder);
17771791
break;
17781792
}

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3535,6 +3535,20 @@ static void RenderTrivialAutoVarInitOptions(const Driver &D,
35353535
CmdArgs.push_back(
35363536
Args.MakeArgString("-ftrivial-auto-var-init-stop-after=" + Val));
35373537
}
3538+
3539+
if (Arg *A = Args.getLastArg(options::OPT_ftrivial_auto_var_init_max_size)) {
3540+
if (!Args.hasArg(options::OPT_ftrivial_auto_var_init) ||
3541+
StringRef(
3542+
Args.getLastArg(options::OPT_ftrivial_auto_var_init)->getValue()) ==
3543+
"uninitialized")
3544+
D.Diag(diag::err_drv_trivial_auto_var_init_max_size_missing_dependency);
3545+
A->claim();
3546+
StringRef Val = A->getValue();
3547+
if (std::stoi(Val.str()) <= 0)
3548+
D.Diag(diag::err_drv_trivial_auto_var_init_max_size_invalid_value);
3549+
CmdArgs.push_back(
3550+
Args.MakeArgString("-ftrivial-auto-var-init-max-size=" + Val));
3551+
}
35383552
}
35393553

35403554
static void RenderOpenCLOptions(const ArgList &Args, ArgStringList &CmdArgs,
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Pattern related max size tests: 1, 1024, 4096
2+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=pattern -ftrivial-auto-var-init-max-size=1 %s -emit-llvm -o - | FileCheck -check-prefix=PATTERN-COMMON -check-prefix=PATTERN-MAX-1 %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=pattern -ftrivial-auto-var-init-max-size=1024 %s -emit-llvm -o - | FileCheck -check-prefix=PATTERN-COMMON -check-prefix=PATTERN-MAX-1024 %s
4+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=pattern -ftrivial-auto-var-init-max-size=4096 %s -emit-llvm -o - | FileCheck -check-prefix=PATTERN-COMMON -check-prefix=PATTERN-MAX-4096 %s
5+
//
6+
// Zero related max size tests: 1, 1024, 4096
7+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=zero -ftrivial-auto-var-init-max-size=1 %s -emit-llvm -o - | FileCheck -check-prefix=ZERO-COMMON -check-prefix=ZERO-MAX-1 %s
8+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=zero -ftrivial-auto-var-init-max-size=1024 %s -emit-llvm -o - | FileCheck -check-prefix=ZERO-COMMON -check-prefix=ZERO-MAX-1024 %s
9+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=zero -ftrivial-auto-var-init-max-size=4096 %s -emit-llvm -o - | FileCheck -check-prefix=ZERO-COMMON -check-prefix=ZERO-MAX-4096 %s
10+
11+
struct Foo {
12+
int x; // we should try to make sure X is initialized.
13+
char buff[1024]; // this one is fine to skip
14+
};
15+
16+
int foo(unsigned n) {
17+
bool var_size_1;
18+
long var_size_8 = 123;
19+
void *var_size_8p;
20+
int var_size_1024[256];
21+
Foo var_size_1028;
22+
int var_size_4096[1024];
23+
// VLA, non-constant size
24+
int var_vla[n];
25+
// builtin, non-constant size
26+
var_size_8p = __builtin_alloca(sizeof(unsigned long long) * n);
27+
// There are 8 variables: var_size_1, var_size_8, var_size_8p, var_size_1024,
28+
// var_size_1028, var_size_4096, var_vla, and a builtin anonymous var ("%5").
29+
// - COMMON (auto-init regardless of the max size): "var_vla", and "%5"
30+
// - Max size 1: "var_size_1"
31+
// - Max size 1024: "var_size_1", "var_size_8", "var_size_8p", "var_size_1024"
32+
// - Max size 4096: "var_size_1", "var_size_8", "var_size_8p", "var_size_1024", "var_size_1028", "var_size_4096"
33+
//
34+
// PATTERN-MAX-1: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
35+
// PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
36+
// PATTERN-MAX-1-NOT: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
37+
// PATTERN-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
38+
// PATTERN-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 -86, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
39+
// PATTERN-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 -86, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
40+
41+
// PATTERN-MAX-1024: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
42+
// PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
43+
// PATTERN-MAX-1024: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
44+
// PATTERN-MAX-1024: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
45+
// PATTERN-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 -86, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
46+
// PATTERN-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 -86, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
47+
48+
// PATTERN-MAX-4096: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
49+
// PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
50+
// PATTERN-MAX-4096: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
51+
// PATTERN-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
52+
// PATTERN-MAX-4096: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 -86, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
53+
// PATTERN-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 -86, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
54+
55+
// PATTERN-COMMON: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %vla.cur, ptr align 4 @__const._Z3fooj.var_vla, i64 4, i1 false), !annotation [[AUTO_INIT:!.+]]
56+
// PATTERN-COMMON: call void @llvm.memset.p0.i64(ptr align 16 %5, i8 -86, i64 %mul, i1 false), !annotation [[AUTO_INIT:!.+]]
57+
58+
// ZERO-MAX-1: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
59+
// ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
60+
// ZERO-MAX-1-NOT: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
61+
// ZERO-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
62+
// ZERO-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
63+
// ZERO-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 0, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
64+
65+
// ZERO-MAX-1024: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
66+
// ZERO-MAX-1024: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
67+
// ZERO-MAX-1024: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
68+
// ZERO-MAX-1024: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
69+
// ZERO-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
70+
// ZERO-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 0, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
71+
72+
// ZERO-MAX-4096: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
73+
// ZERO-MAX-4096: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
74+
// ZERO-MAX-4096: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
75+
// ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
76+
// ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
77+
// ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 0, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
78+
79+
// ZERO-COMMON: call void @llvm.memset.p0.i64(ptr align 16 %vla, i8 0, i64 %3, i1 false), !annotation [[AUTO_INIT:!.+]]
80+
// ZERO-COMMON: call void @llvm.memset.p0.i64(ptr align 16 %5, i8 0, i64 %mul, i1 false), !annotation [[AUTO_INIT:!.+]]
81+
82+
return 0;
83+
}

clang/test/Driver/clang_f_opts.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,19 @@
585585
// CHECK-TRIVIAL-PATTERN-STOP-AFTER-INVALID-VALUE: only accepts positive integers
586586
// CHECK-TRIVIAL-ZERO-STOP-AFTER-INVALID-VALUE: only accepts positive integers
587587

588+
// RUN: %clang -### -S -ftrivial-auto-var-init=pattern -ftrivial-auto-var-init-max-size=1024 %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-PATTERN-MAX-SIZE %s
589+
// RUN: %clang -### -S -ftrivial-auto-var-init=zero -ftrivial-auto-var-init-max-size=1024 %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-ZERO-MAX-SIZE %s
590+
// RUN: not %clang -### -S -ftrivial-auto-var-init-max-size=1024 %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-MAX-SIZE-MISSING-DEPENDENCY %s
591+
// RUN: not %clang -### -S -ftrivial-auto-var-init=pattern -ftrivial-auto-var-init-max-size=0 %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-PATTERN-MAX-SIZE-INVALID-VALUE %s
592+
// RUN: not %clang -### -S -ftrivial-auto-var-init=zero -ftrivial-auto-var-init-max-size=0 %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-ZERO-MAX-SIZE-INVALID-VALUE %s
593+
// CHECK-TRIVIAL-PATTERN-MAX-SIZE-NOT: is used without '-ftrivial-auto-var-init'
594+
// CHECK-TRIVIAL-PATTERN-MAX-SIZE-NOT: only accepts positive integers (in bytes)
595+
// CHECK-TRIVIAL-ZERO-MAX-SIZE-NOT: is used without '-ftrivial-auto-var-init'
596+
// CHECK-TRIVIAL-ZERO-MAX-SIZE-NOT: only accepts positive integers (in bytes)
597+
// CHECK-TRIVIAL-MAX-SIZE-MISSING-DEPENDENCY: used without '-ftrivial-auto-var-init=zero' or
598+
// CHECK-TRIVIAL-PATTERN-MAX-SIZE-INVALID-VALUE: only accepts positive integers (in bytes)
599+
// CHECK-TRIVIAL-ZERO-MAX-SIZE-INVALID-VALUE: only accepts positive integers (in bytes)
600+
588601
// RUN: %clang -### -S -fno-temp-file %s 2>&1 | FileCheck -check-prefix=CHECK-NO-TEMP-FILE %s
589602
// CHECK-NO-TEMP-FILE: "-fno-temp-file"
590603

0 commit comments

Comments
 (0)