Add basic tests for MEMTAG sanitizer. MEMTAG sanitizer uses target
hooks to emit AArch64 specific MTE instructions.
Add new target-specific tests.
The currently generated code currently has the following known
limitations:
1. For basic-1.c testcase, currently we generate:
subg x0, x0, #16, #0
stg x0, [x0, #0]
str w1, [x0]
The subg can be optimized out. Adding #0 to the tag is
non-consequential. The address generation component (x0+16) can be
folded into the addr operands of stg.
2. Need to generate stgp (pre-index, post-index) when appropriate.
Need to look into how aarch64 backend generates the
store-pair/load-pair operations currently. We will likely need to use
the same framework for generating the store-pair-with-tag (pre-indexed
and post-indexed) variants for MTE.
3. Also stzg, stz2g are not generated at all. Basically none of the
store zero and tag the location instructions are implemented yet.
4. vararray-nested-gimple.c shows an outstanding issue. Currently,
there is a (what I think) a superfluous .HWASAN_ALLOCA_UNPOISON.
This causes incorrect code generation, because the expansion of this
.HWASAN_ALLOCA_UNPOISON generates (invalid tagging):
mov x1, sp
mov x2, 0
.L4:
stg sp, [x1], #16
subs x2, x2, 16
bne .L4
IOW, a loop to tag memory block of size 0.
TBD:
- Any suggestions on any of the above will be helpful.
- Are the tests fittingly placed in gcc.target/aarch64 ? Suggestions
on other tests, how to add execution tests are also appreciated.
gcc/testsuite/ChangeLog:
* lib/target-supports.exp:
* gcc.target/aarch64/memtag/alloca-1.c: New test.
* gcc.target/aarch64/memtag/alloca-3.c: New test.
* gcc.target/aarch64/memtag/arguments-1.c: New test.
* gcc.target/aarch64/memtag/arguments-2.c: New test.
* gcc.target/aarch64/memtag/arguments-4.c: New test.
* gcc.target/aarch64/memtag/arguments.c: New test.
* gcc.target/aarch64/memtag/basic-1.c: New test.
* gcc.target/aarch64/memtag/basic-3.c: New test.
* gcc.target/aarch64/memtag/basic-struct.c: New test.
* gcc.target/aarch64/memtag/large-array.c: New test.
* gcc.target/aarch64/memtag/local-no-escape.c: New test.
* gcc.target/aarch64/memtag/memtag.exp: New test.
* gcc.target/aarch64/memtag/no-sanitize-attribute.c: New test.
* gcc.target/aarch64/memtag/vararray-gimple.c: New test.
* gcc.target/aarch64/memtag/vararray-nested-gimple.c: New test.
* gcc.target/aarch64/memtag/vararray.c: New test.
---
[Changes from RFC V1]
- Adjusted some tests as the code generation got bugfixes
- Add vararray-nested-gimple.c to show the current outstanding issue
with VLAs.
[End of changes from RFC V1]
---
.../gcc.target/aarch64/memtag/alloca-1.c | 14 ++++++++
.../gcc.target/aarch64/memtag/alloca-3.c | 27 ++++++++++++++++
.../gcc.target/aarch64/memtag/arguments-1.c | 3 ++
.../gcc.target/aarch64/memtag/arguments-2.c | 3 ++
.../gcc.target/aarch64/memtag/arguments-4.c | 16 ++++++++++
.../gcc.target/aarch64/memtag/arguments.c | 3 ++
.../gcc.target/aarch64/memtag/basic-1.c | 15 +++++++++
.../gcc.target/aarch64/memtag/basic-3.c | 18 +++++++++++
.../gcc.target/aarch64/memtag/basic-struct.c | 23 +++++++++++++
.../gcc.target/aarch64/memtag/large-array.c | 24 ++++++++++++++
.../aarch64/memtag/local-no-escape.c | 20 ++++++++++++
.../gcc.target/aarch64/memtag/memtag.exp | 32 +++++++++++++++++++
.../aarch64/memtag/no-sanitize-attribute.c | 17 ++++++++++
.../aarch64/memtag/vararray-gimple.c | 17 ++++++++++
.../aarch64/memtag/vararray-nested-gimple.c | 21 ++++++++++++
.../gcc.target/aarch64/memtag/vararray.c | 14 ++++++++
gcc/testsuite/lib/target-supports.exp | 12 +++++++
17 files changed, 279 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/large-array.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp
create mode 100644
gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c
create mode 100644
gcc/testsuite/gcc.target/aarch64/memtag/vararray-nested-gimple.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/vararray.c
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c
b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c
new file mode 100644
index 000000000000..370c8ee32b3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+
+extern int use (int *b);
+
+int foo (int n)
+{
+ int *b = __builtin_alloca (n);
+ int a = use (b);
+ return a;
+}
+
+/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */
+/* { dg-final { scan-assembler-times {\tstg\t} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c
b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c
new file mode 100644
index 000000000000..4b3aaafa9618
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+
+extern int use (int *b);
+
+extern int n1;
+extern int n2;
+extern int n3;
+
+int foo (void)
+{
+ int *b1 = __builtin_alloca (n1);
+ int *b2 = __builtin_alloca (n2);
+ int *b3 = __builtin_alloca (n3);
+ int a1 = use (b1);
+ int a2 = use (b2);
+ int a3 = use (b3);
+
+ return a1+a2+a3;
+}
+
+/* With HWASAN_ALLOCA_POISON now calling irg of its own, the number of
+ expected irg is 3 and stg is 4 (3 for tag, 1 for untag each in their
+ respective loop). */
+
+/* { dg-final { scan-assembler-times {\tirg\t} 3 } } */
+/* { dg-final { scan-assembler-times {\tstg\t} 4 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c
b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c
new file mode 100644
index 000000000000..8d1e18a761ac
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=kernel-hwaddress" } */
+/* { dg-error ".*'-fsanitize=memtag' is incompatible with
'-fsanitize=kernel-hwaddress'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c
b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c
new file mode 100644
index 000000000000..7650112dff9d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=kernel-address" } */
+/* { dg-error ".*'-fsanitize=memtag' is incompatible with
'-fsanitize=kernel-address'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c
b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c
new file mode 100644
index 000000000000..c5c64bd12454
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2 -fno-sanitize=memtag" } */
+
+int use (int * x);
+
+void foo (int n)
+{
+ int x = 99;
+ use (&x);
+}
+
+/* { dg-final { scan-assembler-not "irg" } } */
+/* { dg-final { scan-assembler-not "stg" } } */
+/* { dg-final { scan-assembler-not "st2g" } } */
+/* { dg-final { scan-assembler-not "subg" } } */
+/* { dg-final { scan-assembler-not "addg" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments.c
b/gcc/testsuite/gcc.target/aarch64/memtag/arguments.c
new file mode 100644
index 000000000000..83713496c4a6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fsanitize=address" } */
+/* { dg-error ".*'-fsanitize=memtag' is incompatible with
'-fsanitize=address'.*" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c
b/gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c
new file mode 100644
index 000000000000..70b790c6c3e7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+/* FIXME - scan-assembler-times-not subg ? */
+/* FIXME - generate stgp instead of stg + str ? */
+
+int use (int *x);
+
+void foo (int n)
+{
+ int x = 99;
+ use (&x);
+}
+
+/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */
+/* { dg-final { scan-assembler-times {\tstg\t} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c
b/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c
new file mode 100644
index 000000000000..0d86831f409e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+
+int use (int *x);
+
+void foo (int n)
+{
+ int a, b, c;
+ use(&a);
+ use(&b);
+ use(&c);
+}
+
+/* 3 stack vars need 48 bytes: 3 stg to color, 1 st2g + 1 stg to untag. */
+
+/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */
+/* { dg-final { scan-assembler-times {\tstg\t} 4 } } */
+/* { dg-final { scan-assembler-times {\tst2g\t} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c
b/gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c
new file mode 100644
index 000000000000..7751d109371e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+/* FIXME - scan-assembler-times stz2p ? */
+
+struct A
+{
+ long a;
+ long b;
+ long c;
+ long d;
+};
+
+extern void use (struct A *a);
+
+long f (void)
+{
+ struct A a = {0, 0, 64, (long)&a};
+ use (&a);
+ return a.b;
+}
+
+/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */
+/* { dg-final { scan-assembler-times {\tst2g\t} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/large-array.c
b/gcc/testsuite/gcc.target/aarch64/memtag/large-array.c
new file mode 100644
index 000000000000..b5b944f4633b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/large-array.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+/* FIXME - add other checks later. For now make sure this does not ICE. */
+
+#define ARRAY_LEN 12000
+
+int create (void);
+
+void sort (int *data, int n);
+
+void sort_array()
+{
+ int data[ARRAY_LEN], i;
+
+ for (i=0; i<ARRAY_LEN; ++i)
+ {
+ data[i] = create ();
+ }
+
+ sort(data, ARRAY_LEN);
+}
+
+/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */
+/* { dg-final { scan-assembler-times {\tst2g\t} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c
b/gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c
new file mode 100644
index 000000000000..173fe1135bf0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O0" } */
+
+/* FIXME - If x doesnt escape the function, why should MTE tagging be done for
+ x ? */
+
+extern int use (int *x);
+extern int bar (int *x);
+extern int baz (int *x);
+
+int a[10];
+
+int foo (int n)
+{
+ int x = use (a);
+ if (x)
+ return bar (a);
+ else
+ return baz (a);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp
b/gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp
new file mode 100644
index 000000000000..7f45fa99835d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp
@@ -0,0 +1,32 @@
+# Copyright (C) 2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+if [check_effective_target_aarch64_mte] {
+ dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
+ "" "-fsanitize=memtag -march=armv8.5-a+memtag"
+}
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c
b/gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c
new file mode 100644
index 000000000000..d8b448c43a35
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+
+int use (int *x);
+
+__attribute__((no_sanitize("memtag")))
+void foo (int n)
+{
+ int x = 99;
+ use (&x);
+}
+
+/* { dg-final { scan-assembler-not "irg" } } */
+/* { dg-final { scan-assembler-not "stg" } } */
+/* { dg-final { scan-assembler-not "st2g" } } */
+/* { dg-final { scan-assembler-not "subg" } } */
+/* { dg-final { scan-assembler-not "addg" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c
b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c
new file mode 100644
index 000000000000..c960048d6709
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-asan -O2" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
+
+extern int *use (int *b, int n);
+
+int *foo (int n)
+{
+ int b[n];
+ return use (b, n);
+}
+
+/* HWASAN_ALLOCA_POISON is used for alloca and VLAs when MEMTAG is in effect.
+ Although HWASAN_ALLOCA_UNPOISON is (also) used for untagging frame, it
+ doesnt hurt to check it in context of the current test. */
+/* { dg-final { scan-tree-dump "HWASAN_ALLOCA_POISON" "asan1" } } */
+/* { dg-final { scan-tree-dump "HWASAN_ALLOCA_UNPOISON" "asan1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/vararray-nested-gimple.c
b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-nested-gimple.c
new file mode 100644
index 000000000000..1d4447ae0d9f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-nested-gimple.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-asan -O2" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
+
+extern int *use (int *b, int n);
+
+int *foo (int n)
+{
+ int b[n];
+ {
+ int c[n];
+ }
+ return use (b, n);
+}
+
+/* FIXME this testcase shows an issue that there is a superfluous
+ .HWASAN_ALLOCA_UNPOISON for the inner scope, which is unnecessary.
+ The presence of the additional .HWASAN_ALLOCA_UNPOISON here is also
+ a correctness issue. */
+/* { dg-final { scan-tree-dump-times "HWASAN_ALLOCA_POISON" 1 "asan1" } } */
+/* { dg-final { scan-tree-dump-times "HWASAN_ALLOCA_UNPOISON" 1 "asan1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c
b/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c
new file mode 100644
index 000000000000..0dc3443a7896
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2" } */
+
+extern int *use (int *b, int n);
+
+int *foo (int n)
+{
+ int b[n];
+ return use (b, n);
+}
+
+/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */
+/* { dg-final { scan-assembler-times {\tstg\t} 3 } } */
+/* { dg-final { scan-assembler-times-not {\taddg\t}} } */
diff --git a/gcc/testsuite/lib/target-supports.exp
b/gcc/testsuite/lib/target-supports.exp
index a62f459ad7ed..b59afc9bd762 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -4870,6 +4870,18 @@ proc check_effective_target_aarch64_sve1_only { } {
&& ![check_effective_target_aarch64_sve2] }]
}
+# Return 1 if this is an AArch64 target supporting MTE.
+# FIXME what is aarch64_mte. more stubs needed ?
+proc check_effective_target_aarch64_mte { } {
+ if { ![istarget aarch64*-*-*] } {
+ return 0
+ }
+ return [check_no_compiler_messages aarch64_mte assembly {
+ #if !defined (__ARM_FEATURE_MEMORY_TAGGING)
+ #error FOO
+ #endif
+ } "-march=armv8.5-a+memtag"]
+}
# Return the size in bits of an SVE vector, or 0 if the size is variable.
proc aarch64_sve_bits { } {
return [check_cached_effective_target aarch64_sve_bits {
--
2.43.0