jrtc27 updated this revision to Diff 359643.
jrtc27 added a comment.
Drop the --llvm-bin test; only basic-cplusplus.test does that (which happened
to be the one I used as a reference), and that only needs to be done for one
file as it has no relation to the input.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D106243/new/
https://reviews.llvm.org/D106243
Files:
clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
llvm/utils/update_cc_test_checks.py
Index: llvm/utils/update_cc_test_checks.py
===================================================================
--- llvm/utils/update_cc_test_checks.py
+++ llvm/utils/update_cc_test_checks.py
@@ -33,8 +33,8 @@
'%clangxx': ['--driver-mode=g++'],
}
-def get_line2spell_and_mangled(args, clang_args):
- ret = {}
+def get_line2func_list(args, clang_args):
+ ret = collections.defaultdict(list)
# Use clang's JSON AST dump to get the mangled name
json_dump_args = [args.clang] + clang_args + ['-fsyntax-only', '-o', '-']
if '-cc1' not in json_dump_args:
@@ -55,26 +55,37 @@
# Parse the clang JSON and add all children of type FunctionDecl.
# TODO: Should we add checks for global variables being emitted?
- def parse_clang_ast_json(node):
+ def parse_clang_ast_json(node, loc, search):
node_kind = node['kind']
# Recurse for the following nodes that can contain nested function decls:
if node_kind in ('NamespaceDecl', 'LinkageSpecDecl', 'TranslationUnitDecl',
- 'CXXRecordDecl'):
+ 'CXXRecordDecl', 'ClassTemplateSpecializationDecl'):
+ # Specializations must use the loc from the specialization, not the
+ # template, and search for the class's spelling as the specialization
+ # does not mention the method names in the source.
+ if node_kind == 'ClassTemplateSpecializationDecl':
+ inner_loc = node['loc']
+ inner_search = node['name']
+ else:
+ inner_loc = None
+ inner_search = None
if 'inner' in node:
for inner in node['inner']:
- parse_clang_ast_json(inner)
+ parse_clang_ast_json(inner, inner_loc, inner_search)
# Otherwise we ignore everything except functions:
if node_kind not in ('FunctionDecl', 'CXXMethodDecl', 'CXXConstructorDecl',
'CXXDestructorDecl', 'CXXConversionDecl'):
return
+ if loc is None:
+ loc = node['loc']
if node.get('isImplicit') is True and node.get('storageClass') == 'extern':
- common.debug('Skipping builtin function:', node['name'], '@', node['loc'])
+ common.debug('Skipping builtin function:', node['name'], '@', loc)
return
- common.debug('Found function:', node['kind'], node['name'], '@', node['loc'])
- line = node['loc'].get('line')
+ common.debug('Found function:', node['kind'], node['name'], '@', loc)
+ line = loc.get('line')
# If there is no line it is probably a builtin function -> skip
if line is None:
- common.debug('Skipping function without line number:', node['name'], '@', node['loc'])
+ common.debug('Skipping function without line number:', node['name'], '@', loc)
return
# If there is no 'inner' object, it is a function declaration and we can
@@ -88,20 +99,23 @@
has_body = True
break
if not has_body:
- common.debug('Skipping function without body:', node['name'], '@', node['loc'])
+ common.debug('Skipping function without body:', node['name'], '@', loc)
return
spell = node['name']
+ if search is None:
+ search = spell
mangled = node.get('mangledName', spell)
- ret[int(line)-1] = (spell, mangled)
+ ret[int(line)-1].append((spell, mangled, search))
ast = json.loads(stdout)
if ast['kind'] != 'TranslationUnitDecl':
common.error('Clang AST dump JSON format changed?')
sys.exit(2)
- parse_clang_ast_json(ast)
+ parse_clang_ast_json(ast, None, None)
- for line, func_name in sorted(ret.items()):
- common.debug('line {}: found function {}'.format(line+1, func_name), file=sys.stderr)
+ for line, funcs in sorted(ret.items()):
+ for func in funcs:
+ common.debug('line {}: found function {}'.format(line+1, func), file=sys.stderr)
if not ret:
common.warn('Did not find any functions using', ' '.join(json_dump_args))
return ret
@@ -222,7 +236,7 @@
comment_prefix='//', argparse_callback=infer_dependent_args):
# Build a list of filechecked and non-filechecked RUN lines.
run_list = []
- line2spell_and_mangled_list = collections.defaultdict(list)
+ line2func_list = collections.defaultdict(list)
subs = {
'%s' : ti.path,
@@ -296,8 +310,8 @@
# Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
# mangled names. Forward all clang args for now.
- for k, v in get_line2spell_and_mangled(ti.args, clang_args).items():
- line2spell_and_mangled_list[k].append(v)
+ for k, v in get_line2func_list(ti.args, clang_args).items():
+ line2func_list[k].extend(v)
func_dict = builder.finish_and_get_func_dict()
global_vars_seen_dict = {}
@@ -357,15 +371,16 @@
# Skip special separator comments added by commmon.add_global_checks.
if line.strip() == '//' + common.SEPARATOR:
continue
- if idx in line2spell_and_mangled_list:
+ if idx in line2func_list:
added = set()
- for spell, mangled in line2spell_and_mangled_list[idx]:
+ for spell, mangled, search in line2func_list[idx]:
# One line may contain multiple function declarations.
# Skip if the mangled name has been added before.
- # The line number may come from an included file,
- # we simply require the spelling name to appear on the line
- # to exclude functions from other files.
- if mangled in added or spell not in line:
+ # The line number may come from an included file, we simply require
+ # the search string (normally the function's spelling name, but is
+ # the class's spelling name for class specializations) to appear on
+ # the line to exclude functions from other files.
+ if mangled in added or search not in line:
continue
if args.functions is None or any(re.search(regex, spell) for regex in args.functions):
last_line = output_lines[-1].strip()
Index: clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
@@ -0,0 +1,7 @@
+## Test that CHECK lines are generated for explicit template instantiatons
+
+# RUN: cp %S/Inputs/explicit-template-instantiation.cpp %t.cpp && %update_cc_test_checks %t.cpp
+# RUN: diff -u %S/Inputs/explicit-template-instantiation.cpp.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/explicit-template-instantiation.cpp.expected %t.cpp
Index: clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
@@ -0,0 +1,190 @@
+// 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
+
+template <typename T>
+struct Foo {
+private:
+ T x;
+
+public:
+ Foo(T x) : x(x) {}
+ ~Foo() {}
+
+ T get() { return x; }
+ void set(T _x) { x = _x; }
+};
+
+template <typename T>
+struct Bar {
+private:
+ struct Foo<T> foo;
+
+public:
+ Bar(T x) : foo(x) {}
+ ~Bar() {}
+
+ T get() { return foo.get(); }
+ void set(T _x) { foo.set(_x); }
+};
+
+template <typename T>
+struct Baz : Foo<T> {
+public:
+ Baz(T x) : Foo<T>(x) {}
+ ~Baz() {}
+};
+
+// These two specializations should generate lines for all of Foo's methods.
+
+// CHECK-LABEL: @_ZN3FooIcEC1Ec(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i8 [[X:%.*]], i8* [[X_ADDR]], align 1
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[X_ADDR]], align 1
+// CHECK-NEXT: call void @_ZN3FooIcEC2Ec(%struct.Foo* nonnull align 1 dereferenceable(1) [[THIS1]], i8 signext [[TMP0]])
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3FooIcED1Ev(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT: store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: call void @_ZN3FooIcED2Ev(%struct.Foo* nonnull align 1 dereferenceable(1) [[THIS1]]) #[[ATTR2:[0-9]+]]
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3FooIcE3getEv(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT: store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], %struct.Foo* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[X]], align 1
+// CHECK-NEXT: ret i8 [[TMP0]]
+//
+// CHECK-LABEL: @_ZN3FooIcE3setEc(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT: [[_X_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i8 [[_X:%.*]], i8* [[_X_ADDR]], align 1
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[_X_ADDR]], align 1
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], %struct.Foo* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: store i8 [[TMP0]], i8* [[X]], align 1
+// CHECK-NEXT: ret void
+//
+template struct Foo<char>;
+
+// CHECK-LABEL: @_ZN3FooIsEC1Es(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i16, align 2
+// CHECK-NEXT: store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i16 [[X:%.*]], i16* [[X_ADDR]], align 2
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i16, i16* [[X_ADDR]], align 2
+// CHECK-NEXT: call void @_ZN3FooIsEC2Es(%struct.Foo.0* nonnull align 2 dereferenceable(2) [[THIS1]], i16 signext [[TMP0]])
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3FooIsED1Ev(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT: store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: call void @_ZN3FooIsED2Ev(%struct.Foo.0* nonnull align 2 dereferenceable(2) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3FooIsE3getEv(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT: store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO_0:%.*]], %struct.Foo.0* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: [[TMP0:%.*]] = load i16, i16* [[X]], align 2
+// CHECK-NEXT: ret i16 [[TMP0]]
+//
+// CHECK-LABEL: @_ZN3FooIsE3setEs(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT: [[_X_ADDR:%.*]] = alloca i16, align 2
+// CHECK-NEXT: store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i16 [[_X:%.*]], i16* [[_X_ADDR]], align 2
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i16, i16* [[_X_ADDR]], align 2
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO_0:%.*]], %struct.Foo.0* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: store i16 [[TMP0]], i16* [[X]], align 2
+// CHECK-NEXT: ret void
+//
+template struct Foo<short>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Bar.
+
+// CHECK-LABEL: @_ZN3BarIiEC1Ei(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i32 [[X:%.*]], i32* [[X_ADDR]], align 4
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[X_ADDR]], align 4
+// CHECK-NEXT: call void @_ZN3BarIiEC2Ei(%struct.Bar* nonnull align 4 dereferenceable(4) [[THIS1]], i32 [[TMP0]])
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3BarIiED1Ev(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT: store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: call void @_ZN3BarIiED2Ev(%struct.Bar* nonnull align 4 dereferenceable(4) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3BarIiE3getEv(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT: store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[FOO:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], %struct.Bar* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: [[CALL:%.*]] = call i32 @_ZN3FooIiE3getEv(%struct.Foo.1* nonnull align 4 dereferenceable(4) [[FOO]])
+// CHECK-NEXT: ret i32 [[CALL]]
+//
+// CHECK-LABEL: @_ZN3BarIiE3setEi(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT: [[_X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i32 [[_X:%.*]], i32* [[_X_ADDR]], align 4
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[FOO:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], %struct.Bar* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[_X_ADDR]], align 4
+// CHECK-NEXT: call void @_ZN3FooIiE3setEi(%struct.Foo.1* nonnull align 4 dereferenceable(4) [[FOO]], i32 [[TMP0]])
+// CHECK-NEXT: ret void
+//
+template struct Bar<int>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Baz.
+
+// CHECK-LABEL: @_ZN3BazIlEC1El(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Baz*, align 8
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT: store %struct.Baz* [[THIS:%.*]], %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT: store i64 [[X:%.*]], i64* [[X_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Baz*, %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load i64, i64* [[X_ADDR]], align 8
+// CHECK-NEXT: call void @_ZN3BazIlEC2El(%struct.Baz* nonnull align 8 dereferenceable(8) [[THIS1]], i64 [[TMP0]])
+// CHECK-NEXT: ret void
+//
+// CHECK-LABEL: @_ZN3BazIlED1Ev(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.Baz*, align 8
+// CHECK-NEXT: store %struct.Baz* [[THIS:%.*]], %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT: [[THIS1:%.*]] = load %struct.Baz*, %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT: call void @_ZN3BazIlED2Ev(%struct.Baz* nonnull align 8 dereferenceable(8) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT: ret void
+//
+template struct Baz<long>;
Index: clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+template <typename T>
+struct Foo {
+private:
+ T x;
+
+public:
+ Foo(T x) : x(x) {}
+ ~Foo() {}
+
+ T get() { return x; }
+ void set(T _x) { x = _x; }
+};
+
+template <typename T>
+struct Bar {
+private:
+ struct Foo<T> foo;
+
+public:
+ Bar(T x) : foo(x) {}
+ ~Bar() {}
+
+ T get() { return foo.get(); }
+ void set(T _x) { foo.set(_x); }
+};
+
+template <typename T>
+struct Baz : Foo<T> {
+public:
+ Baz(T x) : Foo<T>(x) {}
+ ~Baz() {}
+};
+
+// These two specializations should generate lines for all of Foo's methods.
+
+template struct Foo<char>;
+
+template struct Foo<short>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Bar.
+
+template struct Bar<int>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Baz.
+
+template struct Baz<long>;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits