d: Inline bounds checking for simple array assignments.
authorIain Buclaw <ibuclaw@gdcproject.org>
Sun, 19 Jul 2020 13:18:08 +0000 (15:18 +0200)
committerIain Buclaw <ibuclaw@gdcproject.org>
Thu, 30 Jul 2020 16:02:59 +0000 (18:02 +0200)
This optimizes the code generation of simple array assignments, inlining
the array bounds checking code so there is no reliance on the library
routine _d_arraycopy(), which also deals with postblit and copy
constructors for non-trivial arrays.

gcc/d/ChangeLog:

* expr.cc (ExprVisitor::visit (AssignExp *)): Inline bounds checking
for simple array assignments.

gcc/testsuite/ChangeLog:

* gdc.dg/array1.d: New test.

gcc/d/expr.cc
gcc/testsuite/gdc.dg/array1.d [new file with mode: 0644]

index 58d4943ef2b74e9df479354a298ff3a2d948f5f9..355561a481ed003f2351c46170784fb9c066d933 100644 (file)
@@ -962,14 +962,47 @@ public:
            /* Perform a memcpy operation.  */
            gcc_assert (e->e2->type->ty != Tpointer);
 
-           if (!postblit && !destructor && !array_bounds_check ())
+           if (!postblit && !destructor)
              {
                tree t1 = d_save_expr (d_array_convert (e->e1));
-               tree t2 = d_array_convert (e->e2);
-               tree size = size_mult_expr (d_array_length (t1),
-                                           size_int (etype->size ()));
-               tree result = build_memcpy_call (d_array_ptr (t1),
-                                                d_array_ptr (t2), size);
+               tree t2 = d_save_expr (d_array_convert (e->e2));
+
+               /* References to array data.  */
+               tree t1ptr = d_array_ptr (t1);
+               tree t1len = d_array_length (t1);
+               tree t2ptr = d_array_ptr (t2);
+
+               /* Generate: memcpy(to, from, size)  */
+               tree size = size_mult_expr (t1len, size_int (etype->size ()));
+               tree result = build_memcpy_call (t1ptr, t2ptr, size);
+
+               /* Insert check that array lengths match and do not overlap.  */
+               if (array_bounds_check ())
+                 {
+                   /* tlencmp = (t1len == t2len)  */
+                   tree t2len = d_array_length (t2);
+                   tree tlencmp = build_boolop (EQ_EXPR, t1len, t2len);
+
+                   /* toverlap = (t1ptr + size <= t2ptr
+                                  || t2ptr + size <= t1ptr)  */
+                   tree t1ptrcmp = build_boolop (LE_EXPR,
+                                                 build_offset (t1ptr, size),
+                                                 t2ptr);
+                   tree t2ptrcmp = build_boolop (LE_EXPR,
+                                                 build_offset (t2ptr, size),
+                                                 t1ptr);
+                   tree toverlap = build_boolop (TRUTH_ORIF_EXPR, t1ptrcmp,
+                                                 t2ptrcmp);
+
+                   /* (tlencmp && toverlap) ? memcpy() : _d_arraybounds()  */
+                   tree tassert = build_array_bounds_call (e->loc);
+                   tree tboundscheck = build_boolop (TRUTH_ANDIF_EXPR,
+                                                     tlencmp, toverlap);
+
+                   result = build_condition (void_type_node, tboundscheck,
+                                             result, tassert);
+                 }
+
                this->result_ = compound_expr (result, t1);
              }
            else if ((postblit || destructor) && e->op != TOKblit)
diff --git a/gcc/testsuite/gdc.dg/array1.d b/gcc/testsuite/gdc.dg/array1.d
new file mode 100644 (file)
index 0000000..af81813
--- /dev/null
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-final { scan-assembler-not "_d_arraycopy" } }
+
+void test1()
+{
+    int[10] a1 = void;
+    int[10] a2 = void;
+    a1[] = a2[];
+}
+
+void test2(int[] a1, int[] a2)
+{
+    a1[] = a2[];
+}