diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ced02cc6..49994607 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ jobs: matrix: image: - "clang:13" + - "gcc:11" build_type: [Debug, Release] runs-on: ubuntu-latest diff --git a/include/lauf/asm/builder.h b/include/lauf/asm/builder.h index f60c8a8d..835f6ae7 100644 --- a/include/lauf/asm/builder.h +++ b/include/lauf/asm/builder.h @@ -308,4 +308,3 @@ void lauf_asm_inst_store_field(lauf_asm_builder* b, lauf_asm_type type, size_t f LAUF_HEADER_END #endif // LAUF_ASM_BUILDER_H_INCLUDED - diff --git a/include/lauf/asm/module.h b/include/lauf/asm/module.h index b3c10962..0dcfccb2 100644 --- a/include/lauf/asm/module.h +++ b/include/lauf/asm/module.h @@ -156,4 +156,3 @@ bool lauf_asm_chunk_is_empty(const lauf_asm_chunk* chunk); LAUF_HEADER_END #endif // LAUF_ASM_MODULE_H_INCLUDED - diff --git a/include/lauf/config.h b/include/lauf/config.h index a9784168..b2461a63 100644 --- a/include/lauf/config.h +++ b/include/lauf/config.h @@ -36,22 +36,44 @@ typedef uint64_t lauf_uint; # error "lauf assumes 8 bit bytes" #endif -#if !defined(__clang__) -# error "lauf currently requires clang" -#endif - //=== optimizations ===// -#define LAUF_LIKELY(Cond) __builtin_expect((Cond), 1) -#define LAUF_UNLIKELY(Cond) __builtin_expect((Cond), 0) -#define LAUF_TAIL_CALL [[clang::musttail]] -#define LAUF_NOINLINE [[gnu::noinline]] -#define LAUF_FORCE_INLINE [[gnu::always_inline]] -#define LAUF_UNREACHABLE __builtin_unreachable() +#if defined(__GNUC__) || defined(__GNUG__) +# define LAUF_LIKELY(Cond) __builtin_expect((Cond), 1) +# define LAUF_UNLIKELY(Cond) __builtin_expect((Cond), 0) +# if defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::musttail) +# define LAUF_TAIL_CALL [[clang::musttail]] +# elif defined(__clang__) +# define LAUF_TAIL_CALL [[clang::musttail]] +# else +# define LAUF_TAIL_CALL +# endif +# endif +# define LAUF_NOINLINE [[gnu::noinline]] +# define LAUF_FORCE_INLINE [[gnu::always_inline]] inline +# define LAUF_UNREACHABLE __builtin_unreachable() +#endif //=== configurations ===// #ifndef LAUF_CONFIG_DISPATCH_JUMP_TABLE # define LAUF_CONFIG_DISPATCH_JUMP_TABLE 1 #endif -#endif // LAUF_CONFIG_H_INCLUDED +#if defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::musttail) +# define LAUF_HAS_TAIL_CALL_ELIMINATION 1 +# endif +#endif +//=== warnings ===// +#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__)) +# define LAUF_BITFIELD_CONVERSION(...) \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wconversion\""); \ + __VA_ARGS__; \ + _Pragma("GCC diagnostic pop") +#else +# define LAUF_BITFIELD_CONVERSION(...) __VA_ARGS__ +#endif + +#endif // LAUF_CONFIG_H_INCLUDED diff --git a/include/lauf/runtime/builtin.h b/include/lauf/runtime/builtin.h index 957c623a..db8b6675 100644 --- a/include/lauf/runtime/builtin.h +++ b/include/lauf/runtime/builtin.h @@ -8,7 +8,17 @@ LAUF_HEADER_START -#define LAUF_RUNTIME_BUILTIN_IMPL __attribute__((section("text.lauf_builtin"), aligned(8))) +#if defined(__clang__) +# if defined(__APPLE__) && defined(__MACH__) +# define LAUF_RUNTIME_BUILTIN_IMPL __attribute__((section(".text,lauf_builtin"), aligned(8))) +# else +# define LAUF_RUNTIME_BUILTIN_IMPL __attribute__((section("text.lauf_builtin"), aligned(8))) +# endif +#elif defined(__GNUC__) || defined(__GNUG__) +# define LAUF_RUNTIME_BUILTIN_IMPL __attribute__((section(".text.lauf_builtin"), aligned(8))) +#else +# define LAUF_RUNTIME_BUILTIN_IMPL +#endif typedef union lauf_asm_inst lauf_asm_inst; typedef struct lauf_asm_type lauf_asm_type; @@ -92,4 +102,3 @@ typedef struct lauf_runtime_builtin_library LAUF_HEADER_END #endif // LAUF_RUNTIME_BUILTIN_H_INCLUDED - diff --git a/src/lauf/asm/builder.cpp b/src/lauf/asm/builder.cpp index e244cdcd..ad24f401 100644 --- a/src/lauf/asm/builder.cpp +++ b/src/lauf/asm/builder.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2022-2023 Jonathan Müller and lauf contributors // SPDX-License-Identifier: BSL-1.0 +#include #include #include @@ -372,7 +373,7 @@ LAUF_NOINLINE lauf_asm_inst* emit_body(lauf_asm_inst* ip, lauf_asm_builder* b, assert(insts[dest->offset].op() == lauf::asm_op::block); auto dest_offset = dest->offset + 1; - jump->jump.offset = std::int32_t(dest_offset - cur_offset); + LAUF_BITFIELD_CONVERSION(jump->jump.offset = std::int32_t(dest_offset - cur_offset)); } return ip; @@ -437,7 +438,7 @@ void emit_debug_location(lauf_asm_builder* b) for (auto loc : block.debug_locations) { // We also have the initial block instruction that affects the inst_idx. - loc.inst_idx += block.offset + 1; + loc.inst_idx += static_cast(block.offset + 1); cont.push_back(arena, loc); } } @@ -489,8 +490,7 @@ bool lauf_asm_build_finish(lauf_asm_builder* b) auto result = std::size_t(0); for (auto& block : b->blocks) - if (block.vstack.max_size() > result) - result = block.vstack.max_size(); + result = std::max(block.vstack.max_size(), result); if (result > UINT16_MAX) b->error(context, "per-function vstack size limit exceeded"); @@ -541,7 +541,7 @@ lauf_asm_local* lauf_asm_build_local(lauf_asm_builder* b, lauf_asm_layout layout // The offset is the current size, we don't need to worry about alignment. offset = std::uint16_t(b->local_allocation_size + sizeof(lauf_runtime_stack_frame)); - b->local_allocation_size += layout.size; + b->local_allocation_size += static_cast(layout.size); } else { @@ -553,7 +553,7 @@ lauf_asm_local* lauf_asm_build_local(lauf_asm_builder* b, lauf_asm_layout layout // for a pointer. // Since `layout.alignment` is a multiple of it (as a power of two bigger than it), and // size a multiple of alignment, `layout.alignment + layout.size` is as well. - b->local_allocation_size += layout.alignment + layout.size; + b->local_allocation_size += static_cast(layout.alignment + layout.size); // Since we don't know the exact alignment offset, we can't compute it statically. offset = UINT16_MAX; } @@ -708,6 +708,8 @@ const lauf_asm_block* lauf_asm_inst_branch(lauf_asm_builder* b, const lauf_asm_b b->cur->next[0] = if_false; b->cur->next[1] = if_true; break; + default: + LAUF_UNREACHABLE; } } else @@ -1006,7 +1008,7 @@ void lauf_asm_inst_global_addr(lauf_asm_builder* b, const lauf_asm_global* globa b->cur->insts.push_back(*b, LAUF_BUILD_INST_VALUE(global_addr, global->allocation_idx)); b->cur->vstack.push_constant(*b, [&] { lauf_runtime_value result; - result.as_address.allocation = global->allocation_idx; + LAUF_BITFIELD_CONVERSION(result.as_address.allocation = global->allocation_idx); result.as_address.offset = 0; result.as_address.generation = 0; // Always true for globals. return result; @@ -1240,7 +1242,7 @@ void lauf_asm_inst_aggregate_member(lauf_asm_builder* b, size_t member_index, namespace { -enum load_store_constant +enum load_store_constant : uint8_t { load_store_dynamic, load_store_local, @@ -1381,4 +1383,3 @@ void lauf_asm_inst_store_field(lauf_asm_builder* b, lauf_asm_type type, size_t f lauf_asm_inst_call_builtin(b, builtin); } } - diff --git a/src/lauf/asm/builder.hpp b/src/lauf/asm/builder.hpp index 946e7397..fca9a9a8 100644 --- a/src/lauf/asm/builder.hpp +++ b/src/lauf/asm/builder.hpp @@ -4,6 +4,7 @@ #ifndef SRC_LAUF_ASM_BUILDER_HPP_INCLUDED #define SRC_LAUF_ASM_BUILDER_HPP_INCLUDED +#include #include #include #include @@ -32,7 +33,7 @@ class builder_vstack public: struct value { - enum type_t + enum type_t : uint8_t { unknown, constant, @@ -66,8 +67,7 @@ class builder_vstack void push(arena_base& arena, value v) { _stack.push_back(arena, v); - if (size() > _max) - _max = size(); + _max = std::max(size(), _max); } void push_output(arena_base& arena, std::size_t n) { @@ -176,7 +176,7 @@ struct lauf_asm_block lauf::array_list insts; lauf::array_list debug_locations; - enum + enum : uint8_t { unterminated, terminated, @@ -262,14 +262,14 @@ struct lauf_asm_builder : lauf::intrinsic_arena { \ if (LAUF_UNLIKELY(!(Cond))) \ b->error(LAUF_BUILD_ASSERT_CONTEXT, Msg); \ - } while (0) + } while (false) #define LAUF_BUILD_CHECK_CUR \ do \ { \ if (LAUF_UNLIKELY(b->cur == nullptr)) \ return; \ - } while (0) + } while (false) //=== instruction building ===// #define LAUF_BUILD_INST_NONE(Name) \ @@ -282,7 +282,7 @@ struct lauf_asm_builder : lauf::intrinsic_arena #define LAUF_BUILD_INST_OFFSET(Name, Offset) \ [&](const char* context, std::ptrdiff_t offset) { \ lauf_asm_inst result; \ - result.Name = {lauf::asm_op::Name, std::int32_t(offset)}; \ + LAUF_BITFIELD_CONVERSION(result.Name = {lauf::asm_op::Name, std::int32_t(offset)}); \ if (result.Name.offset != offset) \ b->error(context, "offset too big"); \ return result; \ @@ -315,7 +315,7 @@ struct lauf_asm_builder : lauf::intrinsic_arena #define LAUF_BUILD_INST_VALUE(Name, Value) \ [&](const char* context, std::size_t value) { \ lauf_asm_inst result; \ - result.Name = {lauf::asm_op::Name, std::uint32_t(value)}; \ + LAUF_BITFIELD_CONVERSION(result.Name = {lauf::asm_op::Name, std::uint32_t(value)}); \ if (value != result.Name.value) \ b->error(context, "invalid value"); \ return result; \ @@ -331,4 +331,3 @@ struct lauf_asm_builder : lauf::intrinsic_arena }(LAUF_BUILD_ASSERT_CONTEXT, Index) #endif // SRC_LAUF_ASM_BUILDER_HPP_INCLUDED - diff --git a/src/lauf/backend/qbe.hpp b/src/lauf/backend/qbe.hpp index 0ea3f8b9..536dd2ad 100644 --- a/src/lauf/backend/qbe.hpp +++ b/src/lauf/backend/qbe.hpp @@ -15,14 +15,14 @@ namespace lauf { -enum class qbe_type +enum class qbe_type : uint8_t { - word, - long_, - single, - double_, - byte, - halfword, + word = 0, + long_ = 1, + single = 2, + double_ = 3, + byte = 4, + halfword = 5, value = long_, }; @@ -62,7 +62,7 @@ enum class qbe_block : std::size_t { }; -enum class qbe_cc +enum class qbe_cc : uint8_t { ieq, ine, @@ -426,6 +426,7 @@ class qbe_writer case qbe_type::halfword: return "h"; } + LAUF_UNREACHABLE; } const char* cc_name(qbe_cc cc) @@ -453,6 +454,7 @@ class qbe_writer case qbe_cc::ugt: return "ugt"; } + LAUF_UNREACHABLE; } lauf_writer* _writer; @@ -462,4 +464,3 @@ class qbe_writer } // namespace lauf #endif // SRC_LAUF_BACKEND_QBE_HPP_INCLUDED - diff --git a/src/lauf/frontend/text.cpp b/src/lauf/frontend/text.cpp index c8c1f3ce..b1a8725f 100644 --- a/src/lauf/frontend/text.cpp +++ b/src/lauf/frontend/text.cpp @@ -179,11 +179,14 @@ namespace lauf::text_grammar { namespace dsl = lexy::dsl; -template -constexpr auto callback(Fn... fn) +namespace { - return lexy::bind(lexy::callback(fn...), lexy::parse_state, lexy::values); -} + template + constexpr auto callback(Fn... fn) + { + return lexy::bind(lexy::callback(fn...), lexy::parse_state, lexy::values); + } +} // namespace //=== common ===// struct identifier @@ -222,7 +225,7 @@ struct local_identifier struct signature { static constexpr auto rule = [] { - auto spec = dsl::integer >> LEXY_LIT("=>") + dsl::integer; + auto spec = dsl::integer >> (LEXY_LIT("=>") + dsl::integer); return dsl::parenthesized.opt(spec); }(); @@ -397,7 +400,7 @@ struct data_expr struct repetition { static constexpr auto rule = dsl::square_bracketed(dsl::recurse) - >> dsl::lit_c<'*'> + dsl::integer; + >> (dsl::lit_c<'*'> + dsl::integer); static constexpr auto value = lexy::callback([](const std::string& data, std::size_t n) { @@ -420,10 +423,10 @@ struct global_decl .map(LEXY_LIT("const"), LAUF_ASM_GLOBAL_READ_ONLY); static constexpr auto rule - = LAUF_KEYWORD("global") >> dsl::symbol + dsl::position(dsl::p) - + dsl::opt(dsl::colon >> dsl::p) - + dsl::opt(dsl::equal_sign >> dsl::p) - + dsl::semicolon; + = LAUF_KEYWORD("global") + >> (dsl::symbol + dsl::position(dsl::p) + + dsl::opt(dsl::colon >> dsl::p) + + dsl::opt(dsl::equal_sign >> dsl::p) + dsl::semicolon); static constexpr auto value = callback( [](parse_state& state, lauf_asm_global_permissions perms, auto pos, const std::string& name, @@ -457,16 +460,20 @@ struct global_decl }; //=== instruction ===// -template -constexpr auto inst(Fn fn) -{ - return callback([fn](const parse_state& state, auto... args) { fn(state.builder, args...); }); -} -constexpr auto inst() +namespace { - return callback([](const parse_state& state, auto fn, auto... args) // - { fn(state.builder, args...); }); -} + template + constexpr auto inst(Fn fn) + { + return callback( + [fn](const parse_state& state, const auto&... args) { fn(state.builder, args...); }); + } + [[maybe_unused]] constexpr auto inst() + { + return callback([](const parse_state& state, auto fn, auto... args) // + { fn(state.builder, args...); }); + } +} // namespace struct inst_return { @@ -490,7 +497,7 @@ struct inst_jump }; struct inst_branch { - static constexpr auto rule = LAUF_KEYWORD("branch") >> dsl::p + dsl::p; + static constexpr auto rule = LAUF_KEYWORD("branch") >> (dsl::p + dsl::p); static constexpr auto value = inst(&lauf_asm_inst_branch); }; @@ -568,8 +575,8 @@ struct inst_cc struct inst_let { static constexpr auto rule - = LAUF_KEYWORD("let") >> dsl::position + dsl::p - + dsl::if_(dsl::equal_sign >> dsl::integer); + = LAUF_KEYWORD("let") >> (dsl::position + dsl::p + + dsl::if_(dsl::equal_sign >> dsl::integer)); static constexpr auto value = callback( [](parse_state& state, auto pos, const std::string& name, std::uint16_t idx = 0) { auto value = lauf_asm_inst_value(state.builder, idx); @@ -646,7 +653,7 @@ struct inst_aggregate_member }; static constexpr auto rule - = LAUF_KEYWORD("aggregate_member") >> dsl::p + dsl::integer; + = LAUF_KEYWORD("aggregate_member") >> (dsl::p + dsl::integer); static constexpr auto value = inst( [](lauf_asm_builder* b, const std::vector& members, std::size_t index) { lauf_asm_inst_aggregate_member(b, index, members.data(), members.size()); @@ -655,13 +662,13 @@ struct inst_aggregate_member struct inst_load_field { static constexpr auto rule - = LAUF_KEYWORD("load_field") >> dsl::p + dsl::integer; + = LAUF_KEYWORD("load_field") >> (dsl::p + dsl::integer); static constexpr auto value = inst(&lauf_asm_inst_load_field); }; struct inst_store_field { static constexpr auto rule - = LAUF_KEYWORD("store_field") >> dsl::p + dsl::integer; + = LAUF_KEYWORD("store_field") >> (dsl::p + dsl::integer); static constexpr auto value = inst(&lauf_asm_inst_store_field); }; @@ -670,8 +677,10 @@ struct location static constexpr auto rule = dsl::position; static constexpr auto value = callback([](parse_state& state, auto pos) { auto loc = lexy::get_input_location(state.input->buffer, pos, state.anchor); - lauf_asm_build_debug_location(state.builder, {0, std::uint16_t(loc.line_nr()), - std::uint16_t(loc.column_nr()), false, 0}); + LAUF_BITFIELD_CONVERSION( + lauf_asm_build_debug_location(state.builder, + {0, std::uint16_t(loc.line_nr()), + std::uint16_t(loc.column_nr()), false, 0})); state.anchor = loc.anchor(); }); }; @@ -694,7 +703,7 @@ struct instruction | dsl::p | dsl::p // | dsl::p | dsl::p; - return nested | dsl::else_ >> dsl::p + single + dsl::semicolon; + return nested | dsl::else_ >> (dsl::p + single + dsl::semicolon); }(); static constexpr auto value = lexy::forward; @@ -735,9 +744,9 @@ struct block struct local_decl { - static constexpr auto rule = LAUF_KEYWORD("local") >> dsl::position + dsl::p - + dsl::colon + dsl::p - + dsl::semicolon; + static constexpr auto rule + = LAUF_KEYWORD("local") >> (dsl::position + dsl::p + + dsl::colon + dsl::p + dsl::semicolon); static constexpr auto value = callback( [](parse_state& state, auto pos, const std::string& name, lauf_asm_layout layout) { auto local = lauf_asm_build_local(state.builder, layout); @@ -790,7 +799,7 @@ struct function_decl auto locals = dsl::if_(dsl::list(dsl::p)); - return dsl::curly_bracketed.open() >> locals + (block_list | dsl::else_ >> inst_list); + return dsl::curly_bracketed.open() >> (locals + (block_list | dsl::else_ >> inst_list)); }(); static constexpr auto value = lexy::noop >> callback([](const parse_state& state) { @@ -798,9 +807,9 @@ struct function_decl }); }; - static constexpr auto rule = LAUF_KEYWORD("function") >> dsl::p
- + dsl::if_(dsl::p) - + (dsl::semicolon | dsl::p); + static constexpr auto rule + = LAUF_KEYWORD("function") + >> (dsl::p
+ dsl::if_(dsl::p) + (dsl::semicolon | dsl::p)); static constexpr auto value = lexy::forward; }; @@ -826,9 +835,9 @@ struct module_decl }; } // namespace lauf::text_grammar -lauf_asm_module* lauf_frontend_text(lauf_reader* reader, lauf_frontend_text_options opts) +lauf_asm_module* lauf_frontend_text(lauf_reader* reader, lauf_frontend_text_options options) { - parse_state state(reader, opts); + parse_state state(reader, options); auto callback = state.report_error(); auto result = lexy::parse(reader->buffer, state, callback); @@ -846,4 +855,3 @@ lauf_asm_module* lauf_frontend_text(lauf_reader* reader, lauf_frontend_text_opti return state.mod; } - diff --git a/src/lauf/lib/debug.cpp b/src/lauf/lib/debug.cpp index 84148fa1..500c6842 100644 --- a/src/lauf/lib/debug.cpp +++ b/src/lauf/lib/debug.cpp @@ -140,7 +140,11 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_debug_break, 0, 0, LAUF_RUNTIME_BUILTIN_NO_PROCESS | LAUF_RUNTIME_BUILTIN_NO_PANIC, "break", &lauf_lib_debug_print_all_cstacks) { +#if defined(__clang__) __builtin_debugtrap(); +#elif defined(__STDC_HOSTED__) && (__STDC_HOSTED__ == 0) && defined(__GNUC__) + __builtin_trap(); +#endif LAUF_RUNTIME_BUILTIN_DISPATCH; } @@ -151,10 +155,9 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_debug_read, 0, 1, std::printf("[lauf] debug read: 0x"); --vstack_ptr; - std::scanf("%" SCNx64, &vstack_ptr->as_uint); + [[maybe_unused]] auto _ = std::scanf("%" SCNx64, &vstack_ptr->as_uint); LAUF_RUNTIME_BUILTIN_DISPATCH; } const lauf_runtime_builtin_library lauf_lib_debug = {"lauf.debug", &lauf_lib_debug_read, nullptr}; - diff --git a/src/lauf/lib/int.cpp b/src/lauf/lib/int.cpp index b3a18e0c..b69ea3a3 100644 --- a/src/lauf/lib/int.cpp +++ b/src/lauf/lib/int.cpp @@ -23,6 +23,7 @@ case LAUF_LIB_INT_OVERFLOW_PANIC: \ return Name##_panic; \ } \ + LAUF_UNREACHABLE; \ } namespace @@ -537,6 +538,8 @@ LAUF_RUNTIME_BUILTIN_IMPL bool load_int(const lauf_asm_inst* ip, lauf_runtime_va if constexpr (std::is_unsigned_v) vstack_ptr[1].as_uint = value; + else if constexpr (std::is_same_v) + vstack_ptr[1].as_sint = static_cast(value); else vstack_ptr[1].as_sint = value; @@ -670,4 +673,3 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_int_u64_overflow, 1, 2, no_panic_flags, "u64_overf const lauf_runtime_builtin_library lauf_lib_int = {"lauf.int", &lauf_lib_int_u64_overflow, &lauf_lib_int_u64}; - diff --git a/src/lauf/lib/memory.cpp b/src/lauf/lib/memory.cpp index 0be9942d..2fe4bd02 100644 --- a/src/lauf/lib/memory.cpp +++ b/src/lauf/lib/memory.cpp @@ -210,6 +210,7 @@ lauf_runtime_builtin lauf_lib_memory_addr_add(lauf_lib_memory_addr_overflow over case LAUF_LIB_MEMORY_ADDR_OVERFLOW_PANIC_STRICT: return addr_add_panic_strict; } + LAUF_UNREACHABLE; } lauf_runtime_builtin lauf_lib_memory_addr_sub(lauf_lib_memory_addr_overflow overflow) @@ -223,6 +224,7 @@ lauf_runtime_builtin lauf_lib_memory_addr_sub(lauf_lib_memory_addr_overflow over case LAUF_LIB_MEMORY_ADDR_OVERFLOW_PANIC_STRICT: return addr_sub_panic_strict; } + LAUF_UNREACHABLE; } LAUF_RUNTIME_BUILTIN(lauf_lib_memory_addr_distance, 2, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, @@ -296,4 +298,3 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_memory_cmp, 3, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "c } const lauf_runtime_builtin_library lauf_lib_memory = {"lauf.memory", &lauf_lib_memory_cmp, nullptr}; - diff --git a/src/lauf/runtime/memory.cpp b/src/lauf/runtime/memory.cpp index 25ff7193..0a15ae43 100644 --- a/src/lauf/runtime/memory.cpp +++ b/src/lauf/runtime/memory.cpp @@ -126,7 +126,7 @@ bool lauf_runtime_get_address(lauf_runtime_process* p, lauf_runtime_address* all lauf_runtime_address lauf_runtime_get_global_address(lauf_runtime_process*, const lauf_asm_global* global) { - return {global->allocation_idx, 0, 0}; + LAUF_BITFIELD_CONVERSION(return {global->allocation_idx, 0, 0}); } const char* lauf_runtime_get_cstr(lauf_runtime_process* p, lauf_runtime_address addr) @@ -295,9 +295,9 @@ size_t lauf_runtime_gc(lauf_runtime_process* p) // (We've done a size check already, so the initial offset is fine) auto offset = lauf::align_offset(alloc->ptr, alignof(lauf_runtime_value)); auto ptr = reinterpret_cast(static_cast(alloc->ptr) - + offset); + + offset); - for (auto end = ptr + (alloc->size - offset) / sizeof(lauf_runtime_value); ptr != end; + for (auto end = ptr + ((alloc->size - offset) / sizeof(lauf_runtime_value)); ptr != end; ++ptr) { auto ptr_alloc = p->memory.try_get(ptr->as_address); @@ -527,4 +527,3 @@ bool lauf_runtime_undeclare_weak(lauf_runtime_process* p, lauf_runtime_address a alloc->is_gc_weak = false; return true; } - diff --git a/src/lauf/runtime/memory.hpp b/src/lauf/runtime/memory.hpp index 9412d8c2..dc2d8fb5 100644 --- a/src/lauf/runtime/memory.hpp +++ b/src/lauf/runtime/memory.hpp @@ -41,6 +41,7 @@ constexpr bool is_const(allocation_source source) case allocation_source::heap_memory: return false; } + LAUF_UNREACHABLE; } enum class allocation_status : std::uint8_t @@ -203,13 +204,14 @@ class memory { auto index = _allocations.size(); _allocations.push_back(allocator, alloc); - return {std::uint32_t(index), alloc.generation, 0}; + LAUF_BITFIELD_CONVERSION(return {std::uint32_t(index), alloc.generation, 0}); } lauf_runtime_address new_allocation_unchecked(allocation alloc) { auto index = _allocations.size(); _allocations.push_back_unchecked(alloc); - return {std::uint32_t(index), alloc.generation, 0}; + LAUF_BITFIELD_CONVERSION( + return lauf_runtime_address{std::uint32_t(index), alloc.generation, 0}); } allocation& operator[](std::size_t index) @@ -277,4 +279,3 @@ class memory } // namespace lauf #endif // LAUF_RUNTIME_MEMORY_HPP_INCLUDED - diff --git a/src/lauf/runtime/process.cpp b/src/lauf/runtime/process.cpp index 7002e432..8290fafd 100644 --- a/src/lauf/runtime/process.cpp +++ b/src/lauf/runtime/process.cpp @@ -90,7 +90,8 @@ LAUF_NOINLINE void lauf_runtime_process::do_cleanup(lauf_runtime_process* proces // We don't know the full size. vm->heap_allocator.free_alloc(vm->heap_allocator.user_data, alloc.ptr, 0); else - ; // We don't know the starting address of the allocation. + { // We don't know the starting address of the allocation. + } } else if (alloc.source == lauf::allocation_source::fiber_memory) { @@ -156,6 +157,7 @@ lauf_runtime_fiber_status lauf_runtime_get_fiber_status(const lauf_runtime_fiber case lauf_runtime_fiber::running: return LAUF_RUNTIME_FIBER_RUNNING; } + LAUF_UNREACHABLE; } lauf_runtime_fiber* lauf_runtime_get_fiber_parent(lauf_runtime_process* process, @@ -386,4 +388,3 @@ bool lauf_runtime_increment_step(lauf_runtime_process* process) return true; } - diff --git a/src/lauf/runtime/stacktrace.cpp b/src/lauf/runtime/stacktrace.cpp index e15ca9f4..4e09aa4c 100644 --- a/src/lauf/runtime/stacktrace.cpp +++ b/src/lauf/runtime/stacktrace.cpp @@ -29,6 +29,7 @@ lauf_runtime_stacktrace* lauf_runtime_get_stacktrace(lauf_runtime_process* p case lauf_runtime_fiber::done: return nullptr; } + LAUF_UNREACHABLE; } const lauf_asm_function* lauf_runtime_stacktrace_function(lauf_runtime_stacktrace* st) @@ -60,4 +61,3 @@ void lauf_runtime_destroy_stacktrace(lauf_runtime_stacktrace* st) { delete st; } - diff --git a/src/lauf/support/array.hpp b/src/lauf/support/array.hpp index 22db323b..d240a10d 100644 --- a/src/lauf/support/array.hpp +++ b/src/lauf/support/array.hpp @@ -4,6 +4,7 @@ #ifndef SRC_LAUF_SUPPORT_ARRAY_HPP_INCLUDED #define SRC_LAUF_SUPPORT_ARRAY_HPP_INCLUDED +#include #include #include #include @@ -181,12 +182,11 @@ class array } auto new_capacity = 2 * _capacity; - if (new_capacity < new_size) - new_capacity = new_size; + new_capacity = std::max(new_capacity, new_size); if (!_is_heap && arena.try_expand(_ptr, _capacity, new_capacity)) { - _capacity = new_capacity; + LAUF_BITFIELD_CONVERSION(_capacity = new_capacity); } else { @@ -195,9 +195,9 @@ class array if (_is_heap) ::operator delete(_ptr); - _ptr = static_cast(new_memory); - _capacity = new_capacity; - _is_heap = true; + _ptr = static_cast(new_memory); + LAUF_BITFIELD_CONVERSION(_capacity = new_capacity); + _is_heap = true; } } @@ -207,8 +207,7 @@ class array return; auto new_capacity = 2 * _capacity; - if (new_capacity < new_size) - new_capacity = new_size; + new_capacity = std::max(new_capacity, new_size); if (_capacity == 0) { @@ -228,7 +227,7 @@ class array } else { - _capacity = extended_page_size / sizeof(T); + LAUF_BITFIELD_CONVERSION(_capacity = extended_page_size / sizeof(T)); } } } @@ -293,8 +292,8 @@ class array } void set_pages(page_block block) { - _ptr = static_cast(block.ptr); - _capacity = block.size / sizeof(T); + _ptr = static_cast(block.ptr); + LAUF_BITFIELD_CONVERSION(_capacity = block.size / sizeof(T)); } T* _ptr; @@ -305,4 +304,3 @@ class array } // namespace lauf #endif // SRC_LAUF_SUPPORT_ARRAY_HPP_INCLUDED - diff --git a/src/lauf/vm_execute.cpp b/src/lauf/vm_execute.cpp index 92b7e34e..fc033ed6 100644 --- a/src/lauf/vm_execute.cpp +++ b/src/lauf/vm_execute.cpp @@ -51,6 +51,18 @@ LAUF_FORCE_INLINE bool lauf::_vm_dispatch(const lauf_asm_inst* ip, lauf_runtime_ } #endif +#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__)) +# undef lauf_runtime_builtin_dispatch +LAUF_RUNTIME_BUILTIN_IMPL bool lauf_runtime_builtin_dispatch(const lauf_asm_inst* ip, + lauf_runtime_value* vstack_ptr, + lauf_runtime_stack_frame* frame_ptr, + lauf_runtime_process* process) +{ + return lauf_runtime_builtin_dispatch_inline(ip, vstack_ptr, frame_ptr, process); +} +# define lauf_runtime_builtin_dispatch lauf_runtime_builtin_dispatch_inline +#endif + //=== helper functions ===// // We move expensive calls into a separate function that is tail called. // That way the the hot path contains no function calls, so the compiler doesn't spill stuff. @@ -320,10 +332,18 @@ LAUF_VM_EXECUTE(call_builtin) LAUF_VM_EXECUTE(call_builtin_no_regs) { + // This is a runtime function pointer, needs to specifically refer to + // lauf_runtime_builtin_dispatch +#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__)) +# undef lauf_runtime_builtin_dispatch +#endif auto callee = lauf::uncompress_pointer_offset(&lauf_runtime_builtin_dispatch, ip->call_builtin_no_regs .offset); +#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__)) +# define lauf_runtime_builtin_dispatch lauf_runtime_builtin_dispatch_inline +#endif LAUF_TAIL_CALL return callee(ip, vstack_ptr, frame_ptr, process); } @@ -498,8 +518,9 @@ LAUF_VM_EXECUTE(global_addr) { --vstack_ptr; - vstack_ptr[0].as_address.allocation - = get_global_allocation_idx(frame_ptr, process, ip->global_addr.value); + LAUF_BITFIELD_CONVERSION( + vstack_ptr[0].as_address.allocation + = get_global_allocation_idx(frame_ptr, process, ip->global_addr.value)); vstack_ptr[0].as_address.offset = 0; vstack_ptr[0].as_address.generation = 0; // Always true for globals. @@ -526,7 +547,7 @@ LAUF_VM_EXECUTE(local_addr) auto allocation_idx = frame_ptr->first_local_alloc + ip->local_addr.index; --vstack_ptr; - vstack_ptr[0].as_address.allocation = std::uint32_t(allocation_idx); + LAUF_BITFIELD_CONVERSION(vstack_ptr[0].as_address.allocation = std::uint32_t(allocation_idx)); vstack_ptr[0].as_address.offset = 0; vstack_ptr[0].as_address.generation = frame_ptr->local_generation; @@ -669,8 +690,8 @@ LAUF_VM_EXECUTE(setup_local_alloc) LAUF_TAIL_CALL return grow_allocation_array(ip, vstack_ptr, frame_ptr, process); // Setup the necessary metadata. - frame_ptr->first_local_alloc = process->memory.next_index(); - frame_ptr->local_generation = process->memory.cur_generation(); + LAUF_BITFIELD_CONVERSION(frame_ptr->first_local_alloc = process->memory.next_index()); + LAUF_BITFIELD_CONVERSION(frame_ptr->local_generation = process->memory.cur_generation()); ++ip; LAUF_VM_DISPATCH; @@ -693,11 +714,12 @@ LAUF_VM_EXECUTE(local_alloc) LAUF_VM_EXECUTE(local_alloc_aligned) { // We need to ensure the starting address is aligned. - auto memory = static_cast(frame_ptr->next_frame()); + auto memory = frame_ptr->next_frame(); memory += lauf::align_offset(memory, ip->local_alloc_aligned.alignment()); // However, to increment the offset we need both alignment and size, as that was the offset // computation assumed in the builder. - frame_ptr->next_offset += ip->local_alloc_aligned.alignment() + ip->local_alloc.size; + frame_ptr->next_offset + += static_cast(ip->local_alloc_aligned.alignment()) + ip->local_alloc.size; process->memory.new_allocation_unchecked( lauf::make_local_alloc(memory, ip->local_alloc.size, frame_ptr->local_generation)); @@ -766,7 +788,7 @@ LAUF_VM_EXECUTE(array_element) auto address = vstack_ptr[1].as_address; auto index = vstack_ptr[0].as_sint; - address.offset += lauf_sint(ip->array_element.value) * index; + address.offset += static_cast(lauf_sint(ip->array_element.value) * index); ++vstack_ptr; vstack_ptr[0].as_address = address; @@ -830,4 +852,3 @@ LAUF_VM_EXECUTE(store_global_value) ++ip; LAUF_VM_DISPATCH; } - diff --git a/src/lauf/vm_execute.hpp b/src/lauf/vm_execute.hpp index c0682c34..ca5b4a7b 100644 --- a/src/lauf/vm_execute.hpp +++ b/src/lauf/vm_execute.hpp @@ -57,14 +57,29 @@ inline bool execute(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr, } } // namespace lauf +// Clang supports inline of previously non-inlined function +// GCC does +#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__)) +extern "C" [[gnu::always_inline]] inline bool lauf_runtime_builtin_dispatch_inline( + const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr, lauf_runtime_stack_frame* frame_ptr, + lauf_runtime_process* process) +#else inline bool lauf_runtime_builtin_dispatch(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr, lauf_runtime_stack_frame* frame_ptr, lauf_runtime_process* process) +#endif { assert(ip[1].op() == lauf::asm_op::call_builtin_sig); ip += 2; LAUF_VM_DISPATCH; } -#endif // SRC_LAUF_VM_EXECUTE_HPP_INCLUDED +#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__)) +# define lauf_runtime_builtin_dispatch lauf_runtime_builtin_dispatch_inline +# undef LAUF_RUNTIME_BUILTIN_DISPATCH +# define LAUF_RUNTIME_BUILTIN_DISPATCH \ + LAUF_TAIL_CALL return lauf_runtime_builtin_dispatch_inline(ip, vstack_ptr, frame_ptr, \ + process) +#endif +#endif // SRC_LAUF_VM_EXECUTE_HPP_INCLUDED diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 991c7bc7..62ba6efc 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -3,6 +3,12 @@ add_custom_target(lauf_test_qbe ALL) +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + set(USE_COMPILER clang) +else(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + set(USE_COMPILER gcc) +endif() + file(GLOB test_files CONFIGURE_DEPENDS "*.lauf") foreach(file ${test_files}) get_filename_component(name ${file} NAME) @@ -10,7 +16,7 @@ foreach(file ${test_files}) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}.qbe COMMAND lauf_tool_qbe ${file} > ${name}.qbe DEPENDS ${file} lauf_tool_qbe) add_custom_command(OUTPUT ${name}.s COMMAND qbe ${CMAKE_CURRENT_BINARY_DIR}/${name}.qbe -o ${name}.s DEPENDS ${name}.qbe) - add_custom_command(OUTPUT ${name}.exe COMMAND clang ${CMAKE_CURRENT_SOURCE_DIR}/runtime.c ${name}.s -o ${name}.exe + add_custom_command(OUTPUT ${name}.exe COMMAND ${USE_COMPILER} ${CMAKE_CURRENT_SOURCE_DIR}/runtime.c ${name}.s -o ${name}.exe DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/runtime.c ${name}.s) add_custom_target(lauf_test_qbe_${name} DEPENDS ${name}.exe) diff --git a/tests/lauf/vm.cpp b/tests/lauf/vm.cpp index 82084f0c..a6f42ea0 100644 --- a/tests/lauf/vm.cpp +++ b/tests/lauf/vm.cpp @@ -494,4 +494,3 @@ TEST_CASE("lauf_asm_define_native_function") lauf_asm_destroy_module(mod); } -