xref: /libCEED/interface/ceed-jit-tools.c (revision cea28146778a1ecd9478382de402f01e7a6eddf9)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed/jit-tools.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <string.h>
23 
24 /**
25   @brief Load source file into initalized string buffer, including full text
26            of local files in place of `#include "local.h"`
27 
28   @param ceed                   A Ceed object for error handling
29   @param[in]  source_file_path  Absolute path to source file
30   @param[out] buffer            String buffer for source file contents
31 
32   @return An error code: 0 - success, otherwise - failure
33 
34   @ref Backend
35 **/
36 static inline int CeedLoadSourceToInitalizedBuffer(Ceed ceed,
37     const char *source_file_path, char **buffer) {
38   int ierr;
39   FILE *source_file;
40   long file_size, file_offset = 0;
41   char *temp_buffer;
42 
43   // Debug
44   CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n");
45   CeedDebug256(ceed, 1, "Current source file: ");
46   CeedDebug256(ceed, 255, "%s\n", source_file_path);
47   CeedDebug256(ceed, 1, "Current buffer:\n");
48   CeedDebug256(ceed, 255, "%s\n", *buffer);
49 
50   // Read file to temporary buffer
51   source_file = fopen(source_file_path, "rb");
52   if (!source_file)
53     // LCOV_EXCL_START
54     return CeedError(ceed, CEED_ERROR_MAJOR, "Couldn't open source file: %s",
55                      source_file_path);
56   // LCOV_EXCL_STOP
57   // -- Compute size of source
58   fseek(source_file, 0L, SEEK_END);
59   file_size = ftell(source_file);
60   rewind(source_file);
61   //  -- Allocate memory for entire source file
62   ierr = CeedCalloc(file_size + 1, &temp_buffer); CeedChk(ierr);
63   // -- Copy the file into the buffer
64   if (1 != fread(temp_buffer, file_size, 1, source_file)) {
65     // LCOV_EXCL_START
66     fclose(source_file);
67     ierr = CeedFree(&temp_buffer); CeedChk(ierr);
68     return CeedError(ceed, CEED_ERROR_MAJOR, "Couldn't read source file: %s",
69                      source_file_path);
70     // LCOV_EXCL_STOP
71   }
72   fclose(source_file);
73 
74   // Search for headers to include
75   const char *first_hash = strchr(temp_buffer, '#');
76   while (first_hash) {
77     // -- Check for 'include' keyword
78     const char *next_e = strchr(first_hash, 'e');
79     char keyword[8] = "";
80     if (next_e)
81       strncpy(keyword, &next_e[-6], 7);
82     bool is_hash_include = !strcmp(keyword, "include");
83     // ---- Spaces allowed in '#  include <header.h>'
84     if (next_e)
85       for (CeedInt i = 1; first_hash - next_e + i < -6; i++)
86         is_hash_include &= first_hash[i] == ' ';
87     if (is_hash_include) {
88       // -- Copy into buffer all preceding #
89       long current_size = strlen(*buffer);
90       long copy_size = first_hash - &temp_buffer[file_offset];
91       ierr = CeedRealloc(current_size + copy_size + 2, buffer); CeedChk(ierr);
92       strncpy(&(*buffer)[current_size], "\n", 2);
93       strncpy(&(*buffer)[current_size + 1], &temp_buffer[file_offset], copy_size);
94       strncpy(&(*buffer)[current_size + copy_size], "", 1);
95       // -- Load local "header.h"
96       char *next_quote = strchr(first_hash, '"');
97       char *next_new_line = strchr(first_hash, '\n');
98       bool is_local_header = is_hash_include && next_quote
99                              && (next_new_line - next_quote > 0);
100       if (is_local_header) {
101         // ---- Build source path
102         char *include_source_path;
103         long root_length = strrchr(source_file_path, '/') - source_file_path;
104         long include_file_name_len = strchr(&next_quote[1], '"') - next_quote - 1;
105         ierr = CeedCalloc(root_length + include_file_name_len + 2,
106                           &include_source_path); CeedChk(ierr);
107         strncpy(include_source_path, source_file_path, root_length + 1);
108         strncpy(&include_source_path[root_length + 1], &next_quote[1],
109                 include_file_name_len);
110         strncpy(&include_source_path[root_length + include_file_name_len + 1], "", 1);
111         // ---- Recursive call to load source to buffer
112         ierr = CeedLoadSourceToInitalizedBuffer(ceed, include_source_path, buffer);
113         CeedChk(ierr);
114         ierr = CeedFree(&include_source_path); CeedChk(ierr);
115       }
116       file_offset = strchr(first_hash, '\n') - temp_buffer + 1;
117     }
118     // -- Next hash
119     first_hash = strchr(&first_hash[1], '#');
120   }
121   // Copy rest of source file into buffer
122   long current_size = strlen(*buffer);
123   long copy_size = strlen(&temp_buffer[file_offset]);
124   ierr = CeedRealloc(current_size + copy_size + 2, buffer); CeedChk(ierr);
125   strncpy(&(*buffer)[current_size], "\n", 2);
126   strncpy(&(*buffer)[current_size + 1], &temp_buffer[file_offset], copy_size);
127   strncpy(&(*buffer)[current_size + copy_size + 1], "", 1);
128 
129   // Cleanup
130   ierr = CeedFree(&temp_buffer); CeedChk(ierr);
131 
132   // Debug
133   CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n");
134   CeedDebug256(ceed, 1, "Current source file: ");
135   CeedDebug256(ceed, 255, "%s\n", source_file_path);
136   CeedDebug256(ceed, 1, "Final buffer:\n");
137   CeedDebug256(ceed, 255, "%s\n", *buffer);
138 
139   return CEED_ERROR_SUCCESS;
140 }
141 
142 /**
143   @brief Initalize and load source file into string buffer, including full text
144            of local files in place of `#include "local.h"`.
145          Note: Caller is responsible for freeing the string buffer with `CeedFree()`.
146 
147   @param ceed                   A Ceed object for error handling
148   @param[in]  source_file_path  Absolute path to source file
149   @param[out] buffer            String buffer for source file contents
150 
151   @return An error code: 0 - success, otherwise - failure
152 
153   @ref Backend
154 **/
155 int CeedLoadSourceToBuffer(Ceed ceed, const char *source_file_path,
156                            char **buffer) {
157   int ierr;
158 
159   // Initalize buffer
160   ierr = CeedCalloc(1, buffer); CeedChk(ierr);
161 
162   // Load to initalized buffer
163   ierr = CeedLoadSourceToInitalizedBuffer(ceed, source_file_path, buffer);
164   CeedChk(ierr);
165 
166   return CEED_ERROR_SUCCESS;
167 }
168