e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
746 lines
58 KiB
C
746 lines
58 KiB
C
#include "buildability_internal.h"
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
enum { BUILD_AARCH64_IMM12_MAX = 4095u };
|
|
|
|
bool z_build_backend_is_aarch64_direct(ZDirectBackend backend) { return backend == Z_DIRECT_BACKEND_ELF_AARCH64 || backend == Z_DIRECT_BACKEND_COFF_AARCH64; }
|
|
|
|
static bool build_const_u32_value(const IrValue *value, unsigned *out) {
|
|
if (!value || value->kind != IR_VALUE_INT || value->int_value > UINT32_MAX) return false;
|
|
if (out) *out = (unsigned)value->int_value;
|
|
return true;
|
|
}
|
|
|
|
static bool build_byte_view_const_len(const IrValue *view, unsigned *out) {
|
|
if (!view) return false;
|
|
if (view->kind == IR_VALUE_STRING_LITERAL || view->kind == IR_VALUE_ARRAY_BYTE_VIEW) {
|
|
if (out) *out = view->data_len;
|
|
return true;
|
|
}
|
|
if (view->kind != IR_VALUE_BYTE_SLICE) return false;
|
|
unsigned base_len = 0, start = 0, end = 0;
|
|
if (!build_byte_view_const_len(view->left, &base_len)) return false;
|
|
end = base_len;
|
|
if ((view->index && !build_const_u32_value(view->index, &start)) || (view->right && !build_const_u32_value(view->right, &end))) return false;
|
|
if (start > end || end > base_len) return false;
|
|
if (out) *out = end - start;
|
|
return true;
|
|
}
|
|
|
|
static IrTypeKind build_view_element_type(const IrValue *view) {
|
|
return view && view->element_type != IR_TYPE_UNSUPPORTED ? view->element_type : IR_TYPE_U8;
|
|
}
|
|
|
|
static bool build_check_json_error_label(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (!value || value->kind != IR_VALUE_JSON_ERROR_LABEL) return true;
|
|
if (!value->left) return z_build_diag(ctx, diag, "direct backend JSON error label is missing a status code", value->line, value->column, "missing JSON status");
|
|
if (value->arg_len != 4) return z_build_diag(ctx, diag, "direct backend JSON error label requires four label literals", value->line, value->column, "invalid JSON error labels");
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!value->args[i] || value->args[i]->kind != IR_VALUE_STRING_LITERAL || value->args[i]->type != IR_TYPE_BYTE_VIEW) {
|
|
return z_build_diag(ctx, diag, "direct backend JSON error label requires string literal labels", value->line, value->column, "invalid JSON error label");
|
|
}
|
|
}
|
|
return z_build_check_value(ctx, fun, value->left, false, scratch_slot, diag);
|
|
}
|
|
|
|
static unsigned build_type_index_shift(IrTypeKind type) {
|
|
if (type == IR_TYPE_U8 || type == IR_TYPE_BOOL) return 0;
|
|
if (type == IR_TYPE_U16) return 1;
|
|
if (type == IR_TYPE_I64 || type == IR_TYPE_U64) return 3;
|
|
return 2;
|
|
}
|
|
|
|
static bool build_scaled_index_exceeds_imm12(unsigned start, IrTypeKind element_type) {
|
|
return start > ((unsigned)BUILD_AARCH64_IMM12_MAX >> build_type_index_shift(element_type));
|
|
}
|
|
|
|
static bool build_aarch64_index_load_uses_scratch(const IrFunction *fun, const IrValue *value) {
|
|
if (!fun || value->kind != IR_VALUE_INDEX_LOAD || value->array_index >= fun->local_len) return true;
|
|
const IrLocal *local = &fun->locals[value->array_index];
|
|
unsigned const_index = 0;
|
|
return !(local->is_array && (local->element_type == IR_TYPE_U32 || local->element_type == IR_TYPE_I32 || local->element_type == IR_TYPE_USIZE) &&
|
|
build_const_u32_value(value->index, &const_index) && const_index < local->array_len);
|
|
}
|
|
|
|
static bool build_check_aarch64_byte_view_ptr_spill(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, unsigned scratch_slot, unsigned slot_count, const char *message, ZDiag *diag);
|
|
|
|
static bool build_check_aarch64_byte_view_len_spill(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, unsigned scratch_slot, unsigned slot_count, const char *message, ZDiag *diag) {
|
|
if (!view || view->kind != IR_VALUE_BYTE_SLICE) return true;
|
|
unsigned len = 0;
|
|
if (build_byte_view_const_len(view, &len) && len <= Z_DIRECT_FRAME_LOCAL_LIMIT_BYTES) return true;
|
|
if (!view->index && !view->right) {
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, view->left, scratch_slot, slot_count, message, diag)) return false;
|
|
return build_check_aarch64_byte_view_len_spill(ctx, fun, view->left, scratch_slot, slot_count, message, diag);
|
|
}
|
|
unsigned required_slot = view->right ? 3u : 2u;
|
|
if (scratch_slot + required_slot >= slot_count) return z_build_diag(ctx, diag, message, view->line, view->column, "expression too deep");
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, view->left, scratch_slot, slot_count, message, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, view->left, scratch_slot, slot_count, message, diag)) return false;
|
|
return (!view->index || z_build_check_value(ctx, fun, view->index, false, scratch_slot + 2, diag)) &&
|
|
(!view->right || z_build_check_value(ctx, fun, view->right, false, scratch_slot + 3, diag));
|
|
}
|
|
|
|
static bool build_check_aarch64_byte_view_ptr_spill(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, unsigned scratch_slot, unsigned slot_count, const char *message, ZDiag *diag) {
|
|
if (!view || view->kind != IR_VALUE_BYTE_SLICE) return true;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, view->left, scratch_slot, slot_count, message, diag)) return false;
|
|
unsigned start = 0;
|
|
if (view->index && !build_const_u32_value(view->index, &start)) {
|
|
if (scratch_slot >= slot_count) return z_build_diag(ctx, diag, message, view->line, view->column, "expression too deep");
|
|
if (!z_build_check_value(ctx, fun, view->index, false, scratch_slot + 1, diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static size_t build_value_abi_slots(const IrValue *value) {
|
|
size_t slots = 0;
|
|
for (size_t i = 0; value && i < value->arg_len; i++) {
|
|
slots += value->args[i] && value->args[i]->type == IR_TYPE_BYTE_VIEW ? 2u : 1u;
|
|
}
|
|
return slots;
|
|
}
|
|
|
|
static bool build_array_byte_view_has_storage(const IrFunction *fun, const IrValue *view) {
|
|
if (!view || view->kind != IR_VALUE_ARRAY_BYTE_VIEW || !fun || view->array_index >= fun->local_len) return false;
|
|
const IrLocal *local = &fun->locals[view->array_index];
|
|
return (local->is_array && view->field_offset == 0) || local->is_record;
|
|
}
|
|
|
|
static bool build_aarch64_byte_view_ptr(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, ZDiag *diag) {
|
|
if (!view) return z_build_diag(ctx, diag, "direct AArch64 byte view is missing", 1, 1, "missing byte view");
|
|
if (view->kind == IR_VALUE_LOCAL && fun && view->local_index < fun->local_len && fun->locals[view->local_index].type == IR_TYPE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_VEC_BYTES && fun && view->local_index < fun->local_len && fun->locals[view->local_index].type == IR_TYPE_VEC) return true;
|
|
if (view->kind == IR_VALUE_MAYBE_VALUE && fun && view->local_index < fun->local_len && fun->locals[view->local_index].type == IR_TYPE_MAYBE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_CALL && view->type == IR_TYPE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_FIELD_LOAD && view->type == IR_TYPE_BYTE_VIEW && fun && view->local_index < fun->local_len &&
|
|
(fun->locals[view->local_index].is_record || fun->locals[view->local_index].is_record_ref)) return true;
|
|
if (view->kind == IR_VALUE_STR_RUNTIME && view->type == IR_TYPE_BYTE_VIEW &&
|
|
(ctx->backend == Z_DIRECT_BACKEND_ELF_AARCH64 || ctx->backend == Z_DIRECT_BACKEND_COFF_AARCH64)) return true;
|
|
if (view->kind == IR_VALUE_JSON_ERROR_LABEL && view->type == IR_TYPE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_ARRAY_BYTE_VIEW && fun && view->array_index < fun->local_len) {
|
|
if (!build_array_byte_view_has_storage(fun, view)) return z_build_diag(ctx, diag, "direct AArch64 byte-view array requires a fixed array or record array field", view->line, view->column, "unsupported array view");
|
|
return true;
|
|
}
|
|
if (view->kind == IR_VALUE_STRING_LITERAL) return true;
|
|
if (view->kind == IR_VALUE_BYTE_SLICE) {
|
|
unsigned start = 0;
|
|
if (!build_aarch64_byte_view_ptr(ctx, fun, view->left, diag)) return false;
|
|
if (build_const_u32_value(view->index, &start) && build_scaled_index_exceeds_imm12(start, build_view_element_type(view))) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 byte slice constant start is too large", view->line, view->column, "unsupported byte slice");
|
|
}
|
|
return true;
|
|
}
|
|
return z_build_diag(ctx, diag, "direct AArch64 value is not a supported byte view", view->line, view->column, "unsupported byte view");
|
|
}
|
|
|
|
bool z_build_check_aarch64_byte_view_len(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, ZDiag *diag) {
|
|
unsigned len = 0;
|
|
if (build_byte_view_const_len(view, &len)) return len <= Z_DIRECT_FRAME_LOCAL_LIMIT_BYTES || z_build_diag(ctx, diag, "direct AArch64 byte-view length is too large for this backend", view->line, view->column, "large byte view");
|
|
if (!view) return z_build_diag(ctx, diag, "direct AArch64 byte view is missing", 1, 1, "missing byte view");
|
|
if (view->kind == IR_VALUE_STRING_LITERAL || view->kind == IR_VALUE_ARRAY_BYTE_VIEW) {
|
|
if (view->data_len > Z_DIRECT_FRAME_LOCAL_LIMIT_BYTES) return z_build_diag(ctx, diag, "direct AArch64 byte-view length is too large for this backend", view->line, view->column, "large byte view");
|
|
return true;
|
|
}
|
|
if (view->kind == IR_VALUE_LOCAL && fun && view->local_index < fun->local_len && fun->locals[view->local_index].type == IR_TYPE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_VEC_BYTES && fun && view->local_index < fun->local_len && fun->locals[view->local_index].type == IR_TYPE_VEC) return true;
|
|
if (view->kind == IR_VALUE_MAYBE_VALUE && fun && view->local_index < fun->local_len && fun->locals[view->local_index].type == IR_TYPE_MAYBE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_CALL && view->type == IR_TYPE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_FIELD_LOAD && view->type == IR_TYPE_BYTE_VIEW && fun && view->local_index < fun->local_len &&
|
|
(fun->locals[view->local_index].is_record || fun->locals[view->local_index].is_record_ref)) return true;
|
|
if (view->kind == IR_VALUE_STR_RUNTIME && view->type == IR_TYPE_BYTE_VIEW &&
|
|
(ctx->backend == Z_DIRECT_BACKEND_ELF_AARCH64 || ctx->backend == Z_DIRECT_BACKEND_COFF_AARCH64)) return true;
|
|
if (view->kind == IR_VALUE_JSON_ERROR_LABEL && view->type == IR_TYPE_BYTE_VIEW) return true;
|
|
if (view->kind == IR_VALUE_BYTE_SLICE) {
|
|
return z_build_check_aarch64_byte_view_len(ctx, fun, view->left, diag);
|
|
}
|
|
return z_build_diag(ctx, diag, "direct AArch64 byte-view length currently requires a literal, constant slice, or byte-view local", view->line, view->column, "unsupported byte view length");
|
|
}
|
|
|
|
bool z_build_check_aarch64_byte_view(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, ZDiag *diag) { return build_aarch64_byte_view_ptr(ctx, fun, view, diag) && z_build_check_aarch64_byte_view_len(ctx, fun, view, diag); }
|
|
|
|
bool z_build_check_aarch64_world_write_byte_view(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, ZDiag *diag) {
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, view, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, view, 0, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 World write exceeds scratch register spill capacity", diag)) return false;
|
|
return build_check_aarch64_byte_view_len_spill(ctx, fun, view, 0, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 World write exceeds scratch register spill capacity", diag);
|
|
}
|
|
|
|
static bool build_aarch64_byte_operation(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, bool *skip_left, ZDiag *diag) {
|
|
if (value->kind == IR_VALUE_BYTE_COPY) {
|
|
if (scratch_slot + 3 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 byte copy exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte copy exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte copy exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte copy exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 3, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte copy exceeds scratch register spill capacity", diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_BYTE_FILL) {
|
|
if (scratch_slot + 2 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 byte fill exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte fill exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte fill exceeds scratch register spill capacity", diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_EQ) {
|
|
if (scratch_slot + 3 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 byte-view equality exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view equality exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view equality exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view equality exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view equality exceeds scratch register spill capacity", diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_LEN && !build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view length exceeds scratch register spill capacity", diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_LEN && !z_build_check_aarch64_byte_view_len(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_REMAINING) {
|
|
if (scratch_slot + 1 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 byte-view remaining exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view remaining exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_aarch64_byte_view_len(ctx, fun, value->left, diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_INDEX_LOAD && build_aarch64_index_load_uses_scratch(fun, value) && scratch_slot >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 indexed load exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && scratch_slot >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 byte-view indexed load exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && !build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 byte-view indexed load exceeds scratch register spill capacity", diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && !z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_JSON_ERROR_LABEL && !build_check_json_error_label(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (value->kind == IR_VALUE_CRC32_BYTES) {
|
|
if (scratch_slot + 3 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 CRC32 exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 CRC32 exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 CRC32 exceeds scratch register spill capacity", diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_JSON_VALIDATE_BYTES || value->kind == IR_VALUE_JSON_STREAM_TOKENS_BYTES || value->kind == IR_VALUE_JSON_DIAGNOSTIC_BYTES ||
|
|
value->kind == IR_VALUE_JSON_FIELD || value->kind == IR_VALUE_JSON_LOOKUP_SCALAR || value->kind == IR_VALUE_JSON_STRING_DECODE || value->kind == IR_VALUE_JSON_STRING_FIELD ||
|
|
value->kind == IR_VALUE_JSON_WRITE_STRING) {
|
|
bool two_views = value->kind == IR_VALUE_JSON_FIELD || value->kind == IR_VALUE_JSON_LOOKUP_SCALAR || value->kind == IR_VALUE_JSON_STRING_DECODE || value->kind == IR_VALUE_JSON_WRITE_STRING;
|
|
bool three_views = value->kind == IR_VALUE_JSON_STRING_FIELD;
|
|
unsigned needed_slots = three_views ? 8u : (two_views ? 6u : 2u);
|
|
if (scratch_slot + needed_slots - 1u >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 JSON helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if ((two_views || three_views) && !z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (three_views && !z_build_check_aarch64_byte_view(ctx, fun, value->index, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
if ((two_views || three_views) && !build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
if ((two_views || three_views) && !build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 3, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
if (three_views && !build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->index, scratch_slot + 4, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
if (three_views && !build_check_aarch64_byte_view_len_spill(ctx, fun, value->index, scratch_slot + 5, BUILD_AARCH64_SCRATCH_SLOT_COUNT, "direct AArch64 JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_LEN || value->kind == IR_VALUE_BYTE_VIEW_REMAINING || value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD) *skip_left = true;
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_str_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_STR_RUNTIME) return true;
|
|
if (ctx->backend != Z_DIRECT_BACKEND_ELF_AARCH64 && ctx->backend != Z_DIRECT_BACKEND_COFF_AARCH64) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.str runtime helpers require an object target with runtime relocations", value->line, value->column, "unsupported std.str runtime");
|
|
}
|
|
if (scratch_slot + 8 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.str helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
const IrValue *arg = value->args[i];
|
|
if (arg && arg->type == IR_TYPE_BYTE_VIEW && !z_build_check_aarch64_byte_view(ctx, fun, arg, diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_math_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_MATH_RUNTIME) return true;
|
|
if (ctx->backend != Z_DIRECT_BACKEND_ELF_AARCH64 && ctx->backend != Z_DIRECT_BACKEND_COFF_AARCH64) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.math runtime helpers require an object target with runtime relocations", value->line, value->column, "unsupported std.math runtime");
|
|
}
|
|
if (value->arg_len > 3) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.math helper supports at most three scalar arguments", value->line, value->column, "invalid std.math arity");
|
|
}
|
|
if (scratch_slot + 5 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.math helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!z_build_check_value(ctx, fun, value->args[i], false, scratch_slot + 3 + (unsigned)i, diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_time_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_TIME_RUNTIME) return true;
|
|
if (ctx->backend != Z_DIRECT_BACKEND_ELF_AARCH64 && ctx->backend != Z_DIRECT_BACKEND_COFF_AARCH64) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.time runtime helpers require an object target with runtime relocations", value->line, value->column, "unsupported std.time runtime");
|
|
}
|
|
if (value->arg_len > 3) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.time helper supports at most three scalar arguments", value->line, value->column, "invalid std.time arity");
|
|
}
|
|
if (scratch_slot + 5 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.time helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!z_build_check_value(ctx, fun, value->args[i], false, scratch_slot + 3 + (unsigned)i, diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_term_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_TERM_RUNTIME) return true;
|
|
if (ctx->backend != Z_DIRECT_BACKEND_ELF_AARCH64 && ctx->backend != Z_DIRECT_BACKEND_COFF_AARCH64) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.term runtime helpers require an object target with runtime relocations", value->line, value->column, "unsupported std.term runtime");
|
|
}
|
|
if (value->arg_len > 1) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.term helper supports at most one fallback argument", value->line, value->column, "invalid std.term arity");
|
|
}
|
|
if (scratch_slot + 1 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.term helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (value->arg_len == 1 && !z_build_check_value(ctx, fun, value->args[0], false, scratch_slot, diag)) return false;
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_text_parse_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind == IR_VALUE_TEXT_RUNTIME) {
|
|
if (value->arg_len != 1) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.text helper requires one byte-view argument", value->line, value->column, "invalid std.text arity");
|
|
}
|
|
if (scratch_slot + 2 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.text helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!value->args[0] || !z_build_check_aarch64_byte_view(ctx, fun, value->args[0], diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_PARSE_RUNTIME) {
|
|
if (value->arg_len < 1 || value->arg_len > 2) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.parse helper requires one byte-view argument and optional byte argument", value->line, value->column, "invalid std.parse arity");
|
|
}
|
|
if (scratch_slot + 3 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.parse helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!value->args[0] || !z_build_check_aarch64_byte_view(ctx, fun, value->args[0], diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_PARSE_I32 || value->kind == IR_VALUE_PARSE_U32) {
|
|
if (scratch_slot + 1 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 parse helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!value->left || !z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_fmt_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_FMT_BOOL && value->kind != IR_VALUE_FMT_HEX_U32 && value->kind != IR_VALUE_FMT_I32 &&
|
|
value->kind != IR_VALUE_FMT_U32 && value->kind != IR_VALUE_FMT_USIZE) return true;
|
|
if (ctx->backend != Z_DIRECT_BACKEND_ELF_AARCH64 && ctx->backend != Z_DIRECT_BACKEND_COFF_AARCH64) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.fmt helpers require an object target with runtime relocations", value->line, value->column, "unsupported std.fmt runtime");
|
|
}
|
|
if (scratch_slot + 2 >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 std.fmt helper exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (!value->left || !z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
return true;
|
|
}
|
|
|
|
static bool build_aarch64_proc_runtime(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_PROC_CAPTURE &&
|
|
value->kind != IR_VALUE_PROC_SPAWN_INHERIT &&
|
|
value->kind != IR_VALUE_PROC_CAPTURE_FILES &&
|
|
value->kind != IR_VALUE_PROC_CHILD_SPAWN &&
|
|
value->kind != IR_VALUE_PROC_CHILD_OP &&
|
|
value->kind != IR_VALUE_PROC_CHILD_IO &&
|
|
value->kind != IR_VALUE_PROC_PTY_RESIZE) return true;
|
|
unsigned required = 2;
|
|
const char *message = "direct AArch64 std.proc helper exceeds scratch register spill capacity";
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE) required = value->arg_len == 2 ? 6 : 3;
|
|
if (value->kind == IR_VALUE_PROC_CHILD_IO) required = 3;
|
|
if (value->kind == IR_VALUE_PROC_PTY_RESIZE) required = 3;
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE_FILES) required = value->arg_len == 2 ? 8 : 5;
|
|
if (value->kind == IR_VALUE_PROC_SPAWN_INHERIT && value->arg_len == 4) required = 8;
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->arg_len == 4) required = 8;
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->right) required = 4;
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->index) required = 6;
|
|
if (scratch_slot + required >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, message, value->line, value->column, "expression too deep");
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_SPAWN_INHERIT && value->arg_len == 4) {
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->args[i], diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE && value->arg_len == 2) {
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->args[0], diag)) return false;
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->args[1], diag)) return false;
|
|
return z_build_check_aarch64_byte_view(ctx, fun, value->right, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE_FILES && value->arg_len == 2) {
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->args[0], diag)) return false;
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->args[1], diag)) return false;
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
return z_build_check_aarch64_byte_view(ctx, fun, value->index, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->arg_len == 4) {
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!z_build_check_aarch64_byte_view(ctx, fun, value->args[i], diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
if ((value->kind == IR_VALUE_PROC_SPAWN_INHERIT ||
|
|
value->kind == IR_VALUE_PROC_CAPTURE ||
|
|
value->kind == IR_VALUE_PROC_CAPTURE_FILES ||
|
|
value->kind == IR_VALUE_PROC_CHILD_SPAWN) &&
|
|
!z_build_check_aarch64_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if ((value->kind == IR_VALUE_PROC_CAPTURE ||
|
|
value->kind == IR_VALUE_PROC_CAPTURE_FILES ||
|
|
value->kind == IR_VALUE_PROC_CHILD_IO) &&
|
|
!z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->right && !z_build_check_aarch64_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->index && !z_build_check_aarch64_byte_view(ctx, fun, value->index, diag)) return false;
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE_FILES && !z_build_check_aarch64_byte_view(ctx, fun, value->index, diag)) return false;
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_binary_operator(const ZBuildability *ctx, const IrValue *value, unsigned scratch_slot, unsigned *right_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_BINARY) return true;
|
|
bool supported = true;
|
|
if (ctx->backend == Z_DIRECT_BACKEND_COFF_X64) {
|
|
supported = value->binary_op == IR_BIN_ADD || value->binary_op == IR_BIN_SUB || value->binary_op == IR_BIN_MUL ||
|
|
value->binary_op == IR_BIN_DIV || value->binary_op == IR_BIN_MOD ||
|
|
value->binary_op == IR_BIN_AND || value->binary_op == IR_BIN_OR;
|
|
}
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO_X64 || ctx->backend == Z_DIRECT_BACKEND_MACHO64 || z_build_backend_is_aarch64_direct(ctx->backend)) {
|
|
supported = value->binary_op == IR_BIN_ADD || value->binary_op == IR_BIN_SUB || value->binary_op == IR_BIN_MUL ||
|
|
value->binary_op == IR_BIN_DIV || value->binary_op == IR_BIN_MOD ||
|
|
value->binary_op == IR_BIN_AND || value->binary_op == IR_BIN_OR;
|
|
}
|
|
if (!supported) return z_build_diag(ctx, diag, "direct backend buildability does not support this binary operator", value->line, value->column, "unsupported operator");
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO64 && value->binary_op != IR_BIN_AND && value->binary_op != IR_BIN_OR && scratch_slot >= BUILD_MACHO_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 Mach-O expression nesting exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && value->binary_op != IR_BIN_AND && value->binary_op != IR_BIN_OR && scratch_slot >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 expression nesting exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if ((ctx->backend == Z_DIRECT_BACKEND_MACHO64 || z_build_backend_is_aarch64_direct(ctx->backend)) &&
|
|
value->binary_op != IR_BIN_AND && value->binary_op != IR_BIN_OR) {
|
|
*right_slot = scratch_slot + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_compare(const ZBuildability *ctx, const IrValue *value, unsigned scratch_slot, unsigned *right_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_COMPARE) return true;
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO64 && scratch_slot >= BUILD_MACHO_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 Mach-O expression nesting exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && scratch_slot >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 expression nesting exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
}
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO64 || z_build_backend_is_aarch64_direct(ctx->backend)) *right_slot = scratch_slot + 1;
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_call_shape(const ZBuildability *ctx, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind != IR_VALUE_CALL) return true;
|
|
size_t max_args = (ctx->backend == Z_DIRECT_BACKEND_ELF64 ||
|
|
ctx->backend == Z_DIRECT_BACKEND_MACHO_X64 ||
|
|
ctx->backend == Z_DIRECT_BACKEND_COFF_X64 ||
|
|
ctx->backend == Z_DIRECT_BACKEND_MACHO64) ? 8 : 6;
|
|
size_t abi_slots = build_value_abi_slots(value);
|
|
if (!z_build_backend_is_aarch64_direct(ctx->backend) && abi_slots > max_args) {
|
|
char actual[80];
|
|
snprintf(actual, sizeof(actual), "%zu ABI argument slot(s)", abi_slots);
|
|
return z_build_diag(ctx, diag, "direct backend buildability found a call with too many arguments", value->line, value->column, actual);
|
|
}
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO64 && scratch_slot + abi_slots >= BUILD_MACHO_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 Mach-O call argument nesting exceeds scratch spill capacity", value->line, value->column, "too many nested call arguments");
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && scratch_slot + abi_slots >= BUILD_AARCH64_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 call argument nesting exceeds scratch spill capacity", value->line, value->column, "too many nested call arguments");
|
|
return true;
|
|
}
|
|
|
|
typedef bool (*BuildByteViewCheck)(const ZBuildability *ctx, const IrFunction *fun, const IrValue *view, ZDiag *diag);
|
|
|
|
static bool build_check_two_byte_views(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, BuildByteViewCheck check, ZDiag *diag) {
|
|
return check(ctx, fun, value->left, diag) && check(ctx, fun, value->right, diag);
|
|
}
|
|
|
|
static bool build_kind_is_fmt_buffer_use(IrValueKind kind) {
|
|
return kind == IR_VALUE_FMT_BOOL || kind == IR_VALUE_FMT_HEX_U32 || kind == IR_VALUE_FMT_I32 ||
|
|
kind == IR_VALUE_FMT_U32 || kind == IR_VALUE_FMT_USIZE;
|
|
}
|
|
|
|
static bool build_kind_is_skip_left_view(IrValueKind kind) {
|
|
return kind == IR_VALUE_BYTE_VIEW_LEN || kind == IR_VALUE_BYTE_VIEW_REMAINING || kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD ||
|
|
kind == IR_VALUE_VEC_BYTES;
|
|
}
|
|
|
|
static bool build_check_linear_byte_view_target(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, BuildByteViewCheck check_view, BuildByteViewCheck check_len, bool include_json_parse, bool include_alloc_skip_left, bool *skip_left, ZDiag *diag) {
|
|
if (value->kind == IR_VALUE_BYTE_COPY && !build_check_two_byte_views(ctx, fun, value, check_view, diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_FILL && !check_view(ctx, fun, value->right, diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_EQ && !build_check_two_byte_views(ctx, fun, value, check_view, diag)) return false;
|
|
if (value->kind == IR_VALUE_CRC32_BYTES && !check_view(ctx, fun, value->left, diag)) return false;
|
|
if ((value->kind == IR_VALUE_JSON_VALIDATE_BYTES || value->kind == IR_VALUE_JSON_STREAM_TOKENS_BYTES || value->kind == IR_VALUE_JSON_DIAGNOSTIC_BYTES ||
|
|
(include_json_parse && value->kind == IR_VALUE_JSON_PARSE_BYTES)) &&
|
|
!check_view(ctx, fun, value->left, diag)) return false;
|
|
if ((value->kind == IR_VALUE_JSON_FIELD || value->kind == IR_VALUE_JSON_LOOKUP_SCALAR || value->kind == IR_VALUE_JSON_STRING_DECODE || value->kind == IR_VALUE_JSON_WRITE_STRING) && !build_check_two_byte_views(ctx, fun, value, check_view, diag)) return false;
|
|
if (value->kind == IR_VALUE_JSON_STRING_FIELD && !(check_view(ctx, fun, value->left, diag) && check_view(ctx, fun, value->right, diag) && check_view(ctx, fun, value->index, diag))) return false;
|
|
if (value->kind == IR_VALUE_JSON_WRITE_RUNTIME) {
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (value->args[i] && value->args[i]->type == IR_TYPE_BYTE_VIEW && !check_view(ctx, fun, value->args[i], diag)) return false;
|
|
}
|
|
}
|
|
if (build_kind_is_fmt_buffer_use(value->kind) && !check_view(ctx, fun, value->left, diag)) return false;
|
|
if ((value->kind == IR_VALUE_BYTE_VIEW_LEN || value->kind == IR_VALUE_BYTE_VIEW_REMAINING) && !check_len(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && !check_view(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_JSON_ERROR_LABEL && !build_check_json_error_label(ctx, fun, value, 0, diag)) return false;
|
|
if (include_alloc_skip_left && (value->kind == IR_VALUE_FIXED_BUF_ALLOC || value->kind == IR_VALUE_VEC_INIT) && !check_view(ctx, fun, value->left, diag)) return false;
|
|
if (build_kind_is_skip_left_view(value->kind) || (include_alloc_skip_left && (value->kind == IR_VALUE_FIXED_BUF_ALLOC || value->kind == IR_VALUE_VEC_INIT))) {
|
|
if (skip_left) *skip_left = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_macho64_capacity(const ZBuildability *ctx, const IrValue *value, unsigned scratch_slot, unsigned required, const char *message, ZDiag *diag) {
|
|
if (scratch_slot + required >= BUILD_MACHO_SCRATCH_SLOT_COUNT) {
|
|
return z_build_diag(ctx, diag, message, value->line, value->column, "expression too deep");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_macho64_byte_operation(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind == IR_VALUE_BYTE_COPY) {
|
|
const char *message = "direct AArch64 Mach-O byte copy exceeds scratch register spill capacity";
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 3, message, diag)) return false;
|
|
if (!build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
return build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 3, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_BYTE_FILL) {
|
|
const char *message = "direct AArch64 Mach-O byte fill exceeds scratch register spill capacity";
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 2, message, diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->right, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
return build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_EQ) {
|
|
const char *message = "direct AArch64 Mach-O byte-view equality exceeds scratch register spill capacity";
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 3, message, diag)) return false;
|
|
if (!build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->right, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
return build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->right, scratch_slot + 2, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_macho64_text_args_fmt(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind == IR_VALUE_STR_CONTAINS) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 3, "direct AArch64 Mach-O string contains exceeds scratch register spill capacity", diag)) return false;
|
|
return build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PARSE_I32 || value->kind == IR_VALUE_PARSE_U32) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 1, "direct AArch64 Mach-O parse helper exceeds scratch register spill capacity", diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->left, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PARSE_RUNTIME) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 3, "direct AArch64 Mach-O parse runtime helper exceeds scratch register spill capacity", diag)) return false;
|
|
return value->arg_len && z_build_check_macho_byte_view(ctx, fun, value->args[0], diag);
|
|
}
|
|
if (value->kind == IR_VALUE_ARGS_FIND || value->kind == IR_VALUE_ARGS_CONTAINS || value->kind == IR_VALUE_ARGS_VALUE_AFTER || value->kind == IR_VALUE_ARGS_VALUE_AFTER_PARSE_U32) {
|
|
const char *message = (value->kind == IR_VALUE_ARGS_FIND || value->kind == IR_VALUE_ARGS_CONTAINS) ?
|
|
"direct AArch64 Mach-O args find exceeds scratch register spill capacity" :
|
|
"direct AArch64 Mach-O args option lookup exceeds scratch register spill capacity";
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 1, message, diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->left, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_ARGS_VALUE_AFTER_OR) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 2, "direct AArch64 Mach-O args option fallback exceeds scratch register spill capacity", diag)) return false;
|
|
return build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag);
|
|
}
|
|
if (build_kind_is_fmt_buffer_use(value->kind)) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 2, "direct AArch64 Mach-O fmt helper exceeds scratch register spill capacity", diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->left, diag);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_macho64_view_operation(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_LEN && !build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot, BUILD_MACHO_SCRATCH_SLOT_COUNT, "direct AArch64 Mach-O byte-view length exceeds scratch register spill capacity", diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_LEN && !z_build_check_macho_byte_view_len(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_REMAINING) {
|
|
const char *message = "direct AArch64 Mach-O byte-view remaining exceeds scratch register spill capacity";
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 1, message, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
if (!z_build_check_macho_byte_view_len(ctx, fun, value->left, diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_INDEX_LOAD && build_aarch64_index_load_uses_scratch(fun, value) && scratch_slot >= BUILD_MACHO_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 Mach-O indexed load exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && scratch_slot >= BUILD_MACHO_SCRATCH_SLOT_COUNT) return z_build_diag(ctx, diag, "direct AArch64 Mach-O byte-view indexed load exceeds scratch register spill capacity", value->line, value->column, "expression too deep");
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && !build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, "direct AArch64 Mach-O byte-view indexed load exceeds scratch register spill capacity", diag)) return false;
|
|
if (value->kind == IR_VALUE_BYTE_VIEW_INDEX_LOAD && !z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_JSON_ERROR_LABEL && !build_check_json_error_label(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (value->kind == IR_VALUE_CRC32_BYTES) {
|
|
const char *message = "direct AArch64 Mach-O CRC32 exceeds scratch register spill capacity";
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 3, message, diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!build_check_aarch64_byte_view_ptr_spill(ctx, fun, value->left, scratch_slot, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag)) return false;
|
|
return build_check_aarch64_byte_view_len_spill(ctx, fun, value->left, scratch_slot + 1, BUILD_MACHO_SCRATCH_SLOT_COUNT, message, diag);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_macho64_json_http(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, ZDiag *diag) {
|
|
if ((value->kind == IR_VALUE_FIXED_BUF_ALLOC || value->kind == IR_VALUE_VEC_INIT) && !z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if ((value->kind == IR_VALUE_JSON_PARSE_BYTES || value->kind == IR_VALUE_JSON_VALIDATE_BYTES || value->kind == IR_VALUE_JSON_STREAM_TOKENS_BYTES || value->kind == IR_VALUE_JSON_DIAGNOSTIC_BYTES) && !z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if ((value->kind == IR_VALUE_JSON_FIELD || value->kind == IR_VALUE_JSON_LOOKUP_SCALAR || value->kind == IR_VALUE_JSON_STRING_DECODE || value->kind == IR_VALUE_JSON_WRITE_STRING) && !build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag)) return false;
|
|
if (value->kind == IR_VALUE_JSON_STRING_FIELD) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 8, "direct AArch64 Mach-O JSON helper exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->right, diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->index, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_JSON_WRITE_RUNTIME) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 8, "direct AArch64 Mach-O JSON writer exceeds scratch register spill capacity", diag)) return false;
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (value->args[i] && value->args[i]->type == IR_TYPE_BYTE_VIEW && !z_build_check_macho_byte_view(ctx, fun, value->args[i], diag)) return false;
|
|
}
|
|
}
|
|
if (value->kind == IR_VALUE_HTTP_FETCH && !build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag)) return false;
|
|
if ((value->kind == IR_VALUE_HTTP_RESPONSE_LEN || value->kind == IR_VALUE_HTTP_RESPONSE_HEADERS_LEN || value->kind == IR_VALUE_HTTP_RESPONSE_BODY_OFFSET) && !z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_HTTP_HEADER_VALUE && !build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag)) return false;
|
|
if ((value->kind == IR_VALUE_HTTP_REQUEST_METHOD_NAME || value->kind == IR_VALUE_HTTP_REQUEST_PATH) && !z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (value->kind == IR_VALUE_HTTP_REQUEST_MATCHES) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 6, "direct AArch64 Mach-O HTTP request matcher exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->index, diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->right, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_HTTP_REQUEST_BODY_WITHIN) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 4, "direct AArch64 Mach-O HTTP request body helper exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
}
|
|
if (value->kind == IR_VALUE_HTTP_WRITE_JSON_RESPONSE) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 4, "direct AArch64 Mach-O HTTP JSON response helper exceeds scratch register spill capacity", diag)) return false;
|
|
return build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE) {
|
|
if (value->arg_len == 2) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 6, "direct AArch64 Mach-O std.proc.captureArgs exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->args[0], diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->args[1], diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->right, diag);
|
|
}
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 4, "direct AArch64 Mach-O std.proc.capture exceeds scratch register spill capacity", diag)) return false;
|
|
return build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CAPTURE_FILES) {
|
|
if (value->arg_len == 2) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 8, "direct AArch64 Mach-O std.proc.captureFilesArgs exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->args[0], diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->args[1], diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->right, diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->index, diag);
|
|
}
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 6, "direct AArch64 Mach-O std.proc.captureFiles exceeds scratch register spill capacity", diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->left, diag)) return false;
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->right, diag)) return false;
|
|
return z_build_check_macho_byte_view(ctx, fun, value->index, diag);
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_SPAWN_INHERIT && value->arg_len == 4) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 8, "direct AArch64 Mach-O std.proc.spawnInheritArgs exceeds scratch register spill capacity", diag)) return false;
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->args[i], diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->arg_len == 4) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, 8, "direct AArch64 Mach-O std.proc.spawnChildArgs exceeds scratch register spill capacity", diag)) return false;
|
|
for (size_t i = 0; i < value->arg_len; i++) {
|
|
if (!z_build_check_macho_byte_view(ctx, fun, value->args[i], diag)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_CHILD_SPAWN && value->right) {
|
|
if (!build_check_macho64_capacity(ctx, value, scratch_slot, value->index ? 6 : 4, value->index ? "direct AArch64 Mach-O std.proc.spawnChildInEnv exceeds scratch register spill capacity" : "direct AArch64 Mach-O std.proc.spawnChildIn exceeds scratch register spill capacity", diag)) return false;
|
|
if (!build_check_two_byte_views(ctx, fun, value, z_build_check_macho_byte_view, diag)) return false;
|
|
if (value->index && !z_build_check_macho_byte_view(ctx, fun, value->index, diag)) return false;
|
|
return true;
|
|
}
|
|
if (value->kind == IR_VALUE_PROC_PTY_RESIZE) {
|
|
return build_check_macho64_capacity(ctx, value, scratch_slot, 3, "direct AArch64 Mach-O std.pty.resize exceeds scratch register spill capacity", diag);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool build_check_macho64_target_value(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, bool *skip_left, ZDiag *diag) {
|
|
if (!build_check_macho64_byte_operation(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (!build_check_macho64_text_args_fmt(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (!build_check_macho64_view_operation(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (!build_check_macho64_json_http(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (build_kind_is_skip_left_view(value->kind) && skip_left) *skip_left = true;
|
|
return true;
|
|
}
|
|
|
|
bool z_build_check_target_value(const ZBuildability *ctx, const IrFunction *fun, const IrValue *value, unsigned scratch_slot, bool *skip_left, unsigned *right_slot, ZDiag *diag) {
|
|
if (skip_left) *skip_left = false;
|
|
if (right_slot) *right_slot = scratch_slot;
|
|
if (!ctx || !value) return true;
|
|
if (ctx->backend == Z_DIRECT_BACKEND_COFF_X64) {
|
|
if (!build_check_linear_byte_view_target(ctx, fun, value, z_build_check_coff_byte_view, z_build_check_coff_byte_view_len, false, true, skip_left, diag)) return false;
|
|
}
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO_X64) {
|
|
if (!build_check_linear_byte_view_target(ctx, fun, value, z_build_check_macho_x64_byte_view, z_build_check_macho_x64_byte_view_len, true, false, skip_left, diag)) return false;
|
|
}
|
|
if (ctx->backend == Z_DIRECT_BACKEND_MACHO64) {
|
|
if (!build_check_macho64_target_value(ctx, fun, value, scratch_slot, skip_left, diag)) return false;
|
|
}
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_byte_operation(ctx, fun, value, scratch_slot, skip_left, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_str_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_text_parse_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_fmt_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_proc_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_time_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_term_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
if (z_build_backend_is_aarch64_direct(ctx->backend) && !build_aarch64_math_runtime(ctx, fun, value, scratch_slot, diag)) return false;
|
|
return build_check_binary_operator(ctx, value, scratch_slot, right_slot, diag) &&
|
|
build_check_compare(ctx, value, scratch_slot, right_slot, diag) &&
|
|
build_check_call_shape(ctx, value, scratch_slot, diag);
|
|
}
|
|
|
|
unsigned z_build_target_call_arg_slot(const ZBuildability *ctx, const IrValue *value, unsigned scratch_slot) {
|
|
if (ctx && value && (ctx->backend == Z_DIRECT_BACKEND_MACHO64 || z_build_backend_is_aarch64_direct(ctx->backend)) && value->kind == IR_VALUE_CALL) {
|
|
return scratch_slot + (unsigned)build_value_abi_slots(value);
|
|
}
|
|
return scratch_slot;
|
|
}
|
|
|
|
bool z_build_check_aarch64_function_shape(const ZBuildability *ctx, const IrFunction *fun, ZDiag *diag) {
|
|
size_t abi_slots = 0;
|
|
for (size_t i = 0; i < fun->param_count; i++) abi_slots += fun->locals[i].type == IR_TYPE_BYTE_VIEW ? 2u : 1u;
|
|
if (abi_slots > 8) return z_build_diag(ctx, diag, "direct AArch64 buildability found too many ABI argument slots", fun->line, fun->column, fun->name);
|
|
if (fun->return_type != IR_TYPE_VOID && !z_build_is_elf_scalar(fun->return_type) &&
|
|
fun->return_type != IR_TYPE_BYTE_VIEW && fun->return_type != IR_TYPE_MAYBE_BYTE_VIEW && fun->return_type != IR_TYPE_MAYBE_SCALAR) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 buildability currently supports Void, primitive scalars, byte views, Maybe byte views, and Maybe scalar returns", fun->line, fun->column, z_build_type_name(fun->return_type));
|
|
}
|
|
size_t frame_bytes = fun->frame_bytes ? fun->frame_bytes : fun->local_len * 8u;
|
|
if (frame_bytes > Z_DIRECT_FRAME_LOCAL_LIMIT_BYTES) return z_build_diag(ctx, diag, "direct AArch64 stack frame exceeds the supported per-function locals limit", fun->line, fun->column, fun->name);
|
|
for (size_t i = 0; i < fun->local_len; i++) {
|
|
const IrLocal *local = &fun->locals[i];
|
|
if (local->type == IR_TYPE_BYTE_VIEW) continue;
|
|
if (local->is_record || local->is_record_ref) continue;
|
|
if (local->is_array) {
|
|
bool array_ok = local->element_type == IR_TYPE_U8 || local->element_type == IR_TYPE_BOOL || local->element_type == IR_TYPE_U16 ||
|
|
local->element_type == IR_TYPE_U32 || local->element_type == IR_TYPE_I32 || local->element_type == IR_TYPE_USIZE ||
|
|
local->element_type == IR_TYPE_I64 || local->element_type == IR_TYPE_U64;
|
|
if (!array_ok) return z_build_diag(ctx, diag, "direct AArch64 object buildability does not support this fixed-array local", local->line, local->column, z_build_type_name(local->element_type));
|
|
continue;
|
|
}
|
|
if (local->type == IR_TYPE_MAYBE_BYTE_VIEW || local->type == IR_TYPE_MAYBE_SCALAR) continue;
|
|
if (!z_build_is_elf_scalar(local->type)) {
|
|
return z_build_diag(ctx, diag, "direct AArch64 object buildability does not support this local type", local->line, local->column, z_build_type_name(local->type));
|
|
}
|
|
}
|
|
return true;
|
|
}
|