From 1098b0a34ee094ec6f69963613dc59e3c183a049 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 4 Feb 2024 12:40:52 +0000 Subject: [PATCH] Add wrapper functions for predefined file operations --- src/common.c | 10 ++++ src/lower.c | 82 ++++++++++++++++++++++++++++++-- src/object.c | 2 +- src/parse.c | 42 +++++++++-------- src/rt/fileio.c | 118 +++++++++++++++++++++++++---------------------- src/rt/reflect.c | 13 ++++-- src/symbols.txt | 1 - src/tree.h | 10 ++++ src/util.c | 36 +++++++++++++++ src/util.h | 8 ++++ 10 files changed, 238 insertions(+), 84 deletions(-) diff --git a/src/common.c b/src/common.c index c67fa3c7..9973972f 100644 --- a/src/common.c +++ b/src/common.c @@ -1422,6 +1422,16 @@ bool is_open_coded_builtin(subprogram_kind_t kind) case S_MATCH_GE: case S_MINIMUM: case S_MAXIMUM: + case S_FILE_FLUSH: + case S_FILE_OPEN3: + case S_FILE_MODE: + case S_FILE_CANSEEK: + case S_FILE_SIZE: + case S_FILE_REWIND: + case S_FILE_SEEK: + case S_FILE_TRUNCATE: + case S_FILE_STATE: + case S_FILE_POSITION: return false; default: return true; diff --git a/src/lower.c b/src/lower.c index 81411619..5aa0a4b1 100644 --- a/src/lower.c +++ b/src/lower.c @@ -6620,7 +6620,7 @@ static void lower_pcall(lower_unit_t *lu, tree_t pcall) tree_t decl = tree_ref(pcall); const subprogram_kind_t kind = tree_subkind(decl); - if (is_builtin(kind)) { + if (is_open_coded_builtin(kind)) { lower_builtin(lu, pcall, kind, NULL, NULL); return; } @@ -9864,6 +9864,7 @@ static void lower_decls(lower_unit_t *lu, tree_t scope) lu->cover, tree_to_object(d)); break; case T_FUNC_DECL: + case T_PROC_DECL: { const subprogram_kind_t kind = tree_subkind(d); if (!is_builtin(kind) || is_open_coded_builtin(kind)) @@ -10853,6 +10854,50 @@ static void lower_predef_negate(tree_t decl, const char *op) emit_return(emit_not(eq_reg)); } +static void lower_predef_file_op(lower_unit_t *lu, tree_t decl, const char *fn) +{ + ident_t func = ident_new(fn); + + vcode_type_t rtype = vcode_unit_result(lu->vunit); + vcode_var_t result_var = VCODE_INVALID_VAR; + if (rtype != VCODE_INVALID_TYPE) + result_var = emit_var(rtype, rtype, ident_new("result"), 0); + + vcode_reg_t args[4]; + const int nparams = vcode_count_params(); + + int nargs = 0; + for (int i = 1; i < nparams; i++) + args[nargs++] = vcode_param_reg(i); + + if (result_var != VCODE_INVALID_VAR) + args[nargs++] = emit_index(result_var, VCODE_INVALID_REG); + + assert(nargs < ARRAY_LEN(args)); + + emit_fcall(func, VCODE_INVALID_TYPE, VCODE_INVALID_TYPE, VCODE_CC_INTERNAL, + args, nargs); + + vcode_reg_t result_reg = VCODE_INVALID_REG; + if (result_var != VCODE_INVALID_VAR) + result_reg = emit_load(result_var); + + emit_return(result_reg); +} + +static void lower_predef_file_open3(lower_unit_t *lu, tree_t decl) +{ + vcode_type_t rtype = vcode_unit_result(lu->vunit); + vcode_var_t status_var = emit_var(rtype, rtype, ident_new("status"), 0); + + vcode_reg_t status_reg = emit_index(status_var, VCODE_INVALID_REG); + vcode_reg_t data_reg = emit_unwrap(2); + vcode_reg_t length_reg = emit_uarray_len(2, 0); + + emit_file_open(1, data_reg, length_reg, 3, status_reg); + emit_return(emit_load(status_var)); +} + static void lower_predef(lower_unit_t *lu, object_t *obj) { tree_t decl = tree_from_object(obj); @@ -10862,7 +10907,8 @@ static void lower_predef(lower_unit_t *lu, object_t *obj) assert(!is_open_coded_builtin(kind)); type_t type = tree_type(decl); - vcode_set_result(lower_func_result_type(type_result(type))); + if (type_kind(type) == T_FUNC) + vcode_set_result(lower_func_result_type(type_result(type))); vcode_type_t vcontext = vtype_context(lu->parent->name); emit_param(vcontext, vcontext, ident_new("context")); @@ -10942,8 +10988,38 @@ static void lower_predef(lower_unit_t *lu, object_t *obj) case S_MINIMUM: lower_predef_min_max(lu, decl, VCODE_CMP_LT); break; - default: + case S_FILE_FLUSH: + lower_predef_file_op(lu, decl, "__nvc_flush"); + break; + case S_FILE_OPEN3: + lower_predef_file_open3(lu, decl); + break; + case S_FILE_MODE: + lower_predef_file_op(lu, decl, "__nvc_file_mode"); + break; + case S_FILE_CANSEEK: + lower_predef_file_op(lu, decl, "__nvc_file_canseek"); break; + case S_FILE_SIZE: + lower_predef_file_op(lu, decl, "__nvc_file_size"); + break; + case S_FILE_REWIND: + lower_predef_file_op(lu, decl, "__nvc_rewind"); + break; + case S_FILE_SEEK: + lower_predef_file_op(lu, decl, "__nvc_seek"); + break; + case S_FILE_TRUNCATE: + lower_predef_file_op(lu, decl, "__nvc_truncate"); + break; + case S_FILE_STATE: + lower_predef_file_op(lu, decl, "__nvc_file_state"); + break; + case S_FILE_POSITION: + lower_predef_file_op(lu, decl, "__nvc_file_position"); + break; + default: + fatal_trace("cannot lower predefined function %s", type_pp(type)); } } diff --git a/src/object.c b/src/object.c index 8d8ca72e..7c266738 100644 --- a/src/object.c +++ b/src/object.c @@ -377,7 +377,7 @@ void object_one_time_init(void) // Increment this each time a incompatible change is made to // the on-disk format not expressed in the object items table - const uint32_t format_fudge = 34; + const uint32_t format_fudge = 35; format_digest += format_fudge * UINT32_C(2654435761); diff --git a/src/parse.c b/src/parse.c index 64009b87..33ad4518 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1230,9 +1230,9 @@ static void declare_predefined_ops(tree_t container, type_t t) if (standard() >= STD_08) { ident_t flush_i = ident_new("FLUSH"); - tree_t flush = builtin_proc(flush_i, S_FOREIGN); - tree_set_ident2(flush, ident_new("__nvc_flush")); + tree_t flush = builtin_proc(flush_i, S_FILE_FLUSH); add_port(flush, "F", t, PORT_IN, NULL); + mangle_func(nametab, flush); insert_name(nametab, flush, flush_i); tree_add_decl(container, flush); } @@ -1254,76 +1254,78 @@ static void declare_predefined_ops(tree_t container, type_t t) tree_t origin_begin = search_decls(std, begin_i, 0); assert(origin_begin != NULL); - tree_t file_open3 = builtin_fn(file_open_i, open_status, S_FOREIGN, + tree_t file_open3 = builtin_fn(file_open_i, open_status, + S_FILE_OPEN3, "F", t, "EXTERNAL_NAME", std_string, "OPEN_KIND", open_kind, NULL); tree_set_flag(file_open3, TREE_F_IMPURE); - tree_set_ident2(file_open3, ident_new("__nvc_open3")); tree_set_class(tree_port(file_open3, 0), C_FILE); tree_set_value(tree_port(file_open3, 2), make_ref(read_mode)); + mangle_func(nametab, file_open3); insert_name(nametab, file_open3, file_open_i); tree_add_decl(container, file_open3); - tree_t rewind = builtin_proc(rewind_i, S_FOREIGN); - tree_set_ident2(rewind, ident_new("__nvc_rewind")); + tree_t rewind = builtin_proc(rewind_i, S_FILE_REWIND); add_port(rewind, "F", t, PORT_IN, NULL); + mangle_func(nametab, rewind); insert_name(nametab, rewind, rewind_i); tree_add_decl(container, rewind); std_int = std_type(std, STD_INTEGER); - tree_t seek = builtin_proc(seek_i, S_FOREIGN); - tree_set_ident2(seek, ident_new("__nvc_seek")); + tree_t seek = builtin_proc(seek_i, S_FILE_SEEK); add_port(seek, "F", t, PORT_IN, NULL); add_port(seek, "OFFSET", std_int, PORT_IN, NULL); add_port(seek, "ORIGIN", origin_kind, PORT_IN, make_ref(origin_begin)); + mangle_func(nametab, seek); insert_name(nametab, seek, seek_i); tree_add_decl(container, seek); - tree_t truncate = builtin_proc(truncate_i, S_FOREIGN); - tree_set_ident2(truncate, ident_new("__nvc_truncate")); + tree_t truncate = builtin_proc(truncate_i, S_FILE_TRUNCATE); add_port(truncate, "F", t, PORT_IN, NULL); add_port(truncate, "SIZE", std_int, PORT_IN, NULL); add_port(truncate, "ORIGIN", origin_kind, PORT_IN, make_ref(origin_begin)); + mangle_func(nametab, truncate); insert_name(nametab, truncate, truncate_i); tree_add_decl(container, truncate); - tree_t state = builtin_fn(state_i, open_state, S_FOREIGN, + tree_t state = builtin_fn(state_i, open_state, S_FILE_STATE, "F", t, NULL); - tree_set_ident2(state, ident_new("__nvc_file_state")); tree_set_class(tree_port(state, 0), C_FILE); + mangle_func(nametab, state); insert_name(nametab, state, state_i); tree_add_decl(container, state); - tree_t mode = builtin_fn(mode_i, open_kind, S_FOREIGN, + tree_t mode = builtin_fn(mode_i, open_kind, S_FILE_MODE, "F", t, NULL); - tree_set_ident2(mode, ident_new("__nvc_file_mode")); tree_set_class(tree_port(mode, 0), C_FILE); + mangle_func(nametab, mode); insert_name(nametab, mode, mode_i); tree_add_decl(container, mode); - tree_t position = builtin_fn(position_i, std_int, S_FOREIGN, + tree_t position = builtin_fn(position_i, std_int, S_FILE_POSITION, "F", t, "ORIGIN", origin_kind, NULL); - tree_set_ident2(position, ident_new("__nvc_file_position")); tree_set_class(tree_port(position, 0), C_FILE); tree_set_value(tree_port(position, 1), make_ref(origin_begin)); + mangle_func(nametab, position); insert_name(nametab, position, position_i); tree_add_decl(container, position); - tree_t size = builtin_fn(size_i, std_int, S_FOREIGN, "F", t, NULL); - tree_set_ident2(size, ident_new("__nvc_file_size")); + tree_t size = builtin_fn(size_i, std_int, S_FILE_SIZE, + "F", t, NULL); tree_set_class(tree_port(size, 0), C_FILE); + mangle_func(nametab, size); insert_name(nametab, size, size_i); tree_add_decl(container, size); - tree_t canseek = builtin_fn(canseek_i, std_bool, S_FOREIGN, + tree_t canseek = builtin_fn(canseek_i, std_bool, S_FILE_CANSEEK, "F", t, NULL); - tree_set_ident2(canseek, ident_new("__nvc_file_canseek")); tree_set_class(tree_port(canseek, 0), C_FILE); + mangle_func(nametab, canseek); insert_name(nametab, canseek, canseek_i); tree_add_decl(container, canseek); } diff --git a/src/rt/fileio.c b/src/rt/fileio.c index f6feda66..d045efa3 100644 --- a/src/rt/fileio.c +++ b/src/rt/fileio.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2023 Nick Gasson +// Copyright (C) 2023-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -57,8 +57,10 @@ typedef enum { } file_open_kind_t; DLLEXPORT -void __nvc_flush(FILE **fp) +void __nvc_flush(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FLUSH called on closed file"); @@ -66,8 +68,10 @@ void __nvc_flush(FILE **fp) } DLLEXPORT -void __nvc_rewind(FILE **fp) +void __nvc_rewind(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_REWIND called on closed file"); @@ -75,30 +79,29 @@ void __nvc_rewind(FILE **fp) } DLLEXPORT -void __nvc_seek(FILE **fp, int32_t offset, int8_t origin) +void __nvc_seek(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + off_t offset = args[1].integer; + int8_t origin = args[2].integer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_SEEK called on closed file"); const int whence[3] = { SEEK_SET, SEEK_CUR, SEEK_END }; assert(origin >= 0 && origin < ARRAY_LEN(whence)); - if (fseek(*fp, offset, whence[origin]) < 0) + if (fseeko(*fp, offset, whence[origin]) < 0) jit_msg(NULL, DIAG_FATAL, "FILE_SEEK failed: %s", strerror(errno)); } DLLEXPORT -int8_t __nvc_open3(FILE **fp, const uint8_t *name_ptr, int64_t name_len, - int8_t open_kind) +void __nvc_truncate(jit_scalar_t *args) { - int8_t status; - x_file_open(&status, (void **)fp, name_ptr, name_len, open_kind); - return status; -} + FILE **fp = args[0].pointer; + int64_t size = args[1].integer; + int8_t origin = args[2].integer; -DLLEXPORT -void __nvc_truncate(FILE **fp, int32_t size, int8_t origin) -{ if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_TRUNCATE called on closed file"); @@ -148,72 +151,72 @@ void __nvc_truncate(FILE **fp, int32_t size, int8_t origin) } DLLEXPORT -int8_t __nvc_file_state(FILE **fp) +void __nvc_file_state(jit_scalar_t *args) { - return *fp == NULL ? STATE_CLOSED : STATE_OPEN; + FILE **fp = args[0].pointer; + int8_t *result = args[1].pointer; + + *result = (*fp == NULL ? STATE_CLOSED : STATE_OPEN); } DLLEXPORT -int8_t __nvc_file_mode(FILE **fp) +void __nvc_file_mode(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + int8_t *result = args[1].pointer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_MODE called on closed file"); -#ifdef __MINGW32__ fflush(*fp); - HANDLE handle = (HANDLE)_get_osfhandle(_fileno(*fp)); - const bool can_read = ReadFile(handle, NULL, 0, NULL, NULL); - const bool can_write = WriteFile(handle, NULL, 0, NULL, NULL); - - if (can_read && can_write) - return READ_WRITE_MODE; - else if (can_read) - return READ_MODE; - else if (can_write) - return WRITE_MODE; -#else - const int mode = fcntl(fileno(*fp), F_GETFL); - if (mode < 0) - jit_msg(NULL, DIAG_FATAL, "FILE_MODE failed: %s", strerror(errno)); - - switch (mode & O_ACCMODE) { - case O_RDONLY: return READ_MODE; - case O_WRONLY: return (mode & O_APPEND) ? APPEND_MODE : WRITE_MODE; - case O_RDWR: return READ_WRITE_MODE; + file_mode_t mode; + if (!get_handle_mode(fileno(*fp), &mode)) { + jit_msg(NULL, DIAG_WARN, "cannot determine file mode"); + *result = READ_MODE; } -#endif - - jit_msg(NULL, DIAG_WARN, "cannot determine file mode"); - return READ_MODE; + else + *result = mode; } DLLEXPORT -int64_t __nvc_file_position(FILE **fp, int8_t origin) +void __nvc_file_position(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + int8_t origin = args[1].integer; + int64_t *result = args[2].pointer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_POSITION called on closed file"); - if (origin == FILE_ORIGIN_CURRENT) - return 0; - off_t off = ftello(*fp); if (off < 0) jit_msg(NULL, DIAG_FATAL, "FILE_POSITION failed: %s", strerror(errno)); - if (origin == FILE_ORIGIN_END) { - fseeko(*fp, 0, SEEK_END); - off_t end = ftello(*fp); - fseeko(*fp, off, SEEK_SET); - return end - off; + switch (origin) { + case FILE_ORIGIN_BEGIN: + *result = off; + break; + case FILE_ORIGIN_END: + { + fseeko(*fp, 0, SEEK_END); + off_t end = ftello(*fp); + fseeko(*fp, off, SEEK_SET); + *result = end - off; + } + break; + case FILE_ORIGIN_CURRENT: + *result = 0; + break; } - else - return off; } DLLEXPORT -int64_t __nvc_file_size(FILE **fp, int8_t origin) +void __nvc_file_size(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + int64_t *result = args[1].pointer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_SIZE called on closed file"); @@ -223,16 +226,19 @@ int64_t __nvc_file_size(FILE **fp, int8_t origin) if (!get_handle_info(fileno(*fp), &info)) jit_msg(NULL, DIAG_FATAL, "FILE_SIZE failed: %s", strerror(errno)); - return info.size; + *result = info.size; } DLLEXPORT -int8_t __nvc_file_canseek(FILE **fp) +void __nvc_file_canseek(jit_scalar_t *args) { + FILE **fp = args[0].pointer; + int8_t *result = args[1].pointer; + if (*fp == NULL) jit_msg(NULL, DIAG_FATAL, "FILE_CANSEEK called on closed file"); - return fseek(*fp, 0, SEEK_CUR) == 0; + *result = fseek(*fp, 0, SEEK_CUR) == 0; } void _file_io_init(void) diff --git a/src/rt/reflect.c b/src/rt/reflect.c index 531d6455..197b6672 100644 --- a/src/rt/reflect.c +++ b/src/rt/reflect.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2023 Nick Gasson +// Copyright (C) 2023-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -632,9 +632,16 @@ static value_mirror *get_value_mirror(void *context, jit_scalar_t value, fvm->pt.f_subtype = vm->pt.f_subtype->pt.f_file; FILE *fp = value.pointer; + fflush(fp); + + file_mode_t mode; + if (!get_handle_mode(fileno(fp), &mode)) { + jit_msg(NULL, DIAG_WARN, "cannot determine file mode"); + fvm->pt.f_open_kind = 0; + } + else + fvm->pt.f_open_kind = mode; - extern int8_t __nvc_file_mode(FILE **fp); - fvm->pt.f_open_kind = __nvc_file_mode(&fp); fvm->pt.f_logical_name = get_file_logical_name(fp); vm->pt.f_class = CLASS_FILE; diff --git a/src/symbols.txt b/src/symbols.txt index a57064a6..be3ebde1 100644 --- a/src/symbols.txt +++ b/src/symbols.txt @@ -59,7 +59,6 @@ __nvc_file_size; __nvc_file_state; __nvc_flush; - __nvc_open3; __nvc_rewind; __nvc_seek; __nvc_truncate; diff --git a/src/tree.h b/src/tree.h index 382355b1..49780616 100644 --- a/src/tree.h +++ b/src/tree.h @@ -190,6 +190,16 @@ typedef enum { S_MATCH_EQ, S_MATCH_NEQ, S_INTERNAL, + S_FILE_FLUSH, + S_FILE_OPEN3, + S_FILE_MODE, + S_FILE_CANSEEK, + S_FILE_SIZE, + S_FILE_REWIND, + S_FILE_SEEK, + S_FILE_TRUNCATE, + S_FILE_STATE, + S_FILE_POSITION, } subprogram_kind_t; typedef enum { diff --git a/src/util.c b/src/util.c index 7c8746f9..2eafd1c3 100644 --- a/src/util.c +++ b/src/util.c @@ -1762,6 +1762,42 @@ bool get_handle_path(int fd, text_buf_t *tb) #endif } +bool get_handle_mode(int fd, file_mode_t *mode) +{ +#ifdef __MINGW32__ + HANDLE handle = (HANDLE)_get_osfhandle(fd); + const bool can_read = ReadFile(handle, NULL, 0, NULL, NULL); + const bool can_write = WriteFile(handle, NULL, 0, NULL, NULL); + + if (can_read && can_write) + *mode = FILE_READ_WRITE; + else if (can_write) + *mode = FILE_WRITE; + else + *mode = FILE_READ; + + return true; +#else + const int rc = fcntl(fd, F_GETFL); + if (rc < 0) + return false; + + switch (rc & O_ACCMODE) { + case O_RDONLY: + *mode = FILE_READ; + return true; + case O_WRONLY: + *mode = (rc & O_APPEND) ? FILE_APPEND : FILE_WRITE; + return true; + case O_RDWR: + *mode = FILE_READ_WRITE; + return true; + default: + return false; + } +#endif +} + void run_program(const char *const *args) { #if defined __CYGWIN__ || defined __MINGW32__ diff --git a/src/util.h b/src/util.h index ae4b8f9b..45d9b893 100644 --- a/src/util.h +++ b/src/util.h @@ -343,9 +343,17 @@ typedef struct { timestamp_t mtime; } file_info_t; +typedef enum { + FILE_READ, + FILE_WRITE, + FILE_APPEND, + FILE_READ_WRITE, +} file_mode_t; + bool get_file_info(const char *path, file_info_t *info); bool get_handle_info(int fd, file_info_t *info); bool get_handle_path(int fd, text_buf_t *tb); +bool get_handle_mode(int fd, file_mode_t *mode); void progress(const char *fmt, ...) __attribute__((format(printf, 1, 2))); -- 2.39.2