Skip to content
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
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,53 @@ jobs:
working-directory: build/
run: ctest --output-on-failure

windows:
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]

runs-on: windows-2022

steps:
- uses: actions/checkout@v2
- name: Create Build Environment
run: cmake -E make_directory build

- name: Install Chocolatey and YASM
uses: crazy-max/ghaction-chocolatey@v3
with:
args: install yasm -y
- name: Verify YASM Installation
run: yasm --version

- name: Set up MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: make mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb
- name: Download QBE
shell: msys2 {0}
run: curl https://c9x.me/compile/release/qbe-1.0.tar.xz -o qbe.tar.xz && tar xf qbe.tar.xz
- name: Build QBE
working-directory: qbe-1.0/
shell: msys2 {0}
run: |
make CC="cc" CFLAGS="-std=c99 -g -Wall -Wextra -Wpedantic -O2 -static"
mv qbe $GITHUB_WORKSPACE/qbe.exe
echo "$GITHUB_WORKSPACE" >> "$GITHUB_PATH"
- name: Verify QBE Outside MSYS2
run: qbe.exe -h

- name: Configure
shell: bash
working-directory: build/
run: cmake $GITHUB_WORKSPACE -G"Visual Studio 17 2022"
- name: Build
working-directory: build/
run: cmake --build . --config ${{matrix.build_type}}
- name: Test
working-directory: build/
run: ctest -C ${{matrix.build_type}} --output-on-failure

18 changes: 17 additions & 1 deletion include/lauf/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ typedef uint64_t lauf_uint;
# if defined(__has_cpp_attribute)
# if __has_cpp_attribute(clang::musttail)
# define LAUF_TAIL_CALL [[clang::musttail]]
# ifndef LAUF_HAS_TAIL_CALL_ELIMINATION
# define LAUF_HAS_TAIL_CALL_ELIMINATION 1
# endif
# elif defined(__clang__)
# define LAUF_TAIL_CALL [[clang::musttail]]
# else
Expand All @@ -52,6 +55,13 @@ typedef uint64_t lauf_uint;
# define LAUF_NOINLINE [[gnu::noinline]]
# define LAUF_FORCE_INLINE [[gnu::always_inline]] inline
# define LAUF_UNREACHABLE __builtin_unreachable()
#elif defined(_MSC_VER)
# define LAUF_LIKELY(Cond) (Cond)
# define LAUF_UNLIKELY(Cond) (Cond)
# define LAUF_TAIL_CALL
# define LAUF_NOINLINE __declspec(noinline)
# define LAUF_FORCE_INLINE __forceinline
# define LAUF_UNREACHABLE __assume(0)
#endif

//=== configurations ===//
Expand All @@ -60,7 +70,7 @@ typedef uint64_t lauf_uint;
#endif

#ifndef LAUF_HAS_TAIL_CALL_ELIMINATION
# define LAUF_HAS_TAIL_CALL_ELIMINATION 1
# define LAUF_HAS_TAIL_CALL_ELIMINATION 0
#endif

//=== warnings ===//
Expand All @@ -70,6 +80,12 @@ typedef uint64_t lauf_uint;
_Pragma("GCC diagnostic ignored \"-Wconversion\""); \
__VA_ARGS__; \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
# define LAUF_BITFIELD_CONVERSION(...) \
_Pragma("warning(push)"); \
_Pragma("warning(disable : 4267)"); \
__VA_ARGS__; \
_Pragma("warning(pop)")
#else
# define LAUF_BITFIELD_CONVERSION(...) __VA_ARGS__
#endif
Expand Down
2 changes: 2 additions & 0 deletions include/lauf/runtime/builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ LAUF_HEADER_START
# endif
#elif defined(__GNUC__) || defined(__GNUG__)
# define LAUF_RUNTIME_BUILTIN_IMPL __attribute__((section(".text.lauf_builtin"), aligned(8)))
#elif defined(_MSC_VER)
# define LAUF_RUNTIME_BUILTIN_IMPL __declspec(code_seg(".text.lauf_builtin"))
#else
# define LAUF_RUNTIME_BUILTIN_IMPL
#endif
Expand Down
1 change: 0 additions & 1 deletion include/lauf/runtime/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,3 @@ bool lauf_runtime_undeclare_weak(lauf_runtime_process* p, lauf_runtime_address a
LAUF_HEADER_END

#endif // LAUF_RUNTIME_MEMORY_H_INCLUDED

7 changes: 3 additions & 4 deletions include/lauf/runtime/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ typedef struct lauf_runtime_address
// acess to allocation is an AND, acess to offset a SHIFT, access to generation SHIFT + AND
// (which is the one only necessary for checks). In addition, treating it as an integer and e.g.
// incrementing it changes allocation first, not offset. That way, bugs are caught earlier.
uint64_t allocation : 30;
uint64_t generation : 2;
uint64_t offset : 32;
uint32_t allocation : 30;
uint32_t generation : 2;
uint32_t offset;
} lauf_runtime_address;

static const lauf_runtime_address lauf_runtime_address_null = {0x3FFFFFFF, 0x3, 0xFFFFFFFF};
Expand All @@ -43,4 +43,3 @@ typedef union lauf_runtime_value
LAUF_HEADER_END

#endif // LAUF_RUNTIME_VALUE_H_INCLUDED

20 changes: 15 additions & 5 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,28 @@ 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)

if(NOT LAUF_DISPATCH_JUMP_TABLE)
if(MSVC)
if(NOT LAUF_DISPATCH_JUMP_TABLE)
messsage(WARNING "MSVC does not support disabling of jump tables")
endif()
elseif(NOT LAUF_DISPATCH_JUMP_TABLE)
target_compile_definitions(lauf_core PUBLIC LAUF_CONFIG_DISPATCH_JUMP_TABLE=0)
target_compile_options(lauf_core PRIVATE -fno-jump-tables)
endif()
# Since we're using tail calls for dispatching, we don't want to add frame pointers, ever.
# They would record all previously executed instructions in the call stack.
target_compile_options(lauf_core PRIVATE -fomit-frame-pointer)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
target_compile_options(lauf_core PRIVATE -fomit-frame-pointer)
elseif(MSVC)
target_compile_options(lauf_core PRIVATE /Oy)
endif()

if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(lauf_core PUBLIC LAUF_HAS_TAIL_CALL_ELIMINATION=0)
endif()
# Lacking musttail, GCC will not optimize tail calls without optimizations enabled
target_compile_definitions(lauf_core PUBLIC "LAUF_HAS_TAIL_CALL_ELIMINATION=$<NOT:$<CONFIG:Debug>>")
elseif(MSVC)
# MSVC does not appear capable of properly optimizing tail calls
target_compile_definitions(lauf_core PUBLIC "LAUF_HAS_TAIL_CALL_ELIMINATION=0")
endif()

target_sources(lauf_core PUBLIC
Expand Down
6 changes: 3 additions & 3 deletions src/lauf/asm/builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,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;

LAUF_BITFIELD_CONVERSION(jump->jump.offset = std::int32_t(dest_offset - cur_offset));
jump->jump.offset(std::int32_t(dest_offset - cur_offset));
}

return ip;
Expand Down Expand Up @@ -459,7 +459,7 @@ void emit_debug_location(lauf_asm_builder* b)

bool lauf_asm_build_finish(lauf_asm_builder* b)
{
constexpr auto context = LAUF_BUILD_ASSERT_CONTEXT;
static constexpr auto context = LAUF_BUILD_ASSERT_CONTEXT;

auto insts = [&] {
auto inst_count = estimate_inst_count(context, b);
Expand Down Expand Up @@ -673,7 +673,7 @@ const lauf_asm_block* lauf_asm_inst_branch(lauf_asm_builder* b, const lauf_asm_b
else if (!b->cur->insts.empty() && b->cur->insts.back().op() == lauf::asm_op::cc)
{
// Remove the cc instruction.
auto cc = b->cur->insts.back().cc.value;
auto cc = b->cur->insts.back().cc.value();
b->cur->insts.pop_back();

switch (cc)
Expand Down
4 changes: 2 additions & 2 deletions src/lauf/asm/builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ struct lauf_asm_builder : lauf::intrinsic_arena<lauf_asm_builder>
[&](const char* context, std::ptrdiff_t offset) { \
lauf_asm_inst result; \
LAUF_BITFIELD_CONVERSION(result.Name = {lauf::asm_op::Name, std::int32_t(offset)}); \
if (result.Name.offset != offset) \
if (result.Name.offset() != offset) \
b->error(context, "offset too big"); \
return result; \
}(LAUF_BUILD_ASSERT_CONTEXT, static_cast<std::int64_t>(Offset))
Expand Down Expand Up @@ -316,7 +316,7 @@ struct lauf_asm_builder : lauf::intrinsic_arena<lauf_asm_builder>
[&](const char* context, std::size_t value) { \
lauf_asm_inst result; \
LAUF_BITFIELD_CONVERSION(result.Name = {lauf::asm_op::Name, std::uint32_t(value)}); \
if (value != result.Name.value) \
if (value != result.Name.value()) \
b->error(context, "invalid value"); \
return result; \
}(LAUF_BUILD_ASSERT_CONTEXT, Value)
Expand Down
109 changes: 99 additions & 10 deletions src/lauf/asm/instruction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,60 @@ struct asm_inst_none
asm_op op;
};

#pragma pack(1)
struct asm_inst_offset
{
asm_op op : 8;
std::int32_t offset : 24;
asm_op op : 8;

constexpr std::int32_t offset() const
{
#if _MSC_VER
return (static_cast<int32_t>(static_cast<int8_t>(_offset[2])) << 16) | (_offset[1] << 8)
| _offset[0];

#else
return _offset;
#endif
}
constexpr std::int32_t offset(std::int32_t value)
{
#if _MSC_VER
_offset[0] = static_cast<uint8_t>(value & 0xFF);
_offset[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
_offset[2] = static_cast<uint8_t>((value >> 16) & 0xFF);
return value;
#else
LAUF_BITFIELD_CONVERSION(return _offset = value);
#endif
}

asm_inst_offset() = default;
constexpr asm_inst_offset(asm_op op, std::int32_t offset) : op(op), _offset{}
{
this->offset(offset);
}

private:
#if _MSC_VER
std::uint8_t _offset[3];
#else
std::int32_t _offset : 24;
#endif
};
#pragma pack()

template <typename CurType, typename DestType>
std::ptrdiff_t compress_pointer_offset(CurType* _cur, DestType* _dest)
{
auto cur = (void*)(_cur);
auto dest = (void*)(_dest);
assert(is_aligned(cur, alignof(void*)) && is_aligned(dest, alignof(void*)));
return (void**)dest - (void**)cur;
auto cur = (char*)(_cur);
auto dest = (char*)(_dest);
return _dest ? dest - cur : 0;
}

template <typename DestType, typename CurType>
const DestType* uncompress_pointer_offset(CurType* cur, std::ptrdiff_t offset)
{
return (const DestType*)(reinterpret_cast<void* const*>(cur) + offset);
return (DestType*)(reinterpret_cast<const char*>(cur) + offset);
}

struct asm_inst_signature
Expand All @@ -89,11 +124,66 @@ struct asm_inst_layout
}
};

#pragma pack(1)
struct asm_inst_value
{
asm_op op : 8;
std::uint32_t value : 24;
asm_op op : 8;

constexpr std::uint32_t value() const
{
#if _MSC_VER
if (__builtin_is_constant_evaluated())
{
std::uint8_t array[sizeof(std::uint32_t)]{_value[0], _value[1], _value[2]};
return __builtin_bit_cast(std::uint32_t, array);
}
else
{
return *reinterpret_cast<std::uint8_t const*>(_value)
| (*reinterpret_cast<std::uint16_t const*>(_value + 1) << 8);
}
#else
return _value;
#endif
}
constexpr std::uint32_t value(std::uint32_t value)
{
#if _MSC_VER
if (__builtin_is_constant_evaluated())
{
struct array_type
{
std::uint8_t array[sizeof(value)];
};
auto array = __builtin_bit_cast(array_type, value);
_value[0] = array.array[0];
_value[1] = array.array[1];
_value[2] = array.array[2];
return value;
}
else
{
return *reinterpret_cast<std::uint32_t*>(_value) = value;
}
#else
LAUF_BITFIELD_CONVERSION(return _value = value);
#endif
}

asm_inst_value() = default;
constexpr asm_inst_value(asm_op op, std::uint32_t value) : op(op), _value{}
{
this->value(value);
}

private:
#if _MSC_VER
std::uint8_t _value[3];
#else
std::uint32_t _value : 24;
#endif
};
#pragma pack()

struct asm_inst_stack_idx
{
Expand Down Expand Up @@ -127,4 +217,3 @@ union lauf_asm_inst
};

#endif // SRC_LAUF_ASM_INSTRUCTION_HPP_INCLUDED

Loading