Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GCC support #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ FetchContent_MakeAvailable(nanobench)
add_executable(lauf_benchmark_chunk)
target_sources(lauf_benchmark_chunk PRIVATE chunk.cpp)
target_link_libraries(lauf_benchmark_chunk PRIVATE foonathan::lauf::core nanobench)
target_compile_features(lauf_benchmark_chunk PRIVATE cxx_std_17)
target_compile_features(lauf_benchmark_chunk PRIVATE cxx_std_20)

130 changes: 121 additions & 9 deletions include/lauf/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,134 @@ typedef uint64_t lauf_uint;
# error "lauf assumes 8 bit bytes"
#endif

#if !defined(__clang__)
/* #if !defined(__clang__)
# error "lauf currently requires clang"
#endif
#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()
#define LAUF_LIKELY(Cond) Cond /*__builtin_expect((Cond), 1)*/
#define LAUF_UNLIKELY(Cond) Cond /*__builtin_expect((Cond), 0)*/
#if defined(__has_cpp_attribute)
# if __has_cpp_attribute(gnu::musttail)
# define LAUF_TAIL_CALL [[gnu::musttail]]
# elif __has_cpp_attribute(clang::musttail)
# define LAUF_TAIL_CALL [[clang::musttail]]
# else
# define LAUF_TAIL_CALL
# endif
#elif defined(__clang__)
# define LAUF_TAIL_CALL [[clang::musttail]]
#else
# define LAUF_TAIL_CALL
#endif
#if defined(_MSC_VER)
# define LAUF_NOINLINE __declspec(noinline)
# define LAUF_FORCE_INLINE __forceinline
# define LAUF_UNREACHABLE __assume(0)
#else
# 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(__clang__) && (defined(__GNUC__) || defined(__GNUG__))
# define LAUF_IGNORE_BITFIELD_WARNING(...) \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wconversion\""); \
__VA_ARGS__; \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
# define LAUF_IGNORE_BITFIELD_WARNING(...) \
_Pragma("warning(push)"); \
_Pragma("warning(disable : 4267)"); \
__VA_ARGS__; \
_Pragma("warning(pop)")
#else
# define LAUF_IGNORE_BITFIELD_WARNING(...) __VA_ARGS__
#endif

#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__))
# define LAUF_IGNORE_CONV_WARNING(...) \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wconversion\""); \
__VA_ARGS__; \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
# define LAUF_IGNORE_CONV_WARNING(...) \
_Pragma("warning(push)"); \
_Pragma("warning(disable : 4267)"); \
__VA_ARGS__; \
_Pragma("warning(pop)")
#else
# define LAUF_IGNORE_CONV_WARNING(...) __VA_ARGS__
#endif

#if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__))
# define LAUF_IGNORE_SIGN_WARNING(...) \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wsign-conversion\""); \
__VA_ARGS__; \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
# define LAUF_IGNORE_SIGN_WARNING(...) \
_Pragma("warning(push)"); \
_Pragma("warning(disable : 4267)"); \
__VA_ARGS__; \
_Pragma("warning(pop)")
#else
# define LAUF_IGNORE_SIGN_WARNING(...) __VA_ARGS__
#endif

#if defined(__GNUC__) && (__GNUC__ == 12 && __GNUC_MINOR__ < 3)
# define LAUF_WORKAROUND_GCC_BOGUS_MEMCPY 1
#else
# define LAUF_WORKAROUND_GCC_BOGUS_MEMCPY 0
#endif

#if defined(_MSC_VER) && !defined(__clang__)
# include <bit>
template <typename T>
static LAUF_FORCE_INLINE bool __builtin_add_overflow(uint64_t a, uint64_t b, T* out)
{
unsigned long long tmp;
# if defined(_M_IX86) || defined(_M_X64)
auto carry = _addcarry_u64(0, a, b, &tmp);
# else
tmp = a + b;
unsigned long long vector = (a & b) ^ ((a ^ b) & ~tmp);
auto carry = vector >> 63;
# endif
*out = tmp;
return carry;
}

template <typename T>
static LAUF_FORCE_INLINE bool __builtin_sub_overflow(uint64_t a, uint64_t b, T* out)
{
unsigned long long tmp;
# if defined(_M_IX86) || defined(_M_X64)
auto borrow = _subborrow_u64(0, a, b, &tmp);
# else
tmp = a - b;
unsigned long long vector = ((a ^ b) & a);
auto borrow = vector >> 63;
# endif
*out = tmp;
return borrow;
}

template <typename T>
static LAUF_FORCE_INLINE bool __builtin_mul_overflow(uint64_t a, uint64_t b, T* out)
{
*out = a * b;
// This test isnt exact, but avoids doing integer division
return ((std::countl_zero(a) + std::countl_zero(b)) < 64);
}
#endif

#endif // LAUF_CONFIG_H_INCLUDED
11 changes: 9 additions & 2 deletions include/lauf/runtime/builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@

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
#else
# define LAUF_RUNTIME_BUILTIN_IMPL
#endif

typedef union lauf_asm_inst lauf_asm_inst;
typedef struct lauf_asm_type lauf_asm_type;
Expand Down Expand Up @@ -92,4 +100,3 @@ typedef struct lauf_runtime_builtin_library
LAUF_HEADER_END

#endif // LAUF_RUNTIME_BUILTIN_H_INCLUDED

4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ endif()
#=== Core library target ===#
add_library(lauf_core)
add_library(foonathan::lauf::core ALIAS lauf_core)
target_compile_features(lauf_core PRIVATE cxx_std_17)
target_compile_features(lauf_core PRIVATE cxx_std_20)
target_include_directories(lauf_core SYSTEM INTERFACE ../include)
target_include_directories(lauf_core PRIVATE ../include .)
target_link_libraries(lauf_core PRIVATE lauf_warnings foonathan::lexy)
Expand Down Expand Up @@ -133,7 +133,7 @@ target_sources(lauf_text PRIVATE ${src_dir}/frontend/text.cpp)
add_library(lauf_qbe)
add_library(foonathan::lauf::qbe ALIAS lauf_qbe)
target_include_directories(lauf_qbe PRIVATE .)
target_compile_features(lauf_qbe PRIVATE cxx_std_17)
target_compile_features(lauf_qbe PRIVATE cxx_std_20)
target_link_libraries(lauf_qbe PRIVATE lauf_warnings lauf_core)

target_sources(lauf_qbe PUBLIC ${include_dir}/backend/qbe.h)
Expand Down
12 changes: 6 additions & 6 deletions src/lauf/asm/builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: BSL-1.0

#include <lauf/asm/builder.hpp>
#include <lauf/config.h>

#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -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_IGNORE_BITFIELD_WARNING(jump->jump.offset = std::int32_t(dest_offset - cur_offset));
}

return ip;
Expand Down Expand Up @@ -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;
LAUF_IGNORE_CONV_WARNING(loc.inst_idx += block.offset + 1);
cont.push_back(arena, loc);
}
}
Expand Down Expand Up @@ -541,7 +542,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;
LAUF_IGNORE_CONV_WARNING(b->local_allocation_size += layout.size);
}
else
{
Expand All @@ -553,7 +554,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;
LAUF_IGNORE_CONV_WARNING(b->local_allocation_size += layout.alignment + layout.size);
// Since we don't know the exact alignment offset, we can't compute it statically.
offset = UINT16_MAX;
}
Expand Down Expand Up @@ -1006,7 +1007,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_IGNORE_BITFIELD_WARNING(result.as_address.allocation = global->allocation_idx);
result.as_address.offset = 0;
result.as_address.generation = 0; // Always true for globals.
return result;
Expand Down Expand Up @@ -1381,4 +1382,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);
}
}

13 changes: 7 additions & 6 deletions src/lauf/asm/builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ struct lauf_asm_block
: sig{input_count, 0}, vstack(arena, input_count), terminator(unterminated), next{}
{}
};
static_assert(std::is_trivially_copyable_v<lauf_asm_block>);

struct lauf_asm_local
{
Expand Down Expand Up @@ -260,14 +261,14 @@ struct lauf_asm_builder : lauf::intrinsic_arena<lauf_asm_builder>
#define LAUF_BUILD_ASSERT(Cond, Msg) \
do \
{ \
if (LAUF_UNLIKELY(!(Cond))) \
if (LAUF_UNLIKELY(!(Cond))) [[unlikely]] \
b->error(LAUF_BUILD_ASSERT_CONTEXT, Msg); \
} while (0)

#define LAUF_BUILD_CHECK_CUR \
do \
{ \
if (LAUF_UNLIKELY(b->cur == nullptr)) \
if (LAUF_UNLIKELY(b->cur == nullptr)) [[unlikely]] \
return; \
} while (0)

Expand All @@ -282,7 +283,7 @@ struct lauf_asm_builder : lauf::intrinsic_arena<lauf_asm_builder>
#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_IGNORE_BITFIELD_WARNING(result.Name = {lauf::asm_op::Name, std::int32_t(offset)}); \
if (result.Name.offset != offset) \
b->error(context, "offset too big"); \
return result; \
Expand Down Expand Up @@ -315,8 +316,9 @@ struct lauf_asm_builder : lauf::intrinsic_arena<lauf_asm_builder>
#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)}; \
if (value != result.Name.value) \
LAUF_IGNORE_BITFIELD_WARNING( \
result.Name = {lauf::asm_op::Name, static_cast<std::int32_t>(std::uint32_t(value))}); \
if (value != static_cast<std::size_t>(result.Name.value)) \
b->error(context, "invalid value"); \
return result; \
}(LAUF_BUILD_ASSERT_CONTEXT, Value)
Expand All @@ -331,4 +333,3 @@ struct lauf_asm_builder : lauf::intrinsic_arena<lauf_asm_builder>
}(LAUF_BUILD_ASSERT_CONTEXT, Index)

#endif // SRC_LAUF_ASM_BUILDER_HPP_INCLUDED

Loading