|
10 | 10 | ExpressionBundle, FindSymbols, FindNodes, ParallelIteration, |
11 | 11 | ParallelTree, Pragma, Prodder, Transfer, List, Transformer, |
12 | 12 | IsPerfectIteration, OpInc, filter_iterations, ccode, |
13 | | - retrieve_iteration_tree, IMask, VECTORIZED) |
| 13 | + retrieve_iteration_tree, IMask, VECTORIZED, Iteration) |
14 | 14 | from devito.passes.iet.engine import iet_pass |
15 | 15 | from devito.passes.iet.langbase import (LangBB, LangTransformer, DeviceAwareMixin, |
16 | 16 | ShmTransformer, make_sections_from_imask) |
@@ -47,15 +47,106 @@ def simd_reg_nbytes(self): |
47 | 47 | return self.platform.simd_reg_nbytes |
48 | 48 |
|
49 | 49 | def _make_simd_pragma(self, iet): |
| 50 | + """ |
| 51 | + Generate SIMD pragma with appropriate clauses. |
| 52 | + |
| 53 | + For GCC, when dealing with non-canonical loop forms (multiple index variables), |
| 54 | + we need to add the 'linear' clause to avoid compilation errors. |
| 55 | + This typically happens with loop blocking when blockinner=True. |
| 56 | + """ |
50 | 57 | indexeds = FindSymbols('indexeds').visit(iet) |
51 | 58 | aligned = {i.base for i in indexeds if i.function.is_DiscreteFunction} |
52 | | - if aligned: |
| 59 | + |
| 60 | + # Check if we need to add linear clause for GCC compatibility |
| 61 | + needs_linear = self._needs_linear_clause(iet) |
| 62 | + linear_vars = self._get_linear_variables(iet) if needs_linear else [] |
| 63 | + |
| 64 | + if aligned and linear_vars: |
| 65 | + # Both aligned and linear clauses needed |
| 66 | + simd = self.langbb['simd-for-aligned-linear'] |
| 67 | + aligned_str = ','.join(str(v) for v in aligned) |
| 68 | + linear_str = ','.join(str(v) for v in linear_vars) |
| 69 | + simd = as_tuple(simd(self.simd_reg_nbytes, aligned_str, linear_str)) |
| 70 | + elif aligned: |
| 71 | + # Only aligned clause needed |
53 | 72 | simd = self.langbb['simd-for-aligned'] |
54 | 73 | simd = as_tuple(simd(self.simd_reg_nbytes, *aligned)) |
| 74 | + elif linear_vars: |
| 75 | + # Only linear clause needed |
| 76 | + simd = self.langbb['simd-for-linear'] |
| 77 | + simd = as_tuple(simd(*linear_vars)) |
55 | 78 | else: |
| 79 | + # Basic SIMD pragma |
56 | 80 | simd = as_tuple(self.langbb['simd-for']) |
57 | 81 |
|
58 | 82 | return simd |
| 83 | + |
| 84 | + def _needs_linear_clause(self, iet): |
| 85 | + """ |
| 86 | + Check if we need to add linear clause for GCC compatibility. |
| 87 | + |
| 88 | + This is needed when: |
| 89 | + 1. Using GCC compiler (not ICC) |
| 90 | + 2. We have nested loops with potential canonical form issues |
| 91 | + 3. GCC version supports OpenMP 4.0+ (>= 4.9) |
| 92 | + """ |
| 93 | + from devito.arch.compiler import GNUCompiler |
| 94 | + from packaging.version import Version |
| 95 | + |
| 96 | + # Only apply for GCC |
| 97 | + if not isinstance(self.compiler, GNUCompiler): |
| 98 | + return False |
| 99 | + |
| 100 | + # Only for GCC 4.9+ that supports OpenMP 4.0 |
| 101 | + try: |
| 102 | + if self.compiler.version < Version("4.9.0"): |
| 103 | + return False |
| 104 | + except (TypeError, ValueError): |
| 105 | + # If we can't determine version, assume it's recent enough |
| 106 | + pass |
| 107 | + |
| 108 | + # Check if we have nested iterations that could cause canonical form issues |
| 109 | + # This is a more conservative approach - we add linear clauses when we detect |
| 110 | + # potential issues with loop structure |
| 111 | + all_iterations = FindNodes(Iteration).visit(iet) |
| 112 | + |
| 113 | + # Look for patterns that suggest complex loop nesting from blocking |
| 114 | + nested_levels = 0 |
| 115 | + has_block_vars = False |
| 116 | + |
| 117 | + for iteration in all_iterations: |
| 118 | + nested_levels += 1 |
| 119 | + # Check for block-like dimension naming patterns |
| 120 | + if hasattr(iteration, 'dim') and iteration.dim: |
| 121 | + dim_name = str(iteration.dim) |
| 122 | + if 'blk' in dim_name or hasattr(iteration.dim, 'is_Block'): |
| 123 | + has_block_vars = True |
| 124 | + |
| 125 | + # If we have deeply nested loops (3+) with potential blocking, |
| 126 | + # we likely need linear clauses |
| 127 | + return nested_levels >= 3 and has_block_vars |
| 128 | + |
| 129 | + def _get_linear_variables(self, iet): |
| 130 | + """ |
| 131 | + Extract variables that should be declared linear in SIMD loops. |
| 132 | + |
| 133 | + For nested loops with blocking, we need to identify variables that |
| 134 | + change linearly with the iteration count. |
| 135 | + """ |
| 136 | + linear_vars = [] |
| 137 | + iterations = FindNodes(Iteration).visit(iet) |
| 138 | + |
| 139 | + for iteration in iterations: |
| 140 | + if hasattr(iteration, 'dim') and iteration.dim: |
| 141 | + dim_name = str(iteration.dim) |
| 142 | + # Add variables that look like block dimensions or nested indices |
| 143 | + if ('blk' in dim_name or |
| 144 | + hasattr(iteration.dim, 'is_Block') or |
| 145 | + # Also add common index variable patterns that might cause issues |
| 146 | + dim_name in ['i', 'j', 'k', 'ii', 'jj', 'kk']): |
| 147 | + linear_vars.append(dim_name) |
| 148 | + |
| 149 | + return list(set(linear_vars)) # Remove duplicates |
59 | 150 |
|
60 | 151 | def _make_simd(self, iet): |
61 | 152 | """ |
|
0 commit comments