2 Copyright (C) 2023-2025 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
20 #define INCLUDE_ALGORITHM
21 #define INCLUDE_STRING
24 #define INCLUDE_VECTOR
28 #include "lto-ltrans-cache.h"
30 static const checksum_t NULL_CHECKSUM
= {
31 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35 /* Computes checksum for given file, returns NULL_CHECKSUM if not
38 file_checksum (char const *filename
)
40 FILE *file
= fopen (filename
, "rb");
45 checksum_t result
= NULL_CHECKSUM
;
47 int ret
= sha1_stream (file
, &result
);
50 result
= NULL_CHECKSUM
;
57 /* Checks identity of two files. */
59 files_identical (char const *first_filename
, char const *second_filename
)
65 if (stat (first_filename
, &st
) < 0 || !S_ISREG (st
.st_mode
))
67 size_t len
= st
.st_size
;
68 if (stat (second_filename
, &st
) < 0 || !S_ISREG (st
.st_mode
))
70 if (len
!= (size_t) st
.st_size
)
74 void *map
[2] = { NULL
, NULL
};
76 fd
= open (first_filename
, O_RDONLY
);
79 map
[0] = mmap (NULL
, len
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
81 if (map
[0] == MAP_FAILED
)
84 fd
= open (second_filename
, O_RDONLY
);
87 map
[1] = mmap (NULL
, len
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
89 if (map
[1] == MAP_FAILED
)
92 ret
= memcmp (map
[0], map
[1], len
) == 0;
104 FILE *f_first
= fopen (first_filename
, "rb");
108 FILE *f_second
= fopen (second_filename
, "rb");
115 const size_t buffer_len
= 64 * 1024;
116 uint8_t *buffer1
= (uint8_t*)xmalloc (buffer_len
);
117 uint8_t *buffer2
= (uint8_t*)xmalloc (buffer_len
);
123 len1
= fread (buffer1
, 1, buffer_len
, f_first
);
124 len2
= fread (buffer2
, 1, buffer_len
, f_second
);
126 if (len1
!= len2
|| memcmp (buffer1
, buffer2
, len1
) != 0)
141 /* Contructor of cache item. */
142 ltrans_file_cache::item::item (std::string input
, std::string output
,
143 checksum_t input_checksum
,
145 input (std::move (input
)), output (std::move (output
)),
146 input_checksum (input_checksum
), last_used (last_used
)
148 lock
= lockfile (this->input
+ ".lock");
150 /* Destructor of cache item. */
151 ltrans_file_cache::item::~item ()
156 /* Reads next cache item from cachedata file.
157 Adds `dir/` prefix to filenames. */
158 static ltrans_file_cache::item
*
159 read_cache_item (FILE* f
, const char* dir
)
164 if (fread (&checksum
, 1, checksum
.size (), f
) != checksum
.size ())
166 if (fread (&last_used
, sizeof (last_used
), 1, f
) != 1)
169 std::string
input (dir
);
170 input
.push_back ('/');
171 std::string output
= input
; /* Copy. */
174 while ((c
= getc (f
)))
180 while ((c
= getc (f
)))
184 output
.push_back (c
);
187 return new ltrans_file_cache::item (&input
[0], &output
[0], checksum
,
191 /* Writes cache item to cachedata file.
192 Removes `dir/` prefix from filenames. */
194 write_cache_item (FILE* f
, ltrans_file_cache::item
*item
, const char* dir
)
196 fwrite (&item
->input_checksum
, 1, item
->input_checksum
.size (), f
);
197 fwrite (&item
->last_used
, sizeof (item
->last_used
), 1, f
);
199 gcc_assert (item
->input
.size () > strlen (dir
));
200 fputs (item
->input
.c_str () + strlen (dir
) + 1, f
);
203 gcc_assert (item
->output
.size () > strlen (dir
));
204 fputs (item
->output
.c_str () + strlen (dir
) + 1, f
);
208 /* Constructor. Resulting cache item filenames will be
209 in format `prefix%d[.ltrans]suffix`. */
210 ltrans_file_cache::ltrans_file_cache (const char* dir
, const char* prefix
,
212 size_t soft_cache_size
):
213 dir (dir
), prefix (prefix
), suffix (suffix
),
214 soft_cache_size (soft_cache_size
)
218 creation_lock
= lockfile (std::string (dir
) + "/lockfile_creation");
219 deletion_lock
= lockfile (std::string (dir
) + "/lockfile_deletion");
221 cache_prefix
= std::string (dir
) + "/" + prefix
;
224 str_buffer
= (char *)xmalloc (cache_prefix
.size ()
225 + sizeof ("4294967295.ltrans")
230 ltrans_file_cache::~ltrans_file_cache ()
239 /* Adds given cache item to all relevant datastructures. */
241 ltrans_file_cache::add_item (ltrans_file_cache::item
* item
)
243 items
.push_back (item
);
244 map_checksum
[item
->input_checksum
] = item
;
245 map_input
[item
->input
] = item
;
247 usage_counter
= std::max (usage_counter
, item
->last_used
);
250 /* Creates cachedata filename for save/load. */
252 ltrans_file_cache::filename_cachedata ()
254 return std::string (dir
) + "/cachedata";
257 /* Loads data about previously cached items from cachedata file.
258 Sorts items by last_used and remaps last_used to small integers.
260 Must be called with creation_lock or deletion_lock held to
261 prevent data race. */
263 ltrans_file_cache::load_cache ()
267 std::string filename
= filename_cachedata ();
268 FILE *file
= fopen (filename
.c_str (), "rb");
273 ltrans_file_cache::item
* _item
;
274 while ((_item
= read_cache_item (file
, dir
)))
279 std::sort (items
.begin (), items
.end (),
281 {return a
->last_used
< b
->last_used
;});
283 for (size_t i
= 0; i
< items
.size (); ++i
)
284 items
[i
]->last_used
= (uint32_t) i
;
285 usage_counter
= (uint32_t) items
.size ();
288 /* Rewrites data about cache items into cachedata file.
290 Must be only called when creation_lock or deletion_lock was held since last
291 call to load_cache. */
293 ltrans_file_cache::save_cache ()
295 std::string filename
= filename_cachedata ();
296 FILE *file
= fopen (filename
.c_str (), "wb");
301 for (item
* _item
: items
)
302 write_cache_item (file
, _item
, dir
);
307 /* Creates new cache item with given checksum.
308 New input/output files are chosen to not collide with other items.
310 Must be called with creation_lock held to prevent data race. */
311 ltrans_file_cache::item
*
312 ltrans_file_cache::create_item (const checksum_t
& checksum
)
314 size_t prefix_len
= cache_prefix
.size ();
316 strcpy (str_buffer
, cache_prefix
.c_str ());
320 sprintf (str_buffer
+ prefix_len
, "%04u%s", cache_free_idx
, suffix
);
322 if (map_input
.find (str_buffer
) == map_input
.end ())
327 std::string input
= str_buffer
;
329 sprintf (str_buffer
+ prefix_len
, "%04u.ltrans%s", cache_free_idx
, suffix
);
331 return new item (std::move (input
), str_buffer
, checksum
, usage_counter
++);
334 /* Adds input file into cache. Cache item with input file identical to
335 added input file will be returned as _item.
336 If the file was already cached, `true` is returned, `false` otherwise.
337 The added input file is deleted (or moved).
339 Must be called with creation_lock held to prevent data race. */
341 ltrans_file_cache::add_to_cache (const char* filename
, item
*& _item
)
343 checksum_t checksum
= file_checksum (filename
);
345 auto it
= map_checksum
.find (checksum
);
347 if (it
!= map_checksum
.end ()
348 && files_identical (filename
, it
->second
->input
.c_str ()))
352 _item
->last_used
= usage_counter
++;
357 /* Cache miss. Move into cache dir. */
358 _item
= create_item (checksum
);
361 rename (filename
, _item
->input
.c_str ());
366 /* If exists, returns cache item corresponding to cached input file. */
367 ltrans_file_cache::item
*
368 ltrans_file_cache::get_item (const char* input
)
370 auto it
= map_input
.find (input
);
371 if (it
== map_input
.end ())
376 /* If no other process holds the deletion_lock, prunes oldest unused cache
379 ltrans_file_cache::try_prune ()
381 if (deletion_lock
.try_lock_write () == 0)
384 deletion_lock
.unlock ();
388 /* Returns true if file exists. */
390 file_exists (char const *name
)
392 return access (name
, R_OK
) == 0;
395 /* Prunes oldest unused cache items over limit.
396 Must be called with deletion_lock held to prevent data race. */
398 ltrans_file_cache::prune ()
401 if (items
.size () > soft_cache_size
)
403 std::vector
<item
*> sorted_items
= std::move (items
);
407 for (size_t i
= 0; i
< sorted_items
.size (); ++i
)
409 ltrans_file_cache::item
* item
= sorted_items
[i
];
410 if ((i
< sorted_items
.size () - soft_cache_size
)
411 || !file_exists (item
->input
.c_str ())
412 || !file_exists (item
->output
.c_str ()))
414 unlink (item
->input
.c_str ());
415 unlink (item
->output
.c_str ());
425 /* Clears cache class, as if only constructor was called. */
427 ltrans_file_cache::cleanup ()
429 map_checksum
.clear ();
432 for (ltrans_file_cache::item
* item
: items
)