Compare commits
5 commits
50c3aac996
...
b16894ebd1
| Author | SHA1 | Date | |
|---|---|---|---|
| b16894ebd1 | |||
| 4c583c8b3b | |||
| 866af8d21c | |||
| d504a90555 | |||
| 649574c573 |
10 changed files with 684 additions and 77 deletions
|
|
@ -62,4 +62,12 @@ namespace pslang::ast
|
||||||
std::size_t type_size(types::type const & type);
|
std::size_t type_size(types::type const & type);
|
||||||
std::size_t type_alignment(types::type const & type);
|
std::size_t type_alignment(types::type const & type);
|
||||||
|
|
||||||
|
struct flat_field_layout
|
||||||
|
{
|
||||||
|
types::type_ptr type;
|
||||||
|
std::size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<flat_field_layout> flat_layout(types::type const & type);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,68 @@ namespace pslang::ast
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct flat_layout_visitor
|
||||||
|
: types::const_visitor<flat_layout_visitor>
|
||||||
|
{
|
||||||
|
using const_visitor::apply;
|
||||||
|
|
||||||
|
std::vector<flat_field_layout> & result;
|
||||||
|
|
||||||
|
flat_layout_visitor(std::vector<flat_field_layout> & result)
|
||||||
|
: result(result)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool apply(types::unit_type const & type)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply(types::primitive_type const & type)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply(types::array_type const & type)
|
||||||
|
{
|
||||||
|
auto const this_offset = current_offset_;
|
||||||
|
auto const element_size = ast::type_size(*type.element_type);
|
||||||
|
for (std::uint64_t i = 0; i < type.size; ++i)
|
||||||
|
{
|
||||||
|
current_offset_ = this_offset + i * element_size;
|
||||||
|
if (apply(*type.element_type)) continue;
|
||||||
|
|
||||||
|
result.push_back({.type = type.element_type, .offset = current_offset_});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply(types::function_type const &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply(types::pointer_type const &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply(types::struct_type const & type)
|
||||||
|
{
|
||||||
|
auto const this_offset = current_offset_;
|
||||||
|
for (auto const & field : type.node->fields)
|
||||||
|
{
|
||||||
|
current_offset_ = this_offset + field.layout.offset;
|
||||||
|
if (apply(*field.inferred_type)) continue;
|
||||||
|
|
||||||
|
result.push_back({.type = field.inferred_type, .offset = current_offset_});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t current_offset_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
types::type_ptr get_type(type const & type)
|
types::type_ptr get_type(type const & type)
|
||||||
|
|
@ -135,4 +197,11 @@ namespace pslang::ast
|
||||||
return alignment_visitor{}.apply(type);
|
return alignment_visitor{}.apply(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<flat_field_layout> flat_layout(types::type const & type)
|
||||||
|
{
|
||||||
|
std::vector<flat_field_layout> result;
|
||||||
|
flat_layout_visitor{result}.apply(type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -441,7 +441,7 @@ namespace pslang::interpreter
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<H, types::half_float>)
|
else if constexpr (std::is_same_v<H, types::half_float>)
|
||||||
{
|
{
|
||||||
return primitive_value(primitive_value_base<H>{{static_cast<float>(value.value)}});
|
return primitive_value(primitive_value_base<H>{types::half_float::from_float(static_cast<float>(value.value))});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
// Store the zero-extended 16-bit GPR @reg_src in 64-bit register @reg_dst
|
// Store the zero-extended 16-bit GPR @reg_src in 64-bit register @reg_dst
|
||||||
void movzx_16(reg reg_src, reg reg_dst);
|
void movzx_16(reg reg_src, reg reg_dst);
|
||||||
|
|
||||||
|
// Store the zero-extended 32-bit GPR @reg_src in 64-bit register @reg_dst
|
||||||
|
void movzx_32(reg reg_src, reg reg_dst);
|
||||||
|
|
||||||
// Store the sign-extended 8-bit GPR @reg_src in 64-bit register @reg_dst
|
// Store the sign-extended 8-bit GPR @reg_src in 64-bit register @reg_dst
|
||||||
void movsx_8(reg reg_src, reg reg_dst);
|
void movsx_8(reg reg_src, reg reg_dst);
|
||||||
|
|
||||||
|
|
@ -198,6 +201,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
// Bitwise and the 64-bit GPR @reg_src with the 64-bit GPR @reg_dst and store the result in @reg_dst
|
// Bitwise and the 64-bit GPR @reg_src with the 64-bit GPR @reg_dst and store the result in @reg_dst
|
||||||
void and_(reg reg_src, reg reg_dst);
|
void and_(reg reg_src, reg reg_dst);
|
||||||
|
|
||||||
|
// Bitwise and the 64-bit GPR @reg_src with the 8-bit @value (sign-extended to 64 bits) and store the result in @reg_dst
|
||||||
|
void and_imm8(reg reg_src, std::int8_t value);
|
||||||
|
|
||||||
// Bitwise or the 64-bit GPR @reg_src with the 64-bit GPR @reg_dst and store the result in @reg_dst
|
// Bitwise or the 64-bit GPR @reg_src with the 64-bit GPR @reg_dst and store the result in @reg_dst
|
||||||
void or_(reg reg_src, reg reg_dst);
|
void or_(reg reg_src, reg reg_dst);
|
||||||
|
|
||||||
|
|
@ -210,6 +216,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
// Right-shift with zero-filling the 64-bit GPR @reg_dst by the value of the low 6 bits of RCX and store the result in @reg_dst
|
// Right-shift with zero-filling the 64-bit GPR @reg_dst by the value of the low 6 bits of RCX and store the result in @reg_dst
|
||||||
void shr(reg reg_dst);
|
void shr(reg reg_dst);
|
||||||
|
|
||||||
|
// Right-shift with zero-filling the 64-bit GPR @reg_dst by 1 bit and store the result in @reg_dst
|
||||||
|
void shr1(reg reg_dst);
|
||||||
|
|
||||||
// Right-shift with sign-extending the 64-bit GPR @reg_dst by the value of the low 6 bits of RCX and store the result in @reg_dst
|
// Right-shift with sign-extending the 64-bit GPR @reg_dst by the value of the low 6 bits of RCX and store the result in @reg_dst
|
||||||
void sar(reg reg_dst);
|
void sar(reg reg_dst);
|
||||||
|
|
||||||
|
|
@ -309,6 +318,11 @@ namespace pslang::jit::linux_x86_64
|
||||||
// NB: RIP holds the address of the _next_ instruction
|
// NB: RIP holds the address of the _next_ instruction
|
||||||
void jump_if_nonzero(std::int32_t offset);
|
void jump_if_nonzero(std::int32_t offset);
|
||||||
|
|
||||||
|
// Conditional jump to 32-bit signed @offset relative to RIP
|
||||||
|
// if SF flag is set
|
||||||
|
// NB: RIP holds the address of the _next_ instruction
|
||||||
|
void jump_if_sign(std::int32_t offset);
|
||||||
|
|
||||||
// Assuming that @opcode refers to the location of a JUMP instruction,
|
// Assuming that @opcode refers to the location of a JUMP instruction,
|
||||||
// replace its 32-bit jump offset with @offset
|
// replace its 32-bit jump offset with @offset
|
||||||
void jump_inject(std::uint8_t * opcode, std::int32_t offset);
|
void jump_inject(std::uint8_t * opcode, std::int32_t offset);
|
||||||
|
|
@ -336,6 +350,22 @@ namespace pslang::jit::linux_x86_64
|
||||||
// of the LEA instruction itself
|
// of the LEA instruction itself
|
||||||
void lea_rip_inject_prev(std::uint8_t * opcode, std::int32_t offset);
|
void lea_rip_inject_prev(std::uint8_t * opcode, std::int32_t offset);
|
||||||
|
|
||||||
|
// Call to 32-bit signed @offset relative to RIP
|
||||||
|
// NB: RIP holds the address of the _next_ instruction
|
||||||
|
void call_imm(std::int32_t offset);
|
||||||
|
|
||||||
|
// Assuming that @opcode refers to the location of a CALL instruction,
|
||||||
|
// replace its 32-bit call offset with @offset
|
||||||
|
void call_imm_inject(std::uint8_t * opcode, std::int32_t offset);
|
||||||
|
|
||||||
|
// Assuming that @opcode refers to the location of a CALL instruction,
|
||||||
|
// replace its 32-bit call offset with @offset, compensating for the size
|
||||||
|
// of the CALL instruction itself
|
||||||
|
void call_imm_inject_prev(std::uint8_t * opcode, std::int32_t offset);
|
||||||
|
|
||||||
|
// Call to address specified in @reg_addr
|
||||||
|
void call_reg(reg reg_addr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename ... Args>
|
template <typename ... Args>
|
||||||
void do_push(Args ... values);
|
void do_push(Args ... values);
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename ... Regs>
|
template <typename ... Regs>
|
||||||
reg find_free_reg(Regs ... regs)
|
reg find_free_reg(reg start, Regs ... regs)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = (int)start; i < 16; ++i)
|
||||||
{
|
{
|
||||||
if ((true && ... && (regs != reg(i))))
|
if ((true && ... && (regs != reg(i))))
|
||||||
return reg(i);
|
return reg(i);
|
||||||
|
|
@ -34,6 +34,22 @@ namespace pslang::jit::linux_x86_64
|
||||||
reg::r9,
|
reg::r9,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static std::array<reg, 2> integer_ret_reg = {
|
||||||
|
reg::rax,
|
||||||
|
reg::rdx,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct small_struct_data
|
||||||
|
{
|
||||||
|
struct octet
|
||||||
|
{
|
||||||
|
bool has_integer = false;
|
||||||
|
bool has_floating_point = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<octet> octets[2];
|
||||||
|
};
|
||||||
|
|
||||||
struct value_address
|
struct value_address
|
||||||
{
|
{
|
||||||
// None means RIP-based addressing
|
// None means RIP-based addressing
|
||||||
|
|
@ -48,6 +64,8 @@ namespace pslang::jit::linux_x86_64
|
||||||
|
|
||||||
std::unordered_map<std::string, std::int32_t> extern_symbols;
|
std::unordered_map<std::string, std::int32_t> extern_symbols;
|
||||||
|
|
||||||
|
std::unordered_map<types::type_ptr, small_struct_data> small_structs;
|
||||||
|
|
||||||
std::unordered_map<ir::node_ref, std::int32_t> nodes;
|
std::unordered_map<ir::node_ref, std::int32_t> nodes;
|
||||||
|
|
||||||
struct resolve_data
|
struct resolve_data
|
||||||
|
|
@ -59,8 +77,43 @@ namespace pslang::jit::linux_x86_64
|
||||||
std::vector<resolve_data> jump_resolve;
|
std::vector<resolve_data> jump_resolve;
|
||||||
std::vector<resolve_data> cjump_resolve;
|
std::vector<resolve_data> cjump_resolve;
|
||||||
std::vector<resolve_data> node_resolve;
|
std::vector<resolve_data> node_resolve;
|
||||||
|
std::vector<resolve_data> call_resolve;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::optional<small_struct_data> classify_small_struct(local_context & lcontext, types::type_ptr type)
|
||||||
|
{
|
||||||
|
// TODO: unit type?
|
||||||
|
|
||||||
|
if (ast::type_size(*type) > 16)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (auto it = lcontext.small_structs.find(type); it != lcontext.small_structs.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
if (std::holds_alternative<types::struct_type>(*type) || std::holds_alternative<types::array_type>(*type))
|
||||||
|
{
|
||||||
|
small_struct_data result;
|
||||||
|
|
||||||
|
auto layout = ast::flat_layout(*type);
|
||||||
|
for (auto const & field : layout)
|
||||||
|
{
|
||||||
|
std::size_t octet_index = field.offset / 8;
|
||||||
|
auto & octet = result.octets[octet_index].emplace();
|
||||||
|
if (types::is_floating_point_type(*field.type))
|
||||||
|
octet.has_floating_point = true;
|
||||||
|
else
|
||||||
|
octet.has_integer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (lcontext.small_structs[type] = result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types::is_floating_point_type(*type))
|
||||||
|
return small_struct_data{.octets = {small_struct_data::octet{.has_floating_point = true}, std::nullopt}};
|
||||||
|
|
||||||
|
return small_struct_data{.octets = {small_struct_data::octet{.has_integer = true}, std::nullopt}};
|
||||||
|
}
|
||||||
|
|
||||||
struct populate_globals_visitor
|
struct populate_globals_visitor
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> & storage;
|
std::vector<std::uint8_t> & storage;
|
||||||
|
|
@ -96,7 +149,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
|
|
||||||
void apply(ir::extern_symbol const & node, types::type_ptr const &)
|
void apply(ir::extern_symbol const & node, types::type_ptr const &)
|
||||||
{
|
{
|
||||||
std::int32_t offset = pcontext.storage.size();
|
std::int32_t offset = pcontext.storage.size();
|
||||||
lcontext.extern_symbols[node.name] = offset;
|
lcontext.extern_symbols[node.name] = offset;
|
||||||
pcontext.foreign_resolve.push_back({node.name, offset});
|
pcontext.foreign_resolve.push_back({node.name, offset});
|
||||||
push_bytes(pcontext.storage.storage, (void *)nullptr);
|
push_bytes(pcontext.storage.storage, (void *)nullptr);
|
||||||
|
|
@ -160,6 +213,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
std::vector<std::int32_t> argument_position;
|
std::vector<std::int32_t> argument_position;
|
||||||
std::unordered_map<ir::node_ref, std::int32_t> stack_position;
|
std::unordered_map<ir::node_ref, std::int32_t> stack_position;
|
||||||
std::int32_t stack_size = 0;
|
std::int32_t stack_size = 0;
|
||||||
|
bool return_value_is_large_struct = false;
|
||||||
|
|
||||||
void apply(ir::node_ref, ir::label const &, types::type_ptr const &)
|
void apply(ir::node_ref, ir::label const &, types::type_ptr const &)
|
||||||
{}
|
{}
|
||||||
|
|
@ -283,6 +337,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
void apply(ir::node_ref it, ir::binary_operation const & node, types::type_ptr const & type)
|
void apply(ir::node_ref it, ir::binary_operation const & node, types::type_ptr const & type)
|
||||||
{
|
{
|
||||||
auto const arg1_type = node.arg1->inferred_type;
|
auto const arg1_type = node.arg1->inferred_type;
|
||||||
|
auto const arg2_type = node.arg2->inferred_type;
|
||||||
bool const is_fp = types::is_floating_point_type(*arg1_type);
|
bool const is_fp = types::is_floating_point_type(*arg1_type);
|
||||||
bool const result_is_fp = types::is_floating_point_type(*type);
|
bool const result_is_fp = types::is_floating_point_type(*type);
|
||||||
auto const arg_size = ast::type_size(*arg1_type);
|
auto const arg_size = ast::type_size(*arg1_type);
|
||||||
|
|
@ -392,7 +447,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
break;
|
break;
|
||||||
case ast::binary_operation_type::right_shift:
|
case ast::binary_operation_type::right_shift:
|
||||||
reg_extend(reg::rax, reg::rax, *arg1_type);
|
reg_extend(reg::rax, reg::rax, *arg1_type);
|
||||||
reg_extend(reg::rbx, reg::rcx, *arg1_type);
|
reg_extend(reg::rbx, reg::rcx, *arg2_type);
|
||||||
if (types::is_unsigned_integer_type(*arg1_type))
|
if (types::is_unsigned_integer_type(*arg1_type))
|
||||||
builder.shr(reg::rax);
|
builder.shr(reg::rax);
|
||||||
else
|
else
|
||||||
|
|
@ -573,24 +628,66 @@ namespace pslang::jit::linux_x86_64
|
||||||
}
|
}
|
||||||
else if (types::is_floating_point_type(*dst_type))
|
else if (types::is_floating_point_type(*dst_type))
|
||||||
{
|
{
|
||||||
if (types::is_unsigned_integer_type(*src_type) && ast::type_size(*src_type) == 8)
|
|
||||||
throw std::runtime_error("64-bit unsigned to floating-point conversion is not implemented");
|
|
||||||
|
|
||||||
reg_extend(reg::rax, reg::rax, *src_type);
|
reg_extend(reg::rax, reg::rax, *src_type);
|
||||||
|
|
||||||
auto dst_size = ast::type_size(*dst_type);
|
auto dst_size = ast::type_size(*dst_type);
|
||||||
if (dst_size <= 4)
|
|
||||||
builder.gpr_to_xmm_32(reg::rax, reg::xmm0);
|
if (types::is_unsigned_integer_type(*src_type) && ast::type_size(*src_type) == 8)
|
||||||
|
{
|
||||||
|
// No u64 -> f32/f64 conversion on x86_64
|
||||||
|
// Emulate it by splitting x = 2 * (x >> 1) + (x & 1)
|
||||||
|
|
||||||
|
reg_extend(reg::rax, reg::rax, *src_type);
|
||||||
|
|
||||||
|
builder.test(reg::rax, reg::rax);
|
||||||
|
auto first_jump = builder.code.size();
|
||||||
|
builder.jump_if_sign(0);
|
||||||
|
|
||||||
|
if (dst_size <= 4)
|
||||||
|
builder.gpr_to_xmm_32(reg::rax, reg::xmm0);
|
||||||
|
else
|
||||||
|
builder.gpr_to_xmm(reg::rax, reg::xmm0);
|
||||||
|
|
||||||
|
auto second_jump = builder.code.size();
|
||||||
|
builder.jump(0);
|
||||||
|
|
||||||
|
auto first_jump_target = builder.code.size();
|
||||||
|
|
||||||
|
builder.mov(reg::rax, reg::rcx);
|
||||||
|
builder.shr1(reg::rcx);
|
||||||
|
builder.and_imm8(reg::rax, 1);
|
||||||
|
builder.or_(reg::rax, reg::rcx);
|
||||||
|
|
||||||
|
if (dst_size <= 4)
|
||||||
|
{
|
||||||
|
builder.gpr_to_xmm_32(reg::rcx, reg::xmm0);
|
||||||
|
builder.add_xmm_32(reg::xmm0, reg::xmm0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.gpr_to_xmm(reg::rcx, reg::xmm0);
|
||||||
|
builder.add_xmm(reg::xmm0, reg::xmm0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto second_jump_target = builder.code.size();
|
||||||
|
|
||||||
|
builder.cjump_inject_prev(builder.code.data() + first_jump, first_jump_target - first_jump);
|
||||||
|
builder.jump_inject_prev(builder.code.data() + second_jump, second_jump_target - second_jump);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
builder.gpr_to_xmm(reg::rax, reg::xmm0);
|
{
|
||||||
|
if (dst_size <= 4)
|
||||||
|
builder.gpr_to_xmm_32(reg::rax, reg::xmm0);
|
||||||
|
else
|
||||||
|
builder.gpr_to_xmm(reg::rax, reg::xmm0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (types::is_floating_point_type(*src_type))
|
else if (types::is_floating_point_type(*src_type))
|
||||||
{
|
{
|
||||||
if (types::is_integer_type(*dst_type))
|
if (types::is_integer_type(*dst_type))
|
||||||
{
|
{
|
||||||
if (types::is_unsigned_integer_type(*dst_type) && ast::type_size(*dst_type) == 8)
|
// TODO: correct floating-point to 64-bit unsigned conversion
|
||||||
throw std::runtime_error("Floating-point to 64-bit unsigned conversion is not implemented");
|
|
||||||
|
|
||||||
auto src_size = ast::type_size(*src_type);
|
auto src_size = ast::type_size(*src_type);
|
||||||
if (src_size <= 4)
|
if (src_size <= 4)
|
||||||
|
|
@ -639,7 +736,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
|
|
||||||
void apply(ir::node_ref it, ir::extern_symbol const & node, types::type_ptr const & type)
|
void apply(ir::node_ref it, ir::extern_symbol const & node, types::type_ptr const & type)
|
||||||
{
|
{
|
||||||
builder.lea_rip(lcontext.extern_symbols[node.name] - (std::int32_t)pcontext.storage.size(), reg::rax);
|
builder.mov_read_rip_prev(lcontext.extern_symbols[node.name] - (std::int32_t)pcontext.storage.size(), reg::rax);
|
||||||
store(it, reg::rax);
|
store(it, reg::rax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -678,6 +775,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
void apply(ir::node_ref, ir::jump_if_zero const & node, types::type_ptr const & type)
|
void apply(ir::node_ref, ir::jump_if_zero const & node, types::type_ptr const & type)
|
||||||
{
|
{
|
||||||
load(node.condition, reg::rax);
|
load(node.condition, reg::rax);
|
||||||
|
reg_extend(reg::rax, reg::rax, *node.condition->inferred_type);
|
||||||
builder.test(reg::rax, reg::rax);
|
builder.test(reg::rax, reg::rax);
|
||||||
lcontext.cjump_resolve.push_back({(std::int32_t)pcontext.storage.size(), node.target});
|
lcontext.cjump_resolve.push_back({(std::int32_t)pcontext.storage.size(), node.target});
|
||||||
builder.jump_if_zero(0);
|
builder.jump_if_zero(0);
|
||||||
|
|
@ -686,19 +784,165 @@ namespace pslang::jit::linux_x86_64
|
||||||
void apply(ir::node_ref, ir::jump_if_nonzero const & node, types::type_ptr const & type)
|
void apply(ir::node_ref, ir::jump_if_nonzero const & node, types::type_ptr const & type)
|
||||||
{
|
{
|
||||||
load(node.condition, reg::rax);
|
load(node.condition, reg::rax);
|
||||||
|
reg_extend(reg::rax, reg::rax, *node.condition->inferred_type);
|
||||||
builder.test(reg::rax, reg::rax);
|
builder.test(reg::rax, reg::rax);
|
||||||
lcontext.cjump_resolve.push_back({(std::int32_t)pcontext.storage.size(), node.target});
|
lcontext.cjump_resolve.push_back({(std::int32_t)pcontext.storage.size(), node.target});
|
||||||
builder.jump_if_nonzero(0);
|
builder.jump_if_nonzero(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(ir::node_ref, ir::call const & node, types::type_ptr const & type)
|
template <typename Node, typename DoCall>
|
||||||
|
void apply_call(ir::node_ref it, Node const & node, types::type_ptr const & type, DoCall && do_call)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Not implemented");
|
bool return_value_is_large_struct = false;
|
||||||
|
if (std::holds_alternative<types::struct_type>(*type) || std::holds_alternative<types::array_type>(*type))
|
||||||
|
return_value_is_large_struct = !classify_small_struct(lcontext, type);
|
||||||
|
|
||||||
|
if (this->return_value_is_large_struct)
|
||||||
|
{
|
||||||
|
builder.push(reg::rdi);
|
||||||
|
stack_size += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute space required for stack arguments
|
||||||
|
std::int32_t arguments_stack_size = 0;
|
||||||
|
for (auto const & argument : node.arguments)
|
||||||
|
{
|
||||||
|
auto size = ast::type_size(*argument->inferred_type);
|
||||||
|
auto alignment = ast::type_size(*argument->inferred_type);
|
||||||
|
auto struct_type = std::get_if<types::struct_type>(argument->inferred_type.get());
|
||||||
|
auto array_type = std::get_if<types::array_type>(argument->inferred_type.get());
|
||||||
|
if (struct_type || array_type)
|
||||||
|
{
|
||||||
|
if (!classify_small_struct(lcontext, argument->inferred_type))
|
||||||
|
{
|
||||||
|
arguments_stack_size += (alignment - (arguments_stack_size % alignment)) % alignment;
|
||||||
|
arguments_stack_size += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure stack is 16-byte aligned after CALL pushes return address on stack
|
||||||
|
// I.e. enforce (stack_size + arguments_stack_size) % 16 == 8
|
||||||
|
arguments_stack_size += (24 - ((arguments_stack_size + stack_size) % 16)) % 16;
|
||||||
|
|
||||||
|
// TODO: handle the case when there weren't enough registers
|
||||||
|
std::uint8_t reg_index = return_value_is_large_struct ? 1 : 0;
|
||||||
|
std::uint8_t fp_reg = 0;
|
||||||
|
std::int32_t stack_offset = 0;
|
||||||
|
for (auto const & argument : node.arguments)
|
||||||
|
{
|
||||||
|
auto size = ast::type_size(*argument->inferred_type);
|
||||||
|
auto alignment = ast::type_alignment(*argument->inferred_type);
|
||||||
|
auto struct_type = std::get_if<types::struct_type>(argument->inferred_type.get());
|
||||||
|
auto array_type = std::get_if<types::array_type>(argument->inferred_type.get());
|
||||||
|
if (struct_type || array_type)
|
||||||
|
{
|
||||||
|
if (auto small_struct = classify_small_struct(lcontext, argument->inferred_type))
|
||||||
|
{
|
||||||
|
auto address = node_address(argument);
|
||||||
|
|
||||||
|
for (int o : {0, 1})
|
||||||
|
{
|
||||||
|
auto & octet = small_struct->octets[o];
|
||||||
|
if (!octet) break;
|
||||||
|
|
||||||
|
if (octet->has_integer)
|
||||||
|
load(argument, integer_arg_reg[reg_index++], 8 * o);
|
||||||
|
else if (octet->has_floating_point)
|
||||||
|
load_xmm(argument, (reg)(fp_reg++), 8, 8 * o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stack_offset += (alignment - (stack_offset % alignment)) % alignment;
|
||||||
|
copy_memory(node_address(argument), {.base = reg::rsp, .offset = stack_offset - arguments_stack_size}, size);
|
||||||
|
stack_offset += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (types::is_integer_like_type(*argument->inferred_type))
|
||||||
|
load(argument, integer_arg_reg[reg_index++]);
|
||||||
|
else if (types::is_floating_point_type(*argument->inferred_type))
|
||||||
|
load_xmm(argument, (reg)(fp_reg++), size);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Unsupported function argument type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (return_value_is_large_struct)
|
||||||
|
{
|
||||||
|
auto address = node_address(it);
|
||||||
|
// Function call node cannot have RIP-relative address
|
||||||
|
builder.lea(address.base.value(), address.offset, reg::rdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments_stack_size > 0)
|
||||||
|
{
|
||||||
|
builder.sub_imm(reg::rsp, arguments_stack_size);
|
||||||
|
stack_size += arguments_stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_call();
|
||||||
|
|
||||||
|
if (arguments_stack_size > 0)
|
||||||
|
{
|
||||||
|
builder.add_imm(reg::rsp, arguments_stack_size);
|
||||||
|
stack_size -= arguments_stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t const size = ast::type_size(*type);
|
||||||
|
auto struct_type = std::get_if<types::struct_type>(type.get());
|
||||||
|
auto array_type = std::get_if<types::array_type>(type.get());
|
||||||
|
if (size == 0)
|
||||||
|
{}
|
||||||
|
else if (struct_type || array_type)
|
||||||
|
{
|
||||||
|
if (auto small_struct = classify_small_struct(lcontext, type))
|
||||||
|
{
|
||||||
|
std::uint8_t reg_index = 0;
|
||||||
|
std::uint8_t fp_reg = 0;
|
||||||
|
for (int o : {0, 1})
|
||||||
|
{
|
||||||
|
auto & octet = small_struct->octets[o];
|
||||||
|
if (!octet) break;
|
||||||
|
|
||||||
|
if (octet->has_integer)
|
||||||
|
store(it, integer_ret_reg[reg_index++], 8 * o);
|
||||||
|
else if (octet->has_floating_point)
|
||||||
|
store_xmm(it, (reg)(fp_reg++), 8, 8 * o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Nothing to be done - return value should already be in place
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (types::is_integer_like_type(*type))
|
||||||
|
store(it, reg::rax);
|
||||||
|
else if (types::is_floating_point_type(*type))
|
||||||
|
store_xmm(it, reg::xmm0, size);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Unsupported return value type");
|
||||||
|
|
||||||
|
if (this->return_value_is_large_struct)
|
||||||
|
{
|
||||||
|
builder.pop(reg::rdi);
|
||||||
|
stack_size -= 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(ir::node_ref, ir::call_pointer const & node, types::type_ptr const & type)
|
void apply(ir::node_ref it, ir::call const & node, types::type_ptr const & type)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Not implemented");
|
apply_call(it, node, type, [&]{
|
||||||
|
lcontext.call_resolve.emplace_back(pcontext.storage.size(), node.target);
|
||||||
|
builder.call_imm(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(ir::node_ref it, ir::call_pointer const & node, types::type_ptr const & type)
|
||||||
|
{
|
||||||
|
apply_call(it, node, type, [&]{
|
||||||
|
load(node.pointer, reg::rax);
|
||||||
|
builder.call_reg(reg::rax);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(ir::node_ref, ir::return_value const & node, types::type_ptr const & type)
|
void apply(ir::node_ref, ir::return_value const & node, types::type_ptr const & type)
|
||||||
|
|
@ -713,7 +957,27 @@ namespace pslang::jit::linux_x86_64
|
||||||
{}
|
{}
|
||||||
else if (struct_type || array_type)
|
else if (struct_type || array_type)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Not implemented");
|
if (auto small_struct = classify_small_struct(lcontext, type))
|
||||||
|
{
|
||||||
|
auto address = node_address(*node.value);
|
||||||
|
|
||||||
|
std::uint8_t reg_index = 0;
|
||||||
|
std::uint8_t fp_reg = 0;
|
||||||
|
for (int o : {0, 1})
|
||||||
|
{
|
||||||
|
auto & octet = small_struct->octets[o];
|
||||||
|
if (!octet) break;
|
||||||
|
|
||||||
|
if (octet->has_integer)
|
||||||
|
load(*node.value, integer_ret_reg[reg_index++], 8 * o);
|
||||||
|
else if (octet->has_floating_point)
|
||||||
|
load_xmm(*node.value, (reg)(fp_reg++), 8, 8 * o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
copy_memory(node_address(*node.value), {reg::rdi, 0}, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (types::is_integer_like_type(*type))
|
else if (types::is_integer_like_type(*type))
|
||||||
load(*node.value, reg::rax);
|
load(*node.value, reg::rax);
|
||||||
|
|
@ -723,11 +987,11 @@ namespace pslang::jit::linux_x86_64
|
||||||
throw std::runtime_error("Unsupported return value type");
|
throw std::runtime_error("Unsupported return value type");
|
||||||
}
|
}
|
||||||
if (stack_size > 0)
|
if (stack_size > 0)
|
||||||
{
|
|
||||||
builder.add_imm(reg::rsp, stack_size);
|
builder.add_imm(reg::rsp, stack_size);
|
||||||
if (lcontext.use_frame_pointer)
|
|
||||||
builder.pop(reg::rbp);
|
builder.pop(reg::rbx);
|
||||||
}
|
if (lcontext.use_frame_pointer)
|
||||||
|
builder.pop(reg::rbp);
|
||||||
builder.ret();
|
builder.ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -735,11 +999,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
{
|
{
|
||||||
auto result_type = function_definition->inferred_result_type;
|
auto result_type = function_definition->inferred_result_type;
|
||||||
|
|
||||||
if (lcontext.use_frame_pointer)
|
if (result_type)
|
||||||
{
|
if (std::holds_alternative<types::struct_type>(*result_type) || std::holds_alternative<types::array_type>(*result_type))
|
||||||
builder.push(reg::rbp);
|
return_value_is_large_struct = !classify_small_struct(lcontext, result_type);
|
||||||
builder.mov(reg::rsp, reg::rbp);
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_size = 0;
|
stack_size = 0;
|
||||||
|
|
||||||
|
|
@ -776,23 +1038,55 @@ namespace pslang::jit::linux_x86_64
|
||||||
auto it = begin;
|
auto it = begin;
|
||||||
|
|
||||||
lcontext.nodes[it] = pcontext.storage.size();
|
lcontext.nodes[it] = pcontext.storage.size();
|
||||||
|
|
||||||
|
if (lcontext.use_frame_pointer)
|
||||||
|
{
|
||||||
|
builder.push(reg::rbp);
|
||||||
|
builder.mov(reg::rsp, reg::rbp);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.push(reg::rbx);
|
||||||
|
|
||||||
if (stack_size > 0)
|
if (stack_size > 0)
|
||||||
builder.sub_imm(reg::rsp, stack_size);
|
builder.sub_imm(reg::rsp, stack_size);
|
||||||
|
|
||||||
// TODO: handle the case when there weren't enough registers
|
// TODO: handle the case when there weren't enough registers
|
||||||
|
|
||||||
std::uint8_t reg_index = 0;
|
// If return value is large struct, RDI stores the pointer to return value
|
||||||
|
std::uint8_t reg_index = return_value_is_large_struct ? 1 : 0;
|
||||||
std::uint8_t fp_reg = 0;
|
std::uint8_t fp_reg = 0;
|
||||||
|
// 8 for the return address
|
||||||
|
// Another 8 for RBP if using frame pointers
|
||||||
|
// Another 8 for saved RBX
|
||||||
|
std::int32_t stack_offset = stack_size + (lcontext.use_frame_pointer ? 16 : 8) + 8;
|
||||||
for (std::size_t i = 0; i < function_definition->arguments.size(); ++i)
|
for (std::size_t i = 0; i < function_definition->arguments.size(); ++i)
|
||||||
{
|
{
|
||||||
auto const & argument = function_definition->arguments[i];
|
auto const & argument = function_definition->arguments[i];
|
||||||
auto size = ast::type_size(*argument.inferred_type);
|
auto size = ast::type_size(*argument.inferred_type);
|
||||||
|
auto alignment = ast::type_alignment(*argument.inferred_type);
|
||||||
auto struct_type = std::get_if<types::struct_type>(argument.inferred_type.get());
|
auto struct_type = std::get_if<types::struct_type>(argument.inferred_type.get());
|
||||||
auto array_type = std::get_if<types::array_type>(argument.inferred_type.get());
|
auto array_type = std::get_if<types::array_type>(argument.inferred_type.get());
|
||||||
if (size == 0) continue;
|
if (size == 0) continue;
|
||||||
if (struct_type || array_type)
|
if (struct_type || array_type)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Not implemented");
|
if (auto small_struct = classify_small_struct(lcontext, argument.inferred_type))
|
||||||
|
{
|
||||||
|
for (std::size_t o : {0, 1})
|
||||||
|
{
|
||||||
|
auto & octet = small_struct->octets[o];
|
||||||
|
if (!octet) break;
|
||||||
|
if (octet->has_integer)
|
||||||
|
builder.mov_write(integer_arg_reg[reg_index++], reg::rsp, stack_size - argument_position[i] + 8 * o);
|
||||||
|
else if (octet->has_floating_point)
|
||||||
|
builder.mov_write_xmm((reg)(fp_reg++), reg::rsp, stack_size - argument_position[i] + 8 * o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stack_offset += (alignment - (stack_offset % alignment)) % alignment;
|
||||||
|
copy_memory({.base = reg::rsp, .offset = stack_offset}, {.base = reg::rsp, .offset = stack_size - argument_position[i]}, size);
|
||||||
|
stack_offset += size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (types::is_integer_like_type(*argument.inferred_type))
|
else if (types::is_integer_like_type(*argument.inferred_type))
|
||||||
{
|
{
|
||||||
|
|
@ -820,7 +1114,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Uncomment to debug per-node instruction generation:
|
// Uncomment to debug per-node instruction generation:
|
||||||
// builder.nop();
|
builder.nop();
|
||||||
|
|
||||||
lcontext.nodes[it] = pcontext.storage.size();
|
lcontext.nodes[it] = pcontext.storage.size();
|
||||||
std::visit([&](auto const & instruction){ apply(it, instruction, it->inferred_type); }, it->instruction);
|
std::visit([&](auto const & instruction){ apply(it, instruction, it->inferred_type); }, it->instruction);
|
||||||
|
|
@ -838,6 +1132,10 @@ namespace pslang::jit::linux_x86_64
|
||||||
builder.movzx_8(reg_src, reg_dst);
|
builder.movzx_8(reg_src, reg_dst);
|
||||||
else if (size == 2)
|
else if (size == 2)
|
||||||
builder.movzx_16(reg_src, reg_dst);
|
builder.movzx_16(reg_src, reg_dst);
|
||||||
|
else if (size == 4)
|
||||||
|
builder.movzx_32(reg_src, reg_dst);
|
||||||
|
else if (reg_src != reg_dst)
|
||||||
|
builder.mov(reg_src, reg_dst);
|
||||||
}
|
}
|
||||||
else if (types::is_signed_integer_type(type))
|
else if (types::is_signed_integer_type(type))
|
||||||
{
|
{
|
||||||
|
|
@ -847,6 +1145,8 @@ namespace pslang::jit::linux_x86_64
|
||||||
builder.movsx_16(reg_src, reg_dst);
|
builder.movsx_16(reg_src, reg_dst);
|
||||||
else if (size == 4)
|
else if (size == 4)
|
||||||
builder.movsx_32(reg_src, reg_dst);
|
builder.movsx_32(reg_src, reg_dst);
|
||||||
|
else if (reg_src != reg_dst)
|
||||||
|
builder.mov(reg_src, reg_dst);
|
||||||
}
|
}
|
||||||
else if (types::is_bool_type(type))
|
else if (types::is_bool_type(type))
|
||||||
{
|
{
|
||||||
|
|
@ -862,25 +1162,25 @@ namespace pslang::jit::linux_x86_64
|
||||||
return {.base = reg::rsp, .offset = stack_size - stack_position.at(it)};
|
return {.base = reg::rsp, .offset = stack_size - stack_position.at(it)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ir::node_ref it, reg reg_dst)
|
void load(ir::node_ref it, reg reg_dst, std::int32_t offset = 0)
|
||||||
{
|
{
|
||||||
auto const address = node_address(it);
|
auto const address = node_address(it);
|
||||||
if (address.base)
|
if (address.base)
|
||||||
builder.mov_read(*address.base, address.offset, reg_dst);
|
builder.mov_read(*address.base, address.offset + offset, reg_dst);
|
||||||
else
|
else
|
||||||
builder.mov_read_rip_prev(address.offset, reg_dst);
|
builder.mov_read_rip_prev(address.offset + offset, reg_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(ir::node_ref it, reg reg_src)
|
void store(ir::node_ref it, reg reg_src, std::int32_t offset = 0)
|
||||||
{
|
{
|
||||||
auto const address = node_address(it);
|
auto const address = node_address(it);
|
||||||
if (address.base)
|
if (address.base)
|
||||||
builder.mov_write(reg_src, *address.base, address.offset);
|
builder.mov_write(reg_src, *address.base, address.offset + offset);
|
||||||
else
|
else
|
||||||
builder.mov_write_rip_prev(reg_src, address.offset);
|
builder.mov_write_rip_prev(reg_src, address.offset + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_xmm(ir::node_ref it, reg reg_dst, std::uint8_t size)
|
void load_xmm(ir::node_ref it, reg reg_dst, std::uint8_t size, std::int32_t offset = 0)
|
||||||
{
|
{
|
||||||
if (size != 4 && size != 8)
|
if (size != 4 && size != 8)
|
||||||
throw std::runtime_error("Bad type size for load_xmm");
|
throw std::runtime_error("Bad type size for load_xmm");
|
||||||
|
|
@ -889,15 +1189,15 @@ namespace pslang::jit::linux_x86_64
|
||||||
if (address.base)
|
if (address.base)
|
||||||
{
|
{
|
||||||
if (size == 4)
|
if (size == 4)
|
||||||
builder.mov_read_xmm_32(*address.base, address.offset, reg_dst);
|
builder.mov_read_xmm_32(*address.base, address.offset + offset, reg_dst);
|
||||||
else
|
else
|
||||||
builder.mov_read_xmm(*address.base, address.offset, reg_dst);
|
builder.mov_read_xmm(*address.base, address.offset + offset, reg_dst);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw std::runtime_error("RIP-relative XMM read is not supported");
|
throw std::runtime_error("RIP-relative XMM read is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_xmm(ir::node_ref it, reg reg_src, std::uint8_t size)
|
void store_xmm(ir::node_ref it, reg reg_src, std::uint8_t size, std::int32_t offset = 0)
|
||||||
{
|
{
|
||||||
if (size != 4 && size != 8)
|
if (size != 4 && size != 8)
|
||||||
throw std::runtime_error("Bad type size for store_xmm");
|
throw std::runtime_error("Bad type size for store_xmm");
|
||||||
|
|
@ -906,9 +1206,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
if (address.base)
|
if (address.base)
|
||||||
{
|
{
|
||||||
if (size == 4)
|
if (size == 4)
|
||||||
builder.mov_write_xmm_32(reg_src, *address.base, address.offset);
|
builder.mov_write_xmm_32(reg_src, *address.base, address.offset + offset);
|
||||||
else
|
else
|
||||||
builder.mov_write_xmm(reg_src, *address.base, address.offset);
|
builder.mov_write_xmm(reg_src, *address.base, address.offset + offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw std::runtime_error("RIP-relative XMM write is not supported");
|
throw std::runtime_error("RIP-relative XMM write is not supported");
|
||||||
|
|
@ -923,7 +1223,7 @@ namespace pslang::jit::linux_x86_64
|
||||||
reg_src = *src.base;
|
reg_src = *src.base;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reg_src = find_free_reg(dst.base);
|
reg_src = find_free_reg(reg::r10, dst.base);
|
||||||
builder.lea_rip_prev(0, reg_src);
|
builder.lea_rip_prev(0, reg_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -932,13 +1232,13 @@ namespace pslang::jit::linux_x86_64
|
||||||
reg_dst = *dst.base;
|
reg_dst = *dst.base;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reg_dst = find_free_reg(reg_src);
|
reg_dst = find_free_reg(reg::r10, reg_src);
|
||||||
builder.lea_rip_prev(storage_size_at_start - (std::int32_t)pcontext.storage.size(), reg_dst);
|
builder.lea_rip_prev(storage_size_at_start - (std::int32_t)pcontext.storage.size(), reg_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t offset = 0;
|
std::int32_t offset = 0;
|
||||||
|
|
||||||
auto copy_reg = find_free_reg(reg_src, reg_dst);
|
auto copy_reg = find_free_reg(reg::r10, reg_src, reg_dst);
|
||||||
|
|
||||||
while (size > 0)
|
while (size > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1032,6 +1332,9 @@ namespace pslang::jit::linux_x86_64
|
||||||
for (auto const & resolve : lcontext.node_resolve)
|
for (auto const & resolve : lcontext.node_resolve)
|
||||||
builder.lea_rip_inject_prev(pcontext.storage.storage.data() + resolve.offset, lcontext.nodes.at(resolve.target) - resolve.offset);
|
builder.lea_rip_inject_prev(pcontext.storage.storage.data() + resolve.offset, lcontext.nodes.at(resolve.target) - resolve.offset);
|
||||||
|
|
||||||
|
for (auto const & resolve : lcontext.call_resolve)
|
||||||
|
builder.call_imm_inject_prev(pcontext.storage.storage.data() + resolve.offset, lcontext.nodes.at(resolve.target) - resolve.offset);
|
||||||
|
|
||||||
auto code_end = pcontext.storage.align();
|
auto code_end = pcontext.storage.align();
|
||||||
pcontext.storage.code.push_back({code_begin, code_end});
|
pcontext.storage.code.push_back({code_begin, code_end});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,11 @@ namespace pslang::jit::linux_x86_64
|
||||||
do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xb7_ub, modrm(MOD_REG, reg_dst, reg_src));
|
do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xb7_ub, modrm(MOD_REG, reg_dst, reg_src));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instruction_builder::movzx_32(reg reg_src, reg reg_dst)
|
||||||
|
{
|
||||||
|
do_push(rex(0, reg_src, {}, reg_dst), 0x89_ub, modrm(MOD_REG, reg_src, reg_dst));
|
||||||
|
}
|
||||||
|
|
||||||
void instruction_builder::movsx_8(reg reg_src, reg reg_dst)
|
void instruction_builder::movsx_8(reg reg_src, reg reg_dst)
|
||||||
{
|
{
|
||||||
do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xbe_ub, modrm(MOD_REG, reg_dst, reg_src));
|
do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xbe_ub, modrm(MOD_REG, reg_dst, reg_src));
|
||||||
|
|
@ -392,6 +397,11 @@ namespace pslang::jit::linux_x86_64
|
||||||
do_push(rex(1, reg_dst, {}, reg_src), 0x23_ub, modrm(MOD_REG, reg_dst, reg_src));
|
do_push(rex(1, reg_dst, {}, reg_src), 0x23_ub, modrm(MOD_REG, reg_dst, reg_src));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instruction_builder::and_imm8(reg reg_src, std::int8_t value)
|
||||||
|
{
|
||||||
|
do_push(rex(1, {}, {}, reg_src), 0x83_ub, modrm(MOD_REG, 0b100, reg_src), value);
|
||||||
|
}
|
||||||
|
|
||||||
void instruction_builder::or_(reg reg_src, reg reg_dst)
|
void instruction_builder::or_(reg reg_src, reg reg_dst)
|
||||||
{
|
{
|
||||||
do_push(rex(1, reg_dst, {}, reg_src), 0x0b_ub, modrm(MOD_REG, reg_dst, reg_src));
|
do_push(rex(1, reg_dst, {}, reg_src), 0x0b_ub, modrm(MOD_REG, reg_dst, reg_src));
|
||||||
|
|
@ -412,6 +422,11 @@ namespace pslang::jit::linux_x86_64
|
||||||
do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b101, reg_dst));
|
do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b101, reg_dst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instruction_builder::shr1(reg reg_dst)
|
||||||
|
{
|
||||||
|
do_push(rex(1, {}, {}, reg_dst), 0xd1_ub, modrm(MOD_REG, 0b101, reg_dst));
|
||||||
|
}
|
||||||
|
|
||||||
void instruction_builder::sar(reg reg_dst)
|
void instruction_builder::sar(reg reg_dst)
|
||||||
{
|
{
|
||||||
do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b111, reg_dst));
|
do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b111, reg_dst));
|
||||||
|
|
@ -567,6 +582,11 @@ namespace pslang::jit::linux_x86_64
|
||||||
do_push(0x0f_ub, 0x85_ub, offset);
|
do_push(0x0f_ub, 0x85_ub, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instruction_builder::jump_if_sign(std::int32_t offset)
|
||||||
|
{
|
||||||
|
do_push(0x0f_ub, 0x88_ub, offset);
|
||||||
|
}
|
||||||
|
|
||||||
void instruction_builder::jump_inject(std::uint8_t * opcode, std::int32_t offset)
|
void instruction_builder::jump_inject(std::uint8_t * opcode, std::int32_t offset)
|
||||||
{
|
{
|
||||||
auto src = (std::uint8_t const *)(&offset);
|
auto src = (std::uint8_t const *)(&offset);
|
||||||
|
|
@ -600,6 +620,30 @@ namespace pslang::jit::linux_x86_64
|
||||||
lea_rip_inject(opcode, offset - 7);
|
lea_rip_inject(opcode, offset - 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instruction_builder::call_imm(std::int32_t offset)
|
||||||
|
{
|
||||||
|
do_push(0xe8_ub, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_builder::call_imm_inject(std::uint8_t * opcode, std::int32_t offset)
|
||||||
|
{
|
||||||
|
auto src = (std::uint8_t const *)(&offset);
|
||||||
|
std::copy(src, src + 4, opcode + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_builder::call_imm_inject_prev(std::uint8_t * opcode, std::int32_t offset)
|
||||||
|
{
|
||||||
|
call_imm_inject(opcode, offset - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_builder::call_reg(reg reg_addr)
|
||||||
|
{
|
||||||
|
if ((std::uint8_t(reg_addr) & 0x8) == 0)
|
||||||
|
do_push(0xff_ub, modrm(MOD_REG, 0b010, reg_addr));
|
||||||
|
else
|
||||||
|
do_push(rex(0, {}, {}, reg_addr), 0xff_ub, modrm(MOD_REG, 0b010, reg_addr));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ... Args>
|
template <typename ... Args>
|
||||||
void instruction_builder::do_push(Args ... values)
|
void instruction_builder::do_push(Args ... values)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,8 @@ namespace pslang::jit::macos_aarch64
|
||||||
{
|
{
|
||||||
lcontext.f16_constants[f16_literal->value.repr] = pcontext.storage.size();
|
lcontext.f16_constants[f16_literal->value.repr] = pcontext.storage.size();
|
||||||
push_bytes(f16_literal->value.repr);
|
push_bytes(f16_literal->value.repr);
|
||||||
|
// Ensure 4-byte alignment
|
||||||
|
push_bytes(std::uint16_t{0});
|
||||||
}
|
}
|
||||||
else if (auto f32_literal = std::get_if<ast::f32_literal>(&node.value))
|
else if (auto f32_literal = std::get_if<ast::f32_literal>(&node.value))
|
||||||
{
|
{
|
||||||
|
|
@ -235,8 +237,8 @@ namespace pslang::jit::macos_aarch64
|
||||||
{
|
{
|
||||||
auto offset = lcontext.f16_constants.at(node.value.repr);
|
auto offset = lcontext.f16_constants.at(node.value.repr);
|
||||||
std::int32_t current = pcontext.storage.size();
|
std::int32_t current = pcontext.storage.size();
|
||||||
builder.ldr_fp_pc(0, 0, (offset - current) / 4);
|
builder.adr(0, (offset - current) / 4);
|
||||||
builder.fcvt(0, 0b10, 0, 0b01);
|
builder.ldr_fp(0, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(ast::f32_literal const & node)
|
void operator()(ast::f32_literal const & node)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ template <typename T>
|
||||||
T value;
|
T value;
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
if constexpr (std::is_same_v<T, ::pslang::types::half_float>)
|
if constexpr (std::is_same_v<T, ::pslang::types::half_float>)
|
||||||
value = {float(std::atof(str.data()))};
|
value = value.from_float(float(std::atof(str.data())));
|
||||||
else if constexpr (std::is_floating_point_v<T>)
|
else if constexpr (std::is_floating_point_v<T>)
|
||||||
value = std::atof(str.data());
|
value = std::atof(str.data());
|
||||||
else if constexpr (std::is_signed_v<T>)
|
else if constexpr (std::is_signed_v<T>)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <pslang/types/half_float.hpp>
|
|
||||||
|
|
||||||
#include <compare>
|
#include <compare>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace pslang::types
|
namespace pslang::types
|
||||||
{
|
{
|
||||||
|
|
@ -13,35 +12,20 @@ namespace pslang::types
|
||||||
|
|
||||||
struct half_float
|
struct half_float
|
||||||
{
|
{
|
||||||
float repr = 0.f;
|
using repr_type = std::uint16_t;
|
||||||
|
repr_type repr;
|
||||||
|
|
||||||
friend bool operator == (half_float const &, half_float const &) = default;
|
static float to_float(half_float value);
|
||||||
friend auto operator <=> (half_float const &, half_float const &) = default;
|
static half_float from_float(float value);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline half_float operator - (half_float f)
|
bool operator == (half_float const &, half_float const &);
|
||||||
{
|
std::partial_ordering operator <=> (half_float const &, half_float const &);
|
||||||
return {-f.repr};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline half_float operator + (half_float f1, half_float f2)
|
half_float operator - (half_float f);
|
||||||
{
|
half_float operator + (half_float f1, half_float f2);
|
||||||
return {f1.repr + f2.repr};
|
half_float operator - (half_float f1, half_float f2);
|
||||||
}
|
half_float operator * (half_float f1, half_float f2);
|
||||||
|
half_float operator / (half_float f1, half_float f2);
|
||||||
inline half_float operator - (half_float f1, half_float f2)
|
|
||||||
{
|
|
||||||
return {f1.repr - f2.repr};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline half_float operator * (half_float f1, half_float f2)
|
|
||||||
{
|
|
||||||
return {f1.repr * f2.repr};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline half_float operator / (half_float f1, half_float f2)
|
|
||||||
{
|
|
||||||
return {f1.repr / f2.repr};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
167
libs/types/source/half_float.cpp
Normal file
167
libs/types/source/half_float.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
#include <pslang/types/half_float.hpp>
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace pslang::types
|
||||||
|
{
|
||||||
|
|
||||||
|
using repr_type = half_float::repr_type;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr repr_type SIGN_BIT = 0x8000u;
|
||||||
|
constexpr repr_type EXPONENT_MASK = 0x7c00u;
|
||||||
|
constexpr repr_type MANTISSA_MASK = 0x03ff;
|
||||||
|
|
||||||
|
bool is_nan(half_float value)
|
||||||
|
{
|
||||||
|
return ((value.repr & EXPONENT_MASK) == EXPONENT_MASK) && ((value.repr & MANTISSA_MASK) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_zero(half_float value)
|
||||||
|
{
|
||||||
|
return (value.repr & (~SIGN_BIT)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
repr_type ordered(half_float value)
|
||||||
|
{
|
||||||
|
return ((value.repr & SIGN_BIT) != 0) ? ~value.repr : (value.repr ^ SIGN_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (half_float const & lhs, half_float const & rhs)
|
||||||
|
{
|
||||||
|
if (is_nan(lhs) || is_nan(rhs)) [[unlikely]]
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (is_zero(lhs) && is_zero(rhs))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return lhs == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::partial_ordering operator <=> (half_float const & lhs, half_float const & rhs)
|
||||||
|
{
|
||||||
|
if (is_nan(lhs) || is_nan(rhs)) [[unlikely]]
|
||||||
|
return std::partial_ordering::unordered;
|
||||||
|
|
||||||
|
if (is_zero(lhs) && is_zero(rhs))
|
||||||
|
return std::partial_ordering::equivalent;
|
||||||
|
|
||||||
|
return ordered(lhs) <=> ordered(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
float half_float::to_float(half_float value)
|
||||||
|
{
|
||||||
|
std::uint32_t sign = (value.repr & SIGN_BIT) >> 15;
|
||||||
|
std::uint32_t exponent = (value.repr & EXPONENT_MASK) >> 10;
|
||||||
|
std::uint32_t mantissa = value.repr & MANTISSA_MASK;
|
||||||
|
|
||||||
|
if (exponent == 0)
|
||||||
|
{
|
||||||
|
// Zero
|
||||||
|
if (mantissa == 0) [[likely]]
|
||||||
|
return std::bit_cast<float>(sign << 31);
|
||||||
|
|
||||||
|
// Subnormal
|
||||||
|
int shift = -14;
|
||||||
|
|
||||||
|
while ((mantissa & 0x400) == 0)
|
||||||
|
{
|
||||||
|
mantissa <<= 1;
|
||||||
|
--shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
mantissa &= 0x3ff;
|
||||||
|
|
||||||
|
return std::bit_cast<float>((sign << 31) | ((shift + 127) << 23) | (mantissa << 13));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend exponent for infinity & NaNs to all ones
|
||||||
|
if (exponent == 0x1fu) [[unlikely]]
|
||||||
|
exponent = 0xffu;
|
||||||
|
|
||||||
|
// Shifting NaN mantissa bits is mostly useless,
|
||||||
|
// but at least we preserve quiet vs signaling NaNs
|
||||||
|
return std::bit_cast<float>((sign << 31) | ((exponent - 15 + 127) << 23) | (mantissa << 13));
|
||||||
|
}
|
||||||
|
|
||||||
|
half_float half_float::from_float(float value)
|
||||||
|
{
|
||||||
|
uint32_t repr;
|
||||||
|
std::memcpy(&repr, &value, sizeof(repr));
|
||||||
|
|
||||||
|
std::uint32_t sign = (repr >> 16) & 0x8000;
|
||||||
|
std::uint32_t mantissa = repr & 0x007FFFFF;
|
||||||
|
std::int32_t exponent = ((repr >> 23) & 0xFF) - 127 + 15;
|
||||||
|
|
||||||
|
if (exponent <= 0)
|
||||||
|
{
|
||||||
|
if (exponent < -10)
|
||||||
|
return {repr_type(sign)};
|
||||||
|
|
||||||
|
mantissa |= 0x00800000;
|
||||||
|
|
||||||
|
uint32_t t = mantissa >> (1 - exponent);
|
||||||
|
|
||||||
|
// Rounding
|
||||||
|
if (t & 0x00001000)
|
||||||
|
t += 0x00002000;
|
||||||
|
|
||||||
|
return {repr_type(sign | (t >> 13))};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponent >= 31)
|
||||||
|
{
|
||||||
|
// Infinities
|
||||||
|
if (mantissa == 0)
|
||||||
|
return {repr_type(sign | 0x7C00)};
|
||||||
|
|
||||||
|
// NaNs
|
||||||
|
return {repr_type(sign | 0x7E00)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rounding
|
||||||
|
mantissa += 0x00001000;
|
||||||
|
|
||||||
|
if (mantissa & 0x00800000)
|
||||||
|
{
|
||||||
|
mantissa = 0;
|
||||||
|
exponent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponent >= 31)
|
||||||
|
return {repr_type(sign | 0x7C00)};
|
||||||
|
|
||||||
|
return {repr_type(sign | (exponent << 10) | (mantissa >> 13))};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline half_float operator - (half_float f)
|
||||||
|
{
|
||||||
|
return {repr_type(f.repr ^ SIGN_BIT)};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline half_float operator + (half_float f1, half_float f2)
|
||||||
|
{
|
||||||
|
return {half_float::from_float(half_float::to_float(f1) + half_float::to_float(f2))};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline half_float operator - (half_float f1, half_float f2)
|
||||||
|
{
|
||||||
|
return {half_float::from_float(half_float::to_float(f1) - half_float::to_float(f2))};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline half_float operator * (half_float f1, half_float f2)
|
||||||
|
{
|
||||||
|
return {half_float::from_float(half_float::to_float(f1) * half_float::to_float(f2))};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline half_float operator / (half_float f1, half_float f2)
|
||||||
|
{
|
||||||
|
return {half_float::from_float(half_float::to_float(f1) / half_float::to_float(f2))};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue