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
9 changes: 8 additions & 1 deletion include/lauf/asm/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ typedef union lauf_runtime_value lauf_runtime_value;
typedef struct lauf_runtime_process lauf_runtime_process;
typedef struct lauf_runtime_stack_frame lauf_runtime_stack_frame;

#if LAUF_HAS_TAIL_CALL_ELIMINATION
typedef bool lauf_runtime_builtin_impl(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process);
#else
typedef union lauf_tail_call_result lauf_tail_call_result;
typedef lauf_tail_call_result lauf_runtime_builtin_impl(const lauf_asm_inst* ip,
lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process);
#endif

/// The layout of a type.
typedef struct lauf_asm_layout
Expand Down Expand Up @@ -72,4 +80,3 @@ extern const lauf_asm_type lauf_asm_type_value;
LAUF_HEADER_END

#endif // LAUF_ASM_TYPE_H_INCLUDED

6 changes: 2 additions & 4 deletions include/lauf/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,8 @@ typedef uint64_t lauf_uint;
# define LAUF_CONFIG_DISPATCH_JUMP_TABLE 1
#endif

#if defined(__has_cpp_attribute)
# if __has_cpp_attribute(clang::musttail)
# define LAUF_HAS_TAIL_CALL_ELIMINATION 1
# endif
#ifndef LAUF_HAS_TAIL_CALL_ELIMINATION
# define LAUF_HAS_TAIL_CALL_ELIMINATION 1
#endif

//=== warnings ===//
Expand Down
72 changes: 55 additions & 17 deletions include/lauf/runtime/builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,23 @@ typedef enum lauf_runtime_builtin_flags
LAUF_RUNTIME_BUILTIN_ALWAYS_PANIC = 1 << 4,
} lauf_runtime_builtin_flags;

#if LAUF_HAS_TAIL_CALL_ELIMINATION
# define LAUF_BUILTIN_RETURN_TYPE bool
#else
typedef union lauf_tail_call_result lauf_tail_call_result;
# define LAUF_BUILTIN_RETURN_TYPE lauf_tail_call_result
#endif

/// Must be tail-called when a buitlin finishes succesfully.
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);
LAUF_RUNTIME_BUILTIN_IMPL LAUF_BUILTIN_RETURN_TYPE lauf_runtime_builtin_dispatch(
const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr, lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process);

/// The signature of the implementation of a builtin.
typedef bool lauf_runtime_builtin_impl(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process);
typedef LAUF_BUILTIN_RETURN_TYPE lauf_runtime_builtin_impl(const lauf_asm_inst* ip,
lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process);

/// A builtin function.
typedef struct lauf_runtime_builtin
Expand All @@ -74,16 +81,47 @@ typedef struct lauf_runtime_builtin
const lauf_runtime_builtin* next;
} lauf_runtime_builtin;

#define LAUF_RUNTIME_BUILTIN(ConstantName, InputCount, OutputCount, Flags, Name, Next) \
static bool ConstantName##_impl(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr, \
lauf_runtime_stack_frame* frame_ptr, \
lauf_runtime_process* process); \
const lauf_runtime_builtin ConstantName \
= {&ConstantName##_impl, InputCount, OutputCount, Flags, Name, Next}; \
LAUF_RUNTIME_BUILTIN_IMPL static bool ConstantName##_impl(const lauf_asm_inst* ip, \
lauf_runtime_value* vstack_ptr, \
lauf_runtime_stack_frame* frame_ptr, \
lauf_runtime_process* process)
#if !LAUF_HAS_TAIL_CALL_ELIMINATION
typedef union lauf_tail_call_result
{
struct
{
bool is_function;
lauf_runtime_builtin_impl* next_func;
const lauf_asm_inst* cur_ip;
lauf_runtime_value* cur_stack_ptr;
lauf_runtime_stack_frame* cur_frame_ptr;
lauf_runtime_process* cur_process;
} function;
struct
{
bool is_function;
bool value;
} value;
} lauf_tail_call_result;

# define LAUF_BUILTIN_RETURN(...) \
return \
{ \
.value \
{ \
false, __VA_ARGS__ \
} \
}
#else
# define LAUF_BUILTIN_RETURN(...) return __VA_ARGS__
#endif

#define LAUF_RUNTIME_BUILTIN(ConstantName, InputCount, OutputCount, Flags, Name, Next) \
static LAUF_BUILTIN_RETURN_TYPE ConstantName##_impl(const lauf_asm_inst* ip, \
lauf_runtime_value* vstack_ptr, \
lauf_runtime_stack_frame* frame_ptr, \
lauf_runtime_process* process); \
const lauf_runtime_builtin ConstantName \
= {&ConstantName##_impl, InputCount, OutputCount, Flags, Name, Next}; \
LAUF_RUNTIME_BUILTIN_IMPL static LAUF_BUILTIN_RETURN_TYPE \
ConstantName##_impl(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr, \
lauf_runtime_stack_frame* frame_ptr, lauf_runtime_process* process)

#define LAUF_RUNTIME_BUILTIN_DISPATCH \
LAUF_TAIL_CALL return lauf_runtime_builtin_dispatch(ip, vstack_ptr, frame_ptr, process)
Expand Down
6 changes: 6 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ endif()
# 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 "GNU")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(lauf_core PUBLIC LAUF_HAS_TAIL_CALL_ELIMINATION=0)
endif()
endif()

target_sources(lauf_core PUBLIC
${include_dir}/config.h
${include_dir}/reader.h
Expand Down
22 changes: 18 additions & 4 deletions src/lauf/asm/builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -839,12 +839,26 @@ void lauf_asm_inst_call_builtin(lauf_asm_builder* b, lauf_runtime_builtin_functi
&& (callee.flags & LAUF_RUNTIME_BUILTIN_CONSTANT_FOLD) != 0)
{
assert(vstack_ptr == vstack + UINT8_MAX);
lauf_asm_inst code[3] = {LAUF_BUILD_INST_NONE(nop),
LAUF_BUILD_INST_SIGNATURE(call_builtin_sig, callee.input_count,
callee.output_count, callee.flags),
LAUF_BUILD_INST_NONE(exit)};
lauf_asm_inst code[3] = {LAUF_BUILD_INST_NONE(nop),
LAUF_BUILD_INST_SIGNATURE(call_builtin_sig, callee.input_count,
callee.output_count, callee.flags),
LAUF_BUILD_INST_NONE(exit)};

#if LAUF_HAS_TAIL_CALL_ELIMINATION
[[maybe_unused]] auto success
= callee.impl(code, vstack_ptr - callee.input_count, nullptr, nullptr);
#else
LAUF_BUILTIN_RETURN_TYPE tail_call_result
= callee.impl(code, vstack_ptr - callee.input_count, nullptr, nullptr);
while (tail_call_result.function.is_function)
{
tail_call_result
= tail_call_result.function.next_func(tail_call_result.function.cur_ip,
tail_call_result.function.cur_stack_ptr,
nullptr, nullptr);
}
[[maybe_unused]] bool success = tail_call_result.value.value;
#endif
if (success)
{
// Pop the input values as the call would.
Expand Down
19 changes: 10 additions & 9 deletions src/lauf/asm/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <lauf/asm/type.h>

#include <algorithm>
#include <lauf/runtime/builtin.h>
#include <lauf/runtime/value.h>
#include <lauf/support/align.hpp>
Expand All @@ -25,27 +26,28 @@ lauf_asm_layout lauf_asm_aggregate_layout(const lauf_asm_layout* member_layouts,
size += lauf::align_offset(size, member_layouts[i].alignment);
size += member_layouts[i].size;

if (member_layouts[i].alignment > alignment)
alignment = member_layouts[i].alignment;
alignment = std::max(member_layouts[i].alignment, alignment);
}
return {size, alignment};
}

namespace
{
LAUF_RUNTIME_BUILTIN_IMPL bool load_value(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process)
LAUF_RUNTIME_BUILTIN_IMPL LAUF_BUILTIN_RETURN_TYPE load_value(const lauf_asm_inst* ip,
lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process)
{
vstack_ptr[1] = *static_cast<const lauf_runtime_value*>(vstack_ptr[1].as_native_ptr);
++vstack_ptr;

LAUF_RUNTIME_BUILTIN_DISPATCH;
}

LAUF_RUNTIME_BUILTIN_IMPL bool store_value(const lauf_asm_inst* ip, lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process)
LAUF_RUNTIME_BUILTIN_IMPL LAUF_BUILTIN_RETURN_TYPE store_value(const lauf_asm_inst* ip,
lauf_runtime_value* vstack_ptr,
lauf_runtime_stack_frame* frame_ptr,
lauf_runtime_process* process)
{
*static_cast<lauf_runtime_value*>(vstack_ptr[1].as_native_ptr) = vstack_ptr[2];
vstack_ptr += 3;
Expand All @@ -60,4 +62,3 @@ const lauf_asm_type lauf_asm_type_value = {LAUF_ASM_NATIVE_LAYOUT_OF(lauf_runtim
&store_value,
"lauf.Value",
nullptr};

7 changes: 3 additions & 4 deletions src/lauf/lib/bits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_bits_shl, 2, 1, panic_flags, "shl", &lauf_lib_bits
++vstack_ptr;

if (LAUF_UNLIKELY(n >= sizeof(lauf_uint) * CHAR_BIT))
return lauf_runtime_panic(process, "shift amount too big");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "shift amount too big"));

vstack_ptr[0].as_uint = x << n;
LAUF_RUNTIME_BUILTIN_DISPATCH;
Expand All @@ -65,7 +65,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_bits_ushr, 2, 1, panic_flags, "ushr", &lauf_lib_bi
++vstack_ptr;

if (LAUF_UNLIKELY(n >= sizeof(lauf_uint) * CHAR_BIT))
return lauf_runtime_panic(process, "shift amount too big");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "shift amount too big"));

vstack_ptr[0].as_uint = x >> n;
LAUF_RUNTIME_BUILTIN_DISPATCH;
Expand All @@ -78,12 +78,11 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_bits_sshr, 2, 1, panic_flags, "sshr", &lauf_lib_bi
++vstack_ptr;

if (LAUF_UNLIKELY(n >= sizeof(lauf_sint) * CHAR_BIT))
return lauf_runtime_panic(process, "shift amount too big");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "shift amount too big"));

static_assert(-1 >> 1 == -1, "compiler does not implement arithmetic right shift");
vstack_ptr[0].as_sint = x >> n;
LAUF_RUNTIME_BUILTIN_DISPATCH;
}

const lauf_runtime_builtin_library lauf_lib_bits = {"lauf.bits", &lauf_lib_bits_sshr, nullptr};

9 changes: 4 additions & 5 deletions src/lauf/lib/fiber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_fiber_create, 1, 1, LAUF_RUNTIME_BUILTIN_DEFAULT,

auto fn = lauf_runtime_get_function_ptr_any(process, address);
if (LAUF_UNLIKELY(fn == nullptr))
return lauf_runtime_panic(process, "invalid function address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid function address"));

auto fiber = lauf_runtime_create_fiber(process, fn);
vstack_ptr[0].as_address = lauf_runtime_get_fiber_handle(fiber);
Expand All @@ -30,10 +30,10 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_fiber_destroy, 1, 0, LAUF_RUNTIME_BUILTIN_DEFAULT,

auto fiber = lauf_runtime_get_fiber_ptr(process, handle);
if (LAUF_UNLIKELY(fiber == nullptr))
return lauf_runtime_panic(process, "invalid fiber handle");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid fiber handle"));

if (LAUF_UNLIKELY(!lauf_runtime_destroy_fiber(process, fiber)))
return false;
LAUF_BUILTIN_RETURN(false);

LAUF_RUNTIME_BUILTIN_DISPATCH;
}
Expand Down Expand Up @@ -70,7 +70,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_fiber_done, 1, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "d
auto handle = vstack_ptr[0].as_address;
auto fiber = lauf_runtime_get_fiber_ptr(process, handle);
if (LAUF_UNLIKELY(fiber == nullptr))
return lauf_runtime_panic(process, "invalid fiber handle");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid fiber handle"));

auto status = lauf_runtime_get_fiber_status(fiber);
vstack_ptr[0].as_uint = status == LAUF_RUNTIME_FIBER_DONE ? 1 : 0;
Expand All @@ -79,4 +79,3 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_fiber_done, 1, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "d
}

const lauf_runtime_builtin_library lauf_lib_fiber = {"lauf.fiber", &lauf_lib_fiber_done, nullptr};

17 changes: 8 additions & 9 deletions src/lauf/lib/heap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_alloc, 2, 1, LAUF_RUNTIME_BUILTIN_DEFAULT, "a
auto allocator = lauf_vm_get_allocator(lauf_runtime_get_vm(process));
auto memory = allocator.heap_alloc(allocator.user_data, size, alignment);
if (memory == nullptr)
return lauf_runtime_panic(process, "out of memory");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "out of memory"));

auto address = lauf_runtime_add_heap_allocation(process, memory, size);

Expand Down Expand Up @@ -53,7 +53,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_free, 1, 0, LAUF_RUNTIME_BUILTIN_DEFAULT, "fr
lauf_runtime_allocation alloc;
if (!lauf_runtime_get_allocation(process, address, &alloc)
|| !lauf_runtime_leak_heap_allocation(process, address))
return lauf_runtime_panic(process, "invalid heap address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid heap address"));

auto allocator = lauf_vm_get_allocator(lauf_runtime_get_vm(process));
allocator.free_alloc(allocator.user_data, alloc.ptr, alloc.size);
Expand All @@ -69,14 +69,14 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_transfer_local, 1, 1, LAUF_RUNTIME_BUILTIN_DE
lauf_runtime_allocation alloc;
if (!lauf_runtime_get_allocation(process, address, &alloc)
|| (alloc.permission & LAUF_RUNTIME_PERM_READ) == 0)
return lauf_runtime_panic(process, "invalid address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid address"));

if (alloc.source == LAUF_RUNTIME_LOCAL_ALLOCATION)
{
auto allocator = lauf_vm_get_allocator(lauf_runtime_get_vm(process));
auto memory = allocator.heap_alloc(allocator.user_data, alloc.size, alignof(void*));
if (memory == nullptr)
return lauf_runtime_panic(process, "out of memory");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "out of memory"));

std::memcpy(memory, alloc.ptr, alloc.size);

Expand Down Expand Up @@ -104,7 +104,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_declare_reachable, 1, 0, LAUF_RUNTIME_BUILTIN
++vstack_ptr;

if (!lauf_runtime_declare_reachable(process, addr))
return lauf_runtime_panic(process, "invalid heap address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid heap address"));

LAUF_RUNTIME_BUILTIN_DISPATCH;
}
Expand All @@ -116,7 +116,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_undeclare_reachable, 1, 0, LAUF_RUNTIME_BUILT
++vstack_ptr;

if (!lauf_runtime_undeclare_reachable(process, addr))
return lauf_runtime_panic(process, "invalid heap address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid heap address"));

LAUF_RUNTIME_BUILTIN_DISPATCH;
}
Expand All @@ -128,7 +128,7 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_declare_weak, 1, 0, LAUF_RUNTIME_BUILTIN_VM_D
++vstack_ptr;

if (!lauf_runtime_declare_weak(process, addr))
return lauf_runtime_panic(process, "invalid address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid address"));

LAUF_RUNTIME_BUILTIN_DISPATCH;
}
Expand All @@ -140,11 +140,10 @@ LAUF_RUNTIME_BUILTIN(lauf_lib_heap_undeclare_weak, 1, 0, LAUF_RUNTIME_BUILTIN_VM
++vstack_ptr;

if (!lauf_runtime_undeclare_weak(process, addr))
return lauf_runtime_panic(process, "invalid address");
LAUF_BUILTIN_RETURN(lauf_runtime_panic(process, "invalid address"));

LAUF_RUNTIME_BUILTIN_DISPATCH;
}

const lauf_runtime_builtin_library lauf_lib_heap
= {"lauf.heap", &lauf_lib_heap_undeclare_weak, nullptr};

Loading