[LLVMdev] [RFC] llvm/include/Support/FileOutputBuffer.h
Nick Kledzik
kledzik at apple.com
Tue May 22 14:47:46 PDT 2012
On May 18, 2012, at 3:07 PM, Michael Spencer wrote:
> On Thu, May 17, 2012 at 3:25 PM, Nick Kledzik <kledzik at apple.com> wrote:
>> I now have an implementation of FileOutputBuffer (OutputBuffer was already taken). The patch supports the functionality listed below and I've tested that it works for lld.
>>
>> To implement the FileOutputBuffer, I needed to add some more functions to llvm/Support/FileSystem.h, including:
>> is_writable_file()
>> is_executable_file()
>> set_file_executable()
>
> For these parts I would like to follow
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3365.html#TOC
> which is what the rest of the fs library is modeled after. So we would
> use:
>
> error_code permissions(const Twine &path, perms p);
>
> If the name is confusing (it says nothing about modification) I'm fine
> with modify_permissions or something similar.
Oh my! I thought you were trying to create an abstraction that could be mapped onto unix or windows. That spec is just a posix interface, but with different names. There is even a table that shows the mapping of all the bit values (e.g. group_read == 040 == S_IRGRP. Seems like it would be less complicated to just implement the posix call interface on windows, than to create all these new names that add no value…
>> unique_file_sized()
>
> Instead of adding this I would much rather add resize_file and call it
> after unique_file.
The problem is that the existing unique_file() function creates the file and returns the file-descriptor. My level does not need that file-descriptor and there is no close() function available in this N3365 spec.
>
>> map_file_pages()
>> unmap_file_pages()
>
> These are good.
But if you are really trying to model N3365, should these go elsewhere?
>> + /// Flushes the content of the buffer to its file and deallocates the
>> + /// buffer. If commit() is not called before this object's destructor
>> + /// is called, the file is deleted in the destructor. The optional parameter
>> + /// is used if it turns out you want the file size to be smaller than
>> + /// initially requested.
>> + virtual error_code commit(int64_t newSmallerSize = -1);
>
> Why is this virtual? I don't ever see a need to subclass FileOutputBuffer.
>
>> + /// If this object was previously committed, the destructor just deletes
>> + /// this object. If this object was not committed, the destructor
>> + /// deallocates the buffer and the target file is never written.
>> + virtual ~FileOutputBuffer();
>
> Same.
I was anticipating there might be alternate implementations - like MemoryBuffer. I'll remove the virtual keyword.
>> +error_code unique_file_sized(const Twine &model, size_t size,
>> + SmallVectorImpl<char> &result_path,
>> + bool makeAbsolute) {
>> + int fd;
>> + if ( error_code ec = unique_file(model, fd, result_path, makeAbsolute, false) )
>> + return ec;
>> +
>> + if ( ::ftruncate(fd, size) == -1 ) {
>> + int error = errno;
>> + result_path.push_back('\0');
>
> This is incorrect because result_path now actually includes a \0. I added a
> function c_str(container) specifically for this case that adds a 0 then removes
> it.
c_str() is only available on SmallString<>, not on its parent SmallVectorImpl<char>.
>> +error_code map_file_pages(const Twine &path, off_t file_offset, size_t size,
>> + bool map_writable, void *&result) {
>> + SmallString<128> path_storage;
>> + StringRef name = path.toNullTerminatedStringRef(path_storage);
>> + int oflags = map_writable ? O_RDWR : O_RDONLY;
>> + int fd = ::open(name.begin(), oflags);
>> + if ( fd == -1 )
>> + return error_code(errno, system_category());
>> + int flags = map_writable ? MAP_SHARED : MAP_PRIVATE;
>> + int prot = map_writable ? (PROT_READ|PROT_WRITE) : PROT_READ;
>> +#ifdef MAP_FILE
>> + flags |= MAP_FILE;
>> +#endif
>> + result = ::mmap(0, size, prot, flags, fd, file_offset);
>> + if (result == MAP_FAILED) {
>> + ::close(fd);
>> + return error_code(errno, system_category());
>> + }
>> +
>> + ::close(fd);
>
> Could AutoFD work here?
Yes!
>> +//===- FileOutputBuffer.cpp - File Output Buffer ----------------*- C++ -*-===//
>> +//
>> +// The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// Utility for creating a in-memory buffer that will be written to a file.
>> +//
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "llvm/Support/FileOutputBuffer.h"
>> +#include "llvm/Support/system_error.h"
>> +#include "llvm/Support/FileSystem.h"
>> +
>> +#include "llvm/Support/raw_ostream.h"
>> +
>> +#include "llvm/ADT/SmallVector.h"
>> +#include "llvm/ADT/OwningPtr.h"
>
> These should be sorted alphabetically except for the main header.
Ok.
>
>
>> + // If file already exists, it must be a regular file (to be mappable).
>> + Twine filePathTwine(filePath);
>
> Twine should not be used like this. It stores references to temporaries. It will
> work in this case because filePath is not a temporary and you haven't invoked
> any operators. It's also uneeded. StringRef is convertable to Twine.
>
>> + sys::fs::file_status stat;
>> + bool writable;
>
> This is only used in the case below, so it should be folded in there.
Fixed.
>> +
>> + // Create new file of specified size in same directory but with random name.
>> + SmallString<128> modelStr;
>> + modelStr.assign(filePath);
>> + modelStr.append(".tmp%%%%%%%");
>> + SmallString<128> tempFilePath;
>> + if ( error_code ec = sys::fs::unique_file_sized(Twine(modelStr), size,
>> + tempFilePath, false) ) {
>
> This should be: Twine(filePath) + ".tmp%%%%%%%"
Yes, that will save some copying from happening.
>
>> + sys::fs::set_file_executable(Twine(tempFilePath));
>> +
>> + // Memory map new file.
>> + void *base;
>> + if ( error_code ec = sys::fs::map_file_pages(Twine(tempFilePath),
>> + 0, size, true, base) ) {
>> + return ec;
>> + }
>> +
>> + // Create FileOutputBuffer object to own mapped range.
>> + uint8_t* start = (uint8_t*)base;
>
> * should be right aligned and the case should be a c++ cast.
>
>> +
>> + return error_code::success();
>> +}
>> +
>> +
>> +error_code FileOutputBuffer::commit(int64_t newSmallerSize) {
>> + // Unmap buffer, letting OS flush dirty pages to file on disk.
>> + if ( error_code ec = sys::fs::unmap_file_pages((void*)bufferStart,
>
> c++ cast.
Fixed.
More information about the llvm-dev
mailing list