diff --git a/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp b/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s +struct RT { + char A; + int B[10][20]; + char C; +}; +struct ST { + int X; + double Y; + struct RT Z; +}; + +int *foo(struct ST *s) { + return &s[1].Z.B[5][13]; +} diff --git a/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp.funcattrs.expected b/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp.funcattrs.expected new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp.funcattrs.expected @@ -0,0 +1,29 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes +// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s +struct RT { + char A; + int B[10][20]; + char C; +}; +struct ST { + int X; + double Y; + struct RT Z; +}; + +// CHECK: Function Attrs: noinline nounwind optnone; +// CHECK-LABEL: @_Z3fooP2ST( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca %struct.ST*, align 8 +// CHECK-NEXT: store %struct.ST* [[S:%.*]], %struct.ST** [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load %struct.ST*, %struct.ST** [[S_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[TMP0]], i64 1 +// CHECK-NEXT: [[Z:%.*]] = getelementptr inbounds [[STRUCT_ST]], %struct.ST* [[ARRAYIDX]], i32 0, i32 2 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_RT:%.*]], %struct.RT* [[Z]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [10 x [20 x i32]], [10 x [20 x i32]]* [[B]], i64 0, i64 5 +// CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds [20 x i32], [20 x i32]* [[ARRAYIDX1]], i64 0, i64 13 +// CHECK-NEXT: ret i32* [[ARRAYIDX2]] +// +int *foo(struct ST *s) { + return &s[1].Z.B[5][13]; +} diff --git a/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp.plain.expected b/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp.plain.expected new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/check-attributes.cpp.plain.expected @@ -0,0 +1,28 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s +struct RT { + char A; + int B[10][20]; + char C; +}; +struct ST { + int X; + double Y; + struct RT Z; +}; + +// CHECK-LABEL: @_Z3fooP2ST( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca %struct.ST*, align 8 +// CHECK-NEXT: store %struct.ST* [[S:%.*]], %struct.ST** [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load %struct.ST*, %struct.ST** [[S_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[TMP0]], i64 1 +// CHECK-NEXT: [[Z:%.*]] = getelementptr inbounds [[STRUCT_ST]], %struct.ST* [[ARRAYIDX]], i32 0, i32 2 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_RT:%.*]], %struct.RT* [[Z]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [10 x [20 x i32]], [10 x [20 x i32]]* [[B]], i64 0, i64 5 +// CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds [20 x i32], [20 x i32]* [[ARRAYIDX1]], i64 0, i64 13 +// CHECK-NEXT: ret i32* [[ARRAYIDX2]] +// +int *foo(struct ST *s) { + return &s[1].Z.B[5][13]; +} diff --git a/clang/test/utils/update_cc_test_checks/check_attrs.test b/clang/test/utils/update_cc_test_checks/check_attrs.test new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/check_attrs.test @@ -0,0 +1,9 @@ +## Test that CHECK lines are generated as expected without --check-attributes +# RUN: cp %S/Inputs/check-attributes.cpp %t.cpp && %update_cc_test_checks %t.cpp +# RUN: diff -u %S/Inputs/check-attributes.cpp.plain.expected %t.cpp +## Test with --check-attributes flag. +# RUN: cp %S/Inputs/check-attributes.cpp %t.cpp && %update_cc_test_checks %t.cpp --check-attributes +# RUN: diff -u %S/Inputs/check-attributes.cpp.funcattrs.expected %t.cpp +## Check that re-running update_cc_test_checks doesn't change the output +# RUN: %update_cc_test_checks %t.cpp +# RUN: diff -u %S/Inputs/check-attributes.cpp.funcattrs.expected %t.cpp diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll @@ -0,0 +1,13 @@ +; RUN: opt -attributor -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -attributor-cgscc -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM + +%struct.RT = type { i8, [10 x [20 x i32]], i8 } +%struct.ST = type { i32, double, %struct.RT } + +define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { +entry: + %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13 + ret i32* %arrayidx +} diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes +; RUN: opt -attributor -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -attributor-cgscc -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM + +%struct.RT = type { i8, [10 x [20 x i32]], i8 } +%struct.ST = type { i32, double, %struct.RT } + +define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable willreturn; +; IS__TUNIT____-LABEL: define {{[^@]+}}@foo +; IS__TUNIT____-SAME: (%struct.ST* nofree readnone [[S:%.*]]) #0 +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S]], i64 1, i32 2, i32 1, i64 5, i64 13 +; IS__TUNIT____-NEXT: ret i32* [[ARRAYIDX]] +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind optsize readnone ssp uwtable willreturn; +; IS__CGSCC____-LABEL: define {{[^@]+}}@foo +; IS__CGSCC____-SAME: (%struct.ST* nofree readnone [[S:%.*]]) #0 +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S]], i64 1, i32 2, i32 1, i64 5, i64 13 +; IS__CGSCC____-NEXT: ret i32* [[ARRAYIDX]] +; +entry: + %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13 + ret i32* %arrayidx +} diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected @@ -0,0 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; RUN: opt -attributor -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -attributor-cgscc -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM + +%struct.RT = type { i8, [10 x [20 x i32]], i8 } +%struct.ST = type { i32, double, %struct.RT } + +define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (%struct.ST* nofree readnone [[S:%.*]]) #0 +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S]], i64 1, i32 2, i32 1, i64 5, i64 13 +; CHECK-NEXT: ret i32* [[ARRAYIDX]] +; +entry: + %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13 + ret i32* %arrayidx +} diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/check_attrs.test b/llvm/test/tools/UpdateTestChecks/update_test_checks/check_attrs.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/check_attrs.test @@ -0,0 +1,9 @@ +## check_attrs test checking that update_test_checks.py works correctly +# RUN: cp -f %S/Inputs/check_attrs.ll %t.ll && %update_test_checks %t.ll --function-signature +# RUN: diff -u %t.ll %S/Inputs/check_attrs.ll.plain.expected +## Also try the --check-attributes flag +# RUN: %update_test_checks %t.ll --check-attributes --function-signature +# RUN: diff -u %t.ll %S/Inputs/check_attrs.ll.funcattrs.expected +## Check that running the script again does not change the result: +# RUN: %update_test_checks %t.ll +# RUN: diff -u %t.ll %S/Inputs/check_attrs.ll.funcattrs.expected diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -364,7 +364,7 @@ scrubber, function_re = handler common.build_function_body_dictionary( function_re, scrubber, [args], raw_tool_output, prefixes, - func_dict, args.verbose, False) + func_dict, args.verbose, False, False) ##### Generator of assembly CHECK lines diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py --- a/llvm/utils/UpdateTestChecks/common.py +++ b/llvm/utils/UpdateTestChecks/common.py @@ -142,7 +142,7 @@ UTC_ADVERT = 'NOTE: Assertions have been autogenerated by ' OPT_FUNCTION_RE = re.compile( - r'^\s*define\s+(?:internal\s+)?[^@]*@(?P[\w.-]+?)\s*' + r'^(\s*;\s*Function\sAttrs:\s(?P[\w\s]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P[\w.-]+?)\s*' r'(?P\((\)|(.*?[\w.-]+?)\))[^{]*)\{\n(?P.*?)^\}$', flags=(re.M | re.S)) @@ -218,11 +218,12 @@ # Build up a dictionary of all the function bodies. class function_body(object): - def __init__(self, string, extra, args_and_sig): + def __init__(self, string, extra, args_and_sig, attrs): self.scrub = string self.extrascrub = extra self.args_and_sig = args_and_sig - def is_same_except_arg_names(self, extrascrub, args_and_sig): + self.attrs = attrs + def is_same_except_arg_names(self, extrascrub, args_and_sig, attrs): arg_names = set() def drop_arg_names(match): arg_names.add(match.group(2)) @@ -231,6 +232,8 @@ if match.group(2) in arg_names: return match.group(1) + match.group(3) return match.group(1) + match.group(2) + match.group(3) + if self.attrs != attrs: + return False ans0 = IR_VALUE_RE.sub(drop_arg_names, self.args_and_sig) ans1 = IR_VALUE_RE.sub(drop_arg_names, args_and_sig) if ans0 != ans1: @@ -244,12 +247,13 @@ def __str__(self): return self.scrub -def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose, record_args): +def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose, record_args, check_attributes): for m in function_re.finditer(raw_tool_output): if not m: continue func = m.group('func') body = m.group('body') + attrs = m.group('attrs') if check_attributes else '' # Determine if we print arguments, the opening brace, or nothing after the function name if record_args and 'args_and_sig' in m.groupdict(): args_and_sig = scrub_body(m.group('args_and_sig').strip()) @@ -271,19 +275,20 @@ for l in scrubbed_body.splitlines(): print(' ' + l, file=sys.stderr) for prefix in prefixes: - if func in func_dict[prefix] and (str(func_dict[prefix][func]) != scrubbed_body or (func_dict[prefix][func] and func_dict[prefix][func].args_and_sig != args_and_sig)): - if func_dict[prefix][func] and func_dict[prefix][func].is_same_except_arg_names(scrubbed_extra, args_and_sig): - func_dict[prefix][func].scrub = scrubbed_extra - func_dict[prefix][func].args_and_sig = args_and_sig - continue - else: - if prefix == prefixes[-1]: - warn('Found conflicting asm under the same prefix: %r!' % (prefix,)) - else: - func_dict[prefix][func] = None + if func in func_dict[prefix]: + if str(func_dict[prefix][func]) != scrubbed_body or (func_dict[prefix][func] and (func_dict[prefix][func].args_and_sig != args_and_sig or func_dict[prefix][func].attrs != attrs)): + if func_dict[prefix][func] and func_dict[prefix][func].is_same_except_arg_names(scrubbed_extra, args_and_sig, attrs): + func_dict[prefix][func].scrub = scrubbed_extra + func_dict[prefix][func].args_and_sig = args_and_sig continue + else: + if prefix == prefixes[-1]: + warn('Found conflicting asm under the same prefix: %r!' % (prefix,)) + else: + func_dict[prefix][func] = None + continue - func_dict[prefix][func] = function_body(scrubbed_body, scrubbed_extra, args_and_sig) + func_dict[prefix][func] = function_body(scrubbed_body, scrubbed_extra, args_and_sig, attrs) ##### Generator of LLVM IR CHECK lines @@ -383,6 +388,10 @@ vars_seen = set() printed_prefixes.append(checkprefix) + attrs = str(func_dict[checkprefix][func_name].attrs) + attrs = '' if attrs == 'None' else attrs + if attrs: + output_lines.append('%s %s: Function Attrs: %s;' % (comment_marker, checkprefix, attrs)) args_and_sig = str(func_dict[checkprefix][func_name].args_and_sig) args_and_sig = genericize_check_lines([args_and_sig], is_analyze, vars_seen)[0] if '[[' in args_and_sig: diff --git a/llvm/utils/update_analyze_test_checks.py b/llvm/utils/update_analyze_test_checks.py --- a/llvm/utils/update_analyze_test_checks.py +++ b/llvm/utils/update_analyze_test_checks.py @@ -122,7 +122,7 @@ for raw_tool_output in re.split(r'Printing analysis ', raw_tool_outputs): common.build_function_body_dictionary( common.ANALYZE_FUNCTION_RE, common.scrub_body, [], - raw_tool_output, prefixes, func_dict, args.verbose, False) + raw_tool_output, prefixes, func_dict, args.verbose, False, False) is_in_function = False is_in_function_start = False diff --git a/llvm/utils/update_cc_test_checks.py b/llvm/utils/update_cc_test_checks.py --- a/llvm/utils/update_cc_test_checks.py +++ b/llvm/utils/update_cc_test_checks.py @@ -131,6 +131,8 @@ help='Use more regex for x86 matching to reduce diffs between various subtargets') parser.add_argument('--function-signature', action='store_true', help='Keep function signature information around for the check line') + parser.add_argument('--check-attributes', action='store_true', + help='Check "Function Attributes" for functions') parser.add_argument('tests', nargs='+') args = common.parse_commandline_args(parser) @@ -189,7 +191,7 @@ if '-emit-llvm' in clang_args: common.build_function_body_dictionary( common.OPT_FUNCTION_RE, common.scrub_body, [], - raw_tool_output, prefixes, func_dict, args.verbose, args.function_signature) + raw_tool_output, prefixes, func_dict, args.verbose, args.function_signature, args.check_attributes) else: print('The clang command line should include -emit-llvm as asm tests ' 'are discouraged in Clang testsuite.', file=sys.stderr) diff --git a/llvm/utils/update_test_checks.py b/llvm/utils/update_test_checks.py --- a/llvm/utils/update_test_checks.py +++ b/llvm/utils/update_test_checks.py @@ -52,6 +52,8 @@ help='Keep function signature information around for the check line') parser.add_argument('--scrub-attributes', action='store_true', help='Remove attribute annotations (#0) from the end of check line') + parser.add_argument('--check-attributes', action='store_true', + help='Check "Function Attributes" for functions') parser.add_argument('tests', nargs='+') initial_args = common.parse_commandline_args(parser) @@ -111,7 +113,7 @@ common.build_function_body_dictionary( common.OPT_FUNCTION_RE, common.scrub_body, [], raw_tool_output, prefixes, func_dict, ti.args.verbose, - ti.args.function_signature) + ti.args.function_signature, ti.args.check_attributes) is_in_function = False is_in_function_start = False