char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Kevin Pougetggml-virtgpu: improve the reliability of the code (#19846)ffaafde16

char/aliceai-t5-moe
12.9 KiB333 linesraw
1#!/usr/bin/env python3
2"""
3# Generated by Claude AI
4
5Script to completely regenerate the GGML remoting codebase from YAML configuration.
6
7This script reads api_functions.yaml and regenerates all the header files and
8implementation templates for the GGML remoting layer.
9
10Usage:
11  python regenerate_remoting.py
12
13The script will:
141. Read ggmlremoting_functions.yaml configuration
152. Generate updated header files
163. Generate implementation templates in dedicated files
174. Show a summary of what was generated
18"""
19
20import yaml
21from typing import Dict, List, Any
22from pathlib import Path
23import os
24import subprocess
25import shutil
26import logging
27
28NL = '\n' # can't have f"{'\n'}" in f-strings
29
30
31class RemotingCodebaseGenerator:
32    def __init__(self, yaml_path: str = "ggmlremoting_functions.yaml"):
33        """Initialize the generator with the YAML configuration."""
34        self.yaml_path = yaml_path
35
36        if not Path(yaml_path).exists():
37            raise FileNotFoundError(f"Configuration file {yaml_path} not found")
38
39        with open(yaml_path, 'r') as f:
40            self.config = yaml.safe_load(f)
41
42        self.functions = self.config['functions']
43        self.naming_patterns = self.config['naming_patterns']
44        self.config_data = self.config['config']
45
46        # Check if clang-format is available
47        self.clang_format_available = self._check_clang_format_available()
48
49    def _check_clang_format_available(self) -> bool:
50        """Check if clang-format is available in the system PATH."""
51        return shutil.which("clang-format") is not None
52
53    def _format_file_with_clang_format(self, file_path: Path) -> bool:
54        """Format a file with clang-format -i. Returns True if successful, False otherwise."""
55        if not self.clang_format_available:
56            return False
57
58        try:
59            subprocess.run(
60                ["clang-format", "-i", str(file_path)],
61                check=True,
62                capture_output=True,
63                text=True
64            )
65            return True
66        except subprocess.CalledProcessError:
67            logging.exception(f"   ⚠️  clang-format failed for {file_path}")
68            return False
69        except Exception as e:
70            logging.exception(f"   ⚠️  Unexpected error formatting {file_path}: {e}")
71            return False
72
73    def generate_enum_name(self, group_name: str, function_name: str) -> str:
74        """Generate the APIR_COMMAND_TYPE enum name for a function."""
75        prefix = self.naming_patterns['enum_prefix']
76        return f"{prefix}{group_name.upper()}_{function_name.upper()}"
77
78    def generate_backend_function_name(self, group_name: str, function_name: str) -> str:
79        """Generate the backend function name."""
80        function_key = f"{group_name}_{function_name}"
81        overrides = self.naming_patterns.get('backend_function_overrides', {})
82
83        if function_key in overrides:
84            return overrides[function_key]
85
86        prefix = self.naming_patterns['backend_function_prefix']
87        return f"{prefix}{group_name}_{function_name}"
88
89    def generate_frontend_function_name(self, group_name: str, function_name: str) -> str:
90        """Generate the frontend function name."""
91        prefix = self.naming_patterns['frontend_function_prefix']
92        return f"{prefix}{group_name}_{function_name}"
93
94    def get_enabled_functions(self) -> List[Dict[str, Any]]:
95        """Get all enabled functions with their metadata."""
96        functions = []
97        enum_value = 0
98
99        for group_name, group_data in self.functions.items():
100            group_description = group_data['group_description']
101
102            for function_name, func_metadata in group_data['functions'].items():
103                # Handle case where func_metadata is None or empty (functions with only comments)
104                if func_metadata is None:
105                    func_metadata = {}
106
107                # Functions are enabled by default unless explicitly disabled
108                if func_metadata.get('enabled', True):
109                    functions.append({
110                        'group_name': group_name,
111                        'function_name': function_name,
112                        'enum_name': self.generate_enum_name(group_name, function_name),
113                        'enum_value': enum_value,
114                        'backend_function': self.generate_backend_function_name(group_name, function_name),
115                        'frontend_function': self.generate_frontend_function_name(group_name, function_name),
116                        'frontend_return': func_metadata.get('frontend_return', 'void'),
117                        'frontend_extra_params': func_metadata.get('frontend_extra_params', []),
118                        'group_description': group_description,
119                        'deprecated': func_metadata.get('deprecated', False),
120                    })
121                    enum_value += 1
122
123        return functions
124
125    def generate_apir_backend_header(self) -> str:
126        """Generate the complete apir_backend.h file."""
127        functions = self.get_enabled_functions()
128
129        # Generate the enum section
130        enum_lines = ["typedef enum ApirBackendCommandType {"]
131        current_group = None
132
133        for func in functions:
134            # Add comment for new group
135            if func['group_name'] != current_group:
136                enum_lines.append("")
137                enum_lines.append(f"  /* {func['group_description']} */")
138                current_group = func['group_name']
139
140            enum_lines.append(f"  {func['enum_name']} = {func['enum_value']},")
141
142        # Add the count
143        total_count = len(functions)
144        enum_lines.append("\n  // last command_type index + 1")
145        enum_lines.append(f"  APIR_BACKEND_DISPATCH_TABLE_COUNT = {total_count},")
146        enum_lines.append("} ApirBackendCommandType;")
147
148        # Generate function name mapping
149        func_lines = []
150        func_lines.append("static inline const char * apir_dispatch_command_name(ApirBackendCommandType type) {")
151        func_lines.append("    switch (type) {")
152
153        current_group = None
154        for func in functions:
155            # Add comment for new group
156            if func['group_name'] != current_group:
157                func_lines.append(f"        /* {func['group_description']} */")
158                current_group = func['group_name']
159
160            # Generate clean function name without backend_ prefix
161            clean_name = f"{func['group_name']}_{func['function_name']}"
162            func_lines.append(f"        case {func['enum_name']}:")
163            func_lines.append(f"            return \"{clean_name}\";")
164
165        func_lines.append("")
166        func_lines.append("        default:")
167        func_lines.append("            return \"unknown\";")
168        func_lines.append("    }")
169        func_lines.append("}")
170
171        # Full header template
172        header_content = NL.join(enum_lines) + "\n\n" + NL.join(func_lines) + "\n"
173
174        return header_content
175
176    def generate_backend_dispatched_header(self) -> str:
177        """Generate the complete backend-dispatched.h file."""
178        functions = self.get_enabled_functions()
179
180        # Function declarations
181        decl_lines = []
182        current_group = None
183
184        for func in functions:
185            if func['group_name'] != current_group:
186                decl_lines.append(f"\n/* {func['group_description']} */")
187                current_group = func['group_name']
188
189            signature = "uint32_t"
190            params = "apir_encoder *enc, apir_decoder *dec, virgl_apir_context *ctx"
191            if func['deprecated']:
192                decl_lines.append(f"/* {func['enum_name']} is deprecated. Keeping the handler for backward compatibility. */")
193
194            decl_lines.append(f"{signature} {func['backend_function']}({params});")
195
196        # Dispatch table
197        table_lines = []
198        current_group = None
199
200        for func in functions:
201            if func['group_name'] != current_group:
202                table_lines.append(f"\n  /* {func['group_description']} */")
203                table_lines.append("")
204                current_group = func['group_name']
205
206            deprecated = " /* DEPRECATED */" if func['deprecated'] else ""
207            table_lines.append(f"  /* {func['enum_name']}  = */ {func['backend_function']}{deprecated},")
208
209        header_content = f'''\
210#pragma once
211
212{NL.join(decl_lines)}
213
214extern "C" {{
215static const backend_dispatch_t apir_backend_dispatch_table[APIR_BACKEND_DISPATCH_TABLE_COUNT] = {{
216  {NL.join(table_lines)}
217}};
218}}
219'''
220        return header_content
221
222    def generate_virtgpu_forward_header(self) -> str:
223        """Generate the complete virtgpu-forward.gen.h file."""
224        functions = self.get_enabled_functions()
225
226        decl_lines = []
227        current_group = None
228
229        for func in functions:
230            if func['group_name'] != current_group:
231                decl_lines.append("")
232                decl_lines.append(f"/* {func['group_description']} */")
233                current_group = func['group_name']
234
235            if func['deprecated']:
236                decl_lines.append(f"/* {func['frontend_function']} is deprecated. */")
237                continue
238
239            # Build parameter list
240            params = [self.naming_patterns['frontend_base_param']]
241            params.extend(func['frontend_extra_params'])
242            param_str = ', '.join(params)
243
244            decl_lines.append(f"{func['frontend_return']} {func['frontend_function']}({param_str});")
245
246        header_content = f'''\
247#pragma once
248{NL.join(decl_lines)}
249'''
250        return header_content
251
252    def regenerate_codebase(self) -> None:
253        """Regenerate the entire remoting codebase."""
254        logging.info("🔄 Regenerating GGML Remoting Codebase...")
255        logging.info("=" * 50)
256
257        # Detect if we're running from frontend directory
258        current_dir = os.getcwd()
259        is_frontend_dir = current_dir.endswith('ggml-virtgpu')
260
261        if is_frontend_dir:
262            # Running from ggml/src/ggml-virtgpu-apir
263            logging.info("📍 Detected frontend directory execution")
264            frontend_base = Path(".")
265        else:
266            # Running from project root (fallback to original behavior)
267            logging.info("📍 Detected project root execution")
268            base_path = self.config_data.get('base_path', 'ggml/src')
269            frontend_base = Path(base_path) / "ggml-virtgpu"
270
271        # Compute final file paths
272        backend_base = frontend_base / "backend"
273        apir_backend_path = backend_base / "shared" / "apir_backend.gen.h"
274        backend_dispatched_path = backend_base / "backend-dispatched.gen.h"
275        virtgpu_forward_path = frontend_base / "virtgpu-forward.gen.h"
276
277        # Create output directories for each file
278        apir_backend_path.parent.mkdir(parents=True, exist_ok=True)
279        backend_dispatched_path.parent.mkdir(parents=True, exist_ok=True)
280        virtgpu_forward_path.parent.mkdir(parents=True, exist_ok=True)
281
282        # Generate header files
283        logging.info("📁 Generating header files...")
284
285        apir_backend_content = self.generate_apir_backend_header()
286        apir_backend_path.write_text(apir_backend_content)
287        logging.info(f"   ✅ {apir_backend_path.resolve()}")
288
289        backend_dispatched_content = self.generate_backend_dispatched_header()
290        backend_dispatched_path.write_text(backend_dispatched_content)
291        logging.info(f"   ✅ {backend_dispatched_path.resolve()}")
292
293        virtgpu_forward_content = self.generate_virtgpu_forward_header()
294        virtgpu_forward_path.write_text(virtgpu_forward_content)
295        logging.info(f"   ✅ {virtgpu_forward_path.resolve()}")
296
297        # Format generated files with clang-format
298        generated_files = [apir_backend_path, backend_dispatched_path, virtgpu_forward_path]
299
300        if not self.clang_format_available:
301            logging.warning("\n⚠️clang-format not found in PATH. Generated files will not be formatted.\n"
302                            "   Install clang-format to enable automatic code formatting.")
303        else:
304            logging.info("\n🎨 Formatting files with clang-format...")
305            for file_path in generated_files:
306                if self._format_file_with_clang_format(file_path):
307                    logging.info(f"   ✅ Formatted {file_path.name}")
308                else:
309                    logging.warning(f"   ❌ Failed to format {file_path.name}")
310
311        # Generate summary
312        functions = self.get_enabled_functions()
313        total_functions = len(functions)
314
315        logging.info("\n📊 Generation Summary:")
316        logging.info("=" * 50)
317        logging.info(f"   Total functions: {total_functions}")
318        logging.info(f"   Function groups: {len(self.functions)}")
319        logging.info("   Header files: 3")
320        logging.info(f"   Working directory: {current_dir}")
321
322
323def main():
324    try:
325        generator = RemotingCodebaseGenerator()
326        generator.regenerate_codebase()
327    except Exception as e:
328        logging.exception(f"❌ Error: {e}")
329        exit(1)
330
331
332if __name__ == "__main__":
333    main()