C-Chads / tinycc

C-Chads Maintained fork of Tiny C Compiler, regularly pulled from https://repo.or.cz/w/tinycc.git
GNU Lesser General Public License v2.1
77 stars 8 forks source link

VFS support #4

Open stevefan1999-personal opened 10 months ago

stevefan1999-personal commented 10 months ago

We need it so that we can build TCC to make it run on WASM, or make it possible so that we can run TCC on Compiler Explorer.

I personally uses libtcc mainly for experimental headers and libraries embedding, so I can just compile libtcc.c and leverage the ONE_SOURCE feature. And then I instruct the C compiler to override the necessary functions.

    if cfg!(feature = "vfs") {
        cc.define("open", "vfs_open");
        cc.define("read", "vfs_read");
        cc.define("lseek", "vfs_lseek");
        cc.define("close", "vfs_close");
    }

And then I implemented a tiny VFS wrapper:

extern "C" {
    fn open(path: *const c_char, oflag: c_int, ap: VaList) -> c_int;
}

#[no_mangle]
pub unsafe extern "C" fn vfs_open(path: *const c_char, oflag: c_int, mut args: ...) -> c_int {
    fn insert_vfs(vfs: Box<impl VFS + Send + Sync + Clone + 'static>) -> c_int {
        loop {
            let vfs = vfs.clone();
            let key: c_int = unsafe { RNG.gen_range(0..c_int::MAX) };
            if let Ok(_) = unsafe { FILES.try_insert(key, vfs) } {
                return key;
            }
        }
    }

    if let Ok(path) = CStr::from_ptr(path).to_str() {
        let prefix = "memory:///headers/";

        if path.starts_with(prefix) {
            let path = path.strip_prefix(prefix).unwrap();

            if let Some(file) = crate::headers::ASSET_MAP.get(path) {
                return insert_vfs(Box::new(MemoryVFS::from_static(file.contents_bytes)));
            }
        }

        let prefix = "memory:///libraries/";

        if path.starts_with(prefix) {
            let path = path.strip_prefix(prefix).unwrap();
            if let Some(file) = crate::LIBRARIES.get_file(path) {
                return insert_vfs(Box::new(MemoryVFS::from_static(file.contents())));
            }
        }
    }

    let fd = open(path, oflag, args.as_va_list());
    if fd >= 0 {
        insert_vfs(Box::new(PosixVFS::new(fd)))
    } else {
        fd
    }
}

This one worked out pretty well, but I think this is very hacky, and there is a lack of write support. Specifically, if we want to run this on Godbolt, we need to capture the output executable, but the current implementation of TCC writes directly to a file descriptor. Instead, we should have a dynamic stream API that supports byte stream or file stream.

EscapeCharacter-dev commented 9 months ago

This is a mirror of tinycc.

stevefan1999-personal commented 9 months ago

@stevefan1999-personal Sounds good. Go for it dude. Fork the repo into another thing here on CChads (or your user) and you can start working

Sure thing

stevefan1999-personal commented 9 months ago

This is a mirror of tinycc.

I don't think this is exactly it because there are some changes not in mob branch. But I will try to push to both sides